Skip to content

Commit

Permalink
Improve notebook cell model lifecycle (#13675)
Browse files Browse the repository at this point in the history
  • Loading branch information
msujew authored May 6, 2024
1 parent b20a751 commit ef04dc7
Show file tree
Hide file tree
Showing 9 changed files with 40 additions and 28 deletions.
2 changes: 1 addition & 1 deletion packages/monaco/src/browser/monaco-text-model-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export class MonacoTextModelService implements ITextModelService {
* creates a model which is not saved by the model service.
* this will therefore also not be created on backend side.
*/
createUnmangedModel(raw: monaco.Uri | URI): Promise<MonacoEditorModel> {
createUnmanagedModel(raw: monaco.Uri | URI): Promise<MonacoEditorModel> {
return this.loadModel(new URI(raw.toString()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ export class NotebookMonacoTextModelService {
protected readonly monacoTextModelService: MonacoTextModelService;

protected readonly cellmodels = new ReferenceCollection<string, MonacoEditorModel>(
uri => this.monacoTextModelService.createUnmangedModel(new URI(uri))
uri => this.monacoTextModelService.createUnmanagedModel(new URI(uri))
);

getOrCreateNotebookCellModelReference(uri: URI): Promise<Reference<MonacoEditorModel>> {
return this.cellmodels.acquire(uri.toString());
}

async createTextModelsForNotebook(notebook: NotebookModel): Promise<void> {
await Promise.all(notebook.cells.map(cell => this.getOrCreateNotebookCellModelReference(cell.uri)));
await Promise.all(notebook.cells.map(cell => cell.resolveTextModel()));
}

get onDidCreateNotebookCellModel(): Event<MonacoEditorModel> {
Expand Down
2 changes: 1 addition & 1 deletion packages/notebook/src/browser/service/notebook-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export class NotebookService implements Disposable {
this.notebookModels.set(resource.uri.toString(), model);
// Resolve cell text models right after creating the notebook model
// This ensures that all text models are available in the plugin host
this.textModelService.createTextModelsForNotebook(model);
await this.textModelService.createTextModelsForNotebook(model);
this.didAddNotebookDocumentEmitter.fire(model);
model.onDidDispose(() => {
this.notebookModels.delete(resource.uri.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,6 @@ export class NotebookCellModel implements NotebookCell, Disposable {
this.onDidChangeMetadataEmitter.dispose();
this.onDidChangeInternalMetadataEmitter.dispose();
this.onDidChangeLanguageEmitter.dispose();
this.textModel?.dispose();
this.toDispose.dispose();
}

Expand Down Expand Up @@ -356,9 +355,10 @@ export class NotebookCellModel implements NotebookCell, Disposable {

const ref = await this.textModelService.getOrCreateNotebookCellModelReference(this.uri);
this.textModel = ref.object;
this.textModel.onDidChangeContent(e => {
this.toDispose.push(ref);
this.toDispose.push(this.textModel.onDidChangeContent(e => {
this.props.source = e.model.getText();
});
}));
return ref.object;
}

Expand Down
6 changes: 1 addition & 5 deletions packages/notebook/src/browser/view-model/notebook-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ export class NotebookModel implements Saveable, Disposable {

}

protected async replaceCells(start: number, deleteCount: number, newCells: CellData[], computeUndoRedo: boolean): Promise<void> {
protected replaceCells(start: number, deleteCount: number, newCells: CellData[], computeUndoRedo: boolean): void {
const cells = newCells.map(cell => {
const handle = this.nextHandle++;
return this.cellModelFactory({
Expand Down Expand Up @@ -362,10 +362,6 @@ export class NotebookModel implements Saveable, Disposable {
async () => this.replaceCells(start, deleteCount, newCells, false));
}

// Ensure that all text model have been created
// Otherwise we run into a race condition once we fire `onDidChangeContent`
await Promise.all(cells.map(cell => cell.resolveTextModel()));

this.onDidAddOrRemoveCellEmitter.fire({ rawEvent: { kind: NotebookCellsChangeType.ModelChange, changes }, newCellIds: cells.map(cell => cell.handle) });
this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.ModelChange, changes });
if (cells.length > 0) {
Expand Down
2 changes: 1 addition & 1 deletion packages/notebook/src/common/notebook-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ export namespace CellUri {
const s = handle.toString(_radix);
const p = s.length < _lengths.length ? _lengths[s.length - 1] : 'z';

const fragment = `${p}${s}s${Buffer.from(BinaryBuffer.fromString(notebook.scheme).buffer).toString('base64')} `;
const fragment = `${p}${s}s${Buffer.from(BinaryBuffer.fromString(notebook.scheme).buffer).toString('base64')}`;
return notebook.withScheme(cellUriScheme).withFragment(fragment);
}

Expand Down
4 changes: 4 additions & 0 deletions packages/plugin-ext/src/plugin/editors-and-documents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export class EditorsAndDocumentsExtImpl implements EditorsAndDocumentsExt {
private readonly editors = new Map<string, TextEditorExt>();

async $acceptEditorsAndDocumentsDelta(delta: EditorsAndDocumentsDelta): Promise<void> {
this.acceptEditorsAndDocumentsDelta(delta);
}

acceptEditorsAndDocumentsDelta(delta: EditorsAndDocumentsDelta): void {
const removedDocuments = new Array<DocumentDataExt>();
const addedDocuments = new Array<DocumentDataExt>();
const removedEditors = new Array<TextEditorExt>();
Expand Down
16 changes: 13 additions & 3 deletions packages/plugin-ext/src/plugin/notebook/notebook-document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import * as typeConverters from '../type-converters';
import { ModelAddedData, NotebookCellDto, NotebookCellsChangedEventDto, NotebookModelAddedData, NotebookOutputDto } from '../../common';
import { NotebookRange } from '../types-impl';
import { DocumentsExtImpl } from '../documents';
import { UriComponents } from '../../common/uri-components';

class RawContentChangeEvent {

Expand Down Expand Up @@ -345,6 +346,9 @@ export class NotebookDocument implements Disposable {
return;
}

const addedDocuments: ModelAddedData[] = [];
const removedDocuments: UriComponents[] = [];

const contentChangeEvents: RawContentChangeEvent[] = [];

splices.reverse().forEach(splice => {
Expand All @@ -353,9 +357,7 @@ export class NotebookDocument implements Disposable {

const extCell = new Cell(this, this.editorsAndDocuments, cell);
if (!initialization) {
this.editorsAndDocuments.$acceptEditorsAndDocumentsDelta({
addedDocuments: [Cell.asModelAddData(cell)]
});
addedDocuments.push(Cell.asModelAddData(cell));
}
return extCell;
});
Expand All @@ -364,10 +366,18 @@ export class NotebookDocument implements Disposable {
const deletedItems = this.cells.splice(splice.start, splice.deleteCount, ...newCells);
for (const cell of deletedItems) {
changeEvent.deletedItems.push(cell.apiCell);
removedDocuments.push(cell.uri.toComponents());
}
contentChangeEvents.push(changeEvent);
});

if (addedDocuments.length > 0 || removedDocuments.length > 0) {
this.editorsAndDocuments.acceptEditorsAndDocumentsDelta({
addedDocuments,
removedDocuments
});
}

if (bucket) {
for (const changeEvent of contentChangeEvents) {
bucket.push(changeEvent.asApiEvent());
Expand Down
26 changes: 14 additions & 12 deletions packages/plugin-ext/src/plugin/notebook/notebooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { CancellationToken, Disposable, DisposableCollection, Emitter, Event, UR
import { URI as TheiaURI } from '../types-impl';
import * as theia from '@theia/plugin';
import {
CommandRegistryExt, ModelAddedData, NotebookCellStatusBarListDto, NotebookDataDto,
CommandRegistryExt, NotebookCellStatusBarListDto, NotebookDataDto,
NotebookDocumentsAndEditorsDelta, NotebookDocumentShowOptions, NotebookDocumentsMain, NotebookEditorAddData, NotebookEditorsMain, NotebooksExt, NotebooksMain, Plugin,
PLUGIN_RPC_CONTEXT
} from '../../common';
Expand Down Expand Up @@ -205,7 +205,6 @@ export class NotebooksExtImpl implements NotebooksExt {

async $acceptDocumentsAndEditorsDelta(delta: NotebookDocumentsAndEditorsDelta): Promise<void> {
const removedCellDocuments: UriComponents[] = [];
const addedCellDocuments: ModelAddedData[] = [];
if (delta.removedDocuments) {
for (const uri of delta.removedDocuments) {
const revivedUri = URI.fromComponents(uri);
Expand All @@ -226,10 +225,12 @@ export class NotebooksExtImpl implements NotebooksExt {
}
}

// publish all removed cell documents first
await this.textDocumentsAndEditors.$acceptEditorsAndDocumentsDelta({
removedDocuments: removedCellDocuments
});
if (removedCellDocuments.length > 0) {
// publish all removed cell documents first
this.textDocumentsAndEditors.acceptEditorsAndDocumentsDelta({
removedDocuments: removedCellDocuments
});
}

if (delta.addedDocuments) {
for (const modelData of delta.addedDocuments) {
Expand All @@ -250,17 +251,18 @@ export class NotebooksExtImpl implements NotebooksExt {
this.documents.get(uri.toString())?.dispose();
this.documents.set(uri.toString(), document);

addedCellDocuments.push(...modelData.cells.map(cell => Cell.asModelAddData(cell)));
if (modelData.cells.length > 0) {
// Publish new cell documents before calling the notebook document open event
// During this event, extensions might request the cell document and we want to make sure it is available
this.textDocumentsAndEditors.acceptEditorsAndDocumentsDelta({
addedDocuments: modelData.cells.map(cell => Cell.asModelAddData(cell))
});
}

this.onDidOpenNotebookDocumentEmitter.fire(document.apiNotebook);
}
}

// publish all added cell documents in a separate call
await this.textDocumentsAndEditors.$acceptEditorsAndDocumentsDelta({
addedDocuments: addedCellDocuments
});

if (delta.addedEditors) {
for (const editorModelData of delta.addedEditors) {
if (this.editors.has(editorModelData.id)) {
Expand Down

0 comments on commit ef04dc7

Please sign in to comment.