Skip to content
86 changes: 35 additions & 51 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"publisher": "ChiaNetwork",
"version": "1.2.6",
"engines": {
"vscode": "^1.85.0"
"vscode": "^1.110.0"
},
"categories": [
"Programming Languages"
Expand Down Expand Up @@ -158,14 +158,14 @@
"test": "node ./out/test/runTest.js"
},
"dependencies": {
"vscode-languageclient": "^9.0.1"
"vscode-languageclient": "^10.0.1"
},
"devDependencies": {
"@stylistic/eslint-plugin": "^5.10.0",
"@types/glob": "^9.0.0",
"@types/mocha": "^10.0.10",
"@types/node": "25.9.3",
"@types/vscode": "^1.85.0",
"@types/vscode": "^1.110.0",
"@typescript-eslint/eslint-plugin": "^8.61.1",
"@typescript-eslint/parser": "^8.60.1",
"@vscode/test-electron": "^3.0.0",
Expand Down
157 changes: 156 additions & 1 deletion src/lsp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,164 @@ export class WorkaroundFeature implements StaticFeature {
}
}

export class LogOutputChannelWrapper implements vscode.LogOutputChannel {
private readonly outputChannel: vscode.OutputChannel;
private readonly onDidChangeLogLevelEmitter = new vscode.EventEmitter<vscode.LogLevel>();
private readonly disposables: vscode.Disposable[] = [];
private _logLevel: vscode.LogLevel;

readonly onDidChangeLogLevel: vscode.Event<vscode.LogLevel>;

constructor(outputChannel: vscode.OutputChannel, logLevel: vscode.LogLevel = vscode.env.logLevel) {
this.outputChannel = outputChannel;
this._logLevel = logLevel;
this.onDidChangeLogLevel = this.onDidChangeLogLevelEmitter.event;

this.disposables.push(
vscode.env.onDidChangeLogLevel((level) => {
this._logLevel = level;
this.onDidChangeLogLevelEmitter.fire(level);
}),
this.onDidChangeLogLevelEmitter,
);
}

get logLevel(): vscode.LogLevel {
return this._logLevel;
}

get name(): string {
return this.outputChannel.name;
}

append(value: string): void {
this.info(value);
}

appendLine(value: string): void {
this.append(value);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Append breaks output channel contract

Medium Severity

LogOutputChannelWrapper routes append and appendLine through info(), so each write is subject to canLog at info level and gets a timestamp, level tag, and forced newline. That differs from a plain OutputChannel (and from native LogOutputChannel legacy writes), so vscode-languageclient output can disappear when the editor log level is above info or look garbled when multiple append calls should form one line.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 027a714. Configure here.


clear(): void {
this.outputChannel.clear();
}

replace(value: string): void {
this.clear();
this.info(value);
}

show(preserveFocus?: boolean): void;
show(column?: vscode.ViewColumn, preserveFocus?: boolean): void;
show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
if (typeof columnOrPreserveFocus === 'number') {
this.outputChannel.show(columnOrPreserveFocus, preserveFocus);
} else {
this.outputChannel.show(columnOrPreserveFocus);
}
}

hide(): void {
this.outputChannel.hide();
}
Comment thread
cursor[bot] marked this conversation as resolved.

dispose(): void {
for (const disposable of this.disposables) {
disposable.dispose();
}
this.outputChannel.dispose();
}

trace(message: string, ...args: any[]): void {
this.writeLog(vscode.LogLevel.Trace, message, args, true);
}

debug(message: string, ...args: any[]): void {
this.writeLog(vscode.LogLevel.Debug, message, args);
}

info(message: string, ...args: any[]): void {
this.writeLog(vscode.LogLevel.Info, message, args);
}

warn(message: string, ...args: any[]): void {
this.writeLog(vscode.LogLevel.Warning, message, args);
}

error(error: string | Error, ...args: any[]): void {
if (!this.canLog(vscode.LogLevel.Error)) {
return;
}
if (error instanceof Error) {
this.appendFormattedLine(vscode.LogLevel.Error, this.formatMessage(error.stack ?? error.message, args));
} else {
this.writeLog(vscode.LogLevel.Error, error, args);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log wrapper ignores log level

Medium Severity

The wrapper’s trace, debug, info, warn, and error methods always append to the output channel and never honor logLevel or VS Code’s log-level filtering. logLevel stays fixed at 0 and onDidChangeLogLevel is never fired, so verbosity cannot match the editor’s log settings.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0d3fbe6. Configure here.


private writeLog(level: vscode.LogLevel, message: string, args: any[], verbose = false): void {
if (!this.canLog(level)) {
return;
}
this.appendFormattedLine(level, this.formatMessage(message, args, verbose));
}

private canLog(messageLevel: vscode.LogLevel): boolean {
return this._logLevel !== vscode.LogLevel.Off && this._logLevel <= messageLevel;
}

private appendFormattedLine(level: vscode.LogLevel, message: string): void {
this.outputChannel.append(`${this.getCurrentTimestamp()} [${this.stringifyLogLevel(level)}] ${message}\n`);
}

private formatMessage(message: string, args: any[], verbose = false): string {
const parts: any[] = [message, ...args];
let result = '';
for (let i = 0; i < parts.length; i++) {
let part = parts[i];
if (part instanceof Error) {
part = verbose ? (part.stack ?? part.message) : part.message;
} else if (typeof part === 'object' && part !== null) {
try {
part = JSON.stringify(part);
} catch {
part = String(part);
}
}
result += (i > 0 ? ' ' : '') + String(part);
}
return result;
}

private getCurrentTimestamp(): string {
const toTwoDigits = (value: number) => (value < 10 ? `0${value}` : `${value}`);
const toThreeDigits = (value: number) => (value < 10 ? `00${value}` : value < 100 ? `0${value}` : `${value}`);
const now = new Date();
return `${now.getFullYear()}-${toTwoDigits(now.getMonth() + 1)}-${toTwoDigits(now.getDate())} ${toTwoDigits(now.getHours())}:${toTwoDigits(now.getMinutes())}:${toTwoDigits(now.getSeconds())}.${toThreeDigits(now.getMilliseconds())}`;
}

private stringifyLogLevel(level: vscode.LogLevel): string {
switch (level) {
case vscode.LogLevel.Trace:
return 'trace';
case vscode.LogLevel.Debug:
return 'debug';
case vscode.LogLevel.Info:
return 'info';
case vscode.LogLevel.Warning:
return 'warning';
case vscode.LogLevel.Error:
return 'error';
default:
return 'off';
}
}
}

async function activateServer(context: vscode.ExtensionContext) {
const workspaceClientInstanceId = 'chialisp';
const outputChannel: vscode.OutputChannel = vscode.window.createOutputChannel(workspaceClientInstanceId);
const rawOutputChannel: vscode.OutputChannel = vscode.window.createOutputChannel(workspaceClientInstanceId);
const outputChannel: vscode.LogOutputChannel = new LogOutputChannelWrapper(rawOutputChannel);
var ourExtensionPath = vscode.extensions.getExtension(ourExtension)?.extensionPath;

if (!ourExtensionPath) {
Expand Down
2 changes: 1 addition & 1 deletion src/test/suite/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as path from 'path';
import * as Mocha from 'mocha';
import Mocha from 'mocha';
import {glob} from 'glob';

export function run(): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion test/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
FROM codercom/code-server:4.96.4-focal
FROM codercom/code-server:4.125.0-focal
RUN sudo ln -s /usr/lib/code-server/lib/node /bin/node
COPY launch.json /home/coder/.vscode/launch.json
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
"module": "node16",
"moduleResolution": "node16",
"target": "ES2020",
"outDir": "out",
"lib": [
Expand Down
Loading