Skip to content

Commit

Permalink
feat: support Deno enabling specified workspace paths (#635)
Browse files Browse the repository at this point in the history
Closes: #633
  • Loading branch information
kitsonk committed Mar 21, 2022
1 parent 8ed6a99 commit f0a4cb5
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 44 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
],
"preLaunchTask": {
"type": "npm",
"script": "watch"
"script": "esbuild"
}
}
]
Expand Down
1 change: 1 addition & 0 deletions client/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.

export const ENABLE_PATHS = "enablePaths";
export const ENABLEMENT_FLAG = "deno:lspReady";
export const EXTENSION_ID = "denoland.vscode-deno";
export const EXTENSION_NS = "deno";
Expand Down
54 changes: 47 additions & 7 deletions client/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.

import * as commands from "./commands";
import { ENABLEMENT_FLAG, EXTENSION_NS } from "./constants";
import { ENABLE_PATHS, ENABLEMENT_FLAG, EXTENSION_NS } from "./constants";
import { DenoTextDocumentContentProvider, SCHEME } from "./content_provider";
import { DenoDebugConfigurationProvider } from "./debug_config_provider";
import type { EnabledPaths } from "./shared_types";
import { DenoStatusBar } from "./status_bar";
import { activateTaskProvider } from "./tasks";
import { getTsApi } from "./ts_api";
import type { DenoExtensionContext, Settings } from "./types";
import { assert } from "./util";

import * as path from "path";
import * as vscode from "vscode";

/** The language IDs we care about. */
Expand All @@ -28,6 +28,7 @@ const workspaceSettingsKeys: Array<keyof Settings> = [
"codeLens",
"config",
"enable",
"enablePaths",
"importMap",
"internalDebug",
"lint",
Expand All @@ -41,8 +42,9 @@ const workspaceSettingsKeys: Array<keyof Settings> = [
/** These are keys of settings that can apply to an individual resource, like
* a file or folder. */
const resourceSettingsKeys: Array<keyof Settings> = [
"enable",
"codeLens",
"enable",
"enablePaths",
];

/** Convert a workspace configuration to `Settings` for a workspace. */
Expand All @@ -53,9 +55,6 @@ function configToWorkspaceSettings(
for (const key of workspaceSettingsKeys) {
workspaceSettings[key] = config.get(key);
}
for (const key of resourceSettingsKeys) {
workspaceSettings[key] = config.get(key);
}
return workspaceSettings;
}

Expand All @@ -76,6 +75,31 @@ function configToResourceSettings(
return resourceSettings;
}

function getEnabledPaths(): EnabledPaths[] {
const items = [] as EnabledPaths[];
if (!vscode.workspace.workspaceFolders) {
return items;
}
for (const workspaceFolder of vscode.workspace.workspaceFolders) {
const config = vscode.workspace.getConfiguration(
EXTENSION_NS,
workspaceFolder,
);
const enabledPaths = config.get<string[]>(ENABLE_PATHS);
if (!enabledPaths || !enabledPaths.length) {
continue;
}
const paths = enabledPaths.map((folder) =>
vscode.Uri.joinPath(workspaceFolder.uri, folder).fsPath
);
items.push({
workspace: workspaceFolder.uri.fsPath,
paths,
});
}
return items;
}

function getWorkspaceSettings(): Settings {
const config = vscode.workspace.getConfiguration(EXTENSION_NS);
return configToWorkspaceSettings(config);
Expand Down Expand Up @@ -103,6 +127,7 @@ function handleConfigurationChange(event: vscode.ConfigurationChangeEvent) {
),
};
}
extensionContext.enabledPaths = getEnabledPaths();
extensionContext.tsApi.refresh();
extensionContext.statusBar.refresh(extensionContext);

Expand All @@ -113,14 +138,19 @@ function handleConfigurationChange(event: vscode.ConfigurationChangeEvent) {
}
}

function handleChangeWorkspaceFolders() {
extensionContext.enabledPaths = getEnabledPaths();
extensionContext.tsApi.refresh();
}

