Skip to content

Commit

Permalink
feat: remove update template context logic
Browse files Browse the repository at this point in the history
close #455, close #565
  • Loading branch information
johnsoncodehk committed Apr 9, 2022
1 parent 2e1ff3d commit bd53809
Show file tree
Hide file tree
Showing 21 changed files with 340 additions and 463 deletions.
4 changes: 0 additions & 4 deletions extensions/vscode-vue-language-features/README.md
Expand Up @@ -104,10 +104,6 @@ export {}

</details>

## Limitations

- Due to performance, *.ts content update don't update template diagnosis for now. ([#565](https://github.com/johnsoncodehk/volar/issues/565)) (Block by [microsoft/TypeScript#41051](https://github.com/microsoft/TypeScript/issues/41051))

## Notes

### Vetur
Expand Down
3 changes: 1 addition & 2 deletions extensions/vscode-vue-language-features/src/common.ts
Expand Up @@ -137,7 +137,7 @@ async function doActivate(context: vscode.ExtensionContext, createLc: CreateLang
vscode.workspace.onDidChangeConfiguration(async () => {
const nowUseSecondServer = useSecondServer();
if (_useSecondServer !== nowUseSecondServer) {
const reload = await vscode.window.showInformationMessage('Please reload VSCode to switch low power mode.', 'Reload Window');
const reload = await vscode.window.showInformationMessage('Please reload VSCode to restart language servers.', 'Reload Window');
if (reload === undefined) return; // cancel
vscode.commands.executeCommand('workbench.action.reloadWindow');
}
Expand Down Expand Up @@ -240,7 +240,6 @@ function getInitializationOptions(
documentColor: true,
documentFormatting: true,
} : undefined,
initializationMessage: initMessage,
};
return initializationOptions;
}
1 change: 0 additions & 1 deletion packages/shared/src/types.ts
Expand Up @@ -81,5 +81,4 @@ export interface ServerInitializationOptions {
documentColor?: boolean
documentFormatting?: boolean,
}
initializationMessage?: string
}
34 changes: 16 additions & 18 deletions packages/typescript-vue-plugin/src/index.ts
Expand Up @@ -34,22 +34,22 @@ const init: ts.server.PluginModuleFactory = (modules) => {
});
const _tsPluginApis = apis.register(tsRuntime);
const tsPluginProxy: Partial<ts.LanguageService> = {
getSemanticDiagnostics: apiHook(tsRuntime.getTsLs().getSemanticDiagnostics, false),
getEncodedSemanticClassifications: apiHook(tsRuntime.getTsLs().getEncodedSemanticClassifications, false),
getCompletionsAtPosition: apiHook(_tsPluginApis.getCompletionsAtPosition, false),
getCompletionEntryDetails: apiHook(tsRuntime.getTsLs().getCompletionEntryDetails, false), // not sure
getCompletionEntrySymbol: apiHook(tsRuntime.getTsLs().getCompletionEntrySymbol, false), // not sure
getQuickInfoAtPosition: apiHook(tsRuntime.getTsLs().getQuickInfoAtPosition, false),
getSignatureHelpItems: apiHook(tsRuntime.getTsLs().getSignatureHelpItems, false),
getRenameInfo: apiHook(tsRuntime.getTsLs().getRenameInfo, false),
getSemanticDiagnostics: apiHook(tsRuntime.getTsLs().getSemanticDiagnostics),
getEncodedSemanticClassifications: apiHook(tsRuntime.getTsLs().getEncodedSemanticClassifications),
getCompletionsAtPosition: apiHook(_tsPluginApis.getCompletionsAtPosition),
getCompletionEntryDetails: apiHook(tsRuntime.getTsLs().getCompletionEntryDetails),
getCompletionEntrySymbol: apiHook(tsRuntime.getTsLs().getCompletionEntrySymbol),
getQuickInfoAtPosition: apiHook(tsRuntime.getTsLs().getQuickInfoAtPosition),
getSignatureHelpItems: apiHook(tsRuntime.getTsLs().getSignatureHelpItems),
getRenameInfo: apiHook(tsRuntime.getTsLs().getRenameInfo),

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

// TODO: now is handle by vue server
// prepareCallHierarchy: apiHook(tsLanguageService.rawLs.prepareCallHierarchy, false),
Expand All @@ -75,12 +75,10 @@ const init: ts.server.PluginModuleFactory = (modules) => {

function apiHook<T extends (...args: any) => any>(
api: T,
shouldUpdateTemplateScript: boolean | ((...args: Parameters<T>) => boolean) = true,
) {
const handler = {
apply(target: (...args: any) => any, thisArg: any, argumentsList: Parameters<T>) {
const _shouldUpdateTemplateScript = typeof shouldUpdateTemplateScript === 'boolean' ? shouldUpdateTemplateScript : shouldUpdateTemplateScript.apply(null, argumentsList);
tsRuntime.update(_shouldUpdateTemplateScript);
tsRuntime.update();
return target.apply(thisArg, argumentsList);
}
};
Expand Down
126 changes: 15 additions & 111 deletions packages/vue-code-gen/src/generators/template.ts
@@ -1,10 +1,10 @@
import * as SourceMaps from '@volar/source-map';
import { CodeGen } from '@volar/code-gen';
import { camelize, hyphenate, isHTMLTag, isSVGTag, isGloballyWhitelisted } from '@vue/shared';
import { camelize, hyphenate, isHTMLTag, isSVGTag } from '@vue/shared';
import * as CompilerDOM from '@vue/compiler-dom';
import * as CompilerCore from '@vue/compiler-core';
import { EmbeddedFileMappingData } from '../types';
import type * as ts from 'typescript/lib/tsserverlibrary';
import { colletVars, walkInterpolationFragment } from '../transform';

const capabilitiesSet = {
all: { basic: true, diagnostic: true, references: true, definitions: true, rename: true, completion: true, semanticTokens: true },
Expand Down Expand Up @@ -269,110 +269,6 @@ export function generate(
attrNames,
};

function walkInterpolationFragment(code: string, cb: (fragment: string, offset: number | undefined) => void) {

let ctxVarOffsets: number[] = [];
let localVarOffsets: number[] = [];

const ast = ts.createSourceFile('/foo.ts', code, ts.ScriptTarget.ESNext);
const varCb = (localVar: ts.Identifier) => {
if (!!localVars[localVar.text] || isGloballyWhitelisted(localVar.text)) {
localVarOffsets.push(localVar.getStart(ast));
}
else {
ctxVarOffsets.push(localVar.getStart(ast));
}
};
ast.forEachChild(node => walkIdentifiers(node, varCb));

ctxVarOffsets = ctxVarOffsets.sort((a, b) => a - b);
localVarOffsets = localVarOffsets.sort((a, b) => a - b);

if (ctxVarOffsets.length) {

cb(code.substring(0, ctxVarOffsets[0]), 0);

for (let i = 0; i < ctxVarOffsets.length - 1; i++) {
cb('__VLS_ctx.', undefined);
cb(code.substring(ctxVarOffsets[i], ctxVarOffsets[i + 1]), ctxVarOffsets[i]);
}

cb('__VLS_ctx.', undefined);
cb(code.substring(ctxVarOffsets[ctxVarOffsets.length - 1]), ctxVarOffsets[ctxVarOffsets.length - 1]);
}
else {
cb(code, 0);
}
}
function walkIdentifiers(node: ts.Node, cb: (varNode: ts.Identifier) => void) {

const blockVars: string[] = [];

if (ts.isIdentifier(node)) {
cb(node);
}
else if (ts.isPropertyAccessExpression(node)) {
walkIdentifiers(node.expression, cb);
}
else if (ts.isVariableDeclaration(node)) {

colletVars(node.name, blockVars);

for (const varName of blockVars)
localVars[varName] = (localVars[varName] ?? 0) + 1;

if (node.initializer)
walkIdentifiers(node.initializer, cb);
}
else if (ts.isArrowFunction(node)) {

const functionArgs: string[] = [];

for (const param of node.parameters)
colletVars(param.name, functionArgs);

for (const varName of functionArgs)
localVars[varName] = (localVars[varName] ?? 0) + 1;

walkIdentifiers(node.body, cb);

for (const varName of functionArgs)
localVars[varName]--;
}
else if (ts.isObjectLiteralExpression(node)) {
for (const prop of node.properties) {
if (ts.isPropertyAssignment(prop)) {
walkIdentifiers(prop.initializer, cb);
}
}
}
else {
node.forEachChild(node => walkIdentifiers(node, cb));
}

for (const varName of blockVars)
localVars[varName]--;
}
function colletVars(node: ts.Node, result: string[]) {
if (ts.isIdentifier(node)) {
result.push(node.text);
}
else if (ts.isObjectBindingPattern(node)) {
for (const el of node.elements) {
colletVars(el.name, result);
}
}
else if (ts.isArrayBindingPattern(node)) {
for (const el of node.elements) {
if (ts.isBindingElement(el)) {
colletVars(el.name, result);
}
}
}
else {
node.forEachChild(node => colletVars(node, result));
}
}
function collectTags(node: CompilerDOM.TemplateChildNode) {
if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
const patchForNode = getPatchForSlotNode(node);
Expand Down Expand Up @@ -551,7 +447,7 @@ export function generate(
if (leftExpressionRange && leftExpressionText) {

const collentAst = ts.createSourceFile('/foo.ts', `const [${leftExpressionText}]`, ts.ScriptTarget.ESNext);
colletVars(collentAst, forBlockVars);
colletVars(ts, collentAst, forBlockVars);

for (const varName of forBlockVars)
localVars[varName] = (localVars[varName] ?? 0) + 1;
Expand Down Expand Up @@ -1322,7 +1218,7 @@ export function generate(
tsCodeGen.addText(`const `);

const collentAst = ts.createSourceFile('/foo.ts', `const ${prop.exp.content}`, ts.ScriptTarget.ESNext);
colletVars(collentAst, slotBlockVars);
colletVars(ts, collentAst, slotBlockVars);

writeCode(
prop.exp.content,
Expand Down Expand Up @@ -1760,10 +1656,18 @@ export function generate(
}
}
function writeObjectProperty(mapCode: string, sourceRange: SourceMaps.Range, mapMode: SourceMaps.Mode, data: EmbeddedFileMappingData) {
if (validTsVar.test(mapCode) || (mapCode.startsWith('[') && mapCode.endsWith(']'))) {
if (validTsVar.test(mapCode)) {
writeCode(mapCode, sourceRange, mapMode, data);
return 1;
}
else if (mapCode.startsWith('[') && mapCode.endsWith(']')) {
writeInterpolation(
mapCode,
sourceRange.start,
data,
);
return 1;
}
else {
writeCodeWithQuotes(mapCode, sourceRange, data);
return 2;
Expand Down Expand Up @@ -1863,7 +1767,7 @@ export function generate(
prefix = '',
suffix = '',
) {
walkInterpolationFragment(prefix + mapCode + suffix, (frag, fragOffset) => {
walkInterpolationFragment(ts, prefix + mapCode + suffix, (frag, fragOffset) => {
if (fragOffset === undefined) {
tsCodeGen.addText(frag);
}
Expand Down Expand Up @@ -1891,7 +1795,7 @@ export function generate(
);
tsCodeGen.addText(addSubfix);
}
});
}, localVars);
}
function writeFormatCode(mapCode: string, sourceOffset: number, formatWrapper: [string, string]) {
tsFormatCodeGen.addText(formatWrapper[0]);
Expand Down
130 changes: 130 additions & 0 deletions packages/vue-code-gen/src/transform.ts
@@ -0,0 +1,130 @@
import { isGloballyWhitelisted } from '@vue/shared';
import type * as ts from 'typescript/lib/tsserverlibrary';

export function walkInterpolationFragment(
ts: typeof import('typescript/lib/tsserverlibrary'),
code: string,
cb: (fragment: string, offset: number | undefined) => void,
localVars: Record<string, number>,
) {

let ctxVarOffsets: number[] = [];
let localVarOffsets: number[] = [];

const ast = ts.createSourceFile('/foo.ts', code, ts.ScriptTarget.ESNext);
const varCb = (localVar: ts.Identifier) => {
if (
!!localVars[localVar.text] ||
isGloballyWhitelisted(localVar.text) ||
localVar.text === '$style'
) {
localVarOffsets.push(localVar.getStart(ast));
}
else {
ctxVarOffsets.push(localVar.getStart(ast));
}
};
ast.forEachChild(node => walkIdentifiers(ts, node, varCb, localVars));

ctxVarOffsets = ctxVarOffsets.sort((a, b) => a - b);
localVarOffsets = localVarOffsets.sort((a, b) => a - b);

if (ctxVarOffsets.length) {

cb(code.substring(0, ctxVarOffsets[0]), 0);

for (let i = 0; i < ctxVarOffsets.length - 1; i++) {
cb('__VLS_ctx.', undefined);
cb(code.substring(ctxVarOffsets[i], ctxVarOffsets[i + 1]), ctxVarOffsets[i]);
}

cb('__VLS_ctx.', undefined);
cb(code.substring(ctxVarOffsets[ctxVarOffsets.length - 1]), ctxVarOffsets[ctxVarOffsets.length - 1]);
}
else {
cb(code, 0);
}
}

function walkIdentifiers(
ts: typeof import('typescript/lib/tsserverlibrary'),
node: ts.Node,
cb: (varNode: ts.Identifier) => void,
localVars: Record<string, number>,
) {

const blockVars: string[] = [];

if (ts.isIdentifier(node)) {
cb(node);
}
else if (ts.isPropertyAccessExpression(node)) {
walkIdentifiers(ts, node.expression, cb, localVars);
}
else if (ts.isVariableDeclaration(node)) {

colletVars(ts, node.name, blockVars);

for (const varName of blockVars)
localVars[varName] = (localVars[varName] ?? 0) + 1;

if (node.initializer)
walkIdentifiers(ts, node.initializer, cb, localVars);
}
else if (ts.isArrowFunction(node)) {

const functionArgs: string[] = [];

for (const param of node.parameters)
colletVars(ts, param.name, functionArgs);

for (const varName of functionArgs)
localVars[varName] = (localVars[varName] ?? 0) + 1;

walkIdentifiers(ts, node.body, cb, localVars);

for (const varName of functionArgs)
localVars[varName]--;
}
else if (ts.isObjectLiteralExpression(node)) {
for (const prop of node.properties) {
if (ts.isPropertyAssignment(prop)) {
walkIdentifiers(ts, prop.initializer, cb, localVars);
}
}
}
else if (ts.isTypeReferenceNode(node)) {
// ignore
}
else {
node.forEachChild(node => walkIdentifiers(ts, node, cb, localVars));
}

for (const varName of blockVars)
localVars[varName]--;
}

export function colletVars(
ts: typeof import('typescript/lib/tsserverlibrary'),
node: ts.Node,
result: string[],
) {
if (ts.isIdentifier(node)) {
result.push(node.text);
}
else if (ts.isObjectBindingPattern(node)) {
for (const el of node.elements) {
colletVars(ts, el.name, result);
}
}
else if (ts.isArrayBindingPattern(node)) {
for (const el of node.elements) {
if (ts.isBindingElement(el)) {
colletVars(ts, el.name, result);
}
}
}
else {
node.forEachChild(node => colletVars(ts, node, result));
}
}

0 comments on commit bd53809

Please sign in to comment.