Skip to content

Commit 4a16e57

Browse files
fix(language-service): skip const props = completion in StringLiteral (#5786)
1 parent 56ae259 commit 4a16e57

File tree

4 files changed

+78
-32
lines changed

4 files changed

+78
-32
lines changed

packages/language-core/lib/plugins.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export function createPlugins(pluginContext: Parameters<VueLanguagePlugin>[0]) {
2222
useMdFilePlugin,
2323
useHtmlFilePlugin,
2424
vueRootTagsPlugin,
25+
vueTsx,
2526
vueScriptJsPlugin,
2627
vueStyleCss,
2728
vueTemplateHtmlPlugin,
@@ -31,7 +32,6 @@ export function createPlugins(pluginContext: Parameters<VueLanguagePlugin>[0]) {
3132
vueSfcCustomBlocks,
3233
vueSfcScriptsFormat,
3334
vueSfcTemplate,
34-
vueTsx,
3535
...pluginContext.vueCompilerOptions.plugins,
3636
];
3737

packages/language-service/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ export function createVueLanguageServicePlugins(
5151
createVueGlobalTypesErrorPlugin(),
5252
createVueScopedClassLinksPlugin(),
5353
createVueSfcPlugin(),
54-
createVueSuggestDefineAssignmentPlugin(),
5554
createVueTemplateRefLinksPlugin(),
5655
createEmmetPlugin({
5756
mappedLanguages: {
@@ -61,6 +60,7 @@ export function createVueLanguageServicePlugins(
6160
}),
6261

6362
// TS related plugins
63+
createVueSuggestDefineAssignmentPlugin(ts),
6464
createTypeScriptDocCommentTemplatePlugin(ts),
6565
createTypeScriptSyntacticPlugin(ts),
6666
createVueInlayHintsPlugin(ts),

packages/language-service/lib/plugins/vue-autoinsert-dotvalue.ts

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export function create(
5555
if (sourceOffset < startTagEnd || sourceOffset > endTagStart) {
5656
continue;
5757
}
58-
if (isBlacklistNode(ts, ast, sourceOffset - startTagEnd, false)) {
58+
if (shouldSkip(ts, ast, sourceOffset - startTagEnd, false)) {
5959
return;
6060
}
6161
}
@@ -86,7 +86,7 @@ function isCharacterTyping(document: TextDocument, change: { text: string; range
8686
return charReg.test(lastCharacter) && !charReg.test(nextCharacter);
8787
}
8888

89-
function isBlacklistNode(ts: typeof import('typescript'), node: ts.Node, pos: number, allowAccessDotValue: boolean) {
89+
function shouldSkip(ts: typeof import('typescript'), node: ts.Node, pos: number, allowAccessDotValue: boolean) {
9090
if (ts.isVariableDeclaration(node) && pos >= node.name.getFullStart() && pos <= node.name.getEnd()) {
9191
return true;
9292
}
@@ -123,47 +123,48 @@ function isBlacklistNode(ts: typeof import('typescript'), node: ts.Node, pos: nu
123123
ts.isCallExpression(node)
124124
&& ts.isIdentifier(node.expression)
125125
&& isWatchOrUseFunction(node.expression.text)
126-
&& isTopLevelArgOrArrayTopLevelItemItem(node)
126+
&& isTopLevelArgOrArrayTopLevelItemItem(ts, node, pos)
127127
) {
128128
return true;
129129
}
130130
else {
131-
let _isBlacklistNode = false;
131+
let _shouldSkip = false;
132132
node.forEachChild(node => {
133-
if (_isBlacklistNode) {
133+
if (_shouldSkip) {
134134
return;
135135
}
136136
if (pos >= node.getFullStart() && pos <= node.getEnd()) {
137-
if (isBlacklistNode(ts, node, pos, allowAccessDotValue)) {
138-
_isBlacklistNode = true;
137+
if (shouldSkip(ts, node, pos, allowAccessDotValue)) {
138+
_shouldSkip = true;
139139
}
140140
}
141141
});
142-
return _isBlacklistNode;
142+
return _shouldSkip;
143143
}
144+
}
144145

145-
function isWatchOrUseFunction(fnName: string) {
146-
return fnName === 'watch'
147-
|| fnName === 'unref'
148-
|| fnName === 'triggerRef'
149-
|| fnName === 'isRef'
150-
|| hyphenateAttr(fnName).startsWith('use-');
151-
}
152-
function isTopLevelArgOrArrayTopLevelItemItem(node: ts.CallExpression) {
153-
for (const arg of node.arguments) {
154-
if (pos >= arg.getFullStart() && pos <= arg.getEnd()) {
155-
if (ts.isIdentifier(arg)) {
156-
return true;
157-
}
158-
if (ts.isArrayLiteralExpression(arg)) {
159-
for (const el of arg.elements) {
160-
if (pos >= el.getFullStart() && pos <= el.getEnd()) {
161-
return ts.isIdentifier(el);
162-
}
146+
function isWatchOrUseFunction(fnName: string) {
147+
return fnName === 'watch'
148+
|| fnName === 'unref'
149+
|| fnName === 'triggerRef'
150+
|| fnName === 'isRef'
151+
|| hyphenateAttr(fnName).startsWith('use-');
152+
}
153+
154+
function isTopLevelArgOrArrayTopLevelItemItem(ts: typeof import('typescript'), node: ts.CallExpression, pos: number) {
155+
for (const arg of node.arguments) {
156+
if (pos >= arg.getFullStart() && pos <= arg.getEnd()) {
157+
if (ts.isIdentifier(arg)) {
158+
return true;
159+
}
160+
if (ts.isArrayLiteralExpression(arg)) {
161+
for (const el of arg.elements) {
162+
if (pos >= el.getFullStart() && pos <= el.getEnd()) {
163+
return ts.isIdentifier(el);
163164
}
164165
}
165-
return false;
166166
}
167+
return false;
167168
}
168169
}
169170
}

packages/language-service/lib/plugins/vue-suggest-define-assignment.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
import type { CompletionItem, CompletionItemKind, LanguageServicePlugin } from '@volar/language-service';
1+
import type { CompletionItem, CompletionItemKind, LanguageServicePlugin, TextDocument } from '@volar/language-service';
22
import { type TextRange, tsCodegen } from '@vue/language-core';
3+
import type * as ts from 'typescript';
4+
import { URI } from 'vscode-uri';
35
import { resolveEmbeddedCode } from '../utils';
46

5-
export function create(): LanguageServicePlugin {
7+
const documentToSourceFile = new WeakMap<TextDocument, ts.SourceFile>();
8+
9+
export function create(ts: typeof import('typescript')): LanguageServicePlugin {
610
return {
711
name: 'vue-suggest-define-assignment',
812
capabilities: {
@@ -11,7 +15,7 @@ export function create(): LanguageServicePlugin {
1115
create(context) {
1216
return {
1317
isAdditionalCompletion: true,
14-
async provideCompletionItems(document) {
18+
async provideCompletionItems(document, position) {
1519
const info = resolveEmbeddedCode(context, document.uri);
1620
if (!info?.code.id.startsWith('script_')) {
1721
return;
@@ -30,6 +34,11 @@ export function create(): LanguageServicePlugin {
3034
return;
3135
}
3236

37+
const sourceFile = getSourceFile(ts, document);
38+
if (shouldSkip(ts, sourceFile, document.offsetAt(position))) {
39+
return;
40+
}
41+
3342
const result: CompletionItem[] = [];
3443
const mappings = [...context.language.maps.forEach(info.code)];
3544

@@ -93,3 +102,39 @@ export function create(): LanguageServicePlugin {
93102
},
94103
};
95104
}
105+
106+
function shouldSkip(ts: typeof import('typescript'), node: ts.Node, pos: number) {
107+
if (ts.isStringLiteral(node) && pos >= node.getFullStart() && pos <= node.getEnd()) {
108+
return true;
109+
}
110+
else if (ts.isTemplateLiteral(node) && pos >= node.getFullStart() && pos <= node.getEnd()) {
111+
return true;
112+
}
113+
else {
114+
let _shouldSkip = false;
115+
node.forEachChild(node => {
116+
if (_shouldSkip) {
117+
return;
118+
}
119+
if (pos >= node.getFullStart() && pos <= node.getEnd()) {
120+
if (shouldSkip(ts, node, pos)) {
121+
_shouldSkip = true;
122+
}
123+
}
124+
});
125+
return _shouldSkip;
126+
}
127+
}
128+
129+
function getSourceFile(ts: typeof import('typescript'), document: TextDocument): ts.SourceFile {
130+
let sourceFile = documentToSourceFile.get(document);
131+
if (!sourceFile) {
132+
sourceFile = ts.createSourceFile(
133+
URI.parse(document.uri).path,
134+
document.getText(),
135+
ts.ScriptTarget.Latest,
136+
);
137+
documentToSourceFile.set(document, sourceFile);
138+
}
139+
return sourceFile;
140+
}

0 commit comments

Comments
 (0)