UNPKG

monaco-editor

Version:
233 lines (232 loc) • 13.5 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; import { EventType, addDisposableListener, h } from '../../../../../base/browser/dom.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import { autorun, autorunWithStore, derived, observableFromEvent, observableValue } from '../../../../../base/common/observable.js'; import { derivedDisposable, derivedWithSetter } from '../../../../../base/common/observableInternal/derived.js'; import { DiffEditorSash } from '../components/diffEditorSash.js'; import { appendRemoveOnDispose, applyStyle, prependRemoveOnDispose } from '../utils.js'; import { EditorGutter } from '../utils/editorGutter.js'; import { ActionRunnerWithContext } from '../../multiDiffEditor/utils.js'; import { LineRange, LineRangeSet } from '../../../../common/core/lineRange.js'; import { OffsetRange } from '../../../../common/core/offsetRange.js'; import { Range } from '../../../../common/core/range.js'; import { TextEdit } from '../../../../common/core/textEdit.js'; import { DetailedLineRangeMapping } from '../../../../common/diff/rangeMapping.js'; import { TextModelText } from '../../../../common/model/textModelText.js'; import { MenuWorkbenchToolBar } from '../../../../../platform/actions/browser/toolbar.js'; import { IMenuService, MenuId } from '../../../../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { WorkbenchHoverDelegate } from '../../../../../platform/hover/browser/hover.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; const emptyArr = []; const width = 35; let DiffEditorGutter = class DiffEditorGutter extends Disposable { constructor(diffEditorRoot, _diffModel, _editors, _options, _sashLayout, _boundarySashes, _instantiationService, _contextKeyService, _menuService) { super(); this._diffModel = _diffModel; this._editors = _editors; this._options = _options; this._sashLayout = _sashLayout; this._boundarySashes = _boundarySashes; this._instantiationService = _instantiationService; this._contextKeyService = _contextKeyService; this._menuService = _menuService; this._menu = this._register(this._menuService.createMenu(MenuId.DiffEditorHunkToolbar, this._contextKeyService)); this._actions = observableFromEvent(this, this._menu.onDidChange, () => this._menu.getActions()); this._hasActions = this._actions.map(a => a.length > 0); this._showSash = derived(this, reader => this._options.renderSideBySide.read(reader) && this._hasActions.read(reader)); this.width = derived(this, reader => this._hasActions.read(reader) ? width : 0); this.elements = h('div.gutter@gutter', { style: { position: 'absolute', height: '100%', width: width + 'px' } }, []); this._currentDiff = derived(this, (reader) => { const model = this._diffModel.read(reader); if (!model) { return undefined; } const mappings = model.diff.read(reader)?.mappings; const cursorPosition = this._editors.modifiedCursor.read(reader); if (!cursorPosition) { return undefined; } return mappings?.find(m => m.lineRangeMapping.modified.contains(cursorPosition.lineNumber)); }); this._selectedDiffs = derived(this, (reader) => { /** @description selectedDiffs */ const model = this._diffModel.read(reader); const diff = model?.diff.read(reader); // Return `emptyArr` because it is a constant. [] is always a new array and would trigger a change. if (!diff) { return emptyArr; } const selections = this._editors.modifiedSelections.read(reader); if (selections.every(s => s.isEmpty())) { return emptyArr; } const selectedLineNumbers = new LineRangeSet(selections.map(s => LineRange.fromRangeInclusive(s))); const selectedMappings = diff.mappings.filter(m => m.lineRangeMapping.innerChanges && selectedLineNumbers.intersects(m.lineRangeMapping.modified)); const result = selectedMappings.map(mapping => ({ mapping, rangeMappings: mapping.lineRangeMapping.innerChanges.filter(c => selections.some(s => Range.areIntersecting(c.modifiedRange, s))) })); if (result.length === 0 || result.every(r => r.rangeMappings.length === 0)) { return emptyArr; } return result; }); this._register(prependRemoveOnDispose(diffEditorRoot, this.elements.root)); this._register(addDisposableListener(this.elements.root, 'click', () => { this._editors.modified.focus(); })); this._register(applyStyle(this.elements.root, { display: this._hasActions.map(a => a ? 'block' : 'none') })); derivedDisposable(this, reader => { const showSash = this._showSash.read(reader); return !showSash ? undefined : new DiffEditorSash(diffEditorRoot, this._sashLayout.dimensions, this._options.enableSplitViewResizing, this._boundarySashes, derivedWithSetter(this, reader => this._sashLayout.sashLeft.read(reader) - width, (v, tx) => this._sashLayout.sashLeft.set(v + width, tx)), () => this._sashLayout.resetSash()); }).recomputeInitiallyAndOnChange(this._store); this._register(new EditorGutter(this._editors.modified, this.elements.root, { getIntersectingGutterItems: (range, reader) => { const model = this._diffModel.read(reader); if (!model) { return []; } const diffs = model.diff.read(reader); if (!diffs) { return []; } const selection = this._selectedDiffs.read(reader); if (selection.length > 0) { const m = DetailedLineRangeMapping.fromRangeMappings(selection.flatMap(s => s.rangeMappings)); return [ new DiffGutterItem(m, true, MenuId.DiffEditorSelectionToolbar, undefined, model.model.original.uri, model.model.modified.uri) ]; } const currentDiff = this._currentDiff.read(reader); return diffs.mappings.map(m => new DiffGutterItem(m.lineRangeMapping.withInnerChangesFromLineRanges(), m.lineRangeMapping === currentDiff?.lineRangeMapping, MenuId.DiffEditorHunkToolbar, undefined, model.model.original.uri, model.model.modified.uri)); }, createView: (item, target) => { return this._instantiationService.createInstance(DiffToolBar, item, target, this); }, })); this._register(addDisposableListener(this.elements.gutter, EventType.MOUSE_WHEEL, (e) => { if (this._editors.modified.getOption(104 /* EditorOption.scrollbar */).handleMouseWheel) { this._editors.modified.delegateScrollFromMouseWheelEvent(e); } }, { passive: false })); } computeStagedValue(mapping) { const c = mapping.innerChanges ?? []; const modified = new TextModelText(this._editors.modifiedModel.get()); const original = new TextModelText(this._editors.original.getModel()); const edit = new TextEdit(c.map(c => c.toTextEdit(modified))); const value = edit.apply(original); return value; } layout(left) { this.elements.gutter.style.left = left + 'px'; } }; DiffEditorGutter = __decorate([ __param(6, IInstantiationService), __param(7, IContextKeyService), __param(8, IMenuService) ], DiffEditorGutter); export { DiffEditorGutter }; class DiffGutterItem { constructor(mapping, showAlways, menuId, rangeOverride, originalUri, modifiedUri) { this.mapping = mapping; this.showAlways = showAlways; this.menuId = menuId; this.rangeOverride = rangeOverride; this.originalUri = originalUri; this.modifiedUri = modifiedUri; } get id() { return this.mapping.modified.toString(); } get range() { return this.rangeOverride ?? this.mapping.modified; } } let DiffToolBar = class DiffToolBar extends Disposable { constructor(_item, target, gutter, instantiationService) { super(); this._item = _item; this._elements = h('div.gutterItem', { style: { height: '20px', width: '34px' } }, [ h('div.background@background', {}, []), h('div.buttons@buttons', {}, []), ]); this._showAlways = this._item.map(this, item => item.showAlways); this._menuId = this._item.map(this, item => item.menuId); this._isSmall = observableValue(this, false); this._lastItemRange = undefined; this._lastViewRange = undefined; const hoverDelegate = this._register(instantiationService.createInstance(WorkbenchHoverDelegate, 'element', true, { position: { hoverPosition: 1 /* HoverPosition.RIGHT */ } })); this._register(appendRemoveOnDispose(target, this._elements.root)); this._register(autorun(reader => { /** @description update showAlways */ const showAlways = this._showAlways.read(reader); this._elements.root.classList.toggle('noTransition', true); this._elements.root.classList.toggle('showAlways', showAlways); setTimeout(() => { this._elements.root.classList.toggle('noTransition', false); }, 0); })); this._register(autorunWithStore((reader, store) => { this._elements.buttons.replaceChildren(); const i = store.add(instantiationService.createInstance(MenuWorkbenchToolBar, this._elements.buttons, this._menuId.read(reader), { orientation: 1 /* ActionsOrientation.VERTICAL */, hoverDelegate, toolbarOptions: { primaryGroup: g => g.startsWith('primary'), }, overflowBehavior: { maxItems: this._isSmall.read(reader) ? 1 : 3 }, hiddenItemStrategy: 0 /* HiddenItemStrategy.Ignore */, actionRunner: new ActionRunnerWithContext(() => { const item = this._item.get(); const mapping = item.mapping; return { mapping, originalWithModifiedChanges: gutter.computeStagedValue(mapping), originalUri: item.originalUri, modifiedUri: item.modifiedUri, }; }), menuOptions: { shouldForwardArgs: true, }, })); store.add(i.onDidChangeMenuItems(() => { if (this._lastItemRange) { this.layout(this._lastItemRange, this._lastViewRange); } })); })); } layout(itemRange, viewRange) { this._lastItemRange = itemRange; this._lastViewRange = viewRange; let itemHeight = this._elements.buttons.clientHeight; this._isSmall.set(this._item.get().mapping.original.startLineNumber === 1 && itemRange.length < 30, undefined); // Item might have changed itemHeight = this._elements.buttons.clientHeight; const middleHeight = itemRange.length / 2 - itemHeight / 2; const margin = itemHeight; let effectiveCheckboxTop = itemRange.start + middleHeight; const preferredViewPortRange = OffsetRange.tryCreate(margin, viewRange.endExclusive - margin - itemHeight); const preferredParentRange = OffsetRange.tryCreate(itemRange.start + margin, itemRange.endExclusive - itemHeight - margin); if (preferredParentRange && preferredViewPortRange && preferredParentRange.start < preferredParentRange.endExclusive) { effectiveCheckboxTop = preferredViewPortRange.clip(effectiveCheckboxTop); effectiveCheckboxTop = preferredParentRange.clip(effectiveCheckboxTop); } this._elements.buttons.style.top = `${effectiveCheckboxTop - itemRange.start}px`; } }; DiffToolBar = __decorate([ __param(3, IInstantiationService) ], DiffToolBar);