Skip to content

Commit 2ae6ba7

Browse files
authored
Merge pull request #6 from SSlinky/TextMate
Major Efficiency Update
2 parents 5d82e60 + 694a6ac commit 2ae6ba7

File tree

8 files changed

+352
-455
lines changed

8 files changed

+352
-455
lines changed

client/src/syntaxes/vba.tmLanguage.yaml

Lines changed: 259 additions & 141 deletions
Large diffs are not rendered by default.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"icon": "images/vba-lsp-icon.png",
66
"author": "SSlinky",
77
"license": "MIT",
8-
"version": "1.2.0",
8+
"version": "1.3.0",
99
"repository": {
1010
"type": "git",
1111
"url": "https://github.com/SSlinky/VBA-LanguageServer"

server/src/project/document.ts

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Diagnostic, SemanticTokens, SymbolInformation, SymbolKind } from 'vscode-languageserver';
1+
import { CancellationToken, Diagnostic, LSPErrorCodes, ResponseError, SemanticTokens, SymbolInformation, SymbolKind } from 'vscode-languageserver';
22
import { Workspace } from './workspace';
33
import { FoldableElement } from './elements/special';
44
import { BaseSyntaxElement, HasAttribute, HasSemanticToken, HasSymbolInformation } from './elements/base';
@@ -9,17 +9,7 @@ import { SemanticTokensManager } from '../capabilities/semanticTokens';
99
import { sleep } from '../utils/helpers';
1010

1111

