mirror of https://github.com/astral-sh/ruff
Add rendering of rule markdown for terminal output (#2747)
Add rendering of rule markdown for terminal output
This is achieved by making use of the `mdcat` crate.
See the following links for details:
- https://crates.io/crates/mdcat
- https://github.com/swsnr/mdcat
- https://docs.rs/mdcat/latest/mdcat/
This commit is contained in:
parent
1b61d4e18b
commit
551b810aeb
File diff suppressed because it is too large
Load Diff
|
|
@ -896,7 +896,7 @@ mod tests {
|
||||||
for rule in Rule::iter() {
|
for rule in Rule::iter() {
|
||||||
let code = rule.code();
|
let code = rule.code();
|
||||||
let (linter, rest) =
|
let (linter, rest) =
|
||||||
Linter::parse_code(code).unwrap_or_else(|| panic!("couldn't parse {:?}", code));
|
Linter::parse_code(code).unwrap_or_else(|| panic!("couldn't parse {code:?}"));
|
||||||
assert_eq!(code, format!("{}{rest}", linter.common_prefix()));
|
assert_eq!(code, format!("{}{rest}", linter.common_prefix()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ fn has_duplicates(s: &str) -> bool {
|
||||||
for ch in s.chars() {
|
for ch in s.chars() {
|
||||||
if escaped {
|
if escaped {
|
||||||
escaped = false;
|
escaped = false;
|
||||||
let pair = format!("\\{}", ch);
|
let pair = format!("\\{ch}");
|
||||||
if !seen.insert(pair) {
|
if !seen.insert(pair) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -915,7 +915,7 @@ impl<'a> Generator<'a> {
|
||||||
}
|
}
|
||||||
Constant::None => self.p("None"),
|
Constant::None => self.p("None"),
|
||||||
Constant::Bool(b) => self.p(if *b { "True" } else { "False" }),
|
Constant::Bool(b) => self.p(if *b { "True" } else { "False" }),
|
||||||
Constant::Int(i) => self.p(&format!("{}", i)),
|
Constant::Int(i) => self.p(&format!("{i}")),
|
||||||
Constant::Tuple(tup) => {
|
Constant::Tuple(tup) => {
|
||||||
if let [elt] = &**tup {
|
if let [elt] = &**tup {
|
||||||
self.p("(");
|
self.p("(");
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,10 @@ glob = { version = "0.3.0" }
|
||||||
ignore = { version = "0.4.18" }
|
ignore = { version = "0.4.18" }
|
||||||
itertools = { version = "0.10.5" }
|
itertools = { version = "0.10.5" }
|
||||||
log = { version = "0.4.17" }
|
log = { version = "0.4.17" }
|
||||||
|
mdcat = { version = "1.0.0", default-features = false }
|
||||||
notify = { version = "5.0.0" }
|
notify = { version = "5.0.0" }
|
||||||
path-absolutize = { version = "3.0.14", features = ["once_cell_cache"] }
|
path-absolutize = { version = "3.0.14", features = ["once_cell_cache"] }
|
||||||
|
pulldown-cmark = { version = "0.9.2", default-features = false, features = ['simd'] }
|
||||||
quick-junit = { version = "0.3.2" }
|
quick-junit = { version = "0.3.2" }
|
||||||
rayon = { version = "1.5.3" }
|
rayon = { version = "1.5.3" }
|
||||||
regex = { version = "1.6.0" }
|
regex = { version = "1.6.0" }
|
||||||
|
|
@ -50,9 +52,10 @@ rustc-hash = { version = "1.1.0" }
|
||||||
serde = { version = "1.0.147", features = ["derive"] }
|
serde = { version = "1.0.147", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.87" }
|
serde_json = { version = "1.0.87" }
|
||||||
similar = { version = "2.2.1" }
|
similar = { version = "2.2.1" }
|
||||||
|
strum = { version = "0.24.1" }
|
||||||
|
syntect = { version = "5.0.0", default-features = false, features = ["parsing", "regex-fancy", "default-themes", "default-syntaxes"] }
|
||||||
textwrap = { version = "0.16.0" }
|
textwrap = { version = "0.16.0" }
|
||||||
walkdir = { version = "2.3.2" }
|
walkdir = { version = "2.3.2" }
|
||||||
strum = "0.24.1"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_cmd = { version = "2.0.4" }
|
assert_cmd = { version = "2.0.4" }
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ pub enum Command {
|
||||||
rule: Rule,
|
rule: Rule,
|
||||||
|
|
||||||
/// Output format
|
/// Output format
|
||||||
#[arg(long, value_enum, default_value = "text")]
|
#[arg(long, value_enum, default_value = "markdown")]
|
||||||
format: HelpFormat,
|
format: HelpFormat,
|
||||||
},
|
},
|
||||||
/// List all supported upstream linters
|
/// List all supported upstream linters
|
||||||
|
|
@ -284,6 +284,7 @@ pub struct CheckArgs {
|
||||||
pub enum HelpFormat {
|
pub enum HelpFormat {
|
||||||
Text,
|
Text,
|
||||||
Json,
|
Json,
|
||||||
|
Markdown,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::module_name_repetitions)]
|
#[allow(clippy::module_name_repetitions)]
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,15 @@ use std::path::{Path, PathBuf};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
|
use colored::control::SHOULD_COLORIZE;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use ignore::Error;
|
use ignore::Error;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
|
use mdcat::terminal::{TerminalProgram, TerminalSize};
|
||||||
|
use mdcat::{Environment, ResourceAccess, Settings};
|
||||||
use path_absolutize::path_dedot;
|
use path_absolutize::path_dedot;
|
||||||
|
use pulldown_cmark::{Options, Parser};
|
||||||
#[cfg(not(target_family = "wasm"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use ruff::cache::CACHE_DIR_NAME;
|
use ruff::cache::CACHE_DIR_NAME;
|
||||||
|
|
@ -20,6 +24,7 @@ use ruff::resolver::PyprojectDiscovery;
|
||||||
use ruff::settings::flags;
|
use ruff::settings::flags;
|
||||||
use ruff::{fix, fs, packaging, resolver, warn_user_once, AutofixAvailability, IOError};
|
use ruff::{fix, fs, packaging, resolver, warn_user_once, AutofixAvailability, IOError};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use syntect::parsing::SyntaxSet;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::args::{HelpFormat, Overrides};
|
use crate::args::{HelpFormat, Overrides};
|
||||||
|
|
@ -281,9 +286,10 @@ struct Explanation<'a> {
|
||||||
pub fn rule(rule: &Rule, format: HelpFormat) -> Result<()> {
|
pub fn rule(rule: &Rule, format: HelpFormat) -> Result<()> {
|
||||||
let (linter, _) = Linter::parse_code(rule.code()).unwrap();
|
let (linter, _) = Linter::parse_code(rule.code()).unwrap();
|
||||||
let mut stdout = BufWriter::new(io::stdout().lock());
|
let mut stdout = BufWriter::new(io::stdout().lock());
|
||||||
match format {
|
|
||||||
HelpFormat::Text => {
|
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
|
|
||||||
|
match format {
|
||||||
|
HelpFormat::Text | HelpFormat::Markdown => {
|
||||||
output.push_str(&format!("# {} ({})", rule.as_ref(), rule.code()));
|
output.push_str(&format!("# {} ({})", rule.as_ref(), rule.code()));
|
||||||
output.push('\n');
|
output.push('\n');
|
||||||
output.push('\n');
|
output.push('\n');
|
||||||
|
|
@ -308,22 +314,46 @@ pub fn rule(rule: &Rule, format: HelpFormat) -> Result<()> {
|
||||||
output.push_str("Message formats:");
|
output.push_str("Message formats:");
|
||||||
for format in rule.message_formats() {
|
for format in rule.message_formats() {
|
||||||
output.push('\n');
|
output.push('\n');
|
||||||
output.push_str(&format!("* {}", format));
|
output.push_str(&format!("* {format}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writeln!(stdout, "{}", output)?;
|
|
||||||
}
|
}
|
||||||
HelpFormat::Json => {
|
HelpFormat::Json => {
|
||||||
writeln!(
|
output.push_str(&serde_json::to_string_pretty(&Explanation {
|
||||||
stdout,
|
|
||||||
"{}",
|
|
||||||
serde_json::to_string_pretty(&Explanation {
|
|
||||||
code: rule.code(),
|
code: rule.code(),
|
||||||
linter: linter.name(),
|
linter: linter.name(),
|
||||||
summary: rule.message_formats()[0],
|
summary: rule.message_formats()[0],
|
||||||
})?
|
})?);
|
||||||
)?;
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match format {
|
||||||
|
HelpFormat::Json | HelpFormat::Text => {
|
||||||
|
writeln!(stdout, "{output}")?;
|
||||||
|
}
|
||||||
|
HelpFormat::Markdown => {
|
||||||
|
let parser = Parser::new_ext(
|
||||||
|
&output,
|
||||||
|
Options::ENABLE_TASKLISTS | Options::ENABLE_STRIKETHROUGH,
|
||||||
|
);
|
||||||
|
|
||||||
|
let cwd = std::env::current_dir()?;
|
||||||
|
let env = &Environment::for_local_directory(&cwd)?;
|
||||||
|
|
||||||
|
let terminal = if SHOULD_COLORIZE.should_colorize() {
|
||||||
|
TerminalProgram::detect()
|
||||||
|
} else {
|
||||||
|
TerminalProgram::Dumb
|
||||||
|
};
|
||||||
|
|
||||||
|
let settings = &Settings {
|
||||||
|
resource_access: ResourceAccess::LocalOnly,
|
||||||
|
syntax_set: SyntaxSet::load_defaults_newlines(),
|
||||||
|
terminal_capabilities: terminal.capabilities(),
|
||||||
|
terminal_size: TerminalSize::detect().unwrap_or_default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
mdcat::push_tty(settings, env, &mut stdout, parser)?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,30 @@ pub fn linter(format: HelpFormat) {
|
||||||
println!("{}", serde_json::to_string_pretty(&linters).unwrap());
|
println!("{}", serde_json::to_string_pretty(&linters).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HelpFormat::Markdown => {
|
||||||
|
#[allow(clippy::print_stdout)]
|
||||||
|
{
|
||||||
|
println!("| {:>6} | {:<27} |", "Prefix", "Name");
|
||||||
|
println!("| {:>6} | {:<27} |", "------", "-".repeat(27));
|
||||||
|
}
|
||||||
|
for linter in Linter::iter() {
|
||||||
|
let prefix = match linter.common_prefix() {
|
||||||
|
"" => linter
|
||||||
|
.upstream_categories()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|UpstreamCategory(prefix, ..)| prefix.as_ref())
|
||||||
|
.join("/"),
|
||||||
|
prefix => prefix.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(clippy::print_stdout)]
|
||||||
|
{
|
||||||
|
println!("| {:>6} | {:<27} |", prefix, linter.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ pub fn main(args: &Args) -> Result<()> {
|
||||||
output.push_str(explanation.trim());
|
output.push_str(explanation.trim());
|
||||||
|
|
||||||
if args.dry_run {
|
if args.dry_run {
|
||||||
println!("{}", output);
|
println!("{output}");
|
||||||
} else {
|
} else {
|
||||||
fs::create_dir_all("docs/rules")?;
|
fs::create_dir_all("docs/rules")?;
|
||||||
fs::write(format!("docs/rules/{}.md", rule.as_ref()), output)?;
|
fs::write(format!("docs/rules/{}.md", rule.as_ref()), output)?;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue