@@ -13,6 +13,10 @@ import { ICommandContext } from '../../command_manager';
13
13
import { CodeEditor } from '@jupyterlab/codeeditor' ;
14
14
import IEditor = CodeEditor . IEditor ;
15
15
import { VirtualDocument } from '../../virtual/document' ;
16
+ import {
17
+ IObservableUndoableList ,
18
+ IObservableList
19
+ } from '@jupyterlab/observables' ;
16
20
17
21
export class NotebookAdapter extends WidgetAdapter < NotebookPanel > {
18
22
editor : Notebook ;
@@ -125,7 +129,6 @@ export class NotebookAdapter extends WidgetAdapter<NotebookPanel> {
125
129
this . console . log ( this . document_path , 'ready for connection' ) ;
126
130
127
131
this . init_virtual ( ) ;
128
- this . connect_contentChanged_signal ( ) ;
129
132
130
133
// connect the document, but do not open it as the adapter will handle this
131
134
// after registering all features
@@ -139,83 +142,98 @@ export class NotebookAdapter extends WidgetAdapter<NotebookPanel> {
139
142
) ;
140
143
141
144
this . widget . content . activeCellChanged . connect ( this . activeCellChanged , this ) ;
142
- this . widget . model . cells . changed . connect ( async ( cells , change ) => {
143
- let cellsAdded : ICellModel [ ] = [ ] ;
144
- let cellsRemoved : ICellModel [ ] = [ ] ;
145
- const type = this . type ;
146
-
147
- if ( change . type === 'set' ) {
148
- // handling of conversions is important, because the editors get re-used and their handlers inherited,
149
- // so we need to clear our handlers from editors of e.g. markdown cells which previously were code cells.
150
- let convertedToMarkdownOrRaw = [ ] ;
151
- let convertedToCode = [ ] ;
152
-
153
- if ( change . newValues . length === change . oldValues . length ) {
154
- // during conversion the cells should not get deleted nor added
155
- for ( let i = 0 ; i < change . newValues . length ; i ++ ) {
156
- if (
157
- change . oldValues [ i ] . type === type &&
158
- change . newValues [ i ] . type !== type
159
- ) {
160
- convertedToMarkdownOrRaw . push ( change . newValues [ i ] ) ;
161
- } else if (
162
- change . oldValues [ i ] . type !== type &&
163
- change . newValues [ i ] . type === type
164
- ) {
165
- convertedToCode . push ( change . newValues [ i ] ) ;
166
- }
145
+ this . widget . model . cells . changed . connect ( this . handle_cell_change , this ) ;
146
+ this . editor . modelChanged . connect ( notebook => {
147
+ // note: this should not usually happen;
148
+ // there is no default action that would trigger this,
149
+ // its just a failsafe in case if another extension decides
150
+ // to swap the notebook model
151
+ this . console . warn (
152
+ 'Model changed, connecting cell change handler; this is not something we were expecting'
153
+ ) ;
154
+ notebook . model . cells . changed . connect ( this . handle_cell_change , this ) ;
155
+ } ) ;
156
+ }
157
+
158
+ async handle_cell_change (
159
+ cells : IObservableUndoableList < ICellModel > ,
160
+ change : IObservableList . IChangedArgs < ICellModel >
161
+ ) {
162
+ let cellsAdded : ICellModel [ ] = [ ] ;
163
+ let cellsRemoved : ICellModel [ ] = [ ] ;
164
+ const type = this . type ;
165
+
166
+ if ( change . type === 'set' ) {
167
+ // handling of conversions is important, because the editors get re-used and their handlers inherited,
168
+ // so we need to clear our handlers from editors of e.g. markdown cells which previously were code cells.
169
+ let convertedToMarkdownOrRaw = [ ] ;
170
+ let convertedToCode = [ ] ;
171
+
172
+ if ( change . newValues . length === change . oldValues . length ) {
173
+ // during conversion the cells should not get deleted nor added
174
+ for ( let i = 0 ; i < change . newValues . length ; i ++ ) {
175
+ if (
176
+ change . oldValues [ i ] . type === type &&
177
+ change . newValues [ i ] . type !== type
178
+ ) {
179
+ convertedToMarkdownOrRaw . push ( change . newValues [ i ] ) ;
180
+ } else if (
181
+ change . oldValues [ i ] . type !== type &&
182
+ change . newValues [ i ] . type === type
183
+ ) {
184
+ convertedToCode . push ( change . newValues [ i ] ) ;
167
185
}
168
- cellsAdded = convertedToCode ;
169
- cellsRemoved = convertedToMarkdownOrRaw ;
170
186
}
171
- } else if ( change . type == 'add' ) {
172
- cellsAdded = change . newValues . filter (
173
- cellModel => cellModel . type === type
174
- ) ;
175
- }
176
- // note: editorRemoved is not emitted for removal of cells by change of type 'remove' (but only during cell type conversion)
177
- // because there is no easy way to get the widget associated with the removed cell(s) - because it is no
178
- // longer in the notebook widget list! It would need to be tracked on our side, but it is not necessary
179
- // as (except for a tiny memory leak) it should not impact the functionality in any way
180
-
181
- if (
182
- cellsRemoved . length ||
183
- cellsAdded . length ||
184
- change . type === 'move' ||
185
- change . type === 'remove'
186
- ) {
187
- // in contrast to the file editor document which can be only changed by the modification of the editor content,
188
- // the notebook document cna also get modified by a change in the number or arrangement of editors themselves;
189
- // for this reason each change has to trigger documents update (so that LSP mirror is in sync).
190
- await this . update_documents ( ) ;
187
+ cellsAdded = convertedToCode ;
188
+ cellsRemoved = convertedToMarkdownOrRaw ;
191
189
}
190
+ } else if ( change . type == 'add' ) {
191
+ cellsAdded = change . newValues . filter (
192
+ cellModel => cellModel . type === type
193
+ ) ;
194
+ }
195
+ // note: editorRemoved is not emitted for removal of cells by change of type 'remove' (but only during cell type conversion)
196
+ // because there is no easy way to get the widget associated with the removed cell(s) - because it is no
197
+ // longer in the notebook widget list! It would need to be tracked on our side, but it is not necessary
198
+ // as (except for a tiny memory leak) it should not impact the functionality in any way
199
+
200
+ if (
201
+ cellsRemoved . length ||
202
+ cellsAdded . length ||
203
+ change . type === 'move' ||
204
+ change . type === 'remove'
205
+ ) {
206
+ // in contrast to the file editor document which can be only changed by the modification of the editor content,
207
+ // the notebook document cna also get modified by a change in the number or arrangement of editors themselves;
208
+ // for this reason each change has to trigger documents update (so that LSP mirror is in sync).
209
+ await this . update_documents ( ) ;
210
+ }
192
211
193
- for ( let cellModel of cellsRemoved ) {
194
- let cellWidget = this . widget . content . widgets . find (
195
- cell => cell . model . id === cellModel . id
196
- ) ;
197
- this . known_editors_ids . delete ( cellWidget . editor . uuid ) ;
198
-
199
- // for practical purposes this editor got removed from our consideration;
200
- // it might seem that we should instead look for the editor indicated by
201
- // the oldValues[i] cellModel, but this one got already transferred to the
202
- // markdown cell in newValues[i]
203
- this . editorRemoved . emit ( {
204
- editor : cellWidget . editor
205
- } ) ;
206
- }
212
+ for ( let cellModel of cellsRemoved ) {
213
+ let cellWidget = this . widget . content . widgets . find (
214
+ cell => cell . model . id === cellModel . id
215
+ ) ;
216
+ this . known_editors_ids . delete ( cellWidget . editor . uuid ) ;
217
+
218
+ // for practical purposes this editor got removed from our consideration;
219
+ // it might seem that we should instead look for the editor indicated by
220
+ // the oldValues[i] cellModel, but this one got already transferred to the
221
+ // markdown cell in newValues[i]
222
+ this . editorRemoved . emit ( {
223
+ editor : cellWidget . editor
224
+ } ) ;
225
+ }
207
226
208
- for ( let cellModel of cellsAdded ) {
209
- let cellWidget = this . widget . content . widgets . find (
210
- cell => cell . model . id === cellModel . id
211
- ) ;
212
- this . known_editors_ids . add ( cellWidget . editor . uuid ) ;
227
+ for ( let cellModel of cellsAdded ) {
228
+ let cellWidget = this . widget . content . widgets . find (
229
+ cell => cell . model . id === cellModel . id
230
+ ) ;
231
+ this . known_editors_ids . add ( cellWidget . editor . uuid ) ;
213
232
214
- this . editorAdded . emit ( {
215
- editor : cellWidget . editor
216
- } ) ;
217
- }
218
- } ) ;
233
+ this . editorAdded . emit ( {
234
+ editor : cellWidget . editor
235
+ } ) ;
236
+ }
219
237
}
220
238
221
239
get editors ( ) : CodeEditor . IEditor [ ] {
0 commit comments