Skip to content

Commit

Permalink
feat: Add Node#getSymbolsInScope(meaning) and `TypeChecker#getSymbo…
Browse files Browse the repository at this point in the history
…lsInScope(node, meaning)`.
  • Loading branch information
dsherret committed May 19, 2019
1 parent b9b0cb0 commit 25ab43d
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 5 deletions.
17 changes: 17 additions & 0 deletions lib/ts-morph.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4236,6 +4236,14 @@ export declare class Node<NodeType extends ts.Node = ts.Node> implements TextRan
* Gets the compiler symbol or undefined if it doesn't exist.
*/
getSymbol(): Symbol | undefined;
/**
* Gets the symbols in the scope of the node.
*
* Note: This will always return the local symbols. If you want the export symbol from a local symbol, then
* use the `#getExportSymbol()` method on the symbol.
* @param meaning - Meaning of symbol to filter by.
*/
getSymbolsInScope(meaning: SymbolFlags): Symbol[];
/**
* Gets the specified local symbol by name or throws if it doesn't exist.
*
Expand Down Expand Up @@ -9932,6 +9940,15 @@ export declare class TypeChecker {
* @param type - Literal type to get the base type of.
*/
getBaseTypeOfLiteralType(type: Type): Type<ts.Type>;
/**
* Gets the symbols in the scope of the provided node.
*
* Note: This will always return the local symbols. If you want the export symbol from a local symbol, then
* use the `#getExportSymbolOfSymbol(symbol)` method.
* @param node - Node to check the scope for.
* @param meaning - Meaning of symbol to filter by.
*/
getSymbolsInScope(node: Node, meaning: SymbolFlags): Symbol[];
}

export declare class Type<TType extends ts.Type = ts.Type> {
Expand Down
13 changes: 12 additions & 1 deletion src/compiler/ast/common/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ProjectContext } from "../../../ProjectContext";
import { getNextMatchingPos, getNextNonWhitespacePos, getPreviousNonWhitespacePos, getPreviousMatchingPos, getTextFromFormattingEdits,
insertIntoParentTextRange, replaceSourceFileTextForFormatting, replaceSourceFileTextStraight } from "../../../manipulation";
import { WriterFunction } from "../../../types";
import { SyntaxKind, ts } from "../../../typescript";
import { ts, SyntaxKind, SymbolFlags } from "../../../typescript";
import { ArrayUtils, getParentSyntaxList, getSyntaxKindName, getTextFromStringOrWriter, isStringKind, printNode, PrintNodeOptions, StringUtils,
TypeGuards, StoredComparer } from "../../../utils";
import { FormatCodeSettings } from "../../tools";
Expand Down Expand Up @@ -256,6 +256,17 @@ export class Node<NodeType extends ts.Node = ts.Node> implements TextRange {
return undefined;
}

/**
* Gets the symbols in the scope of the node.
*
* Note: This will always return the local symbols. If you want the export symbol from a local symbol, then
* use the `#getExportSymbol()` method on the symbol.
* @param meaning - Meaning of symbol to filter by.
*/
getSymbolsInScope(meaning: SymbolFlags): Symbol[] {
return this._context.typeChecker.getSymbolsInScope(this, meaning);
}

/**
* Gets the specified local symbol by name or throws if it doesn't exist.
*
Expand Down
13 changes: 13 additions & 0 deletions src/compiler/tools/TypeChecker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,19 @@ export class TypeChecker {
return this._context.compilerFactory.getType(this.compilerObject.getBaseTypeOfLiteralType(type.compilerType));
}

/**
* Gets the symbols in the scope of the provided node.
*
* Note: This will always return the local symbols. If you want the export symbol from a local symbol, then
* use the `#getExportSymbolOfSymbol(symbol)` method.
* @param node - Node to check the scope for.
* @param meaning - Meaning of symbol to filter by.
*/
getSymbolsInScope(node: Node, meaning: SymbolFlags) {
return this.compilerObject.getSymbolsInScope(node.compilerNode, meaning)
.map(s => this._context.compilerFactory.getSymbol(s));
}

private _getDefaultTypeFormatFlags(enclosingNode?: Node) {
let formatFlags = (TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.NoTruncation | TypeFormatFlags.UseFullyQualifiedType |
TypeFormatFlags.WriteTypeArgumentsOfSignature) as TypeFormatFlags;
Expand Down
18 changes: 17 additions & 1 deletion src/tests/compiler/ast/common/nodeTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { hasParsedTokens } from "../../../../compiler/ast/utils";
import { Project } from "../../../../Project";
import * as errors from "../../../../errors";
import { WriterFunction } from "../../../../types";
import { NewLineKind, SyntaxKind, ts } from "../../../../typescript";
import { NewLineKind, SyntaxKind, ts, SymbolFlags } from "../../../../typescript";
import { TypeGuards } from "../../../../utils";
import { getInfoFromText } from "../../testHelpers";

Expand Down Expand Up @@ -1988,4 +1988,20 @@ class MyClass {
doTest("const t = 5;\nclass U {}\ninterface I", "m", undefined);
});
});

describe(nameof<Node>(n => n.getSymbolsInScope), () => {
function doTest(text: string, selectNode: (sourceFile: SourceFile) => Node, meaning: SymbolFlags, expectedSymbolNames: string[]) {
const { sourceFile } = getInfoFromText(text);
const node = selectNode(sourceFile);
const result = node.getSymbolsInScope(meaning);
expect(result.map(s => s.getName()).sort()).to.deep.equal(expectedSymbolNames.sort());
}

it("should get all the symbols in the provided scope filtered by meaning", () => {
doTest("const var = 5; function a() { function b() {} const c = ''; function e() { function f() {} } }",
sourceFile => sourceFile.getFunctionOrThrow("a").getVariableDeclarationOrThrow("c"),
SymbolFlags.BlockScopedVariable,
["c"]);
});
});
});
21 changes: 18 additions & 3 deletions src/tests/compiler/tools/typeCheckerTests.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { expect } from "chai";
import { CallExpression, TypeChecker, NamedNode } from "../../../compiler";
import { InvalidOperationError } from "../../../errors";
import { SyntaxKind } from "../../../typescript";
import { CallExpression, TypeChecker, NamedNode, SourceFile, Node } from "../../../compiler";
import { SyntaxKind, SymbolFlags } from "../../../typescript";
import { getInfoFromText, getInfoFromTextWithDescendant } from "../testHelpers";

describe(nameof(TypeChecker), () => {
Expand Down Expand Up @@ -70,4 +69,20 @@ describe(nameof(TypeChecker), () => {
doTest("function foo(){}; foo();", "foo");
});
});

describe(nameof<TypeChecker>(p => p.getSymbolsInScope), () => {
function doTest(text: string, selectNode: (sourceFile: SourceFile) => Node, meaning: SymbolFlags, expectedSymbolNames: string[]) {
const { sourceFile, project } = getInfoFromText(text);
const node = selectNode(sourceFile);
const result = project.getTypeChecker().getSymbolsInScope(node, meaning);
expect(result.map(s => s.getName()).sort()).to.deep.equal(expectedSymbolNames.sort());
}

it("should get all the symbols in the provided scope filtered by meaning", () => {
doTest("function a() { function b() {} const c = ''; function e() { function f() {} } }",
sourceFile => sourceFile.getFunctionOrThrow("a").getVariableDeclarationOrThrow("c"),
SymbolFlags.Function,
["a", "b", "e"]);
});
});
});

0 comments on commit 25ab43d

Please sign in to comment.