Skip to content

Commit 90763f9

Browse files
committed
Merge branch 'release-1.8' of https://github.com/Microsoft/TypeScript into projectSizeLimitFor18-2
2 parents 4d0f488 + de7d429 commit 90763f9

File tree

1 file changed

+217
-9
lines changed

1 file changed

+217
-9
lines changed

src/services/navigationBar.ts

Lines changed: 217 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
/* @internal */
44
namespace ts.NavigationBar {
55
export function getNavigationBarItems(sourceFile: SourceFile, compilerOptions: CompilerOptions): ts.NavigationBarItem[] {
6+
// TODO: Handle JS files differently in 'navbar' calls for now, but ideally we should unify
7+
// the 'navbar' and 'navto' logic for TypeScript and JavaScript.
8+
if (isSourceFileJavaScript(sourceFile)) {
9+
return getJsNavigationBarItems(sourceFile, compilerOptions);
10+
}
11+
612
// If the source file has any child items, then it included in the tree
713
// and takes lexical ownership of all other top-level items.
814
let hasGlobalNode = false;
@@ -130,7 +136,7 @@ namespace ts.NavigationBar {
130136

131137
return topLevelNodes;
132138
}
133-
139+
134140
function sortNodes(nodes: Node[]): Node[] {
135141
return nodes.slice(0).sort((n1: Declaration, n2: Declaration) => {
136142
if (n1.name && n2.name) {
@@ -147,7 +153,7 @@ namespace ts.NavigationBar {
147153
}
148154
});
149155
}
150-
156+
151157
function addTopLevelNodes(nodes: Node[], topLevelNodes: Node[]): void {
152158
nodes = sortNodes(nodes);
153159

@@ -178,8 +184,8 @@ namespace ts.NavigationBar {
178184

179185
function isTopLevelFunctionDeclaration(functionDeclaration: FunctionLikeDeclaration) {
180186
if (functionDeclaration.kind === SyntaxKind.FunctionDeclaration) {
181-
// A function declaration is 'top level' if it contains any function declarations
182-
// within it.
187+
// A function declaration is 'top level' if it contains any function declarations
188+
// within it.
183189
if (functionDeclaration.body && functionDeclaration.body.kind === SyntaxKind.Block) {
184190
// Proper function declarations can only have identifier names
185191
if (forEach((<Block>functionDeclaration.body).statements,
@@ -198,7 +204,7 @@ namespace ts.NavigationBar {
198204

199205
return false;
200206
}
201-
207+
202208
function getItemsWorker(nodes: Node[], createItem: (n: Node) => ts.NavigationBarItem): ts.NavigationBarItem[] {
203209
let items: ts.NavigationBarItem[] = [];
204210

@@ -395,19 +401,19 @@ namespace ts.NavigationBar {
395401
let result: string[] = [];
396402

397403
result.push(moduleDeclaration.name.text);
398-
404+
399405
while (moduleDeclaration.body && moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) {
400406
moduleDeclaration = <ModuleDeclaration>moduleDeclaration.body;
401407

402408
result.push(moduleDeclaration.name.text);
403-
}
409+
}
404410

405411
return result.join(".");
406412
}
407413

408414
function createModuleItem(node: ModuleDeclaration): NavigationBarItem {
409415
let moduleName = getModuleName(node);
410-
416+
411417
let childItems = getItemsWorker(getChildNodes((<Block>getInnermostModule(node).body).statements), createChildItem);
412418

413419
return getNavigationBarItem(moduleName,
@@ -534,4 +540,206 @@ namespace ts.NavigationBar {
534540
return getTextOfNodeFromSourceText(sourceFile.text, node);
535541
}
536542
}
537-
}
543+
544+
export function getJsNavigationBarItems(sourceFile: SourceFile, compilerOptions: CompilerOptions): NavigationBarItem[] {
545+
const anonFnText = "<function>";
546+
const anonClassText = "<class>";
547+
let indent = 0;
548+
549+
let rootName = isExternalModule(sourceFile) ?
550+
"\"" + escapeString(getBaseFileName(removeFileExtension(normalizePath(sourceFile.fileName)))) + "\""
551+
: "<global>";
552+
553+
let sourceFileItem = getNavBarItem(rootName, ScriptElementKind.moduleElement, [getNodeSpan(sourceFile)]);
554+
let topItem = sourceFileItem;
555+
556+
// Walk the whole file, because we want to also find function expressions - which may be in variable initializer,
557+
// call arguments, expressions, etc...
558+
forEachChild(sourceFile, visitNode);
559+
560+
function visitNode(node: Node) {
561+
const newItem = createNavBarItem(node);
562+
563+
if (newItem) {
564+
topItem.childItems.push(newItem);
565+
}
566+
567+
// Add a level if traversing into a container
568+
if (newItem && (isFunctionLike(node) || isClassLike(node))) {
569+
const lastTop = topItem;
570+
indent++;
571+
topItem = newItem;
572+
forEachChild(node, visitNode);
573+
topItem = lastTop;
574+
indent--;
575+
576+
// If the last item added was an anonymous function expression, and it had no children, discard it.
577+
if (newItem && newItem.text === anonFnText && newItem.childItems.length === 0) {
578+
topItem.childItems.pop();
579+
}
580+
}
581+
else {
582+
forEachChild(node, visitNode);
583+
}
584+
}
585+
586+
function createNavBarItem(node: Node) : NavigationBarItem {
587+
switch (node.kind) {
588+
case SyntaxKind.VariableDeclaration:
589+
// Only add to the navbar if at the top-level of the file
590+
// Note: "const" and "let" are also SyntaxKind.VariableDeclarations
591+
if(node.parent/*VariableDeclarationList*/.parent/*VariableStatement*/
592+
.parent/*SourceFile*/.kind !== SyntaxKind.SourceFile) {
593+
return undefined;
594+
}
595+
// If it is initialized with a function expression, handle it when we reach the function expression node
596+
const varDecl = node as VariableDeclaration;
597+
if (varDecl.initializer && (varDecl.initializer.kind === SyntaxKind.FunctionExpression ||
598+
varDecl.initializer.kind === SyntaxKind.ArrowFunction ||
599+
varDecl.initializer.kind === SyntaxKind.ClassExpression)) {
600+
return undefined;
601+
}
602+
// Fall through
603+
case SyntaxKind.FunctionDeclaration:
604+
case SyntaxKind.ClassDeclaration:
605+
case SyntaxKind.Constructor:
606+
case SyntaxKind.GetAccessor:
607+
case SyntaxKind.SetAccessor:
608+
// "export default function().." looks just like a regular function/class declaration, except with the 'default' flag
609+
const name = node.flags && (node.flags & NodeFlags.Default) && !(node as (Declaration)).name ? "default" :
610+
node.kind === SyntaxKind.Constructor ? "constructor" :
611+
declarationNameToString((node as (Declaration)).name);
612+
return getNavBarItem(name, getScriptKindForElementKind(node.kind), [getNodeSpan(node)]);
613+
case SyntaxKind.FunctionExpression:
614+
case SyntaxKind.ArrowFunction:
615+
case SyntaxKind.ClassExpression:
616+
return getDefineModuleItem(node) || getFunctionOrClassExpressionItem(node);
617+
case SyntaxKind.MethodDeclaration:
618+
const methodDecl = node as MethodDeclaration;
619+
return getNavBarItem(declarationNameToString(methodDecl.name),
620+
ScriptElementKind.memberFunctionElement,
621+
[getNodeSpan(node)]);
622+
case SyntaxKind.ExportAssignment:
623+
// e.g. "export default <expr>"
624+
return getNavBarItem("default", ScriptElementKind.variableElement, [getNodeSpan(node)]);
625+
case SyntaxKind.ImportClause: // e.g. 'def' in: import def from 'mod' (in ImportDeclaration)
626+
if (!(node as ImportClause).name) {
627+
// No default import (this node is still a parent of named & namespace imports, which are handled below)
628+
return undefined;
629+
}
630+
// fall through
631+
case SyntaxKind.ImportSpecifier: // e.g. 'id' in: import {id} from 'mod' (in NamedImports, in ImportClause)
632+
case SyntaxKind.NamespaceImport: // e.g. '* as ns' in: import * as ns from 'mod' (in ImportClause)
633+
case SyntaxKind.ExportSpecifier: // e.g. 'a' or 'b' in: export {a, foo as b} from 'mod'
634+
// Export specifiers are only interesting if they are reexports from another module, or renamed, else they are already globals
635+
if (node.kind === SyntaxKind.ExportSpecifier) {
636+
if (!(node.parent.parent as ExportDeclaration).moduleSpecifier && !(node as ExportSpecifier).propertyName) {
637+
return undefined;
638+
}
639+
}
640+
const decl = node as (ImportSpecifier | ImportClause | NamespaceImport | ExportSpecifier);
641+
if (!decl.name) {
642+
return undefined;
643+
}
644+
const declName = declarationNameToString(decl.name);
645+
return getNavBarItem(declName, ScriptElementKind.constElement, [getNodeSpan(node)]);
646+
default:
647+
return undefined;
648+
}
649+
}
650+
651+
function getNavBarItem(text: string, kind: string, spans: TextSpan[], kindModifiers = ScriptElementKindModifier.none): NavigationBarItem {
652+
return {
653+
text, kind, kindModifiers, spans, childItems: [], indent, bolded: false, grayed: false
654+
}
655+
}
656+
657+
function getDefineModuleItem(node: Node): NavigationBarItem {
658+
if (node.kind !== SyntaxKind.FunctionExpression && node.kind !== SyntaxKind.ArrowFunction) {
659+
return undefined;
660+
}
661+
662+
// No match if this is not a call expression to an identifier named 'define'
663+
if (node.parent.kind !== SyntaxKind.CallExpression) {
664+
return undefined;
665+
}
666+
const callExpr = node.parent as CallExpression;
667+
if (callExpr.expression.kind !== SyntaxKind.Identifier || callExpr.expression.getText() !== 'define') {
668+
return undefined;
669+
}
670+
671+
// Return a module of either the given text in the first argument, or of the source file path
672+
let defaultName = node.getSourceFile().fileName;
673+
if (callExpr.arguments[0].kind === SyntaxKind.StringLiteral) {
674+
defaultName = ((callExpr.arguments[0]) as StringLiteral).text;
675+
}
676+
return getNavBarItem(defaultName, ScriptElementKind.moduleElement, [getNodeSpan(node.parent)]);
677+
}
678+
679+
function getFunctionOrClassExpressionItem(node: Node): NavigationBarItem {
680+
if (node.kind !== SyntaxKind.FunctionExpression &&
681+
node.kind !== SyntaxKind.ArrowFunction &&
682+
node.kind !== SyntaxKind.ClassExpression) {
683+
return undefined;
684+
}
685+
686+
const fnExpr = node as FunctionExpression | ArrowFunction | ClassExpression;
687+
let fnName: string;
688+
if (fnExpr.name && getFullWidth(fnExpr.name) > 0) {
689+
// The expression has an identifier, so use that as the name
690+
fnName = declarationNameToString(fnExpr.name);
691+
}
692+
else {
693+
// See if it is a var initializer. If so, use the var name.
694+
if (fnExpr.parent.kind === SyntaxKind.VariableDeclaration) {
695+
fnName = declarationNameToString((fnExpr.parent as VariableDeclaration).name);
696+
}
697+
// See if it is of the form "<expr> = function(){...}". If so, use the text from the left-hand side.
698+
else if (fnExpr.parent.kind === SyntaxKind.BinaryExpression &&
699+
(fnExpr.parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken) {
700+
fnName = (fnExpr.parent as BinaryExpression).left.getText();
701+
if (fnName.length > 20) {
702+
fnName = fnName.substring(0, 17) + "...";
703+
}
704+
}
705+
// See if it is a property assignment, and if so use the property name
706+
else if (fnExpr.parent.kind === SyntaxKind.PropertyAssignment &&
707+
(fnExpr.parent as PropertyAssignment).name) {
708+
fnName = (fnExpr.parent as PropertyAssignment).name.getText();
709+
}
710+
else {
711+
fnName = node.kind === SyntaxKind.ClassExpression ? anonClassText : anonFnText;
712+
}
713+
}
714+
const scriptKind = node.kind === SyntaxKind.ClassExpression ? ScriptElementKind.classElement : ScriptElementKind.functionElement;
715+
return getNavBarItem(fnName, scriptKind, [getNodeSpan(node)]);
716+
}
717+
718+
function getNodeSpan(node: Node) {
719+
return node.kind === SyntaxKind.SourceFile
720+
? createTextSpanFromBounds(node.getFullStart(), node.getEnd())
721+
: createTextSpanFromBounds(node.getStart(), node.getEnd());
722+
}
723+
724+
function getScriptKindForElementKind(kind: SyntaxKind) {
725+
switch (kind) {
726+
case SyntaxKind.VariableDeclaration:
727+
return ScriptElementKind.variableElement;
728+
case SyntaxKind.FunctionDeclaration:
729+
return ScriptElementKind.functionElement;
730+
case SyntaxKind.ClassDeclaration:
731+
return ScriptElementKind.classElement;
732+
case SyntaxKind.Constructor:
733+
return ScriptElementKind.constructorImplementationElement;
734+
case SyntaxKind.GetAccessor:
735+
return ScriptElementKind.memberGetAccessorElement;
736+
case SyntaxKind.SetAccessor:
737+
return ScriptElementKind.memberSetAccessorElement;
738+
default:
739+
return "unknown";
740+
}
741+
}
742+
743+
return sourceFileItem.childItems;
744+
}
745+
}

0 commit comments

Comments
 (0)