Skip to content

Commit 93ce0b9

Browse files
committed
Composition over inheritence redesign
1 parent 93a271e commit 93ce0b9

23 files changed

+1327
-1543
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Core
2+
import {
3+
Diagnostic,
4+
Range,
5+
SemanticTokenModifiers,
6+
SemanticTokenTypes,
7+
SymbolInformation,
8+
SymbolKind
9+
} from 'vscode-languageserver';
10+
11+
// Antlr
12+
import { ParserRuleContext, TerminalNode } from 'antlr4ng';
13+
14+
// Project
15+
import { SemanticToken } from '../capabilities/semanticTokens';
16+
import { FoldingRange, FoldingRangeKind } from '../capabilities/folding';
17+
import { BaseContextSyntaxElement, BaseIdentifyableSyntaxElement, HasSemanticTokenCapability } from '../project/elements/base';
18+
19+
20+
abstract class BaseCapability {
21+
element: BaseContextSyntaxElement<ParserRuleContext>
22+
23+
constructor(element: BaseContextSyntaxElement<ParserRuleContext>) {
24+
this.element = element;
25+
}
26+
}
27+
28+
29+
export class FoldingRangeCapability extends BaseCapability {
30+
foldingRangeKind?: FoldingRangeKind;
31+
32+
get foldingRange(): FoldingRange {
33+
return new FoldingRange(this.element.context.range, this.foldingRangeKind);
34+
}
35+
36+
constructor(element: BaseContextSyntaxElement<ParserRuleContext>, foldingRangeKind?: FoldingRangeKind) {
37+
super(element);
38+
this.foldingRangeKind = foldingRangeKind;
39+
}
40+
}
41+
42+
43+
export class DiagnosticCapability extends BaseCapability {
44+
diagnostics: Diagnostic[] = [];
45+
evaluate: (...args: any[]) => Diagnostic[]
46+
47+
constructor(element: BaseContextSyntaxElement<ParserRuleContext>, evaluate?: (...args: any[]) => Diagnostic[]) {
48+
super(element);
49+
this.evaluate = evaluate ?? (() => this.diagnostics);
50+
}
51+
}
52+
53+
54+
export class SemanticTokenCapability extends BaseCapability {
55+
semanticToken: SemanticToken;
56+
57+
constructor(element: BaseContextSyntaxElement<ParserRuleContext> & HasSemanticTokenCapability, tokenType: SemanticTokenTypes, tokenModifiers: SemanticTokenModifiers[], range?: Range, tokLength?: number) {
58+
super(element);
59+
60+
const context = !!element.identifierCapability
61+
? element.identifierCapability.element.context
62+
: element.context;
63+
64+
const startLine = range?.start.line ?? context.range.start.line;
65+
const startChar = range?.start.character ?? context.range.start.character;
66+
const textLength = tokLength ?? context.text.length;
67+
68+
this.semanticToken = new SemanticToken(
69+
element,
70+
startLine,
71+
startChar,
72+
textLength,
73+
tokenType,
74+
tokenModifiers
75+
);
76+
}
77+
}
78+
79+
80+
export class SymbolInformationCapability extends BaseCapability {
81+
private symbolKind: SymbolKind
82+
83+
get SymbolInformation(): SymbolInformation {
84+
const element = this.element as BaseIdentifyableSyntaxElement<ParserRuleContext>;
85+
return SymbolInformation.create(
86+
element.identifierCapability.name,
87+
this.symbolKind,
88+
element.context.range,
89+
element.context.document.uri
90+
)
91+
}
92+
93+
constructor(element: BaseIdentifyableSyntaxElement<ParserRuleContext>, symbolKind: SymbolKind) {
94+
super(element);
95+
this.symbolKind = symbolKind;
96+
}
97+
}
98+
99+
interface IdentifierArgs {
100+
element: BaseContextSyntaxElement<ParserRuleContext>,
101+
getNameContext?: () => ParserRuleContext | TerminalNode | null | undefined,
102+
formatName?: (name: string) => string,
103+
defaultName?: string;
104+
defaultRange?: () => Range;
105+
}
106+
107+
108+
export class IdentifierCapability extends BaseCapability {
109+
nameContext?: ParserRuleContext | TerminalNode | null;
110+
range: Range;
111+
name: string;
112+
113+
constructor(args: IdentifierArgs) {
114+
super(args.element);
115+
116+
this.nameContext = (args.getNameContext ?? (() => args.element.context.rule))();
117+
118+
if (!!this.nameContext) {
119+
// Use the context to set the values.
120+
this.name = (args.formatName ?? ((name: string) => name))(this.nameContext.getText());
121+
this.range = this.nameContext.toRange(args.element.context.document);
122+
} else {
123+
// Use the defaults to set the values.
124+
if (!args.defaultRange) throw new Error("Default range not optional where name context not found.");
125+
this.name = (args.defaultName ?? "Unknown Element");
126+
this.range = !!args.defaultRange ? args.defaultRange() : args.element.context.range;
127+
}
128+
}
129+
}

