Skip to content

Commit

Permalink
refactor: move common TS logic to new package @volar/vue-typescript (
Browse files Browse the repository at this point in the history
…#1004)

* refactor: ignore css stylesheet from imports

* refactor: get stylesheet on-demand and move out from source map

* refactor: get html document on-demand and move out from source map

* refactor: get json document on-demand and move out from source map

* refactor: get pug document on-demand and move out from source map

* refactor: reduce sub-types of source maps

* refactor: calculate css class names on-demand

* refactor: reduce depends pass

* refactor: split context type

* refactor: dependency injection for SFC template compile

* refactor: remove context depend in vue document

* refactor: rename runtime context type name

* refactor: split ts plugin / ts program proxy logic

* refactor: move ts plugin / ts program project logic into vue-tsc and typescript-vue-plugin

* refactor: remove basic TS logic to `@volar/vue-typescript`

* fix: project references missing
  • Loading branch information
johnsoncodehk committed Mar 2, 2022
1 parent edcff74 commit 01adff9
Show file tree
Hide file tree
Showing 82 changed files with 1,741 additions and 1,555 deletions.
4 changes: 2 additions & 2 deletions packages/server/src/commands/unuseSetupSugar.ts
Expand Up @@ -68,8 +68,8 @@ export async function execute(
_scriptSetupAst: NonNullable<typeof scriptSetupAst>,
) {

const ranges = parseUnuseScriptSetupRanges(vueLs.__internal__.context.modules.typescript, _scriptSetupAst);
const scriptRanges = _scriptAst ? parseUseScriptSetupRanges(vueLs.__internal__.context.modules.typescript, _scriptAst) : undefined;
const ranges = parseUnuseScriptSetupRanges(vueLs.__internal__.context.typescript, _scriptSetupAst);
const scriptRanges = _scriptAst ? parseUseScriptSetupRanges(vueLs.__internal__.context.typescript, _scriptAst) : undefined;

const document = _sourceFile.getTextDocument();
const edits: vscode.TextEdit[] = [];
Expand Down
6 changes: 3 additions & 3 deletions packages/server/src/commands/useRefSugar.ts
Expand Up @@ -38,8 +38,8 @@ export async function execute(
_scriptSetupAst: NonNullable<typeof scriptSetupAst>,
) {

const ranges = parseDeclarationRanges(vueLs.__internal__.context.modules.typescript, _scriptSetupAst);
const dotValueRanges = parseDotValueRanges(vueLs.__internal__.context.modules.typescript, _scriptSetupAst);
const ranges = parseDeclarationRanges(vueLs.__internal__.context.typescript, _scriptSetupAst);
const dotValueRanges = parseDotValueRanges(vueLs.__internal__.context.typescript, _scriptSetupAst);
const document = _sourceFile.getTextDocument();
const edits: vscode.TextEdit[] = [];

Expand Down Expand Up @@ -73,7 +73,7 @@ export async function execute(
if (end < _scriptSetup.startTagEnd || start > _scriptSetup.startTagEnd + _scriptSetup.content.length)
return false;

if (vue.isBlacklistNode(vueLs.__internal__.context.modules.typescript, _scriptSetupAst, start - _scriptSetup.startTagEnd))
if (vue.isBlacklistNode(vueLs.__internal__.context.typescript, _scriptSetupAst, start - _scriptSetup.startTagEnd))
return false;

return true;
Expand Down
2 changes: 1 addition & 1 deletion packages/server/src/commands/useSetupSugar.ts
Expand Up @@ -64,7 +64,7 @@ export async function execute(
_scriptAst: NonNullable<typeof scriptAst>,
) {

const ranges = parseUseScriptSetupRanges(vueLs.__internal__.context.modules.typescript, _scriptAst);
const ranges = parseUseScriptSetupRanges(vueLs.__internal__.context.typescript, _scriptAst);
const document = _sourceFile.getTextDocument();
const edits: vscode.TextEdit[] = [];
const scriptStartPos = document.positionAt(_script.startTagEnd);
Expand Down
2 changes: 1 addition & 1 deletion packages/server/src/common.ts
Expand Up @@ -62,7 +62,7 @@ export function createLanguageServer(connection: vscode.Connection, runtimeEnv:

const ts = runtimeEnv.loadTypescript(options);
const formatters = await import('./formatters');
const noStateLs = vue.getDocumentLanguageService(
const noStateLs = vue.getDocumentService(
{ typescript: ts },
(document) => tsConfigs.getPreferences(configuration, document),
(document, options) => tsConfigs.getFormatOptions(configuration, document, options),
Expand Down
2 changes: 1 addition & 1 deletion packages/server/src/features/customFeatures.ts
Expand Up @@ -34,7 +34,7 @@ export function register(
for (const project of [...workspace.projects.values(), workspace.getInferredProjectDontCreate()].filter(shared.notEmpty)) {
const ls = await (await project).getLanguageServiceDontCreate();
if (!ls) continue;
const localTypes = ls.__internal__.getLocalTypesFiles(lsType);
const localTypes = ls.__internal__.tsRuntime.getLocalTypesFiles(lsType);
for (const fileName of localTypes.fileNames) {
connection.workspace.applyEdit({
edit: {
Expand Down
2 changes: 1 addition & 1 deletion packages/server/src/features/documentFeatures.ts
Expand Up @@ -6,7 +6,7 @@ import * as vue from 'vscode-vue-languageservice';
export function register(
connection: vscode.Connection,
documents: vscode.TextDocuments<TextDocument>,
noStateLs: vue.DocumentLanguageService,
noStateLs: vue.DocumentService,
) {
connection.onDocumentFormatting(handler => {
const document = documents.get(handler.textDocument.uri);
Expand Down
56 changes: 17 additions & 39 deletions packages/server/src/features/languageFeatures.ts
Expand Up @@ -17,7 +17,7 @@ import * as useRefSugar from '../commands/useRefSugar';
import * as unuseRefSugar from '../commands/unuseRefSugar';

export function register(
ts: vue.Modules['typescript'],
ts: typeof import('typescript/lib/tsserverlibrary'),
connection: vscode.Connection,
configuration: Configuration | undefined,
documents: vscode.TextDocuments<TextDocument>,
Expand Down Expand Up @@ -97,8 +97,7 @@ export function register(
result = result.concat(referencesCodeLens);
}
if (options?.pugTool) {
result = result.concat(getHtmlResult(sourceFile));
result = result.concat(getPugResult(sourceFile));
result = result.concat(getHtmlPugResult(sourceFile));
}
if (options?.scriptSetupTool) {
result = result.concat(getScriptSetupConvertConvert(sourceFile));
Expand Down Expand Up @@ -162,48 +161,27 @@ export function register(
}
return result;
}
function getHtmlResult(sourceFile: vue.SourceFile) {
const sourceMaps = sourceFile.getHtmlSourceMaps();
function getHtmlPugResult(sourceFile: vue.SourceFile) {
const sourceMaps = sourceFile.getTemplateSourceMaps();
for (const sourceMap of sourceMaps) {
for (const maped of sourceMap.mappings) {
return getPugHtmlConvertCodeLens(
'html',
{
start: sourceMap.sourceDocument.positionAt(maped.sourceRange.start),
end: sourceMap.sourceDocument.positionAt(maped.sourceRange.start),
},
);
}
}
return [];
}
function getPugResult(sourceFile: vue.SourceFile) {
const sourceMaps = sourceFile.getPugSourceMaps();
for (const sourceMap of sourceMaps) {
for (const maped of sourceMap.mappings) {
return getPugHtmlConvertCodeLens(
'pug',
{
start: sourceMap.sourceDocument.positionAt(maped.sourceRange.start),
end: sourceMap.sourceDocument.positionAt(maped.sourceRange.start),
},
);
if (sourceMap.mappedDocument.languageId === 'html' || sourceMap.mappedDocument.languageId === 'jade') {
return [{
range: {
start: sourceMap.sourceDocument.positionAt(maped.sourceRange.start),
end: sourceMap.sourceDocument.positionAt(maped.sourceRange.start),
},
command: {
title: 'pug ' + (sourceMap.mappedDocument.languageId === 'jade' ? '☑' : '☐'),
command: sourceMap.mappedDocument.languageId === 'jade' ? Commands.PUG_TO_HTML : Commands.HTML_TO_PUG,
arguments: [handler.textDocument.uri],
},
}];
}
}
}
return [];
}
function getPugHtmlConvertCodeLens(current: 'html' | 'pug', range: vscode.Range) {
const result: vscode.CodeLens[] = [];
result.push({
range,
command: {
title: 'pug ' + (current === 'pug' ? '☑' : '☐'),
command: current === 'pug' ? Commands.PUG_TO_HTML : Commands.HTML_TO_PUG,
arguments: [handler.textDocument.uri],
},
});
return result;
}
});
connection.onCodeLensResolve(async codeLens => {
const uri = (codeLens.data as any)?.uri as string | undefined; // TODO
Expand Down
6 changes: 3 additions & 3 deletions packages/server/src/project.ts
Expand Up @@ -14,7 +14,7 @@ export const renameFileContentCache = new Map<string, string>();

export async function createProject(
runtimeEnv: RuntimeEnvironment,
ts: vue.Modules['typescript'],
ts: typeof import('typescript/lib/tsserverlibrary'),
options: shared.ServerInitializationOptions,
rootPath: string,
tsConfig: string | ts.CompilerOptions,
Expand Down Expand Up @@ -85,7 +85,7 @@ export async function createProject(
vueLs = (async () => {
const workDoneProgress = await connection.window.createWorkDoneProgress();
const vueLs = vue.createLanguageService({ typescript: ts }, languageServiceHost);
vueLs.__internal__.onInitProgress(p => {
vueLs.__internal__.tsRuntime.onInitProgress(p => {
if (p === 0) {
workDoneProgress.begin(getMessageText());
}
Expand Down Expand Up @@ -287,7 +287,7 @@ export async function createProject(
export function getScriptText(
documents: vscode.TextDocuments<TextDocument>,
fileName: string,
sys: vue.Modules['typescript']['sys'],
sys: typeof import('typescript/lib/tsserverlibrary')['sys'],
) {
const uri = shared.fsPathToUri(fileName);
const doc = getDocumentSafely(documents, uri);
Expand Down
11 changes: 10 additions & 1 deletion packages/server/tsconfig.build.json
Expand Up @@ -12,8 +12,17 @@
".vscode-test"
],
"references": [
{
"path": "../html2pug/tsconfig.build.json"
},
{
"path": "../shared/tsconfig.build.json"
},
{
"path": "../vue-code-gen/tsconfig.build.json"
},
{
"path": "../vscode-vue-languageservice/tsconfig.build.json"
}
},
]
}
4 changes: 2 additions & 2 deletions packages/typescript-vue-plugin/package.json
Expand Up @@ -17,7 +17,7 @@
},
"dependencies": {
"@volar/shared": "0.32.1",
"upath": "^2.0.1",
"vscode-vue-languageservice": "0.32.1"
"@volar/vue-typescript": "0.32.1",
"upath": "^2.0.1"
}
}
@@ -1,8 +1,8 @@
import type { ApiLanguageServiceContext } from './types';
import type { TypeScriptFeaturesRuntimeContext } from '@volar/vue-typescript';
import type * as ts from 'typescript/lib/tsserverlibrary';
import * as shared from '@volar/shared';

export function register({ sourceFiles, scriptTsLsRaw, templateTsLsRaw }: ApiLanguageServiceContext) {
export function register({ sourceFiles, scriptTsLsRaw, templateTsLsRaw }: TypeScriptFeaturesRuntimeContext) {

return {
getCompletionsAtPosition,
Expand Down
41 changes: 37 additions & 4 deletions packages/typescript-vue-plugin/src/index.ts
@@ -1,6 +1,7 @@
import * as vue from 'vscode-vue-languageservice'
import * as vue from '@volar/vue-typescript';
import * as shared from '@volar/shared';
import * as path from 'upath';
import * as apis from './apis';

const init: ts.server.PluginModuleFactory = (modules) => {
const { typescript: ts } = modules;
Expand All @@ -22,13 +23,45 @@ const init: ts.server.PluginModuleFactory = (modules) => {
};

const proxyHost = createProxyHost(ts, info);
const vueLs = vue.createLanguageService(modules, proxyHost.host, true);
const tsRuntime = vue.createTypeScriptRuntime({ typescript: ts }, proxyHost.host, true);
const _tsPluginApis = apis.register(tsRuntime.context);
const tsPluginProxy: Partial<ts.LanguageService> = {
getSemanticDiagnostics: tsRuntime.apiHook(tsRuntime.context.scriptTsLsRaw.getSemanticDiagnostics, false),
getEncodedSemanticClassifications: tsRuntime.apiHook(tsRuntime.context.scriptTsLsRaw.getEncodedSemanticClassifications, false),
getCompletionsAtPosition: tsRuntime.apiHook(_tsPluginApis.getCompletionsAtPosition, false),
getCompletionEntryDetails: tsRuntime.apiHook(tsRuntime.context.scriptTsLsRaw.getCompletionEntryDetails, false), // not sure
getCompletionEntrySymbol: tsRuntime.apiHook(tsRuntime.context.scriptTsLsRaw.getCompletionEntrySymbol, false), // not sure
getQuickInfoAtPosition: tsRuntime.apiHook(tsRuntime.context.scriptTsLsRaw.getQuickInfoAtPosition, false),
getSignatureHelpItems: tsRuntime.apiHook(tsRuntime.context.scriptTsLsRaw.getSignatureHelpItems, false),
getRenameInfo: tsRuntime.apiHook(tsRuntime.context.scriptTsLsRaw.getRenameInfo, false),

findRenameLocations: tsRuntime.apiHook(_tsPluginApis.findRenameLocations, true),
getDefinitionAtPosition: tsRuntime.apiHook(_tsPluginApis.getDefinitionAtPosition, false),
getDefinitionAndBoundSpan: tsRuntime.apiHook(_tsPluginApis.getDefinitionAndBoundSpan, false),
getTypeDefinitionAtPosition: tsRuntime.apiHook(_tsPluginApis.getTypeDefinitionAtPosition, false),
getImplementationAtPosition: tsRuntime.apiHook(_tsPluginApis.getImplementationAtPosition, false),
getReferencesAtPosition: tsRuntime.apiHook(_tsPluginApis.getReferencesAtPosition, true),
findReferences: tsRuntime.apiHook(_tsPluginApis.findReferences, true),

// TODO: now is handle by vue server
// prepareCallHierarchy: tsRuntime.apiHook(tsLanguageService.rawLs.prepareCallHierarchy, false),
// provideCallHierarchyIncomingCalls: tsRuntime.apiHook(tsLanguageService.rawLs.provideCallHierarchyIncomingCalls, false),
// provideCallHierarchyOutgoingCalls: tsRuntime.apiHook(tsLanguageService.rawLs.provideCallHierarchyOutgoingCalls, false),
// getEditsForFileRename: tsRuntime.apiHook(tsLanguageService.rawLs.getEditsForFileRename, false),

// TODO
// getCodeFixesAtPosition: tsRuntime.apiHook(tsLanguageService.rawLs.getCodeFixesAtPosition, false),
// getCombinedCodeFix: tsRuntime.apiHook(tsLanguageService.rawLs.getCombinedCodeFix, false),
// applyCodeActionCommand: tsRuntime.apiHook(tsLanguageService.rawLs.applyCodeActionCommand, false),
// getApplicableRefactors: tsRuntime.apiHook(tsLanguageService.rawLs.getApplicableRefactors, false),
// getEditsForRefactor: tsRuntime.apiHook(tsLanguageService.rawLs.getEditsForRefactor, false),
};

vueFilesGetter.set(info.project, proxyHost.getVueFiles);

return new Proxy(info.languageService, {
get: (target: any, property: keyof ts.LanguageService) => {
return vueLs.__internal__.tsPlugin[property] || target[property];
return tsPluginProxy[property] || target[property];
},
});
},
Expand Down Expand Up @@ -58,7 +91,7 @@ function createProxyHost(ts: typeof import('typescript/lib/tsserverlibrary'), in
snapshots: ts.IScriptSnapshot | undefined,
snapshotsVersion: string | undefined,
}>();
const host: vue.LanguageServiceHost = {
const host: vue.LanguageServiceHostBase = {
getNewLine: () => info.project.getNewLine(),
useCaseSensitiveFileNames: () => info.project.useCaseSensitiveFileNames(),
readFile: path => info.project.readFile(path),
Expand Down
2 changes: 1 addition & 1 deletion packages/typescript-vue-plugin/tsconfig.build.json
Expand Up @@ -16,7 +16,7 @@
"path": "../shared/tsconfig.build.json"
},
{
"path": "../vscode-vue-languageservice/tsconfig.build.json"
"path": "../vue-typescript/tsconfig.build.json"
},
],
}
3 changes: 1 addition & 2 deletions packages/vscode-vue-languageservice/package.json
Expand Up @@ -16,12 +16,11 @@
"typescript": "latest"
},
"dependencies": {
"@volar/code-gen": "0.32.1",
"@volar/html2pug": "0.32.1",
"@volar/shared": "0.32.1",
"@volar/source-map": "0.32.1",
"@volar/transforms": "0.32.1",
"@volar/vue-code-gen": "0.32.1",
"@volar/vue-typescript": "0.32.1",
"@vscode/emmet-helper": "^2.8.3",
"@vue/reactivity": "^3.2.27",
"@vue/shared": "^3.2.27",
Expand Down
77 changes: 77 additions & 0 deletions packages/vscode-vue-languageservice/src/documentService.ts
@@ -0,0 +1,77 @@
import { TextDocument } from 'vscode-languageserver-textdocument';
import * as autoClosingTags from './services/autoClosingTags';
import * as autoCreateQuotes from './services/autoCreateQuotes';
import * as autoWrapBrackets from './services/autoWrapParentheses';
import * as colorPresentations from './services/colorPresentation';
import * as documentColor from './services/documentColor';
import * as documentSymbol from './services/documentSymbol';
import * as foldingRanges from './services/foldingRanges';
import * as formatting from './services/formatting';
import * as linkedEditingRanges from './services/linkedEditingRange';
import * as selectionRanges from './services/selectionRanges';
import { createSourceFile, SourceFile } from '@volar/vue-typescript';
import { DocumentServiceRuntimeContext, LanguageServiceHost } from './types';
import { createBasicRuntime } from '@volar/vue-typescript';

import type * as _0 from 'vscode-languageserver-protocol';

export interface DocumentService extends ReturnType<typeof getDocumentService> { }

export function getDocumentService(
{ typescript: ts }: { typescript: typeof import('typescript/lib/tsserverlibrary') },
getPreferences: LanguageServiceHost['getPreferences'],
getFormatOptions: LanguageServiceHost['getFormatOptions'],
formatters: Parameters<typeof formatting['register']>[3],
) {
const vueDocuments = new WeakMap<TextDocument, SourceFile>();
const services = createBasicRuntime();
const context: DocumentServiceRuntimeContext = {
compilerOptions: {},
typescript: ts,
...services,
getVueDocument,
};
return {
doFormatting: formatting.register(context, getPreferences, getFormatOptions, formatters),
getFoldingRanges: foldingRanges.register(context, getPreferences, getFormatOptions),
getSelectionRanges: selectionRanges.register(context, getPreferences, getFormatOptions),
doQuoteComplete: autoCreateQuotes.register(context),
doTagComplete: autoClosingTags.register(context),
doParentheseWrap: autoWrapBrackets.register(context),
findLinkedEditingRanges: linkedEditingRanges.register(context),
findDocumentSymbols: documentSymbol.register(context, getPreferences, getFormatOptions),
findDocumentColors: documentColor.register(context),
getColorPresentations: colorPresentations.register(context),
}
function getVueDocument(document: TextDocument) {

if (document.languageId !== 'vue')
return;

const cacheVueDoc = vueDocuments.get(document);
if (cacheVueDoc) {

const oldText = cacheVueDoc.getTextDocument().getText();
const newText = document.getText();

if (oldText.length !== newText.length || oldText !== newText) {
cacheVueDoc.update(document.getText(), document.version.toString());
}

return cacheVueDoc;
}
const vueDoc = createSourceFile(
document.uri,
document.getText(),
document.version.toString(),
context.htmlLs,
context.compileTemplate,
context.compilerOptions,
context.typescript,
context.getCssVBindRanges,
context.getCssClasses,
);
vueDocuments.set(document, vueDoc);
return vueDoc;
}
}
4 changes: 2 additions & 2 deletions packages/vscode-vue-languageservice/src/index.ts
Expand Up @@ -2,7 +2,7 @@ export { getSemanticTokenLegend } from './services/semanticTokens';
export { getTriggerCharacters } from './services/completion';
export { margeWorkspaceEdits } from './services/rename';
export { isRefType, isBlacklistNode } from './services/refAutoClose';
export * from './utils/sourceMaps';
export * from './documentService';
export * from './languageService';
export * from './sourceFile';
export * from './types';
export * from '@volar/vue-typescript';

0 comments on commit 01adff9

Please sign in to comment.