Files
theia-code-os/packages/editor/src/browser/editor-contribution.ts
mawkone 8bb5110148
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
deploy: current vibn theia state
Made-with: Cursor
2026-02-27 12:01:08 -08:00

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',
});
}
}