Merge pull request #2682 from Microsoft/GetOccurrencesSingleFile

Ensure that getOccurrences not return items not in the file asked for.
This commit is contained in:
CyrusNajmabadi 2015-04-09 16:19:38 -07:00
commit 97a3e71dc7
4 changed files with 697 additions and 566 deletions

View file

@ -336,6 +336,9 @@ module Harness.LanguageService {
getOccurrencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[] {
return unwrapJSONCallResult(this.shim.getOccurrencesAtPosition(fileName, position));
}
getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): ts.DocumentHighlights[] {
return unwrapJSONCallResult(this.shim.getDocumentHighlights(fileName, position, JSON.stringify(filesToSearch)));
}
getNavigateToItems(searchValue: string): ts.NavigateToItem[] {
return unwrapJSONCallResult(this.shim.getNavigateToItems(searchValue));
}

View file

@ -488,6 +488,10 @@ module ts.server {
});
}
getDocumentHighlights(fileName: string, position: number): DocumentHighlights[] {
throw new Error("Not Implemented Yet.");
}
getOutliningSpans(fileName: string): OutliningSpan[] {
throw new Error("Not Implemented Yet.");
}

View file

@ -944,8 +944,11 @@ 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[];
getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[];
/** @deprecated */
getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[];
getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[];
getNavigationBarItems(fileName: string): NavigationBarItem[];
@ -1011,6 +1014,23 @@ module ts {
isWriteAccess: boolean;
}
export interface DocumentHighlights {
fileName: string;
highlightSpans: HighlightSpan[];
}
export module DocumentHighlightKind {
export const none = "none";
export const definition = "definition";
export const reference = "reference";
export const writtenReference = "writtenReference";
}
export interface HighlightSpan {
textSpan: TextSpan;
kind: string;
}
export interface NavigateToItem {
name: string;
kind: string;
@ -1315,92 +1335,92 @@ module ts {
}
// TODO: move these to enums
export class ScriptElementKind {
static unknown = "";
static warning = "warning";
export module ScriptElementKind {
export const unknown = "";
export const warning = "warning";
// predefined type (void) or keyword (class)
static keyword = "keyword";
export const keyword = "keyword";
// top level script node
static scriptElement = "script";
export const scriptElement = "script";
// module foo {}
static moduleElement = "module";
export const moduleElement = "module";
// class X {}
static classElement = "class";
export const classElement = "class";
// interface Y {}
static interfaceElement = "interface";
export const interfaceElement = "interface";
// type T = ...
static typeElement = "type";
export const typeElement = "type";
// enum E
static enumElement = "enum";
export const enumElement = "enum";
// Inside module and script only
// let v = ..
static variableElement = "var";
export const variableElement = "var";
// Inside function
static localVariableElement = "local var";
export const localVariableElement = "local var";
// Inside module and script only
// function f() { }
static functionElement = "function";
export const functionElement = "function";
// Inside function
static localFunctionElement = "local function";
export const localFunctionElement = "local function";
// class X { [public|private]* foo() {} }
static memberFunctionElement = "method";
export const memberFunctionElement = "method";
// class X { [public|private]* [get|set] foo:number; }
static memberGetAccessorElement = "getter";
static memberSetAccessorElement = "setter";
export const memberGetAccessorElement = "getter";
export const memberSetAccessorElement = "setter";
// class X { [public|private]* foo:number; }
// interface Y { foo:number; }
static memberVariableElement = "property";
export const memberVariableElement = "property";
// class X { constructor() { } }
static constructorImplementationElement = "constructor";
export const constructorImplementationElement = "constructor";
// interface Y { ():number; }
static callSignatureElement = "call";
export const callSignatureElement = "call";
// interface Y { []:number; }
static indexSignatureElement = "index";
export const indexSignatureElement = "index";
// interface Y { new():Y; }
static constructSignatureElement = "construct";
export const constructSignatureElement = "construct";
// function foo(*Y*: string)
static parameterElement = "parameter";
export const parameterElement = "parameter";
static typeParameterElement = "type parameter";
export const typeParameterElement = "type parameter";
static primitiveType = "primitive type";
export const primitiveType = "primitive type";
static label = "label";
export const label = "label";
static alias = "alias";
export const alias = "alias";
static constElement = "const";
export const constElement = "const";
static letElement = "let";
export const letElement = "let";
}
export class ScriptElementKindModifier {
static none = "";
static publicMemberModifier = "public";
static privateMemberModifier = "private";
static protectedMemberModifier = "protected";
static exportedModifier = "export";
static ambientModifier = "declare";
static staticModifier = "static";
export module ScriptElementKindModifier {
export const none = "";
export const publicMemberModifier = "public";
export const privateMemberModifier = "private";
export const protectedMemberModifier = "protected";
export const exportedModifier = "export";
export const ambientModifier = "declare";
export const staticModifier = "static";
}
export class ClassificationTypeNames {
@ -3961,20 +3981,19 @@ module ts {
if (results) {
let sourceFile = getCanonicalFileName(normalizeSlashes(fileName));
// ensure the results are in the file we're interested in
results.forEach((value) => {
let targetFile = getCanonicalFileName(normalizeSlashes(value.fileName));
Debug.assert(sourceFile == targetFile, `Unexpected file in results. Found results in ${targetFile} expected only results in ${sourceFile}.`);
});
// Get occurrences only supports reporting occurrences for the file queried. So
// filter down to that list.
results = filter(results, r => r.fileName === fileName);
}
return results;
}
/// References and Occurrences
function getOccurrencesAtPositionCore(fileName: string, position: number): ReferenceEntry[] {
function getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[] {
synchronizeHostData();
filesToSearch = map(filesToSearch, normalizeSlashes);
let sourceFilesToSearch = filter(program.getSourceFiles(), f => contains(filesToSearch, f.fileName));
let sourceFile = getValidSourceFile(fileName);
let node = getTouchingWord(sourceFile, position);
@ -3982,11 +4001,83 @@ module ts {
return undefined;
}
if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.SuperKeyword ||
isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || isNameOfExternalModuleImportOrDeclaration(node)) {
return convertReferences(getReferencesForNode(node, [sourceFile], /*searchOnlyInCurrentFile*/ true, /*findInStrings:*/ false, /*findInComments:*/ false));
return getSemanticDocumentHighlights(node) || getSyntacticDocumentHighlights(node);
function getHighlightSpanForNode(node: Node): HighlightSpan {
let start = node.getStart();
let end = node.getEnd();
return {
fileName: sourceFile.fileName,
textSpan: createTextSpanFromBounds(start, end),
kind: DocumentHighlightKind.none
};
}
function getSemanticDocumentHighlights(node: Node): DocumentHighlights[] {
if (node.kind === SyntaxKind.Identifier ||
node.kind === SyntaxKind.ThisKeyword ||
node.kind === SyntaxKind.SuperKeyword ||
isLiteralNameOfPropertyDeclarationOrIndexAccess(node) ||
isNameOfExternalModuleImportOrDeclaration(node)) {
let referencedSymbols = getReferencedSymbolsForNodes(node, sourceFilesToSearch, /*findInStrings:*/ false, /*findInComments:*/ false);
return convertReferencedSymbols(referencedSymbols);
}
return undefined;
function convertReferencedSymbols(referencedSymbols: ReferencedSymbol[]): DocumentHighlights[] {
if (!referencedSymbols) {
return undefined;
}
let fileNameToDocumentHighlights: Map<DocumentHighlights> = {};
let result: DocumentHighlights[] = [];
for (let referencedSymbol of referencedSymbols) {
for (let referenceEntry of referencedSymbol.references) {
let fileName = referenceEntry.fileName;
let documentHighlights = getProperty(fileNameToDocumentHighlights, fileName);
if (!documentHighlights) {
documentHighlights = { fileName, highlightSpans: [] };
fileNameToDocumentHighlights[fileName] = documentHighlights;
result.push(documentHighlights);
}
documentHighlights.highlightSpans.push({
textSpan: referenceEntry.textSpan,
kind: referenceEntry.isWriteAccess ? DocumentHighlightKind.writtenReference : DocumentHighlightKind.reference
});
}
}
return result;
}
}
function getSyntacticDocumentHighlights(node: Node): DocumentHighlights[] {
let fileName = sourceFile.fileName;
var highlightSpans = getHighlightSpans(node);
if (!highlightSpans || highlightSpans.length === 0) {
return undefined;
}
return [{ fileName, highlightSpans }];
// returns true if 'node' is defined and has a matching 'kind'.
function hasKind(node: Node, kind: SyntaxKind) {
return node !== undefined && node.kind === kind;
}
// Null-propagating 'parent' function.
function parent(node: Node): Node {
return node && node.parent;
}
function getHighlightSpans(node: Node): HighlightSpan[] {
if (node) {
switch (node.kind) {
case SyntaxKind.IfKeyword:
case SyntaxKind.ElseKeyword:
@ -4061,116 +4152,9 @@ module ts {
return getModifierOccurrences(node.kind, node.parent);
}
}
}
return undefined;
function getIfElseOccurrences(ifStatement: IfStatement): ReferenceEntry[] {
let keywords: Node[] = [];
// Traverse upwards through all parent if-statements linked by their else-branches.
while (hasKind(ifStatement.parent, SyntaxKind.IfStatement) && (<IfStatement>ifStatement.parent).elseStatement === ifStatement) {
ifStatement = <IfStatement>ifStatement.parent;
}
// Now traverse back down through the else branches, aggregating if/else keywords of if-statements.
while (ifStatement) {
let children = ifStatement.getChildren();
pushKeywordIf(keywords, children[0], SyntaxKind.IfKeyword);
// Generally the 'else' keyword is second-to-last, so we traverse backwards.
for (let i = children.length - 1; i >= 0; i--) {
if (pushKeywordIf(keywords, children[i], SyntaxKind.ElseKeyword)) {
break;
}
}
if (!hasKind(ifStatement.elseStatement, SyntaxKind.IfStatement)) {
break
}
ifStatement = <IfStatement>ifStatement.elseStatement;
}
let result: ReferenceEntry[] = [];
// We'd like to highlight else/ifs together if they are only separated by whitespace
// (i.e. the keywords are separated by no comments, no newlines).
for (let i = 0; i < keywords.length; i++) {
if (keywords[i].kind === SyntaxKind.ElseKeyword && i < keywords.length - 1) {
let elseKeyword = keywords[i];
let ifKeyword = keywords[i + 1]; // this *should* always be an 'if' keyword.
let shouldHighlightNextKeyword = true;
// Avoid recalculating getStart() by iterating backwards.
for (let j = ifKeyword.getStart() - 1; j >= elseKeyword.end; j--) {
if (!isWhiteSpace(sourceFile.text.charCodeAt(j))) {
shouldHighlightNextKeyword = false;
break;
}
}
if (shouldHighlightNextKeyword) {
result.push({
fileName: fileName,
textSpan: createTextSpanFromBounds(elseKeyword.getStart(), ifKeyword.end),
isWriteAccess: false
});
i++; // skip the next keyword
continue;
}
}
// Ordinary case: just highlight the keyword.
result.push(getReferenceEntryFromNode(keywords[i]));
}
return result;
}
function getReturnOccurrences(returnStatement: ReturnStatement): ReferenceEntry[] {
let func = <FunctionLikeDeclaration>getContainingFunction(returnStatement);
// If we didn't find a containing function with a block body, bail out.
if (!(func && hasKind(func.body, SyntaxKind.Block))) {
return undefined;
}
let keywords: Node[] = []
forEachReturnStatement(<Block>func.body, returnStatement => {
pushKeywordIf(keywords, returnStatement.getFirstToken(), SyntaxKind.ReturnKeyword);
});
// Include 'throw' statements that do not occur within a try block.
forEach(aggregateOwnedThrowStatements(func.body), throwStatement => {
pushKeywordIf(keywords, throwStatement.getFirstToken(), SyntaxKind.ThrowKeyword);
});
return map(keywords, getReferenceEntryFromNode);
}
function getThrowOccurrences(throwStatement: ThrowStatement) {
let owner = getThrowStatementOwner(throwStatement);
if (!owner) {
return undefined;
}
let keywords: Node[] = [];
forEach(aggregateOwnedThrowStatements(owner), throwStatement => {
pushKeywordIf(keywords, throwStatement.getFirstToken(), SyntaxKind.ThrowKeyword);
});
// If the "owner" is a function, then we equate 'return' and 'throw' statements in their
// ability to "jump out" of the function, and include occurrences for both.
if (isFunctionBlock(owner)) {
forEachReturnStatement(<Block>owner, returnStatement => {
pushKeywordIf(keywords, returnStatement.getFirstToken(), SyntaxKind.ReturnKeyword);
});
}
return map(keywords, getReferenceEntryFromNode);
}
/**
@ -4240,91 +4224,6 @@ module ts {
return undefined;
}
function getTryCatchFinallyOccurrences(tryStatement: TryStatement): ReferenceEntry[] {
let keywords: Node[] = [];
pushKeywordIf(keywords, tryStatement.getFirstToken(), SyntaxKind.TryKeyword);
if (tryStatement.catchClause) {
pushKeywordIf(keywords, tryStatement.catchClause.getFirstToken(), SyntaxKind.CatchKeyword);
}
if (tryStatement.finallyBlock) {
let finallyKeyword = findChildOfKind(tryStatement, SyntaxKind.FinallyKeyword, sourceFile);
pushKeywordIf(keywords, finallyKeyword, SyntaxKind.FinallyKeyword);
}
return map(keywords, getReferenceEntryFromNode);
}
function getLoopBreakContinueOccurrences(loopNode: IterationStatement): ReferenceEntry[] {
let keywords: Node[] = [];
if (pushKeywordIf(keywords, loopNode.getFirstToken(), SyntaxKind.ForKeyword, SyntaxKind.WhileKeyword, SyntaxKind.DoKeyword)) {
// If we succeeded and got a do-while loop, then start looking for a 'while' keyword.
if (loopNode.kind === SyntaxKind.DoStatement) {
let loopTokens = loopNode.getChildren();
for (let i = loopTokens.length - 1; i >= 0; i--) {
if (pushKeywordIf(keywords, loopTokens[i], SyntaxKind.WhileKeyword)) {
break;
}
}
}
}
let breaksAndContinues = aggregateAllBreakAndContinueStatements(loopNode.statement);
forEach(breaksAndContinues, statement => {
if (ownsBreakOrContinueStatement(loopNode, statement)) {
pushKeywordIf(keywords, statement.getFirstToken(), SyntaxKind.BreakKeyword, SyntaxKind.ContinueKeyword);
}
});
return map(keywords, getReferenceEntryFromNode);
}
function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement): ReferenceEntry[] {
let keywords: Node[] = [];
pushKeywordIf(keywords, switchStatement.getFirstToken(), SyntaxKind.SwitchKeyword);
// Go through each clause in the switch statement, collecting the 'case'/'default' keywords.
forEach(switchStatement.caseBlock.clauses, clause => {
pushKeywordIf(keywords, clause.getFirstToken(), SyntaxKind.CaseKeyword, SyntaxKind.DefaultKeyword);
let breaksAndContinues = aggregateAllBreakAndContinueStatements(clause);
forEach(breaksAndContinues, statement => {
if (ownsBreakOrContinueStatement(switchStatement, statement)) {
pushKeywordIf(keywords, statement.getFirstToken(), SyntaxKind.BreakKeyword);
}
});
});
return map(keywords, getReferenceEntryFromNode);
}
function getBreakOrContinueStatementOccurences(breakOrContinueStatement: BreakOrContinueStatement): ReferenceEntry[] {
let owner = getBreakOrContinueOwner(breakOrContinueStatement);
if (owner) {
switch (owner.kind) {
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
return getLoopBreakContinueOccurrences(<IterationStatement>owner)
case SyntaxKind.SwitchStatement:
return getSwitchCaseDefaultOccurrences(<SwitchStatement>owner);
}
}
return undefined;
}
function aggregateAllBreakAndContinueStatements(node: Node): BreakOrContinueStatement[] {
let statementAccumulator: BreakOrContinueStatement[] = []
aggregate(node);
@ -4376,38 +4275,7 @@ module ts {
return undefined;
}
function getConstructorOccurrences(constructorDeclaration: ConstructorDeclaration): ReferenceEntry[] {
let declarations = constructorDeclaration.symbol.getDeclarations()
let keywords: Node[] = [];
forEach(declarations, declaration => {
forEach(declaration.getChildren(), token => {
return pushKeywordIf(keywords, token, SyntaxKind.ConstructorKeyword);
});
});
return map(keywords, getReferenceEntryFromNode);
}
function getGetAndSetOccurrences(accessorDeclaration: AccessorDeclaration): ReferenceEntry[] {
let keywords: Node[] = [];
tryPushAccessorKeyword(accessorDeclaration.symbol, SyntaxKind.GetAccessor);
tryPushAccessorKeyword(accessorDeclaration.symbol, SyntaxKind.SetAccessor);
return map(keywords, getReferenceEntryFromNode);
function tryPushAccessorKeyword(accessorSymbol: Symbol, accessorKind: SyntaxKind): void {
let accessor = getDeclarationOfKind(accessorSymbol, accessorKind);
if (accessor) {
forEach(accessor.getChildren(), child => pushKeywordIf(keywords, child, SyntaxKind.GetKeyword, SyntaxKind.SetKeyword));
}
}
}
function getModifierOccurrences(modifier: SyntaxKind, declaration: Node): ReferenceEntry[] {
function getModifierOccurrences(modifier: SyntaxKind, declaration: Node): HighlightSpan[] {
let container = declaration.parent;
// Make sure we only highlight the keyword when it makes sense to do so.
@ -4470,7 +4338,7 @@ module ts {
}
});
return map(keywords, getReferenceEntryFromNode);
return map(keywords, getHighlightSpanForNode);
function getFlagFromModifier(modifier: SyntaxKind) {
switch (modifier) {
@ -4492,16 +4360,6 @@ module ts {
}
}
// returns true if 'node' is defined and has a matching 'kind'.
function hasKind(node: Node, kind: SyntaxKind) {
return node !== undefined && node.kind === kind;
}
// Null-propagating 'parent' function.
function parent(node: Node): Node {
return node && node.parent;
}
function pushKeywordIf(keywordList: Node[], token: Node, ...expected: SyntaxKind[]): boolean {
if (token && contains(expected, token.kind)) {
keywordList.push(token);
@ -4510,6 +4368,258 @@ module ts {
return false;
}
function getGetAndSetOccurrences(accessorDeclaration: AccessorDeclaration): HighlightSpan[] {
let keywords: Node[] = [];
tryPushAccessorKeyword(accessorDeclaration.symbol, SyntaxKind.GetAccessor);
tryPushAccessorKeyword(accessorDeclaration.symbol, SyntaxKind.SetAccessor);
return map(keywords, getHighlightSpanForNode);
function tryPushAccessorKeyword(accessorSymbol: Symbol, accessorKind: SyntaxKind): void {
let accessor = getDeclarationOfKind(accessorSymbol, accessorKind);
if (accessor) {
forEach(accessor.getChildren(), child => pushKeywordIf(keywords, child, SyntaxKind.GetKeyword, SyntaxKind.SetKeyword));
}
}
}
function getConstructorOccurrences(constructorDeclaration: ConstructorDeclaration): HighlightSpan[] {
let declarations = constructorDeclaration.symbol.getDeclarations()
let keywords: Node[] = [];
forEach(declarations, declaration => {
forEach(declaration.getChildren(), token => {
return pushKeywordIf(keywords, token, SyntaxKind.ConstructorKeyword);
});
});
return map(keywords, getHighlightSpanForNode);
}
function getLoopBreakContinueOccurrences(loopNode: IterationStatement): HighlightSpan[] {
let keywords: Node[] = [];
if (pushKeywordIf(keywords, loopNode.getFirstToken(), SyntaxKind.ForKeyword, SyntaxKind.WhileKeyword, SyntaxKind.DoKeyword)) {
// If we succeeded and got a do-while loop, then start looking for a 'while' keyword.
if (loopNode.kind === SyntaxKind.DoStatement) {
let loopTokens = loopNode.getChildren();
for (let i = loopTokens.length - 1; i >= 0; i--) {
if (pushKeywordIf(keywords, loopTokens[i], SyntaxKind.WhileKeyword)) {
break;
}
}
}
}
let breaksAndContinues = aggregateAllBreakAndContinueStatements(loopNode.statement);
forEach(breaksAndContinues, statement => {
if (ownsBreakOrContinueStatement(loopNode, statement)) {
pushKeywordIf(keywords, statement.getFirstToken(), SyntaxKind.BreakKeyword, SyntaxKind.ContinueKeyword);
}
});
return map(keywords, getHighlightSpanForNode);
}
function getBreakOrContinueStatementOccurences(breakOrContinueStatement: BreakOrContinueStatement): HighlightSpan[] {
let owner = getBreakOrContinueOwner(breakOrContinueStatement);
if (owner) {
switch (owner.kind) {
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
return getLoopBreakContinueOccurrences(<IterationStatement>owner)
case SyntaxKind.SwitchStatement:
return getSwitchCaseDefaultOccurrences(<SwitchStatement>owner);
}
}
return undefined;
}
function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement): HighlightSpan[] {
let keywords: Node[] = [];
pushKeywordIf(keywords, switchStatement.getFirstToken(), SyntaxKind.SwitchKeyword);
// Go through each clause in the switch statement, collecting the 'case'/'default' keywords.
forEach(switchStatement.caseBlock.clauses, clause => {
pushKeywordIf(keywords, clause.getFirstToken(), SyntaxKind.CaseKeyword, SyntaxKind.DefaultKeyword);
let breaksAndContinues = aggregateAllBreakAndContinueStatements(clause);
forEach(breaksAndContinues, statement => {
if (ownsBreakOrContinueStatement(switchStatement, statement)) {
pushKeywordIf(keywords, statement.getFirstToken(), SyntaxKind.BreakKeyword);
}
});
});
return map(keywords, getHighlightSpanForNode);
}
function getTryCatchFinallyOccurrences(tryStatement: TryStatement): HighlightSpan[] {
let keywords: Node[] = [];
pushKeywordIf(keywords, tryStatement.getFirstToken(), SyntaxKind.TryKeyword);
if (tryStatement.catchClause) {
pushKeywordIf(keywords, tryStatement.catchClause.getFirstToken(), SyntaxKind.CatchKeyword);
}
if (tryStatement.finallyBlock) {
let finallyKeyword = findChildOfKind(tryStatement, SyntaxKind.FinallyKeyword, sourceFile);
pushKeywordIf(keywords, finallyKeyword, SyntaxKind.FinallyKeyword);
}
return map(keywords, getHighlightSpanForNode);
}
function getThrowOccurrences(throwStatement: ThrowStatement): HighlightSpan[] {
let owner = getThrowStatementOwner(throwStatement);
if (!owner) {
return undefined;
}
let keywords: Node[] = [];
forEach(aggregateOwnedThrowStatements(owner), throwStatement => {
pushKeywordIf(keywords, throwStatement.getFirstToken(), SyntaxKind.ThrowKeyword);
});
// If the "owner" is a function, then we equate 'return' and 'throw' statements in their
// ability to "jump out" of the function, and include occurrences for both.
if (isFunctionBlock(owner)) {
forEachReturnStatement(<Block>owner, returnStatement => {
pushKeywordIf(keywords, returnStatement.getFirstToken(), SyntaxKind.ReturnKeyword);
});
}
return map(keywords, getHighlightSpanForNode);
}
function getReturnOccurrences(returnStatement: ReturnStatement): HighlightSpan[] {
let func = <FunctionLikeDeclaration>getContainingFunction(returnStatement);
// If we didn't find a containing function with a block body, bail out.
if (!(func && hasKind(func.body, SyntaxKind.Block))) {
return undefined;
}
let keywords: Node[] = []
forEachReturnStatement(<Block>func.body, returnStatement => {
pushKeywordIf(keywords, returnStatement.getFirstToken(), SyntaxKind.ReturnKeyword);
});
// Include 'throw' statements that do not occur within a try block.
forEach(aggregateOwnedThrowStatements(func.body), throwStatement => {
pushKeywordIf(keywords, throwStatement.getFirstToken(), SyntaxKind.ThrowKeyword);
});
return map(keywords, getHighlightSpanForNode);
}
function getIfElseOccurrences(ifStatement: IfStatement): HighlightSpan[] {
let keywords: Node[] = [];
// Traverse upwards through all parent if-statements linked by their else-branches.
while (hasKind(ifStatement.parent, SyntaxKind.IfStatement) && (<IfStatement>ifStatement.parent).elseStatement === ifStatement) {
ifStatement = <IfStatement>ifStatement.parent;
}
// Now traverse back down through the else branches, aggregating if/else keywords of if-statements.
while (ifStatement) {
let children = ifStatement.getChildren();
pushKeywordIf(keywords, children[0], SyntaxKind.IfKeyword);
// Generally the 'else' keyword is second-to-last, so we traverse backwards.
for (let i = children.length - 1; i >= 0; i--) {
if (pushKeywordIf(keywords, children[i], SyntaxKind.ElseKeyword)) {
break;
}
}
if (!hasKind(ifStatement.elseStatement, SyntaxKind.IfStatement)) {
break
}
ifStatement = <IfStatement>ifStatement.elseStatement;
}
let result: HighlightSpan[] = [];
// We'd like to highlight else/ifs together if they are only separated by whitespace
// (i.e. the keywords are separated by no comments, no newlines).
for (let i = 0; i < keywords.length; i++) {
if (keywords[i].kind === SyntaxKind.ElseKeyword && i < keywords.length - 1) {
let elseKeyword = keywords[i];
let ifKeyword = keywords[i + 1]; // this *should* always be an 'if' keyword.
let shouldCombindElseAndIf = true;
// Avoid recalculating getStart() by iterating backwards.
for (let j = ifKeyword.getStart() - 1; j >= elseKeyword.end; j--) {
if (!isWhiteSpace(sourceFile.text.charCodeAt(j))) {
shouldCombindElseAndIf = false;
break;
}
}
if (shouldCombindElseAndIf) {
result.push({
fileName: fileName,
textSpan: createTextSpanFromBounds(elseKeyword.getStart(), ifKeyword.end),
kind: DocumentHighlightKind.reference
});
i++; // skip the next keyword
continue;
}
}
// Ordinary case: just highlight the keyword.
result.push(getHighlightSpanForNode(keywords[i]));
}
return result;
}
}
}
/// References and Occurrences
function getOccurrencesAtPositionCore(fileName: string, position: number): ReferenceEntry[] {
synchronizeHostData();
return convertDocumentHighlights(getDocumentHighlights(fileName, position, [fileName]));
function convertDocumentHighlights(documentHighlights: DocumentHighlights[]): ReferenceEntry[] {
if (!documentHighlights) {
return undefined;
}
let result: ReferenceEntry[] = [];
for (let entry of documentHighlights) {
for (let highlightSpan of entry.highlightSpans) {
result.push({
fileName: entry.fileName,
textSpan: highlightSpan.textSpan,
isWriteAccess: highlightSpan.kind === DocumentHighlightKind.writtenReference
});
}
}
return result;
}
}
function convertReferences(referenceSymbols: ReferencedSymbol[]): ReferenceEntry[] {
@ -4518,9 +4628,11 @@ module ts {
}
let referenceEntries: ReferenceEntry[] = [];
for (let referenceSymbol of referenceSymbols) {
addRange(referenceEntries, referenceSymbol.references);
}
return referenceEntries;
}
@ -4561,10 +4673,10 @@ module ts {
}
Debug.assert(node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.NumericLiteral || node.kind === SyntaxKind.StringLiteral);
return getReferencesForNode(node, program.getSourceFiles(), /*searchOnlyInCurrentFile*/ false, findInStrings, findInComments);
return getReferencedSymbolsForNodes(node, program.getSourceFiles(), findInStrings, findInComments);
}
function getReferencesForNode(node: Node, sourceFiles: SourceFile[], searchOnlyInCurrentFile: boolean, findInStrings: boolean, findInComments: boolean): ReferencedSymbol[]{
function getReferencedSymbolsForNodes(node: Node, sourceFiles: SourceFile[], findInStrings: boolean, findInComments: boolean): ReferencedSymbol[]{
// Labels
if (isLabelName(node)) {
if (isJumpStatementTarget(node)) {
@ -4621,15 +4733,9 @@ module ts {
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, symbolToIndex);
}
else {
let internedName = getInternedName(symbol, node, declarations)
forEach(sourceFiles, sourceFile => {
for (let sourceFile of sourceFiles) {
cancellationToken.throwIfCancellationRequested();
let nameTable = getNameTable(sourceFile);
@ -4638,7 +4744,6 @@ module ts {
result = result || [];
getReferencesInNode(sourceFile, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex);
}
});
}
}
@ -6255,6 +6360,7 @@ module ts {
getReferencesAtPosition,
findReferences,
getOccurrencesAtPosition,
getDocumentHighlights,
getNameOrDottedNameSpan,
getBreakpointStatementAtPosition,
getNavigateToItems,

View file

@ -135,11 +135,21 @@ module ts {
findReferences(fileName: string, position: number): string;
/**
* @deprecated
* Returns a JSON-encoded value of the type:
* { fileName: string; textSpan: { start: number; length: number}; isWriteAccess: boolean }[]
*/
getOccurrencesAtPosition(fileName: string, position: number): string;
/**
* Returns a JSON-encoded value of the type:
* { fileName: string; highlights: { start: number; length: number, isDefinition: boolean }[] }[]
*
* @param fileToSearch A JSON encoded string[] containing the file names that should be
* considered when searching.
*/
getDocumentHighlights(fileName: string, position: number, filesToSearch: string): string;
/**
* Returns a JSON-encoded value of the type:
* { name: string; kind: string; kindModifiers: string; containerName: string; containerKind: string; matchKind: string; fileName: string; textSpan: { start: number; length: number}; } [] = [];
@ -590,6 +600,14 @@ module ts {
});
}
public getDocumentHighlights(fileName: string, position: number, filesToSearch: string): string {
return this.forwardJSONCall(
"getDocumentHighlights('" + fileName + "', " + position + ")",
() => {
return this.languageService.getDocumentHighlights(fileName, position, JSON.parse(filesToSearch));
});
}
/// COMPLETION LISTS
/**