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 { DetailedLineRangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping';
|
||||||
import { LineRangeEdit } from 'vs/workbench/contrib/mergeEditor/browser/model/editing';
|
import { LineRangeEdit } from 'vs/workbench/contrib/mergeEditor/browser/model/editing';
|
||||||
import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange';
|
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 { IMergeDiffComputer } from './diffComputer';
|
||||||
import { autorun, IObservable, IReader, ITransaction, observableSignal, observableValue, transaction } from 'vs/base/common/observable';
|
import { autorun, IObservable, IReader, ITransaction, observableSignal, observableValue, transaction } from 'vs/base/common/observable';
|
||||||
import { UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo';
|
import { UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo';
|
||||||
|
@ -24,7 +24,7 @@ export class TextModelDiffs extends Disposable {
|
||||||
private _isDisposed = false;
|
private _isDisposed = false;
|
||||||
|
|
||||||
public get isApplyingChange() {
|
public get isApplyingChange() {
|
||||||
return this._barrier.isActive;
|
return this._barrier.isOccupied;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -44,14 +44,14 @@ export class TextModelDiffs extends Disposable {
|
||||||
|
|
||||||
this._register(
|
this._register(
|
||||||
baseTextModel.onDidChangeContent(
|
baseTextModel.onDidChangeContent(
|
||||||
this._barrier.makeExclusive(() => {
|
this._barrier.makeExclusiveOrSkip(() => {
|
||||||
recomputeSignal.trigger(undefined);
|
recomputeSignal.trigger(undefined);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
this._register(
|
this._register(
|
||||||
textModel.onDidChangeContent(
|
textModel.onDidChangeContent(
|
||||||
this._barrier.makeExclusive(() => {
|
this._barrier.makeExclusiveOrSkip(() => {
|
||||||
recomputeSignal.trigger(undefined);
|
recomputeSignal.trigger(undefined);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { ArrayQueue, CompareResult } from 'vs/base/common/arrays';
|
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 { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||||
import { IObservable, autorunOpts, observableFromEvent } from 'vs/base/common/observable';
|
import { IObservable, autorunOpts, observableFromEvent } from 'vs/base/common/observable';
|
||||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget';
|
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 { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
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(
|
export function setStyle(
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
style: {
|
style: {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { autorunWithStore, IObservable } from 'vs/base/common/observable';
|
||||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget';
|
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget';
|
||||||
import { ScrollType } from 'vs/editor/common/editorCommon';
|
import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||||
import { DocumentLineRangeMap } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping';
|
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 { BaseCodeEditorView } from 'vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView';
|
||||||
import { IMergeEditorLayout } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor';
|
import { IMergeEditorLayout } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor';
|
||||||
import { MergeEditorViewModel } from 'vs/workbench/contrib/mergeEditor/browser/view/viewModel';
|
import { MergeEditorViewModel } from 'vs/workbench/contrib/mergeEditor/browser/view/viewModel';
|
||||||
|
@ -62,7 +62,7 @@ export class ScrollSynchronizer extends Disposable {
|
||||||
|
|
||||||
this._store.add(
|
this._store.add(
|
||||||
this.input1View.editor.onDidScrollChange(
|
this.input1View.editor.onDidScrollChange(
|
||||||
this.reentrancyBarrier.makeExclusive((c) => {
|
this.reentrancyBarrier.makeExclusiveOrSkip((c) => {
|
||||||
if (c.scrollTopChanged) {
|
if (c.scrollTopChanged) {
|
||||||
handleInput1OnScroll();
|
handleInput1OnScroll();
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ export class ScrollSynchronizer extends Disposable {
|
||||||
|
|
||||||
this._store.add(
|
this._store.add(
|
||||||
this.input2View.editor.onDidScrollChange(
|
this.input2View.editor.onDidScrollChange(
|
||||||
this.reentrancyBarrier.makeExclusive((c) => {
|
this.reentrancyBarrier.makeExclusiveOrSkip((c) => {
|
||||||
if (!this.model) {
|
if (!this.model) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ export class ScrollSynchronizer extends Disposable {
|
||||||
);
|
);
|
||||||
this._store.add(
|
this._store.add(
|
||||||
this.inputResultView.editor.onDidScrollChange(
|
this.inputResultView.editor.onDidScrollChange(
|
||||||
this.reentrancyBarrier.makeExclusive((c) => {
|
this.reentrancyBarrier.makeExclusiveOrSkip((c) => {
|
||||||
if (c.scrollTopChanged) {
|
if (c.scrollTopChanged) {
|
||||||
if (this.shouldAlignResult) {
|
if (this.shouldAlignResult) {
|
||||||
this.input1View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate);
|
this.input1View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate);
|
||||||
|
@ -146,7 +146,7 @@ export class ScrollSynchronizer extends Disposable {
|
||||||
const baseView = this.baseView.read(reader);
|
const baseView = this.baseView.read(reader);
|
||||||
if (baseView) {
|
if (baseView) {
|
||||||
store.add(baseView.editor.onDidScrollChange(
|
store.add(baseView.editor.onDidScrollChange(
|
||||||
this.reentrancyBarrier.makeExclusive((c) => {
|
this.reentrancyBarrier.makeExclusiveOrSkip((c) => {
|
||||||
if (c.scrollTopChanged) {
|
if (c.scrollTopChanged) {
|
||||||
if (!this.model) {
|
if (!this.model) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||||
import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor';
|
import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor';
|
||||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||||
import { IEditorPane, IEditorPaneScrollPosition, isEditorPaneWithScrolling } from 'vs/workbench/common/editor';
|
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 { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||||
import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar';
|
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.paneInitialScrollTop.set(pane, pane.getScrollPosition());
|
||||||
this.paneDisposables.add(pane.onDidChangeScroll(() =>
|
this.paneDisposables.add(pane.onDidChangeScroll(() =>
|
||||||
this._reentrancyBarrier.runExclusively(() => {
|
this._reentrancyBarrier.runExclusivelyOrSkip(() => {
|
||||||
this.onDidEditorPaneScroll(pane);
|
this.onDidEditorPaneScroll(pane);
|
||||||
})
|
})
|
||||||
));
|
));
|
||||||
|
|
Loading…
Reference in a new issue