Skip to content

Commit

Permalink
refactor(language-core): extract SFC root tags to separate virtual code
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed Aug 9, 2024
1 parent 67be01c commit b2965ff
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 99 deletions.
2 changes: 2 additions & 0 deletions packages/language-core/lib/plugins.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import useHtmlFilePlugin from './plugins/file-html';
import useMdFilePlugin from './plugins/file-md';
import useVueFilePlugin from './plugins/file-vue';
import vueRootTagsPlugin from './plugins/vue-root-tags';
import vueScriptJsPlugin from './plugins/vue-script-js';
import vueSfcCustomBlocks from './plugins/vue-sfc-customblocks';
import vueSfcScriptsFormat from './plugins/vue-sfc-scripts';
Expand All @@ -20,6 +21,7 @@ export function createPlugins(pluginContext: Parameters<VueLanguagePlugin>[0]) {
useVueFilePlugin,
useMdFilePlugin,
useHtmlFilePlugin,
vueRootTagsPlugin,
vueScriptJsPlugin,
vueTemplateHtmlPlugin,
vueTemplateInlineCssPlugin,
Expand Down
80 changes: 80 additions & 0 deletions packages/language-core/lib/plugins/vue-root-tags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { CodeMapping } from '@volar/language-core';
import { replaceSourceRange } from 'muggle-string';
import type { VueCodeInformation, VueLanguagePlugin } from '../types';
import { allCodeFeatures } from './shared';

const plugin: VueLanguagePlugin = () => {

return {

version: 2.1,

getEmbeddedCodes() {
return [{
id: 'root_tags',
lang: 'vue-root-tags',
}];
},

resolveEmbeddedCode(_fileName, sfc, embeddedFile) {
if (embeddedFile.id === 'root_tags') {
embeddedFile.content.push([sfc.content, undefined, 0, allCodeFeatures]);
for (const block of [
sfc.script,
sfc.scriptSetup,
sfc.template,
...sfc.styles,
...sfc.customBlocks,
]) {
if (block) {
replaceSourceRange(embeddedFile.content, undefined, block.startTagEnd, block.endTagStart, '\n\n');
}
}
const mappings = embeddedFile.content
.filter(s => typeof s !== 'string')
.map<CodeMapping>(m => {
const text = m[0];
const start = m[2] as number;
return {
sourceOffsets: [start],
generatedOffsets: [start],
lengths: [text.length],
data: m[3] as VueCodeInformation,
};
});

// fix folding range end position failed to mapping
for (const block of [
sfc.script,
sfc.scriptSetup,
sfc.template,
...sfc.styles,
...sfc.customBlocks,
]) {
const offsets: number[] = [];
if (block) {
let content = block.content;
if (content.endsWith('\r\n')) {
content = content.slice(0, -2);
}
else if (content.endsWith('\n')) {
content = content.slice(0, -1);
}
const offset = content.lastIndexOf('\n') + 1;
offsets.push(block.startTagEnd + offset);
}
if (offsets.length) {
mappings.push({
sourceOffsets: offsets,
generatedOffsets: offsets,
lengths: offsets.map(() => 0),
data: { structure: true },
});
}
}
}
},
};
};

export default plugin;
1 change: 1 addition & 0 deletions packages/language-core/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export interface SfcBlock {
}

export interface Sfc {
content: string;
template: SfcBlock & {
ast: CompilerDOM.RootNode | undefined;
errors: CompilerDOM.CompilerError[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { Code, Sfc, SfcBlock, VueLanguagePluginReturn } from '../types';
import { buildMappings } from '../utils/buildMappings';
import { VueEmbeddedCode } from './embeddedFile';

export function computedFiles(
export function computedEmbeddedCodes(
plugins: VueLanguagePluginReturn[],
fileName: string,
sfc: Sfc
Expand Down
70 changes: 0 additions & 70 deletions packages/language-core/lib/virtualFile/computedMappings.ts

This file was deleted.

9 changes: 7 additions & 2 deletions packages/language-core/lib/virtualFile/computedSfc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@ export function computedSfc(
ts: typeof import('typescript'),
plugins: VueLanguagePluginReturn[],
fileName: string,
snapshot: () => ts.IScriptSnapshot,
getSnapshot: () => ts.IScriptSnapshot,
parsed: () => SFCParseResult | undefined
): Sfc {

const untrackedSnapshot = () => {
pauseTracking();
const res = snapshot();
const res = getSnapshot();
resetTracking();
return res;
};
const content = computed(() => {
const snapshot = getSnapshot();
return snapshot.getText(0, snapshot.getLength());
});
const template = computedNullableSfcBlock(
'template',
'html',
Expand Down Expand Up @@ -137,6 +141,7 @@ export function computedSfc(
);

return {
get content() { return content(); },
get template() { return template(); },
get script() { return script(); },
get scriptSetup() { return scriptSetup(); },
Expand Down
30 changes: 19 additions & 11 deletions packages/language-core/lib/virtualFile/vueFile.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
import type { VirtualCode } from '@volar/language-core';
import { Signal, signal } from 'computeds';
import { computed, Signal, signal } from 'computeds';
import type * as ts from 'typescript';
import type { VueCompilerOptions, VueLanguagePluginReturn } from '../types';
import { computedFiles } from './computedFiles';
import { computedMappings } from './computedMappings';
import { computedEmbeddedCodes } from './computedEmbeddedCodes';
import { computedSfc } from './computedSfc';
import { computedVueSfc } from './computedVueSfc';
import { allCodeFeatures } from '../plugins';

export class VueVirtualCode implements VirtualCode {

// sources

id = 'main';

_snapshot: Signal<ts.IScriptSnapshot>;
getSnapshot: Signal<ts.IScriptSnapshot>;

// computeds

getVueSfc = computedVueSfc(this.plugins, this.fileName, this.languageId, () => this._snapshot());
sfc = computedSfc(this.ts, this.plugins, this.fileName, () => this._snapshot(), this.getVueSfc);
getMappings = computedMappings(() => this._snapshot(), this.sfc);
getEmbeddedCodes = computedFiles(this.plugins, this.fileName, this.sfc);
getVueSfc = computedVueSfc(this.plugins, this.fileName, this.languageId, () => this.getSnapshot());
sfc = computedSfc(this.ts, this.plugins, this.fileName, () => this.getSnapshot(), this.getVueSfc);
getMappings = computed(() => {
const snapshot = this.getSnapshot();
return [{
sourceOffsets: [0],
generatedOffsets: [0],
lengths: [snapshot.getLength()],
data: allCodeFeatures,
}];
});
getEmbeddedCodes = computedEmbeddedCodes(this.plugins, this.fileName, this.sfc);

// others

get embeddedCodes() {
return this.getEmbeddedCodes();
}
get snapshot() {
return this._snapshot();
return this.getSnapshot();
}
get mappings() {
return this.getMappings();
Expand All @@ -42,10 +50,10 @@ export class VueVirtualCode implements VirtualCode {
public plugins: VueLanguagePluginReturn[],
public ts: typeof import('typescript'),
) {
this._snapshot = signal(initSnapshot);
this.getSnapshot = signal(initSnapshot);
}

update(newSnapshot: ts.IScriptSnapshot) {
this._snapshot.set(newSnapshot);
this.getSnapshot.set(newSnapshot);
}
}
2 changes: 1 addition & 1 deletion packages/language-service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ function getCommonLanguageServicePlugins(
createVueExtractFilePlugin(ts, getTsPluginClient),
createEmmetPlugin({
mappedLanguages: {
'vue': 'html',
'vue-root-tags': 'html',
'postcss': 'scss',
},
}),
Expand Down
16 changes: 2 additions & 14 deletions packages/language-service/lib/plugins/vue-sfc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,9 @@ import { URI } from 'vscode-uri';

let sfcDataProvider: html.IHTMLDataProvider | undefined;

export interface Provide {
'vue/vueFile': (document: TextDocument) => vue.VueVirtualCode | undefined;
}

export function create(): LanguageServicePlugin {
const htmlPlugin = createHtmlService({
documentSelector: ['vue'],
documentSelector: ['vue-root-tags'],
useDefaultDataProvider: false,
getCustomData(context) {
sfcDataProvider ??= html.newHTMLDataProvider('vue', loadLanguageBlocks(context.env.locale ?? 'en'));
Expand Down Expand Up @@ -47,21 +43,13 @@ export function create(): LanguageServicePlugin {
return {
...htmlPlugin,
name: 'vue-sfc',
create(context): LanguageServicePluginInstance<Provide> {
create(context): LanguageServicePluginInstance {
const htmlPluginInstance = htmlPlugin.create(context);

return {

...htmlPluginInstance,

provide: {
'vue/vueFile': document => {
return worker(document, context, vueFile => {
return vueFile;
});
},
},

provideDocumentLinks: undefined,

async resolveEmbeddedCodeFormattingOptions(sourceScript, virtualCode, options) {
Expand Down

0 comments on commit b2965ff

Please sign in to comment.