deploy: current vibn theia state
Some checks failed
Playwright Tests / Playwright Tests (ubuntu-22.04, Node.js 22.x) (push) Has been cancelled
3PP License Check / 3PP License Check (11, 22.x, ubuntu-22.04) (push) Has been cancelled
Publish packages to NPM / Perform Publishing (push) Has been cancelled

Made-with: Cursor
This commit is contained in:
2026-02-27 12:01:08 -08:00
commit 8bb5110148
3782 changed files with 640947 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
// *****************************************************************************
// 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 * as React from '@theia/core/shared/react';
import * as DOMPurify from '@theia/core/shared/dompurify';
import { ConsoleItem } from './console-session';
import { Severity } from '@theia/core/lib/common/severity';
import Anser = require('anser');
export class AnsiConsoleItem implements ConsoleItem {
protected readonly htmlContent: string;
constructor(
public readonly content: string,
public readonly severity?: Severity
) {
this.htmlContent = new Anser().ansiToHtml(this.content, {
use_classes: true,
remove_empty: true
});
}
get visible(): boolean {
return !!this.htmlContent;
}
render(): React.ReactNode {
return <div
className='theia-console-ansi-console-item'
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(this.htmlContent) }} // eslint-disable-line react/no-danger
/>;
}
}

View File

@@ -0,0 +1,68 @@
// *****************************************************************************
// 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 { interfaces, Container, injectable } from '@theia/core/shared/inversify';
import { MenuPath } from '@theia/core';
import { TreeProps } from '@theia/core/lib/browser/tree';
import { SourceTreeWidget, TreeElementNode } from '@theia/core/lib/browser/source-tree';
import { ConsoleItem } from './console-session';
import { Severity } from '@theia/core/lib/common/severity';
@injectable()
export class ConsoleContentWidget extends SourceTreeWidget {
static CONTEXT_MENU: MenuPath = ['console-context-menu'];
static override createContainer(parent: interfaces.Container, props?: Partial<TreeProps>): Container {
const child = SourceTreeWidget.createContainer(parent, {
contextMenuPath: ConsoleContentWidget.CONTEXT_MENU,
viewProps: {
followOutput: true
},
...props
});
child.unbind(SourceTreeWidget);
child.bind(ConsoleContentWidget).toSelf();
return child;
}
protected override createTreeElementNodeClassNames(node: TreeElementNode): string[] {
const classNames = super.createTreeElementNodeClassNames(node);
if (node.element) {
const className = this.toClassName((node.element as ConsoleItem));
if (className) {
classNames.push(className);
}
}
return classNames;
}
protected toClassName(item: ConsoleItem): string | undefined {
if (item.severity === Severity.Error) {
return ConsoleItem.errorClassName;
}
if (item.severity === Severity.Warning) {
return ConsoleItem.warningClassName;
}
if (item.severity === Severity.Info) {
return ConsoleItem.infoClassName;
}
if (item.severity === Severity.Log) {
return ConsoleItem.logClassName;
}
return undefined;
}
}

View File