12-
export interface ProjectDocument {
13-
name: string;
14-
textDocument: TextDocument;
15-
languageServerSemanticTokens: (range?: Range) => SemanticTokens | null;
16-
languageServerSymbolInformationAsync(): Promise<SymbolInformation[]>;
17-
get foldableElements(): FoldingRange[];
18-
parse(): void;
19-
}
20-
21-
22-
export abstract class BaseProjectDocument implements ProjectDocument {
12+
export abstract class BaseProjectDocument {
2313
readonly workspace: Workspace;
2414
readonly textDocument: TextDocument;
2515
readonly name: string;
@@ -38,10 +28,6 @@ export abstract class BaseProjectDocument implements ProjectDocument {
3828
isBusy = false;
3929
abstract symbolKind: SymbolKind
4030

41-
get foldableElements() {
42-
return this._foldableElements;
43-
}
44-
4531
get activeAttributeElement() {
4632
return this._attributeElements?.at(-1);
4733
}
@@ -52,7 +38,7 @@ export abstract class BaseProjectDocument implements ProjectDocument {
5238
this.name = name;
5339
}
5440

55-
static create(workspace: Workspace, document: TextDocument): VbaClassDocument | VbaModuleDocument {
41+
static create(workspace: Workspace, document: TextDocument): BaseProjectDocument {
5642
const slashParts = document.uri.split('/').at(-1);
5743
const dotParts = slashParts?.split('.');
5844
const extension = dotParts?.at(-1);
@@ -76,19 +62,21 @@ export abstract class BaseProjectDocument implements ProjectDocument {
7662
return this._semanticTokens.getSemanticTokens(range);
7763
};
7864

79-
async languageServerSymbolInformationAsync() {
65+
async languageServerSymbolInformationAsync(token: CancellationToken): Promise<SymbolInformation[]> {
8066
while (this.isBusy) {
8167
await sleep(5);
68+
if (token.isCancellationRequested) {
69+
return [];
70+
}
8271
}
8372
return this._symbolInformations;
8473
}
8574

86-
parse = (): void => {
75+
parseAsync = async (token: CancellationToken): Promise<void> => {
8776
this.isBusy = true;
88-
console.log('Parsing document');
89-
(new SyntaxParser()).parse(this);
90-
console.log('Finished parsing document');
91-
this.isBusy = false;
77+
if (await (new SyntaxParser()).parseAsync(this, token)) {
78+
this.isBusy = false;
79+
}
9280
};
9381

9482
registerNamedElementDeclaration(element: any) {
@@ -169,9 +157,20 @@ export abstract class BaseProjectDocument implements ProjectDocument {
169157
* @returns a number for some reason.
170158
*/
171159
registerSymbolInformation = (element: HasSymbolInformation): number => {
172-
console.debug(`Registering symbol for ${element.symbolInformation.name}`);
173160
return this._symbolInformations.push(element.symbolInformation);
174161
};
162+
163+
/** Get document information */
164+
async getFoldingRanges(token: CancellationToken): Promise<FoldingRange[]> {
165+
while (this.isBusy) {
166+
await sleep(5);
167+
if (token.isCancellationRequested) {
168+
return [];
169+
}
170+
}
171+
this.workspace.connection.console.info('Processing request for Folding Range');
172+
return this._foldableElements;
173+
}
175174
}
176175

177176

server/src/project/elements/module.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ export class ModuleElement extends BaseContextSyntaxElement implements HasSymbol
2222
}
2323

2424
get symbolInformation(): SymbolInformation {
25-
console.warn(`Creating symbol with name ${this._name}`);
2625
return SymbolInformationFactory.create(
2726
this, this.symbolKind
2827
);

server/src/project/parser/vbaSyntaxParser.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,54 @@ import { VbaClassDocument, VbaModuleDocument } from '../document';
1313
import { FoldableElement } from '../elements/special';
1414
import { ConstDeclarationsElement, EnumBlockDeclarationElement, EnumMemberDeclarationElement, MethodBlockDeclarationElement, VariableDeclarationsElement } from '../elements/memory';
1515
import { ModuleElement } from '../elements/module';
16+
import { sleep } from '../../utils/helpers';
17+
import { CancellationToken } from 'vscode-languageserver';
1618

1719
export class SyntaxParser {
20+
private static _lockIdentifier = 0;
21+
22+
private static _acquireLock(): number {
23+
this._lockIdentifier += 1;
24+
return this._lockIdentifier;
25+
}
26+
27+
private static _hasLock(lockIdentifier: number): boolean {
28+
return this._lockIdentifier === lockIdentifier;
29+
}
30+
31+
private static _releaseLock(): void {
32+
this._lockIdentifier = 0;
33+
}
34+
35+
async parseAsync(document: VbaClassDocument | VbaModuleDocument, token: CancellationToken): Promise<boolean> {
36+
// token.onCancellationRequested(e => {
37+
// throw new Error("No");
38+
// });
39+
40+
// Refuse to do anything that seems like too much work.
41+
if (document.textDocument.lineCount > 1000) {
42+
// TODO: Make this an option that people can increase or decrease.
43+
console.log(`Document oversize: ${document.textDocument.lineCount} lines.`);
44+
console.warn(`Syntax parsing has been disabled to prevent crashing.`);
45+
return false;
46+
}
47+
48+
// Wait a few seconds to see if any other input has ocurred.
49+
const lock = SyntaxParser._acquireLock();
50+
await sleep(1000);
51+
if (!SyntaxParser._hasLock(lock)) {
52+
console.info('Newer lock detected. Cancelling parse.');
53+
return false;
54+
}
55+
SyntaxParser._releaseLock();
56+
57+
// Parse the document.
58+
this.parse(document);
59+
return true;
60+
}
61+
1862
parse(document: VbaClassDocument | VbaModuleDocument) {
63+
console.info('Parsing the document.');
1964
const listener = new VbaTreeWalkListener(document);
2065
const parser = this.createParser(document.textDocument);
2166

server/src/project/workspace.ts

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { CompletionItem, CompletionParams, DidChangeConfigurationNotification, DidChangeConfigurationParams, DidChangeWatchedFilesParams, DocumentSymbolParams, FoldingRange, FoldingRangeParams, Hover, HoverParams, SemanticTokensParams, SemanticTokensRangeParams, SymbolInformation, TextDocuments, WorkspaceFoldersChangeEvent, _Connection } from 'vscode-languageserver';
2-
import { BaseProjectDocument, ProjectDocument } from './document';
1+
import { CancellationToken, CancellationTokenSource, CompletionItem, CompletionParams, DidChangeConfigurationNotification, DidChangeConfigurationParams, DidChangeWatchedFilesParams, DocumentSymbolParams, FoldingRange, FoldingRangeParams, Hover, HoverParams, SemanticTokensParams, SemanticTokensRangeParams, SymbolInformation, TextDocuments, WorkspaceFoldersChangeEvent, _Connection } from 'vscode-languageserver';
2+
import { BaseProjectDocument } from './document';
33
import { LanguageServerConfiguration } from '../server';
44
import { hasConfigurationCapability } from '../capabilities/workspaceFolder';
55
import { TextDocument } from 'vscode-languageserver-textdocument';
@@ -11,13 +11,13 @@ import { TextDocument } from 'vscode-languageserver-textdocument';
1111
*/
1212
export class Workspace {
1313
private _events: WorkspaceEvents;
14-
private _documents: ProjectDocument[] = [];
15-
private _activeDocument?: ProjectDocument;
14+
private _documents: BaseProjectDocument[] = [];
15+
private _activeDocument?: BaseProjectDocument;
1616
private _publicScopeDeclarations: Map<string, any> = new Map();
1717

1818
readonly connection: _Connection;
1919

20-
activateDocument(document: ProjectDocument) {
20+
activateDocument(document: BaseProjectDocument) {
2121
this._activeDocument = document;
2222
}
2323

@@ -59,8 +59,9 @@ class WorkspaceEvents {
5959
private readonly _workspace: Workspace;
6060
private readonly _documents: TextDocuments<TextDocument>;
6161
private readonly _configuration: LanguageServerConfiguration;
62+
private _parseCancellationToken?: CancellationTokenSource;
6263

63-
activeDocument?: ProjectDocument;
64+
activeDocument?: BaseProjectDocument;
6465

6566
constructor(params: {connection: _Connection, workspace: Workspace, configuration: LanguageServerConfiguration}) {
6667
this._workspace = params.workspace;
@@ -72,17 +73,16 @@ class WorkspaceEvents {
7273
}
7374

7475
private initialiseConnectionEvents(connection: _Connection) {
75-
console.log('Initialising connection events...');
7676
connection.onInitialized(() => this.onInitialized());
7777
connection.onCompletion(params => this.onCompletion(params));
7878
connection.onCompletionResolve(item => this.onCompletionResolve(item));
7979
connection.onDidChangeConfiguration(params => this.onDidChangeConfiguration(params));
8080
connection.onDidChangeWatchedFiles(params => this.onDidChangeWatchedFiles(params));
81-
connection.onDocumentSymbol((params) => this.onDocumentSymbolAsync(params));
81+
connection.onDocumentSymbol(async (params, token) => await this.onDocumentSymbolAsync(params, token));
8282
connection.onHover(params => this.onHover(params));
8383

8484
if (hasConfigurationCapability(this._configuration)) {
85-
connection.onFoldingRanges((params) => this.onFoldingRanges(params));
85+
connection.onFoldingRanges(async (params, token) => this.onFoldingRanges(params, token));
8686
}
8787

8888
connection.onRequest((method: string, params: object | object[] | any) => {
@@ -101,19 +101,16 @@ class WorkspaceEvents {
101101
}
102102

103103
private initialiseDocumentsEvents() {
104-
console.log('Initialising documents events...');
105-
this._documents.onDidChangeContent(e => this.onDidChangeContent(e.document));
104+
this._documents.onDidChangeContent(async (e) => await this.onDidChangeContentAsync(e.document));
106105
}
107106

108107
/** Connection event handlers */
109108

110109
private onCompletion(params: CompletionParams): never[] {
111-
console.log(`onCompletion: ${params}`);
112110
return [];
113111
}
114112

115113
private onCompletionResolve(item: CompletionItem): CompletionItem {
116-
console.log(`onCompletionResolve: ${item.label}`);
117114
return item;
118115
}
119116

@@ -127,28 +124,22 @@ class WorkspaceEvents {
127124

128125
// TODO: Should trigger a full workspace refresh.
129126
private onDidChangeWorkspaceFolders(e: WorkspaceFoldersChangeEvent) {
130-
console.log(`onDidChangeWorkspaceFolders: ${e}`);
131127
this._workspace.connection.console.log(`Workspace folder change event received.\n${e}`);
132128
}
133129

134-
private async onDocumentSymbolAsync(params: DocumentSymbolParams): Promise<SymbolInformation[]> {
135-
console.log(`onDocumentSymbolAsync: ${params.textDocument.uri}`);
136-
return await this.activeDocument?.languageServerSymbolInformationAsync() ?? [];
130+
private async onDocumentSymbolAsync(params: DocumentSymbolParams, token: CancellationToken): Promise<SymbolInformation[]> {
131+
return await this.activeDocument?.languageServerSymbolInformationAsync(token) ?? [];
137132
}
138133

139-
private onFoldingRanges(params: FoldingRangeParams): FoldingRange[] {
140-
const foldingRanges = this._workspace.activeDocument?.foldableElements ?? [];
141-
console.log(`onFoldingRanges: ${params.textDocument.uri} (${foldingRanges.length} ranges)`);
142-
return foldingRanges;
134+
private async onFoldingRanges(params: FoldingRangeParams, token: CancellationToken): Promise<FoldingRange[]> {
135+
return await this._workspace.activeDocument?.getFoldingRanges(token) ?? [];
143136
}
144137

145138
private onHover(params: HoverParams): Hover {
146-
console.log(`onHover: ${params.position.line},${params.position.character}`);
147139
return { contents: '' };
148140
}
149141

150142
private onInitialized(): void {
151-
console.log('onInitialized:---');
152143
const connection = this._workspace.connection;
153144
// Register for client configuration notification changes.
154145
connection.client.register(DidChangeConfigurationNotification.type, undefined);
@@ -167,11 +158,14 @@ class WorkspaceEvents {
167158
* This event handler is called whenever a `TextDocuments<TextDocument>` is changed.
168159
* @param doc The document that changed.
169160
*/
170-
onDidChangeContent(doc: TextDocument) {
171-
console.log('onDidChangeContent:--- ' + doc.uri);
161+
async onDidChangeContentAsync(doc: TextDocument) {
162+
// this._parseCancellationToken?.cancel();
163+
// this._parseCancellationToken?.dispose();
164+
172165
this.activeDocument = BaseProjectDocument.create(this._workspace, doc);
173-
this.activeDocument.parse();
166+
this._parseCancellationToken = new CancellationTokenSource();
167+
await this.activeDocument.parseAsync(this._parseCancellationToken.token);
168+
this._parseCancellationToken = undefined;
174169
this._workspace.activateDocument(this.activeDocument);
175170
}
176-
177-
}
171+
}

0 commit comments

Comments
 (0)