@@ -15,7 +15,7 @@ import {
1515 isInTag ,
1616 getLineAtPosition
1717} from '../../../lib/documents' ;
18- import { pathToUrl , flatten , isNotNullOrUndefined , modifyLines } from '../../../utils' ;
18+ import { pathToUrl , flatten , isNotNullOrUndefined , modifyLines , getIndent } from '../../../utils' ;
1919import { CodeActionsProvider } from '../../interfaces' ;
2020import { SnapshotFragment , SvelteSnapshotFragment } from '../DocumentSnapshot' ;
2121import { LSAndTSDocResolver } from '../LSAndTSDocResolver' ;
@@ -163,76 +163,93 @@ export class CodeActionsProviderImpl implements CodeActionsProvider {
163163 const docs = new SnapshotFragmentMap ( this . lsAndTsDocResolver ) ;
164164 docs . set ( tsDoc . filePath , { fragment, snapshot : tsDoc } ) ;
165165
166- return await Promise . all (
167- codeFixes . map ( async ( fix ) => {
168- const documentChanges = await Promise . all (
169- fix . changes . map ( async ( change ) => {
170- const { snapshot , fragment } = await docs . retrieve ( change . fileName ) ;
171- return TextDocumentEdit . create (
172- OptionalVersionedTextDocumentIdentifier . create (
173- pathToUrl ( change . fileName ) ,
174- null
175- ) ,
176- change . textChanges
177- . map ( ( edit ) => {
178- if (
179- fix . fixName === 'import' &&
180- fragment instanceof SvelteSnapshotFragment
181- ) {
182- return this . completionProvider . codeActionChangeToTextEdit (
183- document ,
184- fragment ,
185- edit ,
186- true ,
187- isInTag ( range . start , document . scriptInfo ) ||
188- isInTag ( range . start , document . moduleScriptInfo )
189- ) ;
190- }
191-
192- if (
193- ! isNoTextSpanInGeneratedCode (
194- snapshot . getFullText ( ) ,
195- edit . span
196- )
197- ) {
198- return undefined ;
199- }
200-
201- let originalRange = mapRangeToOriginal (
202- fragment ,
203- convertRange ( fragment , edit . span )
204- ) ;
205-
206- if ( fix . fixName === 'unusedIdentifier' ) {
207- originalRange = this . checkRemoveImportCodeActionRange (
208- edit ,
209- fragment ,
210- originalRange
211- ) ;
212- }
213-
214- if ( fix . fixName === 'fixMissingFunctionDeclaration' ) {
215- originalRange = this . checkEndOfFileCodeInsert (
216- originalRange ,
217- range ,
218- document
219- ) ;
220- }
221-
222- return TextEdit . replace ( originalRange , edit . newText ) ;
223- } )
224- . filter ( isNotNullOrUndefined )
225- ) ;
226- } )
227- ) ;
228- return CodeAction . create (
229- fix . description ,
230- {
231- documentChanges
232- } ,
233- CodeActionKind . QuickFix
166+ const codeActionsPromises = codeFixes . map ( async ( fix ) => {
167+ const documentChangesPromises = fix . changes . map ( async ( change ) => {
168+ const { snapshot , fragment } = await docs . retrieve ( change . fileName ) ;
169+ return TextDocumentEdit . create (
170+ OptionalVersionedTextDocumentIdentifier . create (
171+ pathToUrl ( change . fileName ) ,
172+ null
173+ ) ,
174+ change . textChanges
175+ . map ( ( edit ) => {
176+ if (
177+ fix . fixName === 'import' &&
178+ fragment instanceof SvelteSnapshotFragment
179+ ) {
180+ return this . completionProvider . codeActionChangeToTextEdit (
181+ document ,
182+ fragment ,
183+ edit ,
184+ true ,
185+ isInTag ( range . start , document . scriptInfo ) ||
186+ isInTag ( range . start , document . moduleScriptInfo )
187+ ) ;
188+ }
189+
190+ if ( ! isNoTextSpanInGeneratedCode ( snapshot . getFullText ( ) , edit . span ) ) {
191+ return undefined ;
192+ }
193+
194+ let originalRange = mapRangeToOriginal (
195+ fragment ,
196+ convertRange ( fragment , edit . span )
197+ ) ;
198+
199+ if ( fix . fixName === 'unusedIdentifier' ) {
200+ originalRange = this . checkRemoveImportCodeActionRange (
201+ edit ,
202+ fragment ,
203+ originalRange
204+ ) ;
205+ }
206+
207+ if ( fix . fixName === 'fixMissingFunctionDeclaration' ) {
208+ originalRange = this . checkEndOfFileCodeInsert (
209+ originalRange ,
210+ range ,
211+ document
212+ ) ;
213+ }
214+
215+ if ( fix . fixName === 'disableJsDiagnostics' ) {
216+ if ( edit . newText . includes ( 'ts-nocheck' ) ) {
217+ return this . checkTsNoCheckCodeInsert ( document , edit ) ;
218+ }
219+
220+ return this . checkDisableJsDiagnosticsCodeInsert (
221+ originalRange ,
222+ document ,
223+ edit
224+ ) ;
225+ }
226+
227+ if ( originalRange . start . line < 0 || originalRange . end . line < 0 ) {
228+ return undefined ;
229+ }
230+
231+ return TextEdit . replace ( originalRange , edit . newText ) ;
232+ } )
233+ . filter ( isNotNullOrUndefined )
234234 ) ;
235- } )
235+ } ) ;
236+ const documentChanges = await Promise . all ( documentChangesPromises ) ;
237+ return CodeAction . create (
238+ fix . description ,
239+ {
240+ documentChanges
241+ } ,
242+ CodeActionKind . QuickFix
243+ ) ;
244+ } ) ;
245+
246+ const codeActions = await Promise . all ( codeActionsPromises ) ;
247+
248+ // filter out empty code action
249+ return codeActions . filter ( ( codeAction ) =>
250+ codeAction . edit ?. documentChanges ?. every (
251+ ( change ) => ( < TextDocumentEdit > change ) . edits . length > 0
252+ )
236253 ) ;
237254 }
238255
@@ -398,6 +415,47 @@ export class CodeActionsProviderImpl implements CodeActionsProvider {
398415 return resultRange ;
399416 }
400417
418+ private checkTsNoCheckCodeInsert (
419+ document : Document ,
420+ edit : ts . TextChange
421+ ) : TextEdit | undefined {
422+ if ( ! document . scriptInfo ) {
423+ return undefined ;
424+ }
425+
426+ const newText = ts . sys . newLine + edit . newText ;
427+
428+ return TextEdit . insert ( document . scriptInfo . startPos , newText ) ;
429+ }
430+
431+ private checkDisableJsDiagnosticsCodeInsert (
432+ originalRange : Range ,
433+ document : Document ,
434+ edit : ts . TextChange
435+ ) : TextEdit {
436+ const startOffset = document . offsetAt ( originalRange . start ) ;
437+ const text = document . getText ( ) ;
438+
439+ // svetlte2tsx removes export in instance script
440+ const insertedAfterExport = text . slice ( 0 , startOffset ) . trim ( ) . endsWith ( 'export' ) ;
441+
442+ if ( ! insertedAfterExport ) {
443+ return TextEdit . replace ( originalRange , edit . newText ) ;
444+ }
445+
446+ const position = document . positionAt ( text . lastIndexOf ( 'export' , startOffset ) ) ;
447+
448+ // fix the length of trailing indent
449+ const linesOfNewText = edit . newText . split ( '\n' ) ;
450+ if ( / ^ [ \t ] * $ / . test ( linesOfNewText [ linesOfNewText . length - 1 ] ) ) {
451+ const line = getLineAtPosition ( originalRange . start , document . getText ( ) ) ;
452+ const indent = getIndent ( line ) ;
453+ linesOfNewText [ linesOfNewText . length - 1 ] = indent ;
454+ }
455+
456+ return TextEdit . insert ( position , linesOfNewText . join ( '\n' ) ) ;
457+ }
458+
401459 private async getLSAndTSDoc ( document : Document ) {
402460 return this . lsAndTsDocResolver . getLSAndTSDoc ( document ) ;
403461 }
0 commit comments