4
4
*--------------------------------------------------------------------------------------------*/
5
5
6
6
import { Emitter , Event } from 'vs/base/common/event' ;
7
- import { DisposableStore , MutableDisposable , combinedDisposable } from 'vs/base/common/lifecycle' ;
7
+ import { DisposableStore , MutableDisposable } from 'vs/base/common/lifecycle' ;
8
8
import { isEqual } from 'vs/base/common/resources' ;
9
9
import { URI } from 'vs/base/common/uri' ;
10
10
import { IConfigurationService } from 'vs/platform/configuration/common/configuration' ;
11
11
import { IMarkerService } from 'vs/platform/markers/common/markers' ;
12
12
import { IThemeService } from 'vs/platform/theme/common/themeService' ;
13
- import { IActiveNotebookEditor , ICellViewModel , INotebookEditor , INotebookViewCellsUpdateEvent } from 'vs/workbench/contrib/notebook/browser/notebookBrowser' ;
14
- import { CellKind , NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon' ;
15
- import { INotebookExecutionStateService , NotebookExecutionType , type ICellExecutionStateChangedEvent , type IExecutionStateChangedEvent } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService' ;
13
+ import { IActiveNotebookEditor , ICellViewModel , INotebookEditor , type INotebookViewCellsUpdateEvent } from 'vs/workbench/contrib/notebook/browser/notebookBrowser' ;
14
+ import { CellKind , NotebookCellsChangeType , NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon' ;
15
+ import { INotebookExecutionStateService , NotebookExecutionType } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService' ;
16
16
import { OutlineChangeEvent , OutlineConfigKeys , OutlineTarget } from 'vs/workbench/services/outline/browser/outline' ;
17
17
import { OutlineEntry } from './OutlineEntry' ;
18
18
import { IOutlineModelService } from 'vs/editor/contrib/documentSymbols/browser/outlineModel' ;
19
19
import { CancellationToken } from 'vs/base/common/cancellation' ;
20
20
import { NotebookOutlineConstants , NotebookOutlineEntryFactory } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory' ;
21
+ import { Delayer } from 'vs/base/common/async' ;
21
22
22
23
export class NotebookCellOutlineProvider {
23
24
private readonly _disposables = new DisposableStore ( ) ;
@@ -41,7 +42,6 @@ export class NotebookCellOutlineProvider {
41
42
}
42
43
43
44
private readonly _outlineEntryFactory : NotebookOutlineEntryFactory ;
44
-
45
45
constructor (
46
46
private readonly _editor : INotebookEditor ,
47
47
private readonly _target : OutlineTarget ,
@@ -53,29 +53,34 @@ export class NotebookCellOutlineProvider {
53
53
) {
54
54
this . _outlineEntryFactory = new NotebookOutlineEntryFactory ( notebookExecutionStateService ) ;
55
55
56
- const selectionListener = new MutableDisposable ( ) ;
57
- this . _disposables . add ( selectionListener ) ;
58
-
59
- selectionListener . value = combinedDisposable (
60
- Event . debounce < void , void > (
61
- _editor . onDidChangeSelection ,
62
- ( last , _current ) => last ,
63
- 200
64
- ) ( this . _recomputeActive , this ) ,
65
- Event . debounce < INotebookViewCellsUpdateEvent , INotebookViewCellsUpdateEvent > (
66
- _editor . onDidChangeViewCells ,
67
- ( last , _current ) => last ?? _current ,
68
- 200
69
- ) ( this . _recomputeState , this )
56
+ this . _disposables . add ( Event . debounce < void , void > (
57
+ _editor . onDidChangeSelection ,
58
+ ( last , _current ) => last ,
59
+ 200
60
+ ) ( ( ) => {
61
+ this . _recomputeActive ( ) ;
62
+ } , this ) )
63
+ this . _disposables . add ( Event . debounce < INotebookViewCellsUpdateEvent , INotebookViewCellsUpdateEvent > (
64
+ _editor . onDidChangeViewCells ,
65
+ ( last , _current ) => last ?? _current ,
66
+ 200
67
+ ) ( ( ) => {
68
+ this . _recomputeActive ( ) ;
69
+ } , this )
70
70
) ;
71
71
72
+ // .3s of a delay is sufficient, 100-200s is too quick and will unnecessarily block the ui thread.
73
+ // Given we're only updating the outline when the user types, we can afford to wait a bit.
74
+ const delayer = this . _disposables . add ( new Delayer < void > ( 300 ) ) ;
75
+ const delayedRecompute = ( ) => delayer . trigger ( ( ) => this . _recomputeState ( ) ) ;
76
+
72
77
this . _disposables . add ( _configurationService . onDidChangeConfiguration ( e => {
73
78
if ( e . affectsConfiguration ( NotebookSetting . outlineShowMarkdownHeadersOnly ) ||
74
79
e . affectsConfiguration ( NotebookSetting . outlineShowCodeCells ) ||
75
80
e . affectsConfiguration ( NotebookSetting . outlineShowCodeCellSymbols ) ||
76
81
e . affectsConfiguration ( NotebookSetting . breadcrumbsShowCodeCells )
77
82
) {
78
- this . _recomputeState ( ) ;
83
+ delayedRecompute ( ) ;
79
84
}
80
85
} ) ) ;
81
86
@@ -84,17 +89,28 @@ export class NotebookCellOutlineProvider {
84
89
} ) ) ;
85
90
86
91
this . _disposables . add (
87
- Event . debounce < ICellExecutionStateChangedEvent | IExecutionStateChangedEvent > (
88
- notebookExecutionStateService . onDidChangeExecution ,
89
- ( last , _current ) => last ?? _current ,
90
- 200 ) ( e => {
91
- if ( e . type === NotebookExecutionType . cell && ! ! this . _editor . textModel && e . affectsNotebook ( this . _editor . textModel ?. uri ) ) {
92
- this . _recomputeState ( ) ;
93
- }
94
- } )
92
+ notebookExecutionStateService . onDidChangeExecution ( e => {
93
+ if ( e . type === NotebookExecutionType . cell && ! ! this . _editor . textModel && e . affectsNotebook ( this . _editor . textModel ?. uri ) ) {
94
+ delayedRecompute ( ) ;
95
+ }
96
+ } )
95
97
) ;
96
98
97
- this . _recomputeState ( ) ;
99
+ const disposable = this . _disposables . add ( new DisposableStore ( ) ) ;
100
+ const monitorModelChanges = ( ) => {
101
+ disposable . clear ( ) ;
102
+ if ( ! this . _editor . textModel ) {
103
+ return ;
104
+ }
105
+ disposable . add ( this . _editor . textModel . onDidChangeContent ( contentChanges => {
106
+ if ( contentChanges . rawEvents . some ( c => c . kind === NotebookCellsChangeType . ChangeCellContent ) ) {
107
+ delayedRecompute ( ) ;
108
+ }
109
+ } ) ) ;
110
+ }
111
+ this . _disposables . add ( this . _editor . onDidChangeModel ( monitorModelChanges ) ) ;
112
+ monitorModelChanges ( ) ;
113
+ this . _recomputeState ( )
98
114
}
99
115
100
116
dispose ( ) : void {
@@ -104,10 +120,6 @@ export class NotebookCellOutlineProvider {
104
120
this . _disposables . dispose ( ) ;
105
121
}
106
122
107
- init ( ) : void {
108
- this . _recomputeState ( ) ;
109
- }
110
-
111
123
async setFullSymbols ( cancelToken : CancellationToken ) {
112
124
const notebookEditorWidget = this . _editor ;
113
125
@@ -126,7 +138,6 @@ export class NotebookCellOutlineProvider {
126
138
127
139
this . _recomputeState ( ) ;
128
140
}
129
-
130
141
private _recomputeState ( ) : void {
131
142
this . _entriesDisposables . clear ( ) ;
132
143
this . _activeEntry = undefined ;
@@ -159,11 +170,6 @@ export class NotebookCellOutlineProvider {
159
170
const entries : OutlineEntry [ ] = [ ] ;
160
171
for ( const cell of notebookCells ) {
161
172
entries . push ( ...this . _outlineEntryFactory . getOutlineEntries ( cell , this . _target , entries . length ) ) ;
162
- // send an event whenever any of the cells change
163
- this . _entriesDisposables . add ( cell . model . onDidChangeContent ( ( ) => {
164
- this . _recomputeState ( ) ;
165
- this . _onDidChange . fire ( { } ) ;
166
- } ) ) ;
167
173
}
168
174
169
175
// build a tree from the list of entries
0 commit comments