Skip to content

Commit 4400334

Browse files
committed
Eliminate assumption of common class path
- Allows the vscode workspace to be misaligned with respect to the class path - Allows files from multiple, independent projects to be opened at once, and referenced files to be found correctly - Wildcard imports are also now handled more efficiently Fixes #7
1 parent 8cafab9 commit 4400334

File tree

2 files changed

+43
-54
lines changed

2 files changed

+43
-54
lines changed

server/src/action-context.ts

Lines changed: 29 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,13 @@ const stringMatcher = /(?:".*?"|'.*?'|["'].*?$)/g; //Matches st
1414
const braceMatcher = /(?:{(?:(?!{).)*?}|{(?:(?!{).)*?$)/g; //Matches well paired braces (complete or open to end of string)
1515
const bracketMatcher = /(?:\[(?:(?!\[).)*?\]|\[(?:(?!\[).)*?$)/g; //Matches well paired square brackets (complete or open to end of string)
1616
const matchedParens = /(?:\((?:(?!\().)*?\))/g; //Matches well paired parentheses
17-
const slashMatcher = /\//g; //Matches forward slashes
18-
const asExtensionMatcher = /\.as$/i; //Matches strings ending in .as
1917

2018
interface SymbolChainLink { identifier: string; called: boolean; }
2119

2220
export class ActionContext {
2321

24-
public static rootPath: string = ''; //TODO: Need a more accurate value for this to avoid i/o errors when the vscode folder is not the class root
25-
2622
private static _classes: ActionClass[] = [];
23+
private static _wildcardImports: { [path: string]: Promise<string[]> } = Object.create(null);
2724
private static _classLookup: { [fullTypeOrUri: string]: ActionClass } = Object.create(null);
2825
private static _intrinsicImports: { [shortType: string]: ActionClass } = Object.create(null);
2926
private static _globalCompletions: CompletionItem[] = [];
@@ -85,7 +82,7 @@ export class ActionContext {
8582
}
8683
}
8784
returnType = nextClass.importMap[returnType] || returnType;
88-
nextClass = await this.getClassByFullType(returnType);
85+
nextClass = await this.getClassByFullType(returnType, nextClass.baseUri);
8986
if (symbolCalled && nextClass.constructorMethod) {
9087
nextVisibility = VisibilityFilter.PUBLIC_INSTANCE;
9188
}
@@ -124,7 +121,7 @@ export class ActionContext {
124121

125122
let member: ActionParameter;
126123
if (ambientClass.superClass && chainLength === 1 && symbolChain[0].identifier === 'super') {
127-
let superClass = await this.getClassByFullType(ambientClass.superClass);
124+
let superClass = await this.getClassByFullType(ambientClass.superClass, ambientClass.baseUri);
128125
member = superClass && superClass.constructorMethod;
129126
} else {
130127
let memberAndClass = await this.traverseSymbolChainToMember(symbolChain, ambientClass, lineIndex);
@@ -247,7 +244,7 @@ export class ActionContext {
247244
symbolName = symbolChain[0].identifier;
248245
returnType = ambientClass.importMap[symbolName];
249246
if (returnType) {
250-
return [null, await this.getClassByFullType(returnType)];
247+
return [null, await this.getClassByFullType(returnType, ambientClass.baseUri)];
251248
} else if (this._intrinsicImports[symbolName]) {
252249
return [null, this._intrinsicImports[symbolName]];
253250
}
@@ -287,7 +284,7 @@ export class ActionContext {
287284
}
288285
}
289286
returnType = nextClass.importMap[returnType] || returnType;
290-
nextClass = await this.getClassByFullType(returnType);
287+
nextClass = await this.getClassByFullType(returnType, nextClass.baseUri);
291288
if (symbolCalled && nextClass.constructorMethod) {
292289
nextVisibility = VisibilityFilter.PUBLIC_INSTANCE;
293290
}
@@ -373,7 +370,7 @@ export class ActionContext {
373370
}
374371

375372
if (actionClass.superClass) {
376-
let superActionClass = await this.getClassByFullType(actionClass.superClass);
373+
let superActionClass = await this.getClassByFullType(actionClass.superClass, actionClass.baseUri);
377374
if (superActionClass) {
378375
completionItems = completionItems.concat(await this.getInnerSymbols(superActionClass, visibilityFilter, null, false, skipMap, rank));
379376
}
@@ -451,27 +448,18 @@ export class ActionContext {
451448
this._classLookup[actionClass.fileUri] = actionClass;
452449
}
453450

454-
public static getClassByFullType(fullType: string): Thenable<ActionClass> {
455-
if (! fullType) {
456-
return Promise.resolve(null);
457-
}
458-
if (this._classLookup[fullType]) {
459-
return Promise.resolve(this._classLookup[fullType]);
460-
}
461-
return this.requestClassParse(fullType);
462-
}
463-
464-
public static requestClassParse(fullType: string, forceReparse: boolean = false): Thenable<ActionClass> {
465-
ActionConfig.LOG_LEVEL !== LogLevel.NONE && logIt({ level: LogLevel.INFO, message: `request class parse! ${fullType}` });
451+
public static getClassByFullType(fullType: string, basePath: string, forceReparse = false): Thenable<ActionClass> {
452+
ActionConfig.LOG_LEVEL !== LogLevel.NONE && logIt({ level: LogLevel.VERBOSE, message: `Request class parse! ${fullType}` });
466453

467454
if (fullType in this._classLookup && !forceReparse) {
468455
return Promise.resolve(this._classLookup[fullType]);
469456
} else {
470-
let path = this.typeOrPackageToPath(fullType);
471-
ActionConfig.LOG_LEVEL !== LogLevel.NONE && logIt({ level: LogLevel.INFO, message: `Type to path: ${path}` });
457+
let path = this.typeOrPackageToPath(fullType, basePath);
458+
ActionConfig.LOG_LEVEL !== LogLevel.NONE && logIt({ level: LogLevel.VERBOSE, message: `Type to path: ${path}` });
472459
let promises: [PromiseLike<any>, Promise<string>] = [ActionParser.initialise(), LoadQueue.enqueue(path)];
473460
return Promise.all(promises)
474461
.then(res => {
462+
if (! res) return null;
475463
let ac = ActionParser.parseFile(path, res[1]);
476464
this.registerClass(ac);
477465
return ac;
@@ -480,18 +468,22 @@ export class ActionContext {
480468
}
481469
}
482470

483-
public static requestPackageParse(packageDescriptor: string, forceReparse: boolean = false): Promise<string[]> {
484-
return DirectoryUtility.fileNamesInDirectory(this.typeOrPackageToPath(packageDescriptor), /\.as$/i)
485-
.then(fileNames => fileNames.map(fileName => {
486-
let fullType = this.pathToFullType(fileName);
487-
if (fullType && (forceReparse || !this._classLookup[fullType])) {
488-
let path = this.typeOrPackageToPath(fullType);
489-
LoadQueue.enqueue(path)
490-
.then(contents => this.registerClass(ActionParser.parseFile(path, contents)))
491-
.catch(e => ActionConfig.LOG_LEVEL !== LogLevel.NONE && logIt({ level: LogLevel.ERROR, message: `Load error! ${path} ${e}` }));
492-
}
493-
return fullType;
494-
}));
471+
public static requestPackageParse(packageDescriptor: string, basePath: string, forceReparse = false): Promise<string[]> {
472+
let lookupKey = basePath + packageDescriptor;
473+
if (!forceReparse && lookupKey in this._wildcardImports) {
474+
return this._wildcardImports[lookupKey];
475+
}
476+
this._wildcardImports[lookupKey] = DirectoryUtility.fileNamesInDirectory(this.typeOrPackageToPath(packageDescriptor, basePath), /\.as$/i)
477+
.then(fileNames => Promise.all(fileNames.map(fileName => {
478+
return LoadQueue.enqueue(fileName)
479+
.then(contents => {
480+
if (! contents) return null;
481+
let parsedClass = ActionParser.parseFile(fileName, contents);
482+
this.registerClass(parsedClass);
483+
return parsedClass.fullType;
484+
});
485+
})));
486+
return this._wildcardImports[lookupKey];
495487
}
496488

497489
public static fullTypeToPackage(fullType: string): string {
@@ -506,26 +498,14 @@ export class ActionContext {
506498
return rawType.indexOf('.') === -1;
507499
}
508500

509-
public static typeOrPackageToPath(typeDescriptor: string): string {
501+
public static typeOrPackageToPath(typeDescriptor: string, basePath: string): string {
510502
let isPackage = this.fullTypeToShortType(typeDescriptor) === '*';
511-
let path = DirectoryUtility.concatPaths(this.rootPath, DirectoryUtility.typeToPath(typeDescriptor));
503+
let path = DirectoryUtility.concatPaths(basePath, DirectoryUtility.typeToPath(typeDescriptor));
512504
if (isPackage) {
513505
return path.substring(0, path.length-1);
514506
} else {
515507
return path + '.as';
516508
}
517509
}
518510

519-
public static pathToFullType(path: string): string {
520-
if (path.indexOf(this.rootPath) !== 0) {
521-
ActionConfig.LOG_LEVEL !== LogLevel.NONE && logIt({ level: LogLevel.WARNING, message: `Can't determine type from path: '${path}' as it does not begin with the current rootPath: '${this.rootPath}'!` });
522-
return '';
523-
}
524-
if (path.substr(-3).toLowerCase() !== '.as') {
525-
ActionConfig.LOG_LEVEL !== LogLevel.NONE && logIt({ level: LogLevel.WARNING, message: `Unexpected file extension in path '${path}'!` });
526-
return '';
527-
}
528-
return path.substring(this.rootPath.length).replace(slashMatcher, '.').replace(asExtensionMatcher, '');
529-
}
530-
531511
}

server/src/action-elements.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { ActionContext } from './action-context';
22
import { logIt, LogLevel, ActionConfig } from './config';
33
import { SignatureInformation } from 'vscode-languageserver';
4+
import { DirectoryUtility } from './file-system-utilities';
45

56
export class ActionClass {
67

78
public shortType: string;
89
public fullType: string;
910
public fileUri: string;
11+
public baseUri: string;
1012
public isIntrinsic: boolean;
1113
public superClass: string;
1214
public parentPackage: string;
@@ -60,17 +62,23 @@ export class ActionClass {
6062
}
6163
}
6264

63-
public registerRegularImport(fullType: string, shortType?: string): void {
65+
public registerRegularImport(fullType: string, shortType?: string, skipParse?: boolean): void {
6466
shortType = shortType || ActionContext.fullTypeToShortType(fullType);
6567
this._imports.push(fullType);
6668
this._importMap[shortType] = fullType;
67-
ActionContext.requestClassParse(fullType);
69+
if (! skipParse) {
70+
ActionContext.getClassByFullType(fullType, this.baseUri, false);
71+
}
6872
}
6973

7074
public registerWildcardImport(wildcardImport: string): void {
7175
this._wildcardImports.push(wildcardImport);
72-
ActionContext.requestPackageParse(wildcardImport)
73-
.then(packageMembers => packageMembers.forEach(fullType => this.registerRegularImport(fullType)));
76+
ActionContext.requestPackageParse(wildcardImport, this.baseUri, false)
77+
.then(packageMembers => packageMembers.forEach(importType => {
78+
if (importType && importType !== this.fullType) {
79+
this.registerRegularImport(importType, null, true);
80+
}
81+
}));
7482
}
7583

7684
public getMemberByName(memberName: string, filter: VisibilityFilter = VisibilityFilter.PUBLIC_INSTANCE, lineNumber: number = null): PromiseLike<ActionParameter> {
@@ -103,7 +111,7 @@ export class ActionClass {
103111
if (lookup[memberName]) {
104112
return Promise.resolve(lookup[memberName]);
105113
} else if (this.superClass) {
106-
return ActionContext.getClassByFullType(this.superClass)
114+
return ActionContext.getClassByFullType(this.superClass, this.baseUri, false)
107115
.then(parsedSuperClass => parsedSuperClass && parsedSuperClass.getMemberByName(memberName, filter));
108116
}
109117
return null;
@@ -146,6 +154,7 @@ export class ActionClass {
146154
});
147155
}
148156
this._importMap[this.shortType] = this.fullType;
157+
this.baseUri = DirectoryUtility.fileUriAndTypeToBaseUri(this.fileUri, this.fullType);
149158
}
150159

151160
public get publicMembers(): ActionParameter[] { return this._publicInstanceMembers; }

0 commit comments

Comments
 (0)