Skip to content

Commit b61f8e8

Browse files
committed
refactor(language_server): share code for command oxc.fixAll and code action source.fixAll.oxc
1 parent 7bee177 commit b61f8e8

File tree

2 files changed

+31
-64
lines changed

2 files changed

+31
-64
lines changed

crates/oxc_language_server/src/code_actions.rs

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,32 @@ pub fn apply_all_fix_code_action<'a>(
8080
reports: impl Iterator<Item = &'a DiagnosticReport>,
8181
uri: &Uri,
8282
) -> Option<CodeAction> {
83-
let mut quick_fixes: Vec<TextEdit> = vec![];
83+
let quick_fixes: Vec<TextEdit> = fix_all_text_edit(reports);
84+
85+
if quick_fixes.is_empty() {
86+
return None;
87+
}
88+
89+
Some(CodeAction {
90+
title: "quick fix".to_string(),
91+
kind: Some(CODE_ACTION_KIND_SOURCE_FIX_ALL_OXC),
92+
is_preferred: Some(true),
93+
edit: Some(WorkspaceEdit {
94+
#[expect(clippy::disallowed_types)]
95+
changes: Some(std::collections::HashMap::from([(uri.clone(), quick_fixes)])),
96+
..WorkspaceEdit::default()
97+
}),
98+
disabled: None,
99+
data: None,
100+
diagnostics: None,
101+
command: None,
102+
})
103+
}
104+
105+
/// Collect all text edits from the provided diagnostic reports, which can be applied at once.
106+
/// This is useful for implementing a "fix all" code action / command that applies multiple fixes in one go.
107+
pub fn fix_all_text_edit<'a>(reports: impl Iterator<Item = &'a DiagnosticReport>) -> Vec<TextEdit> {
108+
let mut text_edits: Vec<TextEdit> = vec![];
84109

85110
for report in reports {
86111
let fix = match &report.fixed_content {
@@ -119,29 +144,12 @@ pub fn apply_all_fix_code_action<'a>(
119144
// and return them as one workspace edit.
120145
// it is possible that one fix will change the range for the next fix
121146
// see oxc-project/oxc#10422
122-
quick_fixes.push(TextEdit {
147+
text_edits.push(TextEdit {
123148
range: fixed_content.range,
124149
new_text: fixed_content.code.clone(),
125150
});
126151
}
127152
}
128153

129-
if quick_fixes.is_empty() {
130-
return None;
131-
}
132-
133-
Some(CodeAction {
134-
title: "quick fix".to_string(),
135-
kind: Some(CODE_ACTION_KIND_SOURCE_FIX_ALL_OXC),
136-
is_preferred: Some(true),
137-
edit: Some(WorkspaceEdit {
138-
#[expect(clippy::disallowed_types)]
139-
changes: Some(std::collections::HashMap::from([(uri.clone(), quick_fixes)])),
140-
..WorkspaceEdit::default()
141-
}),
142-
disabled: None,
143-
data: None,
144-
diagnostics: None,
145-
command: None,
146-
})
154+
text_edits
147155
}

crates/oxc_language_server/src/worker.rs

Lines changed: 3 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ use tower_lsp_server::{
1212

1313
use crate::{
1414
ConcurrentHashMap,
15-
code_actions::{apply_all_fix_code_action, apply_fix_code_actions},
15+
code_actions::{apply_all_fix_code_action, apply_fix_code_actions, fix_all_text_edit},
1616
formatter::server_formatter::ServerFormatter,
1717
linter::{
18-
error_with_position::{DiagnosticReport, PossibleFixContent},
18+
error_with_position::DiagnosticReport,
1919
server_linter::{ServerLinter, ServerLinterRun, normalize_path},
2020
},
2121
options::Options,
@@ -286,48 +286,7 @@ impl WorkspaceWorker {
286286
return vec![];
287287
}
288288

289-
let mut text_edits = vec![];
290-
291-
for report in value {
292-
let fix = match &report.fixed_content {
293-
PossibleFixContent::None => None,
294-
PossibleFixContent::Single(fixed_content) => Some(fixed_content),
295-
// For multiple fixes, we take the first one as a representative fix.
296-
// Applying all possible fixes at once is not possible in this context.
297-
PossibleFixContent::Multiple(multi) => {
298-
// for a real linter fix, we expect at least 3 fixes
299-
if multi.len() > 2 {
300-
multi.first()
301-
} else {
302-
debug!("Multiple fixes found, but only ignore fixes available");
303-
#[cfg(debug_assertions)]
304-
{
305-
if !multi.is_empty() {
306-
debug_assert!(multi[0].message.as_ref().is_some());
307-
debug_assert!(
308-
multi[0].message.as_ref().unwrap().starts_with("Disable")
309-
);
310-
debug_assert!(
311-
multi[0].message.as_ref().unwrap().ends_with("for this line")
312-
);
313-
}
314-
}
315-
// this fix is only for "ignore this line/file" fixes
316-
// do not apply them for "fix all" code action
317-
None
318-
}
319-
}
320-
};
321-
322-
if let Some(fixed_content) = &fix {
323-
text_edits.push(TextEdit {
324-
range: fixed_content.range,
325-
new_text: fixed_content.code.clone(),
326-
});
327-
}
328-
}
329-
330-
text_edits
289+
fix_all_text_edit(value.iter())
331290
}
332291

333292
/// Handle file changes that are watched by the client

0 commit comments

Comments
 (0)