22import * as Atom from "atom"
33import * as ACP from "atom/autocomplete-plus"
44import * 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"
610import { GetClientFunction , TSClient } from "../../client"
711import { handlePromise } from "../../utils"
812import { ApplyEdits } from "../pluginManager"
@@ -58,10 +62,10 @@ export class AutocompleteProvider implements ACP.AutocompleteProvider {
5862 if ( ! location ) return [ ]
5963
6064 // 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 [ ]
6569
6670 // Don't show autocomplete if we're in a string.template and not in a template expression
6771 if (
@@ -72,7 +76,12 @@ export class AutocompleteProvider implements ACP.AutocompleteProvider {
7276 }
7377
7478 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+ } )
7685
7786 suggestions = fuzzaldrin . filter ( suggestions , prefix , {
7887 key : "displayText" ,
@@ -181,11 +190,17 @@ export class AutocompleteProvider implements ACP.AutocompleteProvider {
181190 }
182191
183192 // 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 [ ] > {
189204 if ( this . lastSuggestions && ! activatedManually ) {
190205 const lastLoc = this . lastSuggestions . location
191206 const lastCol = getNormalizedCol ( this . lastSuggestions . prefix , lastLoc . offset )
@@ -199,7 +214,11 @@ export class AutocompleteProvider implements ACP.AutocompleteProvider {
199214 }
200215
201216 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+ } )
203222
204223 this . lastSuggestions = {
205224 client,
@@ -213,19 +232,21 @@ export class AutocompleteProvider implements ACP.AutocompleteProvider {
213232 }
214233}
215234
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+ } ) {
222244 if ( parseInt ( client . version . split ( "." ) [ 0 ] , 10 ) >= 3 ) {
223245 // use completionInfo
224246 const completions = await client . execute ( "completionInfo" , {
225- prefix,
226247 includeExternalModuleExports : false ,
227248 includeInsertTextCompletions : true ,
228- triggerCharacter : activatedManually ? undefined : ( prefix . slice ( - 1 ) as any ) ,
249+ triggerCharacter,
229250 ...location ,
230251 } )
231252 return completions . body ! . entries . map (
@@ -234,7 +255,6 @@ async function getSuggestionsInternal(
234255 } else {
235256 // use deprecated completions
236257 const completions = await client . execute ( "completions" , {
237- prefix,
238258 includeExternalModuleExports : false ,
239259 includeInsertTextCompletions : true ,
240260 ...location ,
@@ -392,3 +412,25 @@ const kindMap: {[key in protocol.ScriptElementKind]: ACPCompletionType | undefin
392412 index : undefined ,
393413 construct : undefined ,
394414}
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