Skip to content

Commit bc9a3fd

Browse files
authored
feat: support attachments (#2760)
sveltejs/svelte#15000
1 parent 2bdbf6d commit bc9a3fd

File tree

12 files changed

+62
-14
lines changed

12 files changed

+62
-14
lines changed

packages/language-server/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"globrex": "^0.1.2",
6060
"lodash": "^4.17.21",
6161
"prettier": "~3.3.3",
62-
"prettier-plugin-svelte": "^3.3.0",
62+
"prettier-plugin-svelte": "^3.4.0",
6363
"svelte": "^4.2.19",
6464
"svelte2tsx": "workspace:~",
6565
"typescript": "^5.8.2",
@@ -69,6 +69,6 @@
6969
"vscode-languageserver": "9.0.1",
7070
"vscode-languageserver-protocol": "3.17.5",
7171
"vscode-languageserver-types": "3.17.5",
72-
"vscode-uri": "~3.0.0"
72+
"vscode-uri": "~3.1.0"
7373
}
7474
}

packages/language-server/src/plugins/svelte/features/SvelteTags.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export type SvelteLogicTag = 'each' | 'if' | 'await' | 'key' | 'snippet';
88
/**
99
* Special svelte syntax tags.
1010
*/
11-
export type SvelteTag = SvelteLogicTag | 'html' | 'debug' | 'const' | 'render';
11+
export type SvelteTag = SvelteLogicTag | 'html' | 'debug' | 'const' | 'render' | 'attach';
1212

1313
/**
1414
* For each tag, a documentation in markdown format.
@@ -95,11 +95,19 @@ It accepts a comma-separated list of variable names (not arbitrary expressions).
9595
https://svelte.dev/docs/svelte/@debug
9696
`,
9797
const: `\`{@const ...}\`\\
98-
Defines a local constant}\\
98+
Defines a local constant\\
9999
#### Usage:
100100
\`{@const a = b + c}\`\\
101101
\\
102102
https://svelte.dev/docs/svelte/@const
103+
`,
104+
attach: `\`{@attach ...}\`\\
105+
Defines an attachment that is attached to an element or component\\
106+
#### Usage:
107+
\`<div {@attach (node) => {...}}></div>\`\\
108+
\`<Component {@attach namedAttachment} />\`\\
109+
\\
110+
https://svelte.dev/docs/svelte/@attach
103111
`
104112
};
105113

packages/language-server/src/plugins/svelte/features/getCompletions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ function getCompletionsWithRegardToTriggerCharacter(
122122
{ tag: 'html', label: 'html' },
123123
{ tag: 'debug', label: 'debug' },
124124
{ tag: 'const', label: 'const' },
125-
{ tag: 'render', label: 'render' }
125+
{ tag: 'render', label: 'render' },
126+
{ tag: 'attach', label: 'attach' }
126127
]);
127128
}
128129

packages/language-server/src/plugins/svelte/features/getHoverInfo.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ const tagPossibilities: Array<{ tag: SvelteTag | ':else'; values: string[] }> =
111111
{ tag: 'debug' as const, values: ['@debug'] },
112112
{ tag: 'const' as const, values: ['@const'] },
113113
{ tag: 'render' as const, values: ['@render'] },
114+
{ tag: 'attach' as const, values: ['@attach'] },
114115
// this tag has multiple possibilities
115116
{ tag: ':else' as const, values: [':else'] }
116117
];

packages/language-server/test/plugins/svelte/features/getCompletions.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ describe('SveltePlugin#getCompletions', () => {
6363
});
6464

6565
it('should return completions for @', () => {
66-
expectCompletionsFor('{@').toEqual(['html', 'debug', 'const', 'render']);
66+
expectCompletionsFor('{@').toEqual(['html', 'debug', 'const', 'render', 'attach']);
6767
});
6868

6969
describe('should return no completions for :', () => {

packages/svelte-check/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,6 @@
5858
"vscode-languageserver": "8.0.2",
5959
"vscode-languageserver-protocol": "3.17.2",
6060
"vscode-languageserver-types": "3.17.2",
61-
"vscode-uri": "~3.0.0"
61+
"vscode-uri": "~3.1.0"
6262
}
6363
}

packages/svelte-vscode/syntaxes/svelte.tmLanguage.src.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,8 +360,17 @@ repository:
360360
patterns:
361361
- include: '#attributes-directives'
362362
- include: '#attributes-keyvalue'
363+
- include: '#attributes-attach'
363364
- include: '#attributes-interpolated'
364365

366+
# Attachments
367+
attributes-attach:
368+
begin: (?<!:|=)\s*({@attach\s)
369+
end: (\})
370+
captures: { 1: { name: entity.other.attribute-name.svelte } }
371+
contentName: meta.embedded.expression.svelte source.ts
372+
patterns: [ include: source.ts ]
373+
365374
# Interpolated shorthand attributes, like `{variable}` sitting by itself.
366375
attributes-interpolated:
367376
begin: (?<!:|=)\s*({)

packages/svelte2tsx/src/htmlxtojsx_v2/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
import { EventHandler } from '../svelte2tsx/nodes/event-handler';
4242
import { ComponentEvents } from '../svelte2tsx/nodes/ComponentEvents';
4343
import { analyze } from 'periscopic';
44+
import { handleAttachTag } from './nodes/AttachTag';
4445

4546
export interface TemplateProcessResult {
4647
/**
@@ -290,6 +291,9 @@ export function convertHtmlxToJsx(
290291
case 'RenderTag':
291292
handleRenderTag(str, node);
292293
break;
294+
case 'AttachTag':
295+
handleAttachTag(node, element);
296+
break;
293297
case 'InlineComponent':
294298
if (element) {
295299
element.child = new InlineComponent(str, node, element);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { BaseNode } from '../../interfaces';
2+
import { Element } from './Element';
3+
import { InlineComponent } from './InlineComponent';
4+
5+
/**
6+
* {@attach xxx} ---> [Symbol()]: xxx
7+
*/
8+
export function handleAttachTag(tag: BaseNode, element: Element | InlineComponent): void {
9+
// element.addAttachment(attr);
10+
if (element instanceof InlineComponent) {
11+
element.addProp(['[Symbol("@attach")]'], [[tag.expression.start, tag.expression.end]]);
12+
} else {
13+
element.addAttribute(['[Symbol("@attach")]'], [[tag.expression.start, tag.expression.end]]);
14+
}
15+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{ svelteHTML.createElement("div", { [Symbol("@attach")]:x,}); }
2+
{ svelteHTML.createElement("div", { [Symbol("@attach")]:(node) => {},}); }
3+
4+
{ const $$_pmoC0C = __sveltets_2_ensureComponent(Comp); new $$_pmoC0C({ target: __sveltets_2_any(), props: { [Symbol("@attach")]:x,}}); Comp}
5+
{ const $$_pmoC0C = __sveltets_2_ensureComponent(Comp); new $$_pmoC0C({ target: __sveltets_2_any(), props: { [Symbol("@attach")]:(node) => {},}}); Comp}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<div {@attach x}></div>
2+
<div {@attach (node) => {}}></div>
3+
4+
<Comp {@attach x}></Comp>
5+
<Comp {@attach (node) => {}}></Comp>

pnpm-lock.yaml

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)