Skip to content

Commit

Permalink
GH-7347: Added scroll-lock to the Output view.
Browse files Browse the repository at this point in the history
Closes #7347.
Closes #7008.

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
  • Loading branch information
Akos Kitta committed Jun 3, 2020
1 parent 598d251 commit acb7be7
Show file tree
Hide file tree
Showing 22 changed files with 1,142 additions and 341 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export class SampleOutputChannelWithSeverity
channel.appendLine('hello info2', OutputChannelSeverity.Info);
channel.appendLine('hello error', OutputChannelSeverity.Error);
channel.appendLine('hello warning', OutputChannelSeverity.Warning);
channel.append('inlineInfo1 ');
channel.append('inlineWarning ', OutputChannelSeverity.Warning);
channel.append('inlineError ', OutputChannelSeverity.Error);
channel.append('inlineInfo2', OutputChannelSeverity.Info);
}
}
export const bindSampleOutputChannelWithSeverity = (bind: interfaces.Bind) => {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,4 @@
"vscode-eslint": "https://open-vsx.org/api/dbaeumer/vscode-eslint/2.1.1/file/dbaeumer.vscode-eslint-2.1.1.vsix",
"vscode-references-view": "https://open-vsx.org/api/ms-vscode/references-view/0.0.47/file/ms-vscode.references-view-0.0.47.vsix"
}
}
}
3 changes: 0 additions & 3 deletions packages/languages/compile.tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
{
"path": "../core/compile.tsconfig.json"
},
{
"path": "../output/compile.tsconfig.json"
},
{
"path": "../process/compile.tsconfig.json"
},
Expand Down
1 change: 0 additions & 1 deletion packages/languages/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"@theia/application-package": "^1.2.0",
"@theia/core": "^1.2.0",
"@theia/monaco-editor-core": "^0.19.3",
"@theia/output": "^1.2.0",
"@theia/process": "^1.2.0",
"@theia/workspace": "^1.2.0",
"@types/uuid": "^7.0.3",
Expand Down
43 changes: 23 additions & 20 deletions packages/languages/src/browser/window-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,22 @@
********************************************************************************/

import { injectable, inject } from 'inversify';
import { MessageService } from '@theia/core/lib/common';
import { MessageService, CommandRegistry } from '@theia/core/lib/common';
import { Window, OutputChannel, MessageActionItem, MessageType } from 'monaco-languageclient/lib/services';
import { OutputChannelManager } from '@theia/output/lib/common/output-channel';
import { OutputContribution } from '@theia/output/lib/browser/output-contribution';

