Skip to content

Commit 605bc7a

Browse files
authored
feat: accept notifications for subdirectory deno.json files (#1034)
1 parent 081a549 commit 605bc7a

File tree

6 files changed

+100
-47
lines changed

6 files changed

+100
-47
lines changed

client/src/commands.ts

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import * as tasks from "./tasks";
1414
import { DenoTestController, TestingFeature } from "./testing";
1515
import type {
1616
DenoExtensionContext,
17+
DidChangeDenoConfigurationParams,
1718
DidUpgradeCheckParams,
1819
TestCommandOptions,
1920
} from "./types";
@@ -32,7 +33,7 @@ import * as semver from "semver";
3233
import * as vscode from "vscode";
3334
import { LanguageClient, ServerOptions } from "vscode-languageclient/node";
3435
import type { Location, Position } from "vscode-languageclient/node";
35-
import { getWorkspacesEnabledInfo } from "./enable";
36+
import { getWorkspacesEnabledInfo, setupCheckConfig } from "./enable";
3637
import { denoUpgradePromptAndExecute } from "./upgrade";
3738

3839
// deno-lint-ignore no-explicit-any
@@ -82,13 +83,15 @@ export function startLanguageServer(
8283
if (extensionContext.client) {
8384
const client = extensionContext.client;
8485
extensionContext.client = undefined;
85-
extensionContext.testController?.dispose();
86-
extensionContext.testController = undefined;
86+
for (const disposable of extensionContext.clientSubscriptions ?? []) {
87+
disposable.dispose();
88+
}
8789
extensionContext.statusBar.refresh(extensionContext);
8890
vscode.commands.executeCommand("setContext", ENABLEMENT_FLAG, false);
8991
const timeoutMs = 10_000;
9092
await client.stop(timeoutMs);
9193
}
94+
extensionContext.clientSubscriptions = [];
9295

9396
// Start a new language server
9497
const command = await getDenoCommandPath();
@@ -151,34 +154,76 @@ export function startLanguageServer(
151154
);
152155
extensionContext.serverCapabilities = client.initializeResult?.capabilities;
153156
extensionContext.statusBar.refresh(extensionContext);
154-
context.subscriptions.push(extensionContext.client.onNotification(
155-
"deno/didChangeDenoConfiguration",
156-
() => {
157-
extensionContext.tasksSidebar.refresh();
158-
},
159-
));
160-
context.subscriptions.push(extensionContext.client.onNotification(
161-
"deno/didUpgradeCheck",
162-
(params: DidUpgradeCheckParams) => {
163-
if (extensionContext.serverInfo) {
164-
extensionContext.serverInfo.upgradeAvailable =
165-
params.upgradeAvailable;
166-
extensionContext.statusBar.refresh(extensionContext);
167-
}
168-
},
169-
));
157+
158+
extensionContext.clientSubscriptions.push(
159+
extensionContext.client.onNotification(
160+
"deno/didUpgradeCheck",
161+
(params: DidUpgradeCheckParams) => {
162+
if (extensionContext.serverInfo) {
163+
extensionContext.serverInfo.upgradeAvailable =
164+
params.upgradeAvailable;
165+
extensionContext.statusBar.refresh(extensionContext);
166+
}
167+
},
168+
),
169+
);
170170

171171
if (testingFeature.enabled) {
172-
context.subscriptions.push(new DenoTestController(extensionContext));
172+
extensionContext.clientSubscriptions.push(
173+
new DenoTestController(extensionContext.client),
174+
);
173175
}
174176

175-
context.subscriptions.push(
177+
extensionContext.clientSubscriptions.push(
176178
client.onNotification(
177179
registryState,
178180
createRegistryStateHandler(),
179181
),
180182
);
181183

184+
// TODO(nayeemrmn): LSP version < 1.40.0 don't support the required API for
185+
// "deno/didChangeDenoConfiguration". Remove this eventually.
186+
if (semver.lt(extensionContext.serverInfo.version, "1.40.0")) {
187+
extensionContext.scopesWithDenoJson = new Set();
188+
extensionContext.clientSubscriptions.push(
189+
extensionContext.client.onNotification(
190+
"deno/didChangeDenoConfiguration",
191+
() => {
192+
extensionContext.tasksSidebar.refresh();
193+
},
194+
),
195+
);
196+
extensionContext.clientSubscriptions.push(
197+
await setupCheckConfig(extensionContext),
198+
);
199+
} else {
200+
const scopesWithDenoJson = new Set<string>();
201+
extensionContext.scopesWithDenoJson = scopesWithDenoJson;
202+
extensionContext.clientSubscriptions.push(
203+
extensionContext.client.onNotification(
204+
"deno/didChangeDenoConfiguration",
205+
({ changes }: DidChangeDenoConfigurationParams) => {
206+
let changedScopes = false;
207+
for (const change of changes) {
208+
if (change.type == "added") {
209+
const scopePath = vscode.Uri.parse(change.scopeUri).fsPath;
210+
scopesWithDenoJson.add(scopePath);
211+
changedScopes = true;
212+
} else if (change.type == "removed") {
213+
const scopePath = vscode.Uri.parse(change.scopeUri).fsPath;
214+
scopesWithDenoJson.delete(scopePath);
215+
changedScopes = true;
216+
}
217+
}
218+
if (changedScopes) {
219+
extensionContext.tsApi?.refresh();
220+
}
221+
extensionContext.tasksSidebar.refresh();
222+
},
223+
),
224+
);
225+
}
226+
182227
extensionContext.tsApi.refresh();
183228

184229
if (

client/src/enable.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,12 @@ export async function setupCheckConfig(
9494
if (!uri) {
9595
return;
9696
}
97-
extensionContext.scopesWithDenoJson = [];
97+
extensionContext.scopesWithDenoJson = new Set();
9898
if (
9999
await exists(vscode.Uri.joinPath(uri, "./deno.json")) ||
100100
await exists(vscode.Uri.joinPath(uri, "./deno.jsonc"))
101101
) {
102-
extensionContext.scopesWithDenoJson.push(uri.fsPath);
102+
extensionContext.scopesWithDenoJson.add(uri.fsPath);
103103
}
104104
extensionContext.tsApi?.refresh();
105105
}

client/src/extension.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
} from "./constants";
99
import { DenoTextDocumentContentProvider, SCHEME } from "./content_provider";
1010
import { DenoDebugConfigurationProvider } from "./debug_config_provider";
11-
import { refreshEnableSettings, setupCheckConfig } from "./enable";
11+
import { refreshEnableSettings } from "./enable";
1212
import { DenoStatusBar } from "./status_bar";
1313
import { activateTaskProvider } from "./tasks";
1414
import { getTsApi } from "./ts_api";
@@ -175,18 +175,18 @@ export async function activate(
175175
// Activate the task provider.
176176
context.subscriptions.push(activateTaskProvider(extensionContext));
177177

178-
extensionContext.tsApi = getTsApi(() => ({
179-
enableSettingsUnscoped: extensionContext.enableSettingsUnscoped,
180-
enableSettingsByFolder: extensionContext.enableSettingsByFolder,
181-
scopesWithDenoJson: extensionContext.scopesWithDenoJson,
182-
}));
178+
extensionContext.tsApi = getTsApi(() => {
179+
return {
180+
enableSettingsUnscoped: extensionContext.enableSettingsUnscoped,
181+
enableSettingsByFolder: extensionContext.enableSettingsByFolder,
182+
scopesWithDenoJson: Array.from(extensionContext.scopesWithDenoJson ?? []),
183+
};
184+
});
183185

184186
extensionContext.maxTsServerMemory =
185187
vscode.workspace.getConfiguration(EXTENSION_NS).get("maxTsServerMemory") ??
186188
null;
187189
refreshEnableSettings(extensionContext);
188-
extensionContext.scopesWithDenoJson = [];
189-
context.subscriptions.push(await setupCheckConfig(extensionContext));
190190

191191
extensionContext.tasksSidebar = registerSidebar(
192192
extensionContext,
@@ -235,6 +235,10 @@ export function deactivate(): Thenable<void> | undefined {
235235

236236
const client = extensionContext.client;
237237
extensionContext.client = undefined;
238+
for (const disposable of extensionContext.clientSubscriptions ?? []) {
239+
disposable.dispose();
240+
}
241+
extensionContext.clientSubscriptions = undefined;
238242
extensionContext.statusBar.refresh(extensionContext);
239243
vscode.commands.executeCommand("setContext", ENABLEMENT_FLAG, false);
240244
return client.stop();

client/src/status_bar.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export class DenoStatusBar {
3131
// show only when "enable" is true and language server started
3232
if (
3333
extensionContext.client && extensionContext.serverInfo &&
34-
(extensionContext.scopesWithDenoJson.length != 0 ||
34+
(extensionContext.scopesWithDenoJson?.size ||
3535
extensionContext.enableSettingsUnscoped.enable ||
3636
extensionContext.enableSettingsUnscoped.enablePaths?.length ||
3737
extensionContext.enableSettingsByFolder.find(([_, s]) =>

client/src/testing.ts

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,13 @@ import {
1111
testRunCancel,
1212
testRunProgress,
1313
} from "./lsp_extensions";
14-
import type { DenoExtensionContext } from "./types";
15-
import { assert } from "./util";
1614

1715
import * as vscode from "vscode";
1816
import { FeatureState, MarkupKind } from "vscode-languageclient/node";
1917
import type {
2018
ClientCapabilities,
2119
DocumentSelector,
20+
LanguageClient,
2221
MarkupContent,
2322
ServerCapabilities,
2423
StaticFeature,
@@ -136,19 +135,13 @@ export class DenoTestController implements vscode.Disposable {
136135
#runCount = 0;
137136
#runs = new Map<number, vscode.TestRun>();
138137
#subscriptions: vscode.Disposable[] = [];
139-
#testController: vscode.TestController;
140138

141-
constructor(extensionContext: DenoExtensionContext) {
142-
const testController = extensionContext.testController =
143-
this
144-
.#testController =
145-
vscode.tests
146-
.createTestController("denoTestController", "Deno");
139+
constructor(client: LanguageClient) {
140+
const testController = vscode.tests.createTestController(
141+
"denoTestController",
142+
"Deno",
143+
);
147144
this.#subscriptions.push(testController);
148-
149-
const { client } = extensionContext;
150-
assert(client);
151-
152145
const runHandler = async (
153146
request: vscode.TestRunRequest,
154147
cancellation: vscode.CancellationToken,

client/src/types.d.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,21 @@ interface DenoExperimental {
2727

2828
export interface DenoExtensionContext {
2929
client: LanguageClient | undefined;
30+
clientSubscriptions: { dispose(): unknown }[] | undefined;
3031
clientOptions: LanguageClientOptions;
3132
serverInfo: DenoServerInfo | undefined;
3233
/** The capabilities returned from the server. */
3334
serverCapabilities:
3435
| ServerCapabilities<DenoExperimental>
3536
| undefined;
37+
scopesWithDenoJson: Set<string> | undefined;
3638
statusBar: DenoStatusBar;
37-
testController: vscode.TestController | undefined;
3839
tsApi: TsApi;
3940
outputChannel: vscode.OutputChannel;
4041
tasksSidebar: DenoTasksTreeDataProvider;
4142
maxTsServerMemory: number | null;
4243
enableSettingsUnscoped: EnableSettings;
4344
enableSettingsByFolder: [string, EnableSettings][];
44-
scopesWithDenoJson: string[];
4545
}
4646

4747
export interface TestCommandOptions {
@@ -56,3 +56,14 @@ export interface UpgradeAvailable {
5656
export interface DidUpgradeCheckParams {
5757
upgradeAvailable: UpgradeAvailable | null;
5858
}
59+
60+
export interface DenoConfigurationChangeEvent {
61+
scopeUri: string;
62+
fileUri: string;
63+
type: "added" | "changed" | "removed";
64+
configurationType: "denoJson" | "packageJson";
65+
}
66+
67+
export interface DidChangeDenoConfigurationParams {
68+
changes: DenoConfigurationChangeEvent[];
69+
}

0 commit comments

Comments
 (0)