From 552187a03e2226e06ed22cd040ce6bbeacc4bcaa Mon Sep 17 00:00:00 2001 From: sapphi-red Date: Thu, 10 Dec 2020 23:56:35 +0900 Subject: [PATCH 1/4] Dont include used attributes in suggestions --- .prettierignore | 1 + .../modes/template/services/htmlCompletion.ts | 48 +++++++++++++++++-- .../modes/template/test/completion.test.ts | 21 +++++--- .../features/completion/property.test.ts | 2 +- .../completion/propertyDecorator/Parent.vue | 2 +- 5 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 .prettierignore diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..647f4f6c54 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +server/src/modes/template/test/completion.test.ts diff --git a/server/src/modes/template/services/htmlCompletion.ts b/server/src/modes/template/services/htmlCompletion.ts index 4611317584..dc0cebf784 100644 --- a/server/src/modes/template/services/htmlCompletion.ts +++ b/server/src/modes/template/services/htmlCompletion.ts @@ -42,6 +42,7 @@ export function doComplete( const scanner = createScanner(text, node.start); let currentTag: string; let currentAttributeName = ''; + let currentTagStartOffset: number; function getReplaceRange(replaceStart: number, replaceEnd: number = offset): Range { if (replaceStart > offset) { @@ -147,7 +148,11 @@ export function doComplete( return result; } - function collectAttributeNameSuggestions(nameStart: number, nameEnd: number = offset): CompletionList { + function collectAttributeNameSuggestions( + usedAttributes: Set, + nameStart: number, + nameEnd: number = offset + ): CompletionList { const execArray = /^[:@]/.exec(scanner.getTokenText()); const filterPrefix = execArray ? execArray[0] : ''; const start = filterPrefix ? nameStart + 1 : nameStart; @@ -158,6 +163,16 @@ export function doComplete( tagProviders.forEach(provider => { const priority = provider.priority; provider.collectAttributes(currentTag, (attribute, type, documentation) => { + if ( + usedAttributes.has(normalizeAttribute(attribute)) && + // can listen to same event by adding modifiers + type !== 'event' && + // `class` and `:class`, `style` and `:style` can coexist + attribute !== 'class' && + attribute !== 'style' + ) { + return; + } if ((type === 'event' && filterPrefix !== '@') || (type !== 'event' && filterPrefix === '@')) { return; } @@ -271,6 +286,23 @@ export function doComplete( return offset; } + function collectUsedAttributes(): Set { + const attrScanner = createScanner(text, currentTagStartOffset); + + let token = attrScanner.scan(); + let currentAttributeName!: string; + const attrs = new Set(); + while (token !== TokenType.EOS && token !== TokenType.StartTagClose) { + if (token === TokenType.AttributeName) { + currentAttributeName = normalizeAttribute(attrScanner.getTokenText()); + } else if (token === TokenType.AttributeValue) { + attrs.add(currentAttributeName); + } + token = attrScanner.scan(); + } + return attrs; + } + let token = scanner.scan(); while (token !== TokenType.EOS && scanner.getTokenOffset() <= offset) { @@ -280,6 +312,7 @@ export function doComplete( const endPos = scanNextForEndPos(TokenType.StartTag); return collectTagSuggestions(offset, endPos); } + currentTagStartOffset = scanner.getTokenOffset(); break; case TokenType.StartTag: if (scanner.getTokenOffset() <= offset && offset <= scanner.getTokenEnd()) { @@ -289,7 +322,8 @@ export function doComplete( break; case TokenType.AttributeName: if (scanner.getTokenOffset() <= offset && offset <= scanner.getTokenEnd()) { - return collectAttributeNameSuggestions(scanner.getTokenOffset(), scanner.getTokenEnd()); + const usedAttrs = collectUsedAttributes(); + return collectAttributeNameSuggestions(usedAttrs, scanner.getTokenOffset(), scanner.getTokenEnd()); } currentAttributeName = scanner.getTokenText(); break; @@ -321,7 +355,8 @@ export function doComplete( return collectTagSuggestions(startPos, endTagPos); case ScannerState.WithinTag: case ScannerState.AfterAttributeName: - return collectAttributeNameSuggestions(scanner.getTokenEnd()); + const usedAttrs = collectUsedAttributes(); + return collectAttributeNameSuggestions(usedAttrs, scanner.getTokenEnd()); case ScannerState.BeforeAttributeValue: return collectAttributeValueSuggestions(currentAttributeName, scanner.getTokenEnd()); case ScannerState.AfterOpeningEndTag: @@ -392,3 +427,10 @@ function getWordEnd(s: string, offset: number, limit: number): number { } return offset; } + +function normalizeAttribute(attr: string): string { + // trim modifiers + attr = attr.replace(/\..+$/, ''); + + return attr.replace(/^(?:v-bind:|:)/, '').replace(/^v-on:/, '@'); +} diff --git a/server/src/modes/template/test/completion.test.ts b/server/src/modes/template/test/completion.test.ts index c3d45fc4cc..b142b84fc2 100644 --- a/server/src/modes/template/test/completion.test.ts +++ b/server/src/modes/template/test/completion.test.ts @@ -77,24 +77,21 @@ suite('HTML Completion', () => { .become(' { html` { diff --git a/test/interpolation/features/completion/property.test.ts b/test/interpolation/features/completion/property.test.ts index 1a9a3d69fb..0b52e92ec3 100644 --- a/test/interpolation/features/completion/property.test.ts +++ b/test/interpolation/features/completion/property.test.ts @@ -93,7 +93,7 @@ describe('Should autocomplete interpolation for