From cbeeb519b130f2044b396c73040068646de9328a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 20 Mar 2015 14:31:36 -0700 Subject: [PATCH 1/9] Add a new findReferences API that buckets results with the definition they matched against. --- src/compiler/checker.ts | 13 +- src/harness/harnessLanguageService.ts | 3 + src/server/client.ts | 5 + src/services/services.ts | 381 +++++++++++------- src/services/shims.ts | 19 +- .../baselines/reference/APISample_compile.js | 5 + .../reference/APISample_compile.types | 17 + tests/baselines/reference/APISample_linter.js | 5 + .../reference/APISample_linter.types | 17 + .../reference/APISample_transform.js | 5 + .../reference/APISample_transform.types | 17 + .../baselines/reference/APISample_watcher.js | 5 + .../reference/APISample_watcher.types | 17 + .../fourslash/findAllRefsInsideWithBlock.ts | 2 +- .../getOccurrencesOfUndefinedSymbol.ts | 2 +- .../getOccurrencesSwitchCaseDefaultBroken.ts | 4 +- .../fourslash/getOccurrencesThisNegatives2.ts | 2 +- .../getOccurrencesTryCatchFinallyBroken.ts | 2 + tests/cases/fourslash/localGetReferences.ts | 2 +- .../referencesForIllegalAssignment.ts | 4 +- .../fourslash/referencesForIndexProperty2.ts | 2 +- tests/cases/fourslash/referencesForLabel2.ts | 2 +- 22 files changed, 364 insertions(+), 167 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d50e440c5e9..4275816f723 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7,6 +7,15 @@ module ts { /* @internal */ export let checkTime = 0; + /* @internal */ + export function getSymbolId(symbol: Symbol): number { + if (!symbol.id) { + symbol.id = nextSymbolId++; + } + + return symbol.id; + } + export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker { let Symbol = objectAllocator.getSymbolConstructor(); let Type = objectAllocator.getTypeConstructor(); @@ -250,8 +259,8 @@ module ts { function getSymbolLinks(symbol: Symbol): SymbolLinks { if (symbol.flags & SymbolFlags.Transient) return symbol; - if (!symbol.id) symbol.id = nextSymbolId++; - return symbolLinks[symbol.id] || (symbolLinks[symbol.id] = {}); + var id = getSymbolId(symbol); + return symbolLinks[id] || (symbolLinks[id] = {}); } function getNodeLinks(node: Node): NodeLinks { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 68ac8d916fb..299ba52bdeb 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -330,6 +330,9 @@ module Harness.LanguageService { getReferencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[] { return unwrapJSONCallResult(this.shim.getReferencesAtPosition(fileName, position)); } + findReferences(fileName: string, position: number): ts.ReferencedSymbol[] { + return unwrapJSONCallResult(this.shim.findReferences(fileName, position)); + } getOccurrencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[] { return unwrapJSONCallResult(this.shim.getOccurrencesAtPosition(fileName, position)); } diff --git a/src/server/client.ts b/src/server/client.ts index 07c742db4f8..875e3ffdb5b 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -300,6 +300,11 @@ module ts.server { }); } + findReferences(fileName: string, position: number): ReferencedSymbol[]{ + // Not yet implemented. + return []; + } + getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] { var lineOffset = this.positionToOneBasedLineOffset(fileName, position); var args: protocol.FileLocationRequestArgs = { diff --git a/src/services/services.ts b/src/services/services.ts index 5ff7c578aa0..2918e2910fb 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -937,6 +937,7 @@ module ts { getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[]; getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[]; + findReferences(fileName: string, position: number): ReferencedSymbol[]; getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; getNavigationBarItems(fileName: string): NavigationBarItem[]; @@ -1042,6 +1043,11 @@ module ts { containerName: string; } + export interface ReferencedSymbol { + definition: DefinitionInfo; + references: ReferenceEntry[]; + } + export enum SymbolDisplayPartKind { aliasName, className, @@ -3438,6 +3444,111 @@ module ts { }; } + function createDefinitionInfo(node: Node, symbolKind: string, symbolName: string, containerName: string): DefinitionInfo { + return { + fileName: node.getSourceFile().fileName, + textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()), + kind: symbolKind, + name: symbolName, + containerKind: undefined, + containerName + }; + } + + function getDefinitionInfos(node: Node, symbol: Symbol): DefinitionInfo[] { + // If this is an alias, and the request came at the declaration location + // get the aliased symbol instead. This allows for goto def on an import e.g. + // import {A, B} from "mod"; + // to jump to the implementation directly. + if (symbol.flags & SymbolFlags.Alias) { + let declaration = symbol.declarations[0]; + if (node.kind === SyntaxKind.Identifier && node.parent === declaration) { + symbol = typeInfoResolver.getAliasedSymbol(symbol); + } + } + + // Because name in short-hand property assignment has two different meanings: property name and property value, + // using go-to-definition at such position should go to the variable declaration of the property value rather than + // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition + // is performed at the location of property access, we would like to go to definition of the property in the short-hand + // assignment. This case and others are handled by the following code. + if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) { + let shorthandSymbol = typeInfoResolver.getShorthandAssignmentValueSymbol(symbol.valueDeclaration); + if (!shorthandSymbol) { + return []; + } + + let shorthandDeclarations = shorthandSymbol.getDeclarations(); + let shorthandSymbolKind = getSymbolKind(shorthandSymbol, typeInfoResolver, node); + let shorthandSymbolName = typeInfoResolver.symbolToString(shorthandSymbol); + let shorthandContainerName = typeInfoResolver.symbolToString(symbol.parent, node); + return map(shorthandDeclarations, + declaration => createDefinitionInfo(declaration, shorthandSymbolKind, shorthandSymbolName, shorthandContainerName)); + } + + let result: DefinitionInfo[] = []; + let declarations = symbol.getDeclarations(); + let symbolName = typeInfoResolver.symbolToString(symbol); // Do not get scoped name, just the name of the symbol + let symbolKind = getSymbolKind(symbol, typeInfoResolver, node); + let containerSymbol = symbol.parent; + let containerName = containerSymbol ? typeInfoResolver.symbolToString(containerSymbol, node) : ""; + + if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) && + !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) { + // Just add all the declarations. + forEach(declarations, declaration => { + result.push(createDefinitionInfo(declaration, symbolKind, symbolName, containerName)); + }); + } + + return result; + + function tryAddConstructSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) { + // Applicable only if we are in a new expression, or we are on a constructor declaration + // and in either case the symbol has a construct signature definition, i.e. class + if (isNewExpressionTarget(location) || location.kind === SyntaxKind.ConstructorKeyword) { + if (symbol.flags & SymbolFlags.Class) { + let classDeclaration = symbol.getDeclarations()[0]; + Debug.assert(classDeclaration && classDeclaration.kind === SyntaxKind.ClassDeclaration); + + return tryAddSignature(classDeclaration.members, /*selectConstructors*/ true, symbolKind, symbolName, containerName, result); + } + } + return false; + } + + function tryAddCallSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) { + if (isCallExpressionTarget(location) || isNewExpressionTarget(location) || isNameOfFunctionDeclaration(location)) { + return tryAddSignature(symbol.declarations, /*selectConstructors*/ false, symbolKind, symbolName, containerName, result); + } + return false; + } + + function tryAddSignature(signatureDeclarations: Declaration[], selectConstructors: boolean, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) { + let declarations: Declaration[] = []; + let definition: Declaration; + + forEach(signatureDeclarations, d => { + if ((selectConstructors && d.kind === SyntaxKind.Constructor) || + (!selectConstructors && (d.kind === SyntaxKind.FunctionDeclaration || d.kind === SyntaxKind.MethodDeclaration || d.kind === SyntaxKind.MethodSignature))) { + declarations.push(d); + if ((d).body) definition = d; + } + }); + + if (definition) { + result.push(createDefinitionInfo(definition, symbolKind, symbolName, containerName)); + return true; + } + else if (declarations.length) { + result.push(createDefinitionInfo(declarations[declarations.length - 1], symbolKind, symbolName, containerName)); + return true; + } + + return false; + } + } + /// Goto definition function getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] { synchronizeHostData(); @@ -3453,7 +3564,7 @@ module ts { if (isJumpStatementTarget(node)) { let labelName = (node).text; let label = getTargetLabel((node.parent), (node).text); - return label ? [getDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined; + return label ? [createDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined; } /// Triple slash reference comments @@ -3481,109 +3592,7 @@ module ts { return undefined; } - // If this is an alias, and the request came at the declaration location - // get the aliased symbol instead. This allows for goto def on an import e.g. - // import {A, B} from "mod"; - // to jump to the implementation directelly. - if (symbol.flags & SymbolFlags.Alias) { - let declaration = symbol.declarations[0]; - if (node.kind === SyntaxKind.Identifier && node.parent === declaration) { - symbol = typeInfoResolver.getAliasedSymbol(symbol); - } - } - - - // Because name in short-hand property assignment has two different meanings: property name and property value, - // using go-to-definition at such position should go to the variable declaration of the property value rather than - // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition - // is performed at the location of property access, we would like to go to definition of the property in the short-hand - // assignment. This case and others are handled by the following code. - if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) { - let shorthandSymbol = typeInfoResolver.getShorthandAssignmentValueSymbol(symbol.valueDeclaration); - if (!shorthandSymbol) { - return []; - } - - let shorthandDeclarations = shorthandSymbol.getDeclarations(); - let shorthandSymbolKind = getSymbolKind(shorthandSymbol, typeInfoResolver, node); - let shorthandSymbolName = typeInfoResolver.symbolToString(shorthandSymbol); - let shorthandContainerName = typeInfoResolver.symbolToString(symbol.parent, node); - return map(shorthandDeclarations, - declaration => getDefinitionInfo(declaration, shorthandSymbolKind, shorthandSymbolName, shorthandContainerName)); - } - - let result: DefinitionInfo[] = []; - let declarations = symbol.getDeclarations(); - let symbolName = typeInfoResolver.symbolToString(symbol); // Do not get scoped name, just the name of the symbol - let symbolKind = getSymbolKind(symbol, typeInfoResolver, node); - let containerSymbol = symbol.parent; - let containerName = containerSymbol ? typeInfoResolver.symbolToString(containerSymbol, node) : ""; - - if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) && - !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) { - // Just add all the declarations. - forEach(declarations, declaration => { - result.push(getDefinitionInfo(declaration, symbolKind, symbolName, containerName)); - }); - } - - return result; - - function getDefinitionInfo(node: Node, symbolKind: string, symbolName: string, containerName: string): DefinitionInfo { - return { - fileName: node.getSourceFile().fileName, - textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()), - kind: symbolKind, - name: symbolName, - containerKind: undefined, - containerName - }; - } - - function tryAddSignature(signatureDeclarations: Declaration[], selectConstructors: boolean, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) { - let declarations: Declaration[] = []; - let definition: Declaration; - - forEach(signatureDeclarations, d => { - if ((selectConstructors && d.kind === SyntaxKind.Constructor) || - (!selectConstructors && (d.kind === SyntaxKind.FunctionDeclaration || d.kind === SyntaxKind.MethodDeclaration || d.kind === SyntaxKind.MethodSignature))) { - declarations.push(d); - if ((d).body) definition = d; - } - }); - - if (definition) { - result.push(getDefinitionInfo(definition, symbolKind, symbolName, containerName)); - return true; - } - else if (declarations.length) { - result.push(getDefinitionInfo(declarations[declarations.length - 1], symbolKind, symbolName, containerName)); - return true; - } - - return false; - } - - function tryAddConstructSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) { - // Applicable only if we are in a new expression, or we are on a constructor declaration - // and in either case the symbol has a construct signature definition, i.e. class - if (isNewExpressionTarget(location) || location.kind === SyntaxKind.ConstructorKeyword) { - if (symbol.flags & SymbolFlags.Class) { - let classDeclaration = symbol.getDeclarations()[0]; - Debug.assert(classDeclaration && classDeclaration.kind === SyntaxKind.ClassDeclaration); - - return tryAddSignature(classDeclaration.members, /*selectConstructors*/ true, symbolKind, symbolName, containerName, result); - } - } - return false; - } - - function tryAddCallSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) { - if (isCallExpressionTarget(location) || isNewExpressionTarget(location) || isNameOfFunctionDeclaration(location)) { - return tryAddSignature(symbol.declarations, /*selectConstructors*/ false, symbolKind, symbolName, containerName, result); - } - return false; - } + return getDefinitionInfos(node, symbol); } /// References and Occurrences @@ -3599,7 +3608,7 @@ module ts { if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.SuperKeyword || isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || isNameOfExternalModuleImportOrDeclaration(node)) { - return getReferencesForNode(node, [sourceFile], /*searchOnlyInCurrentFile*/ true, /*findInStrings:*/ false, /*findInComments:*/ false); + return convertReferences(getReferencesForNode(node, [sourceFile], /*searchOnlyInCurrentFile*/ true, /*findInStrings:*/ false, /*findInComments:*/ false)); } switch (node.kind) { @@ -3899,7 +3908,7 @@ module ts { return map(keywords, getReferenceEntryFromNode); } - function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement) { + function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement): ReferenceEntry[] { let keywords: Node[] = []; pushKeywordIf(keywords, switchStatement.getFirstToken(), SyntaxKind.SwitchKeyword); @@ -4022,7 +4031,7 @@ module ts { } } - function getModifierOccurrences(modifier: SyntaxKind, declaration: Node) { + function getModifierOccurrences(modifier: SyntaxKind, declaration: Node): ReferenceEntry[] { let container = declaration.parent; // Make sure we only highlight the keyword when it makes sense to do so. @@ -4127,15 +4136,33 @@ module ts { } } - function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[] { - return findReferences(fileName, position, findInStrings, findInComments); + function convertReferences(referenceSymbols: ReferencedSymbol[]): ReferenceEntry[]{ + if (!referenceSymbols) { + return undefined; + } + + let referenceEntries: ReferenceEntry[] = []; + for (let referenceSymbol of referenceSymbols) { + addRange(referenceEntries, referenceSymbol.references); + } + return referenceEntries; + } + + function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[]{ + var referenceSymbols = findReferencesWorker(fileName, position, findInStrings, findInComments); + return convertReferences(referenceSymbols); } function getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] { - return findReferences(fileName, position, /*findInStrings:*/ false, /*findInComments:*/ false); + var referenceSymbols = findReferencesWorker(fileName, position, /*findInStrings:*/ false, /*findInComments:*/ false); + return convertReferences(referenceSymbols); } - function findReferences(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ReferenceEntry[] { + function findReferences(fileName: string, position: number): ReferencedSymbol[] { + return filter(findReferencesWorker(fileName, position, /*findInStrings:*/ false, /*findInComments:*/ false), rs => !!rs.definition); + } + + function findReferencesWorker(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ReferencedSymbol[] { synchronizeHostData(); let sourceFile = getValidSourceFile(fileName); @@ -4158,14 +4185,14 @@ module ts { return getReferencesForNode(node, program.getSourceFiles(), /*searchOnlyInCurrentFile*/ false, findInStrings, findInComments); } - function getReferencesForNode(node: Node, sourceFiles: SourceFile[], searchOnlyInCurrentFile: boolean, findInStrings: boolean, findInComments: boolean): ReferenceEntry[] { + function getReferencesForNode(node: Node, sourceFiles: SourceFile[], searchOnlyInCurrentFile: boolean, findInStrings: boolean, findInComments: boolean): ReferencedSymbol[]{ // Labels if (isLabelName(node)) { if (isJumpStatementTarget(node)) { let labelDefinition = getTargetLabel((node.parent), (node).text); // if we have a label definition, look within its statement for references, if not, then - // the label is undefined, just return a set of one for the current node. - return labelDefinition ? getLabelReferencesInNode(labelDefinition.parent, labelDefinition) : [getReferenceEntryFromNode(node)]; + // the label is undefined and we have no results.. + return labelDefinition ? getLabelReferencesInNode(labelDefinition.parent, labelDefinition) : undefined; } else { // it is a label definition and not a target, search within the parent labeledStatement @@ -4185,9 +4212,8 @@ module ts { // Could not find a symbol e.g. unknown identifier if (!symbol) { - // Even if we did not find a symbol, we have an identifier, so there is at least - // one reference that we know of. return that instead of undefined. - return [getReferenceEntryFromNode(node)]; + // Can't have references to something that we have no symbol for. + return undefined; } let declarations = symbol.declarations; @@ -4197,7 +4223,7 @@ module ts { return undefined; } - let result: ReferenceEntry[]; + let result: ReferencedSymbol[]; // Compute the meaning from the location and the symbol it references let searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), declarations); @@ -4209,15 +4235,18 @@ module ts { // otherwise we'll need to search globally (i.e. include each file). let scope = getSymbolScope(symbol); + // Maps from a symbol ID to the ReferencedSymbol entry in 'result'. + let symbolToIndex: number[] = []; + if (scope) { result = []; - getReferencesInNode(scope, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result); + getReferencesInNode(scope, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex); } else { if (searchOnlyInCurrentFile) { Debug.assert(sourceFiles.length === 1); result = []; - getReferencesInNode(sourceFiles[0], symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result); + getReferencesInNode(sourceFiles[0], symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex); } else { let internedName = getInternedName(symbol, node, declarations) @@ -4228,7 +4257,7 @@ module ts { if (lookUp(nameTable, internedName)) { result = result || []; - getReferencesInNode(sourceFile, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result); + getReferencesInNode(sourceFile, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex); } }); } @@ -4393,8 +4422,8 @@ module ts { return positions; } - function getLabelReferencesInNode(container: Node, targetLabel: Identifier): ReferenceEntry[] { - let result: ReferenceEntry[] = []; + function getLabelReferencesInNode(container: Node, targetLabel: Identifier): ReferencedSymbol[] { + let references: ReferenceEntry[] = []; let sourceFile = container.getSourceFile(); let labelName = targetLabel.text; let possiblePositions = getPossibleSymbolReferencePositions(sourceFile, labelName, container.getStart(), container.getEnd()); @@ -4409,10 +4438,20 @@ module ts { // Only pick labels that are either the target label, or have a target that is the target label if (node === targetLabel || (isJumpStatementTarget(node) && getTargetLabel(node, labelName) === targetLabel)) { - result.push(getReferenceEntryFromNode(node)); + references.push(getReferenceEntryFromNode(node)); } }); - return result; + + var definition: DefinitionInfo = { + containerKind: "", + containerName: "", + fileName: targetLabel.getSourceFile().fileName, + kind: ScriptElementKind.label, + name: labelName, + textSpan: createTextSpanFromBounds(targetLabel.getStart(), targetLabel.getEnd()) + } + + return [{ definition, references }]; } function isValidReferencePosition(node: Node, searchSymbolName: string): boolean { @@ -4452,7 +4491,9 @@ module ts { searchMeaning: SemanticMeaning, findInStrings: boolean, findInComments: boolean, - result: ReferenceEntry[]): void { + result: ReferencedSymbol[], + symbolToIndex: number[]): void { + let sourceFile = container.getSourceFile(); let tripleSlashDirectivePrefixRegex = /^\/\/\/\s*= 0) { - result.push(getReferenceEntryFromNode(referenceSymbolDeclaration.name)); + var referencedSymbol = getReferencedSymbol(shorthandValueSymbol); + referencedSymbol.references.push(getReferenceEntryFromNode(referenceSymbolDeclaration.name)); } } }); } + return; + + function getReferencedSymbol(symbol: Symbol): ReferencedSymbol { + var symbolId = getSymbolId(symbol); + var index = symbolToIndex[symbolId]; + if (index === undefined) { + index = result.length; + symbolToIndex[symbolId] = index; + + result.push({ + definition: getDefinitionInfos(node, symbol)[0], + references: [] + }); + } + + return result[index]; + } + function isInString(position: number) { let token = getTokenAtPosition(sourceFile, position); return token && token.kind === SyntaxKind.StringLiteral && position > token.getStart(); @@ -4532,7 +4598,7 @@ module ts { } } - function getReferencesForSuperKeyword(superKeyword: Node): ReferenceEntry[] { + function getReferencesForSuperKeyword(superKeyword: Node): ReferencedSymbol[] { let searchSpaceNode = getSuperContainer(superKeyword, /*includeFunctions*/ false); if (!searchSpaceNode) { return undefined; @@ -4555,7 +4621,7 @@ module ts { return undefined; } - let result: ReferenceEntry[] = []; + let references: ReferenceEntry[] = []; let sourceFile = searchSpaceNode.getSourceFile(); let possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "super", searchSpaceNode.getStart(), searchSpaceNode.getEnd()); @@ -4574,14 +4640,16 @@ module ts { // Now make sure the owning class is the same as the search-space // and has the same static qualifier as the original 'super's owner. if (container && (NodeFlags.Static & container.flags) === staticFlag && container.parent.symbol === searchSpaceNode.symbol) { - result.push(getReferenceEntryFromNode(node)); + references.push(getReferenceEntryFromNode(node)); } }); - return result; + var definitions = getDefinitionInfos(superKeyword, searchSpaceNode.symbol); + var definition = definitions[0]; + return [{ definition, references }]; } - function getReferencesForThisKeyword(thisOrSuperKeyword: Node, sourceFiles: SourceFile[]): ReferenceEntry[] { + function getReferencesForThisKeyword(thisOrSuperKeyword: Node, sourceFiles: SourceFile[]): ReferencedSymbol[] { let searchSpaceNode = getThisContainer(thisOrSuperKeyword, /* includeArrowFunctions */ false); // Whether 'this' occurs in a static context within a class. @@ -4616,22 +4684,32 @@ module ts { return undefined; } - let result: ReferenceEntry[] = []; + let references: ReferenceEntry[] = []; let possiblePositions: number[]; if (searchSpaceNode.kind === SyntaxKind.SourceFile) { forEach(sourceFiles, sourceFile => { possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", sourceFile.getStart(), sourceFile.getEnd()); - getThisReferencesInFile(sourceFile, sourceFile, possiblePositions, result); + getThisReferencesInFile(sourceFile, sourceFile, possiblePositions, references); }); } else { let sourceFile = searchSpaceNode.getSourceFile(); possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", searchSpaceNode.getStart(), searchSpaceNode.getEnd()); - getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, result); + getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, references); } - return result; + return [{ + definition: { + containerKind: "", + containerName: "", + fileName: node.getSourceFile().fileName, + kind: ScriptElementKind.variableElement, + name: "this", + textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()) + }, + references: references + }]; function getThisReferencesInFile(sourceFile: SourceFile, searchSpaceNode: Node, possiblePositions: number[], result: ReferenceEntry[]): void { forEach(possiblePositions, position => { @@ -4754,16 +4832,18 @@ module ts { } } - function isRelatableToSearchSet(searchSymbols: Symbol[], referenceSymbol: Symbol, referenceLocation: Node): boolean { + function getRelatedSymbol(searchSymbols: Symbol[], referenceSymbol: Symbol, referenceLocation: Node): Symbol { if (searchSymbols.indexOf(referenceSymbol) >= 0) { - return true; + return referenceSymbol; } // If the reference symbol is an alias, check if what it is aliasing is one of the search // symbols. - if (isImportOrExportSpecifierImportSymbol(referenceSymbol) && - searchSymbols.indexOf(typeInfoResolver.getAliasedSymbol(referenceSymbol)) >= 0) { - return true; + if (isImportOrExportSpecifierImportSymbol(referenceSymbol)) { + var aliasedSymbol = typeInfoResolver.getAliasedSymbol(referenceSymbol); + if (searchSymbols.indexOf(aliasedSymbol) >= 0) { + return aliasedSymbol; + } } // If the reference location is in an object literal, try to get the contextual type for the @@ -4771,7 +4851,7 @@ module ts { // compare to our searchSymbol if (isNameOfPropertyAssignment(referenceLocation)) { return forEach(getPropertySymbolsFromContextualType(referenceLocation), contextualSymbol => { - return forEach(typeInfoResolver.getRootSymbols(contextualSymbol), s => searchSymbols.indexOf(s) >= 0); + return forEach(typeInfoResolver.getRootSymbols(contextualSymbol), s => searchSymbols.indexOf(s) >= 0 ? s : undefined); }); } @@ -4780,7 +4860,7 @@ module ts { return forEach(typeInfoResolver.getRootSymbols(referenceSymbol), rootSymbol => { // if it is in the list, then we are done if (searchSymbols.indexOf(rootSymbol) >= 0) { - return true; + return rootSymbol; } // Finally, try all properties with the same name in any type the containing type extended or implemented, and @@ -4788,10 +4868,10 @@ module ts { if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { let result: Symbol[] = []; getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result); - return forEach(result, s => searchSymbols.indexOf(s) >= 0); + return forEach(result, s => searchSymbols.indexOf(s) >= 0 ? s : undefined); } - return false; + return undefined; }); } @@ -5747,6 +5827,7 @@ module ts { getQuickInfoAtPosition, getDefinitionAtPosition, getReferencesAtPosition, + findReferences, getOccurrencesAtPosition, getNameOrDottedNameSpan, getBreakpointStatementAtPosition, diff --git a/src/services/shims.ts b/src/services/shims.ts index 63e3c9e23ae..adcec5ada2b 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -127,6 +127,12 @@ module ts { * { fileName: string; textSpan: { start: number; length: number}; isWriteAccess: boolean }[] */ getReferencesAtPosition(fileName: string, position: number): string; + + /** + * Returns a JSON-encoded value of the type: + * { definition: ; references: [] }[] + */ + findReferences(fileName: string, position: number): string; /** * Returns a JSON-encoded value of the type: @@ -555,11 +561,6 @@ module ts { /// GET REFERENCES - /** - * Return references to a symbol at the requested position. - * References are separated by "\n". - * Each reference is a "fileindex min lim" sub-string. - */ public getReferencesAtPosition(fileName: string, position: number): string { return this.forwardJSONCall( "getReferencesAtPosition('" + fileName + "', " + position + ")", @@ -568,6 +569,14 @@ module ts { }); } + public findReferences(fileName: string, position: number): string { + return this.forwardJSONCall( + "findReferences('" + fileName + "', " + position + ")", + () => { + return this.languageService.findReferences(fileName, position); + }); + } + public getOccurrencesAtPosition(fileName: string, position: number): string { return this.forwardJSONCall( "getOccurrencesAtPosition('" + fileName + "', " + position + ")", diff --git a/tests/baselines/reference/APISample_compile.js b/tests/baselines/reference/APISample_compile.js index b8f4c7cdd13..0fd72e80158 100644 --- a/tests/baselines/reference/APISample_compile.js +++ b/tests/baselines/reference/APISample_compile.js @@ -1589,6 +1589,7 @@ declare module "typescript" { getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[]; getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[]; + findReferences(fileName: string, position: number): ReferencedSymbol[]; getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; getNavigationBarItems(fileName: string): NavigationBarItem[]; getOutliningSpans(fileName: string): OutliningSpan[]; @@ -1675,6 +1676,10 @@ declare module "typescript" { containerKind: string; containerName: string; } + interface ReferencedSymbol { + definition: DefinitionInfo; + references: ReferenceEntry[]; + } enum SymbolDisplayPartKind { aliasName = 0, className = 1, diff --git a/tests/baselines/reference/APISample_compile.types b/tests/baselines/reference/APISample_compile.types index e1d9aa58961..28763a896dd 100644 --- a/tests/baselines/reference/APISample_compile.types +++ b/tests/baselines/reference/APISample_compile.types @@ -5154,6 +5154,12 @@ declare module "typescript" { >position : number >ReferenceEntry : ReferenceEntry + findReferences(fileName: string, position: number): ReferencedSymbol[]; +>findReferences : (fileName: string, position: number) => ReferencedSymbol[] +>fileName : string +>position : number +>ReferencedSymbol : ReferencedSymbol + getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; >getNavigateToItems : (searchValue: string, maxResultCount?: number) => NavigateToItem[] >searchValue : string @@ -5424,6 +5430,17 @@ declare module "typescript" { containerName: string; >containerName : string + } + interface ReferencedSymbol { +>ReferencedSymbol : ReferencedSymbol + + definition: DefinitionInfo; +>definition : DefinitionInfo +>DefinitionInfo : DefinitionInfo + + references: ReferenceEntry[]; +>references : ReferenceEntry[] +>ReferenceEntry : ReferenceEntry } enum SymbolDisplayPartKind { >SymbolDisplayPartKind : SymbolDisplayPartKind diff --git a/tests/baselines/reference/APISample_linter.js b/tests/baselines/reference/APISample_linter.js index 4e3af8d1be0..a947821455d 100644 --- a/tests/baselines/reference/APISample_linter.js +++ b/tests/baselines/reference/APISample_linter.js @@ -1620,6 +1620,7 @@ declare module "typescript" { getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[]; getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[]; + findReferences(fileName: string, position: number): ReferencedSymbol[]; getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; getNavigationBarItems(fileName: string): NavigationBarItem[]; getOutliningSpans(fileName: string): OutliningSpan[]; @@ -1706,6 +1707,10 @@ declare module "typescript" { containerKind: string; containerName: string; } + interface ReferencedSymbol { + definition: DefinitionInfo; + references: ReferenceEntry[]; + } enum SymbolDisplayPartKind { aliasName = 0, className = 1, diff --git a/tests/baselines/reference/APISample_linter.types b/tests/baselines/reference/APISample_linter.types index 7cf542f0b40..34dc65aa351 100644 --- a/tests/baselines/reference/APISample_linter.types +++ b/tests/baselines/reference/APISample_linter.types @@ -5300,6 +5300,12 @@ declare module "typescript" { >position : number >ReferenceEntry : ReferenceEntry + findReferences(fileName: string, position: number): ReferencedSymbol[]; +>findReferences : (fileName: string, position: number) => ReferencedSymbol[] +>fileName : string +>position : number +>ReferencedSymbol : ReferencedSymbol + getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; >getNavigateToItems : (searchValue: string, maxResultCount?: number) => NavigateToItem[] >searchValue : string @@ -5570,6 +5576,17 @@ declare module "typescript" { containerName: string; >containerName : string + } + interface ReferencedSymbol { +>ReferencedSymbol : ReferencedSymbol + + definition: DefinitionInfo; +>definition : DefinitionInfo +>DefinitionInfo : DefinitionInfo + + references: ReferenceEntry[]; +>references : ReferenceEntry[] +>ReferenceEntry : ReferenceEntry } enum SymbolDisplayPartKind { >SymbolDisplayPartKind : SymbolDisplayPartKind diff --git a/tests/baselines/reference/APISample_transform.js b/tests/baselines/reference/APISample_transform.js index 261ad5b25f6..00c2c63ac10 100644 --- a/tests/baselines/reference/APISample_transform.js +++ b/tests/baselines/reference/APISample_transform.js @@ -1621,6 +1621,7 @@ declare module "typescript" { getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[]; getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[]; + findReferences(fileName: string, position: number): ReferencedSymbol[]; getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; getNavigationBarItems(fileName: string): NavigationBarItem[]; getOutliningSpans(fileName: string): OutliningSpan[]; @@ -1707,6 +1708,10 @@ declare module "typescript" { containerKind: string; containerName: string; } + interface ReferencedSymbol { + definition: DefinitionInfo; + references: ReferenceEntry[]; + } enum SymbolDisplayPartKind { aliasName = 0, className = 1, diff --git a/tests/baselines/reference/APISample_transform.types b/tests/baselines/reference/APISample_transform.types index eb4c7ab122f..c5b769888db 100644 --- a/tests/baselines/reference/APISample_transform.types +++ b/tests/baselines/reference/APISample_transform.types @@ -5250,6 +5250,12 @@ declare module "typescript" { >position : number >ReferenceEntry : ReferenceEntry + findReferences(fileName: string, position: number): ReferencedSymbol[]; +>findReferences : (fileName: string, position: number) => ReferencedSymbol[] +>fileName : string +>position : number +>ReferencedSymbol : ReferencedSymbol + getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; >getNavigateToItems : (searchValue: string, maxResultCount?: number) => NavigateToItem[] >searchValue : string @@ -5520,6 +5526,17 @@ declare module "typescript" { containerName: string; >containerName : string + } + interface ReferencedSymbol { +>ReferencedSymbol : ReferencedSymbol + + definition: DefinitionInfo; +>definition : DefinitionInfo +>DefinitionInfo : DefinitionInfo + + references: ReferenceEntry[]; +>references : ReferenceEntry[] +>ReferenceEntry : ReferenceEntry } enum SymbolDisplayPartKind { >SymbolDisplayPartKind : SymbolDisplayPartKind diff --git a/tests/baselines/reference/APISample_watcher.js b/tests/baselines/reference/APISample_watcher.js index a821bc5011d..91039f112b7 100644 --- a/tests/baselines/reference/APISample_watcher.js +++ b/tests/baselines/reference/APISample_watcher.js @@ -1658,6 +1658,7 @@ declare module "typescript" { getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[]; getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[]; + findReferences(fileName: string, position: number): ReferencedSymbol[]; getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; getNavigationBarItems(fileName: string): NavigationBarItem[]; getOutliningSpans(fileName: string): OutliningSpan[]; @@ -1744,6 +1745,10 @@ declare module "typescript" { containerKind: string; containerName: string; } + interface ReferencedSymbol { + definition: DefinitionInfo; + references: ReferenceEntry[]; + } enum SymbolDisplayPartKind { aliasName = 0, className = 1, diff --git a/tests/baselines/reference/APISample_watcher.types b/tests/baselines/reference/APISample_watcher.types index 47e5a0147fd..09322aaa771 100644 --- a/tests/baselines/reference/APISample_watcher.types +++ b/tests/baselines/reference/APISample_watcher.types @@ -5423,6 +5423,12 @@ declare module "typescript" { >position : number >ReferenceEntry : ReferenceEntry + findReferences(fileName: string, position: number): ReferencedSymbol[]; +>findReferences : (fileName: string, position: number) => ReferencedSymbol[] +>fileName : string +>position : number +>ReferencedSymbol : ReferencedSymbol + getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; >getNavigateToItems : (searchValue: string, maxResultCount?: number) => NavigateToItem[] >searchValue : string @@ -5693,6 +5699,17 @@ declare module "typescript" { containerName: string; >containerName : string + } + interface ReferencedSymbol { +>ReferencedSymbol : ReferencedSymbol + + definition: DefinitionInfo; +>definition : DefinitionInfo +>DefinitionInfo : DefinitionInfo + + references: ReferenceEntry[]; +>references : ReferenceEntry[] +>ReferenceEntry : ReferenceEntry } enum SymbolDisplayPartKind { >SymbolDisplayPartKind : SymbolDisplayPartKind diff --git a/tests/cases/fourslash/findAllRefsInsideWithBlock.ts b/tests/cases/fourslash/findAllRefsInsideWithBlock.ts index 394d30d89d3..fc8a94eaa2d 100644 --- a/tests/cases/fourslash/findAllRefsInsideWithBlock.ts +++ b/tests/cases/fourslash/findAllRefsInsideWithBlock.ts @@ -13,4 +13,4 @@ goTo.marker('1'); verify.referencesCountIs(3); goTo.marker('2'); -verify.referencesCountIs(1); \ No newline at end of file +verify.referencesCountIs(0); \ No newline at end of file diff --git a/tests/cases/fourslash/getOccurrencesOfUndefinedSymbol.ts b/tests/cases/fourslash/getOccurrencesOfUndefinedSymbol.ts index 4893a6079d1..96c3cdc2e39 100644 --- a/tests/cases/fourslash/getOccurrencesOfUndefinedSymbol.ts +++ b/tests/cases/fourslash/getOccurrencesOfUndefinedSymbol.ts @@ -17,4 +17,4 @@ // "any" should not be highlighted goTo.marker(); -verify.occurrencesAtPositionCount(1); +verify.occurrencesAtPositionCount(0); diff --git a/tests/cases/fourslash/getOccurrencesSwitchCaseDefaultBroken.ts b/tests/cases/fourslash/getOccurrencesSwitchCaseDefaultBroken.ts index 018280a4032..25ee54f8115 100644 --- a/tests/cases/fourslash/getOccurrencesSwitchCaseDefaultBroken.ts +++ b/tests/cases/fourslash/getOccurrencesSwitchCaseDefaultBroken.ts @@ -39,8 +39,6 @@ for (var i = 1; i <= test.markers().length; i++) { verify.occurrencesAtPositionCount(8); break; case 4: - case 5: - case 8: verify.occurrencesAtPositionCount(1); break; case 6: @@ -48,6 +46,8 @@ for (var i = 1; i <= test.markers().length; i++) { case 9: verify.occurrencesAtPositionCount(8); break; + case 5: + case 8: case 10: case 11: case 12: diff --git a/tests/cases/fourslash/getOccurrencesThisNegatives2.ts b/tests/cases/fourslash/getOccurrencesThisNegatives2.ts index 65fb37d09b4..0a8b5860feb 100644 --- a/tests/cases/fourslash/getOccurrencesThisNegatives2.ts +++ b/tests/cases/fourslash/getOccurrencesThisNegatives2.ts @@ -143,5 +143,5 @@ test.markers().forEach(m => { goTo.position(m.position, m.fileName) - verify.occurrencesAtPositionCount(1); + verify.occurrencesAtPositionCount(0); }); diff --git a/tests/cases/fourslash/getOccurrencesTryCatchFinallyBroken.ts b/tests/cases/fourslash/getOccurrencesTryCatchFinallyBroken.ts index c866ed96398..01a202a677c 100644 --- a/tests/cases/fourslash/getOccurrencesTryCatchFinallyBroken.ts +++ b/tests/cases/fourslash/getOccurrencesTryCatchFinallyBroken.ts @@ -36,6 +36,8 @@ for (var i = 1; i <= test.markers().length; i++) { switch (i) { case 1: + verify.occurrencesAtPositionCount(0); + break; case 2: case 3: verify.occurrencesAtPositionCount(1); diff --git a/tests/cases/fourslash/localGetReferences.ts b/tests/cases/fourslash/localGetReferences.ts index 6d5f541dd51..e5873efeeb4 100644 --- a/tests/cases/fourslash/localGetReferences.ts +++ b/tests/cases/fourslash/localGetReferences.ts @@ -223,7 +223,7 @@ verify.referencesCountIs(7); // References to unresolved symbol. goTo.marker("12"); -verify.referencesCountIs(1); +verify.referencesCountIs(0); // References to no context. goTo.marker("13"); diff --git a/tests/cases/fourslash/referencesForIllegalAssignment.ts b/tests/cases/fourslash/referencesForIllegalAssignment.ts index 81a45f64153..403bf0432a7 100644 --- a/tests/cases/fourslash/referencesForIllegalAssignment.ts +++ b/tests/cases/fourslash/referencesForIllegalAssignment.ts @@ -6,10 +6,10 @@ ////ba/*4*/r = b/*5*/ar + 1; goTo.marker("1"); -verify.referencesCountIs(1); +verify.referencesCountIs(0); goTo.marker("2"); -verify.referencesCountIs(1); +verify.referencesCountIs(0); goTo.marker("3"); verify.referencesCountIs(3); diff --git a/tests/cases/fourslash/referencesForIndexProperty2.ts b/tests/cases/fourslash/referencesForIndexProperty2.ts index fd9cf0e7183..9dcba3163c6 100644 --- a/tests/cases/fourslash/referencesForIndexProperty2.ts +++ b/tests/cases/fourslash/referencesForIndexProperty2.ts @@ -6,4 +6,4 @@ ////a[/*1*/"blah"]; goTo.marker("1"); -verify.referencesCountIs(1); \ No newline at end of file +verify.referencesCountIs(0); \ No newline at end of file diff --git a/tests/cases/fourslash/referencesForLabel2.ts b/tests/cases/fourslash/referencesForLabel2.ts index 054ce562864..ccb441d60b9 100644 --- a/tests/cases/fourslash/referencesForLabel2.ts +++ b/tests/cases/fourslash/referencesForLabel2.ts @@ -9,4 +9,4 @@ ////} goTo.marker("1"); -verify.referencesCountIs(1); +verify.referencesCountIs(0); From 9412a6d3eb427fafa1456493de760bf10e140a78 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 20 Mar 2015 17:16:16 -0700 Subject: [PATCH 2/9] Improve display of symbol definitions. --- src/services/services.ts | 135 ++++++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 57 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 2918e2910fb..566c2c9b955 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3455,7 +3455,49 @@ module ts { }; } - function getDefinitionInfos(node: Node, symbol: Symbol): DefinitionInfo[] { + /// Goto definition + function getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] { + synchronizeHostData(); + + let sourceFile = getValidSourceFile(fileName); + + let node = getTouchingPropertyName(sourceFile, position); + if (!node) { + return undefined; + } + + // Labels + if (isJumpStatementTarget(node)) { + let labelName = (node).text; + let label = getTargetLabel((node.parent), (node).text); + return label ? [createDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined; + } + + /// Triple slash reference comments + let comment = forEach(sourceFile.referencedFiles, r => (r.pos <= position && position < r.end) ? r : undefined); + if (comment) { + let referenceFile = tryResolveScriptReference(program, sourceFile, comment); + if (referenceFile) { + return [{ + fileName: referenceFile.fileName, + textSpan: createTextSpanFromBounds(0, 0), + kind: ScriptElementKind.scriptElement, + name: comment.fileName, + containerName: undefined, + containerKind: undefined + }]; + } + return undefined; + } + + let symbol = typeInfoResolver.getSymbolAtLocation(node); + + // Could not find a symbol e.g. node is string or number keyword, + // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol + if (!symbol) { + return undefined; + } + // If this is an alias, and the request came at the declaration location // get the aliased symbol instead. This allows for goto def on an import e.g. // import {A, B} from "mod"; @@ -3549,52 +3591,6 @@ module ts { } } - /// Goto definition - function getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] { - synchronizeHostData(); - - let sourceFile = getValidSourceFile(fileName); - - let node = getTouchingPropertyName(sourceFile, position); - if (!node) { - return undefined; - } - - // Labels - if (isJumpStatementTarget(node)) { - let labelName = (node).text; - let label = getTargetLabel((node.parent), (node).text); - return label ? [createDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined; - } - - /// Triple slash reference comments - let comment = forEach(sourceFile.referencedFiles, r => (r.pos <= position && position < r.end) ? r : undefined); - if (comment) { - let referenceFile = tryResolveScriptReference(program, sourceFile, comment); - if (referenceFile) { - return [{ - fileName: referenceFile.fileName, - textSpan: createTextSpanFromBounds(0, 0), - kind: ScriptElementKind.scriptElement, - name: comment.fileName, - containerName: undefined, - containerKind: undefined - }]; - } - return undefined; - } - - let symbol = typeInfoResolver.getSymbolAtLocation(node); - - // Could not find a symbol e.g. node is string or number keyword, - // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol - if (!symbol) { - return undefined; - } - - return getDefinitionInfos(node, symbol); - } - /// References and Occurrences function getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[] { synchronizeHostData(); @@ -4149,20 +4145,23 @@ module ts { } function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[]{ - var referenceSymbols = findReferencesWorker(fileName, position, findInStrings, findInComments); - return convertReferences(referenceSymbols); + var referencedSymbols = findReferencedSymbols(fileName, position, findInStrings, findInComments); + return convertReferences(referencedSymbols); } function getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] { - var referenceSymbols = findReferencesWorker(fileName, position, /*findInStrings:*/ false, /*findInComments:*/ false); - return convertReferences(referenceSymbols); + var referencedSymbols = findReferencedSymbols(fileName, position, /*findInStrings:*/ false, /*findInComments:*/ false); + return convertReferences(referencedSymbols); } - function findReferences(fileName: string, position: number): ReferencedSymbol[] { - return filter(findReferencesWorker(fileName, position, /*findInStrings:*/ false, /*findInComments:*/ false), rs => !!rs.definition); + function findReferences(fileName: string, position: number): ReferencedSymbol[]{ + var referencedSymbols = findReferencedSymbols(fileName, position, /*findInStrings:*/ false, /*findInComments:*/ false); + + // Only include referenced symbols that have a valid definition. + return filter(referencedSymbols, rs => !!rs.definition); } - function findReferencesWorker(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ReferencedSymbol[] { + function findReferencedSymbols(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ReferencedSymbol[] { synchronizeHostData(); let sourceFile = getValidSourceFile(fileName); @@ -4265,6 +4264,24 @@ module ts { return result; + function getDefinition(symbol: Symbol): DefinitionInfo { + let info = getSymbolDisplayPartsDocumentationAndSymbolKind(symbol, node.getSourceFile(), getContainerNode(node), typeInfoResolver, node); + let name = map(info.displayParts, p => p.text).join(""); + let declarations = symbol.declarations; + if (!declarations || declarations.length === 0) { + return undefined; + } + + return { + containerKind: "", + containerName: "", + name, + kind: info.symbolKind, + fileName: declarations[0].getSourceFile().fileName, + textSpan: createTextSpan(declarations[0].getStart(), 0) + }; + } + function isImportOrExportSpecifierName(location: Node): boolean { return location.parent && (location.parent.kind === SyntaxKind.ImportSpecifier || location.parent.kind === SyntaxKind.ExportSpecifier) && @@ -4513,6 +4530,11 @@ module ts { // for. if ((findInStrings && isInString(position)) || (findInComments && isInComment(position))) { + + // In the case where we're looking inside comments/strings, we don't have + // an actual definition. So just use 'undefined' here. Features like + // 'Rename' won't care (as they ignore the definitions), and features like + // 'FindReferences' will just filter out these results. result.push({ definition: undefined, references: [{ @@ -4563,7 +4585,7 @@ module ts { symbolToIndex[symbolId] = index; result.push({ - definition: getDefinitionInfos(node, symbol)[0], + definition: getDefinition(symbol), references: [] }); } @@ -4644,8 +4666,7 @@ module ts { } }); - var definitions = getDefinitionInfos(superKeyword, searchSpaceNode.symbol); - var definition = definitions[0]; + var definition = getDefinition(searchSpaceNode.symbol); return [{ definition, references }]; } From 8798235c68dc6a05af6c13135db6724bb9dbc554 Mon Sep 17 00:00:00 2001 From: steveluc Date: Fri, 20 Mar 2015 21:56:20 -0700 Subject: [PATCH 3/9] Added support in the server for tsconfig.json. When a host opens a file F, the server will check whether F is configured by a tsconfig.json file. If so, the project specified by the tsconfig.json file becomes the LS context for F. --- src/server/editorServices.ts | 277 ++++++++++++++++++++++++----------- src/server/session.ts | 2 +- src/server/tsconfig.json | 17 +++ 3 files changed, 211 insertions(+), 85 deletions(-) create mode 100644 src/server/tsconfig.json diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 1f9df9b46f0..bcb9fb24f3f 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -272,14 +272,29 @@ module ts.server { export class Project { compilerService: CompilerService; - projectOptions: ProjectOptions; projectFilename: string; program: ts.Program; filenameToSourceFile: ts.Map = {}; updateGraphSeq = 0; + /** Used for configured projects which may have multiple open roots */ + openRefCount = 0; - constructor(public projectService: ProjectService) { - this.compilerService = new CompilerService(this); + constructor(public projectService: ProjectService, public projectOptions?: ProjectOptions) { + if (projectOptions && projectOptions.compilerOptions) { + this.compilerService = new CompilerService(this,projectOptions.compilerOptions); + } + else { + this.compilerService = new CompilerService(this); + } + } + + addOpenRef() { + this.openRefCount++; + } + + deleteOpenRef() { + this.openRefCount--; + return this.openRefCount; } openReferencedFile(filename: string) { @@ -299,6 +314,10 @@ module ts.server { } } + isRoot(info: ScriptInfo) { + return this.compilerService.host.roots.some(root => root === info); + } + removeReferencedFile(info: ScriptInfo) { this.compilerService.host.removeReferencedFile(info); this.updateGraph(); @@ -375,11 +394,16 @@ module ts.server { export class ProjectService { filenameToScriptInfo: ts.Map = {}; - // open, non-configured files in two lists + // open, non-configured root files openFileRoots: ScriptInfo[] = []; - openFilesReferenced: ScriptInfo[] = []; - // projects covering open files + // projects built from openFileRoots inferredProjects: Project[] = []; + // projects specified by a tsconfig.json file + configuredProjects: Project[] = []; + // open files referenced by a project + openFilesReferenced: ScriptInfo[] = []; + // open files that are roots of a configured project + openFileRootsConfigured: ScriptInfo[] = []; hostConfiguration: HostConfiguration; constructor(public host: ServerHost, public psLogger: Logger, public eventHandler?: ProjectServiceEventHandler) { @@ -484,44 +508,66 @@ module ts.server { this.printProjects(); } + setConfiguredProjectRoot(info: ScriptInfo) { + for (var i = 0, len = this.configuredProjects.length; i < len; i++) { + let configuredProject = this.configuredProjects[i]; + if (configuredProject.isRoot(info)) { + info.defaultProject = configuredProject; + configuredProject.addOpenRef(); + return true; + } + } + return false; + } + addOpenFile(info: ScriptInfo) { - this.findReferencingProjects(info); - if (info.defaultProject) { - this.openFilesReferenced.push(info); + if (this.setConfiguredProjectRoot(info)) { + this.openFileRootsConfigured.push(info); } else { - // create new inferred project p with the newly opened file as root - info.defaultProject = this.createInferredProject(info); - var openFileRoots: ScriptInfo[] = []; - // for each inferred project root r - for (var i = 0, len = this.openFileRoots.length; i < len; i++) { - var r = this.openFileRoots[i]; - // if r referenced by the new project - if (info.defaultProject.getSourceFile(r)) { - // remove project rooted at r - this.inferredProjects = - copyListRemovingItem(r.defaultProject, this.inferredProjects); - // put r in referenced open file list - this.openFilesReferenced.push(r); - // set default project of r to the new project - r.defaultProject = info.defaultProject; - } - else { - // otherwise, keep r as root of inferred project - openFileRoots.push(r); - } + this.findReferencingProjects(info); + if (info.defaultProject) { + this.openFilesReferenced.push(info); + } + else { + // create new inferred project p with the newly opened file as root + info.defaultProject = this.createInferredProject(info); + var openFileRoots: ScriptInfo[] = []; + // for each inferred project root r + for (var i = 0, len = this.openFileRoots.length; i < len; i++) { + var r = this.openFileRoots[i]; + // if r referenced by the new project + if (info.defaultProject.getSourceFile(r)) { + // remove project rooted at r + this.inferredProjects = + copyListRemovingItem(r.defaultProject, this.inferredProjects); + // put r in referenced open file list + this.openFilesReferenced.push(r); + // set default project of r to the new project + r.defaultProject = info.defaultProject; + } + else { + // otherwise, keep r as root of inferred project + openFileRoots.push(r); + } + } + this.openFileRoots = openFileRoots; + this.openFileRoots.push(info); } - this.openFileRoots = openFileRoots; - this.openFileRoots.push(info); } } + /** + * Remove this file from the set of open, non-configured files. + * @param info The file that has been closed or newly configured + * @param openedByConfig True if info has become a root of a configured project + */ closeOpenFile(info: ScriptInfo) { var openFileRoots: ScriptInfo[] = []; var removedProject: Project; for (var i = 0, len = this.openFileRoots.length; i < len; i++) { // if closed file is root of project - if (info == this.openFileRoots[i]) { + if (info === this.openFileRoots[i]) { // remove that project and remember it removedProject = info.defaultProject; } @@ -530,9 +576,29 @@ module ts.server { } } this.openFileRoots = openFileRoots; + if (!removedProject) { + var openFileRootsConfigured: ScriptInfo[] = []; + + for (var i = 0, len = this.openFileRootsConfigured.length; i < len; i++) { + if (info === this.openFileRootsConfigured[i]) { + if (info.defaultProject.deleteOpenRef() === 0) { + removedProject = info.defaultProject; + } + } + else { + openFileRootsConfigured.push(this.openFileRootsConfigured[i]); + } + } + + this.openFileRootsConfigured = openFileRootsConfigured; + } if (removedProject) { - // remove project from inferred projects list - this.inferredProjects = copyListRemovingItem(removedProject, this.inferredProjects); + if (removedProject.isConfiguredProject()) { + this.configuredProjects = copyListRemovingItem(removedProject, this.configuredProjects); + } + else { + this.inferredProjects = copyListRemovingItem(removedProject, this.inferredProjects); + } var openFilesReferenced: ScriptInfo[] = []; var orphanFiles: ScriptInfo[] = []; // for all open, referenced files f @@ -564,14 +630,22 @@ module ts.server { var referencingProjects: Project[] = []; info.defaultProject = undefined; for (var i = 0, len = this.inferredProjects.length; i < len; i++) { - this.inferredProjects[i].updateGraph(); - if (this.inferredProjects[i] != excludedProject) { - if (this.inferredProjects[i].getSourceFile(info)) { - info.defaultProject = this.inferredProjects[i]; - referencingProjects.push(this.inferredProjects[i]); + var inferredProject = this.inferredProjects[i]; + inferredProject.updateGraph(); + if (inferredProject != excludedProject) { + if (inferredProject.getSourceFile(info)) { + info.defaultProject = inferredProject; + referencingProjects.push(inferredProject); } } } + for (var i = 0, len = this.configuredProjects.length; i < len; i++) { + var configuredProject = this.configuredProjects[i]; + configuredProject.updateGraph(); + if (configuredProject.getSourceFile(info)) { + info.defaultProject = configuredProject; + } + } return referencingProjects; } @@ -676,9 +750,18 @@ module ts.server { * @param filename is absolute pathname */ - openClientFile(filename: string) { - // TODO: tsconfig check - var info = this.openFile(filename, true); + openClientFile(fileName: string) { + var configFileName = this.findConfigFile(fileName); + if (configFileName && (!this.configProjectIsActive(configFileName))) { + var configResult = this.openConfigFile(configFileName, fileName); + if (!configResult.success) { + this.log("Error opening config file " + configFileName + " " + configResult.errorMsg); + } + else { + this.configuredProjects.push(configResult.project); + } + } + var info = this.openFile(fileName, true); this.addOpenFile(info); this.printProjects(); return info; @@ -699,19 +782,6 @@ module ts.server { this.printProjects(); } - getProjectsReferencingFile(filename: string) { - var scriptInfo = ts.lookUp(this.filenameToScriptInfo, filename); - if (scriptInfo) { - var projects: Project[] = []; - for (var i = 0, len = this.inferredProjects.length; i < len; i++) { - if (this.inferredProjects[i].getSourceFile(scriptInfo)) { - projects.push(this.inferredProjects[i]); - } - } - return projects; - } - } - getProjectForFile(filename: string) { var scriptInfo = ts.lookUp(this.filenameToScriptInfo, filename); if (scriptInfo) { @@ -724,9 +794,9 @@ module ts.server { if (scriptInfo) { this.psLogger.startGroup(); this.psLogger.info("Projects for " + filename) - var projects = this.getProjectsReferencingFile(filename); + var projects = this.findReferencingProjects(scriptInfo); for (var i = 0, len = projects.length; i < len; i++) { - this.psLogger.info("Inferred Project " + i.toString()); + this.psLogger.info("Project " + i.toString()); } this.psLogger.endGroup(); } @@ -744,18 +814,58 @@ module ts.server { this.psLogger.info(project.filesToString()); this.psLogger.info("-----------------------------------------------"); } - this.psLogger.info("Open file roots: ") + for (var i = 0, len = this.configuredProjects.length; i < len; i++) { + var project = this.configuredProjects[i]; + project.updateGraph(); + this.psLogger.info("Project (configured) " + (i+this.inferredProjects.length).toString()); + this.psLogger.info(project.filesToString()); + this.psLogger.info("-----------------------------------------------"); + } + this.psLogger.info("Open file roots of inferred projects: ") for (var i = 0, len = this.openFileRoots.length; i < len; i++) { this.psLogger.info(this.openFileRoots[i].fileName); } - this.psLogger.info("Open files referenced: ") + this.psLogger.info("Open files referenced by inferred or configured projects: ") for (var i = 0, len = this.openFilesReferenced.length; i < len; i++) { - this.psLogger.info(this.openFilesReferenced[i].fileName); + var fileInfo = this.openFilesReferenced[i].fileName; + if (this.openFilesReferenced[i].defaultProject.isConfiguredProject()) { + fileInfo += " (configured)"; + } + this.psLogger.info(fileInfo); + } + this.psLogger.info("Open file roots of configured projects: ") + for (var i = 0, len = this.openFileRootsConfigured.length; i < len; i++) { + this.psLogger.info(this.openFileRootsConfigured[i].fileName); } this.psLogger.endGroup(); } - openConfigFile(configFilename: string): ProjectOpenResult { + configProjectIsActive(fileName: string) { + for (var i = 0, len = this.configuredProjects.length; i < len; i++) { + if (this.configuredProjects[i].projectFilename == fileName) { + return true; + } + } + return false; + } + + findConfigFile(openedFileName: string): string { + var searchPath = getDirectoryPath(openedFileName); + while (true) { + var fileName = searchPath + ts.directorySeparator + "tsconfig.json"; + if (sys.fileExists(fileName)) { + return fileName; + } + var parentPath = getDirectoryPath(searchPath); + if (parentPath === searchPath) { + break; + } + searchPath = parentPath; + } + return undefined; + } + + openConfigFile(configFilename: string, clientFileName?: string): ProjectOpenResult { configFilename = ts.normalizePath(configFilename); // file references will be relative to dirPath (or absolute) var dirPath = ts.getDirectoryPath(configFilename); @@ -764,33 +874,27 @@ module ts.server { return { errorMsg: "tsconfig syntax error" }; } else { - // REVIEW: specify no base path so can get absolute path below - var parsedCommandLine = ts.parseConfigFile(rawConfig); - - if (parsedCommandLine.errors) { - // TODO: gather diagnostics and transmit + var parsedCommandLine = ts.parseConfigFile(rawConfig, dirPath); + if (parsedCommandLine.errors && (parsedCommandLine.errors.length > 0)) { return { errorMsg: "tsconfig option errors" }; } else if (parsedCommandLine.fileNames) { - var proj = this.createProject(configFilename); + var projectOptions: ProjectOptions = { + files: parsedCommandLine.fileNames, + compilerOptions: parsedCommandLine.options + }; + var proj = this.createProject(configFilename, projectOptions); for (var i = 0, len = parsedCommandLine.fileNames.length; i < len; i++) { var rootFilename = parsedCommandLine.fileNames[i]; - var normRootFilename = ts.normalizePath(rootFilename); - normRootFilename = getAbsolutePath(normRootFilename, dirPath); - if (this.host.fileExists(normRootFilename)) { - // TODO: pass true for file exiplicitly opened - var info = this.openFile(normRootFilename, false); + if (ts.sys.fileExists(rootFilename)) { + var info = this.openFile(rootFilename, clientFileName == rootFilename); proj.addRoot(info); } else { return { errorMsg: "specified file " + rootFilename + " not found" }; } } - var projectOptions: ProjectOptions = { - files: parsedCommandLine.fileNames, - compilerOptions: parsedCommandLine.options - }; - proj.setProjectOptions(projectOptions); + proj.finishGraph(); return { success: true, project: proj }; } else { @@ -799,8 +903,8 @@ module ts.server { } } - createProject(projectFilename: string) { - var eproj = new Project(this); + createProject(projectFilename: string, projectOptions?: ProjectOptions) { + var eproj = new Project(this, projectOptions); eproj.projectFilename = projectFilename; return eproj; } @@ -811,20 +915,25 @@ module ts.server { host: LSHost; languageService: ts.LanguageService; classifier: ts.Classifier; - settings = ts.getDefaultCompilerOptions(); + settings: ts.CompilerOptions; documentRegistry = ts.createDocumentRegistry(); - constructor(public project: Project) { + constructor(public project: Project, opt?: ts.CompilerOptions) { this.host = new LSHost(project.projectService.host, project); - // override default ES6 (remove when compiler default back at ES5) - this.settings.target = ts.ScriptTarget.ES5; - this.host.setCompilationSettings(this.settings); + if (opt) { + this.setCompilerOptions(opt); + } + else { + this.setCompilerOptions(ts.getDefaultCompilerOptions()); + } this.languageService = ts.createLanguageService(this.host, this.documentRegistry); this.classifier = ts.createClassifier(); } setCompilerOptions(opt: ts.CompilerOptions) { this.settings = opt; + // override default ES6 (remove when compiler default back at ES5) + this.settings.target = ts.ScriptTarget.ES5; this.host.setCompilationSettings(opt); } diff --git a/src/server/session.ts b/src/server/session.ts index 79eefb879da..91c77a4a769 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -554,7 +554,7 @@ module ts.server { result.push(entry); } return result; - }, []); + }, []).sort((a, b) => a.name.localeCompare(b.name)); } getCompletionEntryDetails(line: number, offset: number, diff --git a/src/server/tsconfig.json b/src/server/tsconfig.json new file mode 100644 index 00000000000..2c8538c61e3 --- /dev/null +++ b/src/server/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "module": "commonjs", + "noImplicitAny": true, + "removeComments": true, + "preserveConstEnums": true, + "out": "../../built/local/tsserver.js", + "sourceMap": true + }, + "files": [ + "node.d.ts", + "editorServices.ts", + "protocol.d.ts", + "server.ts", + "session.ts" + ] +} From ae4f164eb284c7cc51bfe7422dd624f0882b7868 Mon Sep 17 00:00:00 2001 From: steveluc Date: Fri, 20 Mar 2015 22:14:39 -0700 Subject: [PATCH 4/9] Added gc of configured projects to handle case in which file is opened in directory configured by tsconfig.json, but file not part of the configured project. --- src/server/editorServices.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index bcb9fb24f3f..e78e6fa8b78 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -508,6 +508,16 @@ module ts.server { this.printProjects(); } + gcConfiguredProjects() { + var configuredProjects: Project[] = []; + for (var i = 0, len = this.configuredProjects.length; i < len; i++) { + if (this.configuredProjects[i].openRefCount > 0) { + configuredProjects.push(this.configuredProjects[i]); + } + } + this.configuredProjects = configuredProjects; + } + setConfiguredProjectRoot(info: ScriptInfo) { for (var i = 0, len = this.configuredProjects.length; i < len; i++) { let configuredProject = this.configuredProjects[i]; @@ -555,6 +565,7 @@ module ts.server { this.openFileRoots.push(info); } } + this.gcConfiguredProjects(); } /** From 19517ac0ddf8e7ed67ee1328f379071d8e8feb51 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 22 Mar 2015 21:37:03 -0700 Subject: [PATCH 5/9] Emit, even in the presence of declaration errors and noEmitOnError. We're reverting to the previous behavior we had until there is more consensus on the best way to deal with this issue. --- src/compiler/program.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index a4c3a65f29c..f902cb3b13a 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -87,11 +87,6 @@ module ts { export function getPreEmitDiagnostics(program: Program): Diagnostic[] { let diagnostics = program.getSyntacticDiagnostics().concat(program.getGlobalDiagnostics()).concat(program.getSemanticDiagnostics()); - - if (program.getCompilerOptions().declaration) { - diagnostics.concat(program.getDeclarationDiagnostics()); - } - return sortAndDeduplicateDiagnostics(diagnostics); } From af6d1ebe012fabd5d6d4c46f1ea1d93558ab9275 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 23 Mar 2015 16:12:14 -0700 Subject: [PATCH 6/9] Revert "Emit, even in the presence of declaration errors and noEmitOnError." This reverts commit 19517ac0ddf8e7ed67ee1328f379071d8e8feb51. --- src/compiler/program.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index f902cb3b13a..a4c3a65f29c 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -87,6 +87,11 @@ module ts { export function getPreEmitDiagnostics(program: Program): Diagnostic[] { let diagnostics = program.getSyntacticDiagnostics().concat(program.getGlobalDiagnostics()).concat(program.getSemanticDiagnostics()); + + if (program.getCompilerOptions().declaration) { + diagnostics.concat(program.getDeclarationDiagnostics()); + } + return sortAndDeduplicateDiagnostics(diagnostics); } From 7b824bac41d804f858110e462948ec7563a0461a Mon Sep 17 00:00:00 2001 From: steveluc Date: Mon, 23 Mar 2015 17:25:45 -0700 Subject: [PATCH 7/9] Make completion prefix matching case insensitive. --- src/server/session.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/session.ts b/src/server/session.ts index 91c77a4a769..47ef40a8809 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -550,7 +550,7 @@ module ts.server { } return completions.entries.reduce((result: protocol.CompletionEntry[], entry: ts.CompletionEntry) => { - if (completions.isMemberCompletion || entry.name.indexOf(prefix) == 0) { + if (completions.isMemberCompletion || (entry.name.toLowerCase().indexOf(prefix.toLowerCase()) == 0)) { result.push(entry); } return result; From 73881203867cfd8bf3e371b42f2282f0b0554a01 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 23 Mar 2015 19:02:46 -0700 Subject: [PATCH 8/9] Fix issue where getSemanticDiagnostics was returning undefined on a .d.ts file. --- src/services/services.ts | 2 +- .../fourslash/getSemanticDiagnosticForDeclaration1.ts | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/getSemanticDiagnosticForDeclaration1.ts diff --git a/src/services/services.ts b/src/services/services.ts index 5ff7c578aa0..ba8b8c00b7d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2389,7 +2389,7 @@ module ts { // If '-d' is enabled, check for emitter error. One example of emitter error is export class implements non-export interface let declarationDiagnostics = program.getDeclarationDiagnostics(targetSourceFile); - return semanticDiagnostics.concat(declarationDiagnostics); + return concatenate(semanticDiagnostics, declarationDiagnostics); } function getCompilerOptionsDiagnostics() { diff --git a/tests/cases/fourslash/getSemanticDiagnosticForDeclaration1.ts b/tests/cases/fourslash/getSemanticDiagnosticForDeclaration1.ts new file mode 100644 index 00000000000..13a2dccfd26 --- /dev/null +++ b/tests/cases/fourslash/getSemanticDiagnosticForDeclaration1.ts @@ -0,0 +1,9 @@ +/// + +// @declaration: true +// @Filename: File.d.ts +//// declare var v: string; + +verify.numberOfErrorsInCurrentFile(0); + + From 48482071f6af1b7ae929bd3815afaef45e98e3c6 Mon Sep 17 00:00:00 2001 From: steveluc Date: Tue, 24 Mar 2015 14:03:21 -0700 Subject: [PATCH 9/9] Moved findConfigFile to program.ts. Addressed pull request comments. --- src/compiler/program.ts | 16 +++++++ src/compiler/tsc.ts | 20 +-------- src/server/editorServices.ts | 42 ++++++------------- .../baselines/reference/APISample_compile.js | 1 + .../reference/APISample_compile.types | 4 ++ tests/baselines/reference/APISample_linter.js | 1 + .../reference/APISample_linter.types | 4 ++ .../reference/APISample_transform.js | 1 + .../reference/APISample_transform.types | 4 ++ .../baselines/reference/APISample_watcher.js | 1 + .../reference/APISample_watcher.types | 4 ++ 11 files changed, 50 insertions(+), 48 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index a4c3a65f29c..cb12e6da4d5 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -10,6 +10,22 @@ module ts { /** The version of the TypeScript compiler release */ export let version = "1.5.0.0"; + export function findConfigFile(searchPath: string): string { + var fileName = "tsconfig.json"; + while (true) { + if (sys.fileExists(fileName)) { + return fileName; + } + var parentPath = getDirectoryPath(searchPath); + if (parentPath === searchPath) { + break; + } + searchPath = parentPath; + fileName = "../" + fileName; + } + return undefined; + } + export function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost { let currentDirectory: string; let existingDirectories: Map = {}; diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 3f82d6c8cf5..a0d21106f43 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -132,23 +132,6 @@ module ts { return typeof JSON === "object" && typeof JSON.parse === "function"; } - function findConfigFile(): string { - var searchPath = normalizePath(sys.getCurrentDirectory()); - var fileName = "tsconfig.json"; - while (true) { - if (sys.fileExists(fileName)) { - return fileName; - } - var parentPath = getDirectoryPath(searchPath); - if (parentPath === searchPath) { - break; - } - searchPath = parentPath; - fileName = "../" + fileName; - } - return undefined; - } - export function executeCommandLine(args: string[]): void { var commandLine = parseCommandLine(args); var configFileName: string; // Configuration file name (if any) @@ -198,7 +181,8 @@ module ts { } } else if (commandLine.fileNames.length === 0 && isJSONSupported()) { - configFileName = findConfigFile(); + var searchPath = normalizePath(sys.getCurrentDirectory()); + configFileName = findConfigFile(searchPath); } if (commandLine.fileNames.length === 0 && !configFileName) { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index e78e6fa8b78..034549ab464 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -280,12 +280,7 @@ module ts.server { openRefCount = 0; constructor(public projectService: ProjectService, public projectOptions?: ProjectOptions) { - if (projectOptions && projectOptions.compilerOptions) { - this.compilerService = new CompilerService(this,projectOptions.compilerOptions); - } - else { - this.compilerService = new CompilerService(this); - } + this.compilerService = new CompilerService(this,projectOptions && projectOptions.compilerOptions); } addOpenRef() { @@ -508,7 +503,7 @@ module ts.server { this.printProjects(); } - gcConfiguredProjects() { + updateConfiguredProjectList() { var configuredProjects: Project[] = []; for (var i = 0, len = this.configuredProjects.length; i < len; i++) { if (this.configuredProjects[i].openRefCount > 0) { @@ -565,7 +560,7 @@ module ts.server { this.openFileRoots.push(info); } } - this.gcConfiguredProjects(); + this.updateConfiguredProjectList(); } /** @@ -762,13 +757,18 @@ module ts.server { */ openClientFile(fileName: string) { - var configFileName = this.findConfigFile(fileName); + var searchPath = ts.normalizePath(getDirectoryPath(fileName)); + var configFileName = ts.findConfigFile(searchPath); + if (configFileName) { + configFileName = getAbsolutePath(configFileName, searchPath); + } if (configFileName && (!this.configProjectIsActive(configFileName))) { var configResult = this.openConfigFile(configFileName, fileName); if (!configResult.success) { this.log("Error opening config file " + configFileName + " " + configResult.errorMsg); } else { + this.log("Opened configuration file " + configFileName,"Info"); this.configuredProjects.push(configResult.project); } } @@ -859,22 +859,6 @@ module ts.server { } return false; } - - findConfigFile(openedFileName: string): string { - var searchPath = getDirectoryPath(openedFileName); - while (true) { - var fileName = searchPath + ts.directorySeparator + "tsconfig.json"; - if (sys.fileExists(fileName)) { - return fileName; - } - var parentPath = getDirectoryPath(searchPath); - if (parentPath === searchPath) { - break; - } - searchPath = parentPath; - } - return undefined; - } openConfigFile(configFilename: string, clientFileName?: string): ProjectOpenResult { configFilename = ts.normalizePath(configFilename); @@ -915,9 +899,9 @@ module ts.server { } createProject(projectFilename: string, projectOptions?: ProjectOptions) { - var eproj = new Project(this, projectOptions); - eproj.projectFilename = projectFilename; - return eproj; + var project = new Project(this, projectOptions); + project.projectFilename = projectFilename; + return project; } } @@ -943,8 +927,6 @@ module ts.server { setCompilerOptions(opt: ts.CompilerOptions) { this.settings = opt; - // override default ES6 (remove when compiler default back at ES5) - this.settings.target = ts.ScriptTarget.ES5; this.host.setCompilationSettings(opt); } diff --git a/tests/baselines/reference/APISample_compile.js b/tests/baselines/reference/APISample_compile.js index b8f4c7cdd13..88d9b7ff918 100644 --- a/tests/baselines/reference/APISample_compile.js +++ b/tests/baselines/reference/APISample_compile.js @@ -1475,6 +1475,7 @@ declare module "typescript" { declare module "typescript" { /** The version of the TypeScript compiler release */ let version: string; + function findConfigFile(searchPath: string): string; function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost; function getPreEmitDiagnostics(program: Program): Diagnostic[]; function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string; diff --git a/tests/baselines/reference/APISample_compile.types b/tests/baselines/reference/APISample_compile.types index e1d9aa58961..eb4ddd7c1d4 100644 --- a/tests/baselines/reference/APISample_compile.types +++ b/tests/baselines/reference/APISample_compile.types @@ -4736,6 +4736,10 @@ declare module "typescript" { let version: string; >version : string + function findConfigFile(searchPath: string): string; +>findConfigFile : (searchPath: string) => string +>searchPath : string + function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost; >createCompilerHost : (options: CompilerOptions, setParentNodes?: boolean) => CompilerHost >options : CompilerOptions diff --git a/tests/baselines/reference/APISample_linter.js b/tests/baselines/reference/APISample_linter.js index 4e3af8d1be0..d688c083e1b 100644 --- a/tests/baselines/reference/APISample_linter.js +++ b/tests/baselines/reference/APISample_linter.js @@ -1506,6 +1506,7 @@ declare module "typescript" { declare module "typescript" { /** The version of the TypeScript compiler release */ let version: string; + function findConfigFile(searchPath: string): string; function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost; function getPreEmitDiagnostics(program: Program): Diagnostic[]; function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string; diff --git a/tests/baselines/reference/APISample_linter.types b/tests/baselines/reference/APISample_linter.types index 7cf542f0b40..86d14b4fb88 100644 --- a/tests/baselines/reference/APISample_linter.types +++ b/tests/baselines/reference/APISample_linter.types @@ -4882,6 +4882,10 @@ declare module "typescript" { let version: string; >version : string + function findConfigFile(searchPath: string): string; +>findConfigFile : (searchPath: string) => string +>searchPath : string + function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost; >createCompilerHost : (options: CompilerOptions, setParentNodes?: boolean) => CompilerHost >options : CompilerOptions diff --git a/tests/baselines/reference/APISample_transform.js b/tests/baselines/reference/APISample_transform.js index 261ad5b25f6..0d7463e8023 100644 --- a/tests/baselines/reference/APISample_transform.js +++ b/tests/baselines/reference/APISample_transform.js @@ -1507,6 +1507,7 @@ declare module "typescript" { declare module "typescript" { /** The version of the TypeScript compiler release */ let version: string; + function findConfigFile(searchPath: string): string; function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost; function getPreEmitDiagnostics(program: Program): Diagnostic[]; function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string; diff --git a/tests/baselines/reference/APISample_transform.types b/tests/baselines/reference/APISample_transform.types index eb4c7ab122f..5e3911d278e 100644 --- a/tests/baselines/reference/APISample_transform.types +++ b/tests/baselines/reference/APISample_transform.types @@ -4832,6 +4832,10 @@ declare module "typescript" { let version: string; >version : string + function findConfigFile(searchPath: string): string; +>findConfigFile : (searchPath: string) => string +>searchPath : string + function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost; >createCompilerHost : (options: CompilerOptions, setParentNodes?: boolean) => CompilerHost >options : CompilerOptions diff --git a/tests/baselines/reference/APISample_watcher.js b/tests/baselines/reference/APISample_watcher.js index a821bc5011d..d2257c88a42 100644 --- a/tests/baselines/reference/APISample_watcher.js +++ b/tests/baselines/reference/APISample_watcher.js @@ -1544,6 +1544,7 @@ declare module "typescript" { declare module "typescript" { /** The version of the TypeScript compiler release */ let version: string; + function findConfigFile(searchPath: string): string; function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost; function getPreEmitDiagnostics(program: Program): Diagnostic[]; function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string; diff --git a/tests/baselines/reference/APISample_watcher.types b/tests/baselines/reference/APISample_watcher.types index 47e5a0147fd..aafd0df6560 100644 --- a/tests/baselines/reference/APISample_watcher.types +++ b/tests/baselines/reference/APISample_watcher.types @@ -5005,6 +5005,10 @@ declare module "typescript" { let version: string; >version : string + function findConfigFile(searchPath: string): string; +>findConfigFile : (searchPath: string) => string +>searchPath : string + function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost; >createCompilerHost : (options: CompilerOptions, setParentNodes?: boolean) => CompilerHost >options : CompilerOptions