UNPKG

monaco-editor

Version:
299 lines (298 loc) • 15.7 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 { getWindow, h, scheduleAtNextAnimationFrame } from '../../../../base/browser/dom.js'; import { SmoothScrollableElement } from '../../../../base/browser/ui/scrollbar/scrollableElement.js'; import { compareBy, numberComparator } from '../../../../base/common/arrays.js'; import { findFirstMax } from '../../../../base/common/arraysFind.js'; import { BugIndicatingError } from '../../../../base/common/errors.js'; import { Disposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { autorun, autorunWithStore, derived, derivedWithStore, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; import { disposableObservableValue, globalTransaction, transaction } from '../../../../base/common/observableInternal/base.js'; import { Scrollable } from '../../../../base/common/scrollable.js'; import './style.css'; import { ObservableElementSizeObserver } from '../diffEditor/utils.js'; import { OffsetRange } from '../../../common/core/offsetRange.js'; import { Selection } from '../../../common/core/selection.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; import { DiffEditorItemTemplate, TemplateData } from './diffEditorItemTemplate.js'; import { ObjectPool } from './objectPool.js'; import { localize } from '../../../../nls.js'; let MultiDiffEditorWidgetImpl = class MultiDiffEditorWidgetImpl extends Disposable { constructor(_element, _dimension, _viewModel, _workbenchUIElementFactory, _parentContextKeyService, _parentInstantiationService) { super(); this._element = _element; this._dimension = _dimension; this._viewModel = _viewModel; this._workbenchUIElementFactory = _workbenchUIElementFactory; this._parentContextKeyService = _parentContextKeyService; this._parentInstantiationService = _parentInstantiationService; this._scrollableElements = h('div.scrollContent', [ h('div@content', { style: { overflow: 'hidden', } }), h('div.monaco-editor@overflowWidgetsDomNode', {}), ]); this._scrollable = this._register(new Scrollable({ forceIntegerValues: false, scheduleAtNextAnimationFrame: (cb) => scheduleAtNextAnimationFrame(getWindow(this._element), cb), smoothScrollDuration: 100, })); this._scrollableElement = this._register(new SmoothScrollableElement(this._scrollableElements.root, { vertical: 1 /* ScrollbarVisibility.Auto */, horizontal: 1 /* ScrollbarVisibility.Auto */, useShadows: false, }, this._scrollable)); this._elements = h('div.monaco-component.multiDiffEditor', {}, [ h('div', {}, [this._scrollableElement.getDomNode()]), h('div.placeholder@placeholder', {}, [h('div', [localize('noChangedFiles', 'No Changed Files')])]), ]); this._sizeObserver = this._register(new ObservableElementSizeObserver(this._element, undefined)); this._objectPool = this._register(new ObjectPool((data) => { const template = this._instantiationService.createInstance(DiffEditorItemTemplate, this._scrollableElements.content, this._scrollableElements.overflowWidgetsDomNode, this._workbenchUIElementFactory); template.setData(data); return template; })); this.scrollTop = observableFromEvent(this, this._scrollableElement.onScroll, () => /** @description scrollTop */ this._scrollableElement.getScrollPosition().scrollTop); this.scrollLeft = observableFromEvent(this, this._scrollableElement.onScroll, () => /** @description scrollLeft */ this._scrollableElement.getScrollPosition().scrollLeft); this._viewItemsInfo = derivedWithStore(this, (reader, store) => { const vm = this._viewModel.read(reader); if (!vm) { return { items: [], getItem: _d => { throw new BugIndicatingError(); } }; } const viewModels = vm.items.read(reader); const map = new Map(); const items = viewModels.map(d => { const item = store.add(new VirtualizedViewItem(d, this._objectPool, this.scrollLeft, delta => { this._scrollableElement.setScrollPosition({ scrollTop: this._scrollableElement.getScrollPosition().scrollTop + delta }); })); const data = this._lastDocStates?.[item.getKey()]; if (data) { transaction(tx => { item.setViewState(data, tx); }); } map.set(d, item); return item; }); return { items, getItem: d => map.get(d) }; }); this._viewItems = this._viewItemsInfo.map(this, items => items.items); this._spaceBetweenPx = 0; this._totalHeight = this._viewItems.map(this, (items, reader) => items.reduce((r, i) => r + i.contentHeight.read(reader) + this._spaceBetweenPx, 0)); this._contextKeyService = this._register(this._parentContextKeyService.createScoped(this._element)); this._instantiationService = this._register(this._parentInstantiationService.createChild(new ServiceCollection([IContextKeyService, this._contextKeyService]))); /** This accounts for documents that are not loaded yet. */ this._lastDocStates = {}; this._contextKeyService.createKey(EditorContextKeys.inMultiDiffEditor.key, true); this._register(autorunWithStore((reader, store) => { const viewModel = this._viewModel.read(reader); if (viewModel && viewModel.contextKeys) { for (const [key, value] of Object.entries(viewModel.contextKeys)) { const contextKey = this._contextKeyService.createKey(key, undefined); contextKey.set(value); store.add(toDisposable(() => contextKey.reset())); } } })); const ctxAllCollapsed = this._parentContextKeyService.createKey(EditorContextKeys.multiDiffEditorAllCollapsed.key, false); this._register(autorun((reader) => { const viewModel = this._viewModel.read(reader); if (viewModel) { const allCollapsed = viewModel.items.read(reader).every(item => item.collapsed.read(reader)); ctxAllCollapsed.set(allCollapsed); } })); this._register(autorun((reader) => { /** @description Update widget dimension */ const dimension = this._dimension.read(reader); this._sizeObserver.observe(dimension); })); this._register(autorun((reader) => { /** @description Update widget dimension */ const items = this._viewItems.read(reader); this._elements.placeholder.classList.toggle('visible', items.length === 0); })); this._scrollableElements.content.style.position = 'relative'; this._register(autorun((reader) => { /** @description Update scroll dimensions */ const height = this._sizeObserver.height.read(reader); this._scrollableElements.root.style.height = `${height}px`; const totalHeight = this._totalHeight.read(reader); this._scrollableElements.content.style.height = `${totalHeight}px`; const width = this._sizeObserver.width.read(reader); let scrollWidth = width; const viewItems = this._viewItems.read(reader); const max = findFirstMax(viewItems, compareBy(i => i.maxScroll.read(reader).maxScroll, numberComparator)); if (max) { const maxScroll = max.maxScroll.read(reader); scrollWidth = width + maxScroll.maxScroll; } this._scrollableElement.setScrollDimensions({ width: width, height: height, scrollHeight: totalHeight, scrollWidth, }); })); _element.replaceChildren(this._elements.root); this._register(toDisposable(() => { _element.replaceChildren(); })); this._register(this._register(autorun(reader => { /** @description Render all */ globalTransaction(tx => { this.render(reader); }); }))); } render(reader) { const scrollTop = this.scrollTop.read(reader); let contentScrollOffsetToScrollOffset = 0; let itemHeightSumBefore = 0; let itemContentHeightSumBefore = 0; const viewPortHeight = this._sizeObserver.height.read(reader); const contentViewPort = OffsetRange.ofStartAndLength(scrollTop, viewPortHeight); const width = this._sizeObserver.width.read(reader); for (const v of this._viewItems.read(reader)) { const itemContentHeight = v.contentHeight.read(reader); const itemHeight = Math.min(itemContentHeight, viewPortHeight); const itemRange = OffsetRange.ofStartAndLength(itemHeightSumBefore, itemHeight); const itemContentRange = OffsetRange.ofStartAndLength(itemContentHeightSumBefore, itemContentHeight); if (itemContentRange.isBefore(contentViewPort)) { contentScrollOffsetToScrollOffset -= itemContentHeight - itemHeight; v.hide(); } else if (itemContentRange.isAfter(contentViewPort)) { v.hide(); } else { const scroll = Math.max(0, Math.min(contentViewPort.start - itemContentRange.start, itemContentHeight - itemHeight)); contentScrollOffsetToScrollOffset -= scroll; const viewPort = OffsetRange.ofStartAndLength(scrollTop + contentScrollOffsetToScrollOffset, viewPortHeight); v.render(itemRange, scroll, width, viewPort); } itemHeightSumBefore += itemHeight + this._spaceBetweenPx; itemContentHeightSumBefore += itemContentHeight + this._spaceBetweenPx; } this._scrollableElements.content.style.transform = `translateY(${-(scrollTop + contentScrollOffsetToScrollOffset)}px)`; } }; MultiDiffEditorWidgetImpl = __decorate([ __param(4, IContextKeyService), __param(5, IInstantiationService) ], MultiDiffEditorWidgetImpl); export { MultiDiffEditorWidgetImpl }; class VirtualizedViewItem extends Disposable { constructor(viewModel, _objectPool, _scrollLeft, _deltaScrollVertical) { super(); this.viewModel = viewModel; this._objectPool = _objectPool; this._scrollLeft = _scrollLeft; this._deltaScrollVertical = _deltaScrollVertical; this._templateRef = this._register(disposableObservableValue(this, undefined)); this.contentHeight = derived(this, reader => this._templateRef.read(reader)?.object.contentHeight?.read(reader) ?? this.viewModel.lastTemplateData.read(reader).contentHeight); this.maxScroll = derived(this, reader => this._templateRef.read(reader)?.object.maxScroll.read(reader) ?? { maxScroll: 0, scrollWidth: 0 }); this.template = derived(this, reader => this._templateRef.read(reader)?.object); this._isHidden = observableValue(this, false); this._isFocused = derived(this, reader => this.template.read(reader)?.isFocused.read(reader) ?? false); this.viewModel.setIsFocused(this._isFocused, undefined); this._register(autorun((reader) => { const scrollLeft = this._scrollLeft.read(reader); this._templateRef.read(reader)?.object.setScrollLeft(scrollLeft); })); this._register(autorun(reader => { const ref = this._templateRef.read(reader); if (!ref) { return; } const isHidden = this._isHidden.read(reader); if (!isHidden) { return; } const isFocused = ref.object.isFocused.read(reader); if (isFocused) { return; } this._clear(); })); } dispose() { this._clear(); super.dispose(); } toString() { return `VirtualViewItem(${this.viewModel.documentDiffItem.modified?.uri.toString()})`; } getKey() { return this.viewModel.getKey(); } setViewState(viewState, tx) { this.viewModel.collapsed.set(viewState.collapsed, tx); this._updateTemplateData(tx); const data = this.viewModel.lastTemplateData.get(); const selections = viewState.selections?.map(Selection.liftSelection); this.viewModel.lastTemplateData.set({ ...data, selections, }, tx); const ref = this._templateRef.get(); if (ref) { if (selections) { ref.object.editor.setSelections(selections); } } } _updateTemplateData(tx) { const ref = this._templateRef.get(); if (!ref) { return; } this.viewModel.lastTemplateData.set({ contentHeight: ref.object.contentHeight.get(), selections: ref.object.editor.getSelections() ?? undefined, }, tx); } _clear() { const ref = this._templateRef.get(); if (!ref) { return; } transaction(tx => { this._updateTemplateData(tx); ref.object.hide(); this._templateRef.set(undefined, tx); }); } hide() { this._isHidden.set(true, undefined); } render(verticalSpace, offset, width, viewPort) { this._isHidden.set(false, undefined); let ref = this._templateRef.get(); if (!ref) { ref = this._objectPool.getUnusedObj(new TemplateData(this.viewModel, this._deltaScrollVertical)); this._templateRef.set(ref, undefined); const selections = this.viewModel.lastTemplateData.get().selections; if (selections) { ref.object.editor.setSelections(selections); } } ref.object.render(verticalSpace, width, offset, viewPort); } }