Skip to content

Commit

Permalink
Add eslint rule for potentially unsafe disposable patterns (#209555)
Browse files Browse the repository at this point in the history
`DisposableStore`/`MutableDisposable` properties should almost always be `readonly`. Otherwise it's easy to accidentally overwrite the property and leak the previous value. This commonly happens with code such as:

```ts
class Foo {
     private disposables = new DisposableStore();

     bar() {
         this.disposables = new DisposableStore(); // leaks old values
         ...
     }
```

This change adds an eslint rule to enforce this and adopts `readonly` for the caught cases. I only needed to add 2 suppression comments, which seems like an acceptable tradeoff for helping catch a common mistake
  • Loading branch information
mjbvz authored Apr 4, 2024
1 parent b4378df commit ae91138
Show file tree
Hide file tree
Showing 85 changed files with 151 additions and 111 deletions.
37 changes: 37 additions & 0 deletions .eslintplugin/code-no-potentially-unsafe-disposables.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as eslint from 'eslint';

/**
* Checks for potentially unsafe usage of `DisposableStore` / `MutableDisposable`.
*
* These have been the source of leaks in the past.
*/
export = new class implements eslint.Rule.RuleModule {

create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
function checkVariableDeclaration(inNode: any) {
context.report({
node: inNode,
message: `Use const for 'DisposableStore' to avoid leaks by accidental reassignment.`
});
}

function checkProperty(inNode: any) {
context.report({
node: inNode,
message: `Use readonly for DisposableStore/MutableDisposable to avoid leaks through accidental reassignment.`
});
}

return {
'VariableDeclaration[kind!="const"] NewExpression[callee.name="DisposableStore"]': checkVariableDeclaration,

'PropertyDefinition[readonly!=true][typeAnnotation.typeAnnotation.typeName.name=/DisposableStore|MutableDisposable/]': checkProperty,
'PropertyDefinition[readonly!=true] NewExpression[callee.name=/DisposableStore|MutableDisposable/]': checkProperty,
};
}
};
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"local/code-no-native-private": "warn",
"local/code-parameter-properties-must-have-explicit-accessibility": "warn",
"local/code-no-nls-in-standalone-editor": "warn",
"local/code-no-potentially-unsafe-disposables": "warn",
"local/code-no-standalone-editor": "warn",
"local/code-no-unexternalized-strings": "warn",
"local/code-must-use-super-dispose": "warn",
Expand Down
2 changes: 1 addition & 1 deletion src/vs/base/browser/ui/findinput/findInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class FindInput extends Widget {
private readonly showCommonFindToggles: boolean;
private fixFocusOnOptionClickEnabled = true;
private imeSessionInProgress = false;
private additionalTogglesDisposables: MutableDisposable<DisposableStore> = this._register(new MutableDisposable());
private readonly additionalTogglesDisposables: MutableDisposable<DisposableStore> = this._register(new MutableDisposable());

protected readonly controls: HTMLDivElement;
protected readonly regex?: RegexToggle;
Expand Down
2 changes: 1 addition & 1 deletion src/vs/base/browser/ui/toolbar/toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class ToolBar extends Disposable {

private _onDidChangeDropdownVisibility = this._register(new EventMultiplexer<boolean>());
readonly onDidChangeDropdownVisibility = this._onDidChangeDropdownVisibility.event;
private disposables = this._register(new DisposableStore());
private readonly disposables = this._register(new DisposableStore());

constructor(container: HTMLElement, contextMenuProvider: IContextMenuProvider, options: IToolBarOptions = { orientation: ActionsOrientation.HORIZONTAL }) {
super();
Expand Down
4 changes: 2 additions & 2 deletions src/vs/base/browser/ui/tree/abstractTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class TreeNodeListDragAndDrop<T, TFilterData, TRef> implements IListDragAndDrop<

private autoExpandNode: ITreeNode<T, TFilterData> | undefined;
private autoExpandDisposable: IDisposable = Disposable.None;
private disposables = new DisposableStore();
private readonly disposables = new DisposableStore();

constructor(private modelProvider: () => ITreeModel<T, TFilterData, TRef>, private dnd: ITreeDragAndDrop<T>) { }

Expand Down Expand Up @@ -1561,7 +1561,7 @@ class StickyScrollWidget<T, TFilterData, TRef> implements IDisposable {
private readonly _rootDomNode: HTMLElement;
private _previousState: StickyScrollState<T, TFilterData, TRef> | undefined;
private _previousElements: HTMLElement[] = [];
private _previousStateDisposables: DisposableStore = new DisposableStore();
private readonly _previousStateDisposables: DisposableStore = new DisposableStore();

private stickyScrollFocus: StickyScrollFocus<T, TFilterData, TRef>;
readonly onDidChangeHasFocus: Event<boolean>;
Expand Down
2 changes: 1 addition & 1 deletion src/vs/base/common/lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ export class MutableDisposable<T extends IDisposable> implements IDisposable {
* exist and cannot be undefined.
*/
export class MandatoryMutableDisposable<T extends IDisposable> implements IDisposable {
private _disposable = new MutableDisposable<T>();
private readonly _disposable = new MutableDisposable<T>();
private _isDisposed = false;

constructor(initialValue: T) {
Expand Down
1 change: 1 addition & 0 deletions src/vs/base/parts/ipc/common/ipc.net.ts
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,7 @@ export class PersistentProtocol implements IMessagePassingProtocol {
private _socket: ISocket;
private _socketWriter: ProtocolWriter;
private _socketReader: ProtocolReader;
// eslint-disable-next-line local/code-no-potentially-unsafe-disposables
private _socketDisposables: DisposableStore;

private readonly _loadEstimator: ILoadEstimator;
Expand Down
2 changes: 1 addition & 1 deletion src/vs/editor/browser/controller/textAreaInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ export class TextAreaInput extends Disposable {

private readonly _asyncTriggerCut: RunOnceScheduler;

private _asyncFocusGainWriteScreenReaderContent: MutableDisposable<RunOnceScheduler> = this._register(new MutableDisposable());
private readonly _asyncFocusGainWriteScreenReaderContent: MutableDisposable<RunOnceScheduler> = this._register(new MutableDisposable());

private _textAreaState: TextAreaState;

Expand Down
2 changes: 1 addition & 1 deletion src/vs/editor/browser/editorDom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ export interface CssProperties {
class RefCountedCssRule {
private _referenceCount: number = 0;
private _styleElement: HTMLStyleElement | undefined;
private _styleElementDisposables: DisposableStore;
private readonly _styleElementDisposables: DisposableStore;

constructor(
public readonly key: string,
Expand Down
2 changes: 1 addition & 1 deletion src/vs/editor/common/model/textModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
private _buffer: model.ITextBuffer;
private _bufferDisposable: IDisposable;
private _options: model.TextModelResolvedOptions;
private _languageSelectionListener = this._register(new MutableDisposable<IDisposable>());
private readonly _languageSelectionListener = this._register(new MutableDisposable<IDisposable>());

private _isDisposed: boolean;
private __isDisposing: boolean;
Expand Down
2 changes: 1 addition & 1 deletion src/vs/editor/contrib/colorPicker/browser/colorDetector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export class ColorDetector extends Disposable implements IEditorContribution {
});
}

private _colorDecorationClassRefs = this._register(new DisposableStore());
private readonly _colorDecorationClassRefs = this._register(new DisposableStore());

private updateColorDecorators(colorData: IColorData[]): void {
this._colorDecorationClassRefs.clear();
Expand Down
4 changes: 2 additions & 2 deletions src/vs/editor/contrib/rename/browser/renameWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ class RenameCandidateListView {
private _minimumWidth: number;
private _typicalHalfwidthCharacterWidth: number;

private _disposables: DisposableStore;
private readonly _disposables: DisposableStore;

// FIXME@ulugbekna: rewrite using event emitters
constructor(parent: HTMLElement, opts: { fontInfo: FontInfo; onFocusChange: (newSymbolName: string) => void; onSelectionChange: () => void }) {
Expand Down Expand Up @@ -788,7 +788,7 @@ class RenameInput implements IDisposable {
private readonly _onDidChange = new Emitter<void>();
public readonly onDidChange = this._onDidChange.event;

private _disposables = new DisposableStore();
private readonly _disposables = new DisposableStore();

get domNode() {
if (!this._domNode) {
Expand Down
2 changes: 1 addition & 1 deletion src/vs/platform/contextview/browser/contextViewService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { getWindow } from 'vs/base/browser/dom';

export class ContextViewHandler extends Disposable implements IContextViewProvider {

private currentViewDisposable = this._register(new MutableDisposable<IDisposable>());
private readonly currentViewDisposable = this._register(new MutableDisposable<IDisposable>());
protected readonly contextView = this._register(new ContextView(this.layoutService.mainContainer, ContextViewDOMPosition.ABSOLUTE));

constructor(
Expand Down
2 changes: 1 addition & 1 deletion src/vs/platform/hover/browser/hover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class WorkbenchHoverDelegate extends Disposable implements IHoverDelegate
return this._delay;
}

private hoverDisposables = this._register(new DisposableStore());
private readonly hoverDisposables = this._register(new DisposableStore());

constructor(
public readonly placement: 'mouse' | 'element',
Expand Down
2 changes: 1 addition & 1 deletion src/vs/platform/policy/node/nativePolicyService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { ILogService } from 'vs/platform/log/common/log';
export class NativePolicyService extends AbstractPolicyService implements IPolicyService {

private throttler = new Throttler();
private watcher = this._register(new MutableDisposable<Watcher>());
private readonly watcher = this._register(new MutableDisposable<Watcher>());

constructor(
@ILogService private readonly logService: ILogService,
Expand Down
2 changes: 1 addition & 1 deletion src/vs/platform/quickinput/browser/quickInputTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ export class QuickInputTree extends Disposable {
private _elementTree = new Array<IQuickPickElement>();
private _itemElements = new Array<QuickPickItemElement>();
// Elements that apply to the current set of elements
private _elementDisposable = this._register(new DisposableStore());
private readonly _elementDisposable = this._register(new DisposableStore());
private _lastHover: IHoverWidget | undefined;
// This is used to prevent setting the checked state of a single element from firing the checked events
// so that we can batch them together. This can probably be improved by handling events differently,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe
private _commitCommandFinished?: RunOnceScheduler;

private _ptyHeuristicsHooks: ICommandDetectionHeuristicsHooks;
private _ptyHeuristics: MandatoryMutableDisposable<IPtyHeuristics>;
private readonly _ptyHeuristics: MandatoryMutableDisposable<IPtyHeuristics>;

get commands(): readonly TerminalCommand[] { return this._commands; }
get executingCommand(): string | undefined { return this._currentCommand.command; }
Expand Down Expand Up @@ -514,7 +514,7 @@ const enum AdjustCommandStartMarkerConstants {
*/
class WindowsPtyHeuristics extends Disposable {

private _onCursorMoveListener = this._register(new MutableDisposable());
private readonly _onCursorMoveListener = this._register(new MutableDisposable());

private _tryAdjustCommandStartMarkerScheduler?: RunOnceScheduler;
private _tryAdjustCommandStartMarkerScannedLineCount: number = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/api/browser/mainThreadTerminalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
* provided through this, even from multiple ext link providers. Xterm should remove lower
* priority intersecting links itself.
*/
private _linkProvider = this._store.add(new MutableDisposable());
private readonly _linkProvider = this._store.add(new MutableDisposable());

private _os: OperatingSystem = OS;

Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/api/common/extHostComments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo

private _commentsMap: Map<vscode.Comment, number> = new Map<vscode.Comment, number>();

private _acceptInputDisposables = new MutableDisposable<DisposableStore>();
private readonly _acceptInputDisposables = new MutableDisposable<DisposableStore>();

readonly value: vscode.CommentThread2;

Expand Down
8 changes: 4 additions & 4 deletions src/vs/workbench/api/common/extHostSCM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ class ExtHostSourceControl implements vscode.SourceControl {
}

private _historyProvider: vscode.SourceControlHistoryProvider | undefined;
private _historyProviderDisposable = new MutableDisposable<DisposableStore>();
private readonly _historyProviderDisposable = new MutableDisposable<DisposableStore>();
private _historyProviderCurrentHistoryItemGroup: vscode.SourceControlHistoryItemGroup | undefined;

get historyProvider(): vscode.SourceControlHistoryProvider | undefined {
Expand Down Expand Up @@ -598,7 +598,7 @@ class ExtHostSourceControl implements vscode.SourceControl {
this.#proxy.$updateSourceControl(this.handle, { commitTemplate });
}

private _acceptInputDisposables = new MutableDisposable<DisposableStore>();
private readonly _acceptInputDisposables = new MutableDisposable<DisposableStore>();
private _acceptInputCommand: vscode.Command | undefined = undefined;

get acceptInputCommand(): vscode.Command | undefined {
Expand All @@ -614,7 +614,7 @@ class ExtHostSourceControl implements vscode.SourceControl {
this.#proxy.$updateSourceControl(this.handle, { acceptInputCommand: internal });
}

private _actionButtonDisposables = new MutableDisposable<DisposableStore>();
private readonly _actionButtonDisposables = new MutableDisposable<DisposableStore>();
private _actionButton: vscode.SourceControlActionButton | undefined;
get actionButton(): vscode.SourceControlActionButton | undefined {
checkProposedApiEnabled(this._extension, 'scmActionButton');
Expand All @@ -639,7 +639,7 @@ class ExtHostSourceControl implements vscode.SourceControl {
}


private _statusBarDisposables = new MutableDisposable<DisposableStore>();
private readonly _statusBarDisposables = new MutableDisposable<DisposableStore>();
private _statusBarCommands: vscode.Command[] | undefined = undefined;

get statusBarCommands(): vscode.Command[] | undefined {
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/api/common/extHostStatusBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
private _name?: string;
private _color?: string | ThemeColor;
private _backgroundColor?: ThemeColor;
// eslint-disable-next-line local/code-no-potentially-unsafe-disposables
private _latestCommandRegistration?: DisposableStore;
private readonly _staleCommandRegistrations = new DisposableStore();
private _command?: {
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/api/common/extHostTerminalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
protected _environmentVariableCollections: Map<string, UnifiedEnvironmentVariableCollection> = new Map();
private _defaultProfile: ITerminalProfile | undefined;
private _defaultAutomationProfile: ITerminalProfile | undefined;
private _lastQuickFixCommands: MutableDisposable<IDisposable> = this._register(new MutableDisposable());
private readonly _lastQuickFixCommands: MutableDisposable<IDisposable> = this._register(new MutableDisposable());

private readonly _bufferer: TerminalDataBufferer;
private readonly _linkProviders: Set<vscode.TerminalLinkProvider> = new Set();
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/browser/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class MenuActions extends Disposable {
private readonly _onDidChange = this._register(new Emitter<void>());
readonly onDidChange = this._onDidChange.event;

private disposables = this._register(new DisposableStore());
private readonly disposables = this._register(new DisposableStore());

constructor(
menuId: MenuId,
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/browser/labels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ class ResourceLabelWidget extends IconLabel {
readonly onDidRender = this._onDidRender.event;

private label: IResourceLabelProps | undefined = undefined;
private decoration = this._register(new MutableDisposable<IDecoration>());
private readonly decoration = this._register(new MutableDisposable<IDecoration>());
private options: IResourceLabelOptions | undefined = undefined;

private computedIconClasses: string[] | undefined = undefined;
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/browser/parts/editor/editorAutoSave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
// Auto save: focus change & window change
private lastActiveEditor: EditorInput | undefined = undefined;
private lastActiveGroupId: GroupIdentifier | undefined = undefined;
private lastActiveEditorControlDisposable = this._register(new DisposableStore());
private readonly lastActiveEditorControlDisposable = this._register(new DisposableStore());

// Auto save: waiting on specific condition
private readonly waitingOnConditionAutoSaveWorkingCopies = new ResourceMap<{ readonly workingCopy: IWorkingCopy; readonly reason: SaveReason; condition: AutoSaveDisabledReason }>(resource => this.uriIdentityService.extUri.getComparisonKey(resource));
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/browser/parts/editor/editorPlaceholder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export abstract class EditorPlaceholder extends EditorPane {

private container: HTMLElement | undefined;
private scrollbar: DomScrollableElement | undefined;
private inputDisposable = this._register(new MutableDisposable());
private readonly inputDisposable = this._register(new MutableDisposable());

constructor(
id: string,
Expand Down
4 changes: 2 additions & 2 deletions src/vs/workbench/browser/parts/editor/editorTitleControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ export interface IEditorTitleControlDimensions {
export class EditorTitleControl extends Themable {

private editorTabsControl: IEditorTabsControl;
private editorTabsControlDisposable = this._register(new DisposableStore());
private readonly editorTabsControlDisposable = this._register(new DisposableStore());

private breadcrumbsControlFactory: BreadcrumbsControlFactory | undefined;
private breadcrumbsControlDisposables = this._register(new DisposableStore());
private readonly breadcrumbsControlDisposables = this._register(new DisposableStore());
private get breadcrumbsControl() { return this.breadcrumbsControlFactory?.control; }

constructor(
Expand Down
4 changes: 2 additions & 2 deletions src/vs/workbench/browser/parts/paneCompositePart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ export abstract class AbstractPaneCompositePart extends CompositePart<PaneCompos
private readonly location: ViewContainerLocation;
private titleContainer: HTMLElement | undefined;
private headerFooterCompositeBarContainer: HTMLElement | undefined;
protected headerFooterCompositeBarDispoables = this._register(new DisposableStore());
protected readonly headerFooterCompositeBarDispoables = this._register(new DisposableStore());
private paneCompositeBarContainer: HTMLElement | undefined;
private paneCompositeBar = this._register(new MutableDisposable<PaneCompositeBar>());
private readonly paneCompositeBar = this._register(new MutableDisposable<PaneCompositeBar>());
private compositeBarPosition: CompositeBarPosition | undefined = undefined;
private emptyPaneMessageElement: HTMLElement | undefined;

Expand Down
4 changes: 2 additions & 2 deletions src/vs/workbench/browser/parts/titlebar/menubarControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export abstract class MenubarControl extends Disposable {

protected topLevelTitles: { [menu: string]: string } = {};

protected mainMenuDisposables: DisposableStore;
protected readonly mainMenuDisposables: DisposableStore;

protected recentlyOpened: IRecentlyOpened = { files: [], workspaces: [] };

Expand Down Expand Up @@ -564,7 +564,7 @@ export class CustomMenubarControl extends MenubarControl {
return result;
}

private reinstallDisposables = this._register(new DisposableStore());
private readonly reinstallDisposables = this._register(new DisposableStore());
private setupCustomMenubar(firstTime: boolean): void {
// If there is no container, we cannot setup the menubar
if (!this.container) {
Expand Down
4 changes: 2 additions & 2 deletions src/vs/workbench/browser/parts/titlebar/titlebarPart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,8 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart {
private lastLayoutDimensions: Dimension | undefined;

private actionToolBar!: WorkbenchToolBar;
private actionToolBarDisposable = this._register(new DisposableStore());
private editorActionsChangeDisposable = this._register(new DisposableStore());
private readonly actionToolBarDisposable = this._register(new DisposableStore());
private readonly editorActionsChangeDisposable = this._register(new DisposableStore());
private actionToolBarElement!: HTMLElement;

private layoutToolbarMenu: IMenu | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class EntitlementsContribution extends Disposable implements IWorkbenchContribut
private isInitialized = false;
private showAccountsBadgeContextKey = new RawContextKey<boolean>(accountsBadgeConfigKey, false).bindTo(this.contextService);
private showChatWelcomeViewContextKey = new RawContextKey<boolean>(chatWelcomeViewConfigKey, false).bindTo(this.contextService);
private accountsMenuBadgeDisposable = this._register(new MutableDisposable());
private readonly accountsMenuBadgeDisposable = this._register(new MutableDisposable());

constructor(
@IContextKeyService private readonly contextService: IContextKeyService,
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/contrib/chat/browser/chatInputPart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
private inputSideToolbarContainer?: HTMLElement;

private followupsContainer!: HTMLElement;
private followupsDisposables = this._register(new DisposableStore());
private readonly followupsDisposables = this._register(new DisposableStore());

private implicitContextContainer!: HTMLElement;
private implicitContextLabel!: HTMLElement;
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/contrib/chat/browser/chatQuick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ class QuickChat extends Disposable {
private sash!: Sash;
private model: ChatModel | undefined;
private _currentQuery: string | undefined;
private maintainScrollTimer: MutableDisposable<IDisposable> = this._register(new MutableDisposable<IDisposable>());
private readonly maintainScrollTimer: MutableDisposable<IDisposable> = this._register(new MutableDisposable<IDisposable>());
private _deferUpdatingDynamicLayout: boolean = false;

constructor(
Expand Down
Loading

0 comments on commit ae91138

Please sign in to comment.