mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
enable outlining atop of new compiler
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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());
|
||||
Reference in New Issue
Block a user