Skip to content

Commit 27c0255

Browse files
committed
refactor(language_server): use LintRunner
1 parent ecd3fb5 commit 27c0255

File tree

9 files changed

+172
-399
lines changed

9 files changed

+172
-399
lines changed

crates/oxc_language_server/src/linter/isolated_lint_handler.rs

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ use tower_lsp_server::{UriExt, lsp_types::Uri};
1010

1111
use oxc_allocator::Allocator;
1212
use oxc_linter::{
13-
AllowWarnDeny, ConfigStore, DirectivesStore, DisableDirectives, Fix, LINTABLE_EXTENSIONS,
14-
LintOptions, LintService, LintServiceOptions, Linter, Message, PossibleFixes, RuleCommentType,
15-
RuntimeFileSystem, read_to_arena_str, read_to_string,
13+
AllowWarnDeny, ConfigStore, DisableDirectives, Fix, FixKind, LINTABLE_EXTENSIONS, LintOptions,
14+
LintRunner, LintRunnerBuilder, LintServiceOptions, Linter, Message, PossibleFixes,
15+
RuleCommentType, RuntimeFileSystem, read_to_arena_str, read_to_string,
1616
};
1717

1818
use super::error_with_position::{
@@ -23,13 +23,14 @@ use super::error_with_position::{
2323
#[derive(Debug, Clone)]
2424
pub struct IsolatedLintHandlerOptions {
2525
pub use_cross_module: bool,
26+
pub type_aware: bool,
27+
pub fix_kind: FixKind,
2628
pub root_path: PathBuf,
2729
pub tsconfig_path: Option<PathBuf>,
2830
}
2931

3032
pub struct IsolatedLintHandler {
31-
service: LintService,
32-
directives_coordinator: DirectivesStore,
33+
runner: LintRunner,
3334
unused_directives_severity: Option<AllowWarnDeny>,
3435
}
3536

@@ -68,8 +69,6 @@ impl IsolatedLintHandler {
6869
config_store: ConfigStore,
6970
options: &IsolatedLintHandlerOptions,
7071
) -> Self {
71-
let directives_coordinator = DirectivesStore::new();
72-
7372
let linter = Linter::new(lint_options, config_store, None);
7473
let mut lint_service_options = LintServiceOptions::new(options.root_path.clone())
7574
.with_cross_module(options.use_cross_module);
@@ -81,12 +80,12 @@ impl IsolatedLintHandler {
8180
lint_service_options = lint_service_options.with_tsconfig(tsconfig_path);
8281
}
8382

84-
let mut service = LintService::new(linter, lint_service_options);
85-
service.set_disable_directives_map(directives_coordinator.map());
86-
8783
Self {
88-
service,
89-
directives_coordinator,
84+
runner: LintRunnerBuilder::new(lint_service_options, linter)
85+
.with_type_aware(options.type_aware)
86+
.with_fix_kind(options.fix_kind)
87+
.build()
88+
.unwrap(),
9089
unused_directives_severity: lint_options.report_unused_directive,
9190
}
9291
}
@@ -113,21 +112,21 @@ impl IsolatedLintHandler {
113112
debug!("lint {}", path.display());
114113
let rope = &Rope::from_str(source_text);
115114

115+
let fs = Box::new(IsolatedLintHandlerFileSystem::new(
116+
path.to_path_buf(),
117+
Arc::from(source_text),
118+
));
119+
116120
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()
121+
.runner
122+
.run_source(&Arc::from(path.as_os_str()), source_text.to_string(), fs)
124123
.iter()
125124
.map(|message| message_to_lsp_diagnostic(message, uri, source_text, rope))
126125
.collect();
127126

128127
// Add unused directives if configured
129128
if let Some(severity) = self.unused_directives_severity
130-
&& let Some(directives) = self.directives_coordinator.get(path)
129+
&& let Some(directives) = self.runner.directives_coordinator().get(path)
131130
{
132131
messages.extend(
133132
create_unused_directives_messages(&directives, severity, source_text)

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: 41 additions & 79 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};
@@ -33,9 +32,19 @@ pub enum ServerLinterRun {
3332
Always,
3433
}
3534

35+
impl ServerLinterRun {
36+
fn matches(&self, run: Run) -> bool {
37+
matches!(
38+
(self, run),
39+
(ServerLinterRun::OnType, Run::OnType)
40+
| (ServerLinterRun::OnSave, Run::OnSave)
41+
| (ServerLinterRun::Always, _)
42+
)
43+
}
44+
}
45+
3646
pub struct ServerLinter {
3747
isolated_linter: Arc<Mutex<IsolatedLintHandler>>,
38-
tsgo_linter: Arc<Option<TsgoLinter>>,
3948
ignore_matcher: LintIgnoreMatcher,
4049
gitignore_glob: Vec<Gitignore>,
4150
lint_on_run: Run,
@@ -46,7 +55,6 @@ pub struct ServerLinter {
4655
#[derive(Debug, Default)]
4756
struct ServerLinterDiagnostics {
4857
isolated_linter: Arc<ConcurrentHashMap<String, Option<Vec<DiagnosticReport>>>>,
49-
tsgo_linter: Arc<ConcurrentHashMap<String, Option<Vec<DiagnosticReport>>>>,
5058
}
5159

5260
impl ServerLinterDiagnostics {
@@ -59,29 +67,15 @@ impl ServerLinterDiagnostics {
5967
reports.extend(diagnostics.clone());
6068
}
6169
}
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-
}
6870
if found { Some(reports) } else { None }
6971
}
7072

7173
pub fn remove_diagnostics(&self, path: &str) {
7274
self.isolated_linter.pin().remove(path);
73-
self.tsgo_linter.pin().remove(path);
7475
}
7576

7677
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
78+
self.isolated_linter.pin().keys().cloned().collect::<Vec<_>>()
8579
}
8680
}
8781

@@ -156,9 +150,11 @@ impl ServerLinter {
156150

157151
let isolated_linter = IsolatedLintHandler::new(
158152
lint_options,
159-
config_store.clone(), // clone because tsgo linter needs it
153+
config_store,
160154
&IsolatedLintHandlerOptions {
161155
use_cross_module,
156+
type_aware: options.type_aware,
157+
fix_kind: FixKind::from(options.fix_kind.clone()),
162158
root_path: root_path.to_path_buf(),
163159
tsconfig_path: options.ts_config_path.as_ref().map(|path| {
164160
let path = Path::new(path).to_path_buf();
@@ -178,11 +174,6 @@ impl ServerLinter {
178174
extended_paths,
179175
lint_on_run: options.run,
180176
diagnostics: ServerLinterDiagnostics::default(),
181-
tsgo_linter: if options.type_aware {
182-
Arc::new(Some(TsgoLinter::new(&root_path, config_store, fix_kind)))
183-
} else {
184-
Arc::new(None)
185-
},
186177
}
187178
}
188179

@@ -329,47 +320,25 @@ impl ServerLinter {
329320
content: Option<String>,
330321
run_type: ServerLinterRun,
331322
) -> Option<Vec<DiagnosticReport>> {
332-
let (oxlint, tsgolint) = match (run_type, self.lint_on_run) {
333-
// run everything on save, or when it is forced
334-
(ServerLinterRun::Always, _) | (ServerLinterRun::OnSave, Run::OnSave) => (true, true),
335-
// run only oxlint on type
336-
// tsgolint does not support memory source_text
337-
(ServerLinterRun::OnType, Run::OnType) => (true, false),
338-
// it does not match, run nothing
339-
(ServerLinterRun::OnType, Run::OnSave) => (false, false),
340-
// In onType mode, only TypeScript type checking runs on save
341-
// If type_aware is disabled (tsgo_linter is None), skip everything to preserve diagnostics
342-
(ServerLinterRun::OnSave, Run::OnType) => {
343-
let should_run_tsgo = self.tsgo_linter.as_ref().is_some();
344-
(false, should_run_tsgo)
345-
}
346-
};
323+
let run = matches!(run_type, ServerLinterRun::Always) || run_type.matches(self.lint_on_run);
347324

348325
// return `None` when both tools do not want to be used
349-
if !oxlint && !tsgolint {
326+
if !run {
350327
return None;
351328
}
352329

353330
if self.is_ignored(uri) {
354331
return None;
355332
}
356333

357-
if oxlint {
358-
let diagnostics = {
359-
let mut isolated_linter = self.isolated_linter.lock().await;
360-
isolated_linter.run_single(uri, content.clone())
361-
};
362-
self.diagnostics.isolated_linter.pin().insert(uri.to_string(), diagnostics);
363-
}
334+
let diagnostics = {
335+
let mut isolated_linter = self.isolated_linter.lock().await;
336+
isolated_linter.run_single(uri, content.clone())
337+
};
364338

365-
if tsgolint && let Some(tsgo_linter) = self.tsgo_linter.as_ref() {
366-
self.diagnostics
367-
.tsgo_linter
368-
.pin()
369-
.insert(uri.to_string(), tsgo_linter.lint_file(uri, content.clone()));
370-
}
339+
self.diagnostics.isolated_linter.pin().insert(uri.to_string(), diagnostics.clone());
371340

372-
self.diagnostics.get_diagnostics(&uri.to_string())
341+
diagnostics
373342
}
374343

375344
pub fn needs_restart(old_options: &LSPLintOptions, new_options: &LSPLintOptions) -> bool {
@@ -465,31 +434,23 @@ mod test {
465434
fn test_get_diagnostics_found_and_none_entries() {
466435
let key = "file:///test.js".to_string();
467436

468-
// Case 1: Both entries present, Some diagnostics
437+
// Case 1: Entry present, Some diagnostics
469438
let diag = DiagnosticReport::default();
470439
let diag_map = ConcurrentHashMap::default();
471-
diag_map.pin().insert(key.clone(), Some(vec![diag.clone()]));
472-
let tsgo_map = ConcurrentHashMap::default();
473-
tsgo_map.pin().insert(key.clone(), Some(vec![diag]));
440+
diag_map.pin().insert(key.clone(), Some(vec![diag]));
474441

475-
let server_diag = super::ServerLinterDiagnostics {
476-
isolated_linter: std::sync::Arc::new(diag_map),
477-
tsgo_linter: std::sync::Arc::new(tsgo_map),
478-
};
442+
let server_diag =
443+
super::ServerLinterDiagnostics { isolated_linter: std::sync::Arc::new(diag_map) };
479444
let result = server_diag.get_diagnostics(&key);
480445
assert!(result.is_some());
481-
assert_eq!(result.unwrap().len(), 2);
446+
assert_eq!(result.unwrap().len(), 1);
482447

483448
// Case 2: Entry present, but value is None
484449
let diag_map_none = ConcurrentHashMap::default();
485450
diag_map_none.pin().insert(key.clone(), None);
486-
let tsgo_map_none = ConcurrentHashMap::default();
487-
tsgo_map_none.pin().insert(key.clone(), None);
488451

489-
let server_diag_none = ServerLinterDiagnostics {
490-
isolated_linter: std::sync::Arc::new(diag_map_none),
491-
tsgo_linter: std::sync::Arc::new(tsgo_map_none),
492-
};
452+
let server_diag_none =
453+
ServerLinterDiagnostics { isolated_linter: std::sync::Arc::new(diag_map_none) };
493454
let result_none = server_diag_none.get_diagnostics(&key);
494455
assert!(result_none.is_some());
495456
assert_eq!(result_none.unwrap().len(), 0);
@@ -505,14 +466,19 @@ mod test {
505466
fn test_lint_on_run_on_type_on_type() {
506467
Tester::new(
507468
"fixtures/linter/lint_on_run/on_type",
508-
Some(LintOptions { type_aware: true, run: Run::OnType, ..Default::default() }),
469+
Some(LintOptions {
470+
type_aware: true,
471+
run: Run::OnType,
472+
fix_kind: LintFixKindFlag::All,
473+
..Default::default()
474+
}),
509475
)
510476
.test_and_snapshot_single_file_with_run_type("on-type.ts", Run::OnType);
511477
}
512478

513479
#[test]
514480
#[cfg(not(target_endian = "big"))]
515-
fn test_lint_on_run_on_type_on_save() {
481+
fn test_lint_on_run_on_save_on_save() {
516482
Tester::new(
517483
"fixtures/linter/lint_on_run/on_save",
518484
Some(LintOptions {
@@ -537,12 +503,12 @@ mod test {
537503

538504
#[test]
539505
#[cfg(not(target_endian = "big"))]
540-
fn test_lint_on_run_on_save_on_save() {
506+
fn test_lint_on_run_on_type_on_save() {
541507
Tester::new(
542-
"fixtures/linter/lint_on_run/on_type",
508+
"fixtures/linter/lint_on_run/on_save",
543509
Some(LintOptions {
544510
type_aware: true,
545-
run: Run::OnSave,
511+
run: Run::OnType,
546512
fix_kind: LintFixKindFlag::All,
547513
..Default::default()
548514
}),
@@ -676,7 +642,6 @@ mod test {
676642
"fixtures/linter/tsgolint",
677643
Some(LintOptions {
678644
type_aware: true,
679-
run: Run::OnSave,
680645
fix_kind: LintFixKindFlag::All,
681646
..Default::default()
682647
}),
@@ -686,10 +651,7 @@ mod test {
686651

687652
#[test]
688653
fn test_ignore_js_plugins() {
689-
let tester = Tester::new(
690-
"fixtures/linter/js_plugins",
691-
Some(LintOptions { run: Run::OnSave, ..Default::default() }),
692-
);
654+
let tester = Tester::new("fixtures/linter/js_plugins", Some(LintOptions::default()));
693655
tester.test_and_snapshot_single_file("index.js");
694656
}
695657

0 commit comments

Comments
 (0)