Replacement spans for import completions

This commit is contained in:
Richard Knoll 2016-08-01 16:58:33 -07:00
parent 4ec8b2b134
commit 98a162be2a
5 changed files with 92 additions and 15 deletions

View File

@ -766,13 +766,29 @@ namespace FourSlash {
}
}
public verifyImportModuleCompletionListContains(symbol: string) {
public verifyImportModuleCompletionListContains(symbol: string, rangeIndex?: number) {
const completions = this.getImportModuleCompletionListAtCaret();
if (completions) {
if (!ts.forEach(completions, completion => completion.name === symbol)) {
const completion = ts.forEach(completions, completion => completion.name === symbol ? completion : undefined);
if (!completion) {
const itemsString = completions.map(item => stringify({ name: item.name, span: item.span })).join(",\n");
this.raiseError(`Expected "${symbol}" to be in list [${itemsString}]`);
}
else if (rangeIndex !== undefined) {
const ranges = this.getRanges();
if (ranges && ranges.length > rangeIndex) {
const range = ranges[rangeIndex];
const start = completion.span.start;
const end = start + completion.span.length;
if (range.start !== start || range.end !== end) {
this.raiseError(`Expected completion span for '${symbol}', ${stringify(completion.span)}, to cover range ${stringify(range)}`);
}
}
else {
this.raiseError(`Expected completion span for '${symbol}' to cover range at index ${rangeIndex}, but no range was found at that index`);
}
}
}
else {
this.raiseError(`No import module completions at position '${this.currentCaretPosition}' when looking for '${symbol}'.`);
@ -2943,12 +2959,12 @@ namespace FourSlashInterface {
this.state.verifyCompletionListItemsCountIsGreaterThan(count, this.negative);
}
public importModuleCompletionListContains(symbol: string): void {
public importModuleCompletionListContains(symbol: string, rangeIndex?: number): void {
if (this.negative) {
this.state.verifyImportModuleCompletionListDoesNotContain(symbol);
}
else {
this.state.verifyImportModuleCompletionListContains(symbol);
this.state.verifyImportModuleCompletionListContains(symbol, rangeIndex);
}
}

View File

@ -2069,7 +2069,7 @@ namespace ts {
* for completions.
* For example, this matches /// <reference path="fragment
*/
const tripleSlashDirectiveFragmentRegex = /^\/\/\/\s*<reference\s+(path|types)\s*=\s*(?:'|")([^'"]+)$/;
const tripleSlashDirectiveFragmentRegex = /^(\/\/\/\s*<reference\s+(path|types)\s*=\s*(?:'|"))([^'"]+)$/;
let commandLineOptionsStringToEnum: CommandLineOptionOfCustomType[];
@ -4517,8 +4517,8 @@ namespace ts {
const literalValue = node.text;
let result: ImportCompletionEntry[];
const nodeStart = node.getStart();
const span: TextSpan = { start: nodeStart, length: nodeStart - node.getEnd() };
// Replace the entire text of the string literal (excluding the quotes)
const span: TextSpan = { start: node.getStart() + 1, length: literalValue.length };
const isRelativePath = startsWith(literalValue, ".");
const scriptDir = getDirectoryPath(node.getSourceFile().path);
@ -4768,22 +4768,36 @@ namespace ts {
}
function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number): ImportCompletionEntry[] {
const node = getTokenAtPosition(sourceFile, position);
if (!node) {
const token = getTokenAtPosition(sourceFile, position);
if (!token) {
return undefined;
}
const commentRanges: CommentRange[] = getLeadingCommentRanges(sourceFile.text, token.pos);
if (!commentRanges || !commentRanges.length) {
return undefined;
}
const span: TextSpan = undefined;
const range = forEach(commentRanges, commentRange => position >= commentRange.pos && position <= commentRange.end && commentRange);
if (!range) {
return undefined;
}
const text = sourceFile.text.substr(range.pos, position - range.pos);
const text = sourceFile.text.substr(node.pos, position);
const match = tripleSlashDirectiveFragmentRegex.exec(text);
if (match) {
const kind= match[1];
const fragment = match[2];
const prefix = match[1];
const kind = match[2];
const toComplete = match[3];
const span: TextSpan = { start: range.pos + prefix.length, length: match[0].length - prefix.length };
const scriptPath = getDirectoryPath(sourceFile.path);
if (kind === "path") {
// Give completions for a relative path
return getCompletionEntriesForDirectoryFragment(fragment, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true, span);
return getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getSupportedExtensions(program.getCompilerOptions()), /*includeExtensions*/true, span);
}
else {
// Give completions based on the typings available

View File

@ -0,0 +1,19 @@
/// <reference path='fourslash.ts' />
// @Filename: test.ts
//// import * as foo0 from "[|./some|]/*0*/
//// import foo1 = require( "[|./some|]/*1*/
//// var foo2 = require( "[|./some|]/*2*/
//// import * as foo3 from "[|./some|]/*3*/";
//// import foo4 = require( "[|./some|]/*4*/";
//// var foo5 = require( "[|./some|]/*5*/";
// @Filename: someFile.ts
//// /*someFile*/
for (let i = 0; i < 6; i++) {
goTo.marker("" + i);
verify.importModuleCompletionListContains("someFile", i);
}

View File

@ -0,0 +1,28 @@
/// <reference path='fourslash.ts' />
// @typeRoots: my_typings
// @Filename: test.ts
//// /// <reference path="[|./some|]/*0*/
//// /// <reference types="[|some|]/*1*/
//// /// <reference path="[|./some|]/*2*/" />
//// /// <reference types="[|some|]/*3*/" />
// @Filename: someFile.ts
//// /*someFile*/
// @Filename: my_typings/some-module/index.d.ts
//// export var x = 9;
goTo.marker("0");
verify.importModuleCompletionListContains("someFile.ts", 0);
goTo.marker("1");
verify.importModuleCompletionListContains("some-module", 1);
goTo.marker("2");
verify.importModuleCompletionListContains("someFile.ts", 2);
goTo.marker("3");
verify.importModuleCompletionListContains("some-module", 3);

View File

@ -125,7 +125,7 @@ declare namespace FourSlashInterface {
completionListItemsCountIsGreaterThan(count: number): void;
completionListIsEmpty(): void;
completionListAllowsNewIdentifier(): void;
importModuleCompletionListContains(symbol: string): void;
importModuleCompletionListContains(symbol: string, rangeIndex?: number): void;
importModuleCompletionListItemsCountIsGreaterThan(count: number): void;
importModuleCompletionListIsEmpty(): void;
memberListIsEmpty(): void;