Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 82 additions & 149 deletions src/server/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ namespace ts.server {

export class SessionClient implements LanguageService {
private sequence = 0;
private lineMaps: ts.Map<number[]> = ts.createMap<number[]>();
private lineMaps: Map<number[]> = createMap<number[]>();
private messages: string[] = [];
private lastRenameEntry: RenameEntry;

Expand All @@ -53,33 +53,27 @@ namespace ts.server {
let lineMap = this.lineMaps.get(fileName);
if (!lineMap) {
const scriptSnapshot = this.host.getScriptSnapshot(fileName);
lineMap = ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength()));
lineMap = computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength()));
this.lineMaps.set(fileName, lineMap);
}
return lineMap;
}

private lineOffsetToPosition(fileName: string, lineOffset: protocol.Location, lineMap?: number[]): number {
lineMap = lineMap || this.getLineMap(fileName);
return ts.computePositionOfLineAndCharacter(lineMap, lineOffset.line - 1, lineOffset.offset - 1);
return computePositionOfLineAndCharacter(lineMap, lineOffset.line - 1, lineOffset.offset - 1);
}

private positionToOneBasedLineOffset(fileName: string, position: number): protocol.Location {
const lineOffset = ts.computeLineAndCharacterOfPosition(this.getLineMap(fileName), position);
const lineOffset = computeLineAndCharacterOfPosition(this.getLineMap(fileName), position);
return {
line: lineOffset.line + 1,
offset: lineOffset.character + 1
};
}

private convertCodeEditsToTextChange(fileName: string, codeEdit: protocol.CodeEdit): ts.TextChange {
const start = this.lineOffsetToPosition(fileName, codeEdit.start);
const end = this.lineOffsetToPosition(fileName, codeEdit.end);

return {
span: ts.createTextSpanFromBounds(start, end),
newText: codeEdit.newText
};
return { span: this.decodeSpan(codeEdit, fileName), newText: codeEdit.newText };
}

