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.
This commit is contained in:
steveluc 2015-02-18 15:12:35 -08:00
parent 3868fb5a6b
commit 4b590836e7
3 changed files with 83 additions and 41 deletions

View file

@ -1,5 +1,5 @@
/// <reference path="session.ts" />
module ts.server {
export interface SessionClientHost extends LanguageServiceHost {
@ -172,7 +172,7 @@ module ts.server {
documentation: [{ kind: "text", text: response.body.documentation }]
};
}
getCompletionsAtPosition(fileName: string, position: number): CompletionInfo {
var lineCol = this.positionToOneBasedLineCol(fileName, position);
var args: protocol.CompletionsRequestArgs = {
@ -185,27 +185,28 @@ module ts.server {
var request = this.processRequest<protocol.CompletionsRequest>(CommandNames.Completions, args);
var response = this.processResponse<protocol.CompletionsResponse>(request);
return this.lastCompletionEntry = {
return {
isMemberCompletion: false,
isNewIdentifierLocation: false,
entries: response.body.map(entry => ({
kind: entry.kind,
kindModifiers: entry.kindModifiers,
name: entry.name,
displayParts: entry.displayParts,
documentation: entry.documentation
})),
entries: response.body,
fileName: fileName,
position: position
};
}
getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails {
if (!this.lastCompletionEntry || this.lastCompletionEntry.fileName !== fileName || this.lastCompletionEntry.position !== position) {
this.getCompletionsAtPosition(fileName, position);
}
var lineCol = this.positionToOneBasedLineCol(fileName, position);
var args: protocol.CompletionDetailsRequestArgs = {
file: fileName,
line: lineCol.line,
col: lineCol.col,
entryNames: [entryName]
};
return <CompletionEntryDetails>this.lastCompletionEntry.entries.filter(entry => entry.name === entryName)[0];
var request = this.processRequest<protocol.CompletionDetailsRequest>(CommandNames.CompletionDetails, args);
var response = this.processResponse<protocol.CompletionDetailsResponse>(request);
Debug.assert(response.body.length == 1, "Unexpected length of completion details response body.");
return response.body[0];
}
getNavigateToItems(searchTerm: string): NavigateToItem[] {

View file

@ -471,6 +471,26 @@ declare module ts.server.protocol {
arguments: CompletionsRequestArgs;
}
/**
* Arguments for completion details request.
*/
export interface CompletionDetailsRequestArgs extends FileLocationRequestArgs {
/**
* Names of one or more entries for which to obtain details.
*/
entryNames: string[];
}
/**
* Completion entry details request; value of command field is
* "completionEntryDetails". Given a file location (file, line,
* col) and an array of completion entry names return more
* detailed information for each completion entry.
*/
export interface CompletionDetailsRequest extends FileLocationRequest {
arguments: CompletionDetailsRequestArgs;
}
/**
* Part of a symbol description.
*/
@ -489,35 +509,42 @@ declare module ts.server.protocol {
/**
* An item found in a completion response.
*/
export interface CompletionItem {
export interface CompletionEntry {
/**
* The symbol's name.
*/
name: string;
/**
* The symbol's kind (such as 'className' or 'parameterName').
*/
kind: string;
/**
* Optional modifiers for the kind (such as 'public').
*/
kindModifiers?: string;
kindModifiers: string;
}
/**
* Additional completion entry details, available on demand
*/
export interface CompletionEntryDetails extends CompletionEntry {
/**
* Display parts of the symbol (similar to quick info).
*/
displayParts?: SymbolDisplayPart[];
displayParts: SymbolDisplayPart[];
/**
* Documentation strings for the symbol.
*/
documentation?: SymbolDisplayPart[];
documentation: SymbolDisplayPart[];
}
export interface CompletionsResponse extends Response {
body?: CompletionItem[];
body?: CompletionEntry[];
}
export interface CompletionDetailsResponse extends Response {
body?: CompletionEntryDetails[];
}
/**

View file

@ -81,7 +81,7 @@ module ts.server {
function formatDiag(fileName: string, project: Project, diag: ts.Diagnostic) {
return {
start: project.compilerService.host.positionToLineCol(fileName, diag.start),
end: project.compilerService.host.positionToLineCol(fileName, diag.start+diag.length),
end: project.compilerService.host.positionToLineCol(fileName, diag.start + diag.length),
text: ts.flattenDiagnosticMessageText(diag.messageText, "\n")
};
}
@ -104,6 +104,7 @@ module ts.server {
export var Change = "change";
export var Close = "close";
export var Completions = "completions";
export var CompletionDetails = "completionEntryDetails";
export var Definition = "definition";
export var Format = "format";
export var Formatonkey = "formatonkey";
@ -486,8 +487,8 @@ module ts.server {
};
});
}
getCompletions(line: number, col: number, prefix: string, fileName: string): protocol.CompletionItem[] {
getCompletions(line: number, col: number, prefix: string, fileName: string): protocol.CompletionEntry[] {
if (!prefix) {
prefix = "";
}
@ -505,27 +506,34 @@ module ts.server {
throw Errors.NoContent;
}
return completions.entries.reduce((result: ts.CompletionEntryDetails[], entry: ts.CompletionEntry) => {
return completions.entries.reduce((result: protocol.CompletionEntry[], entry: ts.CompletionEntry) => {
if (completions.isMemberCompletion || entry.name.indexOf(prefix) == 0) {
var protoEntry = <ts.CompletionEntryDetails>{};
protoEntry.name = entry.name;
protoEntry.kind = entry.kind;
if (entry.kindModifiers && (entry.kindModifiers.length > 0)) {
protoEntry.kindModifiers = entry.kindModifiers;
}
var details = compilerService.languageService.getCompletionEntryDetails(file, position, entry.name);
if (details && (details.documentation) && (details.documentation.length > 0)) {
protoEntry.documentation = details.documentation;
}
if (details && (details.displayParts) && (details.displayParts.length > 0)) {
protoEntry.displayParts = details.displayParts;
}
result.push(protoEntry);
result.push(entry);
}
return result;
}, []);
}
getCompletionEntryDetails(line: number, col: number,
entryNames: string[], fileName: string): protocol.CompletionEntryDetails[] {
var file = ts.normalizePath(fileName);
var project = this.projectService.getProjectForFile(file);
if (!project) {
throw Errors.NoProject;
}
var compilerService = project.compilerService;
var position = compilerService.host.lineColToPosition(file, line, col);
return entryNames.reduce((accum: protocol.CompletionEntryDetails[], entryName: string) => {
var details = compilerService.languageService.getCompletionEntryDetails(file, position, entryName);
if (details) {
accum.push(details);
}
return accum;
}, []);
}
getDiagnostics(delay: number, fileNames: string[]) {
var checkList = fileNames.reduce((accum: PendingErrorCheck[], fileName: string) => {
fileName = ts.normalizePath(fileName);
@ -724,6 +732,12 @@ module ts.server {
response = this.getCompletions(request.arguments.line, request.arguments.col, completionsArgs.prefix, request.arguments.file);
break;
}
case CommandNames.CompletionDetails: {
var completionDetailsArgs = <protocol.CompletionDetailsRequestArgs>request.arguments;
response = this.getCompletionEntryDetails(request.arguments.line, request.arguments.col, completionDetailsArgs.entryNames,
request.arguments.file);
break;
}
case CommandNames.Geterr: {
var geterrArgs = <protocol.GeterrRequestArgs>request.arguments;
response = this.getDiagnostics(geterrArgs.delay, geterrArgs.files);