@injectable()
export class WindowImpl implements Window {

private canAccessOutput: boolean | undefined;
protected static readonly NOOP_CHANNEL: OutputChannel = {
append: () => { },
appendLine: () => { },
dispose: () => { },
show: () => { }
};

@inject(MessageService) protected readonly messageService: MessageService;
@inject(OutputChannelManager) protected readonly outputChannelManager: OutputChannelManager;
@inject(OutputContribution) protected readonly outputContribution: OutputContribution;
@inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry;

showMessage<T extends MessageActionItem>(type: MessageType, message: string, ...actions: T[]): Thenable<T | undefined> {
const originalActions = new Map((actions || []).map(action => [action.title, action] as [string, T]));
Expand All @@ -52,22 +57,20 @@ export class WindowImpl implements Window {
}

createOutputChannel(name: string): OutputChannel {
const outputChannel = this.outputChannelManager.getChannel(name);
// Note: alternatively, we could add `@theia/output` as a `devDependency` and check, for instance,
// the manager for the output channels can be injected or not with `@optional()` but this approach has the same effect.
// The `@theia/languages` extension will be removed anyway: https://github.com/eclipse-theia/theia/issues/7100
if (this.canAccessOutput === undefined) {
this.canAccessOutput = !!this.commandRegistry.getCommand('output:append');
}
if (!this.canAccessOutput) {
return WindowImpl.NOOP_CHANNEL;
}
return {
append: outputChannel.append.bind(outputChannel),
appendLine: outputChannel.appendLine.bind(outputChannel),
show: async (preserveFocus?: boolean) => {
const options = Object.assign({
preserveFocus: false,
}, { preserveFocus });
const activate = !options.preserveFocus;
const reveal = options.preserveFocus;
await this.outputContribution.openView({ activate, reveal });
outputChannel.setVisibility(true);
},
dispose: () => {
this.outputChannelManager.deleteChannel(outputChannel.name);
}
append: text => this.commandRegistry.executeCommand('output:append', { name, text }),
appendLine: text => this.commandRegistry.executeCommand('output:appendLine', { name, text }),
dispose: () => this.commandRegistry.executeCommand('output:dispose', { name }),
show: (preserveFocus: boolean = false) => this.commandRegistry.executeCommand('output:show', { name, options: { preserveFocus } })
};
}
}
11 changes: 10 additions & 1 deletion packages/monaco/src/browser/monaco-context-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
********************************************************************************/

import { injectable, inject } from 'inversify';
import { MenuPath } from '@theia/core/lib/common/menu';
import { EDITOR_CONTEXT_MENU } from '@theia/editor/lib/browser';
import { ContextMenuRenderer, toAnchor } from '@theia/core/lib/browser';
import IContextMenuService = monaco.editor.IContextMenuService;
Expand All @@ -35,7 +36,11 @@ export class MonacoContextMenuService implements IContextMenuService {
// Actions for editor context menu come as 'MenuItemAction' items
// In case of 'Quick Fix' actions come as 'CodeActionAction' items
if (actions.length > 0 && actions[0] instanceof monaco.actions.MenuItemAction) {
this.contextMenuRenderer.render(EDITOR_CONTEXT_MENU, anchor, () => delegate.onHide(false));
this.contextMenuRenderer.render({
menuPath: this.menuPath(),
anchor,
onHide: () => delegate.onHide(false)
});
} else {
const commands = new CommandRegistry();
const menu = new Menu({
Expand All @@ -61,4 +66,8 @@ export class MonacoContextMenuService implements IContextMenuService {
}
}

protected menuPath(): MenuPath {
return EDITOR_CONTEXT_MENU;
}

}
27 changes: 20 additions & 7 deletions packages/monaco/src/browser/monaco-editor-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import URI from '@theia/core/lib/common/uri';
import { EditorPreferenceChange, EditorPreferences, TextEditor, DiffNavigator } from '@theia/editor/lib/browser';
import { DiffUris } from '@theia/core/lib/browser/diff-uris';
import { inject, injectable } from 'inversify';
import { inject, injectable, named } from 'inversify';
import { DisposableCollection, deepClone, Disposable, } from '@theia/core/lib/common';
import { MonacoToProtocolConverter, ProtocolToMonacoConverter, TextDocumentSaveReason } from 'monaco-languageclient';
import { MonacoCommandServiceFactory } from './monaco-command-service';
Expand All @@ -35,14 +35,24 @@ import { MonacoBulkEditService } from './monaco-bulk-edit-service';

import IEditorOverrideServices = monaco.editor.IEditorOverrideServices;
import { ApplicationServer } from '@theia/core/lib/common/application-protocol';
import { OS } from '@theia/core';
import { OS, ContributionProvider } from '@theia/core';
import { KeybindingRegistry, OpenerService, open, WidgetOpenerOptions } from '@theia/core/lib/browser';
import { MonacoResolvedKeybinding } from './monaco-resolved-keybinding';
import { HttpOpenHandlerOptions } from '@theia/core/lib/browser/http-open-handler';

export const MonacoEditorFactory = Symbol('MonacoEditorFactory');
export interface MonacoEditorFactory {
readonly scheme: string;
create(model: MonacoEditorModel, defaultOptions: MonacoEditor.IOptions, defaultOverrides: IEditorOverrideServices): MonacoEditor;
}

@injectable()
export class MonacoEditorProvider {

@inject(ContributionProvider)
@named(MonacoEditorFactory)
protected readonly factories: ContributionProvider<MonacoEditorFactory>;

@inject(MonacoBulkEditService)
protected readonly bulkEditService: MonacoBulkEditService;

Expand Down Expand Up @@ -123,18 +133,18 @@ export class MonacoEditorProvider {

async get(uri: URI): Promise<MonacoEditor> {
await this.editorPreferences.ready;
return this.doCreateEditor((override, toDispose) => this.createEditor(uri, override, toDispose));
return this.doCreateEditor(uri, (override, toDispose) => this.createEditor(uri, override, toDispose));
}

protected async doCreateEditor(factory: (override: IEditorOverrideServices, toDispose: DisposableCollection) => Promise<MonacoEditor>): Promise<MonacoEditor> {
protected async doCreateEditor(uri: URI, factory: (override: IEditorOverrideServices, toDispose: DisposableCollection) => Promise<MonacoEditor>): Promise<MonacoEditor> {
const commandService = this.commandServiceFactory();
const contextKeyService = this.contextKeyService.createScoped();
const { codeEditorService, textModelService, contextMenuService } = this;
const IWorkspaceEditService = this.bulkEditService;
const toDispose = new DisposableCollection(commandService);
const openerService = new monaco.services.OpenerService(codeEditorService, commandService);
openerService.registerOpener({
open: (uri, options) => this.interceptOpen(uri, options)
open: (u, options) => this.interceptOpen(u, options)
});
const editor = await factory({
codeEditorService,
Expand Down Expand Up @@ -245,7 +255,10 @@ export class MonacoEditorProvider {
protected async createMonacoEditor(uri: URI, override: IEditorOverrideServices, toDispose: DisposableCollection): Promise<MonacoEditor> {
const model = await this.getModel(uri, toDispose);
const options = this.createMonacoEditorOptions(model);
const editor = new MonacoEditor(uri, model, document.createElement('div'), this.services, options, override);
const factory = this.factories.getContributions().find(({ scheme }) => uri.scheme === scheme);
const editor = factory
? factory.create(model, options, override)
: new MonacoEditor(uri, model, document.createElement('div'), this.services, options, override);
toDispose.push(this.editorPreferences.onPreferenceChanged(event => {
if (event.affects(uri.toString(), model.languageId)) {
this.updateMonacoEditorOptions(editor, event);
Expand Down Expand Up @@ -471,7 +484,7 @@ export class MonacoEditorProvider {
}

async createInline(uri: URI, node: HTMLElement, options?: MonacoEditor.IOptions): Promise<MonacoEditor> {
return this.doCreateEditor(async (override, toDispose) => {
return this.doCreateEditor(uri, async (override, toDispose) => {
override.contextMenuService = {
showContextMenu: () => {/* no-op*/ }
};
Expand Down
9 changes: 6 additions & 3 deletions packages/monaco/src/browser/monaco-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ import { Languages, Workspace } from '@theia/languages/lib/browser';
import { TextEditorProvider, DiffNavigatorProvider } from '@theia/editor/lib/browser';
import { StrictEditorTextFocusContext } from '@theia/editor/lib/browser/editor-keybinding-contexts';
import { MonacoToProtocolConverter, ProtocolToMonacoConverter } from 'monaco-languageclient';
import { MonacoEditorProvider } from './monaco-editor-provider';
import { MonacoEditorProvider, MonacoEditorFactory } from './monaco-editor-provider';
import { MonacoEditorMenuContribution } from './monaco-menu';
import { MonacoEditorCommandHandlers } from './monaco-command';
import { MonacoKeybindingContribution } from './monaco-keybinding';
import { MonacoLanguages } from './monaco-languages';
import { MonacoWorkspace } from './monaco-workspace';
import { MonacoConfigurations } from './monaco-configurations';
import { MonacoEditorService } from './monaco-editor-service';
import { MonacoTextModelService } from './monaco-text-model-service';
import { MonacoTextModelService, MonacoEditorModelFactory } from './monaco-text-model-service';
import { MonacoContextMenuService } from './monaco-context-menu';
import { MonacoOutlineContribution } from './monaco-outline-contribution';
import { MonacoStatusBarContribution } from './monaco-status-bar-contribution';
Expand All @@ -63,6 +63,7 @@ import { MonacoEditorServices } from './monaco-editor';
import { MonacoColorRegistry } from './monaco-color-registry';
import { ColorRegistry } from '@theia/core/lib/browser/color-registry';
import { MonacoThemingService } from './monaco-theming-service';
import { bindContributionProvider } from '@theia/core';

decorate(injectable(), MonacoToProtocolConverter);
decorate(injectable(), ProtocolToMonacoConverter);
Expand Down Expand Up @@ -101,6 +102,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(MonacoContextMenuService).toSelf().inSingletonScope();
bind(MonacoEditorServices).toSelf().inSingletonScope();
bind(MonacoEditorProvider).toSelf().inSingletonScope();
bindContributionProvider(bind, MonacoEditorFactory);
bindContributionProvider(bind, MonacoEditorModelFactory);
bind(MonacoCommandService).toSelf().inTransientScope();
bind(MonacoCommandServiceFactory).toAutoFactory(MonacoCommandService);
bind(TextEditorProvider).toProvider(context =>
Expand Down Expand Up @@ -151,7 +154,7 @@ export function createMonacoConfigurationService(container: interfaces.Container

_configuration.getValue = (section, overrides, workspace) => {
const overrideIdentifier = overrides && 'overrideIdentifier' in overrides && overrides['overrideIdentifier'] as string || undefined;
const resourceUri = overrides && 'resource' in overrides && overrides['resource'].toString();
const resourceUri = overrides && 'resource' in overrides && !!overrides['resource'] && overrides['resource'].toString();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const proxy = createPreferenceProxy<{ [key: string]: any }>(preferences, preferenceSchemaProvider.getCombinedSchema(), {
resourceUri, overrideIdentifier, style: 'both'
Expand Down
32 changes: 28 additions & 4 deletions packages/monaco/src/browser/monaco-text-model-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,26 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { inject, injectable } from 'inversify';
import { inject, injectable, named } from 'inversify';
import { MonacoToProtocolConverter, ProtocolToMonacoConverter } from 'monaco-languageclient';
import URI from '@theia/core/lib/common/uri';
import { ResourceProvider, ReferenceCollection, Event } from '@theia/core';
import { ResourceProvider, ReferenceCollection, Event, MaybePromise, Resource, ContributionProvider } from '@theia/core';
import { EditorPreferences, EditorPreferenceChange } from '@theia/editor/lib/browser';
import { MonacoEditorModel } from './monaco-editor-model';
import IReference = monaco.editor.IReference;
export { IReference };

export const MonacoEditorModelFactory = Symbol('MonacoEditorModelFactory');
export interface MonacoEditorModelFactory {

readonly scheme: string;

createModel(
resource: Resource,
options?: { encoding?: string | undefined }
): MaybePromise<MonacoEditorModel>;

}

@injectable()
export class MonacoTextModelService implements monaco.editor.ITextModelService {
Expand All @@ -40,6 +54,10 @@ export class MonacoTextModelService implements monaco.editor.ITextModelService {
@inject(ProtocolToMonacoConverter)
protected readonly p2m: ProtocolToMonacoConverter;

@inject(ContributionProvider)
@named(MonacoEditorModelFactory)
protected readonly factories: ContributionProvider<MonacoEditorModelFactory>;

get models(): MonacoEditorModel[] {
return this._models.values();
}
Expand All @@ -52,21 +70,27 @@ export class MonacoTextModelService implements monaco.editor.ITextModelService {
return this._models.onDidCreate;
}

createModelReference(raw: monaco.Uri | URI): Promise<monaco.editor.IReference<MonacoEditorModel>> {
createModelReference(raw: monaco.Uri | URI): Promise<IReference<MonacoEditorModel>> {
return this._models.acquire(raw.toString());
}

protected async loadModel(uri: URI): Promise<MonacoEditorModel> {
await this.editorPreferences.ready;
const resource = await this.resourceProvider(uri);
const model = await (new MonacoEditorModel(resource, this.m2p, this.p2m, { encoding: this.editorPreferences.get('files.encoding') }).load());
const model = await (await this.createModel(resource)).load();
this.updateModel(model);
model.textEditorModel.onDidChangeLanguage(() => this.updateModel(model));
const disposable = this.editorPreferences.onPreferenceChanged(change => this.updateModel(model, change));
model.onDispose(() => disposable.dispose());
return model;
}

protected createModel(resource: Resource): MaybePromise<MonacoEditorModel> {
const options = { encoding: this.editorPreferences.get('files.encoding') };
const factory = this.factories.getContributions().find(({ scheme }) => resource.uri.scheme === scheme);
return factory ? factory.createModel(resource, options) : new MonacoEditorModel(resource, this.m2p, this.p2m, options);
}

protected readonly modelOptions: { [name: string]: (keyof monaco.editor.ITextModelUpdateOptions | undefined) } = {
'editor.tabSize': 'tabSize',
'editor.insertSpaces': 'insertSpaces'
Expand Down
6 changes: 6 additions & 0 deletions packages/output/compile.tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
"references": [
{
"path": "../core/compile.tsconfig.json"
},
{
"path": "../monaco/compile.tsconfig.json"
},
{
"path": "../editor/compile.tsconfig.json"
}
]
}
6 changes: 5 additions & 1 deletion packages/output/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
"version": "1.2.0",
"description": "Theia - Output Extension",
"dependencies": {
"@theia/core": "^1.2.0"
"@theia/core": "^1.2.0",
"@theia/editor": "^1.2.0",
"@theia/monaco": "^1.2.0",
"@types/p-queue": "^2.3.1",
"p-queue": "^2.4.2"
},
"publishConfig": {
"access": "public"
Expand Down
34 changes: 34 additions & 0 deletions packages/output/src/browser/output-context-menu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/********************************************************************************
* Copyright (C) 2020 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 WITH Classpath-exception-2.0
********************************************************************************/
import { injectable } from 'inversify';
import { MenuPath } from '@theia/core/lib/common';
import { MonacoContextMenuService } from '@theia/monaco/lib/browser/monaco-context-menu';

export namespace OutputContextMenu {
export const MENU_PATH: MenuPath = ['output_context_menu'];
export const TEXT_EDIT_GROUP = [...MENU_PATH, '0_text_edit_group'];
export const COMMAND_GROUP = [...MENU_PATH, '1_command_group'];
export const WIDGET_GROUP = [...MENU_PATH, '2_widget_group'];
}

@injectable()
export class OutputContextMenuService extends MonacoContextMenuService {

protected menuPath(): MenuPath {
return OutputContextMenu.MENU_PATH;
}

}
Loading

0 comments on commit acb7be7

Please sign in to comment.