Skip to content

Commit 6b0327b

Browse files
committed
Added basic logging support
1 parent 6c74b88 commit 6b0327b

File tree

5 files changed

+135
-12
lines changed

5 files changed

+135
-12
lines changed

client/src/extension.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
ServerOptions,
1313
TransportKind
1414
} from 'vscode-languageclient/node';
15+
import { VscodeLogger } from './logger';
1516

1617
let client: LanguageClient;
1718

@@ -53,8 +54,14 @@ export function activate(context: ExtensionContext) {
5354
clientOptions
5455
);
5556

57+
// Add logging support for messages received from the server.
58+
client.onNotification("window/logMessage", (params) => {
59+
VscodeLogger.logMessage(params);
60+
})
61+
5662
// Start the client. This will also launch the server
5763
client.start();
64+
VscodeLogger.info('VBAPro activated')
5865
}
5966

6067
export function deactivate(): Thenable<void> | undefined {

client/src/logger.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import * as vscode from 'vscode';
2+
3+
enum LogLevel {
4+
error = 1,
5+
warning = 2,
6+
info = 3,
7+
log = 4,
8+
debug = 5
9+
}
10+
11+
export interface LogMessage {
12+
type: number;
13+
message: string;
14+
level: number;
15+
}
16+
17+
export class VscodeLogger {
18+
private static _outputChannel: vscode.OutputChannel;
19+
static get outputChannel(): vscode.OutputChannel {
20+
if (!VscodeLogger._outputChannel) {
21+
VscodeLogger._outputChannel = vscode.window.createOutputChannel('VBAPro Output');
22+
VscodeLogger._outputChannel.show();
23+
}
24+
return VscodeLogger._outputChannel;
25+
}
26+
27+
static info = (msg: string, lvl?: number) => this.log(LogLevel.info, msg, lvl)
28+
static debug = (msg: string, lvl?: number) => this.log(LogLevel.debug, msg, lvl)
29+
30+
static logMessage(params: LogMessage): void {
31+
this.log(params.type, params.message, params.level);
32+
}
33+
34+
private static log(type: LogLevel, msg: string, lvl?: number): void {
35+
const i = '> '.repeat(lvl ?? 0);
36+
const t = `${this.getFormattedTimestamp()}`;
37+
VscodeLogger.outputChannel.appendLine(`${t} [${LogLevel[type]}] ${i}${msg}`);
38+
}
39+
40+
private static getFormattedTimestamp(): string {
41+
const now = new Date();
42+
43+
const year = now.getFullYear();
44+
const month = (now.getMonth() + 1).toString().padStart(2, "0");
45+
const day = now.getDate().toString().padStart(2, "0");
46+
47+
const hours = now.getHours().toString().padStart(2, "0");
48+
const minutes = now.getMinutes().toString().padStart(2, "0");
49+
const seconds = now.getSeconds().toString().padStart(2, "0");
50+
const milliseconds = now.getMilliseconds().toString().padStart(3, "0");
51+
52+
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
53+
}
54+
}
55+

server/src/logger.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { _Connection } from 'vscode-languageserver';
2+
3+
enum LogLevel {
4+
error = 1,
5+
warning = 2,
6+
info = 3,
7+
log = 4,
8+
debug = 5
9+
}
10+
11+
export class LspLogger {
12+
private readonly connection: _Connection;
13+
14+
constructor(connection: _Connection) {
15+
this.connection = connection;
16+
}
17+
18+
error = (msg: string, lvl?: number) => this.emit(LogLevel.error, msg, lvl)
19+
warning = (msg: string, lvl?: number) => this.emit(LogLevel.warning, msg, lvl)
20+
info = (msg: string, lvl?: number) => this.emit(LogLevel.info, msg, lvl)
21+
log = (msg: string, lvl?: number) => this.emit(LogLevel.log, msg, lvl)
22+
debug = (msg: string, lvl?: number) => this.emit(LogLevel.debug, msg, lvl)
23+
stack = (e: Error) => this.emit(LogLevel.debug, `${e}\n${e.stack}`)
24+
25+
private emit(msgType: LogLevel, msgText: string, msgLevel?: number): void {
26+
this.connection.sendNotification("window/logMessage", {
27+
type: msgType,
28+
message: msgText,
29+
level: msgLevel ?? 0
30+
})
31+
}
32+
}

server/src/project/document.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@ export abstract class BaseProjectDocument {
171171

172172
// Don't parse oversize documents.
173173
if (await this.isOversize) {
174-
console.log(`Document oversize: ${this.textDocument.lineCount} lines.`);
175-
console.warn(`Syntax parsing has been disabled to prevent crashing.`);
174+
this.workspace.logger.debug(`Document oversize: ${this.textDocument.lineCount} lines.`);
175+
this.workspace.logger.warning(`Syntax parsing has been disabled to prevent crashing.`);
176176
this._isBusy = false;
177177
return;
178178
}
@@ -201,8 +201,8 @@ export abstract class BaseProjectDocument {
201201

202202
// Don't parse oversize documents.
203203
if (await this.isOversize) {
204-
console.log(`Document oversize: ${this.textDocument.lineCount} lines.`);
205-
console.warn(`Syntax parsing has been disabled to prevent crashing.`);
204+
this.workspace.logger.debug(`Document oversize: ${this.textDocument.lineCount} lines.`);
205+
this.workspace.logger.warning(`Syntax parsing has been disabled to prevent crashing.`);
206206
return;
207207
}
208208

server/src/project/workspace.ts

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { NamespaceManager } from './scope';
3333
import { ParseCancellationException } from 'antlr4ng';
3434
import { getFormattingEdits } from './formatter';
3535
import { VbaFmtListener } from './parser/vbaListener';
36+
import { LspLogger } from '../logger';
3637

3738

3839
/**
@@ -47,6 +48,8 @@ export class Workspace {
4748
private _activeDocument?: BaseProjectDocument;
4849
private readonly _hasConfigurationCapability: boolean;
4950

51+
logger: LspLogger;
52+
5053
get hasConfigurationCapability() {
5154
return this._hasConfigurationCapability;
5255
}
@@ -63,6 +66,7 @@ export class Workspace {
6366

6467
constructor(params: {connection: _Connection, capabilities: LanguageServerConfiguration}) {
6568
this.connection = params.connection;
69+
this.logger = new LspLogger(this.connection);
6670
this._hasConfigurationCapability = hasConfigurationCapability(params.capabilities);
6771
this.events = new WorkspaceEvents({
6872
workspace: this,
@@ -82,11 +86,24 @@ export class Workspace {
8286
this.parseCancellationTokenSource?.cancel();
8387
this.parseCancellationTokenSource = new CancellationTokenSource();
8488

89+
if (!this.activeDocument) {
90+
this.logger.error('No active document.');
91+
return;
92+
}
93+
8594
// Exceptions thrown by the parser should be ignored.
8695
try {
87-
await this.activeDocument?.parseAsync(this.parseCancellationTokenSource.token);
88-
} catch (error) {
89-
this.connection.console.log(`Parser error: ${error}`)
96+
this.logger.debug(`Parsing document: ${this.activeDocument.name}`);
97+
await this.activeDocument.parseAsync(this.parseCancellationTokenSource.token);
98+
this.logger.info(`Parsed ${this.activeDocument.name}`);
99+
} catch (e) {
100+
if (e instanceof ParseCancellationException) {
101+
this.logger.debug('Parse cancelled successfully.')
102+
} else if (e instanceof Error) {
103+
this.logger.stack(e);
104+
} else {
105+
this.logger.error(`Parse failed: ${e}`)
106+
}
90107
}
91108

92109
this.parseCancellationTokenSource = undefined;
@@ -98,8 +115,20 @@ export class Workspace {
98115

99116
// Exceptions thrown by the parser should be ignored.
100117
let result: VbaFmtListener | undefined;
101-
try { result = await this.activeDocument?.formatParseAsync(this.parseCancellationTokenSource.token); }
102-
catch (error) { this.connection.console.log(`Parser error: ${error}`) }
118+
try {
119+
this.logger.debug(`Format parsing document: ${document.uri}`);
120+
result = await this.activeDocument?.formatParseAsync(this.parseCancellationTokenSource.token);
121+
this.logger.info(`Formatted ${document.uri}`);
122+
}
123+
catch (e) {
124+
if (e instanceof ParseCancellationException) {
125+
this.logger.debug('Parse cancelled successfully.')
126+
} else if (e instanceof Error) {
127+
this.logger.stack(e);
128+
} else {
129+
this.logger.error(`Parse failed: ${e}`)
130+
}
131+
}
103132

104133
this.parseCancellationTokenSource = undefined;
105134
return result;
@@ -240,7 +269,7 @@ class WorkspaceEvents {
240269
return this.activeDocument?.languageServerSemanticTokens(rangeParams.range);
241270
}
242271
default:
243-
console.error(`Unresolved request path: ${method}`);
272+
this.workspace.logger.error(`Unresolved request path: ${method}`);
244273
}
245274
});
246275
}
@@ -265,7 +294,7 @@ class WorkspaceEvents {
265294

266295
// TODO: Should trigger a full workspace refresh.
267296
private onDidChangeWorkspaceFolders(e: WorkspaceFoldersChangeEvent) {
268-
this.workspace.connection.console.log(`Workspace folder change event received.\n${e}`);
297+
this.workspace.logger.debug(`Workspace folder change event received.\n${e}`);
269298
}
270299

271300
private async onDocumentSymbolAsync(params: DocumentSymbolParams, token: CancellationToken): Promise<SymbolInformation[]> {
@@ -297,7 +326,7 @@ class WorkspaceEvents {
297326
}
298327

299328
private onHover(params: HoverParams): Hover {
300-
console.debug(`onHover`);
329+
this.workspace.logger.debug(`onHover`);
301330
return { contents: '' };
302331
}
303332

0 commit comments

Comments
 (0)