function handleDocumentOpen(...documents: vscode.TextDocument[]) {
let didChange = false;
for (const doc of documents) {
if (!LANGUAGES.includes(doc.languageId)) {
continue;
}
const { languageId, uri } = doc;
extensionContext.documentSettings[path.normalize(doc.uri.fsPath)] = {
extensionContext.documentSettings[doc.uri.fsPath] = {
scope: { languageId, uri },
settings: configToResourceSettings(
vscode.workspace.getConfiguration(EXTENSION_NS, { languageId, uri }),
Expand Down Expand Up @@ -158,6 +188,14 @@ export async function activate(
initializationOptions: getWorkspaceSettings(),
};

// When a workspace folder is opened, the updates or changes to the workspace
// folders need to be sent to the TypeScript language service plugin
vscode.workspace.onDidChangeWorkspaceFolders(
handleChangeWorkspaceFolders,
extensionContext,
context.subscriptions,
);

// When a document opens, the language server will query the client to
// determine the specific configuration of a resource, we need to ensure the
// the builtin TypeScript language service has the same "view" of the world,
Expand Down Expand Up @@ -211,10 +249,12 @@ export async function activate(

extensionContext.tsApi = getTsApi(() => ({
documents: extensionContext.documentSettings,
enabledPaths: extensionContext.enabledPaths,
workspace: extensionContext.workspaceSettings,
}));

extensionContext.documentSettings = {};
extensionContext.enabledPaths = getEnabledPaths();
extensionContext.workspaceSettings = getWorkspaceSettings();

// when we activate, it might have been because a document was opened that
Expand Down
13 changes: 12 additions & 1 deletion client/src/shared_types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export interface Settings {
config: string | null;
/** Is the extension enabled or not. */
enable: boolean;
/** If set, indicates that only the paths in the workspace should be Deno
* enabled. */
enablePaths: string[];
/** A path to an import map that should be applied. */
importMap: string | null;
/** A flag that enables additional internal debug information to be printed
Expand All @@ -55,9 +58,17 @@ export interface Settings {
unstable: boolean;
}

export interface EnabledPaths {
/** The file system path of the workspace folder that is partially enabled. */
workspace: string;
/** The file system paths that are Deno enabled. */
paths: string[];
}

export interface PluginSettings {
workspace: Settings;
documents: Record<string, DocumentSettings>;
enabledPaths: EnabledPaths[];
workspace: Settings;
}

export interface DocumentSettings {
Expand Down
3 changes: 2 additions & 1 deletion client/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {
LanguageClientOptions,
} from "vscode-languageclient/node";
import type { DenoServerInfo } from "./server_info";
import type { DocumentSettings, Settings } from "./shared_types";
import type { DocumentSettings, EnabledPaths, Settings } from "./shared_types";
import type { DenoStatusBar } from "./status_bar";

export * from "./shared_types";
Expand All @@ -20,6 +20,7 @@ export interface DenoExtensionContext {
clientOptions: LanguageClientOptions;
/** A record of filepaths and their document settings. */
documentSettings: Record<string, DocumentSettings>;
enabledPaths: EnabledPaths[];
serverInfo: DenoServerInfo | undefined;
statusBar: DenoStatusBar;
tsApi: TsApi;
Expand Down
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 15 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,27 @@
"deno.enable": {
"type": "boolean",
"default": false,
"markdownDescription": "Controls if the Deno Language Server is enabled. When enabled, the extension will disable the built-in VSCode JavaScript and TypeScript language services, and will use the Deno Language Server (`deno lsp`) instead.\n\n**Not recommended to be enabled globally.**",
"markdownDescription": "Controls if the Deno Language Server is enabled. When enabled, the extension will disable the built-in VSCode JavaScript and TypeScript language services, and will use the Deno Language Server instead.\n\nIf you want to enable only part of your workspace folder, consider using `deno.enablePaths` setting instead.\n\n**Not recommended to be enabled globally.**",
"scope": "resource",
"examples": [
true,
false
]
},
"deno.enablePaths": {
"type": "array",
"items": {
"type": "string"
},
"default": [],
"markdownDescription": "Enables the Deno Language Server for specific paths, instead of for the whole workspace folder. This will disable the built in TypeScript/JavaScript language server for those paths.\n\nWhen a value is set, the value of `\"deno.enable\"` is ignored.\n\nThe workspace folder is used as the base for the supplied paths. If for example you have all your Deno code in `worker` path in your workspace, you can add an item with the value of `./worker`, and the Deno will only provide diagnostics for the files within `worker` or any of its sub paths.\n\n**Not recommended to be enabled in user settings.**",
"scope": "resource",
"examples": [
[
"./worker"
]
]
},
"deno.path": {
"type": "string",
"default": null,
Expand Down
15 changes: 8 additions & 7 deletions typescript-deno-plugin/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions typescript-deno-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
"vscode": "^1.53.0"
},
"devDependencies": {
"typescript": "^4.4.2"
"typescript": "^4.6.2"
}
}
}
42 changes: 26 additions & 16 deletions typescript-deno-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const defaultSettings: Settings = {
cache: null,
certificateStores: null,
enable: false,
enablePaths: [],
codeLens: null,
config: null,
importMap: null,
Expand Down Expand Up @@ -67,19 +68,28 @@ class Plugin implements ts.server.PluginModule {
#project!: ts.server.Project;
#projectName!: string;

#getGlobalSettings(): Settings {
return projectSettings.get(this.#projectName)?.workspace ??
defaultSettings;
// determines if a deno is enabled "globally" or not for those APIs which
// don't reference a file name
#denoEnabled(): boolean {
return projectSettings.get(this.#projectName)?.workspace?.enable ??
defaultSettings.enable;
}

#getSetting<K extends keyof Settings>(
fileName: string,
key: K,
): Settings[K] {
// determines if a specific filename is Deno enabled or not.
#fileNameDenoEnabled(fileName: string): boolean {
const settings = projectSettings.get(this.#projectName);
return settings?.documents?.[fileName]?.settings[key] ??
// deno-lint-ignore no-explicit-any
settings?.workspace?.[key] as any ?? defaultSettings[key];
if (settings?.enabledPaths) {
const paths = settings.enabledPaths.find(({ workspace }) =>
fileName.startsWith(workspace)
)?.paths;
if (paths && paths.length) {
return paths.some((path) => fileName.startsWith(path));
}
}
// TODO(@kitsonk): rework all of this to be more like the workspace folders
// used for enabledPaths.
return settings?.documents?.[fileName]?.settings.enable ??
settings?.workspace?.enable ?? defaultSettings.enable;
}

#log = (_msg: string) => {};
Expand All @@ -106,8 +116,8 @@ class Plugin implements ts.server.PluginModule {
const target = (ls as any)[fn];
return (...args) => {
const enabled = fileNameArg !== undefined
? this.#getSetting(args[fileNameArg] as string, "enable")
: this.#getGlobalSettings().enable;
? this.#fileNameDenoEnabled(args[fileNameArg] as string)
: this.#denoEnabled;
return enabled
// in order to keep the `emptyReturn` separate instances, we do some
// analysis here to ensure we are returning a "fresh" `emptyReturn`
Expand All @@ -119,16 +129,16 @@ class Plugin implements ts.server.PluginModule {
};

// This "mutes" diagnostics for things like tsconfig files.
// TODO(@kitsonk) refine this logic to look at roots of projects against
// the workspace folder enablement
const projectGetGlobalProjectErrors = this.#project.getGlobalProjectErrors;
this.#project.getGlobalProjectErrors = () =>
this.#getGlobalSettings().enable
this.#denoEnabled()
? []
: projectGetGlobalProjectErrors.call(this.#project);
const projectGetAllProjectErrors = this.#project.getAllProjectErrors;
this.#project.getAllProjectErrors = () =>
this.#getGlobalSettings().enable
? []
: projectGetAllProjectErrors.call(this.#project);
this.#denoEnabled() ? [] : projectGetAllProjectErrors.call(this.#project);

const commentSelection = callIfDisabled("commentSelection", 0, []);
const findReferences = callIfDisabled("findReferences", 0, undefined);
Expand Down

0 comments on commit f0a4cb5

Please sign in to comment.