Skip to content

Commit 4b59083

Browse files
author
steveluc
committed
Split completions req/response pair into two messages "completions" and
"completionEntryDetails". This mirrors the function of the LS API and increases performance of completion in large projects.
1 parent 3868fb5 commit 4b59083

File tree

3 files changed

+83
-41
lines changed

3 files changed

+83
-41
lines changed

src/server/client.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/// <reference path="session.ts" />
2-
2+
33
module ts.server {
44

55
export interface SessionClientHost extends LanguageServiceHost {
@@ -172,7 +172,7 @@ module ts.server {
172172
documentation: [{ kind: "text", text: response.body.documentation }]
173173
};
174174
}
175-
175+
176176
getCompletionsAtPosition(fileName: string, position: number): CompletionInfo {
177177
var lineCol = this.positionToOneBasedLineCol(fileName, position);
178178
var args: protocol.CompletionsRequestArgs = {
@@ -185,27 +185,28 @@ module ts.server {
185185
var request = this.processRequest<protocol.CompletionsRequest>(CommandNames.Completions, args);
186186
var response = this.processResponse<protocol.CompletionsResponse>(request);
187187

188-
return this.lastCompletionEntry = {
188+
return {
189189
isMemberCompletion: false,
190190
isNewIdentifierLocation: false,
191-
entries: response.body.map(entry => ({
192-
kind: entry.kind,
193-
kindModifiers: entry.kindModifiers,
194-
name: entry.name,
195-
displayParts: entry.displayParts,
196-
documentation: entry.documentation
197-
})),
191+
entries: response.body,
198192
fileName: fileName,
199193
position: position
200194
};
201195
}
202-
196+
203197
getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails {
204-
if (!this.lastCompletionEntry || this.lastCompletionEntry.fileName !== fileName || this.lastCompletionEntry.position !== position) {
205-
this.getCompletionsAtPosition(fileName, position);
206-
}
198+
var lineCol = this.positionToOneBasedLineCol(fileName, position);
199+
var args: protocol.CompletionDetailsRequestArgs = {
200+
file: fileName,
201+
line: lineCol.line,
202+
col: lineCol.col,
203+
entryNames: [entryName]
204+
};
207205

208-
return <CompletionEntryDetails>this.lastCompletionEntry.entries.filter(entry => entry.name === entryName)[0];
206+
var request = this.processRequest<protocol.CompletionDetailsRequest>(CommandNames.CompletionDetails, args);
207+
var response = this.processResponse<protocol.CompletionDetailsResponse>(request);
208+
Debug.assert(response.body.length == 1, "Unexpected length of completion details response body.");
209+
return response.body[0];
209210
}
210211

211212
getNavigateToItems(searchTerm: string): NavigateToItem[] {

src/server/protocol.d.ts

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,26 @@ declare module ts.server.protocol {
471471
arguments: CompletionsRequestArgs;
472472
}
473473

474+
/**
475+
* Arguments for completion details request.
476+
*/
477+
export interface CompletionDetailsRequestArgs extends FileLocationRequestArgs {
478+
/**
479+
* Names of one or more entries for which to obtain details.
480+
*/
481+
entryNames: string[];
482+
}
483+
484+
/**
485+
* Completion entry details request; value of command field is
486+
* "completionEntryDetails". Given a file location (file, line,
487+
* col) and an array of completion entry names return more
488+
* detailed information for each completion entry.
489+
*/
490+
export interface CompletionDetailsRequest extends FileLocationRequest {
491+
arguments: CompletionDetailsRequestArgs;
492+
}
493+
474494
/**
475495
* Part of a symbol description.
476496
*/
@@ -489,35 +509,42 @@ declare module ts.server.protocol {
489509
/**
490510
* An item found in a completion response.
491511
*/
492-
export interface CompletionItem {
512+
export interface CompletionEntry {
493513
/**
494514
* The symbol's name.
495515
*/
496516
name: string;
497-
498517
/**
499518
* The symbol's kind (such as 'className' or 'parameterName').
500519
*/
501520
kind: string;
502-
503521
/**
504522
* Optional modifiers for the kind (such as 'public').
505523
*/
506-
kindModifiers?: string;
507-
524+
kindModifiers: string;
525+
}
526+
527+
/**
528+
* Additional completion entry details, available on demand
529+
*/
530+
export interface CompletionEntryDetails extends CompletionEntry {
508531
/**
509532
* Display parts of the symbol (similar to quick info).
510533
*/
511-
displayParts?: SymbolDisplayPart[];
534+
displayParts: SymbolDisplayPart[];
512535

513536
/**
514537
* Documentation strings for the symbol.
515538
*/
516-
documentation?: SymbolDisplayPart[];
539+
documentation: SymbolDisplayPart[];
517540
}
518541

519542
export interface CompletionsResponse extends Response {
520-
body?: CompletionItem[];
543+
body?: CompletionEntry[];
544+
}
545+
546+
export interface CompletionDetailsResponse extends Response {
547+
body?: CompletionEntryDetails[];
521548
}
522549

523550
/**

src/server/session.ts

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ module ts.server {
8181
function formatDiag(fileName: string, project: Project, diag: ts.Diagnostic) {
8282
return {
8383
start: project.compilerService.host.positionToLineCol(fileName, diag.start),
84-
end: project.compilerService.host.positionToLineCol(fileName, diag.start+diag.length),
84+
end: project.compilerService.host.positionToLineCol(fileName, diag.start + diag.length),
8585
text: ts.flattenDiagnosticMessageText(diag.messageText, "\n")
8686
};
8787
}
@@ -104,6 +104,7 @@ module ts.server {
104104
export var Change = "change";
105105
export var Close = "close";
106106
export var Completions = "completions";
107+
export var CompletionDetails = "completionEntryDetails";
107108
export var Definition = "definition";
108109
export var Format = "format";
109110
export var Formatonkey = "formatonkey";
@@ -486,8 +487,8 @@ module ts.server {
486487
};
487488
});
488489
}
489-
490-
getCompletions(line: number, col: number, prefix: string, fileName: string): protocol.CompletionItem[] {
490+
491+
getCompletions(line: number, col: number, prefix: string, fileName: string): protocol.CompletionEntry[] {
491492
if (!prefix) {
492493
prefix = "";
493494
}
@@ -505,27 +506,34 @@ module ts.server {
505506
throw Errors.NoContent;
506507
}
507508

508-
return completions.entries.reduce((result: ts.CompletionEntryDetails[], entry: ts.CompletionEntry) => {
509+
return completions.entries.reduce((result: protocol.CompletionEntry[], entry: ts.CompletionEntry) => {
509510
if (completions.isMemberCompletion || entry.name.indexOf(prefix) == 0) {
510-
var protoEntry = <ts.CompletionEntryDetails>{};
511-
protoEntry.name = entry.name;
512-
protoEntry.kind = entry.kind;
513-
if (entry.kindModifiers && (entry.kindModifiers.length > 0)) {
514-
protoEntry.kindModifiers = entry.kindModifiers;
515-
}
516-
var details = compilerService.languageService.getCompletionEntryDetails(file, position, entry.name);
517-
if (details && (details.documentation) && (details.documentation.length > 0)) {
518-
protoEntry.documentation = details.documentation;
519-
}
520-
if (details && (details.displayParts) && (details.displayParts.length > 0)) {
521-
protoEntry.displayParts = details.displayParts;
522-
}
523-
result.push(protoEntry);
511+
result.push(entry);
524512
}
525513
return result;
526514
}, []);
527515
}
528516

517+
getCompletionEntryDetails(line: number, col: number,
518+
entryNames: string[], fileName: string): protocol.CompletionEntryDetails[] {
519+
var file = ts.normalizePath(fileName);
520+
var project = this.projectService.getProjectForFile(file);
521+
if (!project) {
522+
throw Errors.NoProject;
523+
}
524+
525+
var compilerService = project.compilerService;
526+
var position = compilerService.host.lineColToPosition(file, line, col);
527+
528+
return entryNames.reduce((accum: protocol.CompletionEntryDetails[], entryName: string) => {
529+
var details = compilerService.languageService.getCompletionEntryDetails(file, position, entryName);
530+
if (details) {
531+
accum.push(details);
532+
}
533+
return accum;
534+
}, []);
535+
}
536+
529537
getDiagnostics(delay: number, fileNames: string[]) {
530538
var checkList = fileNames.reduce((accum: PendingErrorCheck[], fileName: string) => {
531539
fileName = ts.normalizePath(fileName);
@@ -724,6 +732,12 @@ module ts.server {
724732
response = this.getCompletions(request.arguments.line, request.arguments.col, completionsArgs.prefix, request.arguments.file);
725733
break;
726734
}
735+
case CommandNames.CompletionDetails: {
736+
var completionDetailsArgs = <protocol.CompletionDetailsRequestArgs>request.arguments;
737+
response = this.getCompletionEntryDetails(request.arguments.line, request.arguments.col, completionDetailsArgs.entryNames,
738+
request.arguments.file);
739+
break;
740+
}
727741
case CommandNames.Geterr: {
728742
var geterrArgs = <protocol.GeterrRequestArgs>request.arguments;
729743
response = this.getDiagnostics(geterrArgs.delay, geterrArgs.files);

0 commit comments

Comments
 (0)