@@ -0,0 +1,143 @@
// *****************************************************************************
// 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 } from '@theia/core/shared/inversify';
import { Command, CommandContribution, CommandRegistry, MenuContribution, MenuModelRegistry, CommandHandler } from '@theia/core';
import { FrontendApplicationContribution, KeybindingContribution, KeybindingRegistry, CommonCommands } from '@theia/core/lib/browser';
import { ConsoleManager } from './console-manager';
import { ConsoleWidget } from './console-widget';
import { ConsoleContentWidget } from './console-content-widget';
import { nls } from '@theia/core/lib/common/nls';
export namespace ConsoleCommands {
export const SELECT_ALL: Command = {
id: 'console.selectAll'
};
export const COLLAPSE_ALL: Command = {
id: 'console.collapseAll'
};
export const CLEAR: Command = {
id: 'console.clear'
};
export const EXECUTE: Command = {
id: 'console.execute'
};
export const NAVIGATE_BACK: Command = {
id: 'console.navigatePrevious'
};
export const NAVIGATE_FORWARD: Command = {
id: 'console.navigateNext'
};
}
export namespace ConsoleContextMenu {
export const CLIPBOARD = [...ConsoleContentWidget.CONTEXT_MENU, '1_clipboard'];
export const CLEAR = [...ConsoleContentWidget.CONTEXT_MENU, '2_clear'];
}
@injectable()
export class ConsoleContribution implements FrontendApplicationContribution, CommandContribution, KeybindingContribution, MenuContribution {
@inject(ConsoleManager)
protected readonly manager: ConsoleManager;
initialize(): void { }
registerCommands(commands: CommandRegistry): void {
commands.registerCommand(ConsoleCommands.SELECT_ALL, this.newCommandHandler(console => console.selectAll()));
commands.registerCommand(ConsoleCommands.COLLAPSE_ALL, this.newCommandHandler(console => console.collapseAll()));
commands.registerCommand(ConsoleCommands.CLEAR, this.newCommandHandler(console => console.clear()));
commands.registerCommand(ConsoleCommands.EXECUTE, this.newCommandHandler(console => console.execute()));
commands.registerCommand(ConsoleCommands.NAVIGATE_BACK, this.newCommandHandler(console => console.navigateBack()));
commands.registerCommand(ConsoleCommands.NAVIGATE_FORWARD, this.newCommandHandler(console => console.navigateForward()));
}
registerKeybindings(keybindings: KeybindingRegistry): void {
keybindings.registerKeybinding({
command: ConsoleCommands.SELECT_ALL.id,
keybinding: 'ctrlcmd+a',
when: 'consoleContentFocus'
});
keybindings.registerKeybinding({
command: ConsoleCommands.EXECUTE.id,
keybinding: 'enter',
when: 'consoleInputFocus'
});
keybindings.registerKeybinding({
command: ConsoleCommands.NAVIGATE_BACK.id,
keybinding: 'up',
when: 'consoleInputFocus && consoleNavigationBackEnabled'
});
keybindings.registerKeybinding({
command: ConsoleCommands.NAVIGATE_FORWARD.id,
keybinding: 'down',
when: 'consoleInputFocus && consoleNavigationForwardEnabled'
});
}
registerMenus(menus: MenuModelRegistry): void {
menus.registerMenuAction(ConsoleContextMenu.CLIPBOARD, {
commandId: CommonCommands.COPY.id,
label: CommonCommands.COPY.label,
order: 'a1',
});
menus.registerMenuAction(ConsoleContextMenu.CLIPBOARD, {
commandId: ConsoleCommands.SELECT_ALL.id,
label: CommonCommands.SELECT_ALL.label,
order: 'a2'
});
menus.registerMenuAction(ConsoleContextMenu.CLIPBOARD, {
commandId: ConsoleCommands.COLLAPSE_ALL.id,
label: nls.localizeByDefault('Collapse All'),
order: 'a3'
});
menus.registerMenuAction(ConsoleContextMenu.CLEAR, {
commandId: ConsoleCommands.CLEAR.id,
label: nls.localizeByDefault('Clear Console')
});
}
protected newCommandHandler(execute: ConsoleExecuteFunction): ConsoleCommandHandler {
return new ConsoleCommandHandler(this.manager, execute);
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type ConsoleExecuteFunction = (console: ConsoleWidget, ...args: any[]) => any;
export class ConsoleCommandHandler implements CommandHandler {
constructor(
protected readonly manager: ConsoleManager,
protected readonly doExecute: ConsoleExecuteFunction
) { }
isEnabled(): boolean {
return !!this.manager.currentConsole;
}
isVisible(): boolean {
return !!this.manager.currentConsole;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
execute(...args: any[]): any {
const { currentConsole } = this.manager;
if (currentConsole) {
return this.doExecute(currentConsole, ...args);
}
}
}

View File

@@ -0,0 +1,32 @@
// *****************************************************************************
// 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 { ContainerModule } from '@theia/core/shared/inversify';
import { CommandContribution, MenuContribution } from '@theia/core';
import { FrontendApplicationContribution, KeybindingContribution } from '@theia/core/lib/browser';
import { ConsoleContribution } from './console-contribution';
import { ConsoleManager } from './console-manager';
import '../../src/browser/style/index.css';
export default new ContainerModule(bind => {
bind(ConsoleManager).toSelf().inSingletonScope();
bind(ConsoleContribution).toSelf().inSingletonScope();
bind(FrontendApplicationContribution).toService(ConsoleContribution);
bind(CommandContribution).toService(ConsoleContribution);
bind(KeybindingContribution).toService(ConsoleContribution);
bind(MenuContribution).toService(ConsoleContribution);
});

View File

@@ -0,0 +1,76 @@
// *****************************************************************************
// 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 } from '@theia/core/shared/inversify';
@injectable()
export class ConsoleHistory {
static limit = 50;
protected values: string[] = [];
protected index = -1;
push(value: string): void {
this.delete(value);
this.values.push(value);
this.trim();
this.index = this.values.length;
}
protected delete(value: string): void {
const index = this.values.indexOf(value);
if (index !== -1) {
this.values.splice(index, 1);
}
}
protected trim(): void {
const index = this.values.length - ConsoleHistory.limit;
if (index > 0) {
this.values = this.values.slice(index);
}
}
get current(): string | undefined {
return this.values[this.index];
}
get previous(): string | undefined {
this.index = Math.max(this.index - 1, -1);
return this.current;
}
get next(): string | undefined {
this.index = Math.min(this.index + 1, this.values.length);
return this.current;
}
store(): ConsoleHistory.Data {
const { values, index } = this;
return { values, index };
}
restore(object: ConsoleHistory): void {
this.values = object.values;
this.index = object.index;
}
}
export namespace ConsoleHistory {
export interface Data {
values: string[],
index: number
}
}

View File

@@ -0,0 +1,37 @@
// *****************************************************************************
// 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 } from '@theia/core/shared/inversify';
import { ApplicationShell } from '@theia/core/lib/browser';
import { ConsoleWidget } from './console-widget';
@injectable()
export class ConsoleManager {
@inject(ApplicationShell)
protected readonly shell: ApplicationShell;
get activeConsole(): ConsoleWidget | undefined {
const widget = this.shell.activeWidget;
return widget instanceof ConsoleWidget ? widget : undefined;
}
get currentConsole(): ConsoleWidget | undefined {
const widget = this.shell.currentWidget;
return widget instanceof ConsoleWidget ? widget : undefined;
}
}

View File

@@ -0,0 +1,121 @@
// *****************************************************************************
// Copyright (C) 2021 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 } from '@theia/core/shared/inversify';
import { Emitter, Event, Disposable, DisposableCollection } from '@theia/core';
import { ConsoleSession } from './console-session';
import { Severity } from '@theia/core/lib/common/severity';
@injectable()
export class ConsoleSessionManager implements Disposable {
protected readonly sessions = new Map<string, ConsoleSession>();
protected _selectedSession: ConsoleSession | undefined;
protected _severity: Severity | undefined;
protected readonly sessionAddedEmitter = new Emitter<ConsoleSession>();
protected readonly sessionDeletedEmitter = new Emitter<ConsoleSession>();
protected readonly sessionWasShownEmitter = new Emitter<ConsoleSession>();
protected readonly sessionWasHiddenEmitter = new Emitter<ConsoleSession>();
protected readonly selectedSessionChangedEmitter = new Emitter<ConsoleSession | undefined>();
protected readonly severityChangedEmitter = new Emitter<void>();
get onDidAddSession(): Event<ConsoleSession> {
return this.sessionAddedEmitter.event;
}
get onDidDeleteSession(): Event<ConsoleSession> {
return this.sessionDeletedEmitter.event;
}
get onDidShowSession(): Event<ConsoleSession> {
return this.sessionWasShownEmitter.event;
}
get onDidHideSession(): Event<ConsoleSession> {
return this.sessionWasHiddenEmitter.event;
}
get onDidChangeSelectedSession(): Event<ConsoleSession | undefined> {
return this.selectedSessionChangedEmitter.event;
}
get onDidChangeSeverity(): Event<void> {
return this.severityChangedEmitter.event;
}
protected readonly toDispose = new DisposableCollection();
protected readonly toDisposeOnSessionDeletion = new Map<string, Disposable>();
dispose(): void {
this.toDispose.dispose();
}
get severity(): Severity | undefined {
return this._severity;
}
set severity(value: Severity | undefined) {
value = value || Severity.Ignore;
this._severity = value;
for (const session of this.sessions.values()) {
session.severity = value;
}
this.severityChangedEmitter.fire(undefined);
}
get all(): ConsoleSession[] {
return Array.from(this.sessions.values());
}
get selectedSession(): ConsoleSession | undefined {
return this._selectedSession;
}
set selectedSession(session: ConsoleSession | undefined) {
const oldSession = this.selectedSession;
this._selectedSession = session;
this.selectedSessionChangedEmitter.fire(session);
if (oldSession !== session) {
if (oldSession) {
this.sessionWasHiddenEmitter.fire(oldSession);
}
if (session) {
this.sessionWasShownEmitter.fire(session);
}
}
}
get(id: string): ConsoleSession | undefined {
return this.sessions.get(id);
}
add(session: ConsoleSession): void {
this.sessions.set(session.id, session);
this.sessionAddedEmitter.fire(session);
if (this.sessions.size === 1) {
this.selectedSession = session;
}
}
delete(id: string): void {
const session = this.sessions.get(id);
if (this.sessions.delete(id) && session) {
if (this.selectedSession === session) {
// select a new sessions or undefined if none are left
this.selectedSession = this.sessions.values().next().value;
}
session.dispose();
this.sessionDeletedEmitter.fire(session);
}
}
}

View File

@@ -0,0 +1,78 @@
// *****************************************************************************
// 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 } from '@theia/core/shared/inversify';
import { MaybePromise } from '@theia/core/lib/common/types';
import { TreeSource, TreeElement, CompositeTreeElement } from '@theia/core/lib/browser/source-tree';
import { Emitter, Event } from '@theia/core/lib/common/event';
import { Severity } from '@theia/core/lib/common/severity';
export interface ConsoleItem extends TreeElement {
readonly severity?: Severity;
}
export namespace ConsoleItem {
export const errorClassName = 'theia-console-error';
export const warningClassName = 'theia-console-warning';
export const infoClassName = 'theia-console-info';
export const logClassName = 'theia-console-log';
}
export interface CompositeConsoleItem extends ConsoleItem, CompositeTreeElement {
getElements(): MaybePromise<IterableIterator<ConsoleItem>>
}
@injectable()
export abstract class ConsoleSession extends TreeSource {
protected selectedSeverity?: Severity;
protected filterTextValue?: string;
protected readonly selectionEmitter: Emitter<void> = new Emitter<void>();
protected readonly filterEmitter: Emitter<void> = new Emitter<void>();
readonly onSelectionChange: Event<void> = this.selectionEmitter.event;
readonly onFilterChange: Event<void> = this.filterEmitter.event;
override id: string;
get severity(): Severity | undefined {
return this.selectedSeverity;
}
set severity(severity: Severity | undefined) {
if (severity === this.selectedSeverity) {
return;
}
this.selectedSeverity = severity;
this.selectionEmitter.fire(undefined);
this.fireDidChange();
}
get filterText(): string | undefined {
return this.filterTextValue;
}
set filterText(value: string | undefined) {
const normalized = value?.trim() || undefined;
if (normalized === this.filterTextValue) {
return;
}
this.filterTextValue = normalized;
this.filterEmitter.fire(undefined);
this.fireDidChange();
}
abstract override getElements(): MaybePromise<IterableIterator<ConsoleItem>>;
abstract execute(value: string): MaybePromise<void>;
abstract clear(): MaybePromise<void>;
}

View File

@@ -0,0 +1,351 @@
// *****************************************************************************
// 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 { ElementExt } from '@theia/core/shared/@lumino/domutils';
import { injectable, inject, postConstruct, interfaces, Container } from '@theia/core/shared/inversify';
import { TreeSourceNode } from '@theia/core/lib/browser/source-tree';
import { ContextKeyService, ContextKey } from '@theia/core/lib/browser/context-key-service';
import { BaseWidget, PanelLayout, Widget, Message, MessageLoop, StatefulWidget, CompositeTreeNode } from '@theia/core/lib/browser';
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
import URI from '@theia/core/lib/common/uri';
import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
import { ConsoleHistory } from './console-history';
import { ConsoleContentWidget } from './console-content-widget';
import { ConsoleSession } from './console-session';
import { ConsoleSessionManager } from './console-session-manager';
import * as monaco from '@theia/monaco-editor-core';
import { Disposable } from '@theia/core/lib/common/disposable';
import { EditorManager } from '@theia/editor/lib/browser';
import { MonacoEditorService } from '@theia/monaco/lib/browser/monaco-editor-service';
export const ConsoleOptions = Symbol('ConsoleWidgetOptions');
export interface ConsoleOptions {
id: string
title?: {
label?: string
iconClass?: string
caption?: string
}
input: {
uri: URI
options?: MonacoEditor.IOptions
}
inputFocusContextKey?: ContextKey<boolean>
}
@injectable()
export class ConsoleWidget extends BaseWidget implements StatefulWidget {
static styles = {
node: 'theia-console-widget',
content: 'theia-console-content',
input: 'theia-console-input',
};
static createContainer(parent: interfaces.Container, options: ConsoleOptions): Container {
const child = ConsoleContentWidget.createContainer(parent);
child.bind(ConsoleHistory).toSelf();
child.bind(ConsoleOptions).toConstantValue(options);
child.bind(ConsoleWidget).toSelf();
return child;
}
@inject(ConsoleOptions)
protected readonly options: ConsoleOptions;
@inject(ConsoleContentWidget)
readonly content: ConsoleContentWidget;
@inject(ConsoleHistory)
protected readonly history: ConsoleHistory;
@inject(ConsoleSessionManager)
protected readonly sessionManager: ConsoleSessionManager;
@inject(MonacoEditorProvider)
protected readonly editorProvider: MonacoEditorProvider;
@inject(ContextKeyService)
protected readonly contextKeyService: ContextKeyService;
@inject(MonacoEditorService)
protected readonly editorService: MonacoEditorService;
@inject(EditorManager)
protected readonly editorManager: EditorManager;
protected _input: MonacoEditor;
protected _inputFocusContextKey: ContextKey<boolean>;
protected modelChangeListener = Disposable.NULL;
protected _ready: Promise<void> | undefined;
get ready(): Promise<void> {
if (!this._ready) {
throw new Error('ready must not be accessed in the construction phase');
}
return this._ready;
}
constructor() {
super();
this.node.classList.add(ConsoleWidget.styles.node);
}
@postConstruct()
protected init(): void {
this._ready = this.doInit();
}
protected async doInit(): Promise<void> {
const { id, title, inputFocusContextKey } = this.options;
const { label, iconClass, caption } = Object.assign({}, title);
this.id = id;
this.title.closable = true;
this.title.label = label || id;
if (iconClass) {
this.title.iconClass = iconClass;
}
this.title.caption = caption || label || id;
const layout = this.layout = new PanelLayout();
this.content.node.classList.add(ConsoleWidget.styles.content);
this.toDispose.push(this.content);
layout.addWidget(this.content);
const inputWidget = new Widget();
inputWidget.node.classList.add(ConsoleWidget.styles.input);
layout.addWidget(inputWidget);
const input = this._input = await this.createInput(inputWidget.node);
this.toDispose.push(input);
this.toDispose.push(input.getControl().onDidLayoutChange(() => this.resizeContent()));
this.toDispose.push(input.getControl().onDidChangeConfiguration(event => {
if (event.hasChanged(monaco.editor.EditorOption.fontInfo)) {
this.updateFont();
}
}));
this.session = this.sessionManager.selectedSession;
this.toDispose.push(this.sessionManager.onDidChangeSelectedSession(session => {
// Do not clear the session output when `undefined`.
if (session) {
this.session = session;
}
}));
this.updateFont();
if (inputFocusContextKey) {
this.toDispose.push(input.onFocusChanged(() => inputFocusContextKey.set(this.hasInputFocus())));
this.toDispose.push(input.onCursorPositionChanged(() => input.getControl().createContextKey('consoleNavigationBackEnabled', this.consoleNavigationBackEnabled)));
this.toDispose.push(input.onCursorPositionChanged(() => input.getControl().createContextKey('consoleNavigationForwardEnabled', this.consoleNavigationForwardEnabled)));
}
input.getControl().createContextKey('consoleInputFocus', true);
const contentContext = this.contextKeyService.createScoped(this.content.node);
contentContext.setContext('consoleContentFocus', true);
this.toDispose.pushAll([
this.editorManager.onActiveEditorChanged(() => this.setMode()),
this.onDidChangeVisibility(() => this.setMode())
]);
}
protected createInput(node: HTMLElement): Promise<MonacoEditor> {
return this.editorProvider.createInline(this.options.input.uri, node, this.options.input.options);
}
protected updateFont(): void {
const { fontFamily, fontSize, lineHeight } = this._input.getControl().getOption(monaco.editor.EditorOption.fontInfo);
this.content.node.style.fontFamily = fontFamily;
this.content.node.style.fontSize = fontSize + 'px';
this.content.node.style.lineHeight = lineHeight + 'px';
}
protected _session: ConsoleSession | undefined;
set session(session: ConsoleSession | undefined) {
if (this._session === session) {
return;
}
this._session = session;
this.content.source = session;
}
get session(): ConsoleSession | undefined {
return this._session;
}
get input(): MonacoEditor {
return this._input;
}
get consoleNavigationBackEnabled(): boolean {
const editor = this.input.getControl();
return !!editor.getPosition()!.equals({ lineNumber: 1, column: 1 });
}
get consoleNavigationForwardEnabled(): boolean {
const editor = this.input.getControl();
const model = editor.getModel();
if (!model) {
return false;
}
const lineNumber = editor.getModel()!.getLineCount();
const column = editor.getModel()!.getLineMaxColumn(lineNumber);
return !!editor.getPosition()!.equals({ lineNumber, column });
}
selectAll(): void {
const selection = document.getSelection();
if (selection) {
selection.selectAllChildren(this.content.node);
}
}
collapseAll(): void {
const { root } = this.content.model;
if (CompositeTreeNode.is(root)) {
this.content.model.collapseAll(root);
}
}
clear(): void {
if (this.session) {
this.session.clear();
}
}
async execute(value?: string): Promise<void> {
if (value === undefined) {
value = this._input.getControl().getValue();
this._input.getControl().setValue('');
}
this.history.push(value);
if (this.session) {
const listener = this.content.model.onNodeRefreshed(() => {
listener.dispose();
this.revealLastOutput();
});
await this.session.execute(value);
}
}
navigateBack(): void {
const value = this.history.previous;
if (value === undefined) {
return;
}
const editor = this.input.getControl();
editor.setValue(value);
editor.setPosition({
lineNumber: 1,
column: 1
});
}
navigateForward(): void {
const value = this.history.next || '';
const editor = this.input.getControl();
editor.setValue(value);
const lineNumber = editor.getModel()!.getLineCount();
const column = editor.getModel()!.getLineMaxColumn(lineNumber);
editor.setPosition({ lineNumber, column });
}
protected revealLastOutput(): void {
const { root } = this.content.model;
if (TreeSourceNode.is(root)) {
this.content.model.selectNode(root.children[root.children.length - 1]);
}
}
protected override onActivateRequest(msg: Message): void {
super.onActivateRequest(msg);
this._input.focus();
}
protected totalHeight = -1;
protected totalWidth = -1;
protected override onResize(msg: Widget.ResizeMessage): void {
super.onResize(msg);
this.totalWidth = msg.width;
this.totalHeight = msg.height;
this._input.resizeToFit();
this.resizeContent();
}
protected resizeContent(): void {
this.totalHeight = this.totalHeight < 0 ? this.computeHeight() : this.totalHeight;
const inputHeight = this._input.getControl().getLayoutInfo().height;
const contentHeight = this.totalHeight - inputHeight;
this.content.node.style.height = `${contentHeight}px`;
MessageLoop.sendMessage(this.content, new Widget.ResizeMessage(this.totalWidth, contentHeight));
}
protected computeHeight(): number {
const { verticalSum } = ElementExt.boxSizing(this.node);
return this.node.offsetHeight - verticalSum;
}
storeState(): object {
const history = this.history.store();
const input = this.input.storeViewState();
return {
history,
input
};
}
restoreState(oldState: object): void {
if ('history' in oldState) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.history.restore((<any>oldState)['history']);
}
this.input.getControl().setValue(this.history.current || '');
if ('input' in oldState) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.input.restoreViewState((<any>oldState)['input']);
}
}
hasInputFocus(): boolean {
return this._input && this._input.isFocused({ strict: true });
}
override dispose(): void {
super.dispose();
this.modelChangeListener.dispose();
}
// To set the active language for the console input text model.
// https://github.com/microsoft/vscode/blob/2af422737386e792c3fcde7884f9bf47a1aff2f5/src/vs/workbench/contrib/debug/browser/repl.ts#L371-L384
protected setMode(): void {
if (this.isHidden) {
return;
}
const activeEditorControl = this.editorService.getActiveCodeEditor();
if (activeEditorControl) {
this.modelChangeListener.dispose();
this.modelChangeListener = activeEditorControl.onDidChangeModelLanguage(() => this.setMode());
const consoleModel = this._input.getControl().getModel();
const activeEditorModel = activeEditorControl.getModel();
if (consoleModel && activeEditorModel) {
monaco.editor.setModelLanguage(consoleModel, activeEditorModel.getLanguageId());
}
}
}
}

View File

@@ -0,0 +1,49 @@
/********************************************************************************
* 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
********************************************************************************/
.theia-console-content {
font-size: var(--theia-code-font-size);
line-height: var(--theia-code-line-height);
font-family: var(--theia-code-font-family);
}
.theia-console-input {
padding-left: 20px;
border-top: var(--theia-panel-border-width) solid var(--theia-panel-border);
height: calc(var(--theia-content-line-height) * 2);
display: flex;
align-items: center;
}
.theia-console-input:before {
left: 8px;
top: 3px;
position: absolute;
content: "\276f";
line-height: 18px;
}
.theia-console-error {
color: var(--theia-errorForeground);
}
.theia-console-warning {
color: var(--theia-editorWarning-foreground);
}
.theia-console-ansi-console-item {
white-space: pre-wrap;
}

View File

@@ -0,0 +1,28 @@
// *****************************************************************************
// 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
// *****************************************************************************
/* note: this bogus test file is required so that
we are able to run mocha unit tests on this
package, without having any actual unit tests in it.
This way a coverage report will be generated,
showing 0% coverage, instead of no report.
This file can be removed once we have real unit
tests in place. */
describe('console package', () => {
it('support code coverage statistics', () => true);
});