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

Commit 1a0e64d

Browse files
committed
Fixes textEdit completions
Currently the original replacement prefix sent by the LSP server in the `textEdit` field is overwritten in getSuggestions(). This, for example, causes the following case: foo-> to autocomplete to foo->->bar() instead of the expected foo->bar() This commit fixes the issue by saving the original replacement prefix (`->` in the example above), and modifying `getSuggestions()` to append to instead of overwrite this original replacement prefix.
1 parent cc8975d commit 1a0e64d

File tree

1 file changed

+38
-3
lines changed

1 file changed

+38
-3
lines changed

lib/adapters/autocomplete-adapter.ts

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,15 @@ interface SuggestionCacheEntry {
2525
isIncomplete: boolean;
2626
triggerPoint: Point;
2727
triggerChar: string;
28+
triggerPrefix: string;
2829
suggestionMap: Map<ac.AnySuggestion, PossiblyResolvedCompletionItem>;
30+
31+
// Original replacement prefixes as returned by the LSP server.
32+
//
33+
// If the server used the `textEdit` field, this value will be non-null.
34+
// Otherwise, it means that the server did not give us an explicit replacement
35+
// prefix, and therefore this value will be null.
36+
originalReplacementPrefixMap: Map<ac.AnySuggestion, string | null>;
2937
}
3038

3139
type CompletionItemAdjuster =
@@ -84,12 +92,25 @@ export default class AutocompleteAdapter {
8492

8593
// Get the suggestions either from the cache or by calling the language server
8694
const suggestions = await
87-
this.getOrBuildSuggestions(server, request, triggerChar, triggerOnly, onDidConvertCompletionItem);
95+
this.getOrBuildSuggestions(server, request, triggerChar, triggerOnly, request.prefix, onDidConvertCompletionItem);
8896

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

95116
const filtered = !(request.prefix === "" || (triggerChar !== '' && triggerOnly));
@@ -112,6 +133,7 @@ export default class AutocompleteAdapter {
112133
request: ac.SuggestionsRequestedEvent,
113134
triggerChar: string,
114135
triggerOnly: boolean,
136+
triggerPrefix: string,
115137
onDidConvertCompletionItem?: CompletionItemAdjuster,
116138
): Promise<ac.AnySuggestion[]> {
117139
const cache = this._suggestionCache.get(server);
@@ -136,7 +158,20 @@ export default class AutocompleteAdapter {
136158
// Setup the cache for subsequent filtered results
137159
const isComplete = completions == null || Array.isArray(completions) || completions.isIncomplete === false;
138160
const suggestionMap = this.completionItemsToSuggestions(completions, request, onDidConvertCompletionItem);
139-
this._suggestionCache.set(server, { isIncomplete: !isComplete, triggerChar, triggerPoint, suggestionMap });
161+
const originalReplacementPrefixMap = new Map<ac.AnySuggestion, string>(
162+
Array.from(suggestionMap.keys()).map<[ac.AnySuggestion, string]>(
163+
(suggestion) => [suggestion, suggestion.replacementPrefix || '']
164+
)
165+
);
166+
167+
this._suggestionCache.set(server, {
168+
isIncomplete: !isComplete,
169+
triggerChar,
170+
triggerPoint,
171+
triggerPrefix: (triggerChar !== '' && triggerOnly) ? '' : triggerPrefix,
172+
suggestionMap,
173+
originalReplacementPrefixMap,
174+
});
140175

141176
return Array.from(suggestionMap.keys());
142177
}

0 commit comments

Comments
 (0)