@@ -31,14 +31,20 @@ import {
3131 scriptElementKindToCompletionItemKind
3232} from '../utils' ;
3333import { getJsDocTemplateCompletion } from './getJsDocTemplateCompletion' ;
34- import { getComponentAtPosition } from './utils' ;
34+ import { getComponentAtPosition , isPartOfImportStatement } from './utils' ;
3535
3636export interface CompletionEntryWithIdentifer extends ts . CompletionEntry , TextDocumentIdentifier {
3737 position : Position ;
3838}
3939
4040type validTriggerCharacter = '.' | '"' | "'" | '`' | '/' | '@' | '<' | '#' ;
4141
42+ type LastCompletion = {
43+ key : string ;
44+ position : Position ;
45+ completionList : AppCompletionList < CompletionEntryWithIdentifer > | null ;
46+ } ;
47+
4248export class CompletionsProviderImpl implements CompletionsProvider < CompletionEntryWithIdentifer > {
4349 constructor ( private readonly lsAndTsDocResolver : LSAndTSDocResolver ) { }
4450
@@ -48,6 +54,10 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
4854 * Therefore, only use the characters the typescript compiler treats as valid.
4955 */
5056 private readonly validTriggerCharacters = [ '.' , '"' , "'" , '`' , '/' , '@' , '<' , '#' ] as const ;
57+ /**
58+ * For performance reasons, try to reuse the last completion if possible.
59+ */
60+ private lastCompletion ?: LastCompletion ;
5161
5262 private isValidTriggerCharacter (
5363 character : string | undefined
@@ -94,6 +104,21 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
94104 return null ;
95105 }
96106
107+ if (
108+ this . canReuseLastCompletion (
109+ this . lastCompletion ,
110+ triggerKind ,
111+ triggerCharacter ,
112+ document ,
113+ position
114+ )
115+ ) {
116+ this . lastCompletion . position = position ;
117+ return this . lastCompletion . completionList ;
118+ } else {
119+ this . lastCompletion = undefined ;
120+ }
121+
97122 const fragment = await tsDoc . getFragment ( ) ;
98123 if ( ! fragment . isInGenerated ( position ) ) {
99124 return null ;
@@ -143,7 +168,28 @@ export class CompletionsProviderImpl implements CompletionsProvider<CompletionEn
143168 . map ( ( comp ) => mapCompletionItemToOriginal ( fragment , comp ) )
144169 . concat ( eventCompletions ) ;
145170
146- return CompletionList . create ( completionItems , ! ! tsDoc . parserError ) ;
171+ const completionList = CompletionList . create ( completionItems , ! ! tsDoc . parserError ) ;
172+ this . lastCompletion = { key : document . getFilePath ( ) || '' , position, completionList } ;
173+ return completionList ;
174+ }
175+
176+ private canReuseLastCompletion (
177+ lastCompletion : LastCompletion | undefined ,
178+ triggerKind : number | undefined ,
179+ triggerCharacter : string | undefined ,
180+ document : Document ,
181+ position : Position
182+ ) : lastCompletion is LastCompletion {
183+ return (
184+ ! ! lastCompletion &&
185+ lastCompletion . key === document . getFilePath ( ) &&
186+ lastCompletion . position . line === position . line &&
187+ Math . abs ( lastCompletion . position . character - position . character ) < 2 &&
188+ ( triggerKind === CompletionTriggerKind . TriggerForIncompleteCompletions ||
189+ // Special case: `.` is a trigger character, but inside import path completions
190+ // it shouldn't trigger another completion because we can reuse the old one
191+ ( triggerCharacter === '.' && isPartOfImportStatement ( document . getText ( ) , position ) ) )
192+ ) ;
147193 }
148194
149195 private getExistingImports ( document : Document ) {
0 commit comments