@codingame/monaco-vscode-extensions-service-override
Version:
VSCode public API plugged on the monaco editor - extensions service-override
537 lines (533 loc) • 23.3 kB
JavaScript
import { __decorate, __param } from '@codingame/monaco-vscode-api/external/tslib/tslib.es6';
import { Event } from '@codingame/monaco-vscode-api/vscode/vs/base/common/event';
import { DisposableStore, DisposableMap } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle';
import { isEqual } from '@codingame/monaco-vscode-api/vscode/vs/base/common/resources';
import { URI } from '@codingame/monaco-vscode-api/vscode/vs/base/common/uri';
import { IConfigurationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/configuration/common/configuration.service';
import { ILogService } from '@codingame/monaco-vscode-api/vscode/vs/platform/log/common/log.service';
import { ExtHostContext, TabInputKind, TabModelOperationKind, MainContext } from '@codingame/monaco-vscode-api/vscode/vs/workbench/api/common/extHost.protocol';
import { EditorResourceAccessor, SideBySideEditor, GroupModelChangeKind } from '@codingame/monaco-vscode-api/vscode/vs/workbench/common/editor';
import { DiffEditorInput } from '@codingame/monaco-vscode-api/vscode/vs/workbench/common/editor/diffEditorInput';
import { isGroupEditorMoveEvent } from '@codingame/monaco-vscode-api/vscode/vs/workbench/common/editor/editorGroupModel';
import { SideBySideEditorInput } from '@codingame/monaco-vscode-api/vscode/vs/workbench/common/editor/sideBySideEditorInput';
import { AbstractTextResourceEditorInput } from '@codingame/monaco-vscode-api/vscode/vs/workbench/common/editor/textResourceEditorInput';
import { ChatEditorInput } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/chat/browser/widgetHosts/editor/chatEditorInput';
import { CustomEditorInput } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/customEditor/browser/customEditorInput';
import { InteractiveEditorInput } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/interactive/browser/interactiveEditorInput';
import { MergeEditorInput } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput';
import { MultiDiffEditorInput } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput';
import { NotebookEditorInput } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/notebook/common/notebookEditorInput';
import { TerminalEditorInput } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/terminal/browser/terminalEditorInput';
import { WebviewInput } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/webviewPanel/browser/webviewEditorInput';
import { editorGroupToColumn, columnToEditorGroup } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/editor/common/editorGroupColumn';
import { GroupDirection, preferredSideBySideGroupDirection } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/editor/common/editorGroupsService';
import { IEditorGroupsService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/editor/common/editorGroupsService.service';
import { SIDE_GROUP } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/editor/common/editorService';
import { IEditorService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/editor/common/editorService.service';
import { extHostNamedCustomer } from '../../services/extensions/common/extHostCustomers.js';
let MainThreadEditorTabs = class MainThreadEditorTabs {
constructor(
extHostContext,
_editorGroupsService,
_configurationService,
_logService,
editorService
) {
this._editorGroupsService = _editorGroupsService;
this._configurationService = _configurationService;
this._logService = _logService;
this._dispoables = ( new DisposableStore());
this._tabGroupModel = [];
this._groupLookup = ( new Map());
this._tabInfoLookup = ( new Map());
this._multiDiffEditorInputListeners = ( new DisposableMap());
this._proxy = ( extHostContext.getProxy(ExtHostContext.ExtHostEditorTabs));
this._dispoables.add(editorService.onDidEditorsChange(event => {
try {
this._updateTabsModel(event);
} catch {
this._logService.error("Failed to update model, rebuilding");
this._createTabsModel();
}
}));
this._dispoables.add(this._multiDiffEditorInputListeners);
this._dispoables.add(this._editorGroupsService.onDidAddGroup(() => this._createTabsModel()));
this._dispoables.add(this._editorGroupsService.onDidRemoveGroup(() => this._createTabsModel()));
this._editorGroupsService.whenReady.then(() => this._createTabsModel());
}
dispose() {
this._groupLookup.clear();
this._tabInfoLookup.clear();
this._dispoables.dispose();
}
_buildTabObject(group, editor, editorIndex) {
const editorId = editor.editorId;
const tab = {
id: this._generateTabId(editor, group.id),
label: editor.getName(),
editorId,
input: this._editorInputToDto(editor),
isPinned: group.isSticky(editorIndex),
isPreview: !group.isPinned(editorIndex),
isActive: group.isActive(editor),
isDirty: editor.isDirty()
};
return tab;
}
_editorInputToDto(editor) {
if (editor instanceof MergeEditorInput) {
return {
kind: TabInputKind.TextMergeInput,
base: editor.base,
input1: editor.input1.uri,
input2: editor.input2.uri,
result: editor.resource
};
}
if (editor instanceof AbstractTextResourceEditorInput) {
return {
kind: TabInputKind.TextInput,
uri: editor.resource
};
}
if (editor instanceof SideBySideEditorInput && !(editor instanceof DiffEditorInput)) {
const primaryResource = editor.primary.resource;
const secondaryResource = editor.secondary.resource;
if (editor.primary instanceof AbstractTextResourceEditorInput && editor.secondary instanceof AbstractTextResourceEditorInput && isEqual(primaryResource, secondaryResource) && primaryResource && secondaryResource) {
return {
kind: TabInputKind.TextInput,
uri: primaryResource
};
}
return {
kind: TabInputKind.UnknownInput
};
}
if (editor instanceof NotebookEditorInput) {
return {
kind: TabInputKind.NotebookInput,
notebookType: editor.viewType,
uri: editor.resource
};
}
if (editor instanceof CustomEditorInput) {
return {
kind: TabInputKind.CustomEditorInput,
viewType: editor.viewType,
uri: editor.resource
};
}
if (editor instanceof WebviewInput) {
return {
kind: TabInputKind.WebviewEditorInput,
viewType: editor.viewType
};
}
if (editor instanceof TerminalEditorInput) {
return {
kind: TabInputKind.TerminalEditorInput
};
}
if (editor instanceof DiffEditorInput) {
if (editor.modified instanceof AbstractTextResourceEditorInput && editor.original instanceof AbstractTextResourceEditorInput) {
return {
kind: TabInputKind.TextDiffInput,
modified: editor.modified.resource,
original: editor.original.resource
};
}
if (editor.modified instanceof NotebookEditorInput && editor.original instanceof NotebookEditorInput) {
return {
kind: TabInputKind.NotebookDiffInput,
notebookType: editor.original.viewType,
modified: editor.modified.resource,
original: editor.original.resource
};
}
}
if (editor instanceof InteractiveEditorInput) {
return {
kind: TabInputKind.InteractiveEditorInput,
uri: editor.resource,
inputBoxUri: editor.inputResource
};
}
if (editor instanceof ChatEditorInput) {
return {
kind: TabInputKind.ChatEditorInput
};
}
if (editor instanceof MultiDiffEditorInput) {
const diffEditors = [];
for (const resource of (editor?.resources.get() ?? [])) {
if (resource.originalUri && resource.modifiedUri) {
diffEditors.push({
kind: TabInputKind.TextDiffInput,
original: resource.originalUri,
modified: resource.modifiedUri
});
}
}
return {
kind: TabInputKind.MultiDiffEditorInput,
diffEditors
};
}
return {
kind: TabInputKind.UnknownInput
};
}
_generateTabId(editor, groupId) {
let resourceString;
const resource = EditorResourceAccessor.getCanonicalUri(editor, {
supportSideBySide: SideBySideEditor.BOTH
});
if (resource instanceof URI) {
resourceString = ( resource.toString());
} else {
resourceString = `${resource?.primary?.toString()}-${resource?.secondary?.toString()}`;
}
return `${groupId}~${editor.editorId}-${editor.typeId}-${resourceString} `;
}
_onDidGroupActivate() {
const activeGroupId = this._editorGroupsService.activeGroup.id;
const activeGroup = this._groupLookup.get(activeGroupId);
if (activeGroup) {
activeGroup.isActive = true;
this._proxy.$acceptTabGroupUpdate(activeGroup);
}
}
_onDidTabLabelChange(groupId, editorInput, editorIndex) {
const tabId = this._generateTabId(editorInput, groupId);
const tabInfo = this._tabInfoLookup.get(tabId);
if (tabInfo) {
tabInfo.tab.label = editorInput.getName();
this._proxy.$acceptTabOperation({
groupId,
index: editorIndex,
tabDto: tabInfo.tab,
kind: TabModelOperationKind.TAB_UPDATE
});
} else {
this._logService.error("Invalid model for label change, rebuilding");
this._createTabsModel();
}
}
_onDidTabOpen(groupId, editorInput, editorIndex) {
const group = this._editorGroupsService.getGroup(groupId);
const groupInModel = this._groupLookup.get(groupId) !== undefined;
if (!group || !groupInModel) {
this._createTabsModel();
return;
}
const tabs = this._groupLookup.get(groupId)?.tabs;
if (!tabs) {
return;
}
const tabObject = this._buildTabObject(group, editorInput, editorIndex);
tabs.splice(editorIndex, 0, tabObject);
const tabId = this._generateTabId(editorInput, groupId);
this._tabInfoLookup.set(tabId, {
group,
editorInput,
tab: tabObject
});
if (editorInput instanceof MultiDiffEditorInput) {
this._multiDiffEditorInputListeners.set(editorInput, Event.fromObservableLight(editorInput.resources)(() => {
const tabInfo = this._tabInfoLookup.get(tabId);
if (!tabInfo) {
return;
}
tabInfo.tab = this._buildTabObject(group, editorInput, editorIndex);
this._proxy.$acceptTabOperation({
groupId,
index: editorIndex,
tabDto: tabInfo.tab,
kind: TabModelOperationKind.TAB_UPDATE
});
}));
}
this._proxy.$acceptTabOperation({
groupId,
index: editorIndex,
tabDto: tabObject,
kind: TabModelOperationKind.TAB_OPEN
});
}
_onDidTabClose(groupId, editorIndex) {
const group = this._editorGroupsService.getGroup(groupId);
const tabs = this._groupLookup.get(groupId)?.tabs;
if (!group || !tabs) {
this._createTabsModel();
return;
}
const removedTab = tabs.splice(editorIndex, 1);
if (removedTab.length === 0) {
return;
}
this._tabInfoLookup.delete(removedTab[0]?.id ?? "");
if (removedTab[0]?.input instanceof MultiDiffEditorInput) {
this._multiDiffEditorInputListeners.deleteAndDispose(removedTab[0]?.input);
}
this._proxy.$acceptTabOperation({
groupId,
index: editorIndex,
tabDto: removedTab[0],
kind: TabModelOperationKind.TAB_CLOSE
});
}
_onDidTabActiveChange(groupId, editorIndex) {
const tabs = this._groupLookup.get(groupId)?.tabs;
if (!tabs) {
return;
}
const activeTab = tabs[editorIndex];
activeTab.isActive = true;
this._proxy.$acceptTabOperation({
groupId,
index: editorIndex,
tabDto: activeTab,
kind: TabModelOperationKind.TAB_UPDATE
});
}
_onDidTabDirty(groupId, editorIndex, editor) {
const tabId = this._generateTabId(editor, groupId);
const tabInfo = this._tabInfoLookup.get(tabId);
if (!tabInfo) {
this._logService.error("Invalid model for dirty change, rebuilding");
this._createTabsModel();
return;
}
tabInfo.tab.isDirty = editor.isDirty();
this._proxy.$acceptTabOperation({
groupId,
index: editorIndex,
tabDto: tabInfo.tab,
kind: TabModelOperationKind.TAB_UPDATE
});
}
_onDidTabPinChange(groupId, editorIndex, editor) {
const tabId = this._generateTabId(editor, groupId);
const tabInfo = this._tabInfoLookup.get(tabId);
const group = tabInfo?.group;
const tab = tabInfo?.tab;
if (!group || !tab) {
this._logService.error("Invalid model for sticky change, rebuilding");
this._createTabsModel();
return;
}
tab.isPinned = group.isSticky(editorIndex);
this._proxy.$acceptTabOperation({
groupId,
index: editorIndex,
tabDto: tab,
kind: TabModelOperationKind.TAB_UPDATE
});
}
_onDidTabPreviewChange(groupId, editorIndex, editor) {
const tabId = this._generateTabId(editor, groupId);
const tabInfo = this._tabInfoLookup.get(tabId);
const group = tabInfo?.group;
const tab = tabInfo?.tab;
if (!group || !tab) {
this._logService.error("Invalid model for sticky change, rebuilding");
this._createTabsModel();
return;
}
tab.isPreview = !group.isPinned(editorIndex);
this._proxy.$acceptTabOperation({
kind: TabModelOperationKind.TAB_UPDATE,
groupId,
tabDto: tab,
index: editorIndex
});
}
_onDidTabMove(groupId, editorIndex, oldEditorIndex, editor) {
const tabs = this._groupLookup.get(groupId)?.tabs;
if (!tabs) {
this._logService.error("Invalid model for move change, rebuilding");
this._createTabsModel();
return;
}
const removedTab = tabs.splice(oldEditorIndex, 1);
if (removedTab.length === 0) {
return;
}
tabs.splice(editorIndex, 0, removedTab[0]);
this._proxy.$acceptTabOperation({
kind: TabModelOperationKind.TAB_MOVE,
groupId,
tabDto: removedTab[0],
index: editorIndex,
oldIndex: oldEditorIndex
});
}
_createTabsModel() {
if (this._editorGroupsService.groups.length === 0) {
return;
}
this._tabGroupModel = [];
this._groupLookup.clear();
this._tabInfoLookup.clear();
let tabs = [];
for (const group of this._editorGroupsService.groups) {
const currentTabGroupModel = {
groupId: group.id,
isActive: group.id === this._editorGroupsService.activeGroup.id,
viewColumn: editorGroupToColumn(this._editorGroupsService, group),
tabs: []
};
group.editors.forEach((editor, editorIndex) => {
const tab = this._buildTabObject(group, editor, editorIndex);
tabs.push(tab);
this._tabInfoLookup.set(this._generateTabId(editor, group.id), {
group,
tab,
editorInput: editor
});
});
currentTabGroupModel.tabs = tabs;
this._tabGroupModel.push(currentTabGroupModel);
this._groupLookup.set(group.id, currentTabGroupModel);
tabs = [];
}
this._proxy.$acceptEditorTabModel(this._tabGroupModel);
}
_updateTabsModel(changeEvent) {
const event = changeEvent.event;
const groupId = changeEvent.groupId;
switch (event.kind) {
case GroupModelChangeKind.GROUP_ACTIVE:
if (groupId === this._editorGroupsService.activeGroup.id) {
this._onDidGroupActivate();
break;
} else {
return;
}
case GroupModelChangeKind.EDITOR_LABEL:
if (event.editor !== undefined && event.editorIndex !== undefined) {
this._onDidTabLabelChange(groupId, event.editor, event.editorIndex);
break;
}
case GroupModelChangeKind.EDITOR_OPEN:
if (event.editor !== undefined && event.editorIndex !== undefined) {
this._onDidTabOpen(groupId, event.editor, event.editorIndex);
break;
}
case GroupModelChangeKind.EDITOR_CLOSE:
if (event.editorIndex !== undefined) {
this._onDidTabClose(groupId, event.editorIndex);
break;
}
case GroupModelChangeKind.EDITOR_ACTIVE:
if (event.editorIndex !== undefined) {
this._onDidTabActiveChange(groupId, event.editorIndex);
break;
}
case GroupModelChangeKind.EDITOR_DIRTY:
if (event.editorIndex !== undefined && event.editor !== undefined) {
this._onDidTabDirty(groupId, event.editorIndex, event.editor);
break;
}
case GroupModelChangeKind.EDITOR_STICKY:
if (event.editorIndex !== undefined && event.editor !== undefined) {
this._onDidTabPinChange(groupId, event.editorIndex, event.editor);
break;
}
case GroupModelChangeKind.EDITOR_PIN:
if (event.editorIndex !== undefined && event.editor !== undefined) {
this._onDidTabPreviewChange(groupId, event.editorIndex, event.editor);
break;
}
case GroupModelChangeKind.EDITOR_TRANSIENT:
break;
case GroupModelChangeKind.EDITOR_MOVE:
if (isGroupEditorMoveEvent(event) && event.editor && event.editorIndex !== undefined && event.oldEditorIndex !== undefined) {
this._onDidTabMove(groupId, event.editorIndex, event.oldEditorIndex, event.editor);
break;
}
default:
this._createTabsModel();
}
}
$moveTab(tabId, index, viewColumn, preserveFocus) {
const groupId = columnToEditorGroup(this._editorGroupsService, this._configurationService, viewColumn);
const tabInfo = this._tabInfoLookup.get(tabId);
const tab = tabInfo?.tab;
if (!tab) {
throw ( new Error(`Attempted to close tab with id ${tabId} which does not exist`));
}
let targetGroup;
const sourceGroup = this._editorGroupsService.getGroup(tabInfo.group.id);
if (!sourceGroup) {
return;
}
if (this._groupLookup.get(groupId) === undefined) {
let direction = GroupDirection.RIGHT;
if (viewColumn === SIDE_GROUP) {
direction = preferredSideBySideGroupDirection(this._configurationService);
}
targetGroup = this._editorGroupsService.addGroup(
this._editorGroupsService.groups[this._editorGroupsService.groups.length - 1],
direction
);
} else {
targetGroup = this._editorGroupsService.getGroup(groupId);
}
if (!targetGroup) {
return;
}
if (index < 0 || index > targetGroup.editors.length) {
index = targetGroup.editors.length;
}
const editorInput = tabInfo?.editorInput;
if (!editorInput) {
return;
}
sourceGroup.moveEditor(editorInput, targetGroup, {
index,
preserveFocus
});
return;
}
async $closeTab(tabIds, preserveFocus) {
const groups = ( new Map());
for (const tabId of tabIds) {
const tabInfo = this._tabInfoLookup.get(tabId);
const tab = tabInfo?.tab;
const group = tabInfo?.group;
const editorTab = tabInfo?.editorInput;
if (!group || !tab || !tabInfo || !editorTab) {
continue;
}
const groupEditors = groups.get(group);
if (!groupEditors) {
groups.set(group, [editorTab]);
} else {
groupEditors.push(editorTab);
}
}
const results = [];
for (const [group, editors] of groups) {
results.push(await group.closeEditors(editors, {
preserveFocus
}));
}
return results.every(result => result);
}
async $closeGroup(groupIds, preserveFocus) {
const groupCloseResults = [];
for (const groupId of groupIds) {
const group = this._editorGroupsService.getGroup(groupId);
if (group) {
groupCloseResults.push(await group.closeAllEditors());
if (group.count === 0 && this._editorGroupsService.getGroup(group.id)) {
this._editorGroupsService.removeGroup(group);
}
}
}
return groupCloseResults.every(result => result);
}
};
MainThreadEditorTabs = __decorate([extHostNamedCustomer(MainContext.MainThreadEditorTabs), ( __param(1, IEditorGroupsService)), ( __param(2, IConfigurationService)), ( __param(3, ILogService)), ( __param(4, IEditorService))], MainThreadEditorTabs);
export { MainThreadEditorTabs };