diff --git a/.changeset/olive-jobs-sip.md b/.changeset/olive-jobs-sip.md new file mode 100644 index 000000000..8047f8436 --- /dev/null +++ b/.changeset/olive-jobs-sip.md @@ -0,0 +1,5 @@ +--- +"svelte-language-server": patch +--- + +feat: support for comments in tags diff --git a/packages/language-server/package.json b/packages/language-server/package.json index 23cb6aa51..29743d592 100644 --- a/packages/language-server/package.json +++ b/packages/language-server/package.json @@ -59,7 +59,7 @@ "globrex": "^0.1.2", "lodash": "^4.17.21", "prettier": "~3.3.3", - "prettier-plugin-svelte": "^3.4.0", + "prettier-plugin-svelte": "^3.5.0", "svelte": "^4.2.19", "svelte2tsx": "workspace:~", "typescript": "^5.9.2", diff --git a/packages/language-server/src/lib/documents/parseHtml.ts b/packages/language-server/src/lib/documents/parseHtml.ts index afe239e2d..44d1cdc63 100644 --- a/packages/language-server/src/lib/documents/parseHtml.ts +++ b/packages/language-server/src/lib/documents/parseHtml.ts @@ -137,6 +137,27 @@ export function parseHtml(text: string): HTMLDocument { parseAttributeValue(); break; + case TokenType.Unknown: { + const tokenOffset = scanner.getTokenOffset(); + if ( + isInsideTagScannerState() && + text.charCodeAt(tokenOffset) === '/'.charCodeAt(0) + ) { + const nextCharCode = text.charCodeAt(tokenOffset + 1); + if (nextCharCode === '/'.charCodeAt(0)) { + const newlineOffset = text.indexOf('\n', tokenOffset + 2); + const commentEndOffset = newlineOffset === -1 ? text.length : newlineOffset; + restartScannerAt(commentEndOffset, ScannerState.WithinTag); + } else if (nextCharCode === '*'.charCodeAt(0)) { + const blockCommentEnd = text.indexOf('*/', tokenOffset + 2); + const commentEndOffset = + blockCommentEnd === -1 ? text.length : blockCommentEnd + 2; + restartScannerAt(commentEndOffset, ScannerState.WithinTag); + } + } + break; + } + case TokenType.Content: { const expressionEnd = skipExpressionInCurrentRange(); if (expressionEnd > scanner.getTokenEnd()) { @@ -223,6 +244,16 @@ export function parseHtml(text: string): HTMLDocument { } finishAttribute(start, expressionTagEnd); } + + function isInsideTagScannerState() { + const scannerState = scanner.getScannerState(); + return ( + scannerState === ScannerState.WithinTag || + scannerState === ScannerState.AfterAttributeName || + scannerState === ScannerState.BeforeAttributeValue || + scannerState === ScannerState.AfterOpeningStartTag + ); + } } export interface AttributeContext { diff --git a/packages/language-server/test/lib/documents/parseHtml.test.ts b/packages/language-server/test/lib/documents/parseHtml.test.ts index 8349a4066..e5fb6024f 100644 --- a/packages/language-server/test/lib/documents/parseHtml.test.ts +++ b/packages/language-server/test/lib/documents/parseHtml.test.ts @@ -198,6 +198,20 @@ describe('parseHtml', () => { const ariaLabelValue = fooNode.attributes?.['ariaLabel']; assert.strictEqual(ariaLabelValue, `"a{b > c ? "": ""} c"`); }); + + it('parse comments in attributes', () => { + testRootElements( + parseHtml( + ` + checked={a} + /* another comment/>
ignore me
*/ + hello="bar" + /> + ` + ) + ); + }); }); describe('getAttributeContextAtPosition', () => { diff --git a/packages/svelte-vscode/syntaxes/svelte.tmLanguage.src.yaml b/packages/svelte-vscode/syntaxes/svelte.tmLanguage.src.yaml index aadf41d6f..7252cd599 100644 --- a/packages/svelte-vscode/syntaxes/svelte.tmLanguage.src.yaml +++ b/packages/svelte-vscode/syntaxes/svelte.tmLanguage.src.yaml @@ -391,11 +391,23 @@ repository: attributes: patterns: + - include: '#attributes-comments' - include: '#attributes-directives' - include: '#attributes-keyvalue' - include: '#attributes-attach' - include: '#attributes-interpolated' + # Comments within attributes + attributes-comments: + patterns: + # Single-line comment + - match: //.*$ + name: comment.line.double-slash.svelte + # Block comment + - begin: /\* + end: \*/ + name: comment.block.svelte + # Attachments attributes-attach: begin: (? +;function $$render() { +async () => { { svelteHTML.createElement("div", { "foo":`bar`,"baz":`qux`,}); } + + { const $$_tnenopmoC0C = __sveltets_2_ensureComponent(Component); new $$_tnenopmoC0C({ target: __sveltets_2_any(), props: { "foo":`bar`,"baz":`qux`,}}); Component}}; +return { props: /** @type {Record} */ ({}), exports: {}, bindings: "", slots: {}, events: {} }} +const Input__SvelteComponent_ = __sveltets_2_isomorphic_component(__sveltets_2_partial(__sveltets_2_with_any_event($$render()))); +/*Ωignore_startΩ*/type Input__SvelteComponent_ = InstanceType; +/*Ωignore_endΩ*/export default Input__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/comments-in-attributes.v5/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/comments-in-attributes.v5/input.svelte new file mode 100644 index 000000000..69c3add73 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/comments-in-attributes.v5/input.svelte @@ -0,0 +1,13 @@ +
+ + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62dcd5162..75143e60b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,8 +55,8 @@ importers: specifier: ~3.3.3 version: 3.3.3 prettier-plugin-svelte: - specifier: ^3.4.0 - version: 3.4.0(prettier@3.3.3)(svelte@4.2.19) + specifier: ^3.5.0 + version: 3.5.0(prettier@3.3.3)(svelte@4.2.19) svelte: specifier: ^4.2.19 version: 4.2.19 @@ -1694,8 +1694,8 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} - prettier-plugin-svelte@3.4.0: - resolution: {integrity: sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==} + prettier-plugin-svelte@3.5.0: + resolution: {integrity: sha512-2lLO/7EupnjO/95t+XZesXs8Bf3nYLIDfCo270h5QWbj/vjLqmrQ1LiRk9LPggxSDsnVYfehamZNf+rgQYApZg==} peerDependencies: prettier: ^3.0.0 svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 @@ -3486,7 +3486,7 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - prettier-plugin-svelte@3.4.0(prettier@3.3.3)(svelte@4.2.19): + prettier-plugin-svelte@3.5.0(prettier@3.3.3)(svelte@4.2.19): dependencies: prettier: 3.3.3 svelte: 4.2.19