Skip to content

Commit

Permalink
feat: support extend-ignore-re config field
Browse files Browse the repository at this point in the history
resolves #55
  • Loading branch information
tekumara committed Apr 8, 2024
1 parent 4a559ff commit 1a03b6f
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 11 deletions.
63 changes: 55 additions & 8 deletions crates/typos-lsp/src/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,34 @@ impl LanguageServer for Backend<'static, 'static> {
}
}

// copied from https://github.com/crate-ci/typos/blob/c15b28fff9a814f9c12bd24cb1cfc114037e9187/crates/typos-cli/src/file.rs#L741
#[derive(Clone, Debug)]
struct Ignores {
blocks: Vec<std::ops::Range<usize>>,
}

impl Ignores {
fn new(content: &[u8], ignores: &[regex::Regex]) -> Self {
let mut blocks = Vec::new();
if let Ok(content) = std::str::from_utf8(content) {
for ignore in ignores {
for mat in ignore.find_iter(content) {
blocks.push(mat.range());
}
}
}
Self { blocks }
}

fn is_ignored(&self, span: std::ops::Range<usize>) -> bool {
let start = span.start;
let end = span.end.saturating_sub(1);
self.blocks
.iter()
.any(|block| block.contains(&start) || block.contains(&end))
}
}

impl<'s, 'p> Backend<'s, 'p> {
pub fn new(client: Client) -> Self {
Self {
Expand All @@ -248,27 +276,35 @@ impl<'s, 'p> Backend<'s, 'p> {
fn check_text(&self, buffer: &str, uri: &Url) -> Vec<Diagnostic> {
let state = self.state.lock().unwrap();

let (tokenizer, dict) = match uri.to_file_path() {
let (tokenizer, dict, ignore) = match uri.to_file_path() {
Err(_) => {
// eg: uris like untitled:* or term://*
tracing::debug!(
"check_text: Using default policy because cannot convert uri {} to file path",
uri
);
(self.default_policy.tokenizer, self.default_policy.dict)
(
self.default_policy.tokenizer,
self.default_policy.dict,
self.default_policy.ignore,
)
}
Ok(path) => {
let uri_path = url_path_sanitised(uri);

// find relevant tokenizer, and dict for the workspace folder
let (tokenizer, dict) = match state.router.at(&uri_path) {
let (tokenizer, dict, ignore) = match state.router.at(&uri_path) {
Err(_) => {
// ie: file:///
tracing::debug!(
"check_text: Using default policy because no route found for {}",
uri_path
);
(self.default_policy.tokenizer, self.default_policy.dict)
(
self.default_policy.tokenizer,
self.default_policy.dict,
self.default_policy.ignore,
)
}
Ok(Match { value, params: _ }) => {
tracing::debug!("check_text: path {}", &path.display());
Expand All @@ -281,20 +317,31 @@ impl<'s, 'p> Backend<'s, 'p> {
return Vec::default();
}
let policy = value.engine.policy(&path);
(policy.tokenizer, policy.dict)
(policy.tokenizer, policy.dict, policy.ignore)
}
};

(tokenizer, dict)
(tokenizer, dict, ignore)
}
};

let mut accum = AccumulatePosition::new();

// mimics https://github.com/crate-ci/typos/blob/c15b28fff9a814f9c12bd24cb1cfc114037e9187/crates/typos-cli/src/file.rs#L43
// but using check_str instead of check_bytes

let mut ignores: Option<Ignores> = None;

typos::check_str(buffer, tokenizer, dict)
.filter(|typo| {
// skip type if it matches extend-ignore-re
let is_ignored = ignores
.get_or_insert_with(|| Ignores::new(buffer.as_bytes(), ignore))
.is_ignored(typo.span());
tracing::debug!(typo = ?typo, is_ignored = is_ignored, "check_text");
!is_ignored
})
.map(|typo| {
tracing::debug!("check_text: {:?}", typo);

let (line_num, line_pos) = accum.pos(buffer.as_bytes(), typo.byte_offset);

Diagnostic {
Expand Down
9 changes: 8 additions & 1 deletion crates/typos-lsp/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,11 @@ async fn test_config_file() {
Url::from_file_path(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests")).unwrap();
let diag_txt = workspace_folder_uri.join("tests/diagnostics.txt").unwrap();
let changelog_md = workspace_folder_uri.join("tests/CHANGELOG.md").unwrap();
let skip_me = workspace_folder_uri.join("tests/skip_me.txt").unwrap();

let did_open_diag_txt = &did_open_with("fo typos", Some(&diag_txt));

let did_open_changelog_md = &did_open_with("fo typos", Some(&changelog_md));
let did_open_skip_me = &did_open_with("fo typos # skip_me", Some(&skip_me));

let mut server = TestServer::new();
let _ = server
Expand All @@ -187,6 +188,12 @@ async fn test_config_file() {
server.request(&did_open_changelog_md).await,
publish_diagnostics_with(&[], Some(&changelog_md)),
);

// check skip_line is excluded because of default.extend-ignore-re
similar_asserts::assert_eq!(
server.request(&did_open_skip_me).await,
publish_diagnostics_with(&[], Some(&skip_me)),
);
}

#[test_log::test(tokio::test)]
Expand Down
8 changes: 6 additions & 2 deletions crates/typos-lsp/tests/typos.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
[default]
# ignore lines with trailing # skip_me
extend-ignore-re = ["(?Rm)^.*#\\s*skip_me$"]

[default.extend-words]
# tell typos which of the several possible corrections to use
fo = "of"

[files]
# ignore typos in CHANGELOG.md
extend-exclude = ["CHANGELOG.md"]
# ignore typos in CHANGELOG.md, integration_test.rs
extend-exclude = ["CHANGELOG.md", "integration_test.rs"]

0 comments on commit 1a03b6f

Please sign in to comment.