Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/language-core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from './lib/parsers/scriptSetupRanges';
export * from './lib/plugins';
export * from './lib/types';
export * from './lib/utils/collectBindings';
export * from './lib/utils/forEachTemplateNode';
export * from './lib/utils/parseSfc';
export * from './lib/utils/shared';
export * from './lib/virtualFile/vueFile';
Expand Down
39 changes: 1 addition & 38 deletions packages/language-core/lib/codegen/template/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as CompilerDOM from '@vue/compiler-dom';
import type * as ts from 'typescript';
import type { Code, Sfc, VueCompilerOptions } from '../../types';
import { codeFeatures } from '../codeFeatures';
Expand All @@ -7,7 +6,7 @@ import { wrapWith } from '../utils/wrapWith';
import { createTemplateCodegenContext, type TemplateCodegenContext } from './context';
import { generateObjectProperty } from './objectProperty';
import { generateStyleScopedClassReferences } from './styleScopedClasses';
import { generateTemplateChild, getVForNode } from './templateChild';
import { generateTemplateChild } from './templateChild';

export interface TemplateCodegenOptions {
ts: typeof ts;
Expand Down Expand Up @@ -203,39 +202,3 @@ function* generateRootEl(
yield endOfLine;
return `__VLS_RootEl`;
}

export function* forEachElementNode(
node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode,
): Generator<CompilerDOM.ElementNode> {
if (node.type === CompilerDOM.NodeTypes.ROOT) {
for (const child of node.children) {
yield* forEachElementNode(child);
}
}
else if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
const patchForNode = getVForNode(node);
if (patchForNode) {
yield* forEachElementNode(patchForNode);
}
else {
yield node;
for (const child of node.children) {
yield* forEachElementNode(child);
}
}
}
else if (node.type === CompilerDOM.NodeTypes.IF) {
// v-if / v-else-if / v-else
for (const branch of node.branches) {
for (const childNode of branch.children) {
yield* forEachElementNode(childNode);
}
}
}
else if (node.type === CompilerDOM.NodeTypes.FOR) {
// v-for
for (const child of node.children) {
yield* forEachElementNode(child);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as CompilerDOM from '@vue/compiler-dom';
import { forEachElementNode } from '../codegen/template';
import type { Code, VueLanguagePlugin } from '../types';
import { forEachElementNode } from '../utils/forEachTemplateNode';
import { allCodeFeatures } from './shared';

const codeFeatures = {
Expand Down
69 changes: 69 additions & 0 deletions packages/language-core/lib/utils/forEachTemplateNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import * as CompilerDOM from '@vue/compiler-dom';

export function* forEachElementNode(
node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode,
): Generator<CompilerDOM.ElementNode> {
if (node.type === CompilerDOM.NodeTypes.ROOT) {
for (const child of node.children) {
yield* forEachElementNode(child);
}
}
else if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
yield node;
for (const child of node.children) {
yield* forEachElementNode(child);
}
}
else if (node.type === CompilerDOM.NodeTypes.IF) {
for (const branch of node.branches) {
for (const childNode of branch.children) {
yield* forEachElementNode(childNode);
}
}
}
else if (node.type === CompilerDOM.NodeTypes.FOR) {
for (const child of node.children) {
yield* forEachElementNode(child);
}
}
}

export function* forEachInterpolationNode(
node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode | CompilerDOM.SimpleExpressionNode,
): Generator<CompilerDOM.InterpolationNode> {
if (node.type === CompilerDOM.NodeTypes.ROOT) {
for (const child of node.children) {
yield* forEachInterpolationNode(child);
}
}
else if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
for (const child of node.children) {
yield* forEachInterpolationNode(child);
}
}
else if (node.type === CompilerDOM.NodeTypes.TEXT_CALL) {
yield* forEachInterpolationNode(node.content);
}
else if (node.type === CompilerDOM.NodeTypes.COMPOUND_EXPRESSION) {
for (const child of node.children) {
if (typeof child === 'object') {
yield* forEachInterpolationNode(child);
}
}
}
else if (node.type === CompilerDOM.NodeTypes.INTERPOLATION) {
yield node;
}
else if (node.type === CompilerDOM.NodeTypes.IF) {
for (const branch of node.branches) {
for (const childNode of branch.children) {
yield* forEachInterpolationNode(childNode);
}
}
}
else if (node.type === CompilerDOM.NodeTypes.FOR) {
for (const child of node.children) {
yield* forEachInterpolationNode(child);
}
}
}
46 changes: 45 additions & 1 deletion packages/language-service/lib/plugins/vue-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ import type {
LanguageServicePlugin,
TextDocument,
} from '@volar/language-service';
import { hyphenateAttr, hyphenateTag, tsCodegen, type VueVirtualCode } from '@vue/language-core';
import type * as CompilerDOM from '@vue/compiler-dom';
import {
forEachInterpolationNode,
hyphenateAttr,
hyphenateTag,
tsCodegen,
type VueVirtualCode,
} from '@vue/language-core';
import { camelize, capitalize } from '@vue/shared';
import type { ComponentPropInfo } from '@vue/typescript-plugin/lib/requests/getComponentProps';
import { create as createHtmlService } from 'volar-service-html';
Expand Down Expand Up @@ -157,6 +164,43 @@ export function create(
disposable?.dispose();
},

provideAutoInsertSnippet(document, position, autoInsertContext, token) {
if (document.languageId !== languageId) {
return;
}
const info = resolveEmbeddedCode(context, document.uri);
if (info?.code.id !== 'template') {
return;
}

return baseServiceInstance.provideAutoInsertSnippet?.(
{
...document,
getText: () => {
if (info.root.sfc.template?.ast) {
const locs: CompilerDOM.SourceLocation[] = [];
for (const node of forEachInterpolationNode(info.root.sfc.template.ast)) {
locs.push(node.loc);
}
let text = '';
let offset = 0;
for (const loc of locs) {
text += info.root.sfc.template.content.slice(offset, loc.start.offset + 2);
text += ' '.repeat(loc.source.length - 4);
offset = loc.end.offset - 2;
}
text += info.root.sfc.template.content.slice(offset);
return text;
}
return document.getText();
},
},
position,
autoInsertContext,
token,
);
},

async provideCompletionItems(document, position, completionContext, token) {
if (document.languageId !== languageId) {
return;
Expand Down
Loading