navigation tree / bar: Set span of anonymous function to span of VariableDeclaration containing it (#18575)

* navigation tree / bar: Set span of anonymous function to span of VariableDeclaration containing it

* Add back `isFunctionOrClassExpression`
This commit is contained in:
Andy 2017-09-19 14:39:29 -07:00 committed by GitHub
parent 0ae42ea3de
commit 12649516cf
6 changed files with 112 additions and 60 deletions

View file

@ -2571,20 +2571,29 @@ namespace FourSlash {
}
}
public verifyNavigationBar(json: any) {
const items = this.languageService.getNavigationBarItems(this.activeFile.fileName);
if (JSON.stringify(items, replacer) !== JSON.stringify(json)) {
this.raiseError(`verifyNavigationBar failed - expected: ${stringify(json)}, got: ${stringify(items, replacer)}`);
public verifyNavigationBar(json: any, options: { checkSpans?: boolean } | undefined) {
this.verifyNavigationTreeOrBar(json, this.languageService.getNavigationBarItems(this.activeFile.fileName), "Bar", options);
}
public verifyNavigationTree(json: any, options: { checkSpans?: boolean } | undefined) {
this.verifyNavigationTreeOrBar(json, this.languageService.getNavigationTree(this.activeFile.fileName), "Tree", options);
}
private verifyNavigationTreeOrBar(json: any, tree: any, name: "Tree" | "Bar", options: { checkSpans?: boolean } | undefined) {
if (JSON.stringify(tree, replacer) !== JSON.stringify(json)) {
this.raiseError(`verifyNavigation${name} failed - expected: ${stringify(json)}, got: ${stringify(tree, replacer)}`);
}
// Make the data easier to read.
function replacer(key: string, value: any) {
switch (key) {
case "spans":
// We won't ever check this.
return undefined;
return options && options.checkSpans ? value : undefined;
case "start":
case "length":
// Never omit the values in a span, even if they are 0.
return value;
case "childItems":
return value.length === 0 ? undefined : value;
return !value || value.length === 0 ? undefined : value;
default:
// Omit falsy values, those are presumed to be the default.
return value || undefined;
@ -2592,18 +2601,6 @@ namespace FourSlash {
}
}
public verifyNavigationTree(json: any) {
const tree = this.languageService.getNavigationTree(this.activeFile.fileName);
if (JSON.stringify(tree, replacer) !== JSON.stringify(json)) {
this.raiseError(`verifyNavigationTree failed - expected: ${stringify(json)}, got: ${stringify(tree, replacer)}`);
}
function replacer(key: string, value: any) {
// Don't check "spans", and omit falsy values.
return key === "spans" ? undefined : (value || undefined);
}
}
public printNavigationItems(searchValue: string) {
const items = this.languageService.getNavigateToItems(searchValue);
Harness.IO.log(`NavigationItems list (${items.length} items)`);
@ -3533,6 +3530,10 @@ namespace FourSlashInterface {
return this.state.getRanges();
}
public spans(): ts.TextSpan[] {
return this.ranges().map(r => ts.createTextSpan(r.start, r.end - r.start));
}
public rangesByText(): ts.Map<FourSlash.Range[]> {
return this.state.rangesByText();
}
@ -3966,12 +3967,12 @@ namespace FourSlashInterface {
this.state.verifyImportFixAtPosition(expectedTextArray, errorCode);
}
public navigationBar(json: any) {
this.state.verifyNavigationBar(json);
public navigationBar(json: any, options?: { checkSpans?: boolean }) {
this.state.verifyNavigationBar(json, options);
}
public navigationTree(json: any) {
this.state.verifyNavigationTree(json);
public navigationTree(json: any, options?: { checkSpans?: boolean }) {
this.state.verifyNavigationTree(json, options);
}
public navigationItemsListCount(count: number, searchValue: string, matchKind?: string, fileName?: string) {

View file

@ -209,17 +209,24 @@ namespace ts.NavigationBar {
case SyntaxKind.BindingElement:
case SyntaxKind.VariableDeclaration:
const decl = <VariableDeclaration>node;
const name = decl.name;
const { name, initializer } = <VariableDeclaration | BindingElement>node;
if (isBindingPattern(name)) {
addChildrenRecursively(name);
}
else if (decl.initializer && isFunctionOrClassExpression(decl.initializer)) {
// For `const x = function() {}`, just use the function node, not the const.
addChildrenRecursively(decl.initializer);
else if (initializer && isFunctionOrClassExpression(initializer)) {
if (initializer.name) {
// Don't add a node for the VariableDeclaration, just for the initializer.
addChildrenRecursively(initializer);
}
else {
// Add a node for the VariableDeclaration, but not for the initializer.
startNode(node);
forEachChild(initializer, addChildrenRecursively);
endNode();
}
}
else {
addNodeWithRecursiveChild(decl, decl.initializer);
addNodeWithRecursiveChild(node, initializer);
}
break;
@ -644,7 +651,14 @@ namespace ts.NavigationBar {
}
}
function isFunctionOrClassExpression(node: Node): boolean {
return node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction || node.kind === SyntaxKind.ClassExpression;
function isFunctionOrClassExpression(node: Node): node is ArrowFunction | FunctionExpression | ClassExpression {
switch (node.kind) {
case SyntaxKind.ArrowFunction:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ClassExpression:
return true;
default:
return false;
}
}
}

View file

@ -114,6 +114,7 @@ declare namespace FourSlashInterface {
markerNames(): string[];
marker(name?: string): Marker;
ranges(): Range[];
spans(): Array<{ start: number, length: number }>;
rangesByText(): ts.Map<Range[]>;
markerByName(s: string): Marker;
symbolsInScope(range: Range): any[];
@ -250,8 +251,8 @@ declare namespace FourSlashInterface {
fileAfterApplyingRefactorAtMarker(markerName: string, expectedContent: string, refactorNameToApply: string, formattingOptions?: FormatCodeOptions): void;
importFixAtPosition(expectedTextArray: string[], errorCode?: number): void;
navigationBar(json: any): void;
navigationTree(json: any): void;
navigationBar(json: any, options?: { checkSpans?: boolean }): void;
navigationTree(json: any, options?: { checkSpans?: boolean }): void;
navigationItemsListCount(count: number, searchValue: string, matchKind?: string, fileName?: string): void;
navigationItemsListContains(name: string, kind: string, searchValue: string, matchKind: string, fileName?: string, parentName?: string): void;
occurrencesAtPositionContains(range: Range, isWriteAccess?: boolean): void;

View file

@ -46,7 +46,7 @@ verify.navigationTree({
},
{
"text": "x",
"kind": "function",
"kind": "const",
"childItems": [
{
"text": "xx",
@ -90,7 +90,7 @@ verify.navigationTree({
},
{
"text": "cls2",
"kind": "class"
"kind": "const"
},
{
"text": "cls3",
@ -138,7 +138,7 @@ verify.navigationBar([
},
{
"text": "x",
"kind": "function"
"kind": "const"
},
{
"text": "y",
@ -160,7 +160,7 @@ verify.navigationBar([
},
{
"text": "x",
"kind": "function",
"kind": "const",
"childItems": [
{
"text": "xx",
@ -205,7 +205,7 @@ verify.navigationBar([
},
{
"text": "cls2",
"kind": "class"
"kind": "const"
},
{
"text": "cls3",
@ -219,11 +219,6 @@ verify.navigationBar([
"kind": "class",
"indent": 2
},
{
"text": "cls2",
"kind": "class",
"indent": 2
},
{
"text": "cls3",
"kind": "class",

View file

@ -0,0 +1,51 @@
/// <reference path="fourslash.ts" />
////const [|x = () => 0|];
////const f = [|function f() {}|];
const [s0, s1] = test.spans();
const sGlobal = { start: 0, length: 45 };
verify.navigationTree({
text: "<global>",
kind: "script",
spans: [sGlobal],
childItems: [
{
text: "f",
kind: "function",
spans: [s1],
},
{
text: "x",
kind: "const",
spans: [s0],
},
]
}, { checkSpans: true });
verify.navigationBar([
{
text: "<global>",
kind: "script",
spans: [sGlobal],
childItems: [
{
text: "f",
kind: "function",
spans: [s1],
},
{
text: "x",
kind: "const",
spans: [s0],
},
],
},
{
text: "f",
kind: "function",
spans: [s1],
indent: 1,
},
], { checkSpans: true });

View file

@ -17,11 +17,13 @@ verify.navigationBar([
},
{
"text": "func",
"kind": "function"
"kind": "const",
"kindModifiers": "export",
},
{
"text": "func2",
"kind": "function"
"kind": "const",
"kindModifiers": "export",
},
{
"text": "value",
@ -35,18 +37,6 @@ verify.navigationBar([
"kind": "function",
"kindModifiers": "export",
"indent": 1
},
{
"text": "func",
"kind": "function",
"kindModifiers": "export",
"indent": 1
},
{
"text": "func2",
"kind": "function",
"kindModifiers": "export",
"indent": 1
}
]);
@ -61,12 +51,12 @@ verify.navigationTree({
},
{
"text": "func",
"kind": "function",
"kind": "const",
"kindModifiers": "export"
},
{
"text": "func2",
"kind": "function",
"kind": "const",
"kindModifiers": "export"
},
{