fix(vscode): don't lint files in .gitignore and .eslintignore (#1765)

Closed https://github.com/oxc-project/oxc/issues/1752
This commit is contained in:
IWANABETHATGUY 2023-12-21 21:45:32 +08:00 committed by GitHub
parent 2e707bc1c7
commit fc7c857f80
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 4 deletions

1
Cargo.lock generated
View file

@ -1987,6 +1987,7 @@ dependencies = [
"dashmap", "dashmap",
"env_logger", "env_logger",
"futures", "futures",
"globset",
"ignore", "ignore",
"log", "log",
"miette", "miette",

View file

@ -39,3 +39,4 @@ tower-lsp = { workspace = true, features = ["proposed"] }
log = "0.4.20" log = "0.4.20"
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true } serde_json = { workspace = true }
globset = "0.4.14"

View file

@ -4,11 +4,13 @@ mod options;
mod walk; mod walk;
use crate::linter::{DiagnosticReport, ServerLinter}; use crate::linter::{DiagnosticReport, ServerLinter};
use globset::Glob;
use ignore::gitignore::Gitignore;
use log::{debug, error}; use log::{debug, error};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::path::PathBuf; use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
use dashmap::DashMap; use dashmap::DashMap;
@ -33,6 +35,7 @@ struct Backend {
server_linter: ServerLinter, server_linter: ServerLinter,
diagnostics_report_map: DashMap<String, Vec<DiagnosticReport>>, diagnostics_report_map: DashMap<String, Vec<DiagnosticReport>>,
options: Mutex<Options>, options: Mutex<Options>,
gitignore_glob: Mutex<Option<Gitignore>>,
} }
#[derive(Debug, Serialize, Deserialize, Default, PartialEq, PartialOrd, Clone, Copy)] #[derive(Debug, Serialize, Deserialize, Default, PartialEq, PartialOrd, Clone, Copy)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -71,6 +74,7 @@ enum SyntheticRunLevel {
impl LanguageServer for Backend { impl LanguageServer for Backend {
async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> { async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
self.init(params.root_uri)?; self.init(params.root_uri)?;
self.init_ignore_glob().await;
let options = params.initialization_options.and_then(|mut value| { let options = params.initialization_options.and_then(|mut value| {
let settings = value.get_mut("settings")?.take(); let settings = value.get_mut("settings")?.take();
serde_json::from_value::<Options>(settings).ok() serde_json::from_value::<Options>(settings).ok()
@ -166,6 +170,9 @@ impl LanguageServer for Backend {
if run_level < SyntheticRunLevel::OnSave { if run_level < SyntheticRunLevel::OnSave {
return; return;
} }
if self.is_ignored(&params.text_document.uri).await {
return;
}
self.handle_file_update(params.text_document.uri, None).await; self.handle_file_update(params.text_document.uri, None).await;
} }
@ -176,6 +183,10 @@ impl LanguageServer for Backend {
if run_level < SyntheticRunLevel::OnType { if run_level < SyntheticRunLevel::OnType {
return; return;
} }
if self.is_ignored(&params.text_document.uri).await {
return;
}
let content = params.content_changes.first().map(|c| c.text.clone()); let content = params.content_changes.first().map(|c| c.text.clone());
self.handle_file_update(params.text_document.uri, content).await; self.handle_file_update(params.text_document.uri, content).await;
} }
@ -185,6 +196,9 @@ impl LanguageServer for Backend {
if run_level < SyntheticRunLevel::OnType { if run_level < SyntheticRunLevel::OnType {
return; return;
} }
if self.is_ignored(&params.text_document.uri).await {
return;
}
self.handle_file_update(params.text_document.uri, None).await; self.handle_file_update(params.text_document.uri, None).await;
} }
@ -244,12 +258,42 @@ impl Backend {
}; };
Error { code: ErrorCode::ParseError, message, data: None } Error { code: ErrorCode::ParseError, message, data: None }
}) })?;
Ok(())
}
async fn init_ignore_glob(&self) {
let uri = self
.root_uri
.get()
.expect("The root uri should be initialized already")
.as_ref()
.expect("should get uri");
let mut builder = globset::GlobSetBuilder::new();
// Collecting all ignore files
builder.add(Glob::new("**/.eslintignore").unwrap());
builder.add(Glob::new("**/.gitignore").unwrap());
let ignore_file_glob_set = builder.build().unwrap();
let mut gitignore_builder = ignore::gitignore::GitignoreBuilder::new(uri.path());
let walk = ignore::WalkBuilder::new(uri.path())
.ignore(true)
.hidden(false)
.git_global(false)
.build();
for entry in walk.flatten() {
if ignore_file_glob_set.is_match(entry.path()) {
gitignore_builder.add(entry.path());
}
}
*self.gitignore_glob.lock().await = gitignore_builder.build().ok();
} }
#[allow(clippy::ptr_arg)] #[allow(clippy::ptr_arg)]
async fn publish_all_diagnostics(&self, result: &Vec<(PathBuf, Vec<Diagnostic>)>) { async fn publish_all_diagnostics(&self, result: &Vec<(PathBuf, Vec<Diagnostic>)>) {
debug!("{:?}", result);
join_all(result.iter().map(|(path, diagnostics)| { join_all(result.iter().map(|(path, diagnostics)| {
self.client.publish_diagnostics( self.client.publish_diagnostics(
Url::from_file_path(path).unwrap(), Url::from_file_path(path).unwrap(),
@ -276,6 +320,14 @@ impl Backend {
} }
} }
} }
async fn is_ignored(&self, uri: &Url) -> bool {
let Some(ref gitignore_globs) = *self.gitignore_glob.lock().await else {
return false;
};
let path = PathBuf::from(uri.path());
gitignore_globs.matched_path_or_any_parents(&path, path.is_dir()).is_ignore()
}
} }
#[tokio::main] #[tokio::main]
@ -293,7 +345,8 @@ async fn main() {
root_uri: OnceCell::new(), root_uri: OnceCell::new(),
server_linter, server_linter,
diagnostics_report_map, diagnostics_report_map,
options: tokio::sync::Mutex::new(Options::default()), options: Mutex::new(Options::default()),
gitignore_glob: Mutex::new(None),
}) })
.finish(); .finish();