mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-17 00:34:47 -05:00
Support for AMD modules
This commit is contained in:
@@ -1,8 +1,187 @@
|
||||
/// <reference path='utilities.ts' />
|
||||
/// <reference path='services.ts' />
|
||||
|
||||
/* @internal */
|
||||
namespace ts.NavigationBar {
|
||||
export function getJsNavigationBarItems(sourceFile: SourceFile, compilerOptions: CompilerOptions): NavigationBarItem[] {
|
||||
const anonFnText = "<function>";
|
||||
let indent = 0;
|
||||
|
||||
let rootName = isExternalModule(sourceFile) ?
|
||||
"\"" + escapeString(getBaseFileName(removeFileExtension(normalizePath(sourceFile.fileName)))) + "\""
|
||||
: "<global>";
|
||||
|
||||
let sourceFileItem = getNavBarItem(rootName, ScriptElementKind.moduleElement, [getNodeSpan(sourceFile)]);
|
||||
let topItem = sourceFileItem;
|
||||
|
||||
// Walk the whole file, because we want to also find function expressions - which may be in variable initializer,
|
||||
// call arguments, expressions, etc...
|
||||
forEachChild(sourceFile, visitNode);
|
||||
|
||||
function visitNode(node: Node) {
|
||||
const newItem = createNavBarItem(node);
|
||||
|
||||
if (newItem) {
|
||||
topItem.childItems.push(newItem);
|
||||
}
|
||||
|
||||
// Add a level if traversing into a container
|
||||
if (isNavBarContainer(node)) {
|
||||
const lastTop = topItem;
|
||||
indent++;
|
||||
topItem = newItem;
|
||||
forEachChild(node, visitNode);
|
||||
topItem = lastTop;
|
||||
indent--;
|
||||
|
||||
// If the last item added was an anonymous function expression, and it had no children, discard it.
|
||||
if (newItem && newItem.text === anonFnText && newItem.childItems.length === 0) {
|
||||
topItem.childItems.pop();
|
||||
}
|
||||
}
|
||||
else {
|
||||
forEachChild(node, visitNode);
|
||||
}
|
||||
}
|
||||
|
||||
function createNavBarItem(node: Node) : NavigationBarItem {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
// Only add to the navbar if at the top-level of the file
|
||||
// Note: "const" and "let" are also SyntaxKind.VariableDeclarations
|
||||
if(node.parent/*VariableDeclarationList*/.parent/*VariableStatement*/
|
||||
.parent/*SourceFile*/.kind !== SyntaxKind.SourceFile) {
|
||||
return undefined;
|
||||
}
|
||||
// If it is initialized with a function expression, handle it when we reach the function expression node
|
||||
const varDecl = node as VariableDeclaration;
|
||||
if (varDecl.initializer && (varDecl.initializer.kind === SyntaxKind.FunctionExpression ||
|
||||
varDecl.initializer.kind === SyntaxKind.ArrowFunction )) {
|
||||
return undefined;
|
||||
}
|
||||
// Fall through
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.Constructor:
|
||||
// TODO: Check behavior with names that are symbols, indexers, etc.
|
||||
const name = node.kind === SyntaxKind.Constructor ?
|
||||
"constructor" : declarationNameToString((node as (Declaration)).name);
|
||||
|
||||
const elementKind =
|
||||
node.kind === SyntaxKind.VariableDeclaration ? ScriptElementKind.variableElement :
|
||||
node.kind === SyntaxKind.FunctionDeclaration ? ScriptElementKind.functionElement :
|
||||
node.kind === SyntaxKind.ClassDeclaration ? ScriptElementKind.classElement : "constructor";
|
||||
|
||||
return getNavBarItem(name, elementKind, [getNodeSpan(node)]);
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
return getDefineModuleItem(node) || getFunctionExpressionItem(node);
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
const methodDecl = node as MethodDeclaration;
|
||||
return getNavBarItem(declarationNameToString(methodDecl.name),
|
||||
ScriptElementKind.memberFunctionElement,
|
||||
[getNodeSpan(node)]);
|
||||
// TODO: Class properties/getters/setters, export defaults, import/export identifiers
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function isNavBarContainer(node: Node) {
|
||||
// We only want to add a new level when going into a function or a class
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getNavBarItem(text: string, kind: string, spans: TextSpan[], kindModifiers = ScriptElementKindModifier.none): NavigationBarItem {
|
||||
return {
|
||||
text, kind, kindModifiers, spans, childItems: [], indent, bolded: false, grayed: false
|
||||
}
|
||||
}
|
||||
|
||||
function getDefineModuleItem(node: Node): NavigationBarItem {
|
||||
if (node.kind !== SyntaxKind.FunctionExpression && node.kind !== SyntaxKind.ArrowFunction) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// No match if this is not a call expression to an identifier named 'define'
|
||||
if (node.parent.kind !== SyntaxKind.CallExpression) {
|
||||
return undefined;
|
||||
}
|
||||
const callExpr = node.parent as CallExpression;
|
||||
if (callExpr.expression.kind !== SyntaxKind.Identifier || callExpr.expression.getText() !== 'define') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Return a module of either the given text in the first argument, or of the source file path
|
||||
let defaultName = node.getSourceFile().fileName;
|
||||
if (callExpr.arguments[0].kind === SyntaxKind.StringLiteral) {
|
||||
defaultName = ((callExpr.arguments[0]) as StringLiteral).text;
|
||||
}
|
||||
return getNavBarItem(defaultName, ScriptElementKind.moduleElement, [getNodeSpan(node.parent)]);
|
||||
}
|
||||
|
||||
function getFunctionExpressionItem(node: Node): NavigationBarItem {
|
||||
if (node.kind !== SyntaxKind.FunctionExpression && node.kind !== SyntaxKind.ArrowFunction) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const fnExpr = node as FunctionExpression | ArrowFunction;
|
||||
let fnName: string;
|
||||
if (fnExpr.name && getFullWidth(fnExpr.name) > 0) {
|
||||
// The function expression has an identifier, so use that as the name
|
||||
fnName = declarationNameToString(fnExpr.name);
|
||||
}
|
||||
else {
|
||||
// See if it is a var initializer. If so, use the var name.
|
||||
if (fnExpr.parent.kind === SyntaxKind.VariableDeclaration) {
|
||||
fnName = declarationNameToString((fnExpr.parent as VariableDeclaration).name);
|
||||
}
|
||||
// See if it is of the form "<expr> = function(){...}". If so, use the text from the left-hand side.
|
||||
else if (fnExpr.parent.kind === SyntaxKind.BinaryExpression &&
|
||||
(fnExpr.parent as BinaryExpression).operatorToken.kind === SyntaxKind.FirstAssignment) {
|
||||
fnName = (fnExpr.parent as BinaryExpression).left.getText();
|
||||
if (fnName.length > 20) {
|
||||
fnName = fnName.substring(0,20) + "...";
|
||||
}
|
||||
}
|
||||
// See if it is a property assignment, and if so use the property name
|
||||
else if (fnExpr.parent.kind === SyntaxKind.PropertyAssignment &&
|
||||
(fnExpr.parent as PropertyAssignment).name) {
|
||||
fnName = (fnExpr.parent as PropertyAssignment).name.getText();
|
||||
}
|
||||
else {
|
||||
fnName = anonFnText;
|
||||
}
|
||||
}
|
||||
return getNavBarItem(fnName, ScriptElementKind.functionElement, [getNodeSpan(node)]);
|
||||
}
|
||||
|
||||
function getNodeSpan(node: Node) {
|
||||
return node.kind === SyntaxKind.SourceFile
|
||||
? createTextSpanFromBounds(node.getFullStart(), node.getEnd())
|
||||
: createTextSpanFromBounds(node.getStart(), node.getEnd());
|
||||
}
|
||||
|
||||
return sourceFileItem.childItems;
|
||||
}
|
||||
|
||||
export function getNavigationBarItems(sourceFile: SourceFile, compilerOptions: CompilerOptions): ts.NavigationBarItem[] {
|
||||
// TODO: Handle JS files differently in 'navbar' calls for now, but ideally we should unify
|
||||
// the 'navbar' and 'navto' logic for TypeScript and JavaScript.
|
||||
if (isSourceFileJavaScript(sourceFile)) {
|
||||
return getJsNavigationBarItems(sourceFile, compilerOptions);
|
||||
}
|
||||
|
||||
// If the source file has any child items, then it included in the tree
|
||||
// and takes lexical ownership of all other top-level items.
|
||||
let hasGlobalNode = false;
|
||||
|
||||
Reference in New Issue
Block a user