-
Notifications
You must be signed in to change notification settings - Fork 63
Expand file tree
/
Copy pathXmlScope.ts
More file actions
134 lines (122 loc) · 5.3 KB
/
Copy pathXmlScope.ts
File metadata and controls
134 lines (122 loc) · 5.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import { Scope } from './Scope';
import type { XmlFile } from './files/XmlFile';
import type { Program } from './Program';
import util from './util';
import { SymbolTypeFlag } from './SymbolTypeFlag';
import type { BscFile } from './files/BscFile';
import type { BaseFunctionType } from './types/BaseFunctionType';
import { ComponentType } from './types/ComponentType';
import type { ExtraSymbolData } from './interfaces';
import { ParseMode, Parser } from './parser/Parser';
import { isTypeExpression } from './astUtils/reflection';
import { DynamicType } from './types';
export class XmlScope extends Scope {
constructor(
public xmlFile: XmlFile,
public program: Program
) {
super(xmlFile.destPath, program);
}
private typeParser = new Parser();
public get dependencyGraphKey() {
return this.xmlFile.dependencyGraphKey;
}
/**
* Get the parent scope of this scope. If we could find the scope for our parentComponent, use that.
* Otherwise default to global scope
*/
public getParentScope() {
return this.cache.getOrAdd('parentScope', () => {
let scope: Scope | undefined;
let parentComponentName = this.xmlFile.parentComponentName?.text;
if (parentComponentName) {
scope = this.program.getComponentScope(parentComponentName);
}
if (scope) {
return scope;
} else {
return this.program.globalScope ?? null;
}
});
}
public getComponentType() {
let componentElement = this.xmlFile.ast.componentElement;
if (!componentElement?.name) {
return;
}
const parentComponentType = componentElement?.extends
? this.symbolTable.getSymbolType(util.getSgNodeTypeName(componentElement?.extends), { flags: SymbolTypeFlag.typetime, fullName: componentElement?.extends, tableProvider: () => this.symbolTable })
: undefined;
const result = new ComponentType(componentElement.name, parentComponentType as ComponentType);
result.addBuiltInInterfaces();
const iface = componentElement.interfaceElement;
if (!iface) {
return result;
}
//add functions
for (const func of iface.functions ?? []) {
if (func.name) {
const extraData: ExtraSymbolData = {};
const componentFuncType = this.symbolTable.getSymbolType(func.name, { flags: SymbolTypeFlag.runtime, data: extraData, fullName: func.name, tableProvider: () => this.symbolTable });
result.addCallFuncMember(func.name, extraData, componentFuncType as BaseFunctionType, SymbolTypeFlag.runtime, () => this.symbolTable);
}
}
//add fields
for (const field of iface.fields ?? []) {
if (field.id) {
let actualFieldType = field.type ? util.getNodeFieldType(field.type, this.symbolTable, false) : DynamicType.instance;
if (!actualFieldType && field.type) {
//try to parse the type as an expression, this allows for more complex types like arrays or interfaces
try {
const typeAttrValue = field.attributes.find(attr => attr.key === 'type')?.tokens?.value;
const parsed = this.typeParser.parse(field.type ?? '', {
mode: ParseMode.BrighterScript,
srcPath: this.xmlFile.srcPath,
typeOnly: true,
rangeOffset: typeAttrValue?.location?.range.start
});
const ast = parsed?.ast;
const typeExpression = ast?.statements[0];
if (isTypeExpression(typeExpression)) {
actualFieldType = typeExpression.getType({ flags: SymbolTypeFlag.typetime });
}
if (parsed?.diagnostics.length) {
this.program?.diagnostics.register(parsed.diagnostics);
}
} catch (e) {
}
}
field.bscType = actualFieldType;
//TODO: add documentation - need to get previous comment from XML
result.addMember(field.id, {}, actualFieldType, SymbolTypeFlag.runtime);
}
}
return result;
}
public getAllFiles() {
return this.cache.getOrAdd('getAllFiles-xmlScope', () => {
const allFiles = super.getAllFiles();
allFiles.push(this.xmlFile);
return allFiles;
});
}
/**
* Get the list of files referenced by this scope that are actually loaded in the program.
* This does not account for parent scope.
*/
public getOwnFiles() {
return this.cache.getOrAdd('getOwnFiles', () => {
let result = [
this.xmlFile
] as BscFile[];
let scriptDestPaths = this.xmlFile.getOwnDependencies();
for (let destPath of scriptDestPaths) {
let file = this.program.getFile(destPath, false);
if (file) {
result.push(file);
}
}
return result;
});
}
}