Skip to content

Commit

Permalink
feat(language-server): reintroducing full TS support (#4119)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed Mar 20, 2024
1 parent 777b0a1 commit 3758474
Show file tree
Hide file tree
Showing 40 changed files with 769 additions and 642 deletions.
1 change: 0 additions & 1 deletion .vscode/settings.json
Expand Up @@ -16,7 +16,6 @@
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"vue.server.path": "./extensions/vscode/server.js",
"files.exclude": {
"packages/*/*.d.ts": true,
"packages/*/*.js": true,
Expand Down
25 changes: 4 additions & 21 deletions extensions/vscode/package.json
Expand Up @@ -235,22 +235,10 @@
"default": "off",
"description": "Traces the communication between VS Code and the language server."
},
"vue.server.path": {
"type": [
"string",
"null"
],
"default": null,
"description": "Path to node_modules/vue-language-server/bin/vue-language-server.js."
},
"vue.server.runtime": {
"type": "string",
"enum": [
"node",
"bun"
],
"default": "node",
"description": "Vue Language Server runtime."
"vue.server.hybridMode": {
"type": "boolean",
"default": false,
"description": "Vue language server only handles CSS and HTML language support, and tsserver takes over TS language support via TS plugin."
},
"vue.server.maxFileSize": {
"type": "number",
Expand Down Expand Up @@ -368,11 +356,6 @@
"default": "autoKebab",
"description": "Preferred attr name case."
},
"vue.complete.casing.status": {
"type": "boolean",
"default": true,
"description": "Show name casing in status bar."
},
"vue.autoInsert.parentheses": {
"type": "boolean",
"default": true,
Expand Down
79 changes: 43 additions & 36 deletions extensions/vscode/src/common.ts
Expand Up @@ -3,6 +3,8 @@ import {
activateDocumentDropEdit,
activateServerSys,
activateWriteVirtualFiles,
activateTsConfigStatusItem,
activateTsVersionStatusItem,
getTsdk,
} from '@volar/vscode';
import { DiagnosticModel, VueInitializationOptions } from '@vue/language-server';
Expand All @@ -24,22 +26,19 @@ type CreateLanguageClient = (
outputChannel: vscode.OutputChannel,
) => lsp.BaseLanguageClient;

const beginHybridMode = config.server.hybridMode;

export async function activate(context: vscode.ExtensionContext, createLc: CreateLanguageClient) {

const stopCheck = vscode.window.onDidChangeActiveTextEditor(tryActivate);
tryActivate();

function tryActivate() {

if (!vscode.window.activeTextEditor) {
// onWebviewPanel:preview
doActivate(context, createLc);
stopCheck.dispose();
return;
}

const currentLangId = vscode.window.activeTextEditor.document.languageId;
if (currentLangId === 'vue' || (currentLangId === 'markdown' && config.server.vitePress.supportMdFile) || (currentLangId === 'html' && config.server.petiteVue.supportHtmlFile)) {
if (
vscode.window.visibleTextEditors.some(editor => editor.document.languageId === 'vue')
|| (config.server.vitePress.supportMdFile && vscode.window.visibleTextEditors.some(editor => editor.document.languageId === 'vue'))
|| (config.server.petiteVue.supportHtmlFile && vscode.window.visibleTextEditors.some(editor => editor.document.languageId === 'html'))
) {
doActivate(context, createLc);
stopCheck.dispose();
}
Expand All @@ -61,13 +60,6 @@ async function doActivate(context: vscode.ExtensionContext, createLc: CreateLang
outputChannel
);

activateServerMaxOldSpaceSizeChange();
activateRestartRequest();
activateClientRequests();

splitEditors.register(context, client);
doctor.register(context, client);

const selectors: vscode.DocumentFilter[] = [{ language: 'vue' }];

if (config.server.petiteVue.supportHtmlFile) {
Expand All @@ -77,49 +69,63 @@ async function doActivate(context: vscode.ExtensionContext, createLc: CreateLang
selectors.push({ language: 'markdown' });
}

activateConfigWatcher();
activateRestartRequest();

nameCasing.activate(context, client, selectors);
splitEditors.register(context, client);
doctor.register(context, client);

activateAutoInsertion(selectors, client);
activateDocumentDropEdit(selectors, client);
activateWriteVirtualFiles('vue.action.writeVirtualFiles', client);
activateServerSys(client);

async function requestReloadVscode() {
const reload = await vscode.window.showInformationMessage(
'Please reload VSCode to restart language servers.',
'Reload Window'
);
if (!config.server.hybridMode) {
activateTsConfigStatusItem(selectors, 'vue.tsconfig', client);
activateTsVersionStatusItem(selectors, 'vue.tsversion', context, client, text => 'TS ' + text);
}

const hybridModeStatus = vscode.languages.createLanguageStatusItem('vue-hybrid-mode', selectors);
hybridModeStatus.text = config.server.hybridMode ? 'Hybrid Mode: Enabled' : 'Hybrid Mode: Disabled';
hybridModeStatus.command = {
title: 'Open Setting',
command: 'workbench.action.openSettings',
arguments: ['vue.server.hybridMode'],
};
if (!config.server.hybridMode) {
hybridModeStatus.severity = vscode.LanguageStatusSeverity.Warning;
}

async function requestReloadVscode(msg: string) {
const reload = await vscode.window.showInformationMessage(msg, 'Reload Window');
if (reload === undefined) return; // cancel
vscode.commands.executeCommand('workbench.action.reloadWindow');
}

function activateServerMaxOldSpaceSizeChange() {
function activateConfigWatcher() {
context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('vue.server.runtime') || e.affectsConfiguration('vue.server.path')) {
requestReloadVscode();
if (e.affectsConfiguration('vue.server.hybridMode') && config.server.hybridMode !== beginHybridMode) {
requestReloadVscode(
config.server.hybridMode
? 'Please reload VSCode to enable Hybrid Mode.'
: 'Please reload VSCode to disable Hybrid Mode.'
);
}
if (e.affectsConfiguration('vue')) {
else if (e.affectsConfiguration('vue')) {
vscode.commands.executeCommand('vue.action.restartServer');
}
}));
}

async function activateRestartRequest() {
context.subscriptions.push(vscode.commands.registerCommand('vue.action.restartServer', async () => {

await client.stop();

outputChannel.clear();

client.clientOptions.initializationOptions = await getInitializationOptions(context);

await client.start();

activateClientRequests();
}));
}

function activateClientRequests() {
nameCasing.activate(context, client);
}
}

export function deactivate(): Thenable<any> | undefined {
Expand Down Expand Up @@ -151,6 +157,7 @@ async function getInitializationOptions(
tokenModifiers: [],
},
vue: {
hybridMode: beginHybridMode,
additionalExtensions: [
...config.server.additionalExtensions,
...!config.server.petiteVue.supportHtmlFile ? [] : ['html'],
Expand Down
4 changes: 1 addition & 3 deletions extensions/vscode/src/config.ts
Expand Up @@ -16,8 +16,7 @@ export const config = {
return _config().get('doctor')!;
},
get server(): Readonly<{
path: null | string;
runtime: 'node' | 'bun';
hybridMode: boolean;
maxOldSpaceSize: number;
maxFileSize: number;
diagnosticModel: 'push' | 'pull';
Expand Down Expand Up @@ -48,7 +47,6 @@ export const config = {
},
get complete(): Readonly<{
casing: {
status: boolean;
props: 'autoKebab' | 'autoCamel' | 'kebab' | 'camel';
tags: 'autoKebab' | 'autoPascal' | 'kebab' | 'pascal';
};
Expand Down
69 changes: 33 additions & 36 deletions extensions/vscode/src/features/doctor.ts
Expand Up @@ -134,30 +134,6 @@ export async function register(context: vscode.ExtensionContext, client: BaseLan
});
}

// check should use @volar-plugins/vetur instead of vetur
const vetur = vscode.extensions.getExtension('octref.vetur');
if (vetur?.isActive) {
problems.push({
title: 'Use volar-service-vetur instead of Vetur',
message: 'Detected Vetur enabled. Consider disabling Vetur and use [volar-service-vetur](https://github.com/volarjs/services/tree/master/packages/vetur) instead.',
});
}

// #3942, https://github.com/microsoft/TypeScript/issues/57633
for (const extId of ['svelte.svelte-vscode', 'styled-components.vscode-styled-components']) {
const ext = vscode.extensions.getExtension(extId);
if (ext) {
problems.push({
title: `Recommended to disable "${ext.packageJSON.displayName || extId}" in Vue workspace`,
message: [
`This extension's TypeScript Plugin and Vue's TypeScript Plugin are known to cause some conflicts. Until the problem is resolved, it is recommended that you temporarily disable the this extension in the Vue workspace.`,
'',
'Issues: https://github.com/vuejs/language-tools/issues/3942, https://github.com/microsoft/TypeScript/issues/57633',
].join('\n'),
});
}
}

// check using pug but don't install @vue/language-plugin-pug
if (
sfc?.descriptor.template?.lang === 'pug'
Expand Down Expand Up @@ -236,18 +212,39 @@ export async function register(context: vscode.ExtensionContext, client: BaseLan
});
}

// #3942
const namedPipe = await client.sendRequest(GetConnectedNamedPipeServerRequest.type, fileUri.fsPath.replace(/\\/g, '/'));
if (namedPipe?.serverKind === 0) {
problems.push({
title: 'Missing jsconfig/tsconfig',
message: [
'The current file does not have a matching tsconfig/jsconfig, and extension version 2.0 will not work properly for this at the moment.',
'To avoid this problem, you can create a jsconfig in the project root, or downgrade to 1.8.27.',
'',
'Issue: https://github.com/vuejs/language-tools/issues/3942',
].join('\n'),
});
if (config.server.hybridMode) {
// #3942
const namedPipe = await client.sendRequest(GetConnectedNamedPipeServerRequest.type, fileUri.fsPath.replace(/\\/g, '/'));
if (namedPipe?.serverKind === 0) {
problems.push({
title: 'Missing jsconfig/tsconfig',
message: [
'The current file does not have a matching tsconfig/jsconfig, and extension version 2.0 will not work properly for this at the moment.',
'To avoid this problem, you can create a jsconfig in the project root, or downgrade to 1.8.27.',
'',
'Issue: https://github.com/vuejs/language-tools/issues/3942',
].join('\n'),
});
}

// #3942, https://github.com/microsoft/TypeScript/issues/57633
for (const extId of [
'svelte.svelte-vscode',
'styled-components.vscode-styled-components',
'Divlo.vscode-styled-jsx-languageserver',
]) {
const ext = vscode.extensions.getExtension(extId);
if (ext) {
problems.push({
title: `Recommended to disable "${ext.packageJSON.displayName || extId}" in Vue workspace`,
message: [
`This extension's TypeScript Plugin and Vue's TypeScript Plugin are known to cause some conflicts. Until the problem is resolved, it is recommended that you temporarily disable the this extension in the Vue workspace.`,
'',
'Issues: https://github.com/vuejs/language-tools/issues/3942, https://github.com/microsoft/TypeScript/issues/57633',
].join('\n'),
});
}
}
}

// check outdated vue language plugins
Expand Down
22 changes: 9 additions & 13 deletions extensions/vscode/src/features/nameCasing.ts
Expand Up @@ -7,13 +7,16 @@ import { config } from '../config';
export const attrNameCasings = new Map<string, AttrNameCasing>();
export const tagNameCasings = new Map<string, TagNameCasing>();

export async function activate(_context: vscode.ExtensionContext, client: BaseLanguageClient) {
export async function activate(_context: vscode.ExtensionContext, client: BaseLanguageClient, selector: vscode.DocumentSelector) {

await client.start();

const disposes: vscode.Disposable[] = [];
const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
statusBar.command = 'vue.action.nameCasing';
const statusBar = vscode.languages.createLanguageStatusItem('vue-name-casing', selector);
statusBar.command = {
title: 'Open Menu',
command: 'vue.action.nameCasing',
};

update(vscode.window.activeTextEditor?.document);

Expand Down Expand Up @@ -132,12 +135,9 @@ export async function activate(_context: vscode.ExtensionContext, client: BaseLa

async function update(document: vscode.TextDocument | undefined) {
if (
config.complete.casing.status
&& (
document?.languageId === 'vue'
|| (config.server.vitePress.supportMdFile && document?.languageId === 'markdown')
|| (config.server.petiteVue.supportHtmlFile && document?.languageId === 'html')
)
document?.languageId === 'vue'
|| (config.server.vitePress.supportMdFile && document?.languageId === 'markdown')
|| (config.server.petiteVue.supportHtmlFile && document?.languageId === 'html')
) {
let detected: Awaited<ReturnType<typeof detect>> | undefined;
let attrNameCasing = attrNameCasings.get(document.uri.toString());
Expand Down Expand Up @@ -187,10 +187,6 @@ export async function activate(_context: vscode.ExtensionContext, client: BaseLa
}

updateStatusBarText();
statusBar.show();
}
else {
statusBar.hide();
}
}

Expand Down

0 comments on commit 3758474

Please sign in to comment.