mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 10:48:32 +00:00
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
9 changed files with 922 additions and 49 deletions
873
Cargo.lock
generated
873
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -896,7 +896,7 @@ mod tests {
|
|||
for rule in Rule::iter() {
|
||||
let code = rule.code();
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ fn has_duplicates(s: &str) -> bool {
|
|||
for ch in s.chars() {
|
||||
if escaped {
|
||||
escaped = false;
|
||||
let pair = format!("\\{}", ch);
|
||||
let pair = format!("\\{ch}");
|
||||
if !seen.insert(pair) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -915,7 +915,7 @@ impl<'a> Generator<'a> {
|
|||
}
|
||||
Constant::None => self.p("None"),
|
||||
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) => {
|
||||
if let [elt] = &**tup {
|
||||
self.p("(");
|
||||
|
|
|
@ -41,8 +41,10 @@ glob = { version = "0.3.0" }
|
|||
ignore = { version = "0.4.18" }
|
||||
itertools = { version = "0.10.5" }
|
||||
log = { version = "0.4.17" }
|
||||
mdcat = { version = "1.0.0", default-features = false }
|
||||
notify = { version = "5.0.0" }
|
||||
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" }
|
||||
rayon = { version = "1.5.3" }
|
||||
regex = { version = "1.6.0" }
|
||||
|
@ -50,9 +52,10 @@ rustc-hash = { version = "1.1.0" }
|
|||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde_json = { version = "1.0.87" }
|
||||
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" }
|
||||
walkdir = { version = "2.3.2" }
|
||||
strum = "0.24.1"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = { version = "2.0.4" }
|
||||
|
|
|
@ -41,7 +41,7 @@ pub enum Command {
|
|||
rule: Rule,
|
||||
|
||||
/// Output format
|
||||
#[arg(long, value_enum, default_value = "text")]
|
||||
#[arg(long, value_enum, default_value = "markdown")]
|
||||
format: HelpFormat,
|
||||
},
|
||||
/// List all supported upstream linters
|
||||
|
@ -284,6 +284,7 @@ pub struct CheckArgs {
|
|||
pub enum HelpFormat {
|
||||
Text,
|
||||
Json,
|
||||
Markdown,
|
||||
}
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
|
|
|
@ -4,11 +4,15 @@ use std::path::{Path, PathBuf};
|
|||
use std::time::Instant;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use colored::control::SHOULD_COLORIZE;
|
||||
use colored::Colorize;
|
||||
use ignore::Error;
|
||||
use itertools::Itertools;
|
||||
use log::{debug, error};
|
||||
use mdcat::terminal::{TerminalProgram, TerminalSize};
|
||||
use mdcat::{Environment, ResourceAccess, Settings};
|
||||
use path_absolutize::path_dedot;
|
||||
use pulldown_cmark::{Options, Parser};
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use rayon::prelude::*;
|
||||
use ruff::cache::CACHE_DIR_NAME;
|
||||
|
@ -20,6 +24,7 @@ use ruff::resolver::PyprojectDiscovery;
|
|||
use ruff::settings::flags;
|
||||
use ruff::{fix, fs, packaging, resolver, warn_user_once, AutofixAvailability, IOError};
|
||||
use serde::Serialize;
|
||||
use syntect::parsing::SyntaxSet;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::args::{HelpFormat, Overrides};
|
||||
|
@ -281,9 +286,10 @@ struct Explanation<'a> {
|
|||
pub fn rule(rule: &Rule, format: HelpFormat) -> Result<()> {
|
||||
let (linter, _) = Linter::parse_code(rule.code()).unwrap();
|
||||
let mut stdout = BufWriter::new(io::stdout().lock());
|
||||
let mut output = String::new();
|
||||
|
||||
match format {
|
||||
HelpFormat::Text => {
|
||||
let mut output = String::new();
|
||||
HelpFormat::Text | HelpFormat::Markdown => {
|
||||
output.push_str(&format!("# {} ({})", rule.as_ref(), rule.code()));
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
|
@ -308,22 +314,46 @@ pub fn rule(rule: &Rule, format: HelpFormat) -> Result<()> {
|
|||
output.push_str("Message formats:");
|
||||
for format in rule.message_formats() {
|
||||
output.push('\n');
|
||||
output.push_str(&format!("* {}", format));
|
||||
output.push_str(&format!("* {format}"));
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(stdout, "{}", output)?;
|
||||
}
|
||||
HelpFormat::Json => {
|
||||
writeln!(
|
||||
stdout,
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&Explanation {
|
||||
code: rule.code(),
|
||||
linter: linter.name(),
|
||||
summary: rule.message_formats()[0],
|
||||
})?
|
||||
)?;
|
||||
output.push_str(&serde_json::to_string_pretty(&Explanation {
|
||||
code: rule.code(),
|
||||
linter: linter.name(),
|
||||
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(())
|
||||
|
|
|
@ -47,6 +47,30 @@ pub fn linter(format: HelpFormat) {
|
|||
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());
|
||||
|
||||
if args.dry_run {
|
||||
println!("{}", output);
|
||||
println!("{output}");
|
||||
} else {
|
||||
fs::create_dir_all("docs/rules")?;
|
||||
fs::write(format!("docs/rules/{}.md", rule.as_ref()), output)?;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue