Skip to content

Commit

Permalink
plugin: Support LogOutputChannel
Browse files Browse the repository at this point in the history
- Support LogOutputChannel
- Support LogLevel
- Support in namespace/env: logLevel & onDidChangeLogLevel
- Remark: Needs further extension of application to fully support all aspects of a LogOutputViewChannel (i.e. developer logger service, logging to file, extension of output view UI)

Resolves #12017

Contributed on behalf of STMicroelectronics.

Signed-off-by: Nina Doschek <[email protected]>
  • Loading branch information
ndoschek committed Apr 18, 2023
1 parent 1334898 commit 61298da
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 44 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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](<tbd>)

<a name="breaking_changes_1.37.0">[Breaking Changes:](#breaking_changes_1.37.0)</a>
- [core] Inject core preference into `DockPanelRenderer` constructor [12360](https://github.com/eclipse-theia/theia/pull/12360)
Expand Down
3 changes: 2 additions & 1 deletion packages/plugin-ext/src/common/plugin-api-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
28 changes: 20 additions & 8 deletions packages/plugin-ext/src/plugin/output-channel-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
80 changes: 80 additions & 0 deletions packages/plugin-ext/src/plugin/output-channel/logoutput-channel.ts
Original file line number Diff line number Diff line change
@@ -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<theia.LogLevel>;

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;
}
}

}
4 changes: 2 additions & 2 deletions packages/plugin-ext/src/plugin/plugin-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 14 additions & 0 deletions packages/plugin-ext/src/plugin/type-converters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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}`);
}
}
5 changes: 2 additions & 3 deletions packages/plugin-ext/src/plugin/types-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

/**
Expand Down
29 changes: 0 additions & 29 deletions packages/plugin/src/theia-proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<LogLevel>;
}

// #endregion

// #region search in workspace
/**
* The parameters of a query for text search.
Expand Down
121 changes: 121 additions & 0 deletions packages/plugin/src/theia.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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<LogLevel>;

/**
* 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.
*
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -7527,6 +7639,15 @@ export module '@theia/plugin' {
*/
export function asExternalUri(target: Uri): Thenable<Uri>;

/**
* 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<LogLevel>;
}

/**
Expand Down

0 comments on commit 61298da

Please sign in to comment.