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

Commit f4c1ba2

Browse files
authored
Merge pull request #148 from atom/autocomplete-resolve
Resolve implementation for new AutoComplete hook
2 parents 02d8f62 + 9be7924 commit f4c1ba2

File tree

4 files changed

+104
-14
lines changed

4 files changed

+104
-14
lines changed

flow-libs/atom.js.flow

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1763,6 +1763,9 @@ type atom$AutocompleteProvider = {
17631763
getSuggestions: (
17641764
request: atom$AutocompleteRequest,
17651765
) => Promise<?Array<atom$AutocompleteSuggestion>>,
1766+
getSuggestionDetailsOnFocus: (
1767+
suggestion: atom$AutocompleteSuggestion,
1768+
) => Promise<?atom$AutocompleteSuggestion>,
17661769
disableForSelector?: string,
17671770
inclusionPriority?: number,
17681771
excludeLowerPriority?: boolean,

lib/adapters/autocomplete-adapter.js

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,49 @@ export default class AutocompleteAdapter {
1818
return serverCapabilities.completionProvider != null;
1919
}
2020

21-
// Public: Primary entry point for obtaining suggestions for AutoComplete+ by
22-
// querying the language server.
21+
_lastSuggestions: Map<atom$AutocompleteSuggestion, [CompletionItem, boolean]> = new Map();
22+
23+
// Public: Obtain suggestion list for AutoComplete+ by querying the language server using
24+
// the `textDocument/completion` request.
2325
//
2426
// * `connection` A {LanguageClientConnection} to the language server to query.
2527
// * `request` An {Object} with the AutoComplete+ request to satisfy.
2628
//
27-
// Returns a {Promise} of an {Array} of {Object}s containing the AutoComplete+
28-
// suggestions to display.
29+
// Returns a {Promise} of an {Array} of {atom$AutocompleteSuggestion}s containing the
30+
// AutoComplete+ suggestions to display.
2931
async getSuggestions(
3032
connection: LanguageClientConnection,
3133
request: atom$AutocompleteRequest,
3234
): Promise<Array<atom$AutocompleteSuggestion>> {
33-
const completionItems = await connection.completion(
34-
AutocompleteAdapter.requestToTextDocumentPositionParams(request),
35-
);
36-
return AutocompleteAdapter.completionItemsToSuggestions(completionItems, request);
35+
const items = await connection.completion(AutocompleteAdapter.requestToTextDocumentPositionParams(request));
36+
return this.completionItemsToSuggestions(items, request);
37+
}
38+
39+
// Public: Obtain a complete version of a suggestion with additional information
40+
// the language server can provide by way of the `completionItem/resolve` request.
41+
//
42+
// * `connection` A {LanguageClientConnection} to the language server to query.
43+
// * `request` An {Object} with the AutoComplete+ request to satisfy.
44+
//
45+
// Returns a {Promise} of an {atom$AutocompleteSuggestion} with the resolved AutoComplete+
46+
// suggestion.
47+
async completeSuggestion(
48+
connection: LanguageClientConnection,
49+
suggestion: atom$AutocompleteSuggestion,
50+
request: atom$AutocompleteRequest,
51+
): Promise<atom$AutocompleteSuggestion> {
52+
const originalCompletionItem = this._lastSuggestions.get(suggestion);
53+
if (originalCompletionItem != null && originalCompletionItem[1] === false) {
54+
const resolveCompletionItem = await connection.completionItemResolve(originalCompletionItem[0]);
55+
if (resolveCompletionItem != null) {
56+
const resolvedSuggestion = AutocompleteAdapter.completionItemToSuggestion(resolveCompletionItem, request);
57+
this._lastSuggestions.delete(suggestion);
58+
this._lastSuggestions.set(resolvedSuggestion, [resolveCompletionItem, true]);
59+
return resolvedSuggestion;
60+
}
61+
}
62+
63+
return suggestion;
3764
}
3865

3966
// Public: Create TextDocumentPositionParams to be sent to the language server
@@ -59,13 +86,14 @@ export default class AutocompleteAdapter {
5986
// * `request` An {Object} with the AutoComplete+ request to use.
6087
//
6188
// Returns an {Array} of AutoComplete+ suggestions ordered by the CompletionItems sortText.
62-
static completionItemsToSuggestions(
89+
completionItemsToSuggestions(
6390
completionItems: Array<CompletionItem> | CompletionList,
6491
request: atom$AutocompleteRequest,
6592
): Array<atom$AutocompleteSuggestion> {
66-
return (Array.isArray(completionItems) ? completionItems : completionItems.items || [])
93+
this._lastSuggestions = new Map((Array.isArray(completionItems) ? completionItems : completionItems.items || [])
6794
.sort((a, b) => (a.sortText || a.label).localeCompare(b.sortText || b.label))
68-
.map(s => AutocompleteAdapter.completionItemToSuggestion(s, request));
95+
.map(s => [AutocompleteAdapter.completionItemToSuggestion(s, request), [s, false]]));
96+
return Array.from(this._lastSuggestions.keys());
6997
}
7098

7199
// Public: Convert a language server protocol CompletionItem to an AutoComplete+ suggestion.

lib/auto-languageclient.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export default class AutoLanguageClient {
3434
_serverManager: ServerManager;
3535
_linterDelegate: linter$V2IndieDelegate;
3636
_signatureHelpRegistry: ?atomIde$SignatureHelpRegistry;
37+
_lastAutocompleteRequest: ?atom$AutocompleteRequest;
3738

3839
// Available if consumeBusySignal is setup
3940
busySignalService: ?atomIde$BusySignalService;
@@ -348,10 +349,12 @@ export default class AutoLanguageClient {
348349
suggestionPriority: 2,
349350
excludeLowerPriority: false,
350351
getSuggestions: this.getSuggestions.bind(this),
352+
getSuggestionDetailsOnFocus: this.getSuggestionDetailsOnFocus.bind(this),
351353
};
352354
}
353355

354356
async getSuggestions(request: atom$AutocompleteRequest): Promise<Array<atom$AutocompleteSuggestion>> {
357+
this._lastAutocompleteRequest = request;
355358
const server = await this._serverManager.getServer(request.editor);
356359
if (server == null || !AutocompleteAdapter.canAdapt(server.capabilities)) {
357360
return [];
@@ -361,6 +364,23 @@ export default class AutoLanguageClient {
361364
return this.autoComplete.getSuggestions(server.connection, request);
362365
}
363366

367+
async getSuggestionDetailsOnFocus(suggestion: atom$AutocompleteSuggestion): Promise<?atom$AutocompleteSuggestion> {
368+
if (this._lastAutocompleteRequest == null || this.autoComplete == null) {
369+
return null;
370+
}
371+
372+
const server = await this._serverManager.getServer(this._lastAutocompleteRequest.editor);
373+
if (server == null || !AutocompleteAdapter.canAdapt(server.capabilities)) {
374+
return null;
375+
}
376+
377+
if (this._lastAutocompleteRequest != null && this.autoComplete != null) {
378+
return this.autoComplete.completeSuggestion(server.connection, suggestion, this._lastAutocompleteRequest);
379+
}
380+
381+
return null;
382+
}
383+
364384
// Definitions via LS documentHighlight and gotoDefinition------------
365385
provideDefinitions(): atomIde$DefinitionProvider {
366386
return {

test/adapters/autocomplete-adapter.test.js

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,42 @@ describe('AutoCompleteAdapter', () => {
5959
});
6060
});
6161

62+
describe('completeSuggestion', () => {
63+
const partialItems = [
64+
{
65+
label: 'label1',
66+
kind: ls.CompletionItemKind.Keyword,
67+
sortText: 'z',
68+
},
69+
{
70+
label: 'label2',
71+
kind: ls.CompletionItemKind.Field,
72+
sortText: 'a',
73+
},
74+
{
75+
label: 'label3',
76+
kind: ls.CompletionItemKind.Variable,
77+
},
78+
];
79+
80+
const fakeLanguageClient = new ls.LanguageClientConnection(createSpyConnection());
81+
sinon.stub(fakeLanguageClient, 'completion').resolves(partialItems);
82+
sinon.stub(fakeLanguageClient, 'completionItemResolve').resolves({
83+
label: 'label3',
84+
kind: ls.CompletionItemKind.Variable,
85+
detail: 'description3',
86+
documentation: 'a very exciting variable',
87+
});
88+
89+
it('resolves suggestions via LSP given an AutoCompleteRequest', async () => {
90+
const autoCompleteAdapter = new AutoCompleteAdapter();
91+
const results: Array<atom$AutocompleteSuggestion> = await autoCompleteAdapter.getSuggestions(fakeLanguageClient, request);
92+
expect(results[2].description).equals(undefined);
93+
const resolvedItem = await autoCompleteAdapter.completeSuggestion(fakeLanguageClient, results[2], request);
94+
expect(resolvedItem.description).equals('a very exciting variable');
95+
});
96+
});
97+
6298
describe('requestToTextDocumentPositionParams', () => {
6399
it('creates a TextDocumentPositionParams from an AutocompleteRequest', () => {
64100
const result = AutoCompleteAdapter.requestToTextDocumentPositionParams(request);
@@ -69,7 +105,8 @@ describe('AutoCompleteAdapter', () => {
69105

70106
describe('completionItemsToSuggestions', () => {
71107
it('converts LSP CompletionItem array to AutoComplete Suggestions array', () => {
72-
const results = AutoCompleteAdapter.completionItemsToSuggestions(completionItems, request);
108+
const autoCompleteAdapter = new AutoCompleteAdapter();
109+
const results = autoCompleteAdapter.completionItemsToSuggestions(completionItems, request);
73110
expect(results.length).equals(3);
74111
expect(results[0].text).equals('label2');
75112
expect(results[1].description).equals('a very exciting variable');
@@ -78,14 +115,16 @@ describe('AutoCompleteAdapter', () => {
78115

79116
it('converts LSP CompletionList to AutoComplete Suggestions array', () => {
80117
const completionList = {items: completionItems, isIncomplete: false};
81-
const results = AutoCompleteAdapter.completionItemsToSuggestions(completionList, request);
118+
const autoCompleteAdapter = new AutoCompleteAdapter();
119+
const results = autoCompleteAdapter.completionItemsToSuggestions(completionList, request);
82120
expect(results.length).equals(3);
83121
expect(results[0].description).equals('a very exciting field');
84122
expect(results[1].text).equals('label3');
85123
});
86124

87125
it('converts empty array into an empty AutoComplete Suggestions array', () => {
88-
const results = AutoCompleteAdapter.completionItemsToSuggestions([], request);
126+
const autoCompleteAdapter = new AutoCompleteAdapter();
127+
const results = autoCompleteAdapter.completionItemsToSuggestions([], request);
89128
expect(results.length).equals(0);
90129
});
91130
});

0 commit comments

Comments
 (0)