Skip to content

Commit

Permalink
fix: correctly position quick fixes in instance and module (#2531)
Browse files Browse the repository at this point in the history
closes #2529
  • Loading branch information
paoloricciuti authored Oct 10, 2024
1 parent a167a93 commit 156bd7d
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { walk } from 'estree-walker';
import { BaseNode, walk } from 'estree-walker';
import { EOL } from 'os';
// @ts-ignore
import { TemplateNode } from 'svelte/types/compiler/interfaces';
Expand All @@ -25,6 +25,8 @@ import ts from 'typescript';
// but the AST returned by svelte/compiler does. Type as any as a workaround.
type Node = any;

type Ast = Awaited<ReturnType<SvelteDocument['getCompiled']>>['ast'];

/**
* Get applicable quick fixes.
*/
Expand All @@ -41,7 +43,6 @@ export async function getQuickfixActions(
const transpiled = await svelteDoc.getTranspiled();
const content = transpiled.getText();
const lineOffsets = getLineOffsets(content);
const { html } = ast;

const codeActions: CodeAction[] = [];

Expand All @@ -52,7 +53,7 @@ export async function getQuickfixActions(
transpiled,
content,
lineOffsets,
html,
ast,
diagnostic
))
);
Expand All @@ -66,7 +67,7 @@ async function createQuickfixActions(
transpiled: ITranspiledSvelteDocument,
content: string,
lineOffsets: number[],
html: TemplateNode,
ast: Ast,
diagnostic: Diagnostic
): Promise<CodeAction[]> {
const {
Expand All @@ -80,7 +81,21 @@ async function createQuickfixActions(
pos: diagnosticStartOffset,
end: diagnosticEndOffset
};
const node = findTagForRange(html, offsetRange);
const { html, instance, module } = ast;
const tree = [html, instance, module].find((part) => {
return (
part?.start != null &&
offsetRange.pos >= part.start &&
part?.end != null &&
offsetRange.pos <= part.end &&
part?.end != null &&
offsetRange.end <= part.end &&
part?.start != null &&
offsetRange.end >= part.start
);
});

const node = findTagForRange(tree!, offsetRange, tree === html);

const codeActions: CodeAction[] = [];

Expand All @@ -103,7 +118,8 @@ async function createQuickfixActions(
content,
lineOffsets,
node,
diagnostic
diagnostic,
tree === html
)
);

Expand Down Expand Up @@ -146,14 +162,15 @@ function createSvelteIgnoreQuickfixAction(
content: string,
lineOffsets: number[],
node: Node,
diagnostic: Diagnostic
diagnostic: Diagnostic,
isHtml: boolean
): CodeAction {
return CodeAction.create(
getCodeActionTitle(diagnostic),
{
documentChanges: [
TextDocumentEdit.create(textDocument, [
getSvelteIgnoreEdit(transpiled, content, lineOffsets, node, diagnostic)
getSvelteIgnoreEdit(transpiled, content, lineOffsets, node, diagnostic, isHtml)
])
]
},
Expand Down Expand Up @@ -190,7 +207,8 @@ function getSvelteIgnoreEdit(
content: string,
lineOffsets: number[],
node: Node,
diagnostic: Diagnostic
diagnostic: Diagnostic,
isHtml: boolean
) {
const { code } = diagnostic;

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

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

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

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

function findTagForRange(html: Node, range: ts.TextRange) {
let nearest = html;
function findTagForRange(ast: BaseNode, range: ts.TextRange, isHtml: boolean) {
let nearest: BaseNode = ast;

walk(html, {
walk(ast, {
enter(node, parent) {
const { type } = node;
const isBlock = 'block' in node || node.type.toLowerCase().includes('block');
const isFragment = type === 'Fragment';
const keepLooking = isFragment || elementOrComponent.includes(type) || isBlock;
if (!keepLooking) {
this.skip();
return;
if (isHtml) {
const { type } = node;
const isBlock = 'block' in node || node.type.toLowerCase().includes('block');
const isFragment = type === 'Fragment';
const keepLooking = isFragment || elementOrComponent.includes(type) || isBlock;
if (!keepLooking) {
this.skip();
return;
}
}

if (within(node, range) && parent === nearest) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,55 @@ describe('SveltePlugin#getCodeAction', () => {
}
]);
});

it('should provide ignore comment in script tags', async () => {
(
await expectCodeActionFor(svelteIgnoreCodeAction, {
diagnostics: [
{
severity: DiagnosticSeverity.Warning,
code: 'state_referenced_locally',
range: Range.create(
{ line: 13, character: 9 },
{ line: 13, character: 14 }
),
message: '',
source: 'svelte'
}
]
})
).toEqual([
{
edit: {
documentChanges: [
{
edits: [
{
newText: `\t// svelte-ignore state_referenced_locally${EOL}\t`,
range: {
end: {
character: 0,
line: 13
},
start: {
character: 0,
line: 13
}
}
}
],
textDocument: {
uri: getUri(svelteIgnoreCodeAction),
version: null
}
}
]
},
title: '(svelte) Disable state_referenced_locally for this line',
kind: 'quickfix'
}
]);
});
});

describe('It should provide svelte ignore code actions (TypeScript)', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@
href=""
>about</a>
{/if}

<script>
let value = $state("");
let x = value;
</script>

0 comments on commit 156bd7d

Please sign in to comment.