private processRequest<T extends protocol.Request>(command: string, args?: any): T {
Expand Down Expand Up @@ -154,13 +148,10 @@ namespace ts.server {
const request = this.processRequest<protocol.QuickInfoRequest>(CommandNames.Quickinfo, args);
const response = this.processResponse<protocol.QuickInfoResponse>(request);

const start = this.lineOffsetToPosition(fileName, response.body.start);
const end = this.lineOffsetToPosition(fileName, response.body.end);

return {
kind: response.body.kind,
kindModifiers: response.body.kindModifiers,
textSpan: ts.createTextSpanFromBounds(start, end),
textSpan: this.decodeSpan(response.body, fileName),
displayParts: [{ kind: "text", text: response.body.displayString }],
documentation: [{ kind: "text", text: response.body.documentation }],
tags: response.body.tags
Expand Down Expand Up @@ -192,11 +183,8 @@ namespace ts.server {
entries: response.body.map(entry => {

if (entry.replacementSpan !== undefined) {
const { name, kind, kindModifiers, sortText, replacementSpan} = entry;

const convertedSpan = createTextSpanFromBounds(this.lineOffsetToPosition(fileName, replacementSpan.start),
this.lineOffsetToPosition(fileName, replacementSpan.end));
return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan };
const { name, kind, kindModifiers, sortText, replacementSpan } = entry;
return { name, kind, kindModifiers, sortText, replacementSpan: this.decodeSpan(replacementSpan, fileName) };
}

return entry as { name: string, kind: ScriptElementKind, kindModifiers: string, sortText: string };
Expand Down Expand Up @@ -226,36 +214,31 @@ namespace ts.server {
const request = this.processRequest<protocol.NavtoRequest>(CommandNames.Navto, args);
const response = this.processResponse<protocol.NavtoResponse>(request);

return response.body.map(entry => {
const fileName = entry.file;
const start = this.lineOffsetToPosition(fileName, entry.start);
const end = this.lineOffsetToPosition(fileName, entry.end);

return {
name: entry.name,
containerName: entry.containerName || "",
containerKind: entry.containerKind || ScriptElementKind.unknown,
kind: entry.kind,
kindModifiers: entry.kindModifiers,
matchKind: entry.matchKind,
isCaseSensitive: entry.isCaseSensitive,
fileName,
textSpan: ts.createTextSpanFromBounds(start, end)
};
});
return response.body.map(entry => ({
name: entry.name,
containerName: entry.containerName || "",
containerKind: entry.containerKind || ScriptElementKind.unknown,
kind: entry.kind,
kindModifiers: entry.kindModifiers,
matchKind: entry.matchKind,
isCaseSensitive: entry.isCaseSensitive,
fileName: entry.file,
textSpan: this.decodeSpan(entry),
}));
}

getFormattingEditsForRange(file: string, start: number, end: number, _options: ts.FormatCodeOptions): ts.TextChange[] {
const args: protocol.FormatRequestArgs = this.createFileLocationRequestArgsWithEndLineAndOffset(file, start, end);


// TODO: handle FormatCodeOptions
const request = this.processRequest<protocol.FormatRequest>(CommandNames.Format, args);
const response = this.processResponse<protocol.FormatResponse>(request);

return response.body.map(entry => this.convertCodeEditsToTextChange(file, entry));
}

getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): ts.TextChange[] {
getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions): ts.TextChange[] {
return this.getFormattingEditsForRange(fileName, 0, this.host.getScriptSnapshot(fileName).getLength(), options);
}

Expand All @@ -275,19 +258,14 @@ namespace ts.server {
const request = this.processRequest<protocol.DefinitionRequest>(CommandNames.Definition, args);
const response = this.processResponse<protocol.DefinitionResponse>(request);

return response.body.map(entry => {
const fileName = entry.file;
const start = this.lineOffsetToPosition(fileName, entry.start);
const end = this.lineOffsetToPosition(fileName, entry.end);
return {
containerKind: ScriptElementKind.unknown,
containerName: "",
fileName,
textSpan: ts.createTextSpanFromBounds(start, end),
kind: ScriptElementKind.unknown,
name: ""
};
});
return response.body.map(entry => ({
containerKind: ScriptElementKind.unknown,
containerName: "",
fileName: entry.file,
textSpan: this.decodeSpan(entry),
kind: ScriptElementKind.unknown,
name: ""
}));
}

getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] {
Expand All @@ -296,19 +274,14 @@ namespace ts.server {
const request = this.processRequest<protocol.TypeDefinitionRequest>(CommandNames.TypeDefinition, args);
const response = this.processResponse<protocol.TypeDefinitionResponse>(request);

return response.body.map(entry => {
const fileName = entry.file;
const start = this.lineOffsetToPosition(fileName, entry.start);
const end = this.lineOffsetToPosition(fileName, entry.end);
return {
containerKind: ScriptElementKind.unknown,
containerName: "",
fileName,
textSpan: ts.createTextSpanFromBounds(start, end),
kind: ScriptElementKind.unknown,
name: ""
};
});
return response.body.map(entry => ({
containerKind: ScriptElementKind.unknown,
containerName: "",
fileName: entry.file,
textSpan: this.decodeSpan(entry),
kind: ScriptElementKind.unknown,
name: ""
}));
}

getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[] {
Expand All @@ -317,17 +290,12 @@ namespace ts.server {
const request = this.processRequest<protocol.ImplementationRequest>(CommandNames.Implementation, args);
const response = this.processResponse<protocol.ImplementationResponse>(request);

return response.body.map(entry => {
const fileName = entry.file;
const start = this.lineOffsetToPosition(fileName, entry.start);
const end = this.lineOffsetToPosition(fileName, entry.end);
return {
fileName,
textSpan: ts.createTextSpanFromBounds(start, end),
kind: ScriptElementKind.unknown,
displayParts: []
};
});
return response.body.map(entry => ({
fileName: entry.file,
textSpan: this.decodeSpan(entry),
kind: ScriptElementKind.unknown,
displayParts: []
}));
}

findReferences(_fileName: string, _position: number): ReferencedSymbol[] {
Expand All @@ -341,17 +309,12 @@ namespace ts.server {
const request = this.processRequest<protocol.ReferencesRequest>(CommandNames.References, args);
const response = this.processResponse<protocol.ReferencesResponse>(request);

return response.body.refs.map(entry => {
const fileName = entry.file;
const start = this.lineOffsetToPosition(fileName, entry.start);
const end = this.lineOffsetToPosition(fileName, entry.end);
return {
fileName,
textSpan: ts.createTextSpanFromBounds(start, end),
isWriteAccess: entry.isWriteAccess,
isDefinition: entry.isDefinition,
};
});
return response.body.refs.map(entry => ({
fileName: entry.file,
textSpan: this.decodeSpan(entry),
isWriteAccess: entry.isWriteAccess,
isDefinition: entry.isDefinition,
}));
}

getEmitOutput(_fileName: string): EmitOutput {
Expand Down Expand Up @@ -406,22 +369,21 @@ namespace ts.server {
const request = this.processRequest<protocol.RenameRequest>(CommandNames.Rename, args);
const response = this.processResponse<protocol.RenameResponse>(request);
const locations: RenameLocation[] = [];
response.body.locs.map((entry: protocol.SpanGroup) => {
for (const entry of response.body.locs) {
const fileName = entry.file;
entry.locs.map((loc: protocol.TextSpan) => {
const start = this.lineOffsetToPosition(fileName, loc.start);
const end = this.lineOffsetToPosition(fileName, loc.end);
locations.push({ textSpan: ts.createTextSpanFromBounds(start, end), fileName, });
});
});
for (const loc of entry.locs) {
locations.push({ textSpan: this.decodeSpan(loc, fileName), fileName });
}
}

return this.lastRenameEntry = {
canRename: response.body.info.canRename,
displayName: response.body.info.displayName,
fullDisplayName: response.body.info.fullDisplayName,
kind: response.body.info.kind,
kindModifiers: response.body.info.kindModifiers,
localizedErrorMessage: response.body.info.localizedErrorMessage,
triggerSpan: ts.createTextSpanFromBounds(position, position),
triggerSpan: createTextSpanFromBounds(position, position),
fileName,
position,
findInStrings,
Expand Down Expand Up @@ -485,7 +447,11 @@ namespace ts.server {
return this.decodeNavigationTree(response.body, file, lineMap);
}

private decodeSpan(span: protocol.TextSpan, fileName: string, lineMap: number[]) {
private decodeSpan(span: protocol.TextSpan & { file: string }): TextSpan;
private decodeSpan(span: protocol.TextSpan, fileName: string, lineMap?: number[]): TextSpan;
private decodeSpan(span: protocol.TextSpan & { file: string }, fileName?: string, lineMap?: number[]): TextSpan {
fileName = fileName || span.file;
lineMap = lineMap || this.getLineMap(fileName);
return createTextSpanFromBounds(
this.lineOffsetToPosition(fileName, span.start, lineMap),
this.lineOffsetToPosition(fileName, span.end, lineMap));
Expand All @@ -509,19 +475,11 @@ namespace ts.server {
return undefined;
}

const helpItems: protocol.SignatureHelpItems = response.body;
const span = helpItems.applicableSpan;
const start = this.lineOffsetToPosition(fileName, span.start);
const end = this.lineOffsetToPosition(fileName, span.end);

const result: SignatureHelpItems = {
items: helpItems.items,
applicableSpan: { start, length: end - start },
selectedItemIndex: helpItems.selectedItemIndex,
argumentIndex: helpItems.argumentIndex,
argumentCount: helpItems.argumentCount,
};
return result;
const { items, applicableSpan: encodedApplicableSpan, selectedItemIndex, argumentIndex, argumentCount } = response.body;

const applicableSpan = this.decodeSpan(encodedApplicableSpan, fileName);

return { items, applicableSpan, selectedItemIndex, argumentIndex, argumentCount };
}

getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[] {
Expand All @@ -530,17 +488,12 @@ namespace ts.server {
const request = this.processRequest<protocol.OccurrencesRequest>(CommandNames.Occurrences, args);
const response = this.processResponse<protocol.OccurrencesResponse>(request);

return response.body.map(entry => {
const fileName = entry.file;
const start = this.lineOffsetToPosition(fileName, entry.start);
const end = this.lineOffsetToPosition(fileName, entry.end);
return {
fileName,
textSpan: ts.createTextSpanFromBounds(start, end),
isWriteAccess: entry.isWriteAccess,
isDefinition: false
};
});
return response.body.map(entry => ({
fileName: entry.file,
textSpan: this.decodeSpan(entry),
isWriteAccess: entry.isWriteAccess,
isDefinition: false
}));
}

getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[] {
Expand All @@ -549,26 +502,13 @@ namespace ts.server {
const request = this.processRequest<protocol.DocumentHighlightsRequest>(CommandNames.DocumentHighlights, args);
const response = this.processResponse<protocol.DocumentHighlightsResponse>(request);

const self = this;
return response.body.map(convertToDocumentHighlights);

function convertToDocumentHighlights(item: ts.server.protocol.DocumentHighlightsItem): ts.DocumentHighlights {
const { file, highlightSpans } = item;

return {
fileName: file,
highlightSpans: highlightSpans.map(convertHighlightSpan)
};

function convertHighlightSpan(span: ts.server.protocol.HighlightSpan): ts.HighlightSpan {
const start = self.lineOffsetToPosition(file, span.start);
const end = self.lineOffsetToPosition(file, span.end);
return {
textSpan: ts.createTextSpanFromBounds(start, end),
kind: span.kind
};
}
}
return response.body.map(item => ({
fileName: item.file,
highlightSpans: item.highlightSpans.map(span => ({
textSpan: this.decodeSpan(span, item.file),
kind: span.kind
})),
}));
}

getOutliningSpans(_fileName: string): OutliningSpan[] {
Expand Down Expand Up @@ -662,7 +602,7 @@ namespace ts.server {
};
}

private convertCodeEditsToTextChanges(edits: ts.server.protocol.FileCodeEdits[]): FileTextChanges[] {
private convertCodeEditsToTextChanges(edits: protocol.FileCodeEdits[]): FileTextChanges[] {
return edits.map(edit => {
const fileName = edit.fileName;
return {
Expand All @@ -683,11 +623,8 @@ namespace ts.server {
}

convertTextChangeToCodeEdit(change: protocol.CodeEdit, fileName: string): ts.TextChange {
const start = this.lineOffsetToPosition(fileName, change.start);
const end = this.lineOffsetToPosition(fileName, change.end);

return {
span: { start, length: end - start },
span: this.decodeSpan(change, fileName),
newText: change.newText ? change.newText : ""
};
}
Expand All @@ -698,11 +635,7 @@ namespace ts.server {
const request = this.processRequest<protocol.BraceRequest>(CommandNames.Brace, args);
const response = this.processResponse<protocol.BraceResponse>(request);

return response.body.map(entry => {
const start = this.lineOffsetToPosition(fileName, entry.start);
const end = this.lineOffsetToPosition(fileName, entry.end);
return { start, length: end - start };
});
return response.body.map(entry => this.decodeSpan(entry, fileName));
}

getIndentationAtPosition(_fileName: string, _position: number, _options: EditorOptions): number {
Expand Down