import { walk } from 'estree-walker' ; import { EOL } from 'os' ; import { Ast } from 'svelte/types/compiler/interfaces' ; import { CodeAction , CodeActionKind , Diagnostic , DiagnosticSeverity , OptionalVersionedTextDocumentIdentifier , Position , TextDocumentEdit , TextEdit } from 'vscode-languageserver' ; import { getLineOffsets , mapObjWithRangeToOriginal , offsetAt , positionAt } from '../../../../lib/documents' ; import { getIndent , pathToUrl } from '../../../../utils' ; import { SvelteDocument } from '../../SvelteDocument' ; import ts from 'typescript' ; type Node = any ; /**
 * Get applicable quick fixes.
 */ export async function getQuickfixActions ( svelteDoc , svelteDiagnostics ) { const { ast } = await svelteDoc . getCompiled ( ) ; return Promise . all ( svelteDiagnostics . map ( async ( diagnostic ) => await createQuickfixAction ( diagnostic , svelteDoc , ast ) ) ) ; } async function createQuickfixAction ( diagnostic , svelteDoc , ast ) { const textDocument = OptionalVersionedTextDocumentIdentifier . create ( pathToUrl ( svelteDoc . getFilePath ( ) ) , null ) ; return CodeAction . create ( getCodeActionTitle ( diagnostic ) , { documentChanges : [ TextDocumentEdit . create ( textDocument , [ await getSvelteIgnoreEdit ( svelteDoc , ast , diagnostic ) ] ) ] } , CodeActionKind . QuickFix ) ; } function getCodeActionTitle ( diagnostic ) { return `(svelte) Disable ${ diagnostic . code } for this line` ; } /**
 * Whether or not the given diagnostic can be ignored via a
 * <!-- svelte-ignore <code> -->
 */ export function isIgnorableSvelteDiagnostic ( diagnostic ) { const { source , severity , code } = diagnostic ; return ( code && ! nonIgnorableWarnings . includes ( < string > code ) && source === 'svelte' && severity !== DiagnosticSeverity . Error ) ; } const nonIgnorableWarnings = [ 'missing-custom-element-compile-options' , 'unused-export-let' , 'css-unused-selector' ] ; async function getSvelteIgnoreEdit ( svelteDoc , ast , diagnostic ) { const { code , range : { start , end } } = diagnostic ; const transpiled = await svelteDoc . getTranspiled ( ) ; const content = transpiled . getText ( ) ; const lineOffsets = getLineOffsets ( content ) ; const { html } = ast ; const generatedStart = transpiled . getGeneratedPosition ( start ) ; const generatedEnd = transpiled . getGeneratedPosition ( end ) ; const diagnosticStartOffset = offsetAt ( generatedStart , content , lineOffsets ) ; const diagnosticEndOffset = offsetAt ( generatedEnd , content , lineOffsets ) ; const offsetRange = { pos : diagnosticStartOffset , end : diagnosticEndOffset } ; const node = findTagForRange ( html , offsetRange ) ; const nodeStartPosition = positionAt ( node . start , content , lineOffsets ) ; const nodeLineStart = offsetAt ( { line : nodeStartPosition . line , character : 0 } , content , lineOffsets ) ; const afterStartLineStart = content . slice ( nodeLineStart ) ; const indent = getIndent ( afterStartLineStart ) ; const 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 , range ) { let nearest = html ; walk ( html , { 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 ( within ( node , range ) && parent === nearest ) { nearest = node ; } } } ) ; return nearest ; } function within ( node , range ) { return node . end >= range . end && node . start <= range . pos ; }