Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: reintroducing the complete language server #4119

Merged
merged 11 commits into from Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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