diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts
index 8a873a8774d..0d9d01016fb 100644
--- a/src/services/outliningElementsCollector.ts
+++ b/src/services/outliningElementsCollector.ts
@@ -15,95 +15,47 @@
///
-module TypeScript.Services {
- export class OutliningElementsCollector extends TypeScript.DepthLimitedWalker {
- // The maximum depth for collecting spans; this will cause us to miss deeply nested function/modules spans,
- // but will guarantee performance will not be closely tied to tree depth.
- private static MaximumDepth: number = 10;
- private inObjectLiteralExpression: boolean = false;
+module ts {
+ export module OutliningElementsCollector {
+ export function collectElements(sourceFile: SourceFile): TypeScript.TextSpan[] {
+ var elements: TypeScript.TextSpan[] = [];
- private elements: TypeScript.TextSpan[] = [];
-
- constructor() {
- super(OutliningElementsCollector.MaximumDepth);
- }
-
- public visitClassDeclaration(node: TypeScript.ClassDeclarationSyntax): void {
- this.addOutlineRange(node, node.openBraceToken, node.closeBraceToken);
- super.visitClassDeclaration(node);
- }
-
- public visitInterfaceDeclaration(node: TypeScript.InterfaceDeclarationSyntax): void {
- this.addOutlineRange(node, node.body.openBraceToken, node.body.closeBraceToken);
- super.visitInterfaceDeclaration(node);
- }
-
- public visitModuleDeclaration(node: TypeScript.ModuleDeclarationSyntax): void {
- this.addOutlineRange(node, node.openBraceToken, node.closeBraceToken);
- super.visitModuleDeclaration(node);
- }
-
- public visitEnumDeclaration(node: TypeScript.EnumDeclarationSyntax): void {
- this.addOutlineRange(node, node.openBraceToken, node.closeBraceToken);
- super.visitEnumDeclaration(node);
- }
-
- public visitFunctionDeclaration(node: TypeScript.FunctionDeclarationSyntax): void {
- this.addOutlineRange(node, node.block, node.block);
- super.visitFunctionDeclaration(node);
- }
-
- public visitFunctionExpression(node: TypeScript.FunctionExpressionSyntax): void {
- this.addOutlineRange(node, node.block, node.block);
- super.visitFunctionExpression(node);
- }
-
- public visitConstructorDeclaration(node: TypeScript.ConstructorDeclarationSyntax): void {
- this.addOutlineRange(node, node.block, node.block);
- super.visitConstructorDeclaration(node);
- }
-
- public visitMemberFunctionDeclaration(node: TypeScript.MemberFunctionDeclarationSyntax): void {
- this.addOutlineRange(node, node.block, node.block);
- super.visitMemberFunctionDeclaration(node);
- }
-
- public visitGetAccessor(node: TypeScript.GetAccessorSyntax): void {
- if (!this.inObjectLiteralExpression) {
- this.addOutlineRange(node, node.block, node.block);
+ function addOutlineRange(startElement: Node, endElement: Node) {
+ if (startElement && endElement) {
+ // Push the new range
+ elements.push(TypeScript.TextSpan.fromBounds(startElement.pos, endElement.end));
+ }
}
- super.visitGetAccessor(node);
- }
- public visitSetAccessor(node: TypeScript.SetAccessorSyntax): void {
- if (!this.inObjectLiteralExpression) {
- this.addOutlineRange(node, node.block, node.block);
+ function walk(n: Node) {
+ switch (n.kind) {
+ case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.InterfaceDeclaration:
+ case SyntaxKind.ModuleDeclaration:
+ case SyntaxKind.EnumDeclaration:
+ case SyntaxKind.ObjectLiteral:
+ var openBrace = forEach(n.getChildren(), c => c.kind === SyntaxKind.OpenBraceToken && c);
+ var closeBrace = forEach(n.getChildren(), c => c.kind === SyntaxKind.CloseBraceToken && c);
+ addOutlineRange(openBrace, closeBrace);
+ break;
+ case SyntaxKind.Constructor:
+ case SyntaxKind.FunctionDeclaration:
+ case SyntaxKind.Method:
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.SetAccessor:
+ var body = (n).body;
+ if (body) {
+ var openBrace = forEach(body.getChildren(), c => c.kind === SyntaxKind.OpenBraceToken && c);
+ var closeBrace = forEach(body.getChildren(), c => c.kind === SyntaxKind.CloseBraceToken && c);
+ addOutlineRange(openBrace, closeBrace);
+ }
+ break;
+ }
+ forEachChild(n, walk);
}
- super.visitSetAccessor(node);
- }
- public visitObjectLiteralExpression(node: TypeScript.ObjectLiteralExpressionSyntax): void {
- var savedInObjectLiteralExpression = this.inObjectLiteralExpression;
- this.inObjectLiteralExpression = true;
- super.visitObjectLiteralExpression(node);
- this.inObjectLiteralExpression = savedInObjectLiteralExpression;
- }
-
- private addOutlineRange(node: TypeScript.ISyntaxNode, startElement: TypeScript.ISyntaxNodeOrToken, endElement: TypeScript.ISyntaxNodeOrToken) {
- if (startElement && endElement && !isShared(startElement) && !isShared(endElement)) {
- // Compute the position
- var start = TypeScript.start(startElement);
- var end = TypeScript.end(endElement);
-
- // Push the new range
- this.elements.push(TypeScript.TextSpan.fromBounds(start, end));
- }
- }
-
- public static collectElements(node: TypeScript.SourceUnitSyntax): TypeScript.TextSpan[] {
- var collector = new OutliningElementsCollector();
- visitNodeOrToken(collector, node);
- return collector.elements;
+ walk(sourceFile);
+ return elements;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/services/services.ts b/src/services/services.ts
index 6312e61f9d2..36790058ef5 100644
--- a/src/services/services.ts
+++ b/src/services/services.ts
@@ -962,6 +962,7 @@ module ts {
// currently edited file.
private currentfilename: string = "";
private currentFileVersion: number = -1;
+ private currentSourceFile: SourceFile = null;
private currentFileSyntaxTree: TypeScript.SyntaxTree = null;
private currentFileScriptSnapshot: TypeScript.IScriptSnapshot = null;
@@ -969,32 +970,72 @@ module ts {
this.hostCache = new HostCache(host);
}
- public getCurrentFileSyntaxTree(filename: string): TypeScript.SyntaxTree {
+ private initialize(filename: string) {
+ // ensure that both source file and syntax tree are either initialized or not initialized
+ Debug.assert(!!this.currentFileSyntaxTree === !!this.currentSourceFile);
this.hostCache = new HostCache(this.host);
var version = this.hostCache.getVersion(filename);
var syntaxTree: TypeScript.SyntaxTree = null;
+ var sourceFile: SourceFile;
if (this.currentFileSyntaxTree === null || this.currentfilename !== filename) {
var scriptSnapshot = this.hostCache.getScriptSnapshot(filename);
syntaxTree = this.createSyntaxTree(filename, scriptSnapshot);
+ sourceFile = createSourceFileFromScriptSnapshot(filename, scriptSnapshot, getDefaultCompilerOptions(), version, /*isOpen*/ true);
+
+ fixupParentReferences(sourceFile);
}
else if (this.currentFileVersion !== version) {
var scriptSnapshot = this.hostCache.getScriptSnapshot(filename);
syntaxTree = this.updateSyntaxTree(filename, scriptSnapshot, this.currentFileSyntaxTree, this.currentFileVersion);
+
+ var editRange = this.hostCache.getScriptTextChangeRangeSinceVersion(filename, this.currentFileVersion);
+ sourceFile = !editRange
+ ? createSourceFileFromScriptSnapshot(filename, scriptSnapshot, getDefaultCompilerOptions(), version, /*isOpen*/ true)
+ : this.currentSourceFile.update(scriptSnapshot, version, /*isOpen*/ true, editRange);
+
+ fixupParentReferences(sourceFile);
}
if (syntaxTree !== null) {
+ Debug.assert(sourceFile);
// All done, ensure state is up to date
this.currentFileScriptSnapshot = scriptSnapshot;
this.currentFileVersion = version;
this.currentfilename = filename;
this.currentFileSyntaxTree = syntaxTree;
+ this.currentSourceFile = sourceFile;
}
+ function fixupParentReferences(sourceFile: SourceFile) {
+ // normally parent references are set during binding.
+ // however here SourceFile data is used only for syntactic features so running the whole binding process is an overhead.
+ // walk over the nodes and set parent references
+ var parent: Node = sourceFile;
+ function walk(n: Node): void {
+ if (parent) {
+ n.parent = parent;
+ }
+ var saveParent = parent;
+ parent = n;
+ forEachChild(n, walk);
+ parent = saveParent;
+ }
+ forEachChild(sourceFile, walk);
+ }
+ }
+
+ public getCurrentFileSyntaxTree(filename: string): TypeScript.SyntaxTree {
+ this.initialize(filename);
return this.currentFileSyntaxTree;
}
+ public getCurrentSourceFile(filename: string): SourceFile {
+ this.initialize(filename);
+ return this.currentSourceFile;
+ }
+
public getCurrentScriptSnapshot(filename: string): TypeScript.IScriptSnapshot {
// update currentFileScriptSnapshot as a part of 'getCurrentFileSyntaxTree' call
this.getCurrentFileSyntaxTree(filename);
@@ -1093,6 +1134,10 @@ module ts {
}
}
+ function createSourceFileFromScriptSnapshot(filename: string, scriptSnapshot: TypeScript.IScriptSnapshot, settings: CompilerOptions, version: number, isOpen: boolean) {
+ return createSourceFile(filename, scriptSnapshot.getText(0, scriptSnapshot.getLength()), settings.target, version, isOpen);
+ }
+
export function createDocumentRegistry(): DocumentRegistry {
var buckets: Map