Skip to content

Commit c5e889b

Browse files
committed
refactor(language-service): simplify the addition of directive modifiers
1 parent 05107ad commit c5e889b

File tree

1 file changed

+58
-79
lines changed

1 file changed

+58
-79
lines changed

packages/language-service/lib/plugins/vue-template.ts

Lines changed: 58 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,10 @@ export function create(
9797
// https://vuejs.org/api/built-in-directives.html#v-bind
9898
const vOnModifiers: Record<string, string> = {};
9999
const vBindModifiers: Record<string, string> = {};
100+
const vModelModifiers: Record<string, string> = {};
100101
const vOn = builtInData.globalAttributes?.find(x => x.name === 'v-on');
101102
const vBind = builtInData.globalAttributes?.find(x => x.name === 'v-bind');
103+
const vModel = builtInData.globalAttributes?.find(x => x.name === 'v-model');
102104

103105
if (vOn) {
104106
const markdown = (typeof vOn.description === 'string' ? vOn.description : vOn.description?.value) ?? '';
@@ -122,6 +124,13 @@ export function create(
122124
vBindModifiers[name] = desc;
123125
}
124126
}
127+
if (vModel) {
128+
for (const modifier of modelData.globalAttributes ?? []) {
129+
const description = typeof modifier.description === 'object' ? modifier.description.value : modifier.description;
130+
const references = modifier.references?.map(ref => `[${ref.name}](${ref.url})`).join(' | ');
131+
vModelModifiers[modifier.name] = description + '\n\n' + references;
132+
}
133+
}
125134

126135
const disposable = context.env.onDidChangeConfiguration?.(() => initializing = undefined);
127136

@@ -455,81 +464,52 @@ export function create(
455464

456465
function afterHtmlCompletion(completionList: vscode.CompletionList, document: TextDocument) {
457466

458-
do {
459-
const replacement = getReplacement(completionList, document);
460-
if (!replacement) {
461-
break;
462-
}
467+
addDirectiveModifiers();
463468

464-
const hasModifier = replacement.text.includes('.');
465-
if (!hasModifier) {
466-
break;
469+
function addDirectiveModifiers() {
470+
const replacement = getReplacement(completionList, document);
471+
if (!replacement?.text.includes('.')) {
472+
return;
467473
}
468474

469475
const [text, ...modifiers] = replacement.text.split('.');
470476
const isVOn = text.startsWith('v-on:') || text.startsWith('@') && text.length > 1;
471477
const isVBind = text.startsWith('v-bind:') || text.startsWith(':') && text.length > 1;
472478
const isVModel = text.startsWith('v-model:') || text === 'v-model';
473-
const validModifiers =
479+
const currentModifiers =
474480
isVOn ? vOnModifiers
475481
: isVBind ? vBindModifiers
476-
: undefined;
477-
478-
if (validModifiers) {
479-
480-
for (const modifier in validModifiers) {
481-
482-
if (modifiers.includes(modifier)) {
483-
continue;
484-
}
485-
486-
const description = validModifiers[modifier];
487-
const insertText = text + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier;
488-
const newItem: html.CompletionItem = {
489-
label: modifier,
490-
filterText: insertText,
491-
documentation: {
492-
kind: 'markdown',
493-
value: description,
494-
},
495-
textEdit: {
496-
range: replacement.textEdit.range,
497-
newText: insertText,
498-
},
499-
kind: 20 satisfies typeof vscode.CompletionItemKind.EnumMember,
500-
};
482+
: isVModel ? vModelModifiers
483+
: undefined;
501484

502-
completionList.items.push(newItem);
503-
}
485+
if (!currentModifiers) {
486+
return;
504487
}
505-
else if (isVModel) {
506-
507-
for (const modifier of modelData.globalAttributes ?? []) {
508488

509-
if (modifiers.includes(modifier.name)) {
510-
continue;
511-
}
489+
for (const modifier in currentModifiers) {
490+
if (modifiers.includes(modifier)) {
491+
continue;
492+
}
512493

513-
const insertText = text + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier.name;
514-
const newItem: html.CompletionItem = {
515-
label: modifier.name,
516-
filterText: insertText,
517-
documentation: {
518-
kind: 'markdown',
519-
value: (typeof modifier.description === 'object' ? modifier.description.value : modifier.description)
520-
+ '\n\n' + modifier.references?.map(ref => `[${ref.name}](${ref.url})`).join(' | '),
521-
},
522-
textEdit: {
523-
range: replacement.textEdit.range,
524-
newText: insertText,
525-
},
526-
kind: 20 satisfies typeof vscode.CompletionItemKind.EnumMember,
527-
};
494+
const description = currentModifiers[modifier];
495+
const insertText = text + modifiers.slice(0, -1).map(m => '.' + m).join('') + '.' + modifier;
496+
const newItem: html.CompletionItem = {
497+
label: modifier,
498+
filterText: insertText,
499+
documentation: {
500+
kind: 'markdown',
501+
value: description,
502+
},
503+
textEdit: {
504+
range: replacement.textEdit.range,
505+
newText: insertText,
506+
},
507+
kind: 20 satisfies typeof vscode.CompletionItemKind.EnumMember,
508+
};
528509

529-
completionList.items.push(newItem);
530-
}
510+
completionList.items.push(newItem);
531511
}
532-
} while (0);
512+
}
533513

534514
completionList.items = completionList.items.filter(item => !specialTags.has(parseLabel(item.label).name));
535515

@@ -543,13 +523,13 @@ export function create(
543523
}
544524

545525
for (const item of completionList.items) {
546-
const parsedLabelKey = parseItemKey(item.label);
526+
const parsedLabel = parseItemKey(item.label);
547527

548-
if (parsedLabelKey) {
549-
const name = parsedLabelKey.tag;
550-
item.label = parsedLabelKey.leadingSlash ? '/' + name : name;
528+
if (parsedLabel) {
529+
const name = parsedLabel.tag;
530+
item.label = parsedLabel.leadingSlash ? '/' + name : name;
551531

552-
const text = parsedLabelKey.leadingSlash ? `/${name}>` : name;
532+
const text = parsedLabel.leadingSlash ? `/${name}>` : name;
553533
if (item.textEdit) {
554534
item.textEdit.newText = text;
555535
};
@@ -561,20 +541,19 @@ export function create(
561541
}
562542
}
563543

564-
const itemKeyStr = typeof item.documentation === 'string' ? item.documentation : item.documentation?.value;
565-
566-
let parsedItemKey = itemKeyStr ? parseItemKey(itemKeyStr) : undefined;
544+
const itemKey = typeof item.documentation === 'string' ? item.documentation : item.documentation?.value;
545+
let parsedItem = itemKey ? parseItemKey(itemKey) : undefined;
567546
let propInfo: ComponentPropInfo | undefined;
568547

569-
if (parsedItemKey) {
548+
if (parsedItem) {
570549
const documentations: string[] = [];
571550

572-
propInfo = cachedPropInfos.get(parsedItemKey.prop);
551+
propInfo = cachedPropInfos.get(parsedItem.prop);
573552
if (propInfo?.commentMarkdown) {
574553
documentations.push(propInfo.commentMarkdown);
575554
}
576555

577-
let { isEvent, propName } = getPropName(parsedItemKey);
556+
let { isEvent, propName } = getPropName(parsedItem);
578557
if (isEvent) {
579558
// click -> onclick
580559
propName = 'on' + propName;
@@ -605,7 +584,7 @@ export function create(
605584

606585
// for special props without internal item key
607586
if (specialProps.has(propName)) {
608-
parsedItemKey = {
587+
parsedItem = {
609588
type: 'componentProp',
610589
tag: '^',
611590
prop: propName,
@@ -640,12 +619,12 @@ export function create(
640619
item.kind = 6 satisfies typeof vscode.CompletionItemKind.Variable;
641620
tokens.push('\u0000');
642621
}
643-
else if (parsedItemKey) {
622+
else if (parsedItem) {
644623

645-
const isComponent = parsedItemKey.tag !== '*';
646-
const { isEvent, propName } = getPropName(parsedItemKey);
624+
const isComponent = parsedItem.tag !== '*';
625+
const { isEvent, propName } = getPropName(parsedItem);
647626

648-
if (parsedItemKey.type === 'componentProp') {
627+
if (parsedItem.type === 'componentProp') {
649628
if (isComponent || specialProps.has(propName)) {
650629
item.kind = 5 satisfies typeof vscode.CompletionItemKind.Field;
651630
}
@@ -796,13 +775,13 @@ function getReplacement(list: html.CompletionList, doc: TextDocument) {
796775
}
797776

798777
function getPropName(
799-
itemKey: ReturnType<typeof parseItemKey> & {}
778+
parsedItem: ReturnType<typeof parseItemKey> & {}
800779
) {
801-
const name = hyphenateAttr(itemKey.prop);
780+
const name = hyphenateAttr(parsedItem.prop);
802781
if (name.startsWith('on-')) {
803782
return { isEvent: true, propName: name.slice('on-'.length) };
804783
}
805-
else if (itemKey.type === 'componentEvent') {
784+
else if (parsedItem.type === 'componentEvent') {
806785
return { isEvent: true, propName: name };
807786
}
808787
return { isEvent: false, propName: name };

0 commit comments

Comments
 (0)