diff --git a/.autocorrectrc.template b/.autocorrectrc.template index f9e0f63d..4368e567 100644 --- a/.autocorrectrc.template +++ b/.autocorrectrc.template @@ -2,6 +2,10 @@ rules: # Default rules: https://github.com/huacnlee/autocorrect/raw/main/autocorrect/.autocorrectrc.default spellcheck: 2 +# Enable or disable in spatial context +context: + # Enable or disable to format codeblock in Markdown or AsciiDoc etc. + codeblock: 1 textRules: # Config some special rule for some texts # For example, if we wants to let "Hello你好" just warning, and "Hi你好" to ignore diff --git a/autocorrect-cli/src/lib.rs b/autocorrect-cli/src/lib.rs index 4db4a63e..f70dcbe4 100644 --- a/autocorrect-cli/src/lib.rs +++ b/autocorrect-cli/src/lib.rs @@ -175,7 +175,7 @@ where let mut filetype = autocorrect::get_file_extension(filepath); if let Some(ref ftype) = cli.filetype { - filetype = ftype.to_owned(); + filetype = ftype.to_string(); } if !autocorrect::is_support_type(&filetype) { continue; @@ -262,15 +262,13 @@ where // Exit with code = 1 std::process::exit(1); } + } else if cli.formatter == cli::OutputFormatter::Json { + log::info!("{}", autocorrect::json::to_lint_results_json(lint_results)); } else { - if cli.formatter == cli::OutputFormatter::Json { - log::info!("{}", autocorrect::json::to_lint_results_json(lint_results)); - } else { - log::info!( - "{}", - autocorrect::rdjson::to_lint_results_rdjson(lint_results) - ) - } + log::info!( + "{}", + autocorrect::rdjson::to_lint_results_rdjson(lint_results) + ) } } else if cli.fix { progress::finish(&cli, start_t); @@ -365,11 +363,9 @@ fn lint_and_output( progress::warn(cli); } - if cli.formatter.is_diff() { - if result.has_error() { - log::debug!("{}\n{}", filepath, result.error); - return; - } + if cli.formatter.is_diff() && result.has_error() { + log::debug!("{}\n{}", filepath, result.error); + return; } results.push(result.clone()); diff --git a/autocorrect-cli/src/progress.rs b/autocorrect-cli/src/progress.rs index f3b6d970..f1c14e1d 100644 --- a/autocorrect-cli/src/progress.rs +++ b/autocorrect-cli/src/progress.rs @@ -1,8 +1,5 @@ use owo_colors::OwoColorize; -use std::{ - io::{self, Write}, - time::SystemTime, -}; +use std::time::SystemTime; use crate::{cli::Cli, logger::SystemTimeDuration as _}; @@ -11,7 +8,7 @@ pub fn ok(cli: &Cli) { return; } - write!(io::stdout(), "{}", ".".green()).unwrap(); + print!("{}", ".".green()); } pub fn warn(cli: &Cli) { @@ -19,7 +16,7 @@ pub fn warn(cli: &Cli) { return; } - write!(io::stdout(), "{}", ".".yellow()).unwrap(); + print!("{}", ".".yellow()); } pub fn err(cli: &Cli) { @@ -27,7 +24,7 @@ pub fn err(cli: &Cli) { return; } - write!(io::stdout(), "{}", ".".red()).unwrap(); + print!("{}", ".".red()); } /// print time spend from start_t to now diff --git a/autocorrect-lsp/src/lib.rs b/autocorrect-lsp/src/lib.rs index 11139ec9..fa6a3e74 100644 --- a/autocorrect-lsp/src/lib.rs +++ b/autocorrect-lsp/src/lib.rs @@ -34,8 +34,8 @@ impl Backend { .map(|old| std::mem::replace(old, doc.clone())); } - fn get_document<'a>(&'a self, uri: &Url) -> Option> { - self.documents.read().unwrap().get(uri).map(|a| a.clone()) + fn get_document(&self, uri: &Url) -> Option> { + self.documents.read().unwrap().get(uri).cloned() } fn remove_document(&self, uri: &Url) { @@ -47,7 +47,7 @@ impl Backend { let input = document.text.as_str(); let path = document.uri.path(); - let result = autocorrect::lint_for(input, &path); + let result = autocorrect::lint_for(input, path); let diagnostics = result .lines @@ -124,7 +124,7 @@ impl Backend { fn is_ignored(&self, uri: &Url) -> bool { if let Some(ignorer) = self.ignorer.read().unwrap().as_ref() { - if let Some(filepath) = uri.to_file_path().ok() { + if let Ok(filepath) = uri.to_file_path() { return ignorer.is_ignored(&filepath.to_string_lossy()); } } @@ -302,12 +302,12 @@ impl LanguageServer for Backend { self.clear_diagnostics(&text_document.uri).await; let input = document.text.as_str(); - let result = autocorrect::format_for(input, &document.uri.path()); + let result = autocorrect::format_for(input, document.uri.path()); let range = Range::new( Position::new(0, 0), Position { - line: u32::max_value(), - character: u32::max_value(), + line: u32::MAX, + character: u32::MAX, }, ); return Ok(Some(vec![TextEdit::new(range, result.out)])); @@ -345,7 +345,7 @@ impl LanguageServer for Backend { vec![( text_document.uri.clone(), vec![TextEdit { - range: diagnostic.range.clone(), + range: diagnostic.range, new_text: diagnostic.message.clone(), }], )] @@ -370,13 +370,12 @@ pub async fn start() { let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout(); - let (service, socket) = LspService::new(|client| { - return Backend { - client, - work_dir: RwLock::new(PathBuf::new()), - documents: RwLock::new(HashMap::new()), - ignorer: RwLock::new(None), - }; + let (service, socket) = LspService::new(|client| Backend { + client, + work_dir: RwLock::new(PathBuf::new()), + documents: RwLock::new(HashMap::new()), + ignorer: RwLock::new(None), }); + Server::new(stdin, stdout, socket).serve(service).await; } diff --git a/autocorrect/.autocorrectrc.default b/autocorrect/.autocorrectrc.default index 0a81ef05..a0664780 100644 --- a/autocorrect/.autocorrectrc.default +++ b/autocorrect/.autocorrectrc.default @@ -23,6 +23,10 @@ rules: halfwidth-punctuation: 1 # Spellcheck spellcheck: 0 +# Enable or disable in spatial context +context: + # Enable or disable to format codeblock in Markdown or AsciiDoc etc. + codeblock: 1 textRules: # No default text rules. spellcheck: diff --git a/autocorrect/src/code/code.rs b/autocorrect/src/code/code.rs index b1519d8d..ca292c55 100644 --- a/autocorrect/src/code/code.rs +++ b/autocorrect/src/code/code.rs @@ -3,6 +3,7 @@ use super::*; use crate::config::toggle; pub use crate::result::*; use crate::rule::CJK_RE; +use crate::Config; use pest::error::Error; use pest::iterators::{Pair, Pairs}; use pest::RuleType; @@ -44,7 +45,6 @@ fn format_pair(results: &mut O, pair: Pair) { let rule_name = rule_name.as_str(); // println!("rule: {}, {}", rule_name, item.as_str()); - match rule_name { "string" | "link_string" | "mark_string" | "text" | "inner_text" | "comment" | "COMMENT" => { @@ -182,12 +182,19 @@ fn format_or_lint_for_inline_scripts( let part = pair.as_str(); let (base_line, _) = pair.line_col(); + let is_enable_context = + rule_name != "codeblock" || Config::current().is_enabled_context("codeblock"); + if results.is_lint() { // Skip lint if AutoCorrect disabled if !results.is_enabled() { return; } + if !is_enable_context { + return; + } + let sub_result = match rule_name { "inline_style" => Some(lint_for(part, "css")), "inline_javascript" => Some(lint_for(part, "js")), @@ -213,7 +220,7 @@ fn format_or_lint_for_inline_scripts( let mut new_part = String::from(part); // Skip format if AutoCorrect disabled - if results.is_enabled() { + if results.is_enabled() && is_enable_context { let sub_result = match rule_name { "inline_style" => Some(format_for(part, "css")), "inline_javascript" => Some(format_for(part, "js")), @@ -221,6 +228,7 @@ fn format_or_lint_for_inline_scripts( // WARNING: nested codeblock, when call format_for again. // Because codeblock.data has wrap chars, this make overflowed its stack. let mut codeblock = Codeblock::from_pair(pair); + let mut result = format_for(&codeblock.code, &codeblock.lang); codeblock.update_data(&result.out); result.out = codeblock.data; diff --git a/autocorrect/src/code/markdown.rs b/autocorrect/src/code/markdown.rs index bb6b6708..391f5c6e 100644 --- a/autocorrect/src/code/markdown.rs +++ b/autocorrect/src/code/markdown.rs @@ -11,6 +11,8 @@ struct MarkdownParser; #[cfg(test)] mod tests { + use crate::config::SeverityMode; + use super::*; use indoc::indoc; use pretty_assertions::assert_eq; @@ -356,4 +358,36 @@ mod tests { assert_eq!(expected, format_markdown(raw).out); } + + #[test] + fn test_disable_context_codeblock() { + use std::collections::HashMap; + + let last_mode = *crate::config::Config::current() + .context + .get("codeblock") + .unwrap(); + + crate::config::CURRENT_CONFIG.write().unwrap().context = map! { + "codeblock".to_string() => SeverityMode::Off, + }; + + let raw = indoc! {r###" + ```rust + // 这段应该ignore掉 + ``` + "###}; + + let expected = indoc! {r###" + ```rust + // 这段应该ignore掉 + ``` + "###}; + + assert_eq!(expected, format_for(raw, "markdown").to_string()); + + crate::config::CURRENT_CONFIG.write().unwrap().context = map! { + "codeblock".to_string() => last_mode, + }; + } } diff --git a/autocorrect/src/config/mod.rs b/autocorrect/src/config/mod.rs index 83888017..7163096e 100644 --- a/autocorrect/src/config/mod.rs +++ b/autocorrect/src/config/mod.rs @@ -10,7 +10,8 @@ use std::{ collections::HashMap, fs, path::Path, - sync::{Arc, RwLock, RwLockReadGuard}, + rc::Rc, + sync::{RwLock, RwLockReadGuard}, }; use crate::serde_any; @@ -20,7 +21,8 @@ lazy_static! { env!("CARGO_MANIFEST_DIR"), "/.autocorrectrc.default" )); - static ref CURRENT_CONFIG: RwLock = RwLock::new(Config::from_str(&CONFIG_STR).unwrap()); + pub(crate) static ref CURRENT_CONFIG: RwLock = + RwLock::new(Config::from_str(&CONFIG_STR).unwrap()); } pub trait ConfigFileTypes { @@ -45,7 +47,7 @@ impl ConfigFileTypes for HashMap { } } -#[derive(Deserialize, Serialize, Clone, Debug)] +#[derive(Deserialize, Serialize, Default, Clone, Debug)] #[serde(rename_all = "camelCase")] pub struct Config { #[serde(default)] @@ -58,17 +60,8 @@ pub struct Config { // Addition file types map, high priority than default #[serde(default)] pub file_types: HashMap, -} - -impl Default for Config { - fn default() -> Self { - Config { - rules: HashMap::new(), - text_rules: HashMap::new(), - spellcheck: SpellcheckConfig::default(), - file_types: HashMap::new(), - } - } + #[serde(default)] + pub context: HashMap, } pub fn load_file(config_file: &str) -> Result { @@ -132,8 +125,8 @@ impl From for Error { } impl Config { - pub fn current() -> Arc> { - Arc::new(CURRENT_CONFIG.read().unwrap()) + pub fn current() -> Rc> { + Rc::new(CURRENT_CONFIG.read().unwrap()) } pub fn get_file_type(&self, ext: &str) -> Option<&str> { @@ -187,6 +180,15 @@ impl Config { Ok(self.clone()) } + + /// Check is enable format in context + pub fn is_enabled_context(&self, name: &str) -> bool { + if let Some(mode) = self.context.get(name) { + return *mode != SeverityMode::Off; + } + + false + } } // Setup config for test for load tests/.autocorrectrc.test @@ -366,6 +368,7 @@ mod tests { words: vec!["foo".to_string(), "bar".to_string(), "baz".to_string()], ..Default::default() }, + ..Default::default() }; let config1 = Config { @@ -384,6 +387,7 @@ mod tests { words: vec!["foo1".to_string(), "bar1".to_string()], ..Default::default() }, + ..Default::default() }; config.merge(&config1).unwrap(); diff --git a/autocorrect/src/config/severity.rs b/autocorrect/src/config/severity.rs index 707f582a..2572d1ff 100644 --- a/autocorrect/src/config/severity.rs +++ b/autocorrect/src/config/severity.rs @@ -1,7 +1,8 @@ use serde::{Deserialize, Serialize, Serializer}; -#[derive(PartialEq, Eq, Clone, Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] pub enum SeverityMode { + #[default] Off = 0, Error = 1, Warning = 2, diff --git a/autocorrect/src/keyword.rs b/autocorrect/src/keyword.rs index 38ac5bc4..29fb2c0c 100644 --- a/autocorrect/src/keyword.rs +++ b/autocorrect/src/keyword.rs @@ -155,7 +155,7 @@ impl Node { c }; - while node.children.get(&c).is_none() { + while !node.children.contains_key(&c) { if node.fail.is_none() { node = self; break; diff --git a/autocorrect/src/result/json.rs b/autocorrect/src/result/json.rs index e4bcb87f..634eef20 100644 --- a/autocorrect/src/result/json.rs +++ b/autocorrect/src/result/json.rs @@ -21,7 +21,7 @@ pub(crate) fn crate_test_lint_results() -> Vec { let mut lint_result = LintResult::new("hello你好.\n这是第2行"); lint_result.line = 10; lint_result.col = 12; - lint_result.filepath = "./test/foo/bar.rs".to_owned(); + lint_result.filepath = "./test/foo/bar.rs".to_string(); lint_result.push(LineResult { line: 1, col: 1, diff --git a/autocorrect/src/result/mod.rs b/autocorrect/src/result/mod.rs index a9669740..aa545aaa 100644 --- a/autocorrect/src/result/mod.rs +++ b/autocorrect/src/result/mod.rs @@ -5,20 +5,15 @@ use serde_repr::*; use crate::config::toggle; -#[derive(Serialize_repr, Deserialize_repr, PartialEq, Eq, Debug, Clone, Copy)] +#[derive(Serialize_repr, Deserialize_repr, PartialEq, Eq, Debug, Default, Clone, Copy)] #[repr(u8)] pub enum Severity { + #[default] Pass = 0, Error = 1, Warning = 2, } -impl Default for Severity { - fn default() -> Self { - Severity::Pass - } -} - impl Severity { pub fn is_error(&self) -> bool { self == &Severity::Error @@ -146,7 +141,7 @@ impl Results for FormatResult { fn error(&mut self, err: &str) { // Revert out to raw when has error, make sure return raw value. - self.out = self.raw.clone(); + self.out = String::from(&self.raw); self.error = String::from(err); } diff --git a/autocorrect/src/result/rdjson.rs b/autocorrect/src/result/rdjson.rs index a618c2c0..532f4c88 100644 --- a/autocorrect/src/result/rdjson.rs +++ b/autocorrect/src/result/rdjson.rs @@ -114,8 +114,7 @@ pub(crate) fn to_rdjson_diagnostics(lint_result: &LintResult) -> Vec { pub fn to_lint_results_rdjson(lint_results: Vec) -> String { let diagnostics = lint_results .iter() - .map(|r| to_rdjson_diagnostics(r)) - .flatten() + .flat_map(to_rdjson_diagnostics) .collect::>() .join(","); format!( diff --git a/autocorrect/src/rule/fullwidth.rs b/autocorrect/src/rule/fullwidth.rs index 528902b1..aafc5f6a 100644 --- a/autocorrect/src/rule/fullwidth.rs +++ b/autocorrect/src/rule/fullwidth.rs @@ -31,8 +31,8 @@ lazy_static! { } // fullwidth correct punctuations near the CJK chars -pub fn format<'h>(text: &'h str) -> String { - let out = PUNCTUATION_WITH_LEFT_CJK_RE.replace_all(&text, |cap: ®ex::Captures| { +pub fn format(text: &str) -> String { + let out = PUNCTUATION_WITH_LEFT_CJK_RE.replace_all(text, |cap: ®ex::Captures| { fullwidth_replace_part(&cap[0]) }); diff --git a/autocorrect/src/rule/mod.rs b/autocorrect/src/rule/mod.rs index 76e259db..bd1352c1 100644 --- a/autocorrect/src/rule/mod.rs +++ b/autocorrect/src/rule/mod.rs @@ -281,6 +281,10 @@ mod tests { "hello你好 “Quote” 和 ‘Single Quote’ 测试1" => (map!{}, "hello 你好“Quote”和‘Single Quote’测试 1"), "你好-世界" => (map!{}, "你好 - 世界"), "世界-你好" => (map!{"space-dash" => true}, "世界-你好"), + "1你好[世界]" => (map!{ }, "1 你好 [世界]"), + "2你好[世界]" => (map!{ "space-bracket" => true }, "2 你好[世界]"), + "代码`code`例子1" => (map!{}, "代码 `code` 例子 1"), + "代码`code`例子2" => (map!{ "space-backticks" => true }, "代码`code`例子 2"), }; for (input, (disable_rules, expect)) in cases { diff --git a/autocorrect/src/rule/word.rs b/autocorrect/src/rule/word.rs index 1fdfbc2a..99b35308 100644 --- a/autocorrect/src/rule/word.rs +++ b/autocorrect/src/rule/word.rs @@ -119,7 +119,7 @@ pub fn format_no_space_fullwidth_quote(input: &str) -> String { #[cfg(test)] mod tests { - use crate::rule::word::{format_space_backticks, format_space_dash}; + use crate::rule::word::{format_space_backticks, format_space_bracket, format_space_dash}; #[test] fn test_format_space_dash() { @@ -129,6 +129,15 @@ mod tests { assert_eq!(format_space_dash("hello-world"), "hello-world"); } + #[test] + fn test_format_space_bracket() { + assert_eq!(format_space_bracket("你好[世界]"), "你好 [世界]"); + assert_eq!(format_space_bracket("你好(世界)"), "你好 (世界)"); + assert_eq!(format_space_bracket("foo[世界"), "foo[世界"); + assert_eq!(format_space_bracket("你好]world"), "你好]world"); + assert_eq!(format_space_bracket("hello]world"), "hello]world"); + } + #[test] fn test_format_space_backticks() { assert_eq!(format_space_backticks("代码`code`"), "代码 `code`");