2
2
import * as Atom from "atom"
3
3
import * as ACP from "atom/autocomplete-plus"
4
4
import * as fuzzaldrin from "fuzzaldrin"
5
- import { CompletionEntryDetails , CompletionEntryIdentifier } from "typescript/lib/protocol"
5
+ import {
6
+ CompletionEntryDetails ,
7
+ CompletionEntryIdentifier ,
8
+ CompletionsTriggerCharacter ,
9
+ } from "typescript/lib/protocol"
6
10
import { GetClientFunction , TSClient } from "../../client"
7
11
import { handlePromise } from "../../utils"
8
12
import { ApplyEdits } from "../pluginManager"
@@ -58,10 +62,10 @@ export class AutocompleteProvider implements ACP.AutocompleteProvider {
58
62
if ( ! location ) return [ ]
59
63
60
64
// Don't auto-show autocomplete if prefix is empty unless last character is '.'
61
- if ( ! prefix && ! opts . activatedManually ) {
62
- const lastChar = getLastNonWhitespaceChar ( opts . editor . getBuffer ( ) , opts . bufferPosition )
63
- if ( lastChar !== "." ) return [ ]
64
- }
65
+ const triggerCharacter = getTrigger (
66
+ getLastNonWhitespaceChar ( opts . editor . getBuffer ( ) , opts . bufferPosition ) ,
67
+ )
68
+ if ( ! prefix && ! opts . activatedManually && ! triggerCharacter ) return [ ]
65
69
66
70
// Don't show autocomplete if we're in a string.template and not in a template expression
67
71
if (
@@ -72,7 +76,12 @@ export class AutocompleteProvider implements ACP.AutocompleteProvider {
72
76
}
73
77
74
78
try {
75
- let suggestions = await this . getSuggestionsWithCache ( prefix , location , opts . activatedManually )
79
+ let suggestions = await this . getSuggestionsWithCache ( {
80
+ prefix,
81
+ location,
82
+ triggerCharacter,
83
+ activatedManually : opts . activatedManually ,
84
+ } )
76
85
77
86
suggestions = fuzzaldrin . filter ( suggestions , prefix , {
78
87
key : "displayText" ,
@@ -181,11 +190,17 @@ export class AutocompleteProvider implements ACP.AutocompleteProvider {
181
190
}
182
191
183
192
// Try to reuse the last completions we got from tsserver if they're for the same position.
184
- private async getSuggestionsWithCache (
185
- prefix : string ,
186
- location : FileLocationQuery ,
187
- activatedManually : boolean ,
188
- ) : Promise < SuggestionWithDetails [ ] > {
193
+ private async getSuggestionsWithCache ( {
194
+ prefix,
195
+ location,
196
+ triggerCharacter,
197
+ activatedManually,
198
+ } : {
199
+ prefix : string
200
+ location : FileLocationQuery
201
+ triggerCharacter ?: CompletionsTriggerCharacter
202
+ activatedManually : boolean
203
+ } ) : Promise < SuggestionWithDetails [ ] > {
189
204
if ( this . lastSuggestions && ! activatedManually ) {
190
205
const lastLoc = this . lastSuggestions . location
191
206
const lastCol = getNormalizedCol ( this . lastSuggestions . prefix , lastLoc . offset )
@@ -199,7 +214,11 @@ export class AutocompleteProvider implements ACP.AutocompleteProvider {
199
214
}
200
215
201
216
const client = await this . getClient ( location . file )
202
- const suggestions = await getSuggestionsInternal ( client , location , prefix , activatedManually )
217
+ const suggestions = await getSuggestionsInternal ( {
218
+ client,
219
+ location,
220
+ triggerCharacter : activatedManually ? undefined : triggerCharacter ,
221
+ } )
203
222
204
223
this . lastSuggestions = {
205
224
client,
@@ -213,19 +232,21 @@ export class AutocompleteProvider implements ACP.AutocompleteProvider {
213
232
}
214
233
}
215
234
216
- async function getSuggestionsInternal (
217
- client : TSClient ,
218
- location : FileLocationQuery ,
219
- prefix : string ,
220
- activatedManually : boolean ,
221
- ) {
235
+ async function getSuggestionsInternal ( {
236
+ client,
237
+ location,
238
+ triggerCharacter,
239
+ } : {
240
+ client : TSClient
241
+ location : FileLocationQuery
242
+ triggerCharacter ?: CompletionsTriggerCharacter
243
+ } ) {
222
244
if ( parseInt ( client . version . split ( "." ) [ 0 ] , 10 ) >= 3 ) {
223
245
// use completionInfo
224
246
const completions = await client . execute ( "completionInfo" , {
225
- prefix,
226
247
includeExternalModuleExports : false ,
227
248
includeInsertTextCompletions : true ,
228
- triggerCharacter : activatedManually ? undefined : ( prefix . slice ( - 1 ) as any ) ,
249
+ triggerCharacter,
229
250
...location ,
230
251
} )
231
252
return completions . body ! . entries . map (
@@ -234,7 +255,6 @@ async function getSuggestionsInternal(
234
255
} else {
235
256
// use deprecated completions
236
257
const completions = await client . execute ( "completions" , {
237
- prefix,
238
258
includeExternalModuleExports : false ,
239
259
includeInsertTextCompletions : true ,
240
260
...location ,
@@ -392,3 +412,25 @@ const kindMap: {[key in protocol.ScriptElementKind]: ACPCompletionType | undefin
392
412
index : undefined ,
393
413
construct : undefined ,
394
414
}
415
+
416
+ // This may look strange, but it guarantees the list is consistent with the type
417
+ const triggerCharactersMap : { [ K in CompletionsTriggerCharacter ] : null } = {
418
+ "." : null ,
419
+ '"' : null ,
420
+ "'" : null ,
421
+ "`" : null ,
422
+ "/" : null ,
423
+ "@" : null ,
424
+ "<" : null ,
425
+ "#" : null ,
426
+ }
427
+ const triggerCharacters = new Set < CompletionsTriggerCharacter > ( Object . keys ( triggerCharactersMap ) )
428
+ function getTrigger ( prefix : string | undefined ) : CompletionsTriggerCharacter | undefined {
429
+ if ( prefix === undefined ) return undefined
430
+ if ( ! prefix ) return undefined
431
+ const c = prefix . slice ( - 1 )
432
+ if ( triggerCharacters . has ( c as CompletionsTriggerCharacter ) ) {
433
+ return c as CompletionsTriggerCharacter
434
+ }
435
+ return undefined
436
+ }
0 commit comments