enable outlining atop of new compiler

This commit is contained in:
Vladimir Matveev
2014-08-11 13:15:07 -07:00
parent 1a4a822da6
commit 785c083888
3 changed files with 197 additions and 89 deletions
+37 -85
View File
@@ -15,95 +15,47 @@
///<reference path='references.ts' />
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 = (<FunctionDeclaration>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;
}
}
}
}
+55 -4
View File
@@ -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<Map<DocumentRegistryEntry>> = {};
@@ -1140,7 +1185,7 @@ module ts {
var bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ true);
var entry = lookUp(bucket, filename);
if (!entry) {
var sourceFile = createSourceFile(filename, scriptSnapshot.getText(0, scriptSnapshot.getLength()), compilationSettings.target, version, isOpen);
var sourceFile = createSourceFileFromScriptSnapshot(filename, scriptSnapshot, compilationSettings, version, isOpen);
bucket[filename] = entry = {
sourceFile: sourceFile,
@@ -2024,6 +2069,12 @@ module ts {
return syntaxTreeCache.getCurrentFileSyntaxTree(filename);
}
function getCurrentSourceFile(filename: string): SourceFile {
filename = TypeScript.switchToForwardSlashes(filename);
var currentSourceFile = syntaxTreeCache.getCurrentSourceFile(filename);
return currentSourceFile;
}
function getNameOrDottedNameSpan(filename: string, startPos: number, endPos: number): SpanInfo {
function getTypeInfoEligiblePath(filename: string, position: number, isConstructorValidPosition: boolean) {
var sourceUnit = syntaxTreeCache.getCurrentFileSyntaxTree(filename).sourceUnit();
@@ -2100,8 +2151,8 @@ module ts {
function getOutliningRegions(filename: string) {
// doesn't use compiler - no need to synchronize with host
filename = TypeScript.switchToForwardSlashes(filename);
var syntaxTree = getSyntaxTree(filename);
return TypeScript.Services.OutliningElementsCollector.collectElements(syntaxTree.sourceUnit());
var sourceFile = getCurrentSourceFile(filename);
return OutliningElementsCollector.collectElements(sourceFile);
}
function getBraceMatchingAtPosition(filename: string, position: number) {
+105
View File
@@ -0,0 +1,105 @@
/// <reference path="fourslash.ts"/>
////// interface
////interface IFoo[| {
//// getDist(): number;
////}|]
////
////// class members
////class Foo[| {
//// constructor()[| {
//// }|]
////
//// public foo(): number[| {
//// return 0;
//// }|]
////
//// public get X()[| {
//// return 1;
//// }|]
////
//// public set X(v: number)[| {
//// }|]
////
//// public member = function f()[| {
////
//// }|]
////}|]
////
////// modules
////module m1[| {
//// module m2[| { }|]
//// module m3[| {
//// function foo()[| {
////
//// }|]
////
//// interface IFoo2[| {
////
//// }|]
////
//// class foo2 implements IFoo2[| {
////
//// }|]
//// }|]
////}|]
////
////// function declaration
////function foo(): number[| {
//// return 0;
////}|]
////
////// function expressions
////(function f()[| {
////
////}|])
////
////// trivia handeling
////class ClassFooWithTrivia[| /* some comments */
//// /* more trivia */ {
////
////
//// /*some trailing trivia */
////}|] /* even more */
////
////// object literals
////var x = [|{
//// a:1,
//// b:2,
//// get foo() [|{
//// return 1;
//// }|]
////}|]
//////outline with deep nesting
////module m1[|{
//// module m2[| {
//// module m3[| {
//// module m4[| {
//// module m5[| {
//// module m6[| {
//// module m7[| {
//// module m8[| {
//// module m9[| {
//// module m10[| {
//// module m11 {
//// module m12 {
//// export interface IFoo {
//// }
//// }
//// }
//// }|]
//// }|]
//// }|]
//// }|]
//// }|]
//// }|]
//// }|]
//// }|]
//// }|]
////}|]
////
//////outline after a deeply nested node
////class AfterNestedNodes[| {
////}|]
verify.outliningSpansInCurrentFile(test.ranges());