server/src/capabilities/diagnostics.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
import { CodeDescription, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Position, Range, TextDocumentClientCapabilities } from 'vscode-languageserver';
1+
// Core
2+
import { CodeDescription, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Position, Range } from 'vscode-languageserver';
23

34

4-
function hasDiagnosticRelatedInformationCapability(x: TextDocumentClientCapabilities) {
5-
return !!(x && x.publishDiagnostics && x.publishDiagnostics.relatedInformation);
6-
}
7-
85
abstract class BaseDiagnostic implements Diagnostic {
96
range: Range;
107
message: string
@@ -32,6 +29,7 @@ export class MultipleOperatorsDiagnostic extends BaseDiagnostic {
3229
}
3330
}
3431

32+
3533
export class WhileWendDeprecatedDiagnostic extends BaseDiagnostic {
3634
message = "The Do...Loop statement provides a more structured and flexible way to perform looping.";
3735
severity = DiagnosticSeverity.Information;
@@ -40,20 +38,23 @@ export class WhileWendDeprecatedDiagnostic extends BaseDiagnostic {
4038
}
4139
}
4240

41+
4342
export class MissingAttributeDiagnostic extends BaseDiagnostic {
4443
severity = DiagnosticSeverity.Error;
4544
constructor(range: Range, attributeName: string) {
4645
super(range, `Module missing attribute ${attributeName}.`);
4746
}
4847
}
4948

49+
5050
export class DuplicateAttributeDiagnostic extends BaseDiagnostic {
5151
severity = DiagnosticSeverity.Error;
5252
constructor(range: Range, attributeName: string) {
5353
super(range, `Module duplicate attribute ${attributeName}.`);
5454
}
5555
}
5656

57+
5758
// test
5859
export class DuplicateDeclarationDiagnostic extends BaseDiagnostic {
5960
message = "Duplicate declaration in current scope.";
@@ -63,6 +64,7 @@ export class DuplicateDeclarationDiagnostic extends BaseDiagnostic {
6364
}
6465
}
6566

67+
6668
// test
6769
export class ShadowDeclarationDiagnostic extends BaseDiagnostic {
6870
message = "Declaration is shadowed in the local scope.";
@@ -72,13 +74,15 @@ export class ShadowDeclarationDiagnostic extends BaseDiagnostic {
7274
}
7375
}
7476

77+
7578
export class IgnoredAttributeDiagnostic extends BaseDiagnostic {
7679
severity = DiagnosticSeverity.Warning;
7780
constructor(range: Range, attributeName: string) {
7881
super(range, `Unknown attribute '${attributeName}' will be ignored.`);
7982
}
8083
}
8184

85+
8286
export class MissingOptionExplicitDiagnostic extends BaseDiagnostic {
8387
message = "Option Explicit is missing from module header.";
8488
severity = DiagnosticSeverity.Warning;
@@ -87,9 +91,18 @@ export class MissingOptionExplicitDiagnostic extends BaseDiagnostic {
8791
}
8892
}
8993

94+
9095
export class ElementOutOfPlaceDiagnostic extends BaseDiagnostic {
9196
severity = DiagnosticSeverity.Error;
9297
constructor(range: Range, elementName: string) {
9398
super(range, `${elementName}s cannot appear below a Sub, Function, or Property declaration.`);
9499
}
100+
}
101+
102+
103+
export class LegacyFunctionalityDiagnostic extends BaseDiagnostic {
104+
severity = DiagnosticSeverity.Warning;
105+
constructor(range: Range, functionalityType: string) {
106+
super(range, `${functionalityType} are legacy functionality and should be avoided.`);
107+
}
95108
}

server/src/capabilities/folding.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { FoldingRange as LSFoldingRange } from 'vscode-languageserver';
2-
import { FoldableElement } from '../project/elements/special';
1+
import { FoldingRange as VscFoldingRange, Range } from 'vscode-languageserver';
32

43
/**
54
* Enum of known range kinds
@@ -21,7 +20,7 @@ export enum FoldingRangeKind {
2120
}
2221

2322

24-
export class FoldingRange implements LSFoldingRange {
23+
export class FoldingRange implements VscFoldingRange {
2524
/**
2625
* The zero-based line number from where the folded range starts.
2726
*/
@@ -49,9 +48,9 @@ export class FoldingRange implements LSFoldingRange {
4948
*/
5049
kind?: string;
5150

52-
constructor(element: FoldableElement, foldingRangeKind?: FoldingRangeKind) {
53-
this.startLine = element.range.start.line;
54-
this.endLine = element.range.end.line;
51+
constructor(range: Range, foldingRangeKind?: FoldingRangeKind) {
52+
this.startLine = range.start.line;
53+
this.endLine = range.end.line;
5554
this.kind = foldingRangeKind;
5655
}
5756
}

0 commit comments

Comments
 (0)