Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed Mar 19, 2024
1 parent 58a8202 commit 24eb2bf
Show file tree
Hide file tree
Showing 20 changed files with 275 additions and 238 deletions.
4 changes: 4 additions & 0 deletions extensions/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@
"default": "off",
"description": "Traces the communication between VS Code and the language server."
},
"vue.server.hybridMode": {
"type": "boolean",
"default": false
},
"vue.server.path": {
"type": [
"string",
Expand Down
1 change: 1 addition & 0 deletions extensions/vscode/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ async function getInitializationOptions(
tokenModifiers: [],
},
vue: {
hybridMode: config.server.hybridMode,
additionalExtensions: [
...config.server.additionalExtensions,
...!config.server.petiteVue.supportHtmlFile ? [] : ['html'],
Expand Down
1 change: 1 addition & 0 deletions extensions/vscode/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const config = {
return _config().get('doctor')!;
},
get server(): Readonly<{
hybridMode: boolean;
path: null | string;
runtime: 'node' | 'bun';
maxOldSpaceSize: number;
Expand Down
5 changes: 2 additions & 3 deletions packages/component-meta/lib/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ function createCheckerWorker(
let projectVersion = 0;

const scriptSnapshots = new Map<string, ts.IScriptSnapshot>();
const resolvedVueOptions = vue.resolveVueCompilerOptions(parsedCommandLine.vueOptions);
const _host: vue.TypeScriptProjectHost = {
getCurrentDirectory: () => rootPath,
getProjectVersion: () => projectVersion.toString(),
Expand All @@ -88,15 +87,15 @@ function createCheckerWorker(
return scriptSnapshots.get(fileName);
},
getLanguageId: fileName => {
if (resolvedVueOptions.extensions.some(ext => fileName.endsWith(ext))) {
if (parsedCommandLine.vueOptions.extensions.some(ext => fileName.endsWith(ext))) {
return 'vue';
}
return vue.resolveCommonLanguageId(fileName);
},
};

return {
...baseCreate(ts, configFileName, _host, resolvedVueOptions, checkerOptions, globalComponentName),
...baseCreate(ts, configFileName, _host, parsedCommandLine.vueOptions, checkerOptions, globalComponentName),
updateFile(fileName: string, text: string) {
fileName = fileName.replace(windowsPathReg, '/');
scriptSnapshots.set(fileName, ts.ScriptSnapshot.fromString(text));
Expand Down
6 changes: 4 additions & 2 deletions packages/language-core/lib/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ export function getDefaultVueLanguagePlugins(pluginContext: Parameters<VueLangua
const pluginInstances = plugins
.map(plugin => {
try {
return plugin(pluginContext);
const instance = plugin(pluginContext);
instance.name ??= (plugin as any).__moduleName;
return instance;
} catch (err) {
console.warn('[Vue] Failed to create plugin', err);
}
Expand All @@ -42,7 +44,7 @@ export function getDefaultVueLanguagePlugins(pluginContext: Parameters<VueLangua
return pluginInstances.filter(plugin => {
const valid = plugin.version === pluginVersion;
if (!valid) {
console.warn(`[Vue] Plugin ${JSON.stringify(plugin.name)} API version incompatible, expected ${JSON.stringify(pluginVersion)} but got ${JSON.stringify(plugin.version)}`);
console.warn(`[Vue] Plugin ${JSON.stringify(plugin.name)} API version incompatible, expected "${pluginVersion}" but got "${plugin.version}".`);
}
return valid;
});
Expand Down
16 changes: 10 additions & 6 deletions packages/language-core/lib/utils/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as path from 'path-browserify';
import type { RawVueCompilerOptions, VueCompilerOptions, VueLanguagePlugin } from '../types';

export type ParsedCommandLine = ts.ParsedCommandLine & {
vueOptions: Partial<VueCompilerOptions>;
vueOptions: VueCompilerOptions;
};

export function createParsedCommandLineByJson(
Expand All @@ -28,14 +28,15 @@ export function createParsedCommandLineByJson(
} catch (err) { }
}

const resolvedVueOptions = resolveVueCompilerOptions(vueOptions);
const parsed = ts.parseJsonConfigFileContent(
json,
proxyHost.host,
rootDir,
{},
configFileName,
undefined,
(vueOptions.extensions ?? ['.vue']).map(extension => ({
resolvedVueOptions.extensions.map(extension => ({
extension: extension.slice(1),
isMixedContent: true,
scriptKind: ts.ScriptKind.Deferred,
Expand All @@ -49,7 +50,7 @@ export function createParsedCommandLineByJson(

return {
...parsed,
vueOptions,
vueOptions: resolvedVueOptions,
};
}

Expand All @@ -74,14 +75,15 @@ export function createParsedCommandLine(
} catch (err) { }
}

const resolvedVueOptions = resolveVueCompilerOptions(vueOptions);
const parsed = ts.parseJsonSourceFileConfigFileContent(
config,
proxyHost.host,
path.dirname(tsConfigPath),
{},
tsConfigPath,
undefined,
(vueOptions.extensions ?? ['.vue']).map(extension => ({
resolvedVueOptions.extensions.map(extension => ({
extension: extension.slice(1),
isMixedContent: true,
scriptKind: ts.ScriptKind.Deferred,
Expand All @@ -95,7 +97,7 @@ export function createParsedCommandLine(

return {
...parsed,
vueOptions,
vueOptions: resolvedVueOptions,
};
}
catch (err) {
Expand Down Expand Up @@ -163,7 +165,9 @@ function getPartialVueCompilerOptions(
try {
const resolvedPath = resolvePath(pluginPath);
if (resolvedPath) {
return require(resolvedPath);
const plugin = require(resolvedPath);
plugin.__moduleName = pluginPath;
return plugin;
}
else {
console.warn('[Vue] Load plugin failed:', pluginPath);
Expand Down
2 changes: 1 addition & 1 deletion packages/language-plugin-pug/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@
},
"dependencies": {
"@volar/source-map": "~2.1.2",
"volar-service-pug": "0.0.31"
"volar-service-pug": "0.0.34"
}
}
5 changes: 3 additions & 2 deletions packages/language-server/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import type { InitializationOptions } from "@volar/language-server";
export type VueInitializationOptions = InitializationOptions & {
typescript: {
tsdk: string;
}
vue?: {
};
vue: {
hybridMode: boolean;
/**
* @example ['vue1', 'vue2']
*/
Expand Down
33 changes: 15 additions & 18 deletions packages/language-server/node.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Connection } from '@volar/language-server';
import { createConnection, createServer, createSimpleProjectProviderFactory, loadTsdkByPath } from '@volar/language-server/node';
import { createConnection, createServer, createSimpleProjectProviderFactory, createTypeScriptProjectProviderFactory, loadTsdkByPath } from '@volar/language-server/node';
import { ParsedCommandLine, VueCompilerOptions, createParsedCommandLine, createVueLanguagePlugin, parse, resolveVueCompilerOptions } from '@vue/language-core';
import { ServiceEnvironment, convertAttrName, convertTagName, createVueServicePlugins, detect } from '@vue/language-service';
import { DetectNameCasingRequest, GetConvertAttrCasingEditsRequest, GetConvertTagCasingEditsRequest, ParseSFCRequest } from './lib/protocol';
Expand Down Expand Up @@ -33,15 +33,22 @@ connection.onInitialize(async params => {

const result = await server.initialize(
params,
createSimpleProjectProviderFactory(),
options.vue.hybridMode
? createSimpleProjectProviderFactory()
: createTypeScriptProjectProviderFactory(tsdk.typescript, tsdk.diagnosticMessages),
{
watchFileExtensions: ['js', 'cjs', 'mjs', 'ts', 'cts', 'mts', 'jsx', 'tsx', 'json', ...vueFileExtensions],
getServicePlugins() {
return createVueServicePlugins(tsdk.typescript, env => envToVueOptions.get(env)!, tsPluginClient);
return createVueServicePlugins(tsdk.typescript, env => envToVueOptions.get(env)!, options.vue.hybridMode, tsPluginClient);
},
async getLanguagePlugins(serviceEnv, projectContext) {
const [commandLine, vueOptions] = await parseCommandLine();
const resolvedVueOptions = resolveVueCompilerOptions(vueOptions);
const commandLine = await parseCommandLine();
const vueOptions = commandLine?.vueOptions ?? resolveVueCompilerOptions({});
for (const ext of vueFileExtensions) {
if (vueOptions.extensions.includes(`.${ext}`)) {
vueOptions.extensions.push(`.${ext}`);
}
}
const vueLanguagePlugin = createVueLanguagePlugin(
tsdk.typescript,
serviceEnv.typescript!.uriToFileName,
Expand All @@ -60,18 +67,17 @@ connection.onInitialize(async params => {
}
},
commandLine?.options ?? {},
resolvedVueOptions,
vueOptions,
options.codegenStack,
);

envToVueOptions.set(serviceEnv, resolvedVueOptions);
envToVueOptions.set(serviceEnv, vueOptions);

return [vueLanguagePlugin];

async function parseCommandLine() {

let commandLine: ParsedCommandLine | undefined;
let vueOptions: Partial<VueCompilerOptions> = {};

if (projectContext.typescript) {

Expand All @@ -89,16 +95,7 @@ connection.onInitialize(async params => {
}
}

if (commandLine) {
vueOptions = commandLine.vueOptions;
}
vueOptions.extensions = [
...vueOptions.extensions ?? ['.vue'],
...vueFileExtensions.map(ext => '.' + ext),
];
vueOptions.extensions = [...new Set(vueOptions.extensions)];

return [commandLine, vueOptions] as const;
return commandLine;
}
},
},
Expand Down
32 changes: 29 additions & 3 deletions packages/language-service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ export * from './lib/types';
import type { ServiceEnvironment, ServicePlugin } from '@volar/language-service';
import type { VueCompilerOptions } from './lib/types';

import { decorateLanguageServiceForVue } from '@vue/typescript-plugin/lib/common';
import { create as createEmmetServicePlugin } from 'volar-service-emmet';
import { create as createJsonServicePlugin } from 'volar-service-json';
import { create as createPugFormatServicePlugin } from 'volar-service-pug-beautify';
import { create as createTypeScriptServicePlugin } from 'volar-service-typescript';
import { create as createTypeScriptTwoslashQueriesServicePlugin } from 'volar-service-typescript-twoslash-queries';
import { create as createTypeScriptDocCommentTemplateServicePlugin } from 'volar-service-typescript/lib/plugins/docCommentTemplate';
import { create as createTypeScriptSyntacticServicePlugin } from 'volar-service-typescript/lib/plugins/syntactic';
import { create as createCssServicePlugin } from './lib/plugins/css';
import { create as createVueAutoDotValueServicePlugin } from './lib/plugins/vue-autoinsert-dotvalue';
import { create as createVueAutoWrapParenthesesServicePlugin } from './lib/plugins/vue-autoinsert-parentheses';
Expand All @@ -28,11 +31,11 @@ import { create as createVueVisualizeHiddenCallbackParamServicePlugin } from './
export function createVueServicePlugins(
ts: typeof import('typescript'),
getVueOptions: (env: ServiceEnvironment) => VueCompilerOptions,
hybridMode = true,
tsPluginClient?: typeof import('@vue/typescript-plugin/lib/client'),
): ServicePlugin[] {
return [
createTypeScriptServicePlugin(ts),
createTypeScriptTwoslashQueriesServicePlugin(),
const plugins: ServicePlugin[] = [
createTypeScriptTwoslashQueriesServicePlugin(ts),
createCssServicePlugin(),
createPugFormatServicePlugin(),
createJsonServicePlugin(),
Expand All @@ -51,4 +54,27 @@ export function createVueServicePlugins(
createVueToggleVBindServicePlugin(ts),
createEmmetServicePlugin(),
];
if (!hybridMode) {
plugins.push(...createTypeScriptServicePlugin(ts));
for (let i = 0; i < plugins.length; i++) {
const plugin = plugins[i];
if (plugin.name === 'typescript-semantic') {
plugins[i] = {
...plugin,
create(context) {
const created = plugin.create(context);
const languageService = (created.provide as import('volar-service-typescript').Provide)['typescript/languageService']();
const vueOptions = getVueOptions(context.env);
decorateLanguageServiceForVue(context.language.files, languageService, vueOptions, ts);
return created;
},
};
}
}
}
else {
plugins.push(createTypeScriptSyntacticServicePlugin(ts));
plugins.push(createTypeScriptDocCommentTemplateServicePlugin(ts));
}
return plugins;
}
11 changes: 5 additions & 6 deletions packages/language-service/lib/plugins/vue-sfc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,16 @@ export function create(): ServicePlugin {
return {
name: 'vue-sfc',
create(context): ServicePluginInstance<Provide> {

const htmlPlugin = createHtmlService({
documentSelector: ['vue'],
useCustomDataProviders: false,
useDefaultDataProvider: false,
getCustomData(context) {
sfcDataProvider ??= html.newHTMLDataProvider('vue', loadLanguageBlocks(context.env.locale ?? 'en'));
return [sfcDataProvider];
},
}).create(context);
const htmlLanguageService: html.LanguageService = htmlPlugin.provide['html/languageService']();

sfcDataProvider ??= html.newHTMLDataProvider('vue', loadLanguageBlocks(context.env.locale ?? 'en'));

htmlLanguageService.setDataProviders(false, [sfcDataProvider]);

return {

...htmlPlugin,
Expand Down
17 changes: 9 additions & 8 deletions packages/language-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,18 @@
"@volar/typescript": "~2.1.2",
"@vue/compiler-dom": "^3.4.0",
"@vue/language-core": "2.0.6",
"@vue/typescript-plugin": "2.0.6",
"@vue/shared": "^3.4.0",
"computeds": "^0.0.1",
"path-browserify": "^1.0.1",
"volar-service-css": "0.0.31",
"volar-service-emmet": "0.0.31",
"volar-service-html": "0.0.31",
"volar-service-json": "0.0.31",
"volar-service-pug": "0.0.31",
"volar-service-pug-beautify": "0.0.31",
"volar-service-typescript": "0.0.31-patch.1",
"volar-service-typescript-twoslash-queries": "0.0.31",
"volar-service-css": "0.0.34",
"volar-service-emmet": "0.0.34",
"volar-service-html": "0.0.34",
"volar-service-json": "0.0.34",
"volar-service-pug": "0.0.34",
"volar-service-pug-beautify": "0.0.34",
"volar-service-typescript": "0.0.34",
"volar-service-typescript-twoslash-queries": "0.0.34",
"vscode-html-languageservice": "^5.1.0",
"vscode-languageserver-textdocument": "^1.0.11",
"vscode-uri": "^3.0.8"
Expand Down
2 changes: 1 addition & 1 deletion packages/language-service/tests/complete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const normalizeNewline = (text: string) => text.replace(/\r\n/g, '\n');

for (const dirName of testDirs) {

describe.skipIf(dirName === 'core#8811' || dirName === '#2511' || dirName === 'component-auto-import')(`complete: ${dirName}`, async () => {
describe(`complete: ${dirName}`, async () => {

const dir = path.join(baseDir, dirName);
const inputFiles = readFiles(path.join(dir, 'input'));
Expand Down
2 changes: 1 addition & 1 deletion packages/language-service/tests/inlayHint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const testDirs = fs.readdirSync(baseDir);

for (const dirName of testDirs) {

describe.skipIf(dirName === 'missing-props')(`inlay hint: ${dirName}`, async () => {
describe(`inlay hint: ${dirName}`, async () => {

const dir = path.join(baseDir, dirName);
const inputFiles = readFiles(dir);
Expand Down
7 changes: 3 additions & 4 deletions packages/language-service/tests/utils/createTester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createLanguage } from '@volar/typescript';
import * as path from 'path';
import type * as ts from 'typescript';
import { URI } from 'vscode-uri';
import { createParsedCommandLine, createVueLanguagePlugin, createVueServicePlugins, resolveVueCompilerOptions } from '../..';
import { createParsedCommandLine, createVueLanguagePlugin, createVueServicePlugins } from '../..';
import { createMockServiceEnv } from './mockEnv';

export const rootUri = URI.file(path.resolve(__dirname, '../../../../test-workspace/language-service')).toString();
Expand All @@ -26,7 +26,6 @@ function createTester(rootUri: string) {
getScriptSnapshot,
getLanguageId: resolveCommonLanguageId,
};
const resolvedVueOptions = resolveVueCompilerOptions(parsedCommandLine.vueOptions);
const vueLanguagePlugin = createVueLanguagePlugin(
ts,
serviceEnv.typescript!.uriToFileName,
Expand All @@ -45,9 +44,9 @@ function createTester(rootUri: string) {
}
},
parsedCommandLine.options,
resolvedVueOptions,
parsedCommandLine.vueOptions,
);
const vueServicePlugins = createVueServicePlugins(ts, () => resolvedVueOptions);
const vueServicePlugins = createVueServicePlugins(ts, () => parsedCommandLine.vueOptions, false);
const defaultVSCodeSettings: any = {
'typescript.preferences.quoteStyle': 'single',
'javascript.preferences.quoteStyle': 'single',
Expand Down
Loading

0 comments on commit 24eb2bf

Please sign in to comment.