Skip to content

Commit 3758474

Browse files
feat(language-server): reintroducing full TS support (#4119)
1 parent 777b0a1 commit 3758474

File tree

40 files changed

+769
-642
lines changed

40 files changed

+769
-642
lines changed

.vscode/settings.json

-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
"[jsonc]": {
1717
"editor.defaultFormatter": "vscode.json-language-features"
1818
},
19-
"vue.server.path": "./extensions/vscode/server.js",
2019
"files.exclude": {
2120
"packages/*/*.d.ts": true,
2221
"packages/*/*.js": true,

extensions/vscode/package.json

+4-21
Original file line numberDiff line numberDiff line change
@@ -235,22 +235,10 @@
235235
"default": "off",
236236
"description": "Traces the communication between VS Code and the language server."
237237
},
238-
"vue.server.path": {
239-
"type": [
240-
"string",
241-
"null"
242-
],
243-
"default": null,
244-
"description": "Path to node_modules/vue-language-server/bin/vue-language-server.js."
245-
},
246-
"vue.server.runtime": {
247-
"type": "string",
248-
"enum": [
249-
"node",
250-
"bun"
251-
],
252-
"default": "node",
253-
"description": "Vue Language Server runtime."
238+
"vue.server.hybridMode": {
239+
"type": "boolean",
240+
"default": false,
241+
"description": "Vue language server only handles CSS and HTML language support, and tsserver takes over TS language support via TS plugin."
254242
},
255243
"vue.server.maxFileSize": {
256244
"type": "number",
@@ -368,11 +356,6 @@
368356
"default": "autoKebab",
369357
"description": "Preferred attr name case."
370358
},
371-
"vue.complete.casing.status": {
372-
"type": "boolean",
373-
"default": true,
374-
"description": "Show name casing in status bar."
375-
},
376359
"vue.autoInsert.parentheses": {
377360
"type": "boolean",
378361
"default": true,

extensions/vscode/src/common.ts

+43-36
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import {
33
activateDocumentDropEdit,
44
activateServerSys,
55
activateWriteVirtualFiles,
6+
activateTsConfigStatusItem,
7+
activateTsVersionStatusItem,
68
getTsdk,
79
} from '@volar/vscode';
810
import { DiagnosticModel, VueInitializationOptions } from '@vue/language-server';
@@ -24,22 +26,19 @@ type CreateLanguageClient = (
2426
outputChannel: vscode.OutputChannel,
2527
) => lsp.BaseLanguageClient;
2628

29+
const beginHybridMode = config.server.hybridMode;
30+
2731
export async function activate(context: vscode.ExtensionContext, createLc: CreateLanguageClient) {
2832

2933
const stopCheck = vscode.window.onDidChangeActiveTextEditor(tryActivate);
3034
tryActivate();
3135

3236
function tryActivate() {
33-
34-
if (!vscode.window.activeTextEditor) {
35-
// onWebviewPanel:preview
36-
doActivate(context, createLc);
37-
stopCheck.dispose();
38-
return;
39-
}
40-
41-
const currentLangId = vscode.window.activeTextEditor.document.languageId;
42-
if (currentLangId === 'vue' || (currentLangId === 'markdown' && config.server.vitePress.supportMdFile) || (currentLangId === 'html' && config.server.petiteVue.supportHtmlFile)) {
37+
if (
38+
vscode.window.visibleTextEditors.some(editor => editor.document.languageId === 'vue')
39+
|| (config.server.vitePress.supportMdFile && vscode.window.visibleTextEditors.some(editor => editor.document.languageId === 'vue'))
40+
|| (config.server.petiteVue.supportHtmlFile && vscode.window.visibleTextEditors.some(editor => editor.document.languageId === 'html'))
41+
) {
4342
doActivate(context, createLc);
4443
stopCheck.dispose();
4544
}
@@ -61,13 +60,6 @@ async function doActivate(context: vscode.ExtensionContext, createLc: CreateLang
6160
outputChannel
6261
);
6362

64-
activateServerMaxOldSpaceSizeChange();
65-
activateRestartRequest();
66-
activateClientRequests();
67-
68-
splitEditors.register(context, client);
69-
doctor.register(context, client);
70-
7163
const selectors: vscode.DocumentFilter[] = [{ language: 'vue' }];
7264

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

72+
activateConfigWatcher();
73+
activateRestartRequest();
74+
75+
nameCasing.activate(context, client, selectors);
76+
splitEditors.register(context, client);
77+
doctor.register(context, client);
78+
8079
activateAutoInsertion(selectors, client);
8180
activateDocumentDropEdit(selectors, client);
8281
activateWriteVirtualFiles('vue.action.writeVirtualFiles', client);
8382
activateServerSys(client);
8483

85-
async function requestReloadVscode() {
86-
const reload = await vscode.window.showInformationMessage(
87-
'Please reload VSCode to restart language servers.',
88-
'Reload Window'
89-
);
84+
if (!config.server.hybridMode) {
85+
activateTsConfigStatusItem(selectors, 'vue.tsconfig', client);
86+
activateTsVersionStatusItem(selectors, 'vue.tsversion', context, client, text => 'TS ' + text);
87+
}
88+
89+
const hybridModeStatus = vscode.languages.createLanguageStatusItem('vue-hybrid-mode', selectors);
90+
hybridModeStatus.text = config.server.hybridMode ? 'Hybrid Mode: Enabled' : 'Hybrid Mode: Disabled';
91+
hybridModeStatus.command = {
92+
title: 'Open Setting',
93+
command: 'workbench.action.openSettings',
94+
arguments: ['vue.server.hybridMode'],
95+
};
96+
if (!config.server.hybridMode) {
97+
hybridModeStatus.severity = vscode.LanguageStatusSeverity.Warning;
98+
}
99+
100+
async function requestReloadVscode(msg: string) {
101+
const reload = await vscode.window.showInformationMessage(msg, 'Reload Window');
90102
if (reload === undefined) return; // cancel
91103
vscode.commands.executeCommand('workbench.action.reloadWindow');
92104
}
93105

94-
function activateServerMaxOldSpaceSizeChange() {
106+
function activateConfigWatcher() {
95107
context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => {
96-
if (e.affectsConfiguration('vue.server.runtime') || e.affectsConfiguration('vue.server.path')) {
97-
requestReloadVscode();
108+
if (e.affectsConfiguration('vue.server.hybridMode') && config.server.hybridMode !== beginHybridMode) {
109+
requestReloadVscode(
110+
config.server.hybridMode
111+
? 'Please reload VSCode to enable Hybrid Mode.'
112+
: 'Please reload VSCode to disable Hybrid Mode.'
113+
);
98114
}
99-
if (e.affectsConfiguration('vue')) {
115+
else if (e.affectsConfiguration('vue')) {
100116
vscode.commands.executeCommand('vue.action.restartServer');
101117
}
102118
}));
103119
}
104120

105121
async function activateRestartRequest() {
106122
context.subscriptions.push(vscode.commands.registerCommand('vue.action.restartServer', async () => {
107-
108123
await client.stop();
109-
110124
outputChannel.clear();
111-
112125
client.clientOptions.initializationOptions = await getInitializationOptions(context);
113-
114126
await client.start();
115-
116-
activateClientRequests();
117127
}));
118128
}
119-
120-
function activateClientRequests() {
121-
nameCasing.activate(context, client);
122-
}
123129
}
124130

125131
export function deactivate(): Thenable<any> | undefined {
@@ -151,6 +157,7 @@ async function getInitializationOptions(
151157
tokenModifiers: [],
152158
},
153159
vue: {
160+
hybridMode: beginHybridMode,
154161
additionalExtensions: [
155162
...config.server.additionalExtensions,
156163
...!config.server.petiteVue.supportHtmlFile ? [] : ['html'],

extensions/vscode/src/config.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ export const config = {
1616
return _config().get('doctor')!;
1717
},
1818
get server(): Readonly<{
19-
path: null | string;
20-
runtime: 'node' | 'bun';
19+
hybridMode: boolean;
2120
maxOldSpaceSize: number;
2221
maxFileSize: number;
2322
diagnosticModel: 'push' | 'pull';
@@ -48,7 +47,6 @@ export const config = {
4847
},
4948
get complete(): Readonly<{
5049
casing: {
51-
status: boolean;
5250
props: 'autoKebab' | 'autoCamel' | 'kebab' | 'camel';
5351
tags: 'autoKebab' | 'autoPascal' | 'kebab' | 'pascal';
5452
};

extensions/vscode/src/features/doctor.ts

+33-36
Original file line numberDiff line numberDiff line change
@@ -134,30 +134,6 @@ export async function register(context: vscode.ExtensionContext, client: BaseLan
134134
});
135135
}
136136

137-
// check should use @volar-plugins/vetur instead of vetur
138-
const vetur = vscode.extensions.getExtension('octref.vetur');
139-
if (vetur?.isActive) {
140-
problems.push({
141-
title: 'Use volar-service-vetur instead of Vetur',
142-
message: 'Detected Vetur enabled. Consider disabling Vetur and use [volar-service-vetur](https://github.com/volarjs/services/tree/master/packages/vetur) instead.',
143-
});
144-
}
145-
146-
// #3942, https://github.com/microsoft/TypeScript/issues/57633
147-
for (const extId of ['svelte.svelte-vscode', 'styled-components.vscode-styled-components']) {
148-
const ext = vscode.extensions.getExtension(extId);
149-
if (ext) {
150-
problems.push({
151-
title: `Recommended to disable "${ext.packageJSON.displayName || extId}" in Vue workspace`,
152-
message: [
153-
`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.`,
154-
'',
155-
'Issues: https://github.com/vuejs/language-tools/issues/3942, https://github.com/microsoft/TypeScript/issues/57633',
156-
].join('\n'),
157-
});
158-
}
159-
}
160-
161137
// check using pug but don't install @vue/language-plugin-pug
162138
if (
163139
sfc?.descriptor.template?.lang === 'pug'
@@ -236,18 +212,39 @@ export async function register(context: vscode.ExtensionContext, client: BaseLan
236212
});
237213
}
238214

239-
// #3942
240-
const namedPipe = await client.sendRequest(GetConnectedNamedPipeServerRequest.type, fileUri.fsPath.replace(/\\/g, '/'));
241-
if (namedPipe?.serverKind === 0) {
242-
problems.push({
243-
title: 'Missing jsconfig/tsconfig',
244-
message: [
245-
'The current file does not have a matching tsconfig/jsconfig, and extension version 2.0 will not work properly for this at the moment.',
246-
'To avoid this problem, you can create a jsconfig in the project root, or downgrade to 1.8.27.',
247-
'',
248-
'Issue: https://github.com/vuejs/language-tools/issues/3942',
249-
].join('\n'),
250-
});
215+
if (config.server.hybridMode) {
216+
// #3942
217+
const namedPipe = await client.sendRequest(GetConnectedNamedPipeServerRequest.type, fileUri.fsPath.replace(/\\/g, '/'));
218+
if (namedPipe?.serverKind === 0) {
219+
problems.push({
220+
title: 'Missing jsconfig/tsconfig',
221+
message: [
222+
'The current file does not have a matching tsconfig/jsconfig, and extension version 2.0 will not work properly for this at the moment.',
223+
'To avoid this problem, you can create a jsconfig in the project root, or downgrade to 1.8.27.',
224+
'',
225+
'Issue: https://github.com/vuejs/language-tools/issues/3942',
226+
].join('\n'),
227+
});
228+
}
229+
230+
// #3942, https://github.com/microsoft/TypeScript/issues/57633
231+
for (const extId of [
232+
'svelte.svelte-vscode',
233+
'styled-components.vscode-styled-components',
234+
'Divlo.vscode-styled-jsx-languageserver',
235+
]) {
236+
const ext = vscode.extensions.getExtension(extId);
237+
if (ext) {
238+
problems.push({
239+
title: `Recommended to disable "${ext.packageJSON.displayName || extId}" in Vue workspace`,
240+
message: [
241+
`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.`,
242+
'',
243+
'Issues: https://github.com/vuejs/language-tools/issues/3942, https://github.com/microsoft/TypeScript/issues/57633',
244+
].join('\n'),
245+
});
246+
}
247+
}
251248
}
252249

253250
// check outdated vue language plugins

extensions/vscode/src/features/nameCasing.ts

+9-13
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ import { config } from '../config';
77
export const attrNameCasings = new Map<string, AttrNameCasing>();
88
export const tagNameCasings = new Map<string, TagNameCasing>();
99

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

1212
await client.start();
1313

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

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

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

133136
async function update(document: vscode.TextDocument | undefined) {
134137
if (
135-
config.complete.casing.status
136-
&& (
137-
document?.languageId === 'vue'
138-
|| (config.server.vitePress.supportMdFile && document?.languageId === 'markdown')
139-
|| (config.server.petiteVue.supportHtmlFile && document?.languageId === 'html')
140-
)
138+
document?.languageId === 'vue'
139+
|| (config.server.vitePress.supportMdFile && document?.languageId === 'markdown')
140+
|| (config.server.petiteVue.supportHtmlFile && document?.languageId === 'html')
141141
) {
142142
let detected: Awaited<ReturnType<typeof detect>> | undefined;
143143
let attrNameCasing = attrNameCasings.get(document.uri.toString());
@@ -187,10 +187,6 @@ export async function activate(_context: vscode.ExtensionContext, client: BaseLa
187187
}
188188

189189
updateStatusBarText();
190-
statusBar.show();
191-
}
192-
else {
193-
statusBar.hide();
194190
}
195191
}
196192

0 commit comments

Comments
 (0)