Skip to content

Commit 156bd7d

Browse files
fix: correctly position quick fixes in instance and module (#2531)
closes #2529
1 parent a167a93 commit 156bd7d

File tree

3 files changed

+98
-20
lines changed

3 files changed

+98
-20
lines changed

packages/language-server/src/plugins/svelte/features/getCodeActions/getQuickfixes.ts

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { walk } from 'estree-walker';
1+
import { BaseNode, walk } from 'estree-walker';
22
import { EOL } from 'os';
33
// @ts-ignore
44
import { TemplateNode } from 'svelte/types/compiler/interfaces';
@@ -25,6 +25,8 @@ import ts from 'typescript';
2525
// but the AST returned by svelte/compiler does. Type as any as a workaround.
2626
type Node = any;
2727

28+
type Ast = Awaited<ReturnType<SvelteDocument['getCompiled']>>['ast'];
29+
2830
/**
2931
* Get applicable quick fixes.
3032
*/
@@ -41,7 +43,6 @@ export async function getQuickfixActions(
4143
const transpiled = await svelteDoc.getTranspiled();
4244
const content = transpiled.getText();
4345
const lineOffsets = getLineOffsets(content);
44-
const { html } = ast;
4546

4647
const codeActions: CodeAction[] = [];
4748

@@ -52,7 +53,7 @@ export async function getQuickfixActions(
5253
transpiled,
5354
content,
5455
lineOffsets,
55-
html,
56+
ast,
5657
diagnostic
5758
))
5859
);
@@ -66,7 +67,7 @@ async function createQuickfixActions(
6667
transpiled: ITranspiledSvelteDocument,
6768
content: string,
6869
lineOffsets: number[],
69-
html: TemplateNode,
70+
ast: Ast,
7071
diagnostic: Diagnostic
7172
): Promise<CodeAction[]> {
7273
const {
@@ -80,7 +81,21 @@ async function createQuickfixActions(
8081
pos: diagnosticStartOffset,
8182
end: diagnosticEndOffset
8283
};
83-
const node = findTagForRange(html, offsetRange);
84+
const { html, instance, module } = ast;
85+
const tree = [html, instance, module].find((part) => {
86+
return (
87+
part?.start != null &&
88+
offsetRange.pos >= part.start &&
89+
part?.end != null &&
90+
offsetRange.pos <= part.end &&
91+
part?.end != null &&
92+
offsetRange.end <= part.end &&
93+
part?.start != null &&
94+
offsetRange.end >= part.start
95+
);
96+
});
97+
98+
const node = findTagForRange(tree!, offsetRange, tree === html);
8499

85100
const codeActions: CodeAction[] = [];
86101

@@ -103,7 +118,8 @@ async function createQuickfixActions(
103118
content,
104119
lineOffsets,
105120
node,
106-
diagnostic
121+
diagnostic,
122+
tree === html
107123
)
108124
);
109125

@@ -146,14 +162,15 @@ function createSvelteIgnoreQuickfixAction(
146162
content: string,
147163
lineOffsets: number[],
148164
node: Node,
149-
diagnostic: Diagnostic
165+
diagnostic: Diagnostic,
166+
isHtml: boolean
150167
): CodeAction {
151168
return CodeAction.create(
152169
getCodeActionTitle(diagnostic),
153170
{
154171
documentChanges: [
155172
TextDocumentEdit.create(textDocument, [
156-
getSvelteIgnoreEdit(transpiled, content, lineOffsets, node, diagnostic)
173+
getSvelteIgnoreEdit(transpiled, content, lineOffsets, node, diagnostic, isHtml)
157174
])
158175
]
159176
},
@@ -190,7 +207,8 @@ function getSvelteIgnoreEdit(
190207
content: string,
191208
lineOffsets: number[],
192209
node: Node,
193-
diagnostic: Diagnostic
210+
diagnostic: Diagnostic,
211+
isHtml: boolean
194212
) {
195213
const { code } = diagnostic;
196214

@@ -207,26 +225,31 @@ function getSvelteIgnoreEdit(
207225
const indent = getIndent(afterStartLineStart);
208226

209227
// TODO: Make all code action's new line consistent
210-
const ignore = `${indent}<!-- svelte-ignore ${code} -->${EOL}`;
228+
let ignore = `${indent}// svelte-ignore ${code}${EOL}${indent}`;
229+
if (isHtml) {
230+
ignore = `${indent}<!-- svelte-ignore ${code} -->${EOL}`;
231+
}
211232
const position = Position.create(nodeStartPosition.line, 0);
212233

213234
return mapObjWithRangeToOriginal(transpiled, TextEdit.insert(position, ignore));
214235
}
215236

216237
const elementOrComponent = ['Component', 'Element', 'InlineComponent'];
217238

218-
function findTagForRange(html: Node, range: ts.TextRange) {
219-
let nearest = html;
239+
function findTagForRange(ast: BaseNode, range: ts.TextRange, isHtml: boolean) {
240+
let nearest: BaseNode = ast;
220241

221-
walk(html, {
242+
walk(ast, {
222243
enter(node, parent) {
223-
const { type } = node;
224-
const isBlock = 'block' in node || node.type.toLowerCase().includes('block');
225-
const isFragment = type === 'Fragment';
226-
const keepLooking = isFragment || elementOrComponent.includes(type) || isBlock;
227-
if (!keepLooking) {
228-
this.skip();
229-
return;
244+
if (isHtml) {
245+
const { type } = node;
246+
const isBlock = 'block' in node || node.type.toLowerCase().includes('block');
247+
const isFragment = type === 'Fragment';
248+
const keepLooking = isFragment || elementOrComponent.includes(type) || isBlock;
249+
if (!keepLooking) {
250+
this.skip();
251+
return;
252+
}
230253
}
231254

232255
if (within(node, range) && parent === nearest) {

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,55 @@ describe('SveltePlugin#getCodeAction', () => {
421421
}
422422
]);
423423
});
424+
425+
it('should provide ignore comment in script tags', async () => {
426+
(
427+
await expectCodeActionFor(svelteIgnoreCodeAction, {
428+
diagnostics: [
429+
{
430+
severity: DiagnosticSeverity.Warning,
431+
code: 'state_referenced_locally',
432+
range: Range.create(
433+
{ line: 13, character: 9 },
434+
{ line: 13, character: 14 }
435+
),
436+
message: '',
437+
source: 'svelte'
438+
}
439+
]
440+
})
441+
).toEqual([
442+
{
443+
edit: {
444+
documentChanges: [
445+
{
446+
edits: [
447+
{
448+
newText: `\t// svelte-ignore state_referenced_locally${EOL}\t`,
449+
range: {
450+
end: {
451+
character: 0,
452+
line: 13
453+
},
454+
start: {
455+
character: 0,
456+
line: 13
457+
}
458+
}
459+
}
460+
],
461+
textDocument: {
462+
uri: getUri(svelteIgnoreCodeAction),
463+
version: null
464+
}
465+
}
466+
]
467+
},
468+
title: '(svelte) Disable state_referenced_locally for this line',
469+
kind: 'quickfix'
470+
}
471+
]);
472+
});
424473
});
425474

426475
describe('It should provide svelte ignore code actions (TypeScript)', () => {

packages/language-server/test/plugins/svelte/testfiles/svelte-ignore-code-action.svelte

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,9 @@
77
href=""
88
>about</a>
99
{/if}
10+
11+
<script>
12+
let value = $state("");
13+
14+
let x = value;
15+
</script>

0 commit comments

Comments
 (0)