Skip to content

Commit 785c083

Browse files
committed
enable outlining atop of new compiler
1 parent 1a4a822 commit 785c083

File tree

3 files changed

+198
-90
lines changed

3 files changed

+198
-90
lines changed

src/services/outliningElementsCollector.ts

Lines changed: 38 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -15,95 +15,47 @@
1515

1616
///<reference path='references.ts' />
1717

18-
module TypeScript.Services {
19-
export class OutliningElementsCollector extends TypeScript.DepthLimitedWalker {
20-
// The maximum depth for collecting spans; this will cause us to miss deeply nested function/modules spans,
21-
// but will guarantee performance will not be closely tied to tree depth.
22-
private static MaximumDepth: number = 10;
23-
private inObjectLiteralExpression: boolean = false;
24-
25-
private elements: TypeScript.TextSpan[] = [];
26-
27-
constructor() {
28-
super(OutliningElementsCollector.MaximumDepth);
29-
}
30-
31-
public visitClassDeclaration(node: TypeScript.ClassDeclarationSyntax): void {
32-
this.addOutlineRange(node, node.openBraceToken, node.closeBraceToken);
33-
super.visitClassDeclaration(node);
34-
}
35-
36-
public visitInterfaceDeclaration(node: TypeScript.InterfaceDeclarationSyntax): void {
37-
this.addOutlineRange(node, node.body.openBraceToken, node.body.closeBraceToken);
38-
super.visitInterfaceDeclaration(node);
39-
}
40-
41-
public visitModuleDeclaration(node: TypeScript.ModuleDeclarationSyntax): void {
42-
this.addOutlineRange(node, node.openBraceToken, node.closeBraceToken);
43-
super.visitModuleDeclaration(node);
44-
}
45-
46-
public visitEnumDeclaration(node: TypeScript.EnumDeclarationSyntax): void {
47-
this.addOutlineRange(node, node.openBraceToken, node.closeBraceToken);
48-
super.visitEnumDeclaration(node);
49-
}
50-
51-
public visitFunctionDeclaration(node: TypeScript.FunctionDeclarationSyntax): void {
52-
this.addOutlineRange(node, node.block, node.block);
53-
super.visitFunctionDeclaration(node);
54-
}
55-
56-
public visitFunctionExpression(node: TypeScript.FunctionExpressionSyntax): void {
57-
this.addOutlineRange(node, node.block, node.block);
58-
super.visitFunctionExpression(node);
59-
}
60-
61-
public visitConstructorDeclaration(node: TypeScript.ConstructorDeclarationSyntax): void {
62-
this.addOutlineRange(node, node.block, node.block);
63-
super.visitConstructorDeclaration(node);
64-
}
65-
66-
public visitMemberFunctionDeclaration(node: TypeScript.MemberFunctionDeclarationSyntax): void {
67-
this.addOutlineRange(node, node.block, node.block);
68-
super.visitMemberFunctionDeclaration(node);
69-
}
70-
71-
public visitGetAccessor(node: TypeScript.GetAccessorSyntax): void {
72-
if (!this.inObjectLiteralExpression) {
73-
this.addOutlineRange(node, node.block, node.block);
74-
}
75-
super.visitGetAccessor(node);
76-
}
77-
78-
public visitSetAccessor(node: TypeScript.SetAccessorSyntax): void {
79-
if (!this.inObjectLiteralExpression) {
80-
this.addOutlineRange(node, node.block, node.block);
18+
module ts {
19+
export module OutliningElementsCollector {
20+
export function collectElements(sourceFile: SourceFile): TypeScript.TextSpan[] {
21+
var elements: TypeScript.TextSpan[] = [];
22+
23+
function addOutlineRange(startElement: Node, endElement: Node) {
24+
if (startElement && endElement) {
25+
// Push the new range
26+
elements.push(TypeScript.TextSpan.fromBounds(startElement.pos, endElement.end));
27+
}
8128
}
82-
super.visitSetAccessor(node);
83-
}
8429

85-
public visitObjectLiteralExpression(node: TypeScript.ObjectLiteralExpressionSyntax): void {
86-
var savedInObjectLiteralExpression = this.inObjectLiteralExpression;
87-
this.inObjectLiteralExpression = true;
88-
super.visitObjectLiteralExpression(node);
89-
this.inObjectLiteralExpression = savedInObjectLiteralExpression;
90-
}
91-
92-
private addOutlineRange(node: TypeScript.ISyntaxNode, startElement: TypeScript.ISyntaxNodeOrToken, endElement: TypeScript.ISyntaxNodeOrToken) {
93-
if (startElement && endElement && !isShared(startElement) && !isShared(endElement)) {
94-
// Compute the position
95-
var start = TypeScript.start(startElement);
96-
var end = TypeScript.end(endElement);
97-
98-
// Push the new range
99-
this.elements.push(TypeScript.TextSpan.fromBounds(start, end));
30+
function walk(n: Node) {
31+
switch (n.kind) {
32+
case SyntaxKind.ClassDeclaration:
33+
case SyntaxKind.InterfaceDeclaration:
34+
case SyntaxKind.ModuleDeclaration:
35+
case SyntaxKind.EnumDeclaration:
36+
case SyntaxKind.ObjectLiteral:
37+
var openBrace = forEach(n.getChildren(), c => c.kind === SyntaxKind.OpenBraceToken && c);
38+
var closeBrace = forEach(n.getChildren(), c => c.kind === SyntaxKind.CloseBraceToken && c);
39+
addOutlineRange(openBrace, closeBrace);
40+
break;
41+
case SyntaxKind.Constructor:
42+
case SyntaxKind.FunctionDeclaration:
43+
case SyntaxKind.Method:
44+
case SyntaxKind.GetAccessor:
45+
case SyntaxKind.SetAccessor:
46+
var body = (<FunctionDeclaration>n).body;
47+
if (body) {
48+
var openBrace = forEach(body.getChildren(), c => c.kind === SyntaxKind.OpenBraceToken && c);
49+
var closeBrace = forEach(body.getChildren(), c => c.kind === SyntaxKind.CloseBraceToken && c);
50+
addOutlineRange(openBrace, closeBrace);
51+
}
52+
break;
53+
}
54+
forEachChild(n, walk);
10055
}
101-
}
10256

103-
public static collectElements(node: TypeScript.SourceUnitSyntax): TypeScript.TextSpan[] {
104-
var collector = new OutliningElementsCollector();
105-
visitNodeOrToken(collector, node);
106-
return collector.elements;
57+
walk(sourceFile);
58+
return elements;
10759
}
10860
}
109-
}
61+
}

src/services/services.ts

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -962,39 +962,80 @@ module ts {
962962
// currently edited file.
963963
private currentfilename: string = "";
964964
private currentFileVersion: number = -1;
965+
private currentSourceFile: SourceFile = null;
965966
private currentFileSyntaxTree: TypeScript.SyntaxTree = null;
966967
private currentFileScriptSnapshot: TypeScript.IScriptSnapshot = null;
967968

968969
constructor(private host: LanguageServiceHost) {
969970
this.hostCache = new HostCache(host);
970971
}
971972

972-
public getCurrentFileSyntaxTree(filename: string): TypeScript.SyntaxTree {
973+
private initialize(filename: string) {
974+
// ensure that both source file and syntax tree are either initialized or not initialized
975+
Debug.assert(!!this.currentFileSyntaxTree === !!this.currentSourceFile);
973976
this.hostCache = new HostCache(this.host);
974977

975978
var version = this.hostCache.getVersion(filename);
976979
var syntaxTree: TypeScript.SyntaxTree = null;
980+
var sourceFile: SourceFile;
977981

978982
if (this.currentFileSyntaxTree === null || this.currentfilename !== filename) {
979983
var scriptSnapshot = this.hostCache.getScriptSnapshot(filename);
980984
syntaxTree = this.createSyntaxTree(filename, scriptSnapshot);
985+
sourceFile = createSourceFileFromScriptSnapshot(filename, scriptSnapshot, getDefaultCompilerOptions(), version, /*isOpen*/ true);
986+
987+
fixupParentReferences(sourceFile);
981988
}
982989
else if (this.currentFileVersion !== version) {
983990
var scriptSnapshot = this.hostCache.getScriptSnapshot(filename);
984991
syntaxTree = this.updateSyntaxTree(filename, scriptSnapshot, this.currentFileSyntaxTree, this.currentFileVersion);
992+
993+
var editRange = this.hostCache.getScriptTextChangeRangeSinceVersion(filename, this.currentFileVersion);
994+
sourceFile = !editRange
995+
? createSourceFileFromScriptSnapshot(filename, scriptSnapshot, getDefaultCompilerOptions(), version, /*isOpen*/ true)
996+
: this.currentSourceFile.update(scriptSnapshot, version, /*isOpen*/ true, editRange);
997+
998+
fixupParentReferences(sourceFile);
985999
}
9861000

9871001
if (syntaxTree !== null) {
1002+
Debug.assert(sourceFile);
9881003
// All done, ensure state is up to date
9891004
this.currentFileScriptSnapshot = scriptSnapshot;
9901005
this.currentFileVersion = version;
9911006
this.currentfilename = filename;
9921007
this.currentFileSyntaxTree = syntaxTree;
1008+
this.currentSourceFile = sourceFile;
9931009
}
9941010

1011+
function fixupParentReferences(sourceFile: SourceFile) {
1012+
// normally parent references are set during binding.
1013+
// however here SourceFile data is used only for syntactic features so running the whole binding process is an overhead.
1014+
// walk over the nodes and set parent references
1015+
var parent: Node = sourceFile;
1016+
function walk(n: Node): void {
1017+
if (parent) {
1018+
n.parent = parent;
1019+
}
1020+
var saveParent = parent;
1021+
parent = n;
1022+
forEachChild(n, walk);
1023+
parent = saveParent;
1024+
}
1025+
forEachChild(sourceFile, walk);
1026+
}
1027+
}
1028+
1029+
public getCurrentFileSyntaxTree(filename: string): TypeScript.SyntaxTree {
1030+
this.initialize(filename);
9951031
return this.currentFileSyntaxTree;
9961032
}
9971033

1034+
public getCurrentSourceFile(filename: string): SourceFile {
1035+
this.initialize(filename);
1036+
return this.currentSourceFile;
1037+
}
1038+
9981039
public getCurrentScriptSnapshot(filename: string): TypeScript.IScriptSnapshot {
9991040
// update currentFileScriptSnapshot as a part of 'getCurrentFileSyntaxTree' call
10001041
this.getCurrentFileSyntaxTree(filename);
@@ -1093,6 +1134,10 @@ module ts {
10931134
}
10941135
}
10951136

1137+
function createSourceFileFromScriptSnapshot(filename: string, scriptSnapshot: TypeScript.IScriptSnapshot, settings: CompilerOptions, version: number, isOpen: boolean) {
1138+
return createSourceFile(filename, scriptSnapshot.getText(0, scriptSnapshot.getLength()), settings.target, version, isOpen);
1139+
}
1140+
10961141
export function createDocumentRegistry(): DocumentRegistry {
10971142
var buckets: Map<Map<DocumentRegistryEntry>> = {};
10981143

@@ -1140,7 +1185,7 @@ module ts {
11401185
var bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ true);
11411186
var entry = lookUp(bucket, filename);
11421187
if (!entry) {
1143-
var sourceFile = createSourceFile(filename, scriptSnapshot.getText(0, scriptSnapshot.getLength()), compilationSettings.target, version, isOpen);
1188+
var sourceFile = createSourceFileFromScriptSnapshot(filename, scriptSnapshot, compilationSettings, version, isOpen);
11441189

11451190
bucket[filename] = entry = {
11461191
sourceFile: sourceFile,
@@ -2024,6 +2069,12 @@ module ts {
20242069
return syntaxTreeCache.getCurrentFileSyntaxTree(filename);
20252070
}
20262071

2072+
function getCurrentSourceFile(filename: string): SourceFile {
2073+
filename = TypeScript.switchToForwardSlashes(filename);
2074+
var currentSourceFile = syntaxTreeCache.getCurrentSourceFile(filename);
2075+
return currentSourceFile;
2076+
}
2077+
20272078
function getNameOrDottedNameSpan(filename: string, startPos: number, endPos: number): SpanInfo {
20282079
function getTypeInfoEligiblePath(filename: string, position: number, isConstructorValidPosition: boolean) {
20292080
var sourceUnit = syntaxTreeCache.getCurrentFileSyntaxTree(filename).sourceUnit();
@@ -2100,8 +2151,8 @@ module ts {
21002151
function getOutliningRegions(filename: string) {
21012152
// doesn't use compiler - no need to synchronize with host
21022153
filename = TypeScript.switchToForwardSlashes(filename);
2103-
var syntaxTree = getSyntaxTree(filename);
2104-
return TypeScript.Services.OutliningElementsCollector.collectElements(syntaxTree.sourceUnit());
2154+
var sourceFile = getCurrentSourceFile(filename);
2155+
return OutliningElementsCollector.collectElements(sourceFile);
21052156
}
21062157

21072158
function getBraceMatchingAtPosition(filename: string, position: number) {
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/// <reference path="fourslash.ts"/>
2+
3+
////// interface
4+
////interface IFoo[| {
5+
//// getDist(): number;
6+
////}|]
7+
////
8+
////// class members
9+
////class Foo[| {
10+
//// constructor()[| {
11+
//// }|]
12+
////
13+
//// public foo(): number[| {
14+
//// return 0;
15+
//// }|]
16+
////
17+
//// public get X()[| {
18+
//// return 1;
19+
//// }|]
20+
////
21+
//// public set X(v: number)[| {
22+
//// }|]
23+
////
24+
//// public member = function f()[| {
25+
////
26+
//// }|]
27+
////}|]
28+
////
29+
////// modules
30+
////module m1[| {
31+
//// module m2[| { }|]
32+
//// module m3[| {
33+
//// function foo()[| {
34+
////
35+
//// }|]
36+
////
37+
//// interface IFoo2[| {
38+
////
39+
//// }|]
40+
////
41+
//// class foo2 implements IFoo2[| {
42+
////
43+
//// }|]
44+
//// }|]
45+
////}|]
46+
////
47+
////// function declaration
48+
////function foo(): number[| {
49+
//// return 0;
50+
////}|]
51+
////
52+
////// function expressions
53+
////(function f()[| {
54+
////
55+
////}|])
56+
////
57+
////// trivia handeling
58+
////class ClassFooWithTrivia[| /* some comments */
59+
//// /* more trivia */ {
60+
////
61+
////
62+
//// /*some trailing trivia */
63+
////}|] /* even more */
64+
////
65+
////// object literals
66+
////var x = [|{
67+
//// a:1,
68+
//// b:2,
69+
//// get foo() [|{
70+
//// return 1;
71+
//// }|]
72+
////}|]
73+
//////outline with deep nesting
74+
////module m1[|{
75+
//// module m2[| {
76+
//// module m3[| {
77+
//// module m4[| {
78+
//// module m5[| {
79+
//// module m6[| {
80+
//// module m7[| {
81+
//// module m8[| {
82+
//// module m9[| {
83+
//// module m10[| {
84+
//// module m11 {
85+
//// module m12 {
86+
//// export interface IFoo {
87+
//// }
88+
//// }
89+
//// }
90+
//// }|]
91+
//// }|]
92+
//// }|]
93+
//// }|]
94+
//// }|]
95+
//// }|]
96+
//// }|]
97+
//// }|]
98+
//// }|]
99+
////}|]
100+
////
101+
//////outline after a deeply nested node
102+
////class AfterNestedNodes[| {
103+
////}|]
104+
105+
verify.outliningSpansInCurrentFile(test.ranges());

0 commit comments

Comments
 (0)