Open
Description
re: #211122
Issue:
Sometimes when scrolling through a notebook, the sticky scroll lines that appear will render consistently far too late. A header that is in cell 1 of the notebook will not be revealed until far later. I do not know the cause or a consistent set of steps for reproduction.
Information:
All of the code is located in src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts
Here are mermaid diagrams that accurately describe the components and interactions.
Notebook Editor Sticky Scroll Architecture
classDiagram
class NotebookStickyScroll {
-domNode: HTMLElement
-notebookEditor: INotebookEditor
-notebookCellList: INotebookCellList
-layoutFn: Function
-currentStickyLines: Map~OutlineEntry, StickyLineInfo~
-notebookCellOutlineReference: IReference~NotebookCellOutlineDataSource~
-_onDidChangeNotebookStickyScroll: Emitter~number~
+getDomNode(): HTMLElement
+getCurrentStickyHeight(): number
+onDidChangeNotebookStickyScroll: Event~number~
-init(): Promise~void~
-updateConfig(e: NotebookOptionsChangeEvent): void
-updateContent(newMap: Map): void
-onContextMenu(e: MouseEvent): void
+dispose(): void
}
class NotebookStickyLine {
+element: HTMLElement
+foldingIcon: StickyFoldingIcon
+header: HTMLElement
+entry: OutlineEntry
+notebookEditor: INotebookEditor
-toggleFoldRange(currentState: CellFoldingState): void
-focusCell(): void
+getParentCount(entry: OutlineEntry): number
}
class StickyFoldingIcon {
+domNode: HTMLElement
+isCollapsed: boolean
+dimension: number
+setVisible(visible: boolean): void
}
class OutlineEntry {
+cell: ICellViewModel
+index: number
+level: number
+label: string
+parent: OutlineEntry
+asFlatList(flatList: OutlineEntry[]): void
}
class NotebookCellOutlineDataSource {
+entries: OutlineEntry[]
+onDidChange: Event~void~
+computeFullSymbols(token: CancellationToken): Promise~void~
}
class INotebookEditor {
+notebookOptions: NotebookOptions
+scrollTop: number
+visibleRanges: VisibleRange[]
+onDidScroll(): Event~void~
+onDidAttachViewModel(): Event~void~
+focusNotebookCell(cell, focus): void
+getAbsoluteTopOfElement(cell): number
+setScrollTop(top: number): void
+cellAt(index: number): ICellViewModel
+getLayoutInfo(): LayoutInfo
}
class INotebookCellList {
+triggerScrollFromMouseWheelEvent(event: IMouseWheelEvent): void
+getCellViewScrollTop(cell): number
}
%% Relationships
NotebookStickyScroll --> NotebookStickyLine : creates/manages
NotebookStickyScroll --> NotebookCellOutlineDataSource : uses
NotebookStickyScroll --> INotebookEditor : interacts with
NotebookStickyScroll --> INotebookCellList : uses
NotebookStickyLine --> StickyFoldingIcon : contains
NotebookStickyLine --> OutlineEntry : represents
NotebookStickyLine --> INotebookEditor : controls
NotebookCellOutlineDataSource --> OutlineEntry : produces
%% Composition relationships
NotebookStickyScroll *-- NotebookStickyLine
NotebookStickyLine *-- StickyFoldingIcon
Flow Diagram
flowchart TD
A[NotebookStickyScroll Init] --> B[Get Outline Data Source]
B --> C[Compute Full Symbols]
C --> D[Initial Content Update]
D --> E[Setup Event Listeners]
E --> F[Scroll Event]
E --> G[Outline Change Event]
E --> H[View Model Change Event]
E --> I[Options Change Event]
F --> J[Compute Content]
G --> J
H --> J
I --> K[Update Config]
J --> L[Compare with Current Sticky Lines]
L --> M{Lines Changed?}
M -->|Yes| N[Update Content]
M -->|No| O[Dispose Computed Lines]
N --> P[Clear DOM]
P --> Q[Dispose Current Lines]
Q --> R[Render New Lines]
R --> S[Update Height]
S --> T[Fire Change Event]
T --> U[Layout Update]
K --> V{Sticky Enabled?}
V -->|Yes| A
V -->|No| W[Clear & Dispose]
Content Computation Flow
flowchart TD
A[computeContent Function] --> B[Get Editor Scroll Top]
B --> C[Get Visible Range]
C --> D{Visible Range Exists?}
D -->|No| E[Return Empty Map]
D -->|Yes| F{First Cell is Header?}
F -->|Yes| G[Check if Should Show First Cell Sticky]
F -->|No| H[Iterate Visible Cells]
G --> I{Scroll > 22px?}
I -->|Yes| J[Create Sticky Lines for First Cell]
I -->|No| H
H --> K[Get Current Cell & Entry]
K --> L[Get Next Cell & Entry]
L --> M{Next Cell is Header?}
M -->|Yes| N[Calculate Section Bottom]
M -->|No| O[Continue to Next Cell]
N --> P[Calculate Sticky Heights]
P --> Q{Can Render All Lines?}
Q -->|Yes| R[Render All Lines for Section]
Q -->|No| S{Next Section >= Current?}
S -->|Yes| T[Render Next Section Lines]
S -->|No| U{Available Space >= Next Height?}
U -->|Yes| V[Render Limited Lines]
U -->|No| T
O --> W{More Cells?}
W -->|Yes| K
W -->|No| X[Render Lines for Last Section]
J --> Y[Return Sticky Lines Map]
R --> Y
T --> Y
V --> Y
X --> Y
E --> Y
Key Components Interaction
sequenceDiagram
participant User
participant StickyScroll as NotebookStickyScroll
participant Editor as INotebookEditor
participant Outline as NotebookCellOutlineDataSource
participant StickyLine as NotebookStickyLine
User->>Editor: Scrolls notebook
Editor->>StickyScroll: onDidScroll event
StickyScroll->>Outline: Get current entries
StickyScroll->>StickyScroll: computeContent()
StickyScroll->>StickyLine: Create new sticky lines
StickyScroll->>StickyScroll: updateContent()
StickyScroll->>Editor: Fire height change event
Editor->>StickyScroll: layoutFn callback
User->>StickyLine: Click header
StickyLine->>Editor: focusNotebookCell()
StickyLine->>Editor: setScrollTop()
User->>StickyLine: Click folding icon
StickyLine->>StickyLine: toggleFoldRange()
StickyLine->>Editor: Update folding state
User->>StickyScroll: Right-click (context menu)
StickyScroll->>StickyScroll: onContextMenu()
StickyScroll->>Editor: Show context menu