diff --git a/src/server/client.ts b/src/server/client.ts index d51777c2e99..a0b68de840e 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -1,5 +1,5 @@ /// - + 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(CommandNames.Completions, args); var response = this.processResponse(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 this.lastCompletionEntry.entries.filter(entry => entry.name === entryName)[0]; + var request = this.processRequest(CommandNames.CompletionDetails, args); + var response = this.processResponse(request); + Debug.assert(response.body.length == 1, "Unexpected length of completion details response body."); + return response.body[0]; } getNavigateToItems(searchTerm: string): NavigateToItem[] { diff --git a/src/server/protocol.d.ts b/src/server/protocol.d.ts index a446f0176ae..b07fb20d48d 100644 --- a/src/server/protocol.d.ts +++ b/src/server/protocol.d.ts @@ -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[]; } /** diff --git a/src/server/session.ts b/src/server/session.ts index 1a0a8d98a90..bfcf82bce29 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -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 = {}; - 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 = request.arguments; + response = this.getCompletionEntryDetails(request.arguments.line, request.arguments.col, completionDetailsArgs.entryNames, + request.arguments.file); + break; + } case CommandNames.Geterr: { var geterrArgs = request.arguments; response = this.getDiagnostics(geterrArgs.delay, geterrArgs.files);