Skip to content

Commit

Permalink
feat: incremental update (#1718)
Browse files Browse the repository at this point in the history
Co-authored-by: 引证 <3074608+browsnet@users.noreply.github.com>
  • Loading branch information
johnsoncodehk and browsnet committed Aug 16, 2022
1 parent f777ec5 commit 77c417c
Show file tree
Hide file tree
Showing 25 changed files with 1,281 additions and 222 deletions.
21 changes: 21 additions & 0 deletions extensions/vscode-vue-language-features/src/nodeClientMain.ts
Expand Up @@ -31,6 +31,27 @@ export function activate(context: vscode.ExtensionContext) {
},
};
const clientOptions: lsp.LanguageClientOptions = {
middleware: {
handleDiagnostics: (uri, diagnostics, next) => {
const document = vscode.workspace.textDocuments.find(d => d.uri.toString() === uri.toString());
if (document) {
let outdated = false;
for (const diagnostic of diagnostics) {
const data = (diagnostic as any).data;
if (typeof data === 'object' && 'version' in data) {
if (document.version !== data.version) {
outdated = true;
break;
}
}
}
if (outdated) {
return;
}
}
next(uri, diagnostics);
},
},
documentSelector,
initializationOptions: initOptions,
progressOnInitialization: true,
Expand Down
3 changes: 3 additions & 0 deletions packages/alpine-language-core/src/plugins/file-html.ts
Expand Up @@ -6,6 +6,9 @@ const plugin: VueLanguagePlugin = (ctx) => {
const vueHtmlFilePlugin = useVueHtmlFilePlugin(ctx);

return {

order: -1,

parseSFC(fileName, content) {

if (fileName.endsWith('.html')) {
Expand Down
8 changes: 2 additions & 6 deletions packages/source-map/src/index.ts
Expand Up @@ -90,22 +90,18 @@ export class SourceMapBase<Data = undefined> {

const mapped = this.getRange(startOffset, endOffset, sourceToTarget, mapping.mode, mapping.sourceRange, mapping.mappedRange, mapping.data);
if (mapped) {
yield getMapped(mapped);
yield mapped;
}
else if (mapping.additional) {
for (const other of mapping.additional) {
const mapped = this.getRange(startOffset, endOffset, sourceToTarget, other.mode, other.sourceRange, other.mappedRange, mapping.data);
if (mapped) {
yield getMapped(mapped);
yield mapped;
break; // only return first match additional range
}
}
}
}

function getMapped(mapped: [{ start: number, end: number; }, Data]) {
return mapped;
}
}

private getRange(start: number, end: number, sourceToTarget: boolean, mode: Mode, sourceRange: Range, targetRange: Range, data: Data): [{ start: number, end: number; }, Data] | undefined {
Expand Down
8 changes: 4 additions & 4 deletions packages/vue-component-meta/src/index.ts
Expand Up @@ -175,7 +175,7 @@ export function createComponentMetaChecker(tsconfigPath: string, checkerOptions:
const snapshot = host.getScriptSnapshot(componentPath)!;

const vueDefaults = componentPath.endsWith('.vue') && exportName === 'default'
? readVueComponentDefaultProps(core, snapshot.getText(0, snapshot.getLength()), printer)
? readVueComponentDefaultProps(core, snapshot, printer)
: {};
const tsDefaults = !componentPath.endsWith('.vue') ? readTsComponentDefaultProps(
componentPath.substring(componentPath.lastIndexOf('.') + 1), // ts | js | tsx | jsx
Expand Down Expand Up @@ -458,7 +458,7 @@ function createSchemaResolvers(typeChecker: ts.TypeChecker, symbolNode: ts.Expre
};
}

function readVueComponentDefaultProps(core: vue.LanguageContext, vueFileText: string, printer: ts.Printer | undefined) {
function readVueComponentDefaultProps(core: vue.LanguageContext, vueFileScript: ts.IScriptSnapshot, printer: ts.Printer | undefined) {
let result: Record<string, { default?: string, required?: boolean; }> = {};

scriptSetupWorker();
Expand All @@ -468,7 +468,7 @@ function readVueComponentDefaultProps(core: vue.LanguageContext, vueFileText: st

function scriptSetupWorker() {

const vueSourceFile = vue.createSourceFile('/tmp.vue', vueFileText, {}, ts, core.plugins);
const vueSourceFile = vue.createSourceFile('/tmp.vue', vueFileScript, {}, ts, core.plugins);
const descriptor = vueSourceFile.sfc;
const scriptSetupRanges = descriptor.scriptSetupAst ? parseScriptSetupRanges(ts, descriptor.scriptSetupAst) : undefined;

Expand Down Expand Up @@ -520,7 +520,7 @@ function readVueComponentDefaultProps(core: vue.LanguageContext, vueFileText: st

function scriptWorker() {

const vueSourceFile = vue.createSourceFile('/tmp.vue', vueFileText, {}, ts, core.plugins);
const vueSourceFile = vue.createSourceFile('/tmp.vue', vueFileScript, {}, ts, core.plugins);
const descriptor = vueSourceFile.sfc;

if (descriptor.script) {
Expand Down
56 changes: 30 additions & 26 deletions packages/vue-language-core/src/lsContext.ts
Expand Up @@ -2,7 +2,7 @@ import { posix as path } from 'path';
import type * as ts from 'typescript/lib/tsserverlibrary';
import { LanguageServiceHost, VueCompilerOptions } from './types';
import * as localTypes from './utils/localTypes';
import { createSourceFile, EmbeddedFile, VueLanguagePlugin } from './sourceFile';
import { createSourceFile, EmbeddedFile, SourceFile, VueLanguagePlugin } from './sourceFile';
import { createDocumentRegistry } from './documentRegistry';

import * as useHtmlFilePlugin from './plugins/file-html';
Expand Down Expand Up @@ -58,7 +58,11 @@ export function getPlugins(
compilerOptions,
vueCompilerOptions: vueCompilerOptions,
};
const plugins = _plugins.map(plugin => plugin(pluginCtx));
const plugins = _plugins.map(plugin => plugin(pluginCtx)).sort((a, b) => {
const aOrder = a.order ?? 0;
const bOrder = b.order ?? 0;
return aOrder - bOrder;
});

return plugins;
}
Expand Down Expand Up @@ -93,6 +97,7 @@ export function createLanguageContext(
const sharedTypesScript = ts.ScriptSnapshot.fromString(localTypes.getTypesCode(vueCompilerOptions.target));
const scriptSnapshots = new Map<string, [string, ts.IScriptSnapshot]>();
const fileVersions = new WeakMap<EmbeddedFile, string>();
const vueFileVersions = new WeakMap<SourceFile, string>();
const _tsHost: Partial<ts.LanguageServiceHost> = {
fileExists: host.fileExists
? fileName => {
Expand All @@ -117,7 +122,7 @@ export function createLanguageContext(
if (scriptSnapshot) {
documentRegistry.set(vueFileName, createSourceFile(
vueFileName,
scriptSnapshot.getText(0, scriptSnapshot.getLength()),
scriptSnapshot,
vueCompilerOptions,
ts,
plugins,
Expand Down Expand Up @@ -209,16 +214,17 @@ export function createLanguageContext(

// .vue
for (const vueFile of documentRegistry.getAll()) {
const newSnapshot = host.getScriptSnapshot(vueFile.fileName);
if (!newSnapshot) {
// delete
fileNamesToRemove.push(vueFile.fileName);
}
else {
// update
if (vueFile.text !== newSnapshot.getText(0, newSnapshot.getLength())) {
const newVersion = host.getScriptVersion(vueFile.fileName);
if (vueFileVersions.get(vueFile) !== newVersion) {
vueFileVersions.set(vueFile, newVersion);
if (host.getScriptSnapshot(vueFile.fileName)) {
// update
fileNamesToUpdate.push(vueFile.fileName);
}
else {
// delete
fileNamesToRemove.push(vueFile.fileName);
}
}
}

Expand All @@ -230,19 +236,18 @@ export function createLanguageContext(
}

// .ts / .js / .d.ts / .json ...
for (const tsFileVersion of tsFileVersions) {
if (!tsFileNames.has(tsFileVersion[0]) && !host.getScriptSnapshot(tsFileVersion[0])) {
// delete
tsFileVersions.delete(tsFileVersion[0]);
tsFileUpdated = true;
}
else {
// update
const newVersion = host.getScriptVersion(tsFileVersion[0]);
if (tsFileVersion[1] !== newVersion) {
tsFileVersions.set(tsFileVersion[0], newVersion);
tsFileUpdated = true;
for (const [oldTsFileName, oldTsFileVersion] of [...tsFileVersions]) {
const newVersion = host.getScriptVersion(oldTsFileName);
if (oldTsFileVersion !== newVersion) {
if (!tsFileNames.has(oldTsFileName) && !host.getScriptSnapshot(oldTsFileName)) {
// delete
tsFileVersions.delete(oldTsFileName);
}
else {
// update
tsFileVersions.set(oldTsFileName, newVersion);
}
tsFileUpdated = true;
}
}

Expand Down Expand Up @@ -272,12 +277,11 @@ export function createLanguageContext(
}

const sourceFile = documentRegistry.get(fileName);
const scriptText = scriptSnapshot.getText(0, scriptSnapshot.getLength());

if (!sourceFile) {
documentRegistry.set(fileName, createSourceFile(
fileName,
scriptText,
scriptSnapshot,
vueCompilerOptions,
ts,
plugins,
Expand All @@ -297,7 +301,7 @@ export function createLanguageContext(
}
}

sourceFile.text = scriptText;
sourceFile.update(scriptSnapshot);

if (!tsFileUpdated) {
for (const embedded of sourceFile.allEmbeddeds) {
Expand Down
59 changes: 32 additions & 27 deletions packages/vue-language-core/src/sourceFile.ts
@@ -1,5 +1,5 @@
import { SFCBlock, SFCParseResult, SFCScriptBlock, SFCStyleBlock, SFCTemplateBlock } from '@vue/compiler-sfc';
import { computed, ComputedRef, reactive, ref } from '@vue/reactivity';
import { computed, ComputedRef, reactive, shallowRef as ref } from '@vue/reactivity';
import { EmbeddedFileMappingData, TeleportMappingData, VueCompilerOptions, _VueCompilerOptions } from './types';
import { EmbeddedFileSourceMap, Teleport } from './utils/sourceMaps';

Expand All @@ -15,6 +15,7 @@ export type VueLanguagePlugin = (ctx: {
compilerOptions: ts.CompilerOptions,
vueCompilerOptions: _VueCompilerOptions,
}) => {
order?: number;
parseSFC?(fileName: string, content: string): SFCParseResult | undefined;
compileSFCTemplate?(lang: string, template: string, options?: CompilerDom.CompilerOptions): CompilerDom.CodegenResult | undefined;
getEmbeddedFileNames?(fileName: string, sfc: Sfc): string[];
Expand Down Expand Up @@ -82,29 +83,30 @@ export interface EmbeddedFile {

export function createSourceFile(
fileName: string,
_content: string,
scriptSnapshot: ts.IScriptSnapshot,
vueCompilerOptions: VueCompilerOptions,
ts: typeof import('typescript/lib/tsserverlibrary'),
plugins: ReturnType<VueLanguagePlugin>[],
) {

// refs
const fileContent = ref('');
const snapshot = ref(scriptSnapshot);
const fileContent = computed(() => snapshot.value.getText(0, snapshot.value.getLength()));
const sfc = reactive<Sfc>({
template: null,
script: null,
scriptSetup: null,
styles: [],
customBlocks: [],
get templateAst() {
templateAst: computed(() => {
return compiledSFCTemplate.value?.ast;
},
get scriptAst() {
}) as unknown as Sfc['templateAst'],
scriptAst: computed(() => {
return scriptAst.value;
},
get scriptSetupAst() {
}) as unknown as Sfc['scriptAst'],
scriptSetupAst: computed(() => {
return scriptSetupAst.value;
},
}) as unknown as Sfc['scriptSetupAst'],
}) as Sfc /* avoid Sfc unwrap in .d.ts by reactive */;

// use
Expand Down Expand Up @@ -147,7 +149,6 @@ export function createSourceFile(
errors.push(err);
}


if (ast || errors.length) {
return {
errors,
Expand Down Expand Up @@ -321,16 +322,14 @@ export function createSourceFile(
}
});

update(_content);
update(scriptSnapshot, true);

return {
fileName,
get text() {
return fileContent.value;
},
set text(value) {
update(value);
},
update,
get compiledSFCTemplate() {
return compiledSFCTemplate.value;
},
Expand Down Expand Up @@ -399,12 +398,18 @@ export function createSourceFile(
}
return range;
}
function update(newContent: string) {
function update(newScriptSnapshot: ts.IScriptSnapshot, init = false) {

if (fileContent.value === newContent)
if (newScriptSnapshot === snapshot.value && !init) {
return;
}

fileContent.value = newContent;
const change = newScriptSnapshot.getChangeRange(snapshot.value);
snapshot.value = newScriptSnapshot;

if (change) {
// TODO
}

// TODO: wait for https://github.com/vuejs/core/pull/5912
if (parsedSfc.value) {
Expand All @@ -419,8 +424,8 @@ export function createSourceFile(

const newData: Sfc['template'] | null = block ? {
tag: 'template',
start: newContent.substring(0, block.loc.start.offset).lastIndexOf('<'),
end: block.loc.end.offset + newContent.substring(block.loc.end.offset).indexOf('>') + 1,
start: fileContent.value.substring(0, block.loc.start.offset).lastIndexOf('<'),
end: block.loc.end.offset + fileContent.value.substring(block.loc.end.offset).indexOf('>') + 1,
startTagEnd: block.loc.start.offset,
endTagStart: block.loc.end.offset,
content: block.content,
Expand All @@ -438,8 +443,8 @@ export function createSourceFile(

const newData: Sfc['script'] | null = block ? {
tag: 'script',
start: newContent.substring(0, block.loc.start.offset).lastIndexOf('<'),
end: block.loc.end.offset + newContent.substring(block.loc.end.offset).indexOf('>') + 1,
start: fileContent.value.substring(0, block.loc.start.offset).lastIndexOf('<'),
end: block.loc.end.offset + fileContent.value.substring(block.loc.end.offset).indexOf('>') + 1,
startTagEnd: block.loc.start.offset,
endTagStart: block.loc.end.offset,
content: block.content,
Expand All @@ -458,8 +463,8 @@ export function createSourceFile(

const newData: Sfc['scriptSetup'] | null = block ? {
tag: 'scriptSetup',
start: newContent.substring(0, block.loc.start.offset).lastIndexOf('<'),
end: block.loc.end.offset + newContent.substring(block.loc.end.offset).indexOf('>') + 1,
start: fileContent.value.substring(0, block.loc.start.offset).lastIndexOf('<'),
end: block.loc.end.offset + fileContent.value.substring(block.loc.end.offset).indexOf('>') + 1,
startTagEnd: block.loc.start.offset,
endTagStart: block.loc.end.offset,
content: block.content,
Expand All @@ -479,8 +484,8 @@ export function createSourceFile(
const block = blocks[i];
const newData: Sfc['styles'][number] = {
tag: 'style',
start: newContent.substring(0, block.loc.start.offset).lastIndexOf('<'),
end: block.loc.end.offset + newContent.substring(block.loc.end.offset).indexOf('>') + 1,
start: fileContent.value.substring(0, block.loc.start.offset).lastIndexOf('<'),
end: block.loc.end.offset + fileContent.value.substring(block.loc.end.offset).indexOf('>') + 1,
startTagEnd: block.loc.start.offset,
endTagStart: block.loc.end.offset,
content: block.content,
Expand All @@ -506,8 +511,8 @@ export function createSourceFile(
const block = blocks[i];
const newData: Sfc['customBlocks'][number] = {
tag: 'customBlock',
start: newContent.substring(0, block.loc.start.offset).lastIndexOf('<'),
end: block.loc.end.offset + newContent.substring(block.loc.end.offset).indexOf('>') + 1,
start: fileContent.value.substring(0, block.loc.start.offset).lastIndexOf('<'),
end: block.loc.end.offset + fileContent.value.substring(block.loc.end.offset).indexOf('>') + 1,
startTagEnd: block.loc.start.offset,
endTagStart: block.loc.end.offset,
content: block.content,
Expand Down

0 comments on commit 77c417c

Please sign in to comment.