1// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. 2// See LICENSE.txt in the project root for complete license information. 3 4///<reference path='typescript.ts' /> 5 6module TypeScript { 7 export class TypeCollectionContext { 8 public script: Script = null; 9 10 constructor (public scopeChain: ScopeChain, public checker: TypeChecker) { 11 } 12 } 13 14 export class MemberScopeContext { 15 public type: Type = null; 16 public ast: AST = null; 17 public scope: SymbolScope; 18 public options = new AstWalkOptions(); 19 20 constructor (public flow: TypeFlow, public pos: number, public matchFlag: ASTFlags) { 21 } 22 } 23 24 export class EnclosingScopeContext { 25 26 public scopeGetter: () => SymbolScope = null; 27 public objectLiteralScopeGetter: () => SymbolScope = null; 28 public scopeStartAST: AST = null; 29 public skipNextFuncDeclForClass = false; 30 public deepestModuleDecl: ModuleDeclaration = null; 31 public enclosingClassDecl: TypeDeclaration = null; 32 public enclosingObjectLit: UnaryExpression = null; 33 public publicsOnly = true; 34 public useFullAst = false; 35 private scriptFragment: Script; 36 37 constructor (public logger: ILogger, 38 public script: Script, 39 public text: ISourceText, 40 public pos: number, 41 public isMemberCompletion: boolean) { 42 } 43 44 public getScope(): SymbolScope { 45 return this.scopeGetter(); 46 } 47 48 public getObjectLiteralScope(): SymbolScope { 49 return this.objectLiteralScopeGetter(); 50 } 51 52 public getScopeAST() { 53 return this.scopeStartAST; 54 } 55 56 public getScopePosition() { 57 return this.scopeStartAST.minChar; 58 } 59 60 public getScriptFragmentStartAST(): AST { 61 return this.scopeStartAST; 62 } 63 64 public getScriptFragmentPosition(): number { 65 return this.getScriptFragmentStartAST().minChar; 66 } 67 68 public getScriptFragment(): Script { 69 if (this.scriptFragment == null) { 70 var ast = this.getScriptFragmentStartAST(); 71 var minChar = ast.minChar; 72 var limChar = (this.isMemberCompletion ? this.pos : this.pos + 1); 73 this.scriptFragment = TypeScript.quickParse(this.logger, ast, this.text, minChar, limChar, null/*errorCapture*/).Script; 74 } 75 return this.scriptFragment; 76 } 77 } 78 79 export function preFindMemberScope(ast: AST, parent: AST, walker: IAstWalker) { 80 var memScope: MemberScopeContext = walker.state; 81 if (hasFlag(ast.flags, memScope.matchFlag) && ((memScope.pos < 0) || (memScope.pos == ast.limChar))) { 82 memScope.ast = ast; 83 if ((ast.type == null) && (memScope.pos >= 0)) { 84 memScope.flow.inScopeTypeCheck(ast, memScope.scope); 85 } 86 memScope.type = ast.type; 87 memScope.options.stopWalk(); 88 } 89 return ast; 90 } 91 92 export function pushTypeCollectionScope(container: Symbol, 93 valueMembers: ScopedMembers, 94 ambientValueMembers: ScopedMembers, 95 enclosedTypes: ScopedMembers, 96 ambientEnclosedTypes: ScopedMembers, 97 context: TypeCollectionContext, 98 thisType: Type, 99 classType: Type, 100 moduleDecl: ModuleDeclaration) { 101 var builder = new SymbolScopeBuilder(valueMembers, ambientValueMembers, enclosedTypes, ambientEnclosedTypes, null, container); 102 var chain: ScopeChain = new ScopeChain(container, context.scopeChain, builder); 103 chain.thisType = thisType; 104 chain.classType = classType; 105 chain.moduleDecl = moduleDecl; 106 context.scopeChain = chain; 107 } 108 109 export function popTypeCollectionScope(context: TypeCollectionContext) { 110 context.scopeChain = context.scopeChain.previous; 111 } 112 113 export function preFindEnclosingScope(ast: AST, parent: AST, walker: IAstWalker) { 114 var context: EnclosingScopeContext = walker.state; 115 var minChar = ast.minChar; 116 var limChar = ast.limChar; 117 118 // Account for the fact completion list may be called at the end of a file which 119 // is has not been fully re-parsed yet. 120 if (ast.nodeType == NodeType.Script && context.pos > limChar) 121 limChar = context.pos; 122 123 if ((minChar <= context.pos) && 124 (limChar >= context.pos)) { 125 switch (ast.nodeType) { 126 case NodeType.Script: 127 var script = <Script>ast; 128 context.scopeGetter = function () { 129 return script.bod === null ? null : script.bod.enclosingScope; 130 }; 131 context.scopeStartAST = script; 132 break; 133 134 case NodeType.ClassDeclaration: 135 context.scopeGetter = function () { 136 return (ast.type === null || ast.type.instanceType.containedScope === null) ? null : ast.type.instanceType.containedScope; 137 }; 138 context.scopeStartAST = ast; 139 context.enclosingClassDecl = <TypeDeclaration>ast; 140 break; 141 142 case NodeType.ObjectLit: 143 var objectLit = <UnaryExpression>ast; 144 // Only consider target-typed object literals 145 if (objectLit.targetType) { 146 context.scopeGetter = function () { 147 return objectLit.targetType.containedScope; 148 }; 149 context.objectLiteralScopeGetter = function () { 150 return objectLit.targetType.memberScope; 151 } 152 context.enclosingObjectLit = objectLit; 153 } 154 break; 155 156 case NodeType.ModuleDeclaration: 157 context.deepestModuleDecl = <ModuleDeclaration>ast; 158 context.scopeGetter = function () { 159 return ast.type === null ? null : ast.type.containedScope; 160 }; 161 context.scopeStartAST = ast; 162 break; 163 164 case NodeType.InterfaceDeclaration: 165 context.scopeGetter = function () { 166 return (ast.type === null) ? null : ast.type.containedScope; 167 }; 168 context.scopeStartAST = ast; 169 break; 170 171 case NodeType.FuncDecl: { 172 var funcDecl = <FuncDecl>ast; 173 if (context.skipNextFuncDeclForClass) { 174 context.skipNextFuncDeclForClass = false; 175 } 176 else { 177 context.scopeGetter = function () { 178 // The scope of a class constructor is hidden somewhere we don't expect :-S 179 if (funcDecl.isConstructor && hasFlag(funcDecl.fncFlags, FncFlags.ClassMethod)) { 180 if (ast.type && ast.type.enclosingType) { 181 return ast.type.enclosingType.constructorScope; 182 } 183 } 184 185 if (funcDecl.scopeType) { 186 return funcDecl.scopeType.containedScope; 187 } 188 189 if (funcDecl.type) { 190 return funcDecl.type.containedScope; 191 } 192 return null; 193 }; 194 context.scopeStartAST = ast; 195 } 196 } 197 break; 198 } 199 walker.options.goChildren = true; 200 } 201 else { 202 walker.options.goChildren = false; 203 } 204 return ast; 205 } 206 207 // 208 // Find the enclosing scope context from a position inside a script AST. 209 // The "scopeStartAST" of the returned scope is always valid. 210 // Return "null" if the enclosing scope can't be found. 211 // 212 export function findEnclosingScopeAt(logger: ILogger, script: Script, text: ISourceText, pos: number, isMemberCompletion: boolean): EnclosingScopeContext { 213 var context = new EnclosingScopeContext(logger, script, text, pos, isMemberCompletion); 214 215 TypeScript.getAstWalkerFactory().walk(script, preFindEnclosingScope, null, null, context); 216 217 if (context.scopeStartAST === null) 218 return null; 219 return context; 220 } 221}