diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2f7e5301e7605..aff2b70e2ee4a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,10 @@
## History
- [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/)
-## v1.37.0 0 -
+
+## v1.37.0 -
+
+- [plugin] implemented the VS Code `LogOutputChannel` API [#12017](https://github.com/eclipse-theia/theia/pull/12429)
[Breaking Changes:](#breaking_changes_1.37.0)
- [core] Inject core preference into `DockPanelRenderer` constructor [12360](https://github.com/eclipse-theia/theia/pull/12360)
diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts
index e375511feb536..e1f3c90e1d1e4 100644
--- a/packages/plugin-ext/src/common/plugin-api-rpc.ts
+++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts
@@ -286,7 +286,8 @@ export interface TerminalServiceExt {
getEnvironmentVariableCollection(extensionIdentifier: string): theia.EnvironmentVariableCollection;
}
export interface OutputChannelRegistryExt {
- createOutputChannel(name: string, pluginInfo: PluginInfo): theia.OutputChannel
+ createOutputChannel(name: string, pluginInfo: PluginInfo): theia.OutputChannel,
+ createOutputChannel(name: string, pluginInfo: PluginInfo, options?: { log: true }): theia.LogOutputChannel
}
export interface ConnectionMain {
diff --git a/packages/plugin-ext/src/plugin/output-channel-registry.ts b/packages/plugin-ext/src/plugin/output-channel-registry.ts
index 2b1c6b41b1b30..282977c6c07c0 100644
--- a/packages/plugin-ext/src/plugin/output-channel-registry.ts
+++ b/packages/plugin-ext/src/plugin/output-channel-registry.ts
@@ -13,27 +13,39 @@
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************
-import {
- PLUGIN_RPC_CONTEXT as Ext, OutputChannelRegistryMain, PluginInfo, OutputChannelRegistryExt
-} from '../common/plugin-api-rpc';
-import { RPCProtocol } from '../common/rpc-protocol';
+
import * as theia from '@theia/plugin';
+import { PLUGIN_RPC_CONTEXT as Ext, OutputChannelRegistryExt, OutputChannelRegistryMain, PluginInfo } from '../common/plugin-api-rpc';
+import { RPCProtocol } from '../common/rpc-protocol';
+import { LogOutputChannelImpl } from './output-channel/logoutput-channel';
import { OutputChannelImpl } from './output-channel/output-channel-item';
export class OutputChannelRegistryExtImpl implements OutputChannelRegistryExt {
- proxy: OutputChannelRegistryMain;
+ private proxy: OutputChannelRegistryMain;
constructor(rpc: RPCProtocol) {
this.proxy = rpc.getProxy(Ext.OUTPUT_CHANNEL_REGISTRY_MAIN);
}
- createOutputChannel(name: string, pluginInfo: PluginInfo): theia.OutputChannel {
+ createOutputChannel(name: string, pluginInfo: PluginInfo): theia.OutputChannel;
+ createOutputChannel(name: string, pluginInfo: PluginInfo, options?: { log: true; }): theia.LogOutputChannel;
+ createOutputChannel(name: string, pluginInfo: PluginInfo, options?: { log: true; }): theia.OutputChannel | theia.LogOutputChannel {
name = name.trim();
if (!name) {
throw new Error('illegal argument \'name\'. must not be falsy');
- } else {
- return new OutputChannelImpl(name, this.proxy, pluginInfo);
}
+ const isLogOutput = options && typeof options === 'object' && options.log;
+ return isLogOutput
+ ? this.doCreateLogOutputChannel(name, pluginInfo)
+ : this.doCreateOutputChannel(name, pluginInfo);
+ }
+
+ private doCreateOutputChannel(name: string, pluginInfo: PluginInfo): OutputChannelImpl {
+ return new OutputChannelImpl(name, this.proxy, pluginInfo);
+ }
+
+ private doCreateLogOutputChannel(name: string, pluginInfo: PluginInfo): LogOutputChannelImpl {
+ return new LogOutputChannelImpl(name, this.proxy, pluginInfo);
}
}
diff --git a/packages/plugin-ext/src/plugin/output-channel/logoutput-channel.ts b/packages/plugin-ext/src/plugin/output-channel/logoutput-channel.ts
new file mode 100644
index 0000000000000..6b6c932e27b17
--- /dev/null
+++ b/packages/plugin-ext/src/plugin/output-channel/logoutput-channel.ts
@@ -0,0 +1,80 @@
+// *****************************************************************************
+// Copyright (C) 2023 STMicroelectronics and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// http://www.eclipse.org/legal/epl-2.0.
+//
+// This Source Code may also be made available under the following Secondary
+// Licenses when the conditions for such availability set forth in the Eclipse
+// Public License v. 2.0 are satisfied: GNU General Public License, version 2
+// with the GNU Classpath Exception which is available at
+// https://www.gnu.org/software/classpath/license.html.
+//
+// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+// *****************************************************************************
+
+import { AbstractMessageLogger, DEFAULT_LOG_LEVEL, LogLevel } from '@theia/monaco-editor-core/esm/vs/platform/log/common/log';
+import * as theia from '@theia/plugin';
+
+import { OutputChannelRegistryMain, PluginInfo } from '../../common/plugin-api-rpc';
+import { toLogLevel } from '../type-converters';
+
+export class LogOutputChannelImpl extends AbstractMessageLogger implements theia.LogOutputChannel {
+
+ private _disposed: boolean = false;
+ get disposed(): boolean { return this._disposed; }
+
+ override onDidChangeLogLevel: theia.Event;
+
+ constructor(readonly name: string, protected proxy: OutputChannelRegistryMain, protected readonly pluginInfo: PluginInfo) {
+ super();
+ this.setLevel(DEFAULT_LOG_LEVEL);
+ }
+
+ get logLevel(): theia.LogLevel {
+ return toLogLevel(this.getLevel());
+ }
+
+ append(value: string): void {
+ this.info(value);
+ }
+
+ appendLine(value: string): void {
+ this.append(value + '\n');
+ }
+
+ replace(value: string): void {
+ this.info(value);
+ this.proxy.$append(this.name, value, this.pluginInfo);
+ }
+
+ clear(): void {
+ this.proxy.$clear(this.name);
+ }
+
+ show(columnOrPreserveFocus?: theia.ViewColumn | boolean, preserveFocus?: boolean): void {
+ this.proxy.$reveal(this.name, !!(typeof columnOrPreserveFocus === 'boolean' ? columnOrPreserveFocus : preserveFocus));
+ }
+
+ hide(): void {
+ this.proxy.$close(this.name);
+ }
+
+ protected log(level: LogLevel, message: string): void {
+ const now = new Date(Date.now());
+ const eol = message.endsWith('\n') ? '' : '\n';
+ const logMessage = `${now.toISOString()} [${LogLevel[level]}] ${message}${eol}`;
+ this.proxy.$append(this.name, logMessage, this.pluginInfo);
+ }
+
+ override dispose(): void {
+ super.dispose();
+
+ if (!this._disposed) {
+ this.proxy.$dispose(this.name);
+ this._disposed = true;
+ }
+ }
+
+}
diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts
index 18f9e27010ed3..f46484054da23 100644
--- a/packages/plugin-ext/src/plugin/plugin-context.ts
+++ b/packages/plugin-ext/src/plugin/plugin-context.ts
@@ -481,8 +481,8 @@ export function createAPIFactory(
return statusBarMessageRegistryExt.createStatusBarItem(alignment, priority, id);
},
- createOutputChannel(name: string): theia.OutputChannel {
- return outputChannelRegistryExt.createOutputChannel(name, pluginToPluginInfo(plugin));
+ createOutputChannel(name: string, options?: { log: true }): any {
+ return outputChannelRegistryExt.createOutputChannel(name, pluginToPluginInfo(plugin), options);
},
createWebviewPanel(viewType: string,
title: string,
diff --git a/packages/plugin-ext/src/plugin/type-converters.ts b/packages/plugin-ext/src/plugin/type-converters.ts
index 29d8fe90c3329..f918efde7a875 100644
--- a/packages/plugin-ext/src/plugin/type-converters.ts
+++ b/packages/plugin-ext/src/plugin/type-converters.ts
@@ -29,6 +29,7 @@ import { UriComponents } from '../common/uri-components';
import { isReadonlyArray } from '../common/arrays';
import { MarkdownString as MarkdownStringDTO } from '@theia/core/lib/common/markdown-rendering';
import { isObject } from '@theia/core/lib/common';
+import { LogLevel as MonacoLogLevel } from '@theia/monaco-editor-core/esm/vs/platform/log/common/log';
const SIDE_GROUP = -2;
const ACTIVE_GROUP = -1;
@@ -1385,3 +1386,16 @@ export namespace DataTransfer {
return dataTransfer;
}
}
+
+export function toLogLevel(logLevel: MonacoLogLevel): theia.LogLevel {
+ switch (logLevel) {
+ case MonacoLogLevel.Trace: return types.LogLevel.Trace;
+ case MonacoLogLevel.Debug: return types.LogLevel.Debug;
+ case MonacoLogLevel.Info: return types.LogLevel.Info;
+ case MonacoLogLevel.Warning: return types.LogLevel.Warning;
+ case MonacoLogLevel.Error: return types.LogLevel.Error;
+ case MonacoLogLevel.Critical: return types.LogLevel.Error /* the plugin API's max LogLevel is Error */;
+ case MonacoLogLevel.Off: return types.LogLevel.Off;
+ default: throw new Error(`Invalid log level ${logLevel}`);
+ }
+}
diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts
index d74d6b911700c..8da9451f4ca10 100644
--- a/packages/plugin-ext/src/plugin/types-impl.ts
+++ b/packages/plugin-ext/src/plugin/types-impl.ts
@@ -2734,13 +2734,12 @@ export namespace DebugAdapterInlineImplementation {
export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer | DebugAdapterNamedPipeServer | DebugAdapterInlineImplementation;
export enum LogLevel {
+ Off = 0,
Trace = 1,
Debug = 2,
Info = 3,
Warning = 4,
- Error = 5,
- Critical = 6,
- Off = 7
+ Error = 5
}
/**
diff --git a/packages/plugin/src/theia-proposed.d.ts b/packages/plugin/src/theia-proposed.d.ts
index 1b48bf9bb5d6c..d97447cff6a10 100644
--- a/packages/plugin/src/theia-proposed.d.ts
+++ b/packages/plugin/src/theia-proposed.d.ts
@@ -168,35 +168,6 @@ export module '@theia/plugin' {
color?: ThemeColor;
}
- // #region LogLevel: https://github.com/microsoft/vscode/issues/85992
-
- /**
- * The severity level of a log message
- */
- export enum LogLevel {
- Trace = 1,
- Debug = 2,
- Info = 3,
- Warning = 4,
- Error = 5,
- Critical = 6,
- Off = 7
- }
-
- export namespace env {
- /**
- * Current logging level.
- */
- export const logLevel: LogLevel;
-
- /**
- * An [event](#Event) that fires when the log level has changed.
- */
- export const onDidChangeLogLevel: Event;
- }
-
- // #endregion
-
// #region search in workspace
/**
* The parameters of a query for text search.
diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts
index 8200e21202993..f1821a9a9d0dd 100644
--- a/packages/plugin/src/theia.d.ts
+++ b/packages/plugin/src/theia.d.ts
@@ -2702,6 +2702,9 @@ export module '@theia/plugin' {
/**
* An output channel is a container for readonly textual information.
+ *
+ * To get an instance of an `OutputChannel` use
+ * {@link window.createOutputChannel createOutputChannel}.
*/
export interface OutputChannel {
@@ -2765,6 +2768,106 @@ export module '@theia/plugin' {
dispose(): void;
}
+ /**
+ * Log levels
+ */
+ export enum LogLevel {
+
+ /**
+ * No messages are logged with this level.
+ */
+ Off = 0,
+
+ /**
+ * All messages are logged with this level.
+ */
+ Trace = 1,
+
+ /**
+ * Messages with debug and higher log level are logged with this level.
+ */
+ Debug = 2,
+
+ /**
+ * Messages with info and higher log level are logged with this level.
+ */
+ Info = 3,
+
+ /**
+ * Messages with warning and higher log level are logged with this level.
+ */
+ Warning = 4,
+
+ /**
+ * Only error messages are logged with this level.
+ */
+ Error = 5
+ }
+
+ /**
+ * A channel for containing log output.
+ *
+ * To get an instance of a `LogOutputChannel` use
+ * {@link window.createOutputChannel createOutputChannel}.
+ */
+ export interface LogOutputChannel extends OutputChannel {
+
+ /**
+ * The current log level of the channel. Defaults to {@link env.logLevel editor log level}.
+ */
+ readonly logLevel: LogLevel;
+
+ /**
+ * An {@link Event} which fires when the log level of the channel changes.
+ */
+ readonly onDidChangeLogLevel: Event;
+
+ /**
+ * Outputs the given trace message to the channel. Use this method to log verbose information.
+ *
+ * The message is only loggeed if the channel is configured to display {@link LogLevel.Trace trace} log level.
+ *
+ * @param message trace message to log
+ */
+ trace(message: string, ...args: any[]): void;
+
+ /**
+ * Outputs the given debug message to the channel.
+ *
+ * The message is only loggeed if the channel is configured to display {@link LogLevel.Debug debug} log level or lower.
+ *
+ * @param message debug message to log
+ */
+ debug(message: string, ...args: any[]): void;
+
+ /**
+ * Outputs the given information message to the channel.
+ *
+ * The message is only loggeed if the channel is configured to display {@link LogLevel.Info info} log level or lower.
+ *
+ * @param message info message to log
+ */
+ info(message: string, ...args: any[]): void;
+
+ /**
+ * Outputs the given warning message to the channel.
+ *
+ * The message is only loggeed if the channel is configured to display {@link LogLevel.Warning warning} log level or lower.
+ *
+ * @param message warning message to log
+ */
+ warn(message: string, ...args: any[]): void;
+
+ /**
+ * Outputs the given error or error message to the channel.
+ *
+ * The message is only loggeed if the channel is configured to display {@link LogLevel.Error error} log level or lower.
+ *
+ * @param error Error or error message to log
+ */
+ error(error: string | Error, ...args: any[]): void;
+ }
+
/**
* Options to configure the behaviour of a file open dialog.
*
@@ -5250,6 +5353,15 @@ export module '@theia/plugin' {
*/
export function createOutputChannel(name: string): OutputChannel;
+ /**
+ * Creates a new {@link OutputChannel output channel} with the given name.
+ * If options are given, creates a new {@link OutputChannel output channel} with the given name.
+ *
+ * @param name Human-readable string which will be used to represent the channel in the UI.
+ * @param options optional; Options for the log output channel.
+ */
+ export function createOutputChannel(name: string, options?: { log: true }): LogOutputChannel;
+
/**
* Create new terminal.
* @param name - terminal name to display on the UI.
@@ -7527,6 +7639,15 @@ export module '@theia/plugin' {
*/
export function asExternalUri(target: Uri): Thenable;
+ /**
+ * The current log level of the editor.
+ */
+ export const logLevel: LogLevel;
+
+ /**
+ * An {@link Event} which fires when the log level of the editor changes.
+ */
+ export const onDidChangeLogLevel: Event;
}
/**