Skip to content

Commit 3bd339b

Browse files
committed
feat(language_server): provide commands / code actions for unopened files (#10815)
Related oxc-project/oxc-intellij-plugin#177 When the client request code actions or commands for a file. This file must not be open to be requested. In the main, we are just looking into our `diagnostics_map`, which will be filled only on open/change/close of the text document. When the client request now a code action or command for a file, we will lint them only (if not already done) and avoid filling the `diagnostic_map`.
1 parent b2c287f commit 3bd339b

File tree

2 files changed

+39
-22
lines changed

2 files changed

+39
-22
lines changed

crates/oxc_language_server/src/worker.rs

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -100,30 +100,33 @@ impl WorkspaceWorker {
100100
uri: &Uri,
101101
content: Option<String>,
102102
) -> Option<Vec<DiagnosticReport>> {
103-
if self.is_ignored(uri).await {
104-
return None;
103+
let diagnostics = self.lint_file_internal(uri, content).await;
104+
105+
if let Some(diagnostics) = &diagnostics {
106+
self.update_diagnostics(uri, diagnostics).await;
105107
}
106108

107-
Some(self.update_diagnostics(uri, content).await)
109+
diagnostics
108110
}
109111

110-
async fn update_diagnostics(
112+
async fn lint_file_internal(
111113
&self,
112114
uri: &Uri,
113115
content: Option<String>,
114-
) -> Vec<DiagnosticReport> {
115-
let diagnostics = self.server_linter.read().await.run_single(uri, content);
116-
if let Some(diagnostics) = diagnostics {
117-
self.diagnostics_report_map
118-
.read()
119-
.await
120-
.pin()
121-
.insert(uri.to_string(), diagnostics.clone());
122-
123-
return diagnostics;
116+
) -> Option<Vec<DiagnosticReport>> {
117+
if self.is_ignored(uri).await {
118+
return None;
124119
}
125120

126-
vec![]
121+
self.server_linter.read().await.run_single(uri, content)
122+
}
123+
124+
async fn update_diagnostics(&self, uri: &Uri, diagnostics: &[DiagnosticReport]) {
125+
self.diagnostics_report_map
126+
.read()
127+
.await
128+
.pin()
129+
.insert(uri.to_string(), diagnostics.to_owned());
127130
}
128131

129132
async fn revalidate_diagnostics(&self) -> ConcurrentHashMap<String, Vec<DiagnosticReport>> {
@@ -160,11 +163,18 @@ impl WorkspaceWorker {
160163
is_source_fix_all_oxc: bool,
161164
) -> Vec<CodeActionOrCommand> {
162165
let report_map = self.diagnostics_report_map.read().await;
163-
let report_map_ref = report_map.pin();
164-
let Some(value) = report_map_ref.get(&uri.to_string()) else {
165-
return vec![];
166+
let report_map_ref = report_map.pin_owned();
167+
let value = match report_map_ref.get(&uri.to_string()) {
168+
Some(value) => value,
169+
// code actions / commands can be requested without opening the file
170+
// we just internally lint and provide the code actions / commands without refreshing the diagnostic map.
171+
None => &self.lint_file_internal(uri, None).await.unwrap_or_default(),
166172
};
167173

174+
if value.is_empty() {
175+
return vec![];
176+
}
177+
168178
let reports = value
169179
.iter()
170180
.filter(|r| r.diagnostic.range == *range || range_overlaps(*range, r.diagnostic.range));
@@ -194,11 +204,18 @@ impl WorkspaceWorker {
194204

195205
pub async fn get_diagnostic_text_edits(&self, uri: &Uri) -> Vec<TextEdit> {
196206
let report_map = self.diagnostics_report_map.read().await;
197-
let report_map_ref = report_map.pin();
198-
let Some(value) = report_map_ref.get(&uri.to_string()) else {
199-
return vec![];
207+
let report_map_ref = report_map.pin_owned();
208+
let value = match report_map_ref.get(&uri.to_string()) {
209+
Some(value) => value,
210+
// code actions / commands can be requested without opening the file
211+
// we just internally lint and provide the code actions / commands without refreshing the diagnostic map.
212+
None => &self.lint_file_internal(uri, None).await.unwrap_or_default(),
200213
};
201214

215+
if value.is_empty() {
216+
return vec![];
217+
}
218+
202219
let mut text_edits = vec![];
203220

204221
for report in value {

editors/vscode/client/extension.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ suite('code actions', () => {
9999
});
100100

101101
await workspace.applyEdit(edit);
102-
await window.showTextDocument(fileUri);
102+
// await window.showTextDocument(fileUri); -- should also work without opening the file
103103

104104
const codeActions: ProviderResult<Array<CodeAction>> = await commands.executeCommand(
105105
'vscode.executeCodeActionProvider',

0 commit comments

Comments
 (0)