mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 05:19:39 +00:00
Fixes #209324
This commit is contained in:
parent
78cfeea7ba
commit
a163748c59
69
src/vs/base/common/controlFlow.ts
Normal file
69
src/vs/base/common/controlFlow.ts
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
})
|
||||
)
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
})
|
||||
));
|
||||
|
|
Loading…
Reference in a new issue