-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathWorkspace.ts
153 lines (119 loc) · 5.65 KB
/
Workspace.ts
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import { IncludeStatement } from 'autoit3-pegjs';
import { Connection, Diagnostic } from 'vscode-languageserver';
import { URI, Utils } from 'vscode-uri';
import Script from "./Script";
export type scriptList = {
[uri: string]: Script | undefined,
}
type uri = string | URI;
export type diagnosticsListner = (uri: string, diagnostics: Array<Diagnostic>) => void;
export type IncludeResolve = {uri: URI, text: string};
export type IncludePromise = Promise<IncludeResolve|null>;
export type AutoIt3Configuration = {
"installDir": string|null,
"userDefinedLibraries": string[],
"version": string,
};
export class Workspace {
protected scripts: scriptList = {};
protected connection: Connection|null;
protected diagnosticsListners: Array<diagnosticsListner> = [];
constructor(connection: Connection|null = null) {
this.connection = connection;
}
public getConnection(): Connection|null {
return this.connection;
}
public onDiagnostics(fn: diagnosticsListner): void {
this.diagnosticsListners.push(fn);
}
public triggerDiagnostics(uri: string, diagnostics: Array<Diagnostic>): void {
for (const fn of this.diagnosticsListners) {
fn.call(this, uri, diagnostics);
}
}
public add(script: Script): void {
const uri = script.getUri();
if (uri === undefined) {
throw new Error("No URI defined on script object");
}
script.workspace = this;
this.scripts[uri.toString()] = script;
}
public get(uri: uri): Script | undefined {
return this.scripts[uri.toString()];
}
public exists(uri: uri) {
return uri.toString() in this.scripts;
}
public createOrUpdate(uri: uri, text: string): Script {
uri = uri.toString();
let script = this.scripts?.[uri];
if (script !== undefined) {
script.update(text);
return script;
}
script = new Script(text, URI.parse(uri), this);
this.add(script);
script.triggerDiagnostics();
return script;
}
public remove(uri: uri): void {
delete this.scripts[uri.toString()];
}
/**
* Get first declaration statement for matching identifier
* @param uri file uri
* @param identifier identifier to match declarator
* @param includes if includes should be searched as well.
*/
/*getIdentifierDeclarator(uri: string, identifier: Identifier|VariableIdentifier|Macro|null): FunctionDeclaration|VariableDeclaration|null {
if (identifier?.type === "Macro") {
return null;
}
//return this._getIdentifierDeclarator(uri, identifier) ?? (includes ? this.getIdentifierDeclaratorFromIncludes(uri, identifier) : null);
return includes ? this.getIdentifierDeclaratorFromIncludes(uri, identifier) : this._getIdentifierDeclarator(uri, identifier);
}*/
public resolveInclude(include: IncludeStatement): Promise<IncludeResolve | null> {
let promise = this.connection?.workspace.getConfiguration("autoit3").then((configuration: AutoIt3Configuration) => {
let promise:IncludePromise = Promise.resolve(null);
const fileUri = include.file.replace(/\\/g, '/');
promise = include.library ? this.includeLibrary(fileUri, promise, configuration) : this.includeLocal(fileUri, include.location.source, promise);
promise = this.includeUserDefined(fileUri, promise, configuration);
promise = !include.library ? this.includeLibrary(fileUri, promise, configuration) : this.includeLocal(fileUri, include.location.source, promise);
return promise;
}) ?? Promise.resolve(null);
/*
//FIXME: check if this is needed in Workspace class (from FileAstMap)
promise = promise.then(x => {
if (x !== null) {
if (this.exists(x.uri.toString())) {
this.maps[x.uri.toString()].counter++;
} else {
this.add(x.uri.toString(), this.parse(x.text, x.uri.toString()));
}
}
return x;
});
*/
return promise;
}
protected includeLibrary(uri: string, promise: IncludePromise, configuration: AutoIt3Configuration|null): IncludePromise {
return promise.then(x => x === null && typeof configuration?.installDir === "string" ? this.openTextDocument(Utils.resolvePath(URI.file(configuration.installDir), 'Include', uri)) : x);
}
protected includeUserDefined(uri: string, promise: IncludePromise, configuration: AutoIt3Configuration|null): IncludePromise {
for (const path of configuration?.userDefinedLibraries ?? []) {
promise = promise.then(x => x === null ? this.openTextDocument(Utils.resolvePath(URI.file(path), uri)) : null);
}
return promise;
}
protected includeLocal(uri: string, documentUri: string, promise: IncludePromise): IncludePromise {
//HACK: currently i check if the documentUri startsWith 'untitled:' to detect files not yet saved to disk. I cannot find a better solution so far...
return promise.then(x => x === null && !documentUri.startsWith('untitled:') ? this.openTextDocument(Utils.resolvePath(Utils.dirname(URI.parse(documentUri)), uri)) : x);
}
protected openTextDocument(uri: URI): IncludePromise {
const promise = this.connection?.sendRequest<string|null>("openTextDocument", uri.toString()).then<IncludeResolve|null>(x => x === null ? x : ({uri: uri, text: x})) ?? Promise.resolve(null);
promise.then(value => {if (value !== null) {this.createOrUpdate(value.uri, value.text);}} );
return promise;
}
}