@theia/output
Version:
Theia - Output Extension
275 lines (257 loc) • 12.1 kB
text/typescript
// *****************************************************************************
// Copyright (C) 2018 TypeFox 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-only WITH Classpath-exception-2.0
// *****************************************************************************
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
import URI from '@theia/core/lib/common/uri';
import { Widget } from '@theia/core/lib/browser/widgets/widget';
import { MaybePromise } from '@theia/core/lib/common/types';
import { CommonCommands, quickCommand, OpenHandler, open, OpenerOptions, OpenerService, QuickPickItem, QuickPickValue } from '@theia/core/lib/browser';
import { CommandRegistry, MenuModelRegistry, CommandService } from '@theia/core/lib/common';
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
import { OutputWidget } from './output-widget';
import { OutputContextMenu } from './output-context-menu';
import { OutputUri } from '../common/output-uri';
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
import { OutputChannelManager, OutputChannel } from './output-channel';
import { OutputCommands } from './output-commands';
import { QuickPickSeparator, QuickPickService } from '@theia/core/lib/common/quick-pick-service';
import { nls } from '@theia/core/lib/common/nls';
()
export class OutputContribution extends AbstractViewContribution<OutputWidget> implements OpenHandler {
(ClipboardService)
protected readonly clipboardService: ClipboardService;
(CommandService)
protected readonly commandService: CommandService;
(OutputChannelManager)
protected readonly outputChannelManager: OutputChannelManager;
(OpenerService)
protected readonly openerService: OpenerService;
(QuickPickService)
protected readonly quickPickService: QuickPickService;
readonly id: string = `${OutputWidget.ID}-opener`;
constructor() {
super({
widgetId: OutputWidget.ID,
widgetName: OutputWidget.LABEL,
defaultWidgetOptions: {
area: 'bottom'
},
toggleCommandId: 'output:toggle',
toggleKeybinding: 'CtrlCmd+Shift+U'
});
}
()
protected init(): void {
this.outputChannelManager.onChannelWasShown(({ name, preserveFocus }) =>
open(this.openerService, OutputUri.create(name), { activate: !preserveFocus, reveal: true }));
}
override registerCommands(registry: CommandRegistry): void {
super.registerCommands(registry);
registry.registerCommand(OutputCommands.CLEAR__WIDGET, {
isEnabled: arg => {
if (arg instanceof Widget) {
return arg instanceof OutputWidget;
}
return this.shell.currentWidget instanceof OutputWidget;
},
isVisible: arg => {
if (arg instanceof Widget) {
return arg instanceof OutputWidget;
}
return this.shell.currentWidget instanceof OutputWidget;
},
execute: () => {
this.widget.then(widget => {
this.withWidget(widget, output => {
output.clear();
return true;
});
});
}
});
registry.registerCommand(OutputCommands.LOCK__WIDGET, {
isEnabled: widget => this.withWidget(widget, output => !output.isLocked),
isVisible: widget => this.withWidget(widget, output => !output.isLocked),
execute: widget => this.withWidget(widget, output => {
output.lock();
return true;
})
});
registry.registerCommand(OutputCommands.UNLOCK__WIDGET, {
isEnabled: widget => this.withWidget(widget, output => output.isLocked),
isVisible: widget => this.withWidget(widget, output => output.isLocked),
execute: widget => this.withWidget(widget, output => {
output.unlock();
return true;
})
});
registry.registerCommand(OutputCommands.COPY_ALL, {
execute: () => {
const textToCopy = this.tryGetWidget()?.getText();
if (textToCopy) {
this.clipboardService.writeText(textToCopy);
}
}
});
registry.registerCommand(OutputCommands.APPEND, {
execute: ({ name, text }: { name: string, text: string }) => {
if (name && text) {
this.outputChannelManager.getChannel(name).append(text);
}
}
});
registry.registerCommand(OutputCommands.APPEND_LINE, {
execute: ({ name, text }: { name: string, text: string }) => {
if (name && text) {
this.outputChannelManager.getChannel(name).appendLine(text);
}
}
});
registry.registerCommand(OutputCommands.CLEAR, {
execute: ({ name }: { name: string }) => {
if (name) {
this.outputChannelManager.getChannel(name).clear();
}
}
});
registry.registerCommand(OutputCommands.DISPOSE, {
execute: ({ name }: { name: string }) => {
if (name) {
this.outputChannelManager.deleteChannel(name);
}
}
});
registry.registerCommand(OutputCommands.SHOW, {
execute: ({ name, options }: { name: string, options?: { preserveFocus?: boolean } }) => {
if (name) {
const preserveFocus = options && options.preserveFocus || false;
this.outputChannelManager.getChannel(name).show({ preserveFocus });
}
}
});
registry.registerCommand(OutputCommands.HIDE, {
execute: ({ name }: { name: string }) => {
if (name) {
this.outputChannelManager.getChannel(name).hide();
}
}
});
registry.registerCommand(OutputCommands.CLEAR__QUICK_PICK, {
execute: async () => {
const channel = await this.pick({
placeholder: OutputCommands.CLEAR__QUICK_PICK.label!,
channels: this.outputChannelManager.getChannels().slice()
});
if (channel) {
channel.clear();
}
},
isEnabled: () => !!this.outputChannelManager.getChannels().length,
isVisible: () => !!this.outputChannelManager.getChannels().length
});
registry.registerCommand(OutputCommands.SHOW__QUICK_PICK, {
execute: async () => {
const channel = await this.pick({
placeholder: OutputCommands.SHOW__QUICK_PICK.label!,
channels: this.outputChannelManager.getChannels().slice()
});
if (channel) {
const { name } = channel;
registry.executeCommand(OutputCommands.SHOW.id, { name, options: { preserveFocus: false } });
}
},
isEnabled: () => !!this.outputChannelManager.getChannels().length,
isVisible: () => !!this.outputChannelManager.getChannels().length
});
registry.registerCommand(OutputCommands.HIDE__QUICK_PICK, {
execute: async () => {
const channel = await this.pick({
placeholder: OutputCommands.HIDE__QUICK_PICK.label!,
channels: this.outputChannelManager.getVisibleChannels().slice()
});
if (channel) {
const { name } = channel;
registry.executeCommand(OutputCommands.HIDE.id, { name });
}
},
isEnabled: () => !!this.outputChannelManager.getVisibleChannels().length,
isVisible: () => !!this.outputChannelManager.getVisibleChannels().length
});
registry.registerCommand(OutputCommands.DISPOSE__QUICK_PICK, {
execute: async () => {
const channel = await this.pick({
placeholder: OutputCommands.DISPOSE__QUICK_PICK.label!,
channels: this.outputChannelManager.getChannels().slice()
});
if (channel) {
const { name } = channel;
registry.executeCommand(OutputCommands.DISPOSE.id, { name });
}
},
isEnabled: () => !!this.outputChannelManager.getChannels().length,
isVisible: () => !!this.outputChannelManager.getChannels().length
});
}
override registerMenus(registry: MenuModelRegistry): void {
super.registerMenus(registry);
registry.registerMenuAction(OutputContextMenu.TEXT_EDIT_GROUP, {
commandId: CommonCommands.COPY.id
});
registry.registerMenuAction(OutputContextMenu.TEXT_EDIT_GROUP, {
commandId: OutputCommands.COPY_ALL.id,
label: nls.localizeByDefault('Copy All')
});
registry.registerMenuAction(OutputContextMenu.COMMAND_GROUP, {
commandId: quickCommand.id,
label: nls.localizeByDefault('Command Palette...')
});
registry.registerMenuAction(OutputContextMenu.WIDGET_GROUP, {
commandId: OutputCommands.CLEAR__WIDGET.id,
label: nls.localizeByDefault('Clear Output')
});
}
canHandle(uri: URI): MaybePromise<number> {
return OutputUri.is(uri) ? 200 : 0;
}
async open(uri: URI, options?: OpenerOptions): Promise<OutputWidget> {
if (!OutputUri.is(uri)) {
throw new Error(`Expected '${OutputUri.SCHEME}' URI scheme. Got: ${uri} instead.`);
}
const widget = await this.openView(options);
return widget;
}
protected withWidget(
widget: Widget | undefined = this.tryGetWidget(),
predicate: (output: OutputWidget) => boolean = () => true
): boolean | false {
return widget instanceof OutputWidget ? predicate(widget) : false;
}
protected async pick({ channels, placeholder }: { channels: OutputChannel[], placeholder: string }): Promise<OutputChannel | undefined> {
const items: Array<QuickPickValue<OutputChannel> | QuickPickItem | QuickPickSeparator> = [];
const outputChannels = nls.localize('theia/output/outputChannels', 'Output Channels');
const hiddenChannels = nls.localize('theia/output/hiddenChannels', 'Hidden Channels');
for (let i = 0; i < channels.length; i++) {
const channel = channels[i];
if (i === 0) {
items.push({ label: channel.isVisible ? outputChannels : hiddenChannels, type: 'separator' });
} else if (!channel.isVisible && channels[i - 1].isVisible) {
items.push({ label: hiddenChannels, type: 'separator' });
}
items.push({ label: channel.name, value: channel });
}
const selectedItem = await this.quickPickService.show(items, { placeholder });
return selectedItem && ('value' in selectedItem) ? selectedItem.value : undefined;
}
}