diff --git a/packages/language-server/src/lib/documents/parseHtml.ts b/packages/language-server/src/lib/documents/parseHtml.ts index 7ee6b45c1..cfa65ebe4 100644 --- a/packages/language-server/src/lib/documents/parseHtml.ts +++ b/packages/language-server/src/lib/documents/parseHtml.ts @@ -90,6 +90,7 @@ function preprocess(text: string) { export interface AttributeContext { name: string; inValue: boolean; + elementTag: Node; valueRange?: [number, number]; } @@ -122,6 +123,7 @@ export function getAttributeContextAtPosition( if (inTokenRange()) { return { + elementTag: tag, name: currentAttributeName, inValue: false }; @@ -131,6 +133,7 @@ export function getAttributeContextAtPosition( const nextToken = scanner.scan(); return { + elementTag: tag, name: currentAttributeName, inValue: true, valueRange: [ @@ -151,6 +154,7 @@ export function getAttributeContextAtPosition( } return { + elementTag: tag, name: currentAttributeName, inValue: true, valueRange: [start, end] diff --git a/packages/language-server/src/plugins/html/HTMLPlugin.ts b/packages/language-server/src/plugins/html/HTMLPlugin.ts index 18e6a009e..b2260b002 100644 --- a/packages/language-server/src/plugins/html/HTMLPlugin.ts +++ b/packages/language-server/src/plugins/html/HTMLPlugin.ts @@ -32,6 +32,7 @@ import { LinkedEditingRangesProvider } from '../interfaces'; import { isInsideMoustacheTag, toRange } from '../../lib/documents/utils'; +import { possiblyComponent } from '../../utils'; export class HTMLPlugin implements HoverProvider, CompletionsProvider, RenameProvider, LinkedEditingRangesProvider @@ -62,7 +63,7 @@ export class HTMLPlugin } const node = html.findNodeAt(document.offsetAt(position)); - if (!node || this.possiblyComponent(node)) { + if (!node || possiblyComponent(node)) { return null; } @@ -102,12 +103,23 @@ export class HTMLPlugin ) ]); } + const results = this.isInComponentTag(html, document, position) ? // Only allow emmet inside component element tags. // Other attributes/events would be false positives. CompletionList.create([]) : this.lang.doComplete(document, position, html); const items = this.toCompletionItems(results.items); + + items.forEach((item) => { + if (item.label.startsWith('on:') && item.textEdit) { + item.textEdit = { + ...item.textEdit, + newText: item.textEdit.newText.replace('="$1"', '$2="$1"') + }; + } + }); + return CompletionList.create( [ ...this.toCompletionItems(items), @@ -225,7 +237,7 @@ export class HTMLPlugin } const node = html.findNodeAt(document.offsetAt(position)); - if (!node || this.possiblyComponent(node)) { + if (!node || possiblyComponent(node)) { return null; } @@ -244,12 +256,7 @@ export class HTMLPlugin const offset = document.offsetAt(position); const node = html.findNodeAt(offset); - if ( - !node || - this.possiblyComponent(node) || - !node.tag || - !this.isRenameAtTag(node, offset) - ) { + if (!node || possiblyComponent(node) || !node.tag || !this.isRenameAtTag(node, offset)) { return null; } const tagNameStart = node.start + '<'.length; @@ -276,16 +283,6 @@ export class HTMLPlugin return { ranges }; } - /** - * - * The language service is case insensitive, and would provide - * hover info for Svelte components like `Option` which have - * the same name like a html tag. - */ - private possiblyComponent(node: Node): boolean { - return !!node.tag?.[0].match(/[A-Z]/); - } - /** * Returns true if rename happens at the tag name, not anywhere inbetween. */ diff --git a/packages/language-server/src/plugins/svelte/SveltePlugin.ts b/packages/language-server/src/plugins/svelte/SveltePlugin.ts index 34901f141..4233bcfb1 100644 --- a/packages/language-server/src/plugins/svelte/SveltePlugin.ts +++ b/packages/language-server/src/plugins/svelte/SveltePlugin.ts @@ -160,7 +160,7 @@ export class SveltePlugin return null; } - return getCompletions(svelteDoc, position); + return getCompletions(document, svelteDoc, position); } async doHover(document: Document, position: Position): Promise { @@ -168,7 +168,7 @@ export class SveltePlugin return null; } - return getHoverInfo(await this.getSvelteDoc(document), position); + return getHoverInfo(document, await this.getSvelteDoc(document), position); } async getCodeActions( diff --git a/packages/language-server/src/plugins/svelte/features/getCompletions.ts b/packages/language-server/src/plugins/svelte/features/getCompletions.ts index 8b16a17b2..4cef1b66a 100644 --- a/packages/language-server/src/plugins/svelte/features/getCompletions.ts +++ b/packages/language-server/src/plugins/svelte/features/getCompletions.ts @@ -5,10 +5,14 @@ import { CompletionList, CompletionItemKind, CompletionItem, - InsertTextFormat + InsertTextFormat, + MarkupKind } from 'vscode-languageserver'; import { SvelteTag, documentation, getLatestOpeningTag } from './SvelteTags'; -import { isInTag } from '../../../lib/documents'; +import { isInTag, Document } from '../../../lib/documents'; +import { AttributeContext, getAttributeContextAtPosition } from '../../../lib/documents/parseHtml'; +import { getModifierData } from './getModifierData'; +import { attributeCanHaveEventModifier } from './utils'; const HTML_COMMENT_START = '