From 6cc118c0119a5ebdddf92fdf3908919fb45faab7 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 13 Mar 2020 18:12:52 -0700 Subject: [PATCH] Find & Replace Widget --- .../browser/find/simpleFindReplaceWidget.css | 131 ++++++ .../browser/find/simpleFindReplaceWidget.ts | 410 ++++++++++++++++++ .../browser/contrib/notebookFindWidget.ts | 11 +- .../notebook/browser/notebookEditor.ts | 2 +- 4 files changed, 551 insertions(+), 3 deletions(-) create mode 100644 src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.css create mode 100644 src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.css b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.css new file mode 100644 index 00000000000..a6448cec791 --- /dev/null +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.css @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .simple-fr-find-part-wrapper { + overflow: hidden; + z-index: 10; + position: absolute; + top: -45px; + right: 18px; + width: 318px; + max-width: calc(100% - 28px - 28px - 8px); + pointer-events: none; + transition: top 200ms linear; + visibility: hidden; +} + +.monaco-workbench .simple-fr-find-part { + /* visibility: hidden; Use visibility to maintain flex layout while hidden otherwise interferes with transition */ + z-index: 10; + position: relative; + top: 0px; + display: flex; + padding: 4px; + align-items: center; + pointer-events: all; + margin: 0 0 0 17px; +} + +.monaco-workbench .simple-fr-replace-part { + /* visibility: hidden; Use visibility to maintain flex layout while hidden otherwise interferes with transition */ + z-index: 10; + position: relative; + top: 0px; + display: flex; + padding: 4px; + align-items: center; + pointer-events: all; + margin: 0 0 0 17px; +} + +.monaco-workbench .simple-fr-find-part-wrapper .monaco-findInput { + width: 224px; +} + +.monaco-workbench .simple-fr-find-part-wrapper .button { + width: 20px; + height: 20px; + flex: initial; + margin-left: 3px; + background-position: 50%; + background-repeat: no-repeat; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; +} + +.monaco-workbench .simple-fr-find-part-wrapper.visible .simple-fr-find-part { + visibility: visible; +} + +.monaco-workbench .simple-fr-find-part-wrapper .toggle { + position: absolute; + top: 0; + width: 18px; + height: 100%; + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: center; + margin-left: 0px; + pointer-events: all; +} + +.monaco-workbench .simple-fr-find-part-wrapper.visible { + visibility: visible; +} + +.monaco-workbench .simple-fr-find-part-wrapper.visible-transition { + top: 0; +} + +.monaco-workbench .simple-fr-find-part .monaco-findInput { + flex: 1; +} + +.monaco-workbench .simple-fr-find-part .button { + min-width: 20px; + width: 20px; + height: 20px; + display: flex; + flex: initial; + margin-left: 3px; + background-position: center center; + background-repeat: no-repeat; + cursor: pointer; +} + +.monaco-workbench .simple-fr-find-part .button.previous { + background-image: url('images/chevron-previous-light.svg'); +} + +.monaco-workbench .simple-fr-find-part .button.next { + background-image: url('images/chevron-next-light.svg'); +} + +.monaco-workbench .simple-fr-find-part .button.close-fw { + background-image: url('images/close-light.svg'); +} + +.hc-black .monaco-workbench .simple-fr-find-part .button.previous, +.vs-dark .monaco-workbench .simple-fr-find-part .button.previous { + background-image: url('images/chevron-previous-dark.svg'); +} + +.hc-black .monaco-workbench .simple-fr-find-part .button.next, +.vs-dark .monaco-workbench .simple-fr-find-part .button.next { + background-image: url('images/chevron-next-dark.svg'); +} + +.hc-black .monaco-workbench .simple-fr-find-part .button.close-fw, +.vs-dark .monaco-workbench .simple-fr-find-part .button.close-fw { + background-image: url('images/close-dark.svg'); +} + +.monaco-workbench .simple-fr-find-part .button.disabled { + opacity: 0.3; + cursor: default; +} diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts new file mode 100644 index 00000000000..9399ffe1856 --- /dev/null +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget.ts @@ -0,0 +1,410 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./simpleFindReplaceWidget'; +import * as nls from 'vs/nls'; +import * as dom from 'vs/base/browser/dom'; +import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { Delayer } from 'vs/base/common/async'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState'; +import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox'; +import { SimpleButton } from 'vs/editor/contrib/find/findWidget'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry'; +import { IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget'; +import { ReplaceInput, IReplaceInputStyles } from 'vs/base/browser/ui/findinput/replaceInput'; + +const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); +const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find"); +const NLS_PREVIOUS_MATCH_BTN_LABEL = nls.localize('label.previousMatchButton', "Previous match"); +const NLS_NEXT_MATCH_BTN_LABEL = nls.localize('label.nextMatchButton', "Next match"); +const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close"); +const NLS_TOGGLE_REPLACE_MODE_BTN_LABEL = nls.localize('label.toggleReplaceButton', "Toggle Replace mode"); +const NLS_REPLACE_INPUT_LABEL = nls.localize('label.replace', "Replace"); +const NLS_REPLACE_INPUT_PLACEHOLDER = nls.localize('placeholder.replace', "Replace"); +const NLS_REPLACE_BTN_LABEL = nls.localize('label.replaceButton', "Replace"); +const NLS_REPLACE_ALL_BTN_LABEL = nls.localize('label.replaceAllButton', "Replace All"); + +export abstract class SimpleFindReplaceWidget extends Widget { + protected readonly _findInput: FindInput; + private readonly _domNode: HTMLElement; + private readonly _innerFindDomNode: HTMLElement; + private readonly _focusTracker: dom.IFocusTracker; + private readonly _findInputFocusTracker: dom.IFocusTracker; + private readonly _updateHistoryDelayer: Delayer; + private readonly prevBtn: SimpleButton; + private readonly nextBtn: SimpleButton; + + private readonly _replaceInput!: ReplaceInput; + private readonly _innerReplaceDomNode!: HTMLElement; + private _toggleReplaceBtn!: SimpleButton; + private readonly _replaceInputFocusTracker!: dom.IFocusTracker; + private _replaceBtn!: SimpleButton; + private _replaceAllBtn!: SimpleButton; + + + private _isVisible: boolean = false; + private _isReplaceVisible: boolean = false; + private foundMatch: boolean = false; + + constructor( + @IContextViewService private readonly _contextViewService: IContextViewService, + @IContextKeyService contextKeyService: IContextKeyService, + private readonly _state: FindReplaceState = new FindReplaceState(), + showOptionButtons?: boolean + ) { + super(); + + this._domNode = document.createElement('div'); + this._domNode.classList.add('simple-fr-find-part-wrapper'); + this._register(this._state.onFindReplaceStateChange((e) => this._onStateChanged(e))); + + // Toggle replace button + this._toggleReplaceBtn = this._register(new SimpleButton({ + label: NLS_TOGGLE_REPLACE_MODE_BTN_LABEL, + className: 'codicon toggle left', + onTrigger: () => { + this._isReplaceVisible = !this._isReplaceVisible; + this._state.change({ isReplaceRevealed: this._isReplaceVisible }, false); + if (this._isReplaceVisible) { + this._innerReplaceDomNode.style.display = 'flex'; + } else { + this._innerReplaceDomNode.style.display = 'none'; + } + } + })); + this._toggleReplaceBtn.toggleClass('codicon-chevron-down', this._isReplaceVisible); + this._toggleReplaceBtn.toggleClass('codicon-chevron-right', !this._isReplaceVisible); + this._toggleReplaceBtn.setExpanded(this._isReplaceVisible); + this._domNode.appendChild(this._toggleReplaceBtn.domNode); + + + this._innerFindDomNode = document.createElement('div'); + this._innerFindDomNode.classList.add('simple-fr-find-part'); + + this._findInput = this._register(new ContextScopedFindInput(null, this._contextViewService, { + label: NLS_FIND_INPUT_LABEL, + placeholder: NLS_FIND_INPUT_PLACEHOLDER, + validation: (value: string): InputBoxMessage | null => { + if (value.length === 0 || !this._findInput.getRegex()) { + return null; + } + try { + new RegExp(value); + return null; + } catch (e) { + this.foundMatch = false; + this.updateButtons(this.foundMatch); + return { content: e.message }; + } + } + }, contextKeyService, showOptionButtons)); + + // Find History with update delayer + this._updateHistoryDelayer = new Delayer(500); + + this.oninput(this._findInput.domNode, (e) => { + this.foundMatch = this.onInputChanged(); + this.updateButtons(this.foundMatch); + this._delayedUpdateHistory(); + }); + + this._findInput.setRegex(!!this._state.isRegex); + this._findInput.setCaseSensitive(!!this._state.matchCase); + this._findInput.setWholeWords(!!this._state.wholeWord); + + this._register(this._findInput.onDidOptionChange(() => { + this._state.change({ + isRegex: this._findInput.getRegex(), + wholeWord: this._findInput.getWholeWords(), + matchCase: this._findInput.getCaseSensitive() + }, true); + })); + + this._register(this._state.onFindReplaceStateChange(() => { + this._findInput.setRegex(this._state.isRegex); + this._findInput.setWholeWords(this._state.wholeWord); + this._findInput.setCaseSensitive(this._state.matchCase); + this.findFirst(); + })); + + this.prevBtn = this._register(new SimpleButton({ + label: NLS_PREVIOUS_MATCH_BTN_LABEL, + className: 'previous', + onTrigger: () => { + this.find(true); + } + })); + + this.nextBtn = this._register(new SimpleButton({ + label: NLS_NEXT_MATCH_BTN_LABEL, + className: 'next', + onTrigger: () => { + this.find(false); + } + })); + + const closeBtn = this._register(new SimpleButton({ + label: NLS_CLOSE_BTN_LABEL, + className: 'close-fw', + onTrigger: () => { + this.hide(); + } + })); + + this._innerFindDomNode.appendChild(this._findInput.domNode); + this._innerFindDomNode.appendChild(this.prevBtn.domNode); + this._innerFindDomNode.appendChild(this.nextBtn.domNode); + this._innerFindDomNode.appendChild(closeBtn.domNode); + + // _domNode wraps _innerDomNode, ensuring that + this._domNode.appendChild(this._innerFindDomNode); + + this.onkeyup(this._innerFindDomNode, e => { + if (e.equals(KeyCode.Escape)) { + this.hide(); + e.preventDefault(); + return; + } + }); + + this._focusTracker = this._register(dom.trackFocus(this._innerFindDomNode)); + this._register(this._focusTracker.onDidFocus(this.onFocusTrackerFocus.bind(this))); + this._register(this._focusTracker.onDidBlur(this.onFocusTrackerBlur.bind(this))); + + this._findInputFocusTracker = this._register(dom.trackFocus(this._findInput.domNode)); + this._register(this._findInputFocusTracker.onDidFocus(this.onFindInputFocusTrackerFocus.bind(this))); + this._register(this._findInputFocusTracker.onDidBlur(this.onFindInputFocusTrackerBlur.bind(this))); + + this._register(dom.addDisposableListener(this._innerFindDomNode, 'click', (event) => { + event.stopPropagation(); + })); + + // Replace + this._innerReplaceDomNode = document.createElement('div'); + this._innerReplaceDomNode.classList.add('simple-fr-replace-part'); + + this._replaceInput = this._register(new ContextScopedReplaceInput(null, undefined, { + label: NLS_REPLACE_INPUT_LABEL, + placeholder: NLS_REPLACE_INPUT_PLACEHOLDER, + history: [] + }, contextKeyService, false)); + this._innerReplaceDomNode.appendChild(this._replaceInput.domNode); + this._replaceInputFocusTracker = this._register(dom.trackFocus(this._replaceInput.domNode)); + this._register(this._replaceInputFocusTracker.onDidFocus(this.onReplaceInputFocusTrackerFocus.bind(this))); + this._register(this._replaceInputFocusTracker.onDidBlur(this.onReplaceInputFocusTrackerBlur.bind(this))); + + this._domNode.appendChild(this._innerReplaceDomNode); + + if (this._isReplaceVisible) { + this._innerReplaceDomNode.style.display = 'flex'; + } else { + this._innerReplaceDomNode.style.display = 'none'; + } + + this._replaceBtn = this._register(new SimpleButton({ + label: NLS_REPLACE_BTN_LABEL, + className: 'codicon codicon-replace', + onTrigger: () => { + // this._controller.replace(); + } + })); + + // Replace all button + this._replaceAllBtn = this._register(new SimpleButton({ + label: NLS_REPLACE_ALL_BTN_LABEL, + className: 'codicon codicon-replace-all', + onTrigger: () => { + // this._controller.replaceAll(); + } + })); + + this._innerReplaceDomNode.appendChild(this._replaceBtn.domNode); + this._innerReplaceDomNode.appendChild(this._replaceAllBtn.domNode); + + + } + + protected abstract onInputChanged(): boolean; + protected abstract find(previous: boolean): void; + protected abstract findFirst(): void; + protected abstract onFocusTrackerFocus(): void; + protected abstract onFocusTrackerBlur(): void; + protected abstract onFindInputFocusTrackerFocus(): void; + protected abstract onFindInputFocusTrackerBlur(): void; + protected abstract onReplaceInputFocusTrackerFocus(): void; + protected abstract onReplaceInputFocusTrackerBlur(): void; + + protected get inputValue() { + return this._findInput.getValue(); + } + + public get focusTracker(): dom.IFocusTracker { + return this._focusTracker; + } + + public updateTheme(theme: IColorTheme): void { + const inputStyles: IFindInputStyles = { + inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder), + inputActiveOptionBackground: theme.getColor(inputActiveOptionBackground), + inputBackground: theme.getColor(inputBackground), + inputForeground: theme.getColor(inputForeground), + inputBorder: theme.getColor(inputBorder), + inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground), + inputValidationInfoForeground: theme.getColor(inputValidationInfoForeground), + inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder), + inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground), + inputValidationWarningForeground: theme.getColor(inputValidationWarningForeground), + inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder), + inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground), + inputValidationErrorForeground: theme.getColor(inputValidationErrorForeground), + inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder) + }; + this._findInput.style(inputStyles); + const replaceStyles: IReplaceInputStyles = { + inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder), + inputActiveOptionBackground: theme.getColor(inputActiveOptionBackground), + inputBackground: theme.getColor(inputBackground), + inputForeground: theme.getColor(inputForeground), + inputBorder: theme.getColor(inputBorder), + inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground), + inputValidationInfoForeground: theme.getColor(inputValidationInfoForeground), + inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder), + inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground), + inputValidationWarningForeground: theme.getColor(inputValidationWarningForeground), + inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder), + inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground), + inputValidationErrorForeground: theme.getColor(inputValidationErrorForeground), + inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder) + }; + this._replaceInput.style(replaceStyles); + } + + private _onStateChanged(e: FindReplaceStateChangedEvent): void { + this._updateButtons(); + } + + private _updateButtons(): void { + this._findInput.setEnabled(this._isVisible); + this._replaceInput.setEnabled(this._isVisible && this._isReplaceVisible); + let findInputIsNonEmpty = (this._state.searchString.length > 0); + this._replaceBtn.setEnabled(this._isVisible && this._isReplaceVisible && findInputIsNonEmpty); + this._replaceAllBtn.setEnabled(this._isVisible && this._isReplaceVisible && findInputIsNonEmpty); + + dom.toggleClass(this._domNode, 'replaceToggled', this._isReplaceVisible); + this._toggleReplaceBtn.toggleClass('codicon-chevron-right', !this._isReplaceVisible); + this._toggleReplaceBtn.toggleClass('codicon-chevron-down', this._isReplaceVisible); + this._toggleReplaceBtn.setExpanded(this._isReplaceVisible); + } + + + dispose() { + super.dispose(); + + if (this._domNode && this._domNode.parentElement) { + this._domNode.parentElement.removeChild(this._domNode); + } + } + + public getDomNode() { + return this._domNode; + } + + public reveal(initialInput?: string): void { + if (initialInput) { + this._findInput.setValue(initialInput); + } + + if (this._isVisible) { + this._findInput.select(); + return; + } + + this._isVisible = true; + this.updateButtons(this.foundMatch); + + setTimeout(() => { + dom.addClass(this._domNode, 'visible'); + dom.addClass(this._domNode, 'visible-transition'); + this._domNode.setAttribute('aria-hidden', 'false'); + this._findInput.select(); + }, 0); + } + + public show(initialInput?: string): void { + if (initialInput && !this._isVisible) { + this._findInput.setValue(initialInput); + } + + this._isVisible = true; + + setTimeout(() => { + dom.addClass(this._domNode, 'visible'); + dom.addClass(this._domNode, 'visible-transition'); + this._domNode.setAttribute('aria-hidden', 'false'); + }, 0); + } + + public hide(): void { + if (this._isVisible) { + dom.removeClass(this._domNode, 'visible-transition'); + this._domNode.setAttribute('aria-hidden', 'true'); + // Need to delay toggling visibility until after Transition, then visibility hidden - removes from tabIndex list + setTimeout(() => { + this._isVisible = false; + this.updateButtons(this.foundMatch); + dom.removeClass(this._domNode, 'visible'); + }, 200); + } + } + + protected _delayedUpdateHistory() { + this._updateHistoryDelayer.trigger(this._updateHistory.bind(this)); + } + + protected _updateHistory() { + this._findInput.inputBox.addToHistory(); + } + + protected _getRegexValue(): boolean { + return this._findInput.getRegex(); + } + + protected _getWholeWordValue(): boolean { + return this._findInput.getWholeWords(); + } + + protected _getCaseSensitiveValue(): boolean { + return this._findInput.getCaseSensitive(); + } + + protected updateButtons(foundMatch: boolean) { + const hasInput = this.inputValue.length > 0; + this.prevBtn.setEnabled(this._isVisible && hasInput && foundMatch); + this.nextBtn.setEnabled(this._isVisible && hasInput && foundMatch); + } +} + +// theming +registerThemingParticipant((theme, collector) => { + const findWidgetBGColor = theme.getColor(editorWidgetBackground); + if (findWidgetBGColor) { + collector.addRule(`.monaco-workbench .simple-fr-find-part-wrapper { background-color: ${findWidgetBGColor} !important; }`); + } + + const widgetForeground = theme.getColor(editorWidgetForeground); + if (widgetForeground) { + collector.addRule(`.monaco-workbench .simple-fr-find-part-wrapper { color: ${widgetForeground}; }`); + } + + const widgetShadowColor = theme.getColor(widgetShadow); + if (widgetShadowColor) { + collector.addRule(`.monaco-workbench .simple-fr-find-part-wrapper { box-shadow: 0 2px 8px ${widgetShadowColor}; }`); + } +}); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookFindWidget.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookFindWidget.ts index 5aaa416a01b..f04d4630f9d 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookFindWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookFindWidget.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SimpleFindWidget } from 'vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED, INotebookEditor, CellFindMatch, CellState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -14,8 +13,9 @@ import { ICellModelDeltaDecorations, ICellModelDecorations } from 'vs/workbench/ import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; +import { SimpleFindReplaceWidget } from 'vs/workbench/contrib/codeEditor/browser/find/simpleFindReplaceWidget'; -export class NotebookFindWidget extends SimpleFindWidget { +export class NotebookFindWidget extends SimpleFindReplaceWidget { protected _findWidgetFocused: IContextKey; private _findMatches: CellFindMatch[] = []; protected _findMatchesStarts: PrefixSumComputer | null = null; @@ -100,6 +100,13 @@ export class NotebookFindWidget extends SimpleFindWidget { this._findWidgetFocused.reset(); } + protected onReplaceInputFocusTrackerFocus(): void { + // throw new Error('Method not implemented.'); + } + protected onReplaceInputFocusTrackerBlur(): void { + // throw new Error('Method not implemented.'); + } + protected onFindInputFocusTrackerFocus(): void { } protected onFindInputFocusTrackerBlur(): void { } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index f35673642d1..fc8bb1f4105 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -169,7 +169,7 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor { DOM.addClass(this.body, 'cell-list-container'); this.createCellList(); DOM.append(parent, this.body); - DOM.append(this.body, this.findWidget.getDomNode()); + DOM.append(parent, this.findWidget.getDomNode()); } private createCellList(): void { -- GitLab