Skip to content
This repository has been archived by the owner on Dec 15, 2022. It is now read-only.

Fixes textEdit completions #254

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions lib/adapters/autocomplete-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,15 @@ interface SuggestionCacheEntry {
isIncomplete: boolean;
triggerPoint: Point;
triggerChar: string;
triggerPrefix: string;
suggestionMap: Map<ac.AnySuggestion, PossiblyResolvedCompletionItem>;

// Original replacement prefixes as returned by the LSP server.
//
// If the server used the `textEdit` field, this value will be non-null.
// Otherwise, it means that the server did not give us an explicit replacement
// prefix, and therefore this value will be null.
originalReplacementPrefixMap: Map<ac.AnySuggestion, string | null>;
}

type CompletionItemAdjuster =
Expand Down Expand Up @@ -86,10 +94,23 @@ export default class AutocompleteAdapter {
const suggestions = await
this.getOrBuildSuggestions(server, request, triggerChar, triggerOnly, onDidConvertCompletionItem);

// Force unwrapping here is okay since this.getOrBuildSuggestions ensured that the following get()
// would not return undefined.
const cache = this._suggestionCache.get(server)!;
// As the user types more characters to refine filter we must replace those characters on acceptance
const replacementPrefix = (triggerChar !== '' && triggerOnly) ? '' : request.prefix;
const originalReplacementPrefixMap = cache.originalReplacementPrefixMap;
for (const suggestion of suggestions) {
suggestion.replacementPrefix = replacementPrefix;
// Force unwrapping here is okay for similar reasons as in the comment above.
const originalReplacementPrefix = originalReplacementPrefixMap.get(suggestion);
if (originalReplacementPrefix) {
// The server gave us a replacement prefix via the `textEdit` field, which we must honor. However,
// we also need to append the extra bits that the user has typed since we made the initial request.
const extraReplacementPrefix = replacementPrefix.substr(cache.triggerPrefix.length);
suggestion.replacementPrefix = originalReplacementPrefix + extraReplacementPrefix;
} else {
suggestion.replacementPrefix = replacementPrefix;
}
}

const filtered = !(request.prefix === "" || (triggerChar !== '' && triggerOnly));
Expand Down Expand Up @@ -136,7 +157,20 @@ export default class AutocompleteAdapter {
// Setup the cache for subsequent filtered results
const isComplete = completions == null || Array.isArray(completions) || completions.isIncomplete === false;
const suggestionMap = this.completionItemsToSuggestions(completions, request, onDidConvertCompletionItem);
this._suggestionCache.set(server, { isIncomplete: !isComplete, triggerChar, triggerPoint, suggestionMap });
const originalReplacementPrefixMap = new Map<ac.AnySuggestion, string>(
Array.from(suggestionMap.keys()).map<[ac.AnySuggestion, string]>(
(suggestion) => [suggestion, suggestion.replacementPrefix || ''],
),
);

this._suggestionCache.set(server, {
isIncomplete: !isComplete,
triggerChar,
triggerPoint,
triggerPrefix: (triggerChar !== '' && triggerOnly) ? '' : request.prefix,
suggestionMap,
originalReplacementPrefixMap,
});

return Array.from(suggestionMap.keys());
}
Expand Down