From 672cfaf0e4dca285eff9447227db4cf6e076c0c5 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 17 Nov 2016 11:16:13 -0800 Subject: [PATCH 1/2] simplified expression --- src/server/client.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/server/client.ts b/src/server/client.ts index 3b09a92675497..cb4e05e2bc42b 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -216,8 +216,10 @@ namespace ts.server { if (entry.replacementSpan !== undefined) { const { name, kind, kindModifiers, sortText, replacementSpan} = entry; - const convertedSpan = createTextSpanFromBounds(this.lineOffsetToPosition(fileName, replacementSpan.start), - this.lineOffsetToPosition(fileName, replacementSpan.end)); + const startBound = this.lineOffsetToPosition(fileName, replacementSpan.start) + const endBound = this.lineOffsetToPosition(fileName, replacementSpan.end) + + const convertedSpan = createTextSpanFromBounds(startBound, endBound); return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan }; } From 869fb36dbc0faa61bcd2baf32f96cd23a02e0077 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 17 Nov 2016 13:17:31 -0800 Subject: [PATCH 2/2] change to zero indexing for completion results --- src/compiler/types.ts | 5 +++ src/server/scriptInfo.ts | 16 +++++-- src/server/session.ts | 94 +++++++++++++++++++++++----------------- 3 files changed, 72 insertions(+), 43 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b5e3eefbb2d6a..6a36354aef12b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3816,6 +3816,11 @@ namespace ts { export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile; export interface TextSpan { + /** + * 0-indexed position in the file. + * + * ie: the first position in the file has start === 0. + */ start: number; length: number; } diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index 84649863a7b3a..c4b243b75ac41 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -185,13 +185,23 @@ namespace ts.server { } /** - * @param line 1-based index - * @param offset 1-based index + * @param position 0-based position index + * @return ILineInfo with 1-based indexing for line numbers and offsets. */ - positionToLineOffset(position: number): ILineInfo { + positionToOneIndexedLineOffset(position: number): ILineInfo { const index = this.snap().index; const lineOffset = index.charOffsetToLineNumberAndPos(position); return { line: lineOffset.line, offset: lineOffset.offset + 1 }; } + + /** + * @param position 0-based position index + * @return ILineInfo with 0-based indexing for line numbers and offsets. + */ + positionToZeroIndexedLineOffset(position: number): ILineInfo { + const index = this.snap().index; + const lineOffset = index.charOffsetToLineNumberAndPos(position); + return { line: lineOffset.line - 1, offset: lineOffset.offset }; + } } } \ No newline at end of file diff --git a/src/server/session.ts b/src/server/session.ts index 36144b3212d66..f085cf09dbeff 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -53,8 +53,8 @@ namespace ts.server { function formatDiag(fileName: NormalizedPath, project: Project, diag: ts.Diagnostic): protocol.Diagnostic { const scriptInfo = project.getScriptInfoForNormalizedPath(fileName); return { - start: scriptInfo.positionToLineOffset(diag.start), - end: scriptInfo.positionToLineOffset(diag.start + diag.length), + start: scriptInfo.positionToOneIndexedLineOffset(diag.start), + end: scriptInfo.positionToOneIndexedLineOffset(diag.start + diag.length), text: ts.flattenDiagnosticMessageText(diag.messageText, "\n"), code: diag.code }; @@ -396,8 +396,8 @@ namespace ts.server { length: d.length, category: DiagnosticCategory[d.category].toLowerCase(), code: d.code, - startLocation: scriptInfo && scriptInfo.positionToLineOffset(d.start), - endLocation: scriptInfo && scriptInfo.positionToLineOffset(d.start + d.length) + startLocation: scriptInfo && scriptInfo.positionToOneIndexedLineOffset(d.start), + endLocation: scriptInfo && scriptInfo.positionToOneIndexedLineOffset(d.start + d.length) }); } @@ -428,8 +428,8 @@ namespace ts.server { const defScriptInfo = project.getScriptInfo(def.fileName); return { file: def.fileName, - start: defScriptInfo.positionToLineOffset(def.textSpan.start), - end: defScriptInfo.positionToLineOffset(ts.textSpanEnd(def.textSpan)) + start: defScriptInfo.positionToOneIndexedLineOffset(def.textSpan.start), + end: defScriptInfo.positionToOneIndexedLineOffset(ts.textSpanEnd(def.textSpan)) }; }); } @@ -452,8 +452,8 @@ namespace ts.server { const defScriptInfo = project.getScriptInfo(def.fileName); return { file: def.fileName, - start: defScriptInfo.positionToLineOffset(def.textSpan.start), - end: defScriptInfo.positionToLineOffset(ts.textSpanEnd(def.textSpan)) + start: defScriptInfo.positionToOneIndexedLineOffset(def.textSpan.start), + end: defScriptInfo.positionToOneIndexedLineOffset(ts.textSpanEnd(def.textSpan)) }; }); } @@ -469,8 +469,8 @@ namespace ts.server { if (simplifiedResult) { return implementations.map(impl => ({ file: impl.fileName, - start: scriptInfo.positionToLineOffset(impl.textSpan.start), - end: scriptInfo.positionToLineOffset(ts.textSpanEnd(impl.textSpan)) + start: scriptInfo.positionToOneIndexedLineOffset(impl.textSpan.start), + end: scriptInfo.positionToOneIndexedLineOffset(ts.textSpanEnd(impl.textSpan)) })); } else { @@ -492,8 +492,8 @@ namespace ts.server { return occurrences.map(occurrence => { const { fileName, isWriteAccess, textSpan } = occurrence; const scriptInfo = project.getScriptInfo(fileName); - const start = scriptInfo.positionToLineOffset(textSpan.start); - const end = scriptInfo.positionToLineOffset(ts.textSpanEnd(textSpan)); + const start = scriptInfo.positionToOneIndexedLineOffset(textSpan.start); + const end = scriptInfo.positionToOneIndexedLineOffset(ts.textSpanEnd(textSpan)); return { start, end, @@ -539,8 +539,8 @@ namespace ts.server { function convertHighlightSpan(highlightSpan: ts.HighlightSpan): ts.server.protocol.HighlightSpan { const { textSpan, kind } = highlightSpan; - const start = scriptInfo.positionToLineOffset(textSpan.start); - const end = scriptInfo.positionToLineOffset(ts.textSpanEnd(textSpan)); + const start = scriptInfo.positionToOneIndexedLineOffset(textSpan.start); + const end = scriptInfo.positionToOneIndexedLineOffset(ts.textSpanEnd(textSpan)); return { start, end, kind }; } } @@ -624,8 +624,8 @@ namespace ts.server { const locationScriptInfo = project.getScriptInfo(location.fileName); return { file: location.fileName, - start: locationScriptInfo.positionToLineOffset(location.textSpan.start), - end: locationScriptInfo.positionToLineOffset(ts.textSpanEnd(location.textSpan)), + start: locationScriptInfo.positionToOneIndexedLineOffset(location.textSpan.start), + end: locationScriptInfo.positionToOneIndexedLineOffset(ts.textSpanEnd(location.textSpan)), }; }); }, @@ -708,7 +708,7 @@ namespace ts.server { const displayString = ts.displayPartsToString(nameInfo.displayParts); const nameSpan = nameInfo.textSpan; - const nameColStart = scriptInfo.positionToLineOffset(nameSpan.start).offset; + const nameColStart = scriptInfo.positionToOneIndexedLineOffset(nameSpan.start).offset; const nameText = scriptInfo.snap().getText(nameSpan.start, ts.textSpanEnd(nameSpan)); const refs = combineProjectOutput( projects, @@ -720,14 +720,14 @@ namespace ts.server { return references.map(ref => { const refScriptInfo = project.getScriptInfo(ref.fileName); - const start = refScriptInfo.positionToLineOffset(ref.textSpan.start); + const start = refScriptInfo.positionToOneIndexedLineOffset(ref.textSpan.start); const refLineSpan = refScriptInfo.lineToTextSpan(start.line - 1); const lineText = refScriptInfo.snap().getText(refLineSpan.start, ts.textSpanEnd(refLineSpan)).replace(/\r|\n/g, ""); return { file: ref.fileName, start: start, lineText: lineText, - end: refScriptInfo.positionToLineOffset(ts.textSpanEnd(ref.textSpan)), + end: refScriptInfo.positionToOneIndexedLineOffset(ts.textSpanEnd(ref.textSpan)), isWriteAccess: ref.isWriteAccess, isDefinition: ref.isDefinition }; @@ -856,8 +856,8 @@ namespace ts.server { return { kind: quickInfo.kind, kindModifiers: quickInfo.kindModifiers, - start: scriptInfo.positionToLineOffset(quickInfo.textSpan.start), - end: scriptInfo.positionToLineOffset(ts.textSpanEnd(quickInfo.textSpan)), + start: scriptInfo.positionToOneIndexedLineOffset(quickInfo.textSpan.start), + end: scriptInfo.positionToOneIndexedLineOffset(ts.textSpanEnd(quickInfo.textSpan)), displayString: displayString, documentation: docString, }; @@ -952,8 +952,8 @@ namespace ts.server { return edits.map((edit) => { return { - start: scriptInfo.positionToLineOffset(edit.span.start), - end: scriptInfo.positionToLineOffset(ts.textSpanEnd(edit.span)), + start: scriptInfo.positionToOneIndexedLineOffset(edit.span.start), + end: scriptInfo.positionToOneIndexedLineOffset(ts.textSpanEnd(edit.span)), newText: edit.newText ? edit.newText : "" }; }); @@ -966,17 +966,19 @@ namespace ts.server { const scriptInfo = project.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, scriptInfo); - const completions = project.getLanguageService().getCompletionsAtPosition(file, position); + const completions: ts.CompletionInfo = project.getLanguageService().getCompletionsAtPosition(file, position); if (!completions) { return undefined; } + if (simplifiedResult) { return completions.entries.reduce((result: protocol.CompletionEntry[], entry: ts.CompletionEntry) => { if (completions.isMemberCompletion || (entry.name.toLowerCase().indexOf(prefix.toLowerCase()) === 0)) { - const { name, kind, kindModifiers, sortText, replacementSpan } = entry; - const convertedSpan: protocol.TextSpan = - replacementSpan ? this.decorateSpan(replacementSpan, scriptInfo) : undefined; - result.push({ name, kind, kindModifiers, sortText, replacementSpan: convertedSpan }); + const convertedSpan: protocol.TextSpan | undefined = entry.replacementSpan ? + this.toZeroIndexedProtocolSpan(entry.replacementSpan, scriptInfo) : + undefined; + + result.push(toProtocolCompletionEntry(entry, convertedSpan)); } return result; }, []).sort((a, b) => ts.compareStrings(a.name, b.name)); @@ -984,6 +986,11 @@ namespace ts.server { else { return completions; } + + function toProtocolCompletionEntry(entry: ts.CompletionEntry | protocol.CompletionEntry, replacementSpan?: protocol.TextSpan): protocol.CompletionEntry { + entry.replacementSpan = replacementSpan; + return entry; + } } private getCompletionEntryDetails(args: protocol.CompletionDetailsRequestArgs): protocol.CompletionEntryDetails[] { @@ -1044,8 +1051,8 @@ namespace ts.server { return { items: helpItems.items, applicableSpan: { - start: scriptInfo.positionToLineOffset(span.start), - end: scriptInfo.positionToLineOffset(span.start + span.length) + start: scriptInfo.positionToOneIndexedLineOffset(span.start), + end: scriptInfo.positionToOneIndexedLineOffset(span.start + span.length) }, selectedItemIndex: helpItems.selectedItemIndex, argumentIndex: helpItems.argumentIndex, @@ -1119,7 +1126,7 @@ namespace ts.server { text: item.text, kind: item.kind, kindModifiers: item.kindModifiers, - spans: item.spans.map(span => this.decorateSpan(span, scriptInfo)), + spans: item.spans.map(span => this.toOneIndexedProtocolSpan(span, scriptInfo)), childItems: this.decorateNavigationBarItems(item.childItems, scriptInfo), indent: item.indent })); @@ -1140,15 +1147,22 @@ namespace ts.server { text: tree.text, kind: tree.kind, kindModifiers: tree.kindModifiers, - spans: tree.spans.map(span => this.decorateSpan(span, scriptInfo)), + spans: tree.spans.map(span => this.toOneIndexedProtocolSpan(span, scriptInfo)), childItems: map(tree.childItems, item => this.decorateNavigationTree(item, scriptInfo)) }; } - private decorateSpan(span: TextSpan, scriptInfo: ScriptInfo): protocol.TextSpan { + private toOneIndexedProtocolSpan(span: TextSpan, scriptInfo: ScriptInfo): protocol.TextSpan { + return { + start: scriptInfo.positionToOneIndexedLineOffset(span.start), + end: scriptInfo.positionToOneIndexedLineOffset(ts.textSpanEnd(span)) + }; + } + + private toZeroIndexedProtocolSpan(span: TextSpan, scriptInfo: ScriptInfo): protocol.TextSpan { return { - start: scriptInfo.positionToLineOffset(span.start), - end: scriptInfo.positionToLineOffset(ts.textSpanEnd(span)) + start: scriptInfo.positionToZeroIndexedLineOffset(span.start), + end: scriptInfo.positionToZeroIndexedLineOffset(ts.textSpanEnd(span)) }; } @@ -1177,8 +1191,8 @@ namespace ts.server { return navItems.map((navItem) => { const scriptInfo = project.getScriptInfo(navItem.fileName); - const start = scriptInfo.positionToLineOffset(navItem.textSpan.start); - const end = scriptInfo.positionToLineOffset(ts.textSpanEnd(navItem.textSpan)); + const start = scriptInfo.positionToOneIndexedLineOffset(navItem.textSpan.start); + const end = scriptInfo.positionToOneIndexedLineOffset(ts.textSpanEnd(navItem.textSpan)); const bakedItem: protocol.NavtoItem = { name: navItem.name, kind: navItem.kind, @@ -1285,8 +1299,8 @@ namespace ts.server { private convertTextChangeToCodeEdit(change: ts.TextChange, scriptInfo: ScriptInfo): protocol.CodeEdit { return { - start: scriptInfo.positionToLineOffset(change.span.start), - end: scriptInfo.positionToLineOffset(change.span.start + change.span.length), + start: scriptInfo.positionToOneIndexedLineOffset(change.span.start), + end: scriptInfo.positionToOneIndexedLineOffset(change.span.start + change.span.length), newText: change.newText ? change.newText : "" }; } @@ -1301,7 +1315,7 @@ namespace ts.server { return !spans ? undefined : simplifiedResult - ? spans.map(span => this.decorateSpan(span, scriptInfo)) + ? spans.map(span => this.toOneIndexedProtocolSpan(span, scriptInfo)) : spans; }