This commit is contained in:
Henning Dieterichs 2024-04-02 10:50:36 +02:00 committed by Henning Dieterichs
parent 78cfeea7ba
commit a163748c59
5 changed files with 81 additions and 58 deletions

View file

@ -0,0 +1,69 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { BugIndicatingError } from 'vs/base/common/errors';
/*
* This file contains helper classes to manage control flow.
*/
/**
* Prevents code from being re-entrant.
*/
export class ReentrancyBarrier {
private _isOccupied = false;
/**
* Calls `runner` if the barrier is not occupied.
* During the call, the barrier becomes occupied.
*/
public runExclusivelyOrSkip(runner: () => void): void {
if (this._isOccupied) {
return;
}
this._isOccupied = true;
try {
runner();
} finally {
this._isOccupied = false;
}
}
/**
* Calls `runner`. If the barrier is occupied, throws an error.
* During the call, the barrier becomes active.
*/
public runExclusivelyOrThrow(runner: () => void): void {
if (this._isOccupied) {
throw new BugIndicatingError(`ReentrancyBarrier: reentrant call detected!`);
}
this._isOccupied = true;
try {
runner();
} finally {
this._isOccupied = false;
}
}
/**
* Indicates if some runner occupies this barrier.
*/
public get isOccupied() {
return this._isOccupied;
}
public makeExclusiveOrSkip<TFunction extends Function>(fn: TFunction): TFunction {
return ((...args: any[]) => {
if (this._isOccupied) {
return;
}
this._isOccupied = true;
try {
return fn(...args);
} finally {
this._isOccupied = false;
}
}) as any;
}
}

View file

@ -10,7 +10,7 @@ import { ITextModel } from 'vs/editor/common/model';
import { DetailedLineRangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping';
import { LineRangeEdit } from 'vs/workbench/contrib/mergeEditor/browser/model/editing';
import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange';
import { ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/utils';
import { ReentrancyBarrier } from '../../../../../base/common/controlFlow';
import { IMergeDiffComputer } from './diffComputer';
import { autorun, IObservable, IReader, ITransaction, observableSignal, observableValue, transaction } from 'vs/base/common/observable';
import { UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo';
@ -24,7 +24,7 @@ export class TextModelDiffs extends Disposable {
private _isDisposed = false;
public get isApplyingChange() {
return this._barrier.isActive;
return this._barrier.isOccupied;
}
constructor(
@ -44,14 +44,14 @@ export class TextModelDiffs extends Disposable {
this._register(
baseTextModel.onDidChangeContent(
this._barrier.makeExclusive(() => {
this._barrier.makeExclusiveOrSkip(() => {
recomputeSignal.trigger(undefined);
})
)
);
this._register(
textModel.onDidChangeContent(
this._barrier.makeExclusive(() => {
this._barrier.makeExclusiveOrSkip(() => {
recomputeSignal.trigger(undefined);
})
)

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { ArrayQueue, CompareResult } from 'vs/base/common/arrays';
import { BugIndicatingError, onUnexpectedError } from 'vs/base/common/errors';
import { onUnexpectedError } from 'vs/base/common/errors';
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IObservable, autorunOpts, observableFromEvent } from 'vs/base/common/observable';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget';
@ -12,52 +12,6 @@ import { IModelDeltaDecoration } from 'vs/editor/common/model';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
export class ReentrancyBarrier {
private _isActive = false;
public get isActive() {
return this._isActive;
}
public makeExclusive<TFunction extends Function>(fn: TFunction): TFunction {
return ((...args: any[]) => {
if (this._isActive) {
return;
}
this._isActive = true;
try {
return fn(...args);
} finally {
this._isActive = false;
}
}) as any;
}
public runExclusively(fn: () => void): void {
if (this._isActive) {
return;
}
this._isActive = true;
try {
fn();
} finally {
this._isActive = false;
}
}
public runExclusivelyOrThrow(fn: () => void): void {
if (this._isActive) {
throw new BugIndicatingError();
}
this._isActive = true;
try {
fn();
} finally {
this._isActive = false;
}
}
}
export function setStyle(
element: HTMLElement,
style: {

View file

@ -8,7 +8,7 @@ import { autorunWithStore, IObservable } from 'vs/base/common/observable';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget';
import { ScrollType } from 'vs/editor/common/editorCommon';
import { DocumentLineRangeMap } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping';
import { ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/utils';
import { ReentrancyBarrier } from '../../../../../base/common/controlFlow';
import { BaseCodeEditorView } from 'vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView';
import { IMergeEditorLayout } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor';
import { MergeEditorViewModel } from 'vs/workbench/contrib/mergeEditor/browser/view/viewModel';
@ -62,7 +62,7 @@ export class ScrollSynchronizer extends Disposable {
this._store.add(
this.input1View.editor.onDidScrollChange(
this.reentrancyBarrier.makeExclusive((c) => {
this.reentrancyBarrier.makeExclusiveOrSkip((c) => {
if (c.scrollTopChanged) {
handleInput1OnScroll();
}
@ -77,7 +77,7 @@ export class ScrollSynchronizer extends Disposable {
this._store.add(
this.input2View.editor.onDidScrollChange(
this.reentrancyBarrier.makeExclusive((c) => {
this.reentrancyBarrier.makeExclusiveOrSkip((c) => {
if (!this.model) {
return;
}
@ -112,7 +112,7 @@ export class ScrollSynchronizer extends Disposable {
);
this._store.add(
this.inputResultView.editor.onDidScrollChange(
this.reentrancyBarrier.makeExclusive((c) => {
this.reentrancyBarrier.makeExclusiveOrSkip((c) => {
if (c.scrollTopChanged) {
if (this.shouldAlignResult) {
this.input1View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate);
@ -146,7 +146,7 @@ export class ScrollSynchronizer extends Disposable {
const baseView = this.baseView.read(reader);
if (baseView) {
store.add(baseView.editor.onDidScrollChange(
this.reentrancyBarrier.makeExclusive((c) => {
this.reentrancyBarrier.makeExclusiveOrSkip((c) => {
if (c.scrollTopChanged) {
if (!this.model) {
return;

View file

@ -12,7 +12,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IEditorPane, IEditorPaneScrollPosition, isEditorPaneWithScrolling } from 'vs/workbench/common/editor';
import { ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/utils';
import { ReentrancyBarrier } from 'vs/base/common/controlFlow';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar';
@ -75,7 +75,7 @@ export class SyncScroll extends Disposable implements IWorkbenchContribution {
this.paneInitialScrollTop.set(pane, pane.getScrollPosition());
this.paneDisposables.add(pane.onDidChangeScroll(() =>
this._reentrancyBarrier.runExclusively(() => {
this._reentrancyBarrier.runExclusivelyOrSkip(() => {
this.onDidEditorPaneScroll(pane);
})
));