Skip to content

Commit e8308d6

Browse files
authored
Allow writing to different sourcekit output channels (#883)
Issue: #833 IFF sourcekit logMessage notification includes a logName property, we'll use a different output channel to not polute the langauge server's main log. For a known list of logs we'll set them up on startup so we can control if the log level and/or timestamp is included in the log message. For the current known indexing log, for example, all messages will be "Info" so we'll just add timestamp. If new logs are introduced, we'll show log level and timestamp by default
1 parent 6de1461 commit e8308d6

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-0
lines changed

src/sourcekit-lsp/LSPOutputChannel.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the VS Code Swift open source project
4+
//
5+
// Copyright (c) 2024 the VS Code Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of VS Code Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import * as vscode from "vscode";
16+
17+
export interface LSPLogger {
18+
debug(message: string): void;
19+
info(message: string): void;
20+
warn(message: string): void;
21+
error(message: string): void;
22+
}
23+
24+
export class LSPOutputChannel implements LSPLogger {
25+
private _channel: vscode.OutputChannel | undefined;
26+
27+
constructor(
28+
private name: string,
29+
private includeLogLevel: boolean = true,
30+
private includeTimestamp: boolean = true
31+
) {}
32+
33+
private get channel(): vscode.OutputChannel {
34+
if (!this._channel) {
35+
this._channel = vscode.window.createOutputChannel(this.name);
36+
}
37+
return this._channel;
38+
}
39+
40+
dispose() {
41+
this._channel?.dispose();
42+
this._channel = undefined;
43+
}
44+
45+
debug(message: string) {
46+
this.logOutputMessage("Debug", message);
47+
}
48+
49+
info(message: string) {
50+
this.logOutputMessage("Info", message);
51+
}
52+
53+
warn(message: string) {
54+
this.logOutputMessage("Warn", message);
55+
}
56+
57+
error(message: string) {
58+
this.logOutputMessage("Error", message);
59+
}
60+
61+
logOutputMessage(logLevel: string, message: string) {
62+
let formatted = "";
63+
if (this.includeLogLevel) {
64+
formatted = (formatted || "[") + logLevel.padEnd(5);
65+
}
66+
if (this.includeTimestamp) {
67+
formatted += formatted ? " - " : "[";
68+
formatted += new Date().toLocaleTimeString();
69+
}
70+
formatted += formatted ? "] " : " ";
71+
formatted += message;
72+
this.channel.appendLine(formatted);
73+
}
74+
}

src/sourcekit-lsp/LanguageClientManager.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,19 @@ import { FolderContext } from "../FolderContext";
2424
import { LanguageClient } from "vscode-languageclient/node";
2525
import { ArgumentFilter, BuildFlags } from "../toolchain/BuildFlags";
2626
import { DiagnosticsManager } from "../DiagnosticsManager";
27+
import { LSPLogger, LSPOutputChannel } from "./LSPOutputChannel";
28+
29+
interface SourceKitLogMessageParams extends langclient.LogMessageParams {
30+
logName?: string;
31+
}
2732

2833
/** Manages the creation and destruction of Language clients as we move between
2934
* workspace folders
3035
*/
3136
export class LanguageClientManager {
37+
// known log names
38+
static indexingLogName = "SourceKit-LSP: Indexing";
39+
3240
// document selector used by language client
3341
static appleLangDocumentSelector = [
3442
{ scheme: "file", language: "swift" },
@@ -112,6 +120,7 @@ export class LanguageClientManager {
112120
// used by single server support to keep a record of the project folders
113121
// that are not at the root of their workspace
114122
public subFolderWorkspaces: vscode.Uri[];
123+
private namedOutputChannels: Map<string, LSPOutputChannel> = new Map();
115124
/** Get the current state of the underlying LanguageClient */
116125
public get state(): langclient.State {
117126
if (!this.languageClient) {
@@ -121,6 +130,10 @@ export class LanguageClientManager {
121130
}
122131

123132
constructor(public workspaceContext: WorkspaceContext) {
133+
this.namedOutputChannels.set(
134+
LanguageClientManager.indexingLogName,
135+
new LSPOutputChannel(LanguageClientManager.indexingLogName, false, true)
136+
);
124137
this.singleServerSupport = workspaceContext.swiftVersion.isGreaterThanOrEqual(
125138
new Version(5, 7, 0)
126139
);
@@ -232,6 +245,7 @@ export class LanguageClientManager {
232245
this.legacyInlayHints?.dispose();
233246
this.subscriptions.forEach(item => item.dispose());
234247
this.languageClient?.stop();
248+
this.namedOutputChannels.forEach(channel => channel.dispose());
235249
}
236250

237251
/**
@@ -556,6 +570,10 @@ export class LanguageClientManager {
556570
this.workspaceContext.outputChannel.log(`SourceKit-LSP setup`);
557571
}
558572

573+
client.onNotification(langclient.LogMessageNotification.type, params => {
574+
this.logMessage(client, params as SourceKitLogMessageParams);
575+
});
576+
559577
// start client
560578
this.clientReadyPromise = client
561579
.start()
@@ -578,6 +596,34 @@ export class LanguageClientManager {
578596

579597
return this.clientReadyPromise;
580598
}
599+
600+
private logMessage(client: langclient.LanguageClient, params: SourceKitLogMessageParams) {
601+
let logger: LSPLogger = client;
602+
if (params.logName) {
603+
const outputChannel =
604+
this.namedOutputChannels.get(params.logName) ??
605+
new LSPOutputChannel(params.logName);
606+
this.namedOutputChannels.set(params.logName, outputChannel);
607+
logger = outputChannel;
608+
}
609+
switch (params.type) {
610+
case langclient.MessageType.Info:
611+
logger.info(params.message);
612+
break;
613+
case langclient.MessageType.Debug:
614+
logger.debug(params.message);
615+
break;
616+
case langclient.MessageType.Warning:
617+
logger.warn(params.message);
618+
break;
619+
case langclient.MessageType.Error:
620+
logger.error(params.message);
621+
break;
622+
case langclient.MessageType.Log:
623+
logger.info(params.message);
624+
break;
625+
}
626+
}
581627
}
582628

583629
/**

0 commit comments

Comments
 (0)