Skip to content

Commit

Permalink
refactor: simplify language server params for vue
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed Jan 3, 2023
1 parent f4a5179 commit 4820a7c
Show file tree
Hide file tree
Showing 30 changed files with 526 additions and 599 deletions.
46 changes: 20 additions & 26 deletions angular-language-tools/angular-language-server/src/index.ts
@@ -1,7 +1,7 @@
import { createTsLanguageModule, createHtmlLanguageModule, HTMLTemplateFile } from '@volar-examples/angular-language-core';
import createTsPlugin from '@volar-plugins/typescript';
import { createLanguageServer, LanguageServerPlugin } from '@volar/language-server/node';
import type { LanguageServicePlugin, DocumentsAndSourceMaps, Diagnostic } from '@volar/language-service';
import type { LanguageServicePlugin, Diagnostic } from '@volar/language-service';

const plugin: LanguageServerPlugin = () => ({
extraFileExtensions: [{ extension: 'html', isMixedContent: true, scriptKind: 7 }],
Expand All @@ -15,41 +15,35 @@ const plugin: LanguageServerPlugin = () => ({
}
return [];
},
getServicePlugins(_host, service) {
getServicePlugins() {
return [
createTsPlugin(),
createNgTemplateLsPlugin(service.context.documents),
ngTemplatePlugin,
];
},
});

function createNgTemplateLsPlugin(docs: DocumentsAndSourceMaps): LanguageServicePlugin {
const ngTemplatePlugin: LanguageServicePlugin = (context) => ({

return () => {
validation: {

return {
onSyntactic(document) {

validation: {
const file = context.documents.getVirtualFileByUri(document.uri);

onSyntactic(document) {

const file = docs.getRootFileBySourceFileUri(document.uri);

if (file instanceof HTMLTemplateFile) {
return (file.parsed.errors ?? []).map<Diagnostic>(error => ({
range: {
start: { line: error.span.start.line, character: error.span.start.col },
end: { line: error.span.end.line, character: error.span.end.col },
},
severity: error.level === 1 ? 1 : 2,
source: 'ng-template',
message: error.msg,
}));
}
},
if (file instanceof HTMLTemplateFile) {
return (file.parsed.errors ?? []).map<Diagnostic>(error => ({
range: {
start: { line: error.span.start.line, character: error.span.start.col },
end: { line: error.span.end.line, character: error.span.end.col },
},
severity: error.level === 1 ? 1 : 2,
source: 'ng-template',
message: error.msg,
}));
}
};
};
}
},
}
});

createLanguageServer([plugin]);
2 changes: 1 addition & 1 deletion examples/vue-and-svelte-language-server/src/index.ts
Expand Up @@ -3,7 +3,7 @@ import useTsPlugin from '@volar-plugins/typescript';
import { createLanguageServer, LanguageServerInitializationOptions, LanguageServerPlugin } from '@volar/language-server/node';
import * as vue from '@volar/vue-language-core';

const plugin: LanguageServerPlugin<LanguageServerInitializationOptions, vue.LanguageServiceHost> = () => {
const plugin: LanguageServerPlugin<LanguageServerInitializationOptions, vue.VueLanguageServiceHost> = () => {
return {
extraFileExtensions: [
{ extension: 'vue', isMixedContent: true, scriptKind: 7 },
Expand Down
3 changes: 2 additions & 1 deletion packages/language-server/src/common/project.ts
Expand Up @@ -85,7 +85,7 @@ export async function createProject(context: ProjectContext) {
getPlugins() {
return [
...context.serverConfig?.plugins ?? [],
...context.workspace.workspaces.plugins.map(plugin => plugin.getServicePlugins?.(languageServiceHost, languageService!) ?? []).flat(),
...context.workspace.workspaces.plugins.map(plugin => plugin.getServicePlugins?.(languageServiceHost, languageServiceContext) ?? []).flat(),
];
},
env: {
Expand All @@ -105,6 +105,7 @@ export async function createProject(context: ProjectContext) {
},
},
documentRegistry: context.documentRegistry,
getLanguageService: () => languageService!,
});
languageService = embeddedLS.createLanguageService(languageServiceContext);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/language-server/src/common/workspace.ts
Expand Up @@ -131,7 +131,7 @@ export async function createWorkspace(context: WorkspaceContext) {
return findTsconfig(async tsconfig => {
const project = await projects.pathGet(tsconfig);
const ls = project?.getLanguageServiceDontCreate();
const validDoc = ls?.context.pluginContext.typescript?.languageService.getProgram()?.getSourceFile(shared.getPathOfUri(uri.toString()));
const validDoc = ls?.context.typescript?.languageService.getProgram()?.getSourceFile(shared.getPathOfUri(uri.toString()));
return !!validDoc;
});
}
Expand Down
3 changes: 2 additions & 1 deletion packages/language-server/src/types.ts
Expand Up @@ -4,6 +4,7 @@ import type { FileSystemProvider } from 'vscode-html-languageservice';
import type * as ts from 'typescript/lib/tsserverlibrary';
import * as vscode from 'vscode-languageserver';
import { URI } from 'vscode-uri';
import { LanguageServiceRuntimeContext } from '@volar/language-service';

export type FileSystemHost = {
ready(connection: vscode.Connection): void,
Expand Down Expand Up @@ -54,7 +55,7 @@ export type LanguageServerPlugin<

getServicePlugins?(
host: B,
service: embeddedLS.LanguageService,
context: LanguageServiceRuntimeContext,
): embeddedLS.LanguageServicePlugin[];

onInitialize?(
Expand Down
29 changes: 14 additions & 15 deletions packages/language-service/src/baseLanguageService.ts
Expand Up @@ -26,7 +26,7 @@ import * as renamePrepare from './languageFeatures/renamePrepare';
import * as signatureHelp from './languageFeatures/signatureHelp';
import * as diagnostics from './languageFeatures/validation';
import * as workspaceSymbol from './languageFeatures/workspaceSymbols';
import { LanguageServicePlugin, LanguageServicePluginInstance, LanguageServicePluginContext, LanguageServiceRuntimeContext } from './types';
import { LanguageServicePlugin, LanguageServicePluginInstance, LanguageServiceRuntimeContext } from './types';
import type * as ts from 'typescript/lib/tsserverlibrary';

import * as colorPresentations from './documentFeatures/colorPresentations';
Expand All @@ -46,8 +46,9 @@ export function createLanguageServiceContext(options: {
host: LanguageServiceHost,
context: ReturnType<typeof createLanguageContext>,
getPlugins(): (LanguageServicePlugin | LanguageServicePluginInstance)[],
env: LanguageServicePluginContext['env'];
env: LanguageServiceRuntimeContext['env'];
documentRegistry: ts.DocumentRegistry | undefined,
getLanguageService: () => LanguageService,
}) {

const ts = options.host.getTypeScriptModule();
Expand All @@ -59,37 +60,35 @@ export function createLanguageServiceContext(options: {

let plugins: LanguageServicePluginInstance[] | undefined;

const pluginContext: LanguageServicePluginContext = {
env: options.env,
typescript: ts && tsLs ? {
module: ts,
languageServiceHost: options.context.typescript.languageServiceHost,
languageService: tsLs,
} : undefined,
};
const textDocumentMapper = createDocumentsAndSourceMaps(options.context.virtualFiles);
const documents = new WeakMap<ts.IScriptSnapshot, TextDocument>();
const documentVersions = new Map<string, number>();
const context: LanguageServiceRuntimeContext = {
host: options.host,
core: options.context,
env: options.env,
get plugins() {
if (!plugins) {
plugins = []; // avoid infinite loop
plugins = options.getPlugins().map(plugin => {
if (plugin instanceof Function) {
const _plugin = plugin(pluginContext);
_plugin.setup?.(pluginContext);
const _plugin = plugin(this, options.getLanguageService());
_plugin.setup?.(this);
return _plugin;
}
else {
plugin.setup?.(pluginContext);
plugin.setup?.(this);
return plugin;
}
});
}
return plugins;
},
pluginContext,
typescript: ts && tsLs ? {
module: ts,
languageServiceHost: options.context.typescript.languageServiceHost,
languageService: tsLs,
} : undefined,
documents: textDocumentMapper,
getTextDocument,
};
Expand Down Expand Up @@ -162,7 +161,7 @@ export function createLanguageService(context: LanguageServiceRuntimeContext) {
doExecuteCommand: executeCommand.register(context),
getInlayHints: inlayHints.register(context),
callHierarchy: callHierarchy.register(context),
dispose: () => context.pluginContext.typescript?.languageService.dispose(),
dispose: () => context.typescript?.languageService.dispose(),
context,
};
}
6 changes: 3 additions & 3 deletions packages/language-service/src/documentFeatures/format.ts
Expand Up @@ -33,7 +33,7 @@ export function register(context: LanguageServiceRuntimeContext) {
const originalSnapshot = source[0];
const rootVirtualFile = source[1];
const originalDocument = document;
const initialIndentLanguageId = await context.pluginContext.env.configurationHost?.getConfiguration<Record<string, boolean>>('volar.format.initialIndent') ?? { html: true };
const initialIndentLanguageId = await context.env.configurationHost?.getConfiguration<Record<string, boolean>>('volar.format.initialIndent') ?? { html: true };

let level = 0;
let edited = false;
Expand Down Expand Up @@ -229,10 +229,10 @@ export function register(context: LanguageServiceRuntimeContext) {
let edits: vscode.TextEdit[] | null | undefined;
let recover: (() => void) | undefined;

if (formatDocument !== document && isTsDocument(formatDocument) && context.pluginContext.typescript) {
if (formatDocument !== document && isTsDocument(formatDocument) && context.typescript) {
const formatFileName = shared.getPathOfUri(formatDocument.uri);
const formatSnapshot = stringToSnapshot(formatDocument.getText());
const host = context.pluginContext.typescript.languageServiceHost;
const host = context.typescript.languageServiceHost;
const original = {
getProjectVersion: host.getProjectVersion,
getScriptVersion: host.getScriptVersion,
Expand Down
19 changes: 8 additions & 11 deletions packages/language-service/src/types.ts
Expand Up @@ -6,19 +6,15 @@ import type * as vscode from 'vscode-languageserver-protocol';
import type { TextDocument } from 'vscode-languageserver-textdocument';
import { URI } from 'vscode-uri';
import { DocumentsAndSourceMaps } from './documents';
import { LanguageService } from '@volar/language-service';

export * from 'vscode-languageserver-protocol';

export interface LanguageServiceRuntimeContext {
host: LanguageServiceHost;
export interface LanguageServiceRuntimeContext<Host extends LanguageServiceHost = LanguageServiceHost> {
host: Host;
core: LanguageContext;
documents: DocumentsAndSourceMaps;
plugins: LanguageServicePluginInstance[];
pluginContext: LanguageServicePluginContext;
getTextDocument(uri: string): TextDocument | undefined;
};

export interface LanguageServicePluginContext {
typescript: {
module: typeof import('typescript/lib/tsserverlibrary');
languageServiceHost: ts.LanguageServiceHost;
Expand All @@ -30,8 +26,9 @@ export interface LanguageServicePluginContext {
documentContext?: DocumentContext;
fileSystemProvider?: FileSystemProvider;
schemaRequestService?: SchemaRequestService;
},
}
};
getTextDocument(uri: string): TextDocument | undefined;
};

export interface ConfigurationHost {
getConfiguration: (<T> (section: string, scopeUri?: string) => Promise<T | undefined>),
Expand Down Expand Up @@ -63,11 +60,11 @@ export interface ExecuteCommandContext {
applyEdit(paramOrEdit: vscode.ApplyWorkspaceEditParams | vscode.WorkspaceEdit): Promise<vscode.ApplyWorkspaceEditResult>;
}

export type LanguageServicePlugin<T = {}> = ((context: LanguageServicePluginContext) => LanguageServicePluginInstance & T);
export type LanguageServicePlugin<T = {}> = ((context: LanguageServiceRuntimeContext, service: LanguageService) => LanguageServicePluginInstance & T);

export interface LanguageServicePluginInstance {

setup?(context: LanguageServicePluginContext): void;
setup?(context: LanguageServiceRuntimeContext): void;

validation?: {
onSemantic?(document: TextDocument): NullableResult<vscode.Diagnostic[]>;
Expand Down
4 changes: 2 additions & 2 deletions plugins/pug/src/index.ts
Expand Up @@ -9,10 +9,10 @@ const plugin: LanguageServicePlugin<{
updateCustomData(extraData: html.IHTMLDataProvider[]): void,
getPugLs: () => pug.LanguageService,
getPugDocument: (document: TextDocument) => pug.PugDocument | undefined,
}> = (context) => {
}> = (context, service) => {

const pugDocuments = new WeakMap<TextDocument, [number, pug.PugDocument]>();
const htmlPlugin = useHtmlPlugin()(context);
const htmlPlugin = useHtmlPlugin()(context, service);
const pugLs = pug.getLanguageService(htmlPlugin.getHtmlLs());

return {
Expand Down
2 changes: 1 addition & 1 deletion vue-language-tools/typescript-vue-plugin/src/index.ts
Expand Up @@ -37,7 +37,7 @@ const init: ts.server.PluginModuleFactory = (modules) => {
return info.project.__vue_getScriptKind(fileName);
};

const vueTsLsHost: vue.LanguageServiceHost = {
const vueTsLsHost: vue.VueLanguageServiceHost = {
getNewLine: () => info.project.getNewLine(),
useCaseSensitiveFileNames: () => info.project.useCaseSensitiveFileNames(),
readFile: path => info.project.readFile(path),
Expand Down
8 changes: 4 additions & 4 deletions vue-language-tools/vue-component-meta/src/index.ts
Expand Up @@ -77,7 +77,7 @@ function createComponentMetaCheckerWorker(

const scriptSnapshots = new Map<string, ts.IScriptSnapshot>();
const scriptVersions = new Map<string, number>();
const _host: vue.LanguageServiceHost = {
const _host: vue.VueLanguageServiceHost = {
...ts.sys,
getProjectVersion: () => projectVersion.toString(),
getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options), // should use ts.getDefaultLibFilePath not ts.getDefaultLibFileName
Expand Down Expand Up @@ -126,7 +126,7 @@ function createComponentMetaCheckerWorker(
}

export function baseCreate(
_host: vue.LanguageServiceHost,
_host: vue.VueLanguageServiceHost,
checkerOptions: MetaCheckerOptions,
globalComponentName: string,
ts: typeof import('typescript/lib/tsserverlibrary'),
Expand All @@ -136,7 +136,7 @@ export function baseCreate(
*/
const globalComponentSnapshot = ts.ScriptSnapshot.fromString('<script setup lang="ts"></script>');
const metaSnapshots: Record<string, ts.IScriptSnapshot> = {};
const host = new Proxy<Partial<vue.LanguageServiceHost>>({
const host = new Proxy<Partial<vue.VueLanguageServiceHost>>({
getScriptFileNames: () => {
const names = _host.getScriptFileNames();
return [
Expand Down Expand Up @@ -167,7 +167,7 @@ export function baseCreate(
}
return _host[prop as keyof typeof _host];
},
}) as vue.LanguageServiceHost;
}) as vue.VueLanguageServiceHost;
const vueLanguageModules = ts ? vue.createLanguageModules(
ts,
host.getCompilationSettings(),
Expand Down
2 changes: 1 addition & 1 deletion vue-language-tools/vue-language-core/src/types.ts
Expand Up @@ -7,7 +7,7 @@ import { VueEmbeddedFile } from './sourceFile';

export type { SFCParseResult } from '@vue/compiler-sfc';

export type LanguageServiceHost = embedded.LanguageServiceHost & {
export type VueLanguageServiceHost = embedded.LanguageServiceHost & {
getVueCompilationSettings(): VueCompilerOptions,
};

Expand Down

0 comments on commit 4820a7c

Please sign in to comment.