-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #46 from ehuss/markdown-style
Add markdown style guide
- Loading branch information
Showing
6 changed files
with
233 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
target |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[package] | ||
name = "style-check" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
pulldown-cmark = "0.10.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
use std::env; | ||
use std::error::Error; | ||
use std::fs; | ||
use std::path::Path; | ||
|
||
macro_rules! style_error { | ||
($bad:expr, $path:expr, $($arg:tt)*) => { | ||
*$bad = true; | ||
eprint!("error in {}: ", $path.display()); | ||
eprintln!("{}", format_args!($($arg)*)); | ||
}; | ||
} | ||
|
||
fn main() { | ||
let arg = env::args().nth(1).unwrap_or_else(|| { | ||
eprintln!("Please pass a src directory as the first argument"); | ||
std::process::exit(1); | ||
}); | ||
|
||
let mut bad = false; | ||
if let Err(e) = check_directory(&Path::new(&arg), &mut bad) { | ||
eprintln!("error: {}", e); | ||
std::process::exit(1); | ||
} | ||
if bad { | ||
eprintln!("some style checks failed"); | ||
std::process::exit(1); | ||
} | ||
eprintln!("passed!"); | ||
} | ||
|
||
fn check_directory(dir: &Path, bad: &mut bool) -> Result<(), Box<dyn Error>> { | ||
for entry in fs::read_dir(dir)? { | ||
let entry = entry?; | ||
let path = entry.path(); | ||
|
||
if path.is_dir() { | ||
check_directory(&path, bad)?; | ||
continue; | ||
} | ||
|
||
if !matches!( | ||
path.extension().and_then(|p| p.to_str()), | ||
Some("md") | Some("html") | ||
) { | ||
// This may be extended in the future if other file types are needed. | ||
style_error!(bad, path, "expected only md or html in src"); | ||
} | ||
|
||
let contents = fs::read_to_string(&path)?; | ||
if contents.contains("#![feature") { | ||
style_error!(bad, path, "#![feature] attributes are not allowed"); | ||
} | ||
if !cfg!(windows) && contents.contains('\r') { | ||
style_error!( | ||
bad, | ||
path, | ||
"CR characters not allowed, must use LF line endings" | ||
); | ||
} | ||
if contents.contains('\t') { | ||
style_error!(bad, path, "tab characters not allowed, use spaces"); | ||
} | ||
if contents.contains('\u{2013}') { | ||
style_error!(bad, path, "en-dash not allowed, use two dashes like --"); | ||
} | ||
if contents.contains('\u{2014}') { | ||
style_error!(bad, path, "em-dash not allowed, use three dashes like ---"); | ||
} | ||
if !contents.ends_with('\n') { | ||
style_error!(bad, path, "file must end with a newline"); | ||
} | ||
for line in contents.lines() { | ||
if line.ends_with(' ') { | ||
style_error!(bad, path, "lines must not end with spaces"); | ||
} | ||
} | ||
cmark_check(&path, bad, &contents)?; | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn cmark_check(path: &Path, bad: &mut bool, contents: &str) -> Result<(), Box<dyn Error>> { | ||
use pulldown_cmark::{BrokenLink, CodeBlockKind, Event, Options, Parser, Tag}; | ||
|
||
macro_rules! cmark_error { | ||
($bad:expr, $path:expr, $range:expr, $($arg:tt)*) => { | ||
*$bad = true; | ||
let lineno = contents[..$range.start].chars().filter(|&ch| ch == '\n').count() + 1; | ||
eprint!("error in {} (line {}): ", $path.display(), lineno); | ||
eprintln!("{}", format_args!($($arg)*)); | ||
} | ||
} | ||
|
||
let options = Options::all(); | ||
// Can't use `bad` because it would get captured in closure. | ||
let mut link_err = false; | ||
let mut cb = |link: BrokenLink<'_>| { | ||
cmark_error!( | ||
&mut link_err, | ||
path, | ||
link.span, | ||
"broken {:?} link (reference `{}`)", | ||
link.link_type, | ||
link.reference | ||
); | ||
None | ||
}; | ||
let parser = Parser::new_with_broken_link_callback(contents, options, Some(&mut cb)); | ||
|
||
for (event, range) in parser.into_offset_iter() { | ||
match event { | ||
Event::Start(Tag::CodeBlock(CodeBlockKind::Indented)) => { | ||
cmark_error!( | ||
bad, | ||
path, | ||
range, | ||
"indented code blocks should use triple backtick-style \ | ||
with a language identifier" | ||
); | ||
} | ||
Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(languages))) => { | ||
if languages.is_empty() { | ||
cmark_error!( | ||
bad, | ||
path, | ||
range, | ||
"code block should include an explicit language", | ||
); | ||
} | ||
} | ||
_ => {} | ||
} | ||
} | ||
*bad |= link_err; | ||
Ok(()) | ||
} |