Skip to content

Commit a6b9b45

Browse files
committed
refactor(language_server): use LintRunner
1 parent b1c0f28 commit a6b9b45

File tree

9 files changed

+207
-339
lines changed

9 files changed

+207
-339
lines changed

crates/oxc_language_server/src/linter/isolated_lint_handler.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use tower_lsp_server::{UriExt, lsp_types::Uri};
1111
use oxc_allocator::Allocator;
1212
use oxc_linter::{
1313
AllowWarnDeny, ConfigStore, DirectivesStore, DisableDirectives, LINTABLE_EXTENSIONS,
14-
LintOptions, LintService, LintServiceOptions, Linter, Message, RuntimeFileSystem,
15-
create_unused_directives_diagnostics, read_to_arena_str, read_to_string,
14+
LintOptions, LintRunner, LintRunnerBuilder, LintServiceOptions, Linter, Message,
15+
RuntimeFileSystem, create_unused_directives_diagnostics, read_to_arena_str, read_to_string,
1616
};
1717

1818
use super::error_with_position::{
@@ -23,12 +23,13 @@ use super::error_with_position::{
2323
#[derive(Debug, Clone)]
2424
pub struct IsolatedLintHandlerOptions {
2525
pub use_cross_module: bool,
26+
pub type_aware: bool,
2627
pub root_path: PathBuf,
2728
pub tsconfig_path: Option<PathBuf>,
2829
}
2930

3031
pub struct IsolatedLintHandler {
31-
service: LintService,
32+
runner: LintRunner,
3233
directives_coordinator: DirectivesStore,
3334
unused_directives_severity: Option<AllowWarnDeny>,
3435
}
@@ -81,11 +82,11 @@ impl IsolatedLintHandler {
8182
lint_service_options = lint_service_options.with_tsconfig(tsconfig_path);
8283
}
8384

84-
let mut service = LintService::new(linter, lint_service_options);
85-
service.set_disable_directives_map(directives_coordinator.map());
86-
8785
Self {
88-
service,
86+
runner: LintRunnerBuilder::new(lint_service_options, linter)
87+
.with_type_aware(options.type_aware)
88+
.build()
89+
.unwrap(),
8990
directives_coordinator,
9091
unused_directives_severity: lint_options.report_unused_directive,
9192
}
@@ -113,14 +114,14 @@ impl IsolatedLintHandler {
113114
debug!("lint {}", path.display());
114115
let rope = &Rope::from_str(source_text);
115116

117+
let fs = Box::new(IsolatedLintHandlerFileSystem::new(
118+
path.to_path_buf(),
119+
Arc::from(source_text),
120+
));
121+
116122
let mut messages: Vec<DiagnosticReport> = self
117-
.service
118-
.with_file_system(Box::new(IsolatedLintHandlerFileSystem::new(
119-
path.to_path_buf(),
120-
Arc::from(source_text),
121-
)))
122-
.with_paths(vec![Arc::from(path.as_os_str())])
123-
.run_source()
123+
.runner
124+
.run_source(&Arc::from(path.as_os_str()), source_text.to_string(), fs)
124125
.iter()
125126
.map(|message| message_to_lsp_diagnostic(message, uri, source_text, rope))
126127
.collect();

crates/oxc_language_server/src/linter/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,3 @@ pub mod error_with_position;
33
pub mod isolated_lint_handler;
44
pub mod options;
55
pub mod server_linter;
6-
pub mod tsgo_linter;

crates/oxc_language_server/src/linter/server_linter.rs

Lines changed: 21 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use crate::linter::{
1919
error_with_position::DiagnosticReport,
2020
isolated_lint_handler::{IsolatedLintHandler, IsolatedLintHandlerOptions},
2121
options::{LintOptions as LSPLintOptions, Run},
22-
tsgo_linter::TsgoLinter,
2322
};
2423
use crate::utils::normalize_path;
2524
use crate::{ConcurrentHashMap, LINT_CONFIG_FILE};
@@ -35,7 +34,6 @@ pub enum ServerLinterRun {
3534

3635
pub struct ServerLinter {
3736
isolated_linter: Arc<Mutex<IsolatedLintHandler>>,
38-
tsgo_linter: Arc<Option<TsgoLinter>>,
3937
ignore_matcher: LintIgnoreMatcher,
4038
gitignore_glob: Vec<Gitignore>,
4139
lint_on_run: Run,
@@ -46,7 +44,6 @@ pub struct ServerLinter {
4644
#[derive(Debug, Default)]
4745
struct ServerLinterDiagnostics {
4846
isolated_linter: Arc<ConcurrentHashMap<String, Option<Vec<DiagnosticReport>>>>,
49-
tsgo_linter: Arc<ConcurrentHashMap<String, Option<Vec<DiagnosticReport>>>>,
5047
}
5148

5249
impl ServerLinterDiagnostics {
@@ -59,29 +56,15 @@ impl ServerLinterDiagnostics {
5956
reports.extend(diagnostics.clone());
6057
}
6158
}
62-
if let Some(entry) = self.tsgo_linter.pin().get(path) {
63-
found = true;
64-
if let Some(diagnostics) = entry {
65-
reports.extend(diagnostics.clone());
66-
}
67-
}
6859
if found { Some(reports) } else { None }
6960
}
7061

7162
pub fn remove_diagnostics(&self, path: &str) {
7263
self.isolated_linter.pin().remove(path);
73-
self.tsgo_linter.pin().remove(path);
7464
}
7565

7666
pub fn get_cached_files_of_diagnostics(&self) -> Vec<String> {
77-
let isolated_files = self.isolated_linter.pin().keys().cloned().collect::<Vec<_>>();
78-
let tsgo_files = self.tsgo_linter.pin().keys().cloned().collect::<Vec<_>>();
79-
80-
let mut files = Vec::with_capacity(isolated_files.len() + tsgo_files.len());
81-
files.extend(isolated_files);
82-
files.extend(tsgo_files);
83-
files.dedup();
84-
files
67+
self.isolated_linter.pin().keys().cloned().collect::<Vec<_>>()
8568
}
8669
}
8770

@@ -153,9 +136,10 @@ impl ServerLinter {
153136

154137
let isolated_linter = IsolatedLintHandler::new(
155138
lint_options,
156-
config_store.clone(), // clone because tsgo linter needs it
139+
config_store,
157140
&IsolatedLintHandlerOptions {
158141
use_cross_module,
142+
type_aware: options.type_aware,
159143
root_path: root_path.to_path_buf(),
160144
tsconfig_path: options.ts_config_path.as_ref().map(|path| {
161145
let path = Path::new(path).to_path_buf();
@@ -175,11 +159,6 @@ impl ServerLinter {
175159
extended_paths,
176160
lint_on_run: options.run,
177161
diagnostics: ServerLinterDiagnostics::default(),
178-
tsgo_linter: if options.type_aware {
179-
Arc::new(Some(TsgoLinter::new(&root_path, config_store)))
180-
} else {
181-
Arc::new(None)
182-
},
183162
}
184163
}
185164

@@ -326,45 +305,26 @@ impl ServerLinter {
326305
content: Option<String>,
327306
run_type: ServerLinterRun,
328307
) -> Option<Vec<DiagnosticReport>> {
329-
let (oxlint, tsgolint) = match (run_type, self.lint_on_run) {
330-
// run everything on save, or when it is forced
331-
(ServerLinterRun::Always, _) | (ServerLinterRun::OnSave, Run::OnSave) => (true, true),
332-
// run only oxlint on type
333-
// tsgolint does not support memory source_text
334-
(ServerLinterRun::OnType, Run::OnType) => (true, false),
335-
// it does not match, run nothing
336-
(ServerLinterRun::OnType, Run::OnSave) => (false, false),
337-
// In onType mode, only TypeScript type checking runs on save
338-
// If type_aware is disabled (tsgo_linter is None), skip everything to preserve diagnostics
339-
(ServerLinterRun::OnSave, Run::OnType) => {
340-
let should_run_tsgo = self.tsgo_linter.as_ref().is_some();
341-
(false, should_run_tsgo)
342-
}
343-
};
308+
let run = !matches!(
309+
(run_type, self.lint_on_run),
310+
(ServerLinterRun::OnType, Run::OnSave) | (ServerLinterRun::OnSave, Run::OnType)
311+
);
344312

345313
// return `None` when both tools do not want to be used
346-
if !oxlint && !tsgolint {
314+
if !run {
347315
return None;
348316
}
349317

350318
if self.is_ignored(uri) {
351319
return None;
352320
}
353321

354-
if oxlint {
355-
let diagnostics = {
356-
let mut isolated_linter = self.isolated_linter.lock().await;
357-
isolated_linter.run_single(uri, content.clone())
358-
};
359-
self.diagnostics.isolated_linter.pin().insert(uri.to_string(), diagnostics);
360-
}
322+
let diagnostics = {
323+
let mut isolated_linter = self.isolated_linter.lock().await;
324+
isolated_linter.run_single(uri, content.clone())
325+
};
361326

362-
if tsgolint && let Some(tsgo_linter) = self.tsgo_linter.as_ref() {
363-
self.diagnostics
364-
.tsgo_linter
365-
.pin()
366-
.insert(uri.to_string(), tsgo_linter.lint_file(uri, content.clone()));
367-
}
327+
self.diagnostics.isolated_linter.pin().insert(uri.to_string(), diagnostics);
368328

369329
self.diagnostics.get_diagnostics(&uri.to_string())
370330
}
@@ -466,31 +426,23 @@ mod test {
466426
fn test_get_diagnostics_found_and_none_entries() {
467427
let key = "file:///test.js".to_string();
468428

469-
// Case 1: Both entries present, Some diagnostics
429+
// Case 1: Entry present, Some diagnostics
470430
let diag = DiagnosticReport::default();
471431
let diag_map = ConcurrentHashMap::default();
472-
diag_map.pin().insert(key.clone(), Some(vec![diag.clone()]));
473-
let tsgo_map = ConcurrentHashMap::default();
474-
tsgo_map.pin().insert(key.clone(), Some(vec![diag]));
432+
diag_map.pin().insert(key.clone(), Some(vec![diag]));
475433

476-
let server_diag = super::ServerLinterDiagnostics {
477-
isolated_linter: std::sync::Arc::new(diag_map),
478-
tsgo_linter: std::sync::Arc::new(tsgo_map),
479-
};
434+
let server_diag =
435+
super::ServerLinterDiagnostics { isolated_linter: std::sync::Arc::new(diag_map) };
480436
let result = server_diag.get_diagnostics(&key);
481437
assert!(result.is_some());
482-
assert_eq!(result.unwrap().len(), 2);
438+
assert_eq!(result.unwrap().len(), 1);
483439

484440
// Case 2: Entry present, but value is None
485441
let diag_map_none = ConcurrentHashMap::default();
486442
diag_map_none.pin().insert(key.clone(), None);
487-
let tsgo_map_none = ConcurrentHashMap::default();
488-
tsgo_map_none.pin().insert(key.clone(), None);
489443

490-
let server_diag_none = ServerLinterDiagnostics {
491-
isolated_linter: std::sync::Arc::new(diag_map_none),
492-
tsgo_linter: std::sync::Arc::new(tsgo_map_none),
493-
};
444+
let server_diag_none =
445+
ServerLinterDiagnostics { isolated_linter: std::sync::Arc::new(diag_map_none) };
494446
let result_none = server_diag_none.get_diagnostics(&key);
495447
assert!(result_none.is_some());
496448
assert_eq!(result_none.unwrap().len(), 0);
@@ -535,7 +487,7 @@ mod test {
535487
#[cfg(not(target_endian = "big"))]
536488
fn test_lint_on_run_on_save_on_save() {
537489
Tester::new(
538-
"fixtures/linter/lint_on_run/on_type",
490+
"fixtures/linter/lint_on_run/on_save",
539491
Some(LintOptions { type_aware: true, run: Run::OnSave, ..Default::default() }),
540492
)
541493
.test_and_snapshot_single_file_with_run_type("on-save.ts", Run::OnSave);

crates/oxc_language_server/src/linter/tsgo_linter.rs

Lines changed: 0 additions & 67 deletions
This file was deleted.

crates/oxc_language_server/src/snapshots/[email protected]

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,70 @@ source: crates/oxc_language_server/src/tester.rs
55
file: fixtures/linter/lint_on_run/on_save/on-save.ts
66
----------
77

8+
code: "eslint(no-debugger)"
9+
code_description.href: "https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html"
10+
message: "`debugger` statement is not allowed\nhelp: Remove the debugger statement"
11+
range: Range { start: Position { line: 1, character: 0 }, end: Position { line: 1, character: 9 } }
12+
related_information[0].message: ""
13+
related_information[0].location.uri: "file://<variable>/fixtures/linter/lint_on_run/on_save/on-save.ts"
14+
related_information[0].location.range: Range { start: Position { line: 1, character: 0 }, end: Position { line: 1, character: 9 } }
15+
severity: Some(Warning)
16+
source: Some("oxc")
17+
tags: None
18+
fixed: Multiple(
19+
[
20+
FixedContent {
21+
message: Some(
22+
"Remove the debugger statement",
23+
),
24+
code: "",
25+
range: Range {
26+
start: Position {
27+
line: 1,
28+
character: 0,
29+
},
30+
end: Position {
31+
line: 1,
32+
character: 9,
33+
},
34+
},
35+
},
36+
FixedContent {
37+
message: Some(
38+
"Disable no-debugger for this line",
39+
),
40+
code: "// oxlint-disable-next-line no-debugger\n",
41+
range: Range {
42+
start: Position {
43+
line: 1,
44+
character: 0,
45+
},
46+
end: Position {
47+
line: 1,
48+
character: 0,
49+
},
50+
},
51+
},
52+
FixedContent {
53+
message: Some(
54+
"Disable no-debugger for this file",
55+
),
56+
code: "// oxlint-disable no-debugger\n",
57+
range: Range {
58+
start: Position {
59+
line: 0,
60+
character: 0,
61+
},
62+
end: Position {
63+
line: 0,
64+
character: 0,
65+
},
66+
},
67+
},
68+
],
69+
)
70+
71+
872
code: "typescript-eslint(no-floating-promises)"
973
code_description.href: "None"
1074
message: "Promises must be awaited.\nhelp: The promise must end with a call to .catch, or end with a call to .then with a rejection handler, or be explicitly marked as ignored with the `void` operator."

0 commit comments

Comments
 (0)