Skip to content

Commit 8cef26e

Browse files
committed
2 parents b1068a8 + a1f6670 commit 8cef26e

File tree

4 files changed

+332
-60
lines changed

4 files changed

+332
-60
lines changed

server/src/docInfo.ts

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { TextDocument } from 'vscode-languageserver-textdocument';
33
import { LiteralContext } from './antlr/out/vbaParser';
44
import { SemanticToken, sortSemanticTokens } from './capabilities/vbaSemanticTokens';
55
import { sleep, rangeIsChildOfElement } from './utils/helpers';
6-
import { IdentifierElement, MethodElement, ModuleAttribute, ModuleElement, SyntaxElement, VariableAssignElement, VariableDeclarationElement, VariableStatementElement } from './utils/vbaSyntaxElements';
6+
import { IdentifiableSyntaxElement, IdentifierElement, MethodElement, ModuleAttribute, ModuleElement, SyntaxElement, VariableDeclarationElement, VariableStatementElement } from './utils/vbaSyntaxElements';
77
import { ResultsContainer, SyntaxParser } from './utils/vbaSyntaxParser';
88

99

@@ -151,15 +151,14 @@ export class DocumentInformation implements ResultsContainer {
151151
elements: SyntaxElement[] = [];
152152
attrubutes: Map<string, string> = new Map();
153153
isBusy = true;
154+
scope: Scope;
154155

155156
private docUri: string;
156157
private ancestors: SyntaxElement[] = [];
157-
private localNames: Map<string, SyntaxElement> = new Map();
158-
private documentScope: Scope;
159158

160159
constructor(scope: Scope, docUri: string) {
161160
scope.links.set(docUri, new Map());
162-
this.documentScope = scope;
161+
this.scope = scope;
163162
this.docUri = docUri;
164163
}
165164

@@ -208,15 +207,27 @@ export class DocumentInformation implements ResultsContainer {
208207
// emt.hoverText = hoverText;
209208
// }
210209

211-
addScopeDeclaration(emt: MethodElement | VariableDeclarationElement) {
210+
/**
211+
* Use this method to set as the current scope.
212+
* @param emt The function, sub, or property to scope to.
213+
*/
214+
pushScopeElement(emt: MethodElement) {
215+
this.addScopedDeclaration(emt);
216+
}
217+
218+
popScopeElement() {
219+
//
220+
}
221+
222+
addScopedDeclaration(emt: MethodElement | VariableDeclarationElement) {
212223
// Add a declared scope.
213224
// this.addElement(emt);
214225
const elId = emt.identifier!.text;
215226
const link = this.getNameLink(elId, emt.parent?.fqName ?? '', emt.hasPrivateModifier);
216227
link.declarations.push(emt);
217228

218229
// Check the undeclared links and merge if found.
219-
const undeclaredScope = this.documentScope.getScope(`undeclared|${this.docUri}`);
230+
const undeclaredScope = this.scope.getScope(`undeclared|${this.docUri}`);
220231
const undeclaredLink = undeclaredScope.get(elId);
221232
if (undeclaredLink) {
222233
link.merge(undeclaredLink);
@@ -226,7 +237,7 @@ export class DocumentInformation implements ResultsContainer {
226237
this.addElement(emt);
227238
}
228239

229-
addScopeReference(emt: VariableAssignElement) {
240+
addScopedReference(emt: IdentifierElement) {
230241
const link = this.getNameLink(emt.identifier!.text, emt.parent?.fqName ?? '', false, true);
231242
link.references.push(emt);
232243
this.addElement(emt);
@@ -247,8 +258,8 @@ export class DocumentInformation implements ResultsContainer {
247258
}
248259

249260
private getScope(fqName: string, isPrivate: boolean): Map<string, NameLink> {
250-
const globalScope = this.documentScope.getScope('global');
251-
const localScope = this.documentScope.getScope(this.docUri);
261+
const globalScope = this.scope.getScope('global');
262+
const localScope = this.scope.getScope(this.docUri);
252263

253264
const isAtModuleLevel = (fqName ?? '') === this.docUri;
254265
return (isAtModuleLevel && !isPrivate) ? globalScope : localScope;
@@ -257,17 +268,17 @@ export class DocumentInformation implements ResultsContainer {
257268
private searchScopes(identifier: string): Map<string, NameLink> {
258269
const scopeNames = [this.docUri, 'global'];
259270
for (let i = 0; i < scopeNames.length; i++) {
260-
const scope = this.documentScope.getScope(scopeNames[i]);
271+
const scope = this.scope.getScope(scopeNames[i]);
261272
if (scope.has(identifier)) {
262273
return scope;
263274
}
264275
}
265-
return this.documentScope.getScope(`undeclared|${this.docUri}`);
276+
return this.scope.getScope(`undeclared|${this.docUri}`);
266277
}
267278

268279

269280
finalise() {
270-
this.documentScope.processLinks(this.docUri, true);
281+
this.scope.processLinks(this.docUri, true);
271282
this.isBusy = false;
272283
}
273284

@@ -366,18 +377,32 @@ export class DocumentInformation implements ResultsContainer {
366377
class Scope {
367378
// { docUri: { identifier: [ declarationElement, elementLink... ] } }
368379
links: Map<string, Map<string, NameLink>> = new Map();
380+
private subScopes: string[] = [];
381+
private currentDoc = '';
369382

370383
constructor() {
371384
this.links.set('global', new Map());
372385
}
373386

374387
getScope(key: string): Map<string, NameLink> {
388+
if (key !== this.currentDoc) {
389+
this.currentDoc = key;
390+
this.subScopes = ['module'];
391+
}
375392
if (!this.links.has(key)) {
376393
this.links.set(key, new Map());
377394
}
378395
return this.links.get(key)!;
379396
}
380397

398+
pushSubScope(namespace: string) {
399+
this.subScopes.push(namespace);
400+
}
401+
402+
popSubScope() {
403+
this.subScopes.pop();
404+
}
405+
381406
processLinks(key: string, optExplicit = false) {
382407
// TODO: check global for undeclareds
383408
const undeclared = this.getScope(`undeclared|${key}`);
@@ -482,4 +507,31 @@ class NameLink {
482507
private validateMethodSignatures() {
483508
// TODO: implement.
484509
}
510+
}
511+
512+
class Scope2 {
513+
context: SyntaxElement;
514+
parent?: Scope2;
515+
nameRefs: Map<string, NameLink> = new Map();
516+
517+
constructor(ctx: SyntaxElement);
518+
constructor(ctx: SyntaxElement, pnt: Scope2);
519+
constructor(ctx: SyntaxElement, pnt?: Scope2) {
520+
this.context = ctx;
521+
this.parent = pnt;
522+
}
523+
524+
addRef(ctx: IdentifiableSyntaxElement) {
525+
const nameLink = this.getName(ctx.identifier.text);
526+
// if dec
527+
528+
// if not dec
529+
}
530+
531+
getName(identifier: string): NameLink {
532+
if (!this.nameRefs.has(identifier)) {
533+
this.nameRefs.set(identifier, new NameLink());
534+
}
535+
return this.nameRefs.get(identifier)!;
536+
}
485537
}

server/src/utils/nameTree.ts

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import { Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity } from 'vscode-languageserver';
2+
import { AmbiguousIdentifierContext, CertainIdentifierContext } from '../antlr/out/vbaParser';
3+
import { MethodElement, SyntaxElement, VariableDeclarationElement } from './vbaSyntaxElements';
4+
5+
6+
enum NameLinkType {
7+
'variable' = 0,
8+
'method' = 1,
9+
}
10+
11+
class Scope {
12+
context: SyntaxElement;
13+
parent?: Scope;
14+
nameRefs: Map<string, NameLink> = new Map();
15+
16+
constructor(ctx: SyntaxElement);
17+
constructor(ctx: SyntaxElement, pnt: Scope);
18+
constructor(ctx: SyntaxElement, pnt?: Scope) {
19+
this.context = ctx;
20+
this.parent = pnt;
21+
}
22+
23+
addDeclaration(element: VariableDeclarationElement | MethodElement) {
24+
//
25+
}
26+
27+
addRef(ctx: CertainIdentifierContext | AmbiguousIdentifierContext) {
28+
const nameLink = this.getName(ctx.text);
29+
// if dec
30+
31+
// if not dec
32+
}
33+
34+
getName(identifier: string): NameLink {
35+
if (!this.nameRefs.has(identifier)) {
36+
this.nameRefs.set(identifier, new NameLink());
37+
}
38+
return this.nameRefs.get(identifier)!;
39+
}
40+
}
41+
42+
class NameLink {
43+
type: NameLinkType = NameLinkType.variable;
44+
45+
// The original decalarations that affect this name.
46+
// 0: Variable or method not declared.
47+
// 1: Declared once.
48+
// 2: Multiple conflicting declarations.
49+
declarations: SyntaxElement[] = [];
50+
51+
// The places this name is referenced.
52+
references: SyntaxElement[] = [];
53+
diagnostics: Diagnostic[] = [];
54+
55+
private diagnosticRelatedInfo: DiagnosticRelatedInformation[] = [];
56+
57+
merge(link: NameLink) {
58+
this.declarations.concat(link.declarations);
59+
this.references.concat(link.references);
60+
this.diagnostics.concat(link.diagnostics);
61+
62+
if (link.declarations.length > 0) {
63+
this.type = link.type;
64+
}
65+
}
66+
67+
process(optExplicit = false) {
68+
this.processDiagnosticRelatedInformation();
69+
this.validateDeclarationCount(optExplicit);
70+
this.validateMethodSignatures();
71+
72+
this.assignSemanticTokens();
73+
this.assignDiagnostics();
74+
}
75+
76+
private processDiagnosticRelatedInformation() {
77+
this.diagnosticRelatedInfo = this.declarations
78+
.concat(this.references)
79+
.map((x) => DiagnosticRelatedInformation.create(
80+
x.location(),
81+
x.text
82+
));
83+
}
84+
85+
private validateDeclarationCount(optExplicit: boolean) {
86+
if (this.declarations.length === 1) {
87+
return;
88+
}
89+
90+
const undecSeverity = (optExplicit) ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning;
91+
if (this.declarations.length === 0) {
92+
if (optExplicit || this.type !== NameLinkType.variable)
93+
this.references.forEach((x) =>
94+
this.diagnostics.push(Diagnostic.create(
95+
x.range,
96+
"No declaration for variable or method.",
97+
undecSeverity,
98+
404,
99+
'VbaPro',
100+
this.diagnosticRelatedInfo
101+
)));
102+
return;
103+
}
104+
this.declarations.forEach((x) =>
105+
this.diagnostics.push(Diagnostic.create(
106+
x.range,
107+
"Ambiguous variable declaration",
108+
DiagnosticSeverity.Error,
109+
500,
110+
'VbaPro',
111+
this.diagnosticRelatedInfo
112+
)));
113+
}
114+
115+
private assignSemanticTokens() {
116+
if (this.declarations.length === 0) {
117+
return;
118+
}
119+
120+
this.references.forEach((x) => x.semanticToken =
121+
this.declarations[0]
122+
.semanticToken
123+
?.toNewRange(x.range));
124+
}
125+
126+
private assignDiagnostics() {
127+
if (this.diagnostics.length === 0) {
128+
return;
129+
}
130+
const els = this.declarations.concat(this.references);
131+
els.forEach((x) => x.addDiagnostics(this.diagnostics));
132+
}
133+
134+
private validateMethodSignatures() {
135+
// TODO: implement.
136+
}
137+
}

0 commit comments

Comments
 (0)