Merge pull request #14001 from Microsoft/refactor_findallrefs

Refactor findAllReferences. Now supports renamed exports and imports.
This commit is contained in:
Andy
2017-04-14 09:57:54 -07:00
committed by GitHub
149 changed files with 2607 additions and 1415 deletions
+1
View File
@@ -41,6 +41,7 @@ const cmdLineOptions = minimist(process.argv.slice(2), {
boolean: ["debug", "inspect", "light", "colors", "lint", "soft"],
string: ["browser", "tests", "host", "reporter", "stackTraceLimit"],
alias: {
b: "browser",
d: "debug",
t: "tests",
test: "tests",
+51 -41
View File
@@ -81,6 +81,7 @@ namespace ts {
isUndefinedSymbol: symbol => symbol === undefinedSymbol,
isArgumentsSymbol: symbol => symbol === argumentsSymbol,
isUnknownSymbol: symbol => symbol === unknownSymbol,
getMergedSymbol,
getDiagnostics,
getGlobalDiagnostics,
getTypeOfSymbolAtLocation: (symbol, location) => {
@@ -172,6 +173,17 @@ namespace ts {
node = getParseTreeNode(node, isFunctionLike);
return node ? isImplementationOfOverload(node) : undefined;
},
getImmediateAliasedSymbol: symbol => {
Debug.assert((symbol.flags & SymbolFlags.Alias) !== 0, "Should only get Alias here.");
const links = getSymbolLinks(symbol);
if (!links.immediateTarget) {
const node = getDeclarationOfAliasSymbol(symbol);
Debug.assert(!!node);
links.immediateTarget = getTargetOfAliasDeclaration(node, /*dontRecursivelyResolve*/ true);
}
return links.immediateTarget;
},
getAliasedSymbol: resolveAlias,
getEmitResolver,
getExportsOfModule: getExportsOfModuleAsArray,
@@ -1272,14 +1284,14 @@ namespace ts {
return find<Declaration>(symbol.declarations, isAliasSymbolDeclaration);
}
function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration): Symbol {
function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration, dontResolveAlias: boolean): Symbol {
if (node.moduleReference.kind === SyntaxKind.ExternalModuleReference) {
return resolveExternalModuleSymbol(resolveExternalModuleName(node, getExternalModuleImportEqualsDeclarationExpression(node)));
}
return getSymbolOfPartOfRightHandSideOfImportEquals(<EntityName>node.moduleReference);
return getSymbolOfPartOfRightHandSideOfImportEquals(<EntityName>node.moduleReference, dontResolveAlias);
}
function getTargetOfImportClause(node: ImportClause): Symbol {
function getTargetOfImportClause(node: ImportClause, dontResolveAlias: boolean): Symbol {
const moduleSymbol = resolveExternalModuleName(node, (<ImportDeclaration>node.parent).moduleSpecifier);
if (moduleSymbol) {
@@ -1291,22 +1303,22 @@ namespace ts {
const exportValue = moduleSymbol.exports.get("export=");
exportDefaultSymbol = exportValue
? getPropertyOfType(getTypeOfSymbol(exportValue), "default")
: resolveSymbol(moduleSymbol.exports.get("default"));
: resolveSymbol(moduleSymbol.exports.get("default"), dontResolveAlias);
}
if (!exportDefaultSymbol && !allowSyntheticDefaultImports) {
error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol));
}
else if (!exportDefaultSymbol && allowSyntheticDefaultImports) {
return resolveExternalModuleSymbol(moduleSymbol) || resolveSymbol(moduleSymbol);
return resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias);
}
return exportDefaultSymbol;
}
}
function getTargetOfNamespaceImport(node: NamespaceImport): Symbol {
function getTargetOfNamespaceImport(node: NamespaceImport, dontResolveAlias: boolean): Symbol {
const moduleSpecifier = (<ImportDeclaration>node.parent.parent).moduleSpecifier;
return resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier);
return resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias);
}
// This function creates a synthetic symbol that combines the value side of one symbol with the
@@ -1340,12 +1352,9 @@ namespace ts {
return result;
}
function getExportOfModule(symbol: Symbol, name: string): Symbol {
function getExportOfModule(symbol: Symbol, name: string, dontResolveAlias: boolean): Symbol {
if (symbol.flags & SymbolFlags.Module) {
const exportedSymbol = getExportsOfSymbol(symbol).get(name);
if (exportedSymbol) {
return resolveSymbol(exportedSymbol);
}
return resolveSymbol(getExportsOfSymbol(symbol).get(name), dontResolveAlias);
}
}
@@ -1358,9 +1367,9 @@ namespace ts {
}
}
function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration, specifier: ImportOrExportSpecifier): Symbol {
function getExternalModuleMember(node: ImportDeclaration | ExportDeclaration, specifier: ImportOrExportSpecifier, dontResolveAlias?: boolean): Symbol {
const moduleSymbol = resolveExternalModuleName(node, node.moduleSpecifier);
const targetSymbol = resolveESModuleSymbol(moduleSymbol, node.moduleSpecifier);
const targetSymbol = resolveESModuleSymbol(moduleSymbol, node.moduleSpecifier, dontResolveAlias);
if (targetSymbol) {
const name = specifier.propertyName || specifier.name;
if (name.text) {
@@ -1377,11 +1386,11 @@ namespace ts {
symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text);
}
// if symbolFromVariable is export - get its final target
symbolFromVariable = resolveSymbol(symbolFromVariable);
let symbolFromModule = getExportOfModule(targetSymbol, name.text);
symbolFromVariable = resolveSymbol(symbolFromVariable, dontResolveAlias);
let symbolFromModule = getExportOfModule(targetSymbol, name.text, dontResolveAlias);
// If the export member we're looking for is default, and there is no real default but allowSyntheticDefaultImports is on, return the entire module as the default
if (!symbolFromModule && allowSyntheticDefaultImports && name.text === "default") {
symbolFromModule = resolveExternalModuleSymbol(moduleSymbol) || resolveSymbol(moduleSymbol);
symbolFromModule = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias);
}
const symbol = symbolFromModule && symbolFromVariable ?
combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule) :
@@ -1394,45 +1403,46 @@ namespace ts {
}
}
function getTargetOfImportSpecifier(node: ImportSpecifier): Symbol {
return getExternalModuleMember(<ImportDeclaration>node.parent.parent.parent, node);
function getTargetOfImportSpecifier(node: ImportSpecifier, dontResolveAlias: boolean): Symbol {
return getExternalModuleMember(<ImportDeclaration>node.parent.parent.parent, node, dontResolveAlias);
}
function getTargetOfNamespaceExportDeclaration(node: NamespaceExportDeclaration): Symbol {
return resolveExternalModuleSymbol(node.parent.symbol);
function getTargetOfNamespaceExportDeclaration(node: NamespaceExportDeclaration, dontResolveAlias: boolean): Symbol {
return resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias);
}
function getTargetOfExportSpecifier(node: ExportSpecifier): Symbol {
function getTargetOfExportSpecifier(node: ExportSpecifier, dontResolveAlias?: boolean): Symbol {
return (<ExportDeclaration>node.parent.parent).moduleSpecifier ?
getExternalModuleMember(<ExportDeclaration>node.parent.parent, node) :
resolveEntityName(node.propertyName || node.name, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace);
getExternalModuleMember(<ExportDeclaration>node.parent.parent, node, dontResolveAlias) :
resolveEntityName(node.propertyName || node.name, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias);
}
function getTargetOfExportAssignment(node: ExportAssignment): Symbol {
return resolveEntityName(<EntityNameExpression>node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace);
function getTargetOfExportAssignment(node: ExportAssignment, dontResolveAlias: boolean): Symbol {
return resolveEntityName(<EntityNameExpression>node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias);
}
function getTargetOfAliasDeclaration(node: Declaration): Symbol {
function getTargetOfAliasDeclaration(node: Declaration, dontRecursivelyResolve?: boolean): Symbol {
switch (node.kind) {
case SyntaxKind.ImportEqualsDeclaration:
return getTargetOfImportEqualsDeclaration(<ImportEqualsDeclaration>node);
return getTargetOfImportEqualsDeclaration(<ImportEqualsDeclaration>node, dontRecursivelyResolve);
case SyntaxKind.ImportClause:
return getTargetOfImportClause(<ImportClause>node);
return getTargetOfImportClause(<ImportClause>node, dontRecursivelyResolve);
case SyntaxKind.NamespaceImport:
return getTargetOfNamespaceImport(<NamespaceImport>node);
return getTargetOfNamespaceImport(<NamespaceImport>node, dontRecursivelyResolve);
case SyntaxKind.ImportSpecifier:
return getTargetOfImportSpecifier(<ImportSpecifier>node);
return getTargetOfImportSpecifier(<ImportSpecifier>node, dontRecursivelyResolve);
case SyntaxKind.ExportSpecifier:
return getTargetOfExportSpecifier(<ExportSpecifier>node);
return getTargetOfExportSpecifier(<ExportSpecifier>node, dontRecursivelyResolve);
case SyntaxKind.ExportAssignment:
return getTargetOfExportAssignment(<ExportAssignment>node);
return getTargetOfExportAssignment(<ExportAssignment>node, dontRecursivelyResolve);
case SyntaxKind.NamespaceExportDeclaration:
return getTargetOfNamespaceExportDeclaration(<NamespaceExportDeclaration>node);
return getTargetOfNamespaceExportDeclaration(<NamespaceExportDeclaration>node, dontRecursivelyResolve);
}
}
function resolveSymbol(symbol: Symbol): Symbol {
return symbol && symbol.flags & SymbolFlags.Alias && !(symbol.flags & (SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace)) ? resolveAlias(symbol) : symbol;
function resolveSymbol(symbol: Symbol, dontResolveAlias?: boolean): Symbol {
const shouldResolve = !dontResolveAlias && symbol && symbol.flags & SymbolFlags.Alias && !(symbol.flags & (SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace));
return shouldResolve ? resolveAlias(symbol) : symbol;
}
function resolveAlias(symbol: Symbol): Symbol {
@@ -1672,16 +1682,16 @@ namespace ts {
// An external module with an 'export =' declaration resolves to the target of the 'export =' declaration,
// and an external module with no 'export =' declaration resolves to the module itself.
function resolveExternalModuleSymbol(moduleSymbol: Symbol): Symbol {
return moduleSymbol && getMergedSymbol(resolveSymbol(moduleSymbol.exports.get("export="))) || moduleSymbol;
function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol {
return moduleSymbol && getMergedSymbol(resolveSymbol(moduleSymbol.exports.get("export="), dontResolveAlias)) || moduleSymbol;
}
// An external module with an 'export =' declaration may be referenced as an ES6 module provided the 'export ='
// references a symbol that is at least declared as a module or a variable. The target of the 'export =' may
// combine other declarations with the module or variable (e.g. a class/module, function/module, interface/variable).
function resolveESModuleSymbol(moduleSymbol: Symbol, moduleReferenceExpression: Expression): Symbol {
let symbol = resolveExternalModuleSymbol(moduleSymbol);
if (symbol && !(symbol.flags & (SymbolFlags.Module | SymbolFlags.Variable))) {
function resolveESModuleSymbol(moduleSymbol: Symbol, moduleReferenceExpression: Expression, dontResolveAlias: boolean): Symbol {
let symbol = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias);
if (!dontResolveAlias && symbol && !(symbol.flags & (SymbolFlags.Module | SymbolFlags.Variable))) {
error(moduleReferenceExpression, Diagnostics.Module_0_resolves_to_a_non_module_entity_and_cannot_be_imported_using_this_construct, symbolToString(moduleSymbol));
symbol = undefined;
}
+5 -3
View File
@@ -887,10 +887,12 @@ namespace ts {
}
/** Shims `Array.from`. */
export function arrayFrom<T>(iterator: Iterator<T>): T[] {
const result: T[] = [];
export function arrayFrom<T, U>(iterator: Iterator<T>, map: (t: T) => U): U[];
export function arrayFrom<T>(iterator: Iterator<T>): T[];
export function arrayFrom(iterator: Iterator<any>, map?: (t: any) => any): any[] {
const result: any[] = [];
for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) {
result.push(value);
result.push(map ? map(value) : value);
}
return result;
}
+1
View File
@@ -458,6 +458,7 @@ namespace ts {
return Parser.parseIsolatedEntityName(text, languageVersion);
}
// See also `isExternalOrCommonJsModule` in utilities.ts
export function isExternalModule(file: SourceFile): boolean {
return file.externalModuleIndicator !== undefined;
}
+9 -2
View File
@@ -1886,6 +1886,7 @@ namespace ts {
kind: SyntaxKind.ImportDeclaration;
parent?: SourceFile | ModuleBlock;
importClause?: ImportClause;
/** If this is not a StringLiteral it will be a grammar error. */
moduleSpecifier: Expression;
}
@@ -1919,6 +1920,7 @@ namespace ts {
kind: SyntaxKind.ExportDeclaration;
parent?: SourceFile | ModuleBlock;
exportClause?: NamedExports;
/** If this is not a StringLiteral it will be a grammar error. */
moduleSpecifier?: Expression;
}
@@ -2305,8 +2307,8 @@ namespace ts {
// Content of this field should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead
/* @internal */ resolvedModules: Map<ResolvedModuleFull>;
/* @internal */ resolvedTypeReferenceDirectiveNames: Map<ResolvedTypeReferenceDirective>;
/* @internal */ imports: LiteralExpression[];
/* @internal */ moduleAugmentations: LiteralExpression[];
/* @internal */ imports: StringLiteral[];
/* @internal */ moduleAugmentations: StringLiteral[];
/* @internal */ patternAmbientModules?: PatternAmbientModule[];
/* @internal */ ambientModuleNames: string[];
/* @internal */ checkJsDirective: CheckJsDirective | undefined;
@@ -2522,10 +2524,14 @@ namespace ts {
isUndefinedSymbol(symbol: Symbol): boolean;
isArgumentsSymbol(symbol: Symbol): boolean;
isUnknownSymbol(symbol: Symbol): boolean;
/* @internal */ getMergedSymbol(symbol: Symbol): Symbol;
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
/** Follow all aliases to get the original symbol. */
getAliasedSymbol(symbol: Symbol): Symbol;
/** Follow a *single* alias to get the immediately aliased symbol. */
/* @internal */ getImmediateAliasedSymbol(symbol: Symbol): Symbol;
getExportsOfModule(moduleSymbol: Symbol): Symbol[];
/** Unlike `getExportsOfModule`, this includes properties of an `export =` value. */
/* @internal */ getExportsAndPropertiesOfModule(moduleSymbol: Symbol): Symbol[];
@@ -2841,6 +2847,7 @@ namespace ts {
/* @internal */
export interface SymbolLinks {
immediateTarget?: Symbol; // Immediate target of an alias. May be another alias. Do not access directly, use `checker.getImmediateAliasedSymbol` instead.
target?: Symbol; // Resolved (non-alias) target of an alias
type?: Type; // Type of value symbol
declaredType?: Type; // Type of class, interface, enum, type alias, or type parameter
+1 -1
View File
@@ -3134,7 +3134,7 @@ namespace ts {
return isExportDefaultSymbol(symbol) ? symbol.valueDeclaration.localSymbol : undefined;
}
export function isExportDefaultSymbol(symbol: Symbol): boolean {
function isExportDefaultSymbol(symbol: Symbol): boolean {
return symbol && symbol.valueDeclaration && hasModifier(symbol.valueDeclaration, ModifierFlags.Default);
}
+122 -22
View File
@@ -549,10 +549,21 @@ namespace FourSlash {
Harness.IO.log("Unexpected error(s) found. Error list is:");
}
errors.forEach(function (error: ts.Diagnostic) {
Harness.IO.log(" minChar: " + error.start +
", limChar: " + (error.start + error.length) +
", message: " + ts.flattenDiagnosticMessageText(error.messageText, Harness.IO.newLine()) + "\n");
for (const { start, length, messageText } of errors) {
Harness.IO.log(" minChar: " + start +
", limChar: " + (start + length) +
", message: " + ts.flattenDiagnosticMessageText(messageText, Harness.IO.newLine()) + "\n");
}
}
public verifyNoErrors() {
ts.forEachKey(this.inputFiles, fileName => {
const errors = this.getDiagnostics(fileName);
if (errors.length) {
this.printErrorLog(/*expectErrors*/ false, errors);
const error = errors[0];
this.raiseError(`Found an error: ${error.file.fileName}@${error.start}: ${error.messageText}`);
}
});
}
@@ -882,7 +893,68 @@ namespace FourSlash {
}
}
public verifyReferencesAre(expectedReferences: Range[]) {
/** Use `getProgram` instead of accessing this directly. */
private _program: ts.Program;
/** Use `getChecker` instead of accessing this directly. */
private _checker: ts.TypeChecker;
private getProgram(): ts.Program {
return this._program || (this._program = this.languageService.getProgram());
}
private getChecker() {
return this._checker || (this._checker = this.getProgram().getTypeChecker());
}
private getSourceFile(): ts.SourceFile {
const { fileName } = this.activeFile;
const result = this.getProgram().getSourceFile(fileName);
if (!result) {
throw new Error(`Could not get source file ${fileName}`);
}
return result;
}
private getNode(): ts.Node {
return ts.getTouchingPropertyName(this.getSourceFile(), this.currentCaretPosition);
}
private goToAndGetNode(range: Range): ts.Node {
this.goToRangeStart(range);
const node = this.getNode();
this.verifyRange("touching property name", range, node);
return node;
}
private verifyRange(desc: string, expected: Range, actual: ts.Node) {
const actualStart = actual.getStart();
const actualEnd = actual.getEnd();
if (actualStart !== expected.start || actualEnd !== expected.end) {
this.raiseError(`${desc} should be ${expected.start}-${expected.end}, got ${actualStart}-${actualEnd}`);
}
}
private verifySymbol(symbol: ts.Symbol, declarationRanges: Range[]) {
const { declarations } = symbol;
if (declarations.length !== declarationRanges.length) {
this.raiseError(`Expected to get ${declarationRanges.length} declarations, got ${declarations.length}`);
}
ts.zipWith(declarations, declarationRanges, (decl, range) => {
this.verifyRange("symbol declaration", range, decl);
});
}
public verifySymbolAtLocation(startRange: Range, declarationRanges: Range[]): void {
const node = this.goToAndGetNode(startRange);
const symbol = this.getChecker().getSymbolAtLocation(node);
if (!symbol) {
this.raiseError("Could not get symbol at location");
}
this.verifySymbol(symbol, declarationRanges);
}
private verifyReferencesAre(expectedReferences: Range[]) {
const actualReferences = this.getReferencesAtCaret() || [];
if (actualReferences.length > expectedReferences.length) {
@@ -928,7 +1000,7 @@ namespace FourSlash {
for (const startRange of toArray(startRanges)) {
this.goToRangeStart(startRange);
const fullActual = this.findReferencesAtCaret().map<ReferenceJson>(({ definition, references }) => ({
const fullActual = ts.map<ts.ReferencedSymbol, ReferenceJson>(this.findReferencesAtCaret(), ({ definition, references }) => ({
definition: definition.displayParts.map(d => d.text).join(""),
ranges: references
}));
@@ -1108,9 +1180,32 @@ namespace FourSlash {
});
}
public verifyRenameLocations(findInStrings: boolean, findInComments: boolean, ranges?: Range[]) {
const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition);
if (renameInfo.canRename) {
public verifyRangesAreRenameLocations(options?: Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges?: Range[] }) {
const ranges = ts.isArray(options) ? options : options && options.ranges || this.getRanges();
this.verifyRenameLocations(ranges, { ranges, ...options });
}
public verifyRenameLocations(startRanges: Range | Range[], options: Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges: Range[] }) {
let findInStrings: boolean, findInComments: boolean, ranges: Range[];
if (ts.isArray(options)) {
findInStrings = findInComments = false;
ranges = options;
}
else {
findInStrings = !!options.findInStrings;
findInComments = !!options.findInComments;
ranges = options.ranges;
}
for (const startRange of toArray(startRanges)) {
this.goToRangeStart(startRange);
const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition);
if (!renameInfo.canRename) {
this.raiseError("Expected rename to succeed, but it actually failed.");
break;
}
let references = this.languageService.findRenameLocations(
this.activeFile.fileName, this.currentCaretPosition, findInStrings, findInComments);
@@ -1132,13 +1227,10 @@ namespace FourSlash {
ts.zipWith(references, ranges, (reference, range) => {
if (reference.textSpan.start !== range.start || ts.textSpanEnd(reference.textSpan) !== range.end) {
this.raiseError("Rename location results do not match.\n\nExpected: " + stringify(ranges) + "\n\nActual:" + JSON.stringify(references));
this.raiseError("Rename location results do not match.\n\nExpected: " + stringify(ranges) + "\n\nActual:" + stringify(references));
}
});
}
else {
this.raiseError("Expected rename to succeed, but it actually failed.");
}
}
public verifyQuickInfoExists(negative: boolean) {
@@ -2551,8 +2643,8 @@ namespace FourSlash {
}
}
public verifyRangesAreRenameLocations(findInStrings: boolean, findInComments: boolean) {
this.goToEachRange(() => this.verifyRenameLocations(findInStrings, findInComments));
public verifyRangesWithSameTextAreRenameLocations() {
this.rangesByText().forEach(ranges => this.verifyRangesAreRenameLocations(ranges));
}
public verifyRangesWithSameTextAreDocumentHighlights() {
@@ -2591,7 +2683,7 @@ namespace FourSlash {
ts.zipWith(expectedRangesInFile, spansInFile, (expectedRange, span) => {
if (span.textSpan.start !== expectedRange.start || ts.textSpanEnd(span.textSpan) !== expectedRange.end) {
this.raiseError(`verifyDocumentHighlights failed - span does not match, actual: ${JSON.stringify(span.textSpan)}, expected: ${expectedRange.start}--${expectedRange.end}`);
this.raiseError(`verifyDocumentHighlights failed - span does not match, actual: ${stringify(span.textSpan)}, expected: ${expectedRange.start}--${expectedRange.end}`);
}
});
}
@@ -3495,8 +3587,8 @@ namespace FourSlashInterface {
this.state.verifyGetEmitOutputContentsForCurrentFile(expected);
}
public referencesAre(ranges: FourSlash.Range[]) {
this.state.verifyReferencesAre(ranges);
public symbolAtLocation(startRange: FourSlash.Range, ...declarationRanges: FourSlash.Range[]) {
this.state.verifySymbolAtLocation(startRange, declarationRanges);
}
public referencesOf(start: FourSlash.Range, references: FourSlash.Range[]) {
@@ -3563,6 +3655,10 @@ namespace FourSlashInterface {
this.state.verifyCurrentSignatureHelpIs(expected);
}
public noErrors() {
this.state.verifyNoErrors();
}
public numberOfErrorsInCurrentFile(expected: number) {
this.state.verifyNumberOfErrorsInCurrentFile(expected);
}
@@ -3659,8 +3755,12 @@ namespace FourSlashInterface {
this.state.verifyRangesAreOccurrences(isWriteAccess);
}
public rangesAreRenameLocations(findInStrings = false, findInComments = false) {
this.state.verifyRangesAreRenameLocations(findInStrings, findInComments);
public rangesWithSameTextAreRenameLocations() {
this.state.verifyRangesWithSameTextAreRenameLocations();
}
public rangesAreRenameLocations(options?: FourSlash.Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges?: FourSlash.Range[] }) {
this.state.verifyRangesAreRenameLocations(options);
}
public rangesAreDocumentHighlights(ranges?: FourSlash.Range[]) {
@@ -3697,8 +3797,8 @@ namespace FourSlashInterface {
this.state.verifyRenameInfoFailed(message);
}
public renameLocations(findInStrings: boolean, findInComments: boolean, ranges?: FourSlash.Range[]) {
this.state.verifyRenameLocations(findInStrings, findInComments, ranges);
public renameLocations(startRanges: FourSlash.Range | FourSlash.Range[], options: FourSlash.Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges: FourSlash.Range[] }) {
this.state.verifyRenameLocations(startRanges, options);
}
public verifyQuickInfoDisplayParts(kind: string, kindModifiers: string, textSpan: { start: number; length: number; },
+11 -23
View File
@@ -17,34 +17,22 @@ namespace ts.DocumentHighlights {
}
function getSemanticDocumentHighlights(node: Node, typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFilesToSearch: SourceFile[]): DocumentHighlights[] {
const context = new FindAllReferences.DefaultFindReferencesContext(typeChecker, cancellationToken);
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(context, node, sourceFilesToSearch);
return referencedSymbols && convertReferencedSymbols(referencedSymbols);
const referenceEntries = FindAllReferences.getReferenceEntriesForNode(node, sourceFilesToSearch, typeChecker, cancellationToken);
return referenceEntries && convertReferencedSymbols(referenceEntries);
}
function convertReferencedSymbols(referencedSymbols: ReferencedSymbol[]): DocumentHighlights[] {
const fileNameToDocumentHighlights = createMap<DocumentHighlights>();
const result: DocumentHighlights[] = [];
for (const referencedSymbol of referencedSymbols) {
for (const referenceEntry of referencedSymbol.references) {
const fileName = referenceEntry.fileName;
let documentHighlights = fileNameToDocumentHighlights.get(fileName);
if (!documentHighlights) {
documentHighlights = { fileName, highlightSpans: [] };
fileNameToDocumentHighlights.set(fileName, documentHighlights);
result.push(documentHighlights);
}
documentHighlights.highlightSpans.push({
textSpan: referenceEntry.textSpan,
kind: referenceEntry.isWriteAccess ? HighlightSpanKind.writtenReference : HighlightSpanKind.reference,
isInString: referenceEntry.isInString
});
function convertReferencedSymbols(referenceEntries: FindAllReferences.Entry[]): DocumentHighlights[] {
const fileNameToDocumentHighlights = createMap<HighlightSpan[]>();
for (const entry of referenceEntries) {
const { fileName, span } = FindAllReferences.toHighlightSpan(entry);
let highlightSpans = fileNameToDocumentHighlights.get(fileName);
if (!highlightSpans) {
fileNameToDocumentHighlights.set(fileName, highlightSpans = []);
}
highlightSpans.push(span);
}
return result;
return arrayFrom(fileNameToDocumentHighlights.entries(), ([fileName, highlightSpans ]) => ({ fileName, highlightSpans }));
}
function getSyntacticDocumentHighlights(node: Node, sourceFile: SourceFile): DocumentHighlights[] {
File diff suppressed because it is too large Load Diff
+567
View File
@@ -0,0 +1,567 @@
/* Code for finding imports of an exported symbol. Used only by FindAllReferences. */
/* @internal */
namespace ts.FindAllReferences {
export interface ImportsResult {
/** For every import of the symbol, the location and local symbol for the import. */
importSearches: Array<[Identifier, Symbol]>;
/** For rename imports/exports `{ foo as bar }`, `foo` is not a local, so it may be added as a reference immediately without further searching. */
singleReferences: Identifier[];
/** List of source files that may (or may not) use the symbol via a namespace. (For UMD modules this is every file.) */
indirectUsers: SourceFile[];
}
export type ImportTracker = (exportSymbol: Symbol, exportInfo: ExportInfo, isForRename: boolean) => ImportsResult;
/** Creates the imports map and returns an ImportTracker that uses it. Call this lazily to avoid calling `getDirectImportsMap` unnecessarily. */
export function createImportTracker(sourceFiles: SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken): ImportTracker {
const allDirectImports = getDirectImportsMap(sourceFiles, checker, cancellationToken);
return (exportSymbol, exportInfo, isForRename) => {
const { directImports, indirectUsers } = getImportersForExport(sourceFiles, allDirectImports, exportInfo, checker, cancellationToken);
return { indirectUsers, ...getSearchesFromDirectImports(directImports, exportSymbol, exportInfo.exportKind, checker, isForRename) };
};
}
/** Info about an exported symbol to perform recursive search on. */
export interface ExportInfo {
exportingModuleSymbol: Symbol;
exportKind: ExportKind;
}
export const enum ExportKind { Named, Default, ExportEquals }
export const enum ImportExport { Import, Export }
interface AmbientModuleDeclaration extends ModuleDeclaration { body?: ModuleBlock; }
type SourceFileLike = SourceFile | AmbientModuleDeclaration;
// Identifier for the case of `const x = require("y")`.
type Importer = AnyImportSyntax | Identifier | ExportDeclaration;
type ImporterOrCallExpression = Importer | CallExpression;
/** Returns import statements that directly reference the exporting module, and a list of files that may access the module through a namespace. */
function getImportersForExport(
sourceFiles: SourceFile[],
allDirectImports: Map<ImporterOrCallExpression[]>,
{ exportingModuleSymbol, exportKind }: ExportInfo,
checker: TypeChecker,
cancellationToken: CancellationToken
): { directImports: Importer[], indirectUsers: SourceFile[] } {
const markSeenDirectImport = nodeSeenTracker<ImporterOrCallExpression>();
const markSeenIndirectUser = nodeSeenTracker<SourceFileLike>();
const directImports: Importer[] = [];
const isAvailableThroughGlobal = !!exportingModuleSymbol.globalExports;
const indirectUserDeclarations: SourceFileLike[] = isAvailableThroughGlobal ? undefined : [];
handleDirectImports(exportingModuleSymbol);
return { directImports, indirectUsers: getIndirectUsers() };
function getIndirectUsers(): SourceFile[] {
if (isAvailableThroughGlobal) {
// It has `export as namespace`, so anything could potentially use it.
return sourceFiles;
}
// Module augmentations may use this module's exports without importing it.
for (const decl of exportingModuleSymbol.declarations) {
if (isExternalModuleAugmentation(decl)) {
addIndirectUser(decl as SourceFileLike);
}
}
// This may return duplicates (if there are multiple module declarations in a single source file, all importing the same thing as a namespace), but `State.markSearchedSymbol` will handle that.
return indirectUserDeclarations.map(getSourceFileOfNode);
}
function handleDirectImports(exportingModuleSymbol: Symbol): void {
const theseDirectImports = getDirectImports(exportingModuleSymbol);
if (theseDirectImports) for (const direct of theseDirectImports) {
if (!markSeenDirectImport(direct)) {
continue;
}
cancellationToken.throwIfCancellationRequested();
switch (direct.kind) {
case SyntaxKind.CallExpression:
if (!isAvailableThroughGlobal) {
const parent = direct.parent!;
if (exportKind === ExportKind.ExportEquals && parent.kind === SyntaxKind.VariableDeclaration) {
const { name } = parent as ts.VariableDeclaration;
if (name.kind === SyntaxKind.Identifier) {
directImports.push(name);
break;
}
}
// Don't support re-exporting 'require()' calls, so just add a single indirect user.
addIndirectUser(direct.getSourceFile());
}
break;
case SyntaxKind.ImportEqualsDeclaration:
handleNamespaceImport(direct, direct.name, hasModifier(direct, ModifierFlags.Export));
break;
case SyntaxKind.ImportDeclaration:
const namedBindings = direct.importClause && direct.importClause.namedBindings;
if (namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport) {
handleNamespaceImport(direct, namedBindings.name);
}
else {
directImports.push(direct);
}
break;
case SyntaxKind.ExportDeclaration:
if (!direct.exportClause) {
// This is `export * from "foo"`, so imports of this module may import the export too.
handleDirectImports(getContainingModuleSymbol(direct, checker));
}
else {
// This is `export { foo } from "foo"` and creates an alias symbol, so recursive search will get handle re-exports.
directImports.push(direct);
}
break;
}
}
}
function handleNamespaceImport(importDeclaration: ImportEqualsDeclaration | ImportDeclaration, name: Identifier, isReExport?: boolean): void {
if (exportKind === ExportKind.ExportEquals) {
// This is a direct import, not import-as-namespace.
directImports.push(importDeclaration);
}
else if (!isAvailableThroughGlobal) {
const sourceFileLike = getSourceFileLikeForImportDeclaration(importDeclaration);
Debug.assert(sourceFileLike.kind === SyntaxKind.SourceFile || sourceFileLike.kind === SyntaxKind.ModuleDeclaration);
if (isReExport || findNamespaceReExports(sourceFileLike, name, checker)) {
addIndirectUsers(sourceFileLike);
}
else {
addIndirectUser(sourceFileLike);
}
}
}
function addIndirectUser(sourceFileLike: SourceFileLike): boolean {
Debug.assert(!isAvailableThroughGlobal);
const isNew = markSeenIndirectUser(sourceFileLike);
if (isNew) {
indirectUserDeclarations.push(sourceFileLike);
}
return isNew;
}
/** Adds a module and all of its transitive dependencies as possible indirect users. */
function addIndirectUsers(sourceFileLike: SourceFileLike): void {
if (!addIndirectUser(sourceFileLike)) {
return;
}
const moduleSymbol = checker.getMergedSymbol(sourceFileLike.symbol);
Debug.assert(!!(moduleSymbol.flags & SymbolFlags.Module));
const directImports = getDirectImports(moduleSymbol);
if (directImports) for (const directImport of directImports) {
addIndirectUsers(getSourceFileLikeForImportDeclaration(directImport));
}
}
function getDirectImports(moduleSymbol: Symbol): ImporterOrCallExpression[] | undefined {
return allDirectImports.get(getSymbolId(moduleSymbol).toString());
}
}
/**
* Given the set of direct imports of a module, we need to find which ones import the particular exported symbol.
* The returned `importSearches` will result in the entire source file being searched.
* But re-exports will be placed in 'singleReferences' since they cannot be locally referenced.
*/
function getSearchesFromDirectImports(directImports: Importer[], exportSymbol: Symbol, exportKind: ExportKind, checker: TypeChecker, isForRename: boolean): Pick<ImportsResult, "importSearches" | "singleReferences"> {
const exportName = exportSymbol.name;
const importSearches: Array<[Identifier, Symbol]> = [];
const singleReferences: Identifier[] = [];
function addSearch(location: Identifier, symbol: Symbol): void {
importSearches.push([location, symbol]);
}
if (directImports) for (const decl of directImports) {
handleImport(decl);
}
return { importSearches, singleReferences };
function handleImport(decl: Importer): void {
if (decl.kind === SyntaxKind.ImportEqualsDeclaration) {
if (isExternalModuleImportEquals(decl)) {
handleNamespaceImportLike(decl.name);
}
return;
}
if (decl.kind === ts.SyntaxKind.Identifier) {
handleNamespaceImportLike(decl);
return;
}
// Ignore if there's a grammar error
if (decl.moduleSpecifier.kind !== SyntaxKind.StringLiteral) {
return;
}
if (decl.kind === SyntaxKind.ExportDeclaration) {
searchForNamedImport(decl.exportClause);
return;
}
const { importClause } = decl;
const { namedBindings } = importClause;
if (namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport) {
handleNamespaceImportLike(namedBindings.name);
return;
}
if (exportKind === ExportKind.Named) {
searchForNamedImport(namedBindings as NamedImports | undefined);
}
else {
// `export =` might be imported by a default import if `--allowSyntheticDefaultImports` is on, so this handles both ExportKind.Default and ExportKind.ExportEquals
const { name } = importClause;
// If a default import has the same name as the default export, allow to rename it.
// Given `import f` and `export default function f`, we will rename both, but for `import g` we will rename just that.
if (name && (!isForRename || name.text === symbolName(exportSymbol))) {
const defaultImportAlias = checker.getSymbolAtLocation(name);
addSearch(name, defaultImportAlias);
}
// 'default' might be accessed as a named import `{ default as foo }`.
if (!isForRename && exportKind === ExportKind.Default) {
Debug.assert(exportName === "default");
searchForNamedImport(namedBindings as NamedImports | undefined);
}
}
}
/**
* `import x = require("./x") or `import * as x from "./x"`.
* An `export =` may be imported by this syntax, so it may be a direct import.
* If it's not a direct import, it will be in `indirectUsers`, so we don't have to do anything here.
*/
function handleNamespaceImportLike(importName: Identifier): void {
// Don't rename an import that already has a different name than the export.
if (exportKind === ExportKind.ExportEquals && (!isForRename || importName.text === exportName)) {
addSearch(importName, checker.getSymbolAtLocation(importName));
}
}
function searchForNamedImport(namedBindings: NamedImportsOrExports | undefined): void {
if (namedBindings) for (const element of namedBindings.elements) {
const { name, propertyName } = element;
if ((propertyName || name).text !== exportName) {
continue;
}
if (propertyName) {
// This is `import { foo as bar } from "./a"` or `export { foo as bar } from "./a"`. `foo` isn't a local in the file, so just add it as a single reference.
singleReferences.push(propertyName);
if (!isForRename) { // If renaming `foo`, don't touch `bar`, just `foo`.
// Search locally for `bar`.
addSearch(name, checker.getSymbolAtLocation(name));
}
}
else {
const localSymbol = element.kind === SyntaxKind.ExportSpecifier && element.propertyName
? checker.getExportSpecifierLocalTargetSymbol(element) // For re-exporting under a different name, we want to get the re-exported symbol.
: checker.getSymbolAtLocation(name);
addSearch(name, localSymbol);
}
}
}
}
/** Returns 'true' is the namespace 'name' is re-exported from this module, and 'false' if it is only used locally. */
function findNamespaceReExports(sourceFileLike: SourceFileLike, name: Identifier, checker: TypeChecker): boolean {
const namespaceImportSymbol = checker.getSymbolAtLocation(name);
return forEachPossibleImportOrExportStatement(sourceFileLike, statement => {
if (statement.kind !== SyntaxKind.ExportDeclaration) return;
const { exportClause, moduleSpecifier } = statement as ExportDeclaration;
if (moduleSpecifier || !exportClause) return;
for (const element of exportClause.elements) {
if (checker.getExportSpecifierLocalTargetSymbol(element) === namespaceImportSymbol) {
return true;
}
}
});
}
/** Returns a map from a module symbol Id to all import statements that directly reference the module. */
function getDirectImportsMap(sourceFiles: SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken): Map<ImporterOrCallExpression[]> {
const map = createMap<ImporterOrCallExpression[]>();
for (const sourceFile of sourceFiles) {
cancellationToken.throwIfCancellationRequested();
forEachImport(sourceFile, (importDecl, moduleSpecifier) => {
const moduleSymbol = checker.getSymbolAtLocation(moduleSpecifier);
if (moduleSymbol) {
const id = getSymbolId(moduleSymbol).toString();
let imports = map.get(id);
if (!imports) {
map.set(id, imports = []);
}
imports.push(importDecl);
}
});
}
return map;
}
/** Iterates over all statements at the top level or in module declarations. Returns the first truthy result. */
function forEachPossibleImportOrExportStatement<T>(sourceFileLike: SourceFileLike, action: (statement: Statement) => T): T {
return forEach(sourceFileLike.kind === SyntaxKind.SourceFile ? sourceFileLike.statements : sourceFileLike.body.statements, statement =>
action(statement) || (isAmbientModuleDeclaration(statement) && forEach(statement.body && statement.body.statements, action)));
}
/** Calls `action` for each import, re-export, or require() in a file. */
function forEachImport(sourceFile: SourceFile, action: (importStatement: ImporterOrCallExpression, imported: StringLiteral) => void): void {
if (sourceFile.externalModuleIndicator) {
for (const moduleSpecifier of sourceFile.imports) {
action(importerFromModuleSpecifier(moduleSpecifier), moduleSpecifier);
}
}
else {
forEachPossibleImportOrExportStatement(sourceFile, statement => {
switch (statement.kind) {
case SyntaxKind.ExportDeclaration:
case SyntaxKind.ImportDeclaration: {
const decl = statement as ImportDeclaration | ExportDeclaration;
if (decl.moduleSpecifier && decl.moduleSpecifier.kind === SyntaxKind.StringLiteral) {
action(decl, decl.moduleSpecifier as StringLiteral);
}
break;
}
case SyntaxKind.ImportEqualsDeclaration: {
const decl = statement as ImportEqualsDeclaration;
const { moduleReference } = decl;
if (moduleReference.kind === SyntaxKind.ExternalModuleReference &&
moduleReference.expression.kind === SyntaxKind.StringLiteral) {
action(decl, moduleReference.expression as StringLiteral);
}
break;
}
}
});
if (sourceFile.flags & NodeFlags.JavaScriptFile) {
// Find all 'require()' calls.
sourceFile.forEachChild(function recur(node: Node): void {
if (isRequireCall(node, /*checkArgumentIsStringLiteral*/ true)) {
action(node, node.arguments[0] as StringLiteral);
} else {
node.forEachChild(recur);
}
});
}
}
}
function importerFromModuleSpecifier(moduleSpecifier: StringLiteral): Importer {
const decl = moduleSpecifier.parent;
if (decl.kind === SyntaxKind.ImportDeclaration || decl.kind === SyntaxKind.ExportDeclaration) {
return decl as ImportDeclaration | ExportDeclaration;
}
Debug.assert(decl.kind === SyntaxKind.ExternalModuleReference);
return (decl as ExternalModuleReference).parent;
}
export interface ImportedSymbol {
kind: ImportExport.Import;
symbol: Symbol;
isNamedImport: boolean;
}
export interface ExportedSymbol {
kind: ImportExport.Export;
symbol: Symbol;
exportInfo: ExportInfo;
}
/**
* Given a local reference, we might notice that it's an import/export and recursively search for references of that.
* If at an import, look locally for the symbol it imports.
* If an an export, look for all imports of it.
* This doesn't handle export specifiers; that is done in `getReferencesAtExportSpecifier`.
* @param comingFromExport If we are doing a search for all exports, don't bother looking backwards for the imported symbol, since that's the reason we're here.
*/
export function getImportOrExportSymbol(node: Node, symbol: Symbol, checker: TypeChecker, comingFromExport: boolean): ImportedSymbol | ExportedSymbol | undefined {
return comingFromExport ? getExport() : getExport() || getImport();
function getExport(): ExportedSymbol | ImportedSymbol | undefined {
const { parent } = node;
if (symbol.flags & SymbolFlags.Export) {
if (parent.kind === SyntaxKind.PropertyAccessExpression) {
// When accessing an export of a JS module, there's no alias. The symbol will still be flagged as an export even though we're at the use.
// So check that we are at the declaration.
return symbol.declarations.some(d => d === parent) && parent.parent.kind === ts.SyntaxKind.BinaryExpression
? getSpecialPropertyExport(parent.parent as ts.BinaryExpression, /*useLhsSymbol*/ false)
: undefined;
}
else {
const { exportSymbol } = symbol;
Debug.assert(!!exportSymbol);
return exportInfo(exportSymbol, getExportKindForDeclaration(parent));
}
}
else {
const exportNode = parent.kind === SyntaxKind.VariableDeclaration ? getAncestor(parent, SyntaxKind.VariableStatement) : parent;
if (hasModifier(exportNode, ModifierFlags.Export)) {
if (exportNode.kind === SyntaxKind.ImportEqualsDeclaration && (exportNode as ImportEqualsDeclaration).moduleReference === node) {
// We're at `Y` in `export import X = Y`. This is not the exported symbol, the left-hand-side is. So treat this as an import statement.
if (comingFromExport) {
return undefined;
}
const lhsSymbol = checker.getSymbolAtLocation((exportNode as ImportEqualsDeclaration).name);
return { kind: ImportExport.Import, symbol: lhsSymbol, isNamedImport: false };
}
else {
return exportInfo(symbol, getExportKindForDeclaration(exportNode));
}
}
else if (parent.kind === SyntaxKind.ExportAssignment) {
// Get the symbol for the `export =` node; its parent is the module it's the export of.
const exportingModuleSymbol = parent.symbol.parent;
Debug.assert(!!exportingModuleSymbol);
return { kind: ImportExport.Export, symbol, exportInfo: { exportingModuleSymbol, exportKind: ExportKind.ExportEquals } };
}
else if (parent.kind === ts.SyntaxKind.BinaryExpression) {
return getSpecialPropertyExport(parent as ts.BinaryExpression, /*useLhsSymbol*/ true);
}
else if (parent.parent.kind === SyntaxKind.BinaryExpression) {
return getSpecialPropertyExport(parent.parent as ts.BinaryExpression, /*useLhsSymbol*/ true);
}
}
function getSpecialPropertyExport(node: ts.BinaryExpression, useLhsSymbol: boolean): ExportedSymbol | undefined {
let kind: ExportKind;
switch (getSpecialPropertyAssignmentKind(node)) {
case SpecialPropertyAssignmentKind.ExportsProperty:
kind = ExportKind.Named;
break;
case SpecialPropertyAssignmentKind.ModuleExports:
kind = ExportKind.ExportEquals;
break;
default:
return undefined;
}
const sym = useLhsSymbol ? checker.getSymbolAtLocation((node.left as ts.PropertyAccessExpression).name) : symbol;
return sym && exportInfo(sym, kind);
}
}
function getImport(): ImportedSymbol | undefined {
const isImport = isNodeImport(node);
if (!isImport) return;
// A symbol being imported is always an alias. So get what that aliases to find the local symbol.
let importedSymbol = checker.getImmediateAliasedSymbol(symbol);
if (importedSymbol) {
// Search on the local symbol in the exporting module, not the exported symbol.
importedSymbol = skipExportSpecifierSymbol(importedSymbol, checker);
// Similarly, skip past the symbol for 'export ='
if (importedSymbol.name === "export=") {
importedSymbol = checker.getImmediateAliasedSymbol(importedSymbol);
}
if (symbolName(importedSymbol) === symbol.name) { // If this is a rename import, do not continue searching.
return { kind: ImportExport.Import, symbol: importedSymbol, ...isImport };
}
}
}
function exportInfo(symbol: Symbol, kind: ExportKind): ExportedSymbol {
const exportInfo = getExportInfo(symbol, kind, checker);
return exportInfo && { kind: ImportExport.Export, symbol, exportInfo };
}
// Not meant for use with export specifiers or export assignment.
function getExportKindForDeclaration(node: Node): ExportKind | undefined {
return hasModifier(node, ModifierFlags.Default) ? ExportKind.Default : ExportKind.Named;
}
}
function isNodeImport(node: Node): { isNamedImport: boolean } | undefined {
const { parent } = node;
switch (parent.kind) {
case SyntaxKind.ImportEqualsDeclaration:
return (parent as ImportEqualsDeclaration).name === node && isExternalModuleImportEquals(parent as ImportEqualsDeclaration)
? { isNamedImport: false }
: undefined;
case SyntaxKind.ImportSpecifier:
// For a rename import `{ foo as bar }`, don't search for the imported symbol. Just find local uses of `bar`.
return (parent as ImportSpecifier).propertyName ? undefined : { isNamedImport: true };
case SyntaxKind.ImportClause:
case SyntaxKind.NamespaceImport:
Debug.assert((parent as ImportClause | NamespaceImport).name === node);
return { isNamedImport: false };
default:
return undefined;
}
}
export function getExportInfo(exportSymbol: Symbol, exportKind: ExportKind, checker: TypeChecker): ExportInfo | undefined {
const exportingModuleSymbol = checker.getMergedSymbol(exportSymbol.parent); // Need to get merged symbol in case there's an augmentation.
// `export` may appear in a namespace. In that case, just rely on global search.
return isExternalModuleSymbol(exportingModuleSymbol) ? { exportingModuleSymbol, exportKind } : undefined;
}
function symbolName(symbol: Symbol): string {
if (symbol.name !== "default") {
return symbol.name;
}
const name = forEach(symbol.declarations, ({ name }) => name && name.kind === SyntaxKind.Identifier && name.text);
Debug.assert(!!name);
return name;
}
/** If at an export specifier, go to the symbol it refers to. */
function skipExportSpecifierSymbol(symbol: Symbol, checker: TypeChecker): Symbol {
// For `export { foo } from './bar", there's nothing to skip, because it does not create a new alias. But `export { foo } does.
if (symbol.declarations) for (const declaration of symbol.declarations) {
if (isExportSpecifier(declaration) && !(declaration as ExportSpecifier).propertyName && !(declaration as ExportSpecifier).parent.parent.moduleSpecifier) {
return checker.getExportSpecifierLocalTargetSymbol(declaration);
}
}
return symbol;
}
function getContainingModuleSymbol(importer: Importer, checker: TypeChecker): Symbol {
return checker.getMergedSymbol(getSourceFileLikeForImportDeclaration(importer).symbol);
}
function getSourceFileLikeForImportDeclaration(node: ImporterOrCallExpression): SourceFileLike {
if (node.kind === SyntaxKind.CallExpression) {
return node.getSourceFile();
}
const { parent } = node;
if (parent.kind === SyntaxKind.SourceFile) {
return parent as SourceFile;
}
Debug.assert(parent.kind === SyntaxKind.ModuleBlock && isAmbientModuleDeclaration(parent.parent));
return parent.parent as AmbientModuleDeclaration;
}
function isAmbientModuleDeclaration(node: Node): node is AmbientModuleDeclaration {
return node.kind === SyntaxKind.ModuleDeclaration && (node as ModuleDeclaration).name.kind === SyntaxKind.StringLiteral;
}
function isExternalModuleImportEquals({ moduleReference }: ImportEqualsDeclaration): boolean {
return moduleReference.kind === SyntaxKind.ExternalModuleReference && moduleReference.expression.kind === SyntaxKind.StringLiteral;
}
}
+15 -10
View File
@@ -31,21 +31,25 @@ namespace ts.Rename {
return getRenameInfoError(Diagnostics.You_cannot_rename_elements_that_are_defined_in_the_standard_TypeScript_library);
}
// Cannot rename `default` as in `import { default as foo } from "./someModule";
if (node.kind === SyntaxKind.Identifier &&
(node as Identifier).originalKeywordKind === SyntaxKind.DefaultKeyword &&
symbol.parent.flags & ts.SymbolFlags.Module) {
return undefined;
}
const displayName = stripQuotes(getDeclaredName(typeChecker, symbol, node));
const kind = SymbolDisplay.getSymbolKind(typeChecker, symbol, node);
return kind ? getRenameInfoSuccess(displayName, typeChecker.getFullyQualifiedName(symbol), kind, SymbolDisplay.getSymbolModifiers(symbol), node, sourceFile) : undefined;
}
}
else if (node.kind === SyntaxKind.StringLiteral) {
const type = getStringLiteralTypeForNode(<StringLiteral>node, typeChecker);
if (type) {
if (isDefinedInLibraryFile(node)) {
return getRenameInfoError(Diagnostics.You_cannot_rename_elements_that_are_defined_in_the_standard_TypeScript_library);
}
const displayName = stripQuotes(type.text);
return getRenameInfoSuccess(displayName, displayName, ScriptElementKind.variableElement, ScriptElementKindModifier.none, node, sourceFile);
if (isDefinedInLibraryFile(node)) {
return getRenameInfoError(Diagnostics.You_cannot_rename_elements_that_are_defined_in_the_standard_TypeScript_library);
}
const displayName = stripQuotes((node as StringLiteral).text);
return getRenameInfoSuccess(displayName, displayName, ScriptElementKind.variableElement, ScriptElementKindModifier.none, node, sourceFile);
}
}
@@ -84,8 +88,9 @@ namespace ts.Rename {
return createTextSpan(start, width);
}
function nodeIsEligibleForRename(node: Node) {
return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.StringLiteral ||
function nodeIsEligibleForRename(node: Node): boolean {
return node.kind === ts.SyntaxKind.Identifier ||
node.kind === SyntaxKind.StringLiteral ||
isLiteralNameOfPropertyDeclarationOrIndexAccess(node) ||
isThis(node);
}
+18 -24
View File
@@ -10,7 +10,6 @@
/// <reference path='documentRegistry.ts' />
/// <reference path='findAllReferences.ts' />
/// <reference path='goToDefinition.ts' />
/// <reference path='goToImplementation.ts' />
/// <reference path='jsDoc.ts' />
/// <reference path='jsTyping.ts' />
/// <reference path='navigateTo.ts' />
@@ -502,8 +501,8 @@ namespace ts {
public nameTable: Map<number>;
public resolvedModules: Map<ResolvedModuleFull>;
public resolvedTypeReferenceDirectiveNames: Map<ResolvedTypeReferenceDirective>;
public imports: LiteralExpression[];
public moduleAugmentations: LiteralExpression[];
public imports: StringLiteral[];
public moduleAugmentations: StringLiteral[];
private namedDeclarations: Map<Declaration[]>;
public ambientModuleNames: string[];
public checkJsDirective: CheckJsDirective | undefined;
@@ -1410,18 +1409,18 @@ namespace ts {
return GoToDefinition.getDefinitionAtPosition(program, getValidSourceFile(fileName), position);
}
/// Goto implementation
function getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[] {
synchronizeHostData();
return GoToImplementation.getImplementationAtPosition(program.getTypeChecker(), cancellationToken,
program.getSourceFiles(), getTouchingPropertyName(getValidSourceFile(fileName), position));
}
function getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] {
synchronizeHostData();
return GoToDefinition.getTypeDefinitionAtPosition(program.getTypeChecker(), getValidSourceFile(fileName), position);
}
/// Goto implementation
function getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[] {
synchronizeHostData();
return FindAllReferences.getImplementationsAtPosition(program.getTypeChecker(), cancellationToken, program.getSourceFiles(), getValidSourceFile(fileName), position);
}
/// References and Occurrences
function getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[] {
let results = getOccurrencesAtPositionCore(fileName, position);
@@ -1443,10 +1442,7 @@ namespace ts {
return DocumentHighlights.getDocumentHighlights(program.getTypeChecker(), cancellationToken, sourceFile, position, sourceFilesToSearch);
}
/// References and Occurrences
function getOccurrencesAtPositionCore(fileName: string, position: number): ReferenceEntry[] {
synchronizeHostData();
return convertDocumentHighlights(getDocumentHighlights(fileName, position, [fileName]));
function convertDocumentHighlights(documentHighlights: DocumentHighlights[]): ReferenceEntry[] {
@@ -1472,24 +1468,21 @@ namespace ts {
}
function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[] {
const referencedSymbols = findReferencedSymbols(fileName, position, findInStrings, findInComments, /*isForRename*/ true);
return FindAllReferences.convertReferences(referencedSymbols);
return getReferences(fileName, position, { findInStrings, findInComments, isForRename: true });
}
function getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] {
const referencedSymbols = findReferencedSymbols(fileName, position, /*findInStrings*/ false, /*findInComments*/ false, /*isForRename*/ false);
return FindAllReferences.convertReferences(referencedSymbols);
return getReferences(fileName, position);
}
function getReferences(fileName: string, position: number, options?: FindAllReferences.Options) {
synchronizeHostData();
return FindAllReferences.findReferencedEntries(program.getTypeChecker(), cancellationToken, program.getSourceFiles(), getValidSourceFile(fileName), position, options);
}
function findReferences(fileName: string, position: number): ReferencedSymbol[] {
const referencedSymbols = findReferencedSymbols(fileName, position, /*findInStrings*/ false, /*findInComments*/ false, /*isForRename*/ false);
// Only include referenced symbols that have a valid definition.
return filter(referencedSymbols, rs => !!rs.definition);
}
function findReferencedSymbols(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, isForRename: boolean): ReferencedSymbol[] {
synchronizeHostData();
return FindAllReferences.findReferencedSymbols(program.getTypeChecker(), cancellationToken, program.getSourceFiles(), getValidSourceFile(fileName), position, findInStrings, findInComments, isForRename);
return FindAllReferences.findReferencedSymbols(program.getTypeChecker(), cancellationToken, program.getSourceFiles(), getValidSourceFile(fileName), position);
}
/// NavigateTo
@@ -2013,6 +2006,7 @@ namespace ts {
}
/* @internal */
/** Names in the name table are escaped, so an identifier `__foo` will have a name table entry `___foo`. */
export function getNameTable(sourceFile: SourceFile): Map<number> {
if (!sourceFile.nameTable) {
initializeNameTable(sourceFile);
+1 -1
View File
@@ -46,8 +46,8 @@
"documentHighlights.ts",
"documentRegistry.ts",
"findAllReferences.ts",
"importTracker.ts",
"goToDefinition.ts",
"goToImplementation.ts",
"jsDoc.ts",
"jsTyping.ts",
"navigateTo.ts",
+2 -5
View File
@@ -483,12 +483,9 @@ namespace ts {
displayParts: SymbolDisplayPart[];
}
export interface ReferencedSymbolOf<T extends DocumentSpan> {
export interface ReferencedSymbol {
definition: ReferencedSymbolDefinitionInfo;
references: T[];
}
export interface ReferencedSymbol extends ReferencedSymbolOf<ReferenceEntry> {
references: ReferenceEntry[];
}
export enum SymbolDisplayPartKind {
+18 -19
View File
@@ -364,15 +364,6 @@ namespace ts {
}
}
export function getStringLiteralTypeForNode(node: StringLiteral | LiteralTypeNode, typeChecker: TypeChecker): LiteralType {
const searchNode = node.parent.kind === SyntaxKind.LiteralType ? <LiteralTypeNode>node.parent : node;
const type = typeChecker.getTypeAtLocation(searchNode);
if (type && type.flags & TypeFlags.StringLiteral) {
return <LiteralType>type;
}
return undefined;
}
export function isThis(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ThisKeyword:
@@ -1132,6 +1123,21 @@ namespace ts {
return false;
}
}
/** True if the symbol is for an external module, as opposed to a namespace. */
export function isExternalModuleSymbol(moduleSymbol: Symbol): boolean {
Debug.assert(!!(moduleSymbol.flags & SymbolFlags.Module));
return moduleSymbol.name.charCodeAt(0) === CharacterCodes.doubleQuote;
}
/** Returns `true` the first time it encounters a node and `false` afterwards. */
export function nodeSeenTracker<T extends Node>(): (node: T) => boolean {
const seen: Array<true> = [];
return node => {
const id = getNodeId(node);
return !seen[id] && (seen[id] = true);
};
}
}
// Display-part writer helpers
@@ -1300,21 +1306,14 @@ namespace ts {
export function getDeclaredName(typeChecker: TypeChecker, symbol: Symbol, location: Node): string {
// If this is an export or import specifier it could have been renamed using the 'as' syntax.
// If so we want to search for whatever is under the cursor.
if (isImportOrExportSpecifierName(location)) {
return location.getText();
}
else if (isStringOrNumericLiteral(location) &&
location.parent.kind === SyntaxKind.ComputedPropertyName) {
return (<LiteralExpression>location).text;
if (isImportOrExportSpecifierName(location) || isStringOrNumericLiteral(location) && location.parent.kind === SyntaxKind.ComputedPropertyName) {
return location.text;
}
// Try to get the local symbol if we're dealing with an 'export default'
// since that symbol has the "true" name.
const localExportDefaultSymbol = getLocalSymbolForExportDefault(symbol);
const name = typeChecker.symbolToString(localExportDefaultSymbol || symbol);
return name;
return typeChecker.symbolToString(localExportDefaultSymbol || symbol);
}
export function isImportOrExportSpecifierName(location: Node): location is Identifier {
@@ -11,11 +11,6 @@
const ranges = test.ranges();
const [r0, r1] = ranges;
verify.referenceGroups(r0, [
{ definition: "import x", ranges: [r0] },
{ definition: 'module "jquery"', ranges: [r1] }
]);
verify.referenceGroups(r1, [
{ definition: 'module "jquery"', ranges: [r0] },
{ definition: "import x", ranges: [r1] }
]);
// TODO: Want these to be in the same group, but that would require creating a symbol for `x`.
verify.singleReferenceGroup("import x", [r0]);
verify.singleReferenceGroup("import x", [r1]);
@@ -7,4 +7,4 @@
goTo.eof();
edit.insertLine('');
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
+1 -1
View File
@@ -42,4 +42,4 @@ verify.completionListContains('baz');
verify.completionListContains('x');
edit.insert('bar()');
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
+1 -1
View File
@@ -46,4 +46,4 @@ verify.completionListContains('baz');
verify.completionListContains('x');
edit.insert('bar()');
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
+1 -1
View File
@@ -30,4 +30,4 @@ edit.insert('x;');
verify.quickInfoAt("5", "(local var) r2: number");
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -10,4 +10,4 @@
////}
verify.quickInfoAt("", "var M.C.C: typeof M.C");
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -12,6 +12,6 @@
////import {/*1*/} from './foo';
verify.completionsAt("1", ["prototype", "prop1", "prop2"]);
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
goTo.marker('2');
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -32,4 +32,4 @@ verify.not.completionListContains('bar');
verify.not.completionListContains('baz');
edit.insert('foo;');
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -35,4 +35,4 @@ verify.not.completionListContains('bar');
verify.not.completionListContains('baz');
edit.insert('foo;');
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -25,4 +25,4 @@
////var result2 = r2.x;
verify.quickInfoAt("", "var r: CollectionItem");
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -10,8 +10,8 @@
goTo.marker('1');
edit.backspace(6);
edit.insert('T');
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
goTo.marker('2');
edit.insertLine('');
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
+2 -2
View File
@@ -19,7 +19,7 @@
// If we do not, an error will be raised claiming
// that foo's return type is not assignable with
// it's signature return type
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
goTo.marker('1');
edit.insert('D = C << 1,');
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -10,7 +10,7 @@
//// }
////}
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
// Edit and bind and resolve only var decl
goTo.marker("1");
@@ -18,5 +18,4 @@ edit.backspace(1);
edit.insert(" ");
verify.quickInfoIs("var M.C.C: typeof M.C");
// Verify there are no errors
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
+1 -1
View File
@@ -21,4 +21,4 @@ verify.quickInfos({
});
goTo.marker('4');
verify.completionListContains('foo');
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -17,4 +17,4 @@ edit.insert("interface Array<T> { pop(def: T): T; }");
verify.not.errorExistsBetweenMarkers("1", "2");
verify.quickInfoAt("y", "var y: number");
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -12,4 +12,4 @@
////var /**/x = b.foo2().foo(5).foo(); // 'x' is of type 'void'
verify.quickInfoAt("", "var x: void");
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
+1 -1
View File
@@ -11,4 +11,4 @@
////y.length;
verify.quickInfoAt("", "var y: Date[]");
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -41,6 +41,14 @@
////class d extends a.C { constructor() { [|super|](); }
const ranges = test.ranges();
const [r0, r1, r2] = ranges;
verify.referenceGroups([r0, r2], [{ definition: "constructor C(n: number): C (+1 overload)", ranges }]);
verify.referenceGroups(r1, [{ definition: "constructor C(): C (+1 overload)", ranges }]);
const [a0, a1, a2, a3, a4, b0, c0, d0, d1] = ranges;
verify.referenceGroups([a0, a2], defs("constructor C(n: number): C (+1 overload)"));
verify.referenceGroups(a1, defs("constructor C(): C (+1 overload)"));
function defs(definition: string) {
return [
{ definition, ranges: [a0, a1, a2, a3, d0, d1, a4] },
{ definition: "import C", ranges: [b0] },
{ definition: "import C", ranges: [c0] }
]
}
@@ -0,0 +1,28 @@
/// <reference path='fourslash.ts' />
// @Filename: /a.ts
////export default function [|{| "isWriteAccess": true, "isDefinition": true |}f|]() {}
// @Filename: /b.ts
////export import a = require("./a");
// @Filename: /c.ts
////import { a } from "./b";
////a.[|default|]();
////
////declare const x: { [|{| "isWriteAccess": true, "isDefinition": true |}default|]: number };
////x.[|default|];
const [r0, r1, r2, r3] = test.ranges();
verify.singleReferenceGroup("function f(): void", [r0, r1]);
verify.singleReferenceGroup("(property) default: number", [r2, r3]);
verify.rangesAreRenameLocations([r0]);
// Can't rename a default import.
goTo.rangeStart(r1);
verify.renameInfoFailed();
// Can rename a default property.
verify.rangesAreRenameLocations([r2, r3]);
@@ -0,0 +1,24 @@
/// <reference path='fourslash.ts' />
// `export as namespace` results in global search.
// @Filename: /node_modules/a/index.d.ts
////export function [|{| "isWriteAccess": true, "isDefinition": true |}f|](): void;
////export as namespace A;
// @Filename: /b.ts
////import { [|{| "isWriteAccess": true, "isDefinition": true |}f|] } from "a";
// @Filename: /c.ts
////A.[|f|]();
verify.noErrors();
const ranges = test.ranges();
const [r0, r1, r2] = ranges;
const globals = { definition: "function f(): void", ranges: [r0, r2] };
const imports = { definition: "import f", ranges: [r1] };
verify.referenceGroups([r0, r2], [globals, imports]);
verify.referenceGroups(r1, [imports, globals]);
@@ -10,5 +10,13 @@
// @Filename: c.ts
////import { f } from "./a";
verify.singleReferenceGroup("function f(): void");
const ranges = test.ranges();
const [r0, r1, r2] = ranges;
verify.referenceGroups(r0, [
{ definition: "function f(): void", ranges: [r0] },
{ definition: "import g", ranges: [r1, r2] }
]);
verify.referenceGroups(r1, [{ definition: "import g", ranges: [r1, r2] }]);
verify.referenceGroups(r2, [{ definition: "(alias) g(): void\nimport g", ranges: [r1, r2] }]);
verify.goToDefinition("ref", "def");
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
// @Filename: /a.ts
////export {};
////declare global {
//// function [|{| "isWriteAccess": true, "isDefinition": true |}f|](): void;
////}
// @Filename: /b.ts
////[|f|]();
verify.noErrors();
verify.singleReferenceGroup("function f(): void");
@@ -0,0 +1,60 @@
/// <reference path="fourslash.ts" />
// @Filename: /node_modules/a/index.d.ts
////declare function [|{| "isWriteAccess": true, "isDefinition": true |}a|](): void;
////declare namespace [|{| "isWriteAccess": true, "isDefinition": true |}a|] {
//// export const x: number;
////}
////export = [|a|];
// Import with different name and we find local refs
// @Filename: /b.ts
////import * as [|{| "isWriteAccess": true, "isDefinition": true |}b|] from "a";
////[|b|]();
////[|b|].x;
// Import with same name and we find all refs
// @Filename: /c.ts
////import * as [|{| "isWriteAccess": true, "isDefinition": true |}a|] from "a";
////[|a|]();
////[|a|].x;
verify.noErrors();
const ranges = test.ranges();
const [a0, a1, a2, b0, b1, b2, c0, c1, c2] = ranges;
const aRanges = [a0, a1, a2];
const bRanges = [b0, b1, b2];
const cRanges = [c0, c1, c2];
verify.referenceGroups(a0, [
{ definition: "function a(): void\nnamespace a", ranges: aRanges },
{ definition: "import b", ranges: bRanges },
{ definition: "import a", ranges: cRanges }
]);
verify.referenceGroups([a1, a2], [
{ definition: "namespace a\nfunction a(): void", ranges: aRanges },
{ definition: "import b", ranges: bRanges },
{ definition: "import a", ranges: cRanges }
]);
verify.referenceGroups([b0, b0], [
{ definition: "import b", ranges: bRanges }
]);
verify.referenceGroups(b1, [
{ definition: "(alias) b(): void\nimport b", ranges: bRanges }
]);
verify.referenceGroups([c0, c2], [
{ definition: "import a", ranges: cRanges },
{ definition: "namespace a\nfunction a(): void", ranges: aRanges },
{ definition: "import b", ranges: bRanges }
]);
verify.referenceGroups(c1, [
{ definition: "(alias) a(): void\nimport a", ranges: cRanges },
{ definition: "namespace a\nfunction a(): void", ranges: aRanges },
{ definition: "import b", ranges: bRanges }
]);
verify.renameLocations(aRanges, aRanges.concat(cRanges));
verify.rangesAreRenameLocations(bRanges);
verify.rangesAreRenameLocations(cRanges);
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
// @Filename: /node_modules/foo/index.d.ts
////export type [|{| "isWriteAccess": true, "isDefinition": true |}T|] = number;
// @Filename: /a.ts
////import * as foo from "foo";
////declare module "foo" {
//// export const x: [|T|];
////}
verify.noErrors();
verify.singleReferenceGroup("type T = number");
@@ -18,4 +18,3 @@ const ranges = test.ranges();
const [r0, r1] = ranges;
verify.referenceGroups(r0, [{ definition: "interface Test.start", ranges }]);
verify.referenceGroups(r1, [{ definition: "interface Second.Test.start", ranges }]);
@@ -14,5 +14,13 @@
const ranges = test.ranges();
const [r0, r1, r2, r3] = ranges;
verify.referenceGroups([r0, r1, r3], [{ definition: "class Class", ranges }]);
verify.referenceGroups(r2, [{ definition: "constructor Class(): Class", ranges }]);
const classes = { definition: "class Class", ranges: [r0] };
const imports = { definition: "import Class", ranges: [r1, r2] };
const reExports = { definition: "import Class", ranges: [r3] };
verify.referenceGroups(r0, [classes, imports, reExports]);
verify.referenceGroups(r1, [imports, classes, reExports]);
verify.referenceGroups(r2, [
{ definition: "(alias) new Class(): Class\nimport Class", ranges: [r1, r2] },
classes,
reExports
]);
@@ -13,11 +13,20 @@
////export { [|{| "isWriteAccess": true, "isDefinition": true |}Class|] as [|{| "isWriteAccess": true, "isDefinition": true |}C3|] } from "./a";
const ranges = test.rangesByText();
verify.singleReferenceGroup("class Class", ranges.get("Class"));
const classRanges = ranges.get("Class");
const [class0, class1, class2] = classRanges;
const c2Ranges = ranges.get("C2");
const [c2_0, c2_1] = c2Ranges;
const c3Ranges = ranges.get("C3");
const classes = { definition: "class Class", ranges: classRanges };
const c2s = { definition: "import C2", ranges: c2Ranges };
const c3s = { definition: "import C3", ranges: c3Ranges };
const c2s = ranges.get("C2");
const [c2_0, c2_1] = c2s;
verify.referenceGroups(c2_0, [{ definition: "import C2", ranges: c2s }]);
verify.referenceGroups(c2_1, [{ definition: "(alias) new C2(): C2\nimport C2", ranges: c2s }]);
verify.referenceGroups(classRanges, [classes, c2s, c3s]);
verify.singleReferenceGroup("import C3", ranges.get("C3"));
verify.referenceGroups(c2_0, [c2s])
verify.referenceGroups(c2_1, [{ definition: "(alias) new C2(): C2\nimport C2", ranges: c2Ranges }]);
verify.referenceGroups(c3Ranges, [c3s]);
verify.rangesWithSameTextAreRenameLocations();
@@ -0,0 +1,30 @@
/// <reference path='fourslash.ts' />
// @noLib: true
// @Filename: /a.ts
////var [|{| "isWriteAccess": true, "isDefinition": true |}x|];
////export { [|{| "isWriteAccess": true, "isDefinition": true |}x|] };
////export { [|{| "isWriteAccess": true, "isDefinition": true |}x|] as [|{| "isWriteAccess": true, "isDefinition": true |}y|] };
// @Filename: /b.ts
////import { [|{| "isWriteAccess": true, "isDefinition": true |}x|], [|{| "isWriteAccess": true, "isDefinition": true |}y|] } from "./a";
////[|x|]; [|y|];
verify.noErrors();
const [ax0, ax1, ax2, ay, bx0, by0, bx1, by1] = test.ranges();
const axRanges = [ax0, ax1, ax2];
const bxRanges = [bx0, bx1];
const byRanges = [by0, by1];
const axGroup = { definition: "var x: any", ranges: axRanges };
const bxGroup = { definition: "import x", ranges: bxRanges };
const ayGroup = { definition: "import y", ranges: [ay] }
const byGroup = { definition: "import y", ranges: byRanges }
verify.referenceGroups(axRanges, [axGroup, bxGroup, ayGroup, byGroup]);
verify.referenceGroups(bxRanges, [bxGroup, axGroup, ayGroup, byGroup]);
verify.referenceGroups(ay, [ayGroup, byGroup]);
verify.referenceGroups(byRanges, [byGroup, ayGroup]);
verify.rangesWithSameTextAreRenameLocations();
@@ -0,0 +1,37 @@
/// <reference path="fourslash.ts" />
// @Filename: /a.ts
////export const [|{| "isWriteAccess": true, "isDefinition": true |}x|] = 0;
// @Filename: /b.ts
////export const [|{| "isWriteAccess": true, "isDefinition": true |}x|] = 0;
//@Filename: /c.ts
////export { [|{| "isWriteAccess": true, "isDefinition": true |}x|] } from "./b";
////import { [|{| "isWriteAccess": true, "isDefinition": true |}x|] } from "./a";
////[|x|];
// @Filename: /d.ts
////import { [|{| "isWriteAccess": true, "isDefinition": true |}x|] } from "./c";
verify.noErrors();
const [a, b, cFromB, cFromA, cUse, d] = test.ranges();
const cFromARanges = [cFromA, cUse];
const aGroup = { definition: "const x: 0", ranges: [a] };
const cFromAGroup = { definition: "import x", ranges: cFromARanges };
verify.referenceGroups(a, [aGroup, cFromAGroup]);
const bGroup = { definition: "const x: 0", ranges: [b] };
const cFromBGroup = { definition: "import x", ranges: [cFromB] };
const dGroup = { definition: "import x", ranges: [d] };
verify.referenceGroups(b, [bGroup, cFromBGroup, dGroup]);
verify.referenceGroups(cFromB, [cFromBGroup, dGroup, bGroup]);
verify.referenceGroups(cFromARanges, [cFromAGroup, aGroup]);
verify.referenceGroups(d, [dGroup, cFromBGroup, bGroup]);
verify.rangesAreRenameLocations([a, cFromA, cUse]);
verify.rangesAreRenameLocations([b, cFromB, d]);
@@ -0,0 +1,18 @@
/// <reference path="fourslash.ts" />
// @Filename: /a.ts
////export function [|{| "isWriteAccess": true, "isDefinition": true |}foo|](): void {}
// @Filename: /b.ts
////export * from "./a";
// @Filename: /c.ts
////import { [|{| "isWriteAccess": true, "isDefinition": true |}foo|] } from "./b";
verify.noErrors();
const ranges = test.ranges();
const [r0, r1] = ranges;
const a = { definition: "function foo(): void", ranges: [r0] };
const c = { definition: "import foo", ranges: [r1] };
verify.referenceGroups(r0, [a, c]);
verify.referenceGroups(r1, [c, a]);
@@ -0,0 +1,61 @@
/// <reference path="fourslash.ts" />
// @Filename: /a.ts
////export function [|{| "isWriteAccess": true, "isDefinition": true |}foo|](): void {}
// @Filename: /b.ts
////export { [|{| "isWriteAccess": true, "isDefinition": true |}foo|] as [|{| "isWriteAccess": true, "isDefinition": true |}bar|] } from "./a";
// @Filename: /c.ts
////export { [|{| "isWriteAccess": true, "isDefinition": true |}foo|] as [|{| "isWriteAccess": true, "isDefinition": true |}default|] } from "./a";
// @Filename: /d.ts
////export { [|{| "isWriteAccess": true, "isDefinition": true |}default|] } from "./c";
// @Filename: /e.ts
////import { [|{| "isWriteAccess": true, "isDefinition": true |}bar|] } from "./b";
////import [|{| "isWriteAccess": true, "isDefinition": true |}baz|] from "./c";
////import { [|{| "isWriteAccess": true, "isDefinition": true |}default|] as [|{| "isWriteAccess": true, "isDefinition": true |}bang|] } from "./c";
////import [|{| "isWriteAccess": true, "isDefinition": true |}boom|] from "./d";
////[|bar|](); [|baz|](); [|bang|](); [|boom|]();
verify.noErrors();
const [foo0, foo1, bar0, foo2, defaultC, defaultD, bar1, baz0, defaultE, bang0, boom0, bar2, baz1, bang1, boom1] = test.ranges();
const a = { definition: "function foo(): void", ranges: [foo0, foo1, foo2] };
const b = { definition: "import bar", ranges: [bar0] };
const c = { definition: "import default", ranges: [defaultC, defaultE] };
const d = { definition: "import default", ranges: [defaultD] };
const eBar = { definition: "import bar", ranges: [bar1, bar2] };
const eBaz = { definition: "import baz", ranges: [baz0, baz1] };
const eBang = { definition: "import bang", ranges: [bang0, bang1] };
const eBoom = { definition: "import boom", ranges: [boom0, boom1] };
verify.referenceGroups([foo0, foo1, foo2], [a, b, eBar, c, d, eBoom, eBaz, eBang]);
verify.referenceGroups(bar0, [b, eBar]);
verify.referenceGroups(bar1, [eBar, b]);
verify.referenceGroups(bar2, [{ ...eBar, definition: "(alias) bar(): void\nimport bar" }, b]);
verify.referenceGroups([defaultC], [c, d, eBoom, eBaz, eBang]);
verify.referenceGroups(defaultD, [d, eBoom, a, b, eBar,c, eBaz, eBang]);
verify.referenceGroups(defaultE, [c, d, eBoom, eBaz, eBang]);
verify.referenceGroups(baz0, [eBaz]);
verify.referenceGroups(baz1, [{ ...eBaz, definition: "(alias) baz(): void\nimport baz" }]);
verify.referenceGroups(bang0, [eBang]);
verify.referenceGroups(bang1, [{ ...eBang, definition: "(alias) bang(): void\nimport bang" }]);
verify.referenceGroups(boom0, [eBoom]);
verify.referenceGroups(boom1, [{ ...eBoom, definition: "(alias) boom(): void\nimport boom" }]);
test.rangesByText().forEach((ranges, text) => {
if (text === "default") {
for (const range of ranges) {
goTo.rangeStart(defaultC);
verify.renameInfoFailed();
}
}
else {
verify.rangesAreRenameLocations(ranges);
}
});
@@ -0,0 +1,20 @@
/// <reference path="fourslash.ts" />
// @Filename: /a.ts
////export const [|{| "isWriteAccess": true, "isDefinition": true |}x|] = 0;
//@Filename: /b.ts
////import { [|{| "isWriteAccess": true, "isDefinition": true |}x|] as [|{| "isWriteAccess": true, "isDefinition": true |}x|] } from "./a";
////[|x|];
verify.noErrors();
const [r0, r1, r2, r3] = test.ranges();
const aRanges = [r0, r1];
const bRanges = [r2, r3];
const aGroup = { definition: "const x: 0", ranges: aRanges };
const bGroup = { definition: "import x", ranges: bRanges };
verify.referenceGroups(aRanges, [aGroup, bGroup]);
verify.referenceGroups(bRanges, [bGroup]);
verify.rangesAreRenameLocations(aRanges);
verify.rangesAreRenameLocations(aRanges);
@@ -7,7 +7,7 @@
//// obj.[|name|];
const [r0, r1, r2, r3, r4] = test.ranges();
verify.referenceGroups([r0, r3], [{ definition: "var name: string", ranges: [r0, r1, r3] }]);
verify.referenceGroups(r0, [{ definition: "var name: string", ranges: [r0, r1, r3] }]); //r3
verify.referenceGroups(r1, [
{ definition: "var name: string", ranges: [r0, r3] },
{ definition: "(property) name: string", ranges: [r1, r4] }
@@ -13,4 +13,7 @@
const ranges = test.ranges();
const [r0, r1, r2] = ranges;
verify.referenceGroups(ranges, [{ definition: "const SubmissionComp: (submission: any) => any", ranges: [r2, r0, r1] }]);
const imports = { definition: "import SubmissionComp", ranges: [r0, r1] };
const def = { definition: "const SubmissionComp: (submission: any) => any", ranges: [r2] };
verify.referenceGroups([r0, r1], [imports, def]);
verify.referenceGroups(r2, [def, imports]);
+6 -2
View File
@@ -184,6 +184,7 @@ declare namespace FourSlashInterface {
verifyGetEmitOutputForCurrentFile(expected: string): void;
verifyGetEmitOutputContentsForCurrentFile(expected: ts.OutputFile[]): void;
noReferences(markerNameOrRange?: string | Range): void;
symbolAtLocation(startRange: Range, ...declarationRanges: Range[]): void;
/**
* @deprecated, prefer 'referenceGroups'
* Like `referencesAre`, but goes to `start` first.
@@ -197,7 +198,8 @@ declare namespace FourSlashInterface {
referenceGroups(startRanges: Range | Range[], parts: Array<{ definition: string, ranges: Range[] }>): void;
singleReferenceGroup(definition: string, ranges?: Range[]): void;
rangesAreOccurrences(isWriteAccess?: boolean): void;
rangesAreRenameLocations(findInStrings?: boolean, findInComments?: boolean): void;
rangesWithSameTextAreRenameLocations(): void;
rangesAreRenameLocations(options?: Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges?: Range[] });
/**
* Performs `referencesOf` for every range on the whole set.
* If `ranges` is omitted, this is `test.ranges()`.
@@ -215,6 +217,8 @@ declare namespace FourSlashInterface {
currentSignatureParameterCountIs(expected: number): void;
currentSignatureTypeParameterCountIs(expected: number): void;
currentSignatureHelpIs(expected: string): void;
// Checks that there are no compile errors.
noErrors(): void;
numberOfErrorsInCurrentFile(expected: number): void;
baselineCurrentFileBreakpointLocations(): void;
baselineCurrentFileNameOrDottedNameSpans(): void;
@@ -256,7 +260,7 @@ declare namespace FourSlashInterface {
}[]): void;
renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string): void;
renameInfoFailed(message?: string): void;
renameLocations(findInStrings: boolean, findInComments: boolean, ranges?: Range[]): void;
renameLocations(startRanges: Range | Range[], options: Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges: Range[] }): void;
/** Verify the quick info available at the current marker. */
quickInfoIs(expectedText: string, expectedDocumentation?: string): void;
+1 -1
View File
@@ -20,7 +20,7 @@
////typeof C.k./*6*/caller === 'function';
////l./*7*/prototype = Object.prototype;
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
for (var i = 1; i <= 7; i++) {
goTo.marker('' + i);
verify.completionListCount(8);
@@ -8,4 +8,4 @@
////}
verify.quickInfoAt("", "var M.C.C: typeof M.C");
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -20,7 +20,7 @@
//// ofT: T;
//// ofFooNum: Foo<number>;
//// ofInterface: I;
//// ofIG4: { x: number };
//// ofIG4: { x: number };
//// ofIG6: { x: T };
//// ofC2: C<number>;
//// ofC4: C<{ x: T }>
@@ -35,33 +35,33 @@
////// T is any
////var f_/*a1*/r1 = f.prim1;
////var f_/*a2*/r2 = f.prim2;
////var f_/*a3*/r3 = f.ofT;
////var f_/*a3*/r3 = f.ofT;
////var f_/*a4*/r5 = f.ofFooNum;
////var f_/*a5*/r8 = f.ofInterface;
////var f_/*a6*/r12 = f.ofIG4;
////var f_/*a7*/r14 = f.ofIG6;
////var f_/*a7*/r14 = f.ofIG6;
////var f_/*a8*/r18 = f.ofC2;
////var f_/*a9*/r20 = f.ofC4;
////var f_/*a9*/r20 = f.ofC4;
////
////// T is number
////var f2_/*b1*/r1 = f2.prim1;
////var f2_/*b2*/r2 = f2.prim2;
////var f2_/*b3*/r3 = f2.ofT;
////var f2_/*b4*/r5 = f2.ofFooNum;
////var f2_/*b3*/r3 = f2.ofT;
////var f2_/*b4*/r5 = f2.ofFooNum;
////var f2_/*b5*/r8 = f2.ofInterface;
////var f2_/*b6*/r12 = f2.ofIG4;
////var f2_/*b7*/r14 = f2.ofIG6;
////var f2_/*b7*/r14 = f2.ofIG6;
////var f2_/*b8*/r18 = f2.ofC2;
////var f2_/*b9*/r20 = f2.ofC4;
////var f2_/*b9*/r20 = f2.ofC4;
////
////// T is I
////var f3_/*c1*/r1 = f3.prim1;
////var f3_/*c2*/r2 = f3.prim2;
////var f3_/*c3*/r3 = f3.ofT;
////var f3_/*c3*/r3 = f3.ofT;
////var f3_/*c4*/r5 = f3.ofFooNum;
////var f3_/*c5*/r8 = f3.ofInterface;
////var f3_/*c6*/r12 = f3.ofIG4;
////var f3_/*c7*/r14 = f3.ofIG6;
////var f3_/*c7*/r14 = f3.ofIG6;
////var f3_/*c8*/r18 = f3.ofC2;
////var f3_/*c9*/r20 = f3.ofC4;
////
@@ -74,20 +74,20 @@
////var f4_/*d6*/r12 = f4.ofIG4;
////var f4_/*d7*/r14 = f4.ofIG6;
////var f4_/*d8*/r18 = f4.ofC2;
////var f4_/*d9*/r20 = f4.ofC4;
////var f4_/*d9*/r20 = f4.ofC4;
////
////// T is Foo<number>
////var f5_/*e1*/r1 = f5.prim1;
////var f5_/*e2*/r2 = f5.prim2;
////var f5_/*e3*/r3 = f5.ofT;
////var f5_/*e3*/r3 = f5.ofT;
////var f5_/*e4*/r5 = f5.ofFooNum;
////var f5_/*e5*/r8 = f5.ofInterface;
////var f5_/*e6*/r12 = f5.ofIG4;
////var f5_/*e7*/r14 = f5.ofIG6;
////var f5_/*e7*/r14 = f5.ofIG6;
////var f5_/*e8*/r18 = f5.ofC2;
////var f5_/*e9*/r20 = f5.ofC4;
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
verify.quickInfos({
"a1": "var f_r1: number",
@@ -32,7 +32,7 @@
////var f_/*a1*/r4 = f.ofFooT;
////var f_/*a2*/r7 = f.ofFooFooNum;
////var f_/*a3*/r9 = f.ofIG;
////var f_/*a5*/r13 = f.ofIG5;
////var f_/*a5*/r13 = f.ofIG5;
////var f_/*a7*/r17 = f.ofC1;
////
////// T is number
@@ -60,10 +60,10 @@
////var f5_/*e1*/r4 = f5.ofFooT;
////var f5_/*e2*/r7 = f5.ofFooFooNum;
////var f5_/*e3*/r9 = f5.ofIG;
////var f5_/*e5*/r13 = f5.ofIG5;
////var f5_/*e5*/r13 = f5.ofIG5;
////var f5_/*e7*/r17 = f5.ofC1;
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
verify.quickInfos({
a1: "var f_r4: Foo<any>",
@@ -8,12 +8,12 @@
//// value(): T;
////}
////interface ChainedArray<T> extends ChainedObject<Array<T>> {
////
////
//// extend(...sources: any[]): ChainedArray<T>;
////}
//// /*1*/
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
goTo.marker('1');
edit.insert(' ');
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
+1 -1
View File
@@ -20,7 +20,7 @@
////var c/*5*/cc = _(aaa).map(xx => xx.length); // Should not error, should be any[]
////var d/*6*/dd = aaa.map(xx => xx.length); // should not error, should be any[]
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
verify.quickInfos({
1: "var bb: number[]",
2: "var cc: number[]",
+3 -3
View File
@@ -5,14 +5,14 @@
//// }
//// /*2*/
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
goTo.marker('1');
edit.insertLine("constructor(){}");
edit.insertLine("foo(a: T) {");
edit.insertLine(" return a;");
edit.insertLine("}");
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
goTo.marker('2');
edit.insertLine("var x = new C<number>();");
edit.insertLine("var y: number = x.foo(5);");
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -11,4 +11,4 @@
//// /*1*/
goTo.marker('1');
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -63,13 +63,13 @@
//// }
//// /*1*/
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
goTo.marker('1');
edit.insertLine('');
edit.insertLine('');
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
goTo.marker('2');
edit.deleteAtCaret("Cookie".length);
edit.insert("any");
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
edit.insertLine('var narnia = new GenericPlanet2<Cookie, string>('); // shouldn't crash at this point
@@ -30,4 +30,4 @@ verify.quickInfos({
4: "var r4: any",
41: "(method) Underscore.Static.all<any>(list: any[], iterator?: Underscore.Iterator<any, boolean>, context?: any): any"
});
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -30,4 +30,4 @@ verify.quickInfos({
4: "var r4: any",
41: "(method) Underscore.Static.all<any>(list: any[], iterator?: Underscore.Iterator<any, boolean>, context?: any): any"
});
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -5,4 +5,9 @@
////import { [|{| "isWriteAccess": true, "isDefinition": true |}x|] } from "./m";
////const y = [|x|];
verify.singleReferenceGroup("var x: number");
const ranges = test.ranges();
const [r0, r1, r2] = ranges;
const defs = { definition: "var x: number", ranges: [r0] };
const imports = { definition: "import x", ranges: [r1, r2] };
verify.referenceGroups(r0, [defs, imports]);
verify.referenceGroups([r1, r2], [imports, defs]);
@@ -4,6 +4,4 @@
// @Filename: File.d.ts
//// declare var v: string;
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -5,6 +5,6 @@
//// interface privateInterface {}
//// export class Bar implements /*1*/privateInterface/*2*/{ }
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -22,4 +22,4 @@
//// }
verify.allRangesAppearInImplementationList("function_call");
verify.allRangesAppearInImplementationList("declaration");
verify.allRangesAppearInImplementationList("declaration");
@@ -16,7 +16,7 @@
goTo.marker('1');
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
edit.insert("//");
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
+4 -9
View File
@@ -7,16 +7,11 @@
//// class Foo {
//// constructor() {
//// this.[|union|] = 'foo';
//// this./*1*/[|union|] = 100;
//// this.[|union|] = 100;
//// }
//// method() { return this./*2*/[|union|]; }
//// method() { return this.[|union|]; }
//// }
//// var x = new Foo();
//// x./*3*/[|union|];
//// x.[|union|];
goTo.marker('1');
verify.renameLocations(/*findInStrings*/false, /*findInComments*/false);
goTo.marker('2');
verify.renameLocations(/*findInStrings*/false, /*findInComments*/false);
goTo.marker('3');
verify.renameLocations(/*findInStrings*/false, /*findInComments*/false);
verify.rangesAreRenameLocations();
+1 -1
View File
@@ -29,4 +29,4 @@ verify.completionListContains("name");
edit.insert("name;\nsausages.");
verify.completionListContains("eggs");
edit.insert("eggs;");
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -7,5 +7,4 @@
//// * @param {function (string):void} y */
//// function fn(x, y) { }
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
+1 -3
View File
@@ -18,6 +18,4 @@
//// var x = <MyClass {...[|n/*src*/n|]}></MyClass>;
verify.goToDefinition("src", "dst");
goTo.marker('src');
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
verify.rangesAreRenameLocations();
@@ -10,17 +10,17 @@
//// return this.m(0);
//// }
//// }
//// export class B extends A {
//// export class B extends A {
//// constructor(a: string) {
//// super(a);
//// }
//// /*1*/
//// /*1*/
//// }
//// var a = new A("s");
//// var b = new B("s");
//// }
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
goTo.marker('1');
edit.insert("public m(n: number) { return 0; }");
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
+2 -2
View File
@@ -12,7 +12,7 @@
//// export class B extends A { /*1*/ }
//// }
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
goTo.marker('1');
edit.insert("public m(n: number) { return 0; }");
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -17,7 +17,7 @@
//// r4 = x; // 3
////}
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
verify.eval("r1", undefined);
verify.eval("r2", 2);
verify.eval("r3", 2);
+1 -1
View File
@@ -39,4 +39,4 @@ verify.completionListContains('x');
verify.completionListContains('foo');
verify.completionListContains('boo');
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
+1 -1
View File
@@ -30,4 +30,4 @@ verify.completionListContains('x');
verify.completionListContains('foo');
edit.insert('x;');
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -6,6 +6,6 @@
////(y) => y;
////x => x;
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
verify.not.errorExistsBeforeMarker();
verify.not.errorExistsAfterMarker();
+1 -2
View File
@@ -18,7 +18,7 @@
////const x = foo/*foo_value*/;
////const i: foo/*foo_type*/ = { x: 1, y: 2 };
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
verify.navigationItemsListCount(2, "foo", "exact");
verify.navigationItemsListContains("foo", "alias", "foo", "exact");
@@ -49,7 +49,6 @@ verify.goToDefinitionIs("foo_type_declaration");
////const x = bar/*bar_value*/;
////const i: bar/*bar_type*/ = { x: 1, y: 2 };
verify.numberOfErrorsInCurrentFile(0);
verify.navigationItemsListCount(2, "bar", "exact");
verify.navigationItemsListContains("bar", "alias", "bar", "exact");
verify.navigationItemsListContains("bar", "interface", "bar", "exact");
@@ -23,4 +23,4 @@ edit.insert('a');
verify.quickInfoIs("(property) B<string>.bar: string");
goTo.marker('2');
verify.quickInfoIs("var r4: string");
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -16,4 +16,4 @@
////}
verify.quickInfoAt("1", "(property) M2.A.foo: string", undefined);
verify.numberOfErrorsInCurrentFile(0);
verify.noErrors();
@@ -0,0 +1,21 @@
/// <reference path='fourslash.ts'/>
// @Filename: /defA.ts
////declare module "a" {
//// export type [|{| "isWriteAccess": true, "isDefinition": true |}T|] = number;
////}
// @Filename: /defB.ts
////declare module "b" {
//// export import a = require("a");
//// export const x: a.[|T|];
////}
// @Filename: /defC.ts
////declare module "c" {
//// import b = require("b");
//// const x: b.a.[|T|];
////}
verify.noErrors();
verify.singleReferenceGroup("type T = number");
@@ -1,7 +1,7 @@
/// <reference path="fourslash.ts" />
//@Filename: a.ts
////var /*1*/[|x|]: number;
////var [|x|]: number;
//@Filename: b.ts
/////// <reference path="a.ts" />
@@ -11,7 +11,4 @@
/////// <reference path="a.ts" />
////[|x|]++;
goTo.file("a.ts");
goTo.marker("1");
verify.renameLocations( /*findInStrings*/ false, /*findInComments*/ false);
verify.rangesAreRenameLocations();
@@ -5,7 +5,9 @@
////export = [|SomeModule|];
// @Filename: b.ts
////import M = require("./a");
////import C = M.SomeClass;
////import [|M|] = require("./a");
////import C = [|M|].SomeClass;
verify.rangesAreRenameLocations();
const [r0, r1, r2, r3] = test.ranges();
verify.rangesAreRenameLocations([r0, r1]);
verify.rangesAreRenameLocations([r2, r3]);
@@ -1,11 +1,10 @@
/// <reference path="fourslash.ts" />
///////<reference path="./Bar.ts" />
///////<reference path="./Bar.ts" />
////function /**/[|Bar|]() {
////function [|Bar|]() {
//// // This is a reference to Bar in a comment.
//// "this is a reference to Bar in a string"
////}
goTo.marker();
verify.renameLocations(/*findInStrings:*/ false, /*findInComments:*/ false);
verify.rangesAreRenameLocations();
@@ -1,11 +1,11 @@
/// <reference path="fourslash.ts" />
///////<reference path="./Bar.ts" />
///////<reference path="./Bar.ts" />
////function /**/[|Bar|]() {
////function [|Bar|]() {
//// // This is a reference to Bar in a comment.
//// "this is a reference to [|Bar|] in a string"
////}
goTo.marker();
verify.renameLocations(/*findInStrings:*/ true, /*findInComments:*/ false);
const ranges = test.ranges();
verify.renameLocations(ranges[0], { findInStrings: true, ranges })
@@ -1,11 +1,11 @@
/// <reference path="fourslash.ts" />
///////<reference path="./Bar.ts" />
///////<reference path="./Bar.ts" />
////function /**/[|Bar|]() {
////function [|Bar|]() {
//// // This is a reference to [|Bar|] in a comment.
//// "this is a reference to Bar in a string"
////}
goTo.marker();
verify.renameLocations(/*findInStrings:*/ false, /*findInComments:*/ true);
const ranges = test.ranges();
verify.renameLocations(ranges[0], { findInComments: true, ranges });
@@ -1,11 +1,11 @@
/// <reference path="fourslash.ts" />
///////<reference path="./Bar.ts" />
///////<reference path="./Bar.ts" />
////function /**/[|Bar|]() {
//// // This is a reference to [|Bar|] in a comment.
//// "this is a reference to [|Bar|] in a string"
////}
goTo.marker();
verify.renameLocations(/*findInStrings:*/ true, /*findInComments:*/ true);
const ranges = test.ranges();
verify.renameLocations(ranges[0], { findInStrings: true, findInComments: true, ranges });
+2 -3
View File
@@ -6,7 +6,6 @@
// @Filename: b.ts
////import { [|area|] } from './a';
////var t = /**/[|area|](10);
////var t = [|area|](10);
goTo.marker();
verify.renameLocations( /*findInStrings*/ false, /*findInComments*/ false);
verify.rangesAreRenameLocations()
@@ -1,12 +0,0 @@
/// <reference path='fourslash.ts'/>
// @allowJs: true
// @Filename: a.js
////exports./**/[|area|] = function (r) { return r * r; }
// @Filename: b.ts
////import { [|area|] } from './a';
////var t = [|area|](10);
goTo.marker();
verify.renameLocations( /*findInStrings*/ false, /*findInComments*/ false);
+10 -9
View File
@@ -16,14 +16,15 @@ verify.occurrencesAtPositionCount(1);
const ranges = test.ranges();
const [C, B0, B1] = ranges;
verify.referenceGroups([C, B0], [{ definition: "class B", ranges }]);
verify.referenceGroups(B1, [{ definition: "constructor B(): B", ranges }]);
goTo.rangeStart(C);
verify.renameLocations(false, false, [C, B0, B1]);
const classes = { definition: "class B", ranges: [C] };
const imports = { definition: "import B", ranges: [B0, B1] };
verify.referenceGroups(C, [classes, imports]);
verify.referenceGroups(B0, [imports, classes]);
verify.referenceGroups(B1, [
{ definition: "(alias) new B(): B\nimport B", ranges: [B0, B1] },
classes
]);
const rangesInB = [B0, B1];
for (const r of rangesInB) {
goTo.rangeStart(r);
verify.renameLocations(false, false, rangesInB);
}
verify.renameLocations(C, ranges);
verify.rangesAreRenameLocations([B0, B1]);
@@ -16,14 +16,12 @@ verify.occurrencesAtPositionCount(1);
const ranges = test.ranges();
const [C, B0, B1] = ranges;
verify.referenceGroups([C, B0], [{ definition: "class C", ranges }]);
verify.referenceGroups(B1, [{ definition: "constructor C(): B", ranges }]);
const bRanges = [B0, B1];
const classes = { definition: "class C", ranges: [C] };
const imports = { definition: "import B", ranges: [B0, B1] };
verify.referenceGroups(C, [classes, imports]);
verify.referenceGroups(B0, [imports]);
verify.referenceGroups(B1, [{ definition: "(alias) new B(): B\nimport B", ranges: bRanges }]);
goTo.rangeStart(C);
verify.renameLocations(false, false, [C, B0, B1]);
const rangesInB = [B0, B1];
for (const r of rangesInB) {
goTo.rangeStart(r);
verify.renameLocations(false, false, rangesInB);
}
verify.rangesAreRenameLocations([C]);
verify.rangesAreRenameLocations(bRanges);
@@ -1,20 +1,18 @@
/// <reference path='fourslash.ts' />
////interface I {
//// /*1*/[|property1|]: number;
//// property2: string;
////}
////var elems: I[];
////
////var p2: number, property1: number;
////for ({ [|property1|] } = elems[0]; p2 < 100; p2++) {
//// p2 = property1++;
////interface I {
//// [|property1|]: number;
//// property2: string;
////}
////for ({ /*2*/[|property1|]: p2 } = elems[0]; p2 < 100; p2++) {
////var elems: I[];
////
////var p2: number, property1: number;
////for ({ [|property1|] } = elems[0]; p2 < 100; p2++) {
//// p2 = property1++;
////}
////for ({ [|property1|]: p2 } = elems[0]; p2 < 100; p2++) {
////}
goTo.marker("1");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
goTo.marker("2");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
const ranges = test.ranges();
const [r0, , r2] = ranges;
verify.renameLocations([r0, r2], ranges);
@@ -1,7 +1,7 @@
/// <reference path='fourslash.ts' />
/// <reference path='fourslash.ts' />
////interface I {
//// /*1*/[|property1|]: number;
//// [|property1|]: number;
//// property2: string;
////}
////var elems: I[];
@@ -9,12 +9,10 @@
////var property1: number, p2: number;
////for ({ [|property1|] } of elems) {
//// property1++;
////}
////for ({ /*2*/[|property1|]: p2 } of elems) {
////}
goTo.marker("1");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
goTo.marker("2");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
////}
////for ({ [|property1|]: p2 } of elems) {
////}
const ranges = test.ranges();
const [r0, , r2] = ranges;
verify.renameLocations([r0, r2], ranges);
@@ -1,15 +1,13 @@
/// <reference path='fourslash.ts' />
////interface I {
//// /*1*/[|property1|]: number;
//// property2: string;
////}
////var elems: I[], p1: number, property1: number;
////[{ /*2*/[|property1|]: p1 }] = elems;
////[{ [|property1|] }] = elems;
////interface I {
//// [|property1|]: number;
//// property2: string;
////}
////var elems: I[], p1: number, property1: number;
////[{ [|property1|]: p1 }] = elems;
////[{ [|property1|] }] = elems;
goTo.marker("1");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
goTo.marker("2");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
const ranges = test.ranges();
const [r0, r1] = ranges;
verify.renameLocations([r0, r1], ranges);
@@ -1,22 +1,20 @@
/// <reference path='fourslash.ts' />
/// <reference path='fourslash.ts' />
////interface MultiRobot {
//// name: string;
//// skills: {
//// /*1*/[|primary|]: string;
//// [|primary|]: string;
//// secondary: string;
//// };
////}
////let multiRobot: MultiRobot;
////for ({ skills: { /*2*/[|primary|]: primaryA, secondary: secondaryA } } = multiRobot, i = 0; i < 1; i++) {
////for ({ skills: { [|primary|]: primaryA, secondary: secondaryA } } = multiRobot, i = 0; i < 1; i++) {
//// console.log(primaryA);
////}
////for ({ skills: { [|primary|], secondary } } = multiRobot, i = 0; i < 1; i++) {
//// console.log(primary);
////}
goTo.marker("1");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
goTo.marker("2");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
const ranges = test.ranges();
const [r0, r1] = ranges;
verify.renameLocations([r0, r1], ranges);
@@ -1,22 +1,20 @@
/// <reference path='fourslash.ts' />
/// <reference path='fourslash.ts' />
////interface MultiRobot {
//// name: string;
//// skills: {
//// /*1*/[|primary|]: string;
//// [|primary|]: string;
//// secondary: string;
//// };
////}
////let multiRobots: MultiRobot[];
////for ({ skills: { /*2*/[|primary|]: primaryA, secondary: secondaryA } } of multiRobots) {
////for ({ skills: { [|primary|]: primaryA, secondary: secondaryA } } of multiRobots) {
//// console.log(primaryA);
////}
////for ({ skills: { [|primary|], secondary } } of multiRobots) {
//// console.log(primary);
////}
goTo.marker("1");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
goTo.marker("2");
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false);
const ranges = test.ranges();
const [r0, r1] = ranges;
verify.renameLocations([r0, r1], ranges);
@@ -1,13 +1,14 @@
/// <reference path='fourslash.ts'/>
////export default class /*1*/[|DefaultExportedClass|] {
////export default class [|DefaultExportedClass|] {
////}
/////*
//// * Commenting [|DefaultExportedClass|]
//// * Commenting [|{| "inComment": true |}DefaultExportedClass|]
//// */
////
////var x: /*2*/[|DefaultExportedClass|];
////var x: [|DefaultExportedClass|];
////
////var y = new /*3*/[|DefaultExportedClass|];
////var y = new [|DefaultExportedClass|];
goTo.eachMarker(() => verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true));
const ranges = test.ranges();
verify.renameLocations(ranges.filter(r => !(r.marker && r.marker.data.inComment)), { findInComments: true, ranges });
@@ -4,11 +4,12 @@
//// return /*2*/[|DefaultExportedFunction|]
////}
/////**
//// * Commenting [|DefaultExportedFunction|]
//// * Commenting [|{| "inComment": true |}DefaultExportedFunction|]
//// */
////
////var x: typeof /*3*/[|DefaultExportedFunction|];
////
////var y = /*4*/[|DefaultExportedFunction|]();
goTo.eachMarker(() => verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true));
const ranges = test.ranges();
verify.renameLocations(ranges.filter(r => !(r.marker && r.marker.data.inComment)), { findInComments: true, ranges });
@@ -11,10 +11,11 @@
////var y = /*4*/[|f|]();
////
/////**
//// * Commenting [|f|]
//// * Commenting [|{| "inComment": true |}f|]
//// */
////namespace /*5*/[|f|] {
//// var local = 100;
////}
goTo.eachMarker(() => verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true));
const ranges = test.ranges();
verify.renameLocations(ranges.filter(r => !(r.marker && r.marker.data.inComment)), { findInComments: true, ranges });
@@ -7,4 +7,11 @@
////import { [|{| "isWriteAccess": true, "isDefinition": true |}a|] } from './a';
////export { [|{| "isWriteAccess": true, "isDefinition": true |}a|] };
verify.singleReferenceGroup("var a: any");
const ranges = test.ranges();
const [r0, r1, r2] = ranges;
const vars = { definition: "var a: any", ranges: [r0] };
const imports = { definition: "import a", ranges: [r1, r2] };
verify.referenceGroups(r0, [vars, imports]);
verify.referenceGroups(r1, [imports, vars]);
verify.referenceGroups(r2, [imports, vars]);
verify.rangesAreRenameLocations();
@@ -1,14 +1,35 @@
/// <reference path='fourslash.ts' />
////declare namespace N {
//// export var x: number;
////declare namespace [|{| "isWriteAccess": true, "isDefinition": true |}N|] {
//// export var [|{| "isWriteAccess": true, "isDefinition": true |}x|]: number;
////}
////declare module "mod" {
//// export = N;
//// export = [|N|];
////}
////declare module "test" {
//// import * as [|N|] from "mod";
//// export { [|N|] }; // Renaming N here would rename
////declare module "a" {
//// import * as [|{| "isWriteAccess": true, "isDefinition": true |}N|] from "mod";
//// export { [|{| "isWriteAccess": true, "isDefinition": true |}N|] }; // Renaming N here would rename
////}
////declare module "b" {
//// import { [|{| "isWriteAccess": true, "isDefinition": true |}N|] } from "a";
//// export const y: typeof [|N|].[|x|];
////}
verify.rangesAreRenameLocations();
const [N0, x0, N1, a0, a1, b0, b1, x1] = test.ranges();
const nRanges = [N0, N1];
const aRanges = [a0, a1];
const bRanges = [b0, b1];
const xRanges = [x0, x1];
const nGroup = { definition: "namespace N", ranges: nRanges };
const aGroup = { definition: "import N", ranges: aRanges };
const bGroup = { definition: "import N", ranges: [b0, b1] };
verify.referenceGroups(nRanges, [nGroup, aGroup, bGroup]);
verify.referenceGroups([a0, a1], [aGroup, nGroup, bGroup]);
verify.referenceGroups(bRanges, [bGroup, aGroup, nGroup]);
verify.singleReferenceGroup("var N.x: number", xRanges);
verify.renameLocations(nRanges, nRanges.concat(aRanges, bRanges));
verify.rangesAreRenameLocations(aRanges.concat(bRanges));
verify.rangesAreRenameLocations(xRanges);

Some files were not shown because too many files have changed in this diff Show More