224 lines
10 KiB
TypeScript
224 lines
10 KiB
TypeScript
// *****************************************************************************
|
|
// Copyright (C) 2017 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 { EditorManager } from './editor-manager';
|
|
import { TextEditor } from './editor';
|
|
import { injectable, inject, optional, named } from '@theia/core/shared/inversify';
|
|
import { StatusBarAlignment, StatusBar } from '@theia/core/lib/browser/status-bar/status-bar';
|
|
import {
|
|
FrontendApplicationContribution, DiffUris, DockLayout,
|
|
QuickInputService, KeybindingRegistry, KeybindingContribution, SHELL_TABBAR_CONTEXT_SPLIT, ApplicationShell,
|
|
WidgetStatusBarContribution,
|
|
Widget,
|
|
OpenWithService
|
|
} from '@theia/core/lib/browser';
|
|
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
|
|
import { CommandHandler, DisposableCollection, MenuContribution, MenuModelRegistry, ContributionProvider, Prioritizeable } from '@theia/core';
|
|
import { EditorCommands } from './editor-command';
|
|
import { CommandRegistry, CommandContribution } from '@theia/core/lib/common';
|
|
import { SUPPORTED_ENCODINGS } from '@theia/core/lib/common/supported-encodings';
|
|
import { nls } from '@theia/core/lib/common/nls';
|
|
import { CurrentWidgetCommandAdapter } from '@theia/core/lib/browser/shell/current-widget-command-adapter';
|
|
import { EditorWidget } from './editor-widget';
|
|
import { EditorLanguageStatusService } from './language-status/editor-language-status-service';
|
|
import { QuickEditorService } from './quick-editor-service';
|
|
import { SplitEditorContribution } from './split-editor-contribution';
|
|
|
|
@injectable()
|
|
export class EditorContribution implements FrontendApplicationContribution,
|
|
CommandContribution, KeybindingContribution, MenuContribution, WidgetStatusBarContribution<EditorWidget> {
|
|
|
|
@inject(EditorManager) protected readonly editorManager: EditorManager;
|
|
@inject(OpenWithService) protected readonly openWithService: OpenWithService;
|
|
@inject(EditorLanguageStatusService) protected readonly languageStatusService: EditorLanguageStatusService;
|
|
@inject(ApplicationShell) protected readonly shell: ApplicationShell;
|
|
|
|
@inject(ContextKeyService)
|
|
protected readonly contextKeyService: ContextKeyService;
|
|
|
|
@inject(QuickInputService) @optional()
|
|
protected readonly quickInputService: QuickInputService;
|
|
|
|
@inject(ContributionProvider) @named(SplitEditorContribution)
|
|
protected readonly splitEditorContributions: ContributionProvider<SplitEditorContribution>;
|
|
|
|
onStart(): void {
|
|
this.initEditorContextKeys();
|
|
this.openWithService.registerHandler({
|
|
id: 'default',
|
|
label: this.editorManager.label,
|
|
providerName: nls.localizeByDefault('Built-in'),
|
|
canHandle: () => 100,
|
|
// Higher priority than any other handler
|
|
// so that the text editor always appears first in the quick pick
|
|
getOrder: () => 10000,
|
|
open: uri => this.editorManager.open(uri)
|
|
});
|
|
}
|
|
|
|
protected initEditorContextKeys(): void {
|
|
const editorIsOpen = this.contextKeyService.createKey<boolean>('editorIsOpen', false);
|
|
const textCompareEditorVisible = this.contextKeyService.createKey<boolean>('textCompareEditorVisible', false);
|
|
const updateContextKeys = () => {
|
|
const widgets = this.editorManager.all;
|
|
editorIsOpen.set(!!widgets.length);
|
|
textCompareEditorVisible.set(widgets.some(widget => DiffUris.isDiffUri(widget.editor.uri)));
|
|
};
|
|
updateContextKeys();
|
|
for (const widget of this.editorManager.all) {
|
|
widget.disposed.connect(updateContextKeys);
|
|
}
|
|
this.editorManager.onCreated(widget => {
|
|
updateContextKeys();
|
|
widget.disposed.connect(updateContextKeys);
|
|
});
|
|
}
|
|
|
|
protected readonly toDisposeOnCurrentEditorChanged = new DisposableCollection();
|
|
|
|
canHandle(widget: Widget): widget is EditorWidget {
|
|
return widget instanceof EditorWidget;
|
|
}
|
|
|
|
activate(statusBar: StatusBar, widget: EditorWidget): void {
|
|
this.toDisposeOnCurrentEditorChanged.dispose();
|
|
const editor = widget.editor;
|
|
this.updateLanguageStatus(statusBar, editor);
|
|
this.updateEncodingStatus(statusBar, editor);
|
|
this.setCursorPositionStatus(statusBar, editor);
|
|
this.toDisposeOnCurrentEditorChanged.pushAll([
|
|
editor.onLanguageChanged(() => this.updateLanguageStatus(statusBar, editor)),
|
|
editor.onEncodingChanged(() => this.updateEncodingStatus(statusBar, editor)),
|
|
editor.onCursorPositionChanged(() => this.setCursorPositionStatus(statusBar, editor))
|
|
]);
|
|
}
|
|
|
|
deactivate(statusBar: StatusBar): void {
|
|
this.toDisposeOnCurrentEditorChanged.dispose();
|
|
this.updateLanguageStatus(statusBar, undefined);
|
|
this.updateEncodingStatus(statusBar, undefined);
|
|
this.setCursorPositionStatus(statusBar, undefined);
|
|
}
|
|
|
|
protected updateLanguageStatus(statusBar: StatusBar, editor: TextEditor | undefined): void {
|
|
this.languageStatusService.updateLanguageStatus(editor);
|
|
}
|
|
|
|
protected updateEncodingStatus(statusBar: StatusBar, editor: TextEditor | undefined): void {
|
|
if (!editor) {
|
|
statusBar.removeElement('editor-status-encoding');
|
|
return;
|
|
}
|
|
statusBar.setElement('editor-status-encoding', {
|
|
text: SUPPORTED_ENCODINGS[editor.getEncoding()].labelShort,
|
|
alignment: StatusBarAlignment.RIGHT,
|
|
priority: 10,
|
|
command: EditorCommands.CHANGE_ENCODING.id,
|
|
tooltip: nls.localizeByDefault('Select Encoding')
|
|
});
|
|
}
|
|
|
|
protected setCursorPositionStatus(statusBar: StatusBar, editor: TextEditor | undefined): void {
|
|
if (!editor) {
|
|
statusBar.removeElement('editor-status-cursor-position');
|
|
return;
|
|
}
|
|
const { cursor } = editor;
|
|
statusBar.setElement('editor-status-cursor-position', {
|
|
text: nls.localizeByDefault('Ln {0}, Col {1}', cursor.line + 1, editor.getVisibleColumn(cursor)),
|
|
alignment: StatusBarAlignment.RIGHT,
|
|
priority: 100,
|
|
tooltip: EditorCommands.GOTO_LINE_COLUMN.label,
|
|
command: EditorCommands.GOTO_LINE_COLUMN.id
|
|
});
|
|
}
|
|
|
|
registerCommands(commands: CommandRegistry): void {
|
|
commands.registerCommand(EditorCommands.SHOW_ALL_OPENED_EDITORS, {
|
|
execute: () => this.quickInputService?.open(QuickEditorService.PREFIX)
|
|
});
|
|
const splitHandlerFactory = (splitMode: DockLayout.InsertMode): CommandHandler => new CurrentWidgetCommandAdapter(this.shell, {
|
|
isEnabled: title => {
|
|
if (!title?.owner) {
|
|
return false;
|
|
}
|
|
return this.findSplitContribution(title.owner) !== undefined;
|
|
},
|
|
execute: async title => {
|
|
if (!title?.owner) {
|
|
return;
|
|
}
|
|
const contribution = this.findSplitContribution(title.owner);
|
|
if (contribution) {
|
|
await contribution.split(title.owner, splitMode);
|
|
}
|
|
}
|
|
});
|
|
commands.registerCommand(EditorCommands.SPLIT_EDITOR_HORIZONTAL, splitHandlerFactory('split-right'));
|
|
commands.registerCommand(EditorCommands.SPLIT_EDITOR_VERTICAL, splitHandlerFactory('split-bottom'));
|
|
commands.registerCommand(EditorCommands.SPLIT_EDITOR_RIGHT, splitHandlerFactory('split-right'));
|
|
commands.registerCommand(EditorCommands.SPLIT_EDITOR_DOWN, splitHandlerFactory('split-bottom'));
|
|
commands.registerCommand(EditorCommands.SPLIT_EDITOR_UP, splitHandlerFactory('split-top'));
|
|
commands.registerCommand(EditorCommands.SPLIT_EDITOR_LEFT, splitHandlerFactory('split-left'));
|
|
}
|
|
|
|
protected findSplitContribution(widget: Widget): SplitEditorContribution | undefined {
|
|
const prioritized = Prioritizeable.prioritizeAllSync(
|
|
this.splitEditorContributions.getContributions(),
|
|
contribution => contribution.canHandle(widget)
|
|
);
|
|
return prioritized.length > 0 ? prioritized[0].value : undefined;
|
|
}
|
|
|
|
registerKeybindings(keybindings: KeybindingRegistry): void {
|
|
keybindings.registerKeybinding({
|
|
command: EditorCommands.SHOW_ALL_OPENED_EDITORS.id,
|
|
keybinding: 'ctrlcmd+k ctrlcmd+p'
|
|
});
|
|
keybindings.registerKeybinding({
|
|
command: EditorCommands.SPLIT_EDITOR_HORIZONTAL.id,
|
|
keybinding: 'ctrlcmd+\\',
|
|
});
|
|
keybindings.registerKeybinding({
|
|
command: EditorCommands.SPLIT_EDITOR_VERTICAL.id,
|
|
keybinding: 'ctrlcmd+k ctrlcmd+\\',
|
|
});
|
|
}
|
|
|
|
registerMenus(registry: MenuModelRegistry): void {
|
|
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_SPLIT, {
|
|
commandId: EditorCommands.SPLIT_EDITOR_UP.id,
|
|
label: nls.localizeByDefault('Split Up'),
|
|
order: '1',
|
|
});
|
|
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_SPLIT, {
|
|
commandId: EditorCommands.SPLIT_EDITOR_DOWN.id,
|
|
label: nls.localizeByDefault('Split Down'),
|
|
order: '2',
|
|
});
|
|
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_SPLIT, {
|
|
commandId: EditorCommands.SPLIT_EDITOR_LEFT.id,
|
|
label: nls.localizeByDefault('Split Left'),
|
|
order: '3',
|
|
});
|
|
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_SPLIT, {
|
|
commandId: EditorCommands.SPLIT_EDITOR_RIGHT.id,
|
|
label: nls.localizeByDefault('Split Right'),
|
|
order: '4',
|
|
});
|
|
}
|
|
}
|