Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
trungleduc committed Dec 12, 2024
1 parent efbb169 commit 8e48edc
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 218 deletions.
1 change: 1 addition & 0 deletions packages/base/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './tokens';
export * from './localSuggestionsManager';
export * from './baseSuggestionsManager';
export * from './registry';
export * from './tools';
133 changes: 38 additions & 95 deletions packages/base/src/suggestionsPanel/model.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { CellList, Notebook, NotebookPanel } from '@jupyterlab/notebook';
import { ISharedNotebook, NotebookChange } from '@jupyter/ydoc';
import { Cell, ICellModel } from '@jupyterlab/cells';
import { Notebook, NotebookPanel } from '@jupyterlab/notebook';
import { User } from '@jupyterlab/services';
import { PromiseDelegate } from '@lumino/coreutils';
import { ISignal, Signal } from '@lumino/signaling';

import {
IAllSuggestionViewData,
IAllSuggestionData,
IAllSuggestionViewData,
IDict,
ISuggestionChange,
ISuggestionViewData,
ISuggestionData,
ISuggestionsManager,
ISuggestionsModel
ISuggestionsModel,
ISuggestionViewData
} from '../types';
import { ISignal, Signal } from '@lumino/signaling';
import { Cell, ICellModel } from '@jupyterlab/cells';
import { IObservableList } from '@jupyterlab/observables';
import { User } from '@jupyterlab/services';
import { ISharedCell, ISharedNotebook, NotebookChange } from '@jupyter/ydoc';
import { PromiseDelegate } from '@lumino/coreutils';
import { detectCellChangedEvent, getCellMap } from '../tools';

export class SuggestionsModel implements ISuggestionsModel {
constructor(options: SuggestionsModel.IOptions) {
this._userManager = options.userManager;
Expand Down Expand Up @@ -189,6 +191,7 @@ export class SuggestionsModel implements ISuggestionsModel {
} else {
this._allSuggestions = undefined;
}
this._promiseQueue = {};
this._allSuggestionsChanged.emit();
}
async switchManager(manager: ISuggestionsManager | undefined): Promise<void> {
Expand All @@ -210,6 +213,7 @@ export class SuggestionsModel implements ISuggestionsModel {
this._allSuggestions = this._convertAllSuggestionsFromManager(
allSuggestionsFromManager
);
this._promiseQueue = {};
this._allSuggestionsChanged.emit();
}
}
Expand Down Expand Up @@ -263,10 +267,6 @@ export class SuggestionsModel implements ISuggestionsModel {
this
);

this._notebookPanel.content.model?.cells.changed.connect(
this._handleCellListChanged,
this
);
this._notebookPanel.content.model?.sharedModel.changed.connect(
this._handleNotebookChanged,
this
Expand All @@ -280,9 +280,6 @@ export class SuggestionsModel implements ISuggestionsModel {
this._notebookPanel.content.activeCellChanged.disconnect(
this._handleActiveCellChanged
);
this._notebookPanel.content.model?.cells.changed.disconnect(
this._handleCellListChanged
);
}
private _handleActiveCellChanged(
nb: Notebook,
Expand All @@ -294,25 +291,12 @@ export class SuggestionsModel implements ISuggestionsModel {
_: ISharedNotebook,
changed: NotebookChange
) {
const { cellsChange } = changed;
if (cellsChange) {
let haveDelete = false;
let haveInsert: ISharedCell[] | undefined;
for (const c of cellsChange) {
if (c.delete !== undefined) {
haveDelete = true;
}
if (c.insert !== undefined) {
haveInsert = c.insert;
}
}
const cellMap: IDict<ICellModel> = {};
[...(this._notebookPanel?.model?.cells ?? [])].forEach(it => {
cellMap[it.id] = it;
});
const cellChangedEvent = detectCellChangedEvent(changed);
if (cellChangedEvent) {
const { event } = cellChangedEvent;
const cellMap = getCellMap(this._notebookPanel);

if (!haveInsert && haveDelete) {
// Cell deleted
if (event === 'deleted') {
const cellInNotebook = new Set(Object.keys(cellMap));
const cellInModel = [...(this._allSuggestions?.keys() ?? [])];
const removedElements = cellInModel.filter(
Expand All @@ -325,20 +309,23 @@ export class SuggestionsModel implements ISuggestionsModel {
}
}
}
if (haveInsert && haveDelete) {
// Cell moved
const movedCells = haveInsert;
if (event === 'moved') {
const movedCells = cellChangedEvent.movedCells ?? [];
// Only emit rerender signal if the moved cell has suggestions
let needEmit = false;
for (const cell of movedCells) {
const cellId = cell.id;
if (!this._queue[cellId]) {
this._queue[cellId] = new PromiseDelegate<void>();
if (!this._promiseQueue[cellId]) {
this._promiseQueue[cellId] = new PromiseDelegate<void>();
}
const pd = this._queue[cellId];
const pd = this._promiseQueue[cellId];
if (this._allSuggestions?.has(cellId)) {
const cellSuggestions = this._allSuggestions.get(cellId)!;
const allSuggestions = Object.entries(cellSuggestions);
const originalCell = cellMap[cellId];
// In case of a stanalone cell shared model, the `cellModel`
// of the associated suggestion data needs to be updated
// by the parent document, so we need to wait for it.
let shouldWait = false;
if (originalCell && allSuggestions.length > 0) {
needEmit = true;
Expand All @@ -352,63 +339,15 @@ export class SuggestionsModel implements ISuggestionsModel {
}
}

delete this._queue[cellId];
delete this._promiseQueue[cellId];
}
if (needEmit) {
this._allSuggestionsChanged.emit();
}
}
}
}
private async _handleCellListChanged(
cellList: CellList,
changed: IObservableList.IChangedArgs<ICellModel>
) {
// console.log('changed', changed);
// switch (changed.type) {
// case 'remove': {
// const cellInNotebook = new Set([...cellList].map(it => it.id));
// const cellInModel = [...(this._allSuggestions?.keys() ?? [])];
// console.log('cellInNotebook', cellInNotebook);
// console.log('cellInModel', cellInModel);
// const removedElements = cellInModel.filter(
// el => !cellInNotebook.has(el)
// );
// for (const removed of removedElements) {
// const suggestions = this._allSuggestions?.get(removed) ?? {};
// for (const suggestionId in suggestions) {
// await this.deleteSuggestion({ cellId: removed, suggestionId });
// }
// }
// break;
// }
// case 'add': {
// const newCells = changed.newValues;
// let needEmit = false;
// for (const cell of newCells) {
// const id = cell.id;
// if (this._allSuggestions?.has(id)) {
// const cellSuggestions = this._allSuggestions.get(id)!;
// const allSuggestions = Object.values(cellSuggestions);
// needEmit = allSuggestions.length > 0;
// allSuggestions.forEach(it => {
// console.log(
// 'updating original source',
// cell.sharedModel.getSource()
// );
// it.originalCellModel = cell;
// });
// }
// }
// if (needEmit) {
// this._allSuggestionsChanged.emit();
// }
// break;
// }
// default:
// break;
// }
}

private _handleSuggestionChanged(
manager: ISuggestionsManager,
changed: ISuggestionChange
Expand All @@ -429,10 +368,10 @@ export class SuggestionsModel implements ISuggestionsModel {
}
}
}
if (!this._queue[cellId]) {
this._queue[cellId] = new PromiseDelegate<void>();
if (!this._promiseQueue[cellId]) {
this._promiseQueue[cellId] = new PromiseDelegate<void>();
}
const pd = this._queue[cellId];
const pd = this._promiseQueue[cellId];
pd.resolve();
} else {
this._suggestionChanged.emit(newChanged);
Expand All @@ -456,7 +395,11 @@ export class SuggestionsModel implements ISuggestionsModel {
{ cellId?: string }
>(this);

private _queue: IDict<PromiseDelegate<void>> = {};
/**
* Queue of promises used to wait for the update of the cell shared
* model in case of drag-and-drop cell.
*/
private _promiseQueue: IDict<PromiseDelegate<void>> = {};
}

export namespace SuggestionsModel {
Expand Down
16 changes: 0 additions & 16 deletions packages/base/src/suggestionsPanel/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,20 +84,6 @@ export class SuggestionsWidget extends PanelWithToolbar {
}
case 'modified': {
break;
// const suggestion = await this._model.getSuggestion({
// cellId,
// suggestionId
// });
// const cellSuggestionsPanel = this._cellSuggestionsPanel.get(cellId);
// if (!cellSuggestionsPanel) {
// break;
// }
// const allWidgets = [...cellSuggestionsPanel.widgets] as CellWidget[];
// for (const element of allWidgets) {
// if (element.id === suggestionId) {
// break;
// }
// }
}

default:
Expand Down Expand Up @@ -164,8 +150,6 @@ export class SuggestionsWidget extends PanelWithToolbar {
suggestionId,
suggestionData
});


cellSuggestionPanel.addWidget(widget);
});
}
Expand Down
109 changes: 109 additions & 0 deletions packages/base/src/tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { ISharedCell, NotebookChange, YCellType } from '@jupyter/ydoc';
import {
CellModel,
CodeCellModel,
ICellModel,
MarkdownCellModel,
RawCellModel
} from '@jupyterlab/cells';
import { NotebookPanel } from '@jupyterlab/notebook';

import { IDict } from './types';

/**
* Generates a mapping of cell IDs to cell models for a given notebook.
*
* @param {NotebookPanel | null} nb - The notebook panel from which to
* extract cell models.
* @returns {IDict<ICellModel>} - A dictionary mapping cell IDs to their
* respective cell models.
*/
export function getCellMap(nb: NotebookPanel | null): IDict<ICellModel> {
const cellMap: IDict<ICellModel> = {};
[...(nb?.model?.cells ?? [])].forEach(it => {
cellMap[it.id] = it;
});
return cellMap;
}

/**
* Detects changes to cells in a notebook and classifies the type of change.
*
* @param {NotebookChange} changed - The change event to analyze.
* @returns {Object | undefined} - An object describing the type of event:
* - If cells were deleted: `{ event: 'deleted' }`
* - If cells were moved: `{ event: 'moved', movedCells: ISharedCell[] }`
* - Otherwise, returns `undefined`.
*/
export function detectCellChangedEvent(changed: NotebookChange):
| {
event: 'deleted' | 'moved';
movedCells?: ISharedCell[];
}
| undefined {
const { cellsChange } = changed;

if (cellsChange) {
let haveDelete = false;
let haveInsert: ISharedCell[] | undefined;
for (const c of cellsChange) {
if (c.delete !== undefined) {
haveDelete = true;
}
if (c.insert !== undefined) {
haveInsert = c.insert;
}
}
if (!haveInsert && haveDelete) {
return { event: 'deleted' };
}
if (haveInsert && haveDelete) {
return { event: 'moved', movedCells: haveInsert };
}
}
return;
}

/**
* Creates a cell model from a YCellType instance based on its cell type.
*
* @param {Object} options - The options for creating the cell model.
* @param {YCellType} options.yCell - The YCellType instance representing
* the shared cell model.
* @param {string} options.mimeType - The MIME type to assign to the cell
* model (used for code cells).
* @returns {CellModel | undefined} - The created cell model (CodeCellModel,
* MarkdownCellModel, or RawCellModel) or `undefined` if the cell type
* is unsupported.
*/
export function cellModelFromYCell(options: {
yCell: YCellType;
mimeType: string;
}): CellModel | undefined {
let copiedCellModel: CellModel | undefined;
const { yCell, mimeType } = options;
switch (yCell.cell_type) {
case 'code': {
copiedCellModel = new CodeCellModel({
sharedModel: yCell
});
copiedCellModel.mimeType = mimeType;
break;
}
case 'markdown': {
copiedCellModel = new MarkdownCellModel({
sharedModel: yCell
});
break;
}
case 'raw': {
copiedCellModel = new RawCellModel({
sharedModel: yCell
});
break;
}
default:
break;
}
return copiedCellModel;
}
5 changes: 5 additions & 0 deletions packages/base/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ export interface ISuggestionsModel extends IDisposable {
*/
getCellIndex(cellId?: string): number;

/**
* Retrieves the current active cell
*
* @returns The cell instance, or -1 if the cell is not found.
*/
getActiveCell(): Cell<ICellModel> | null | undefined;
}

Expand Down
Loading

0 comments on commit 8e48edc

Please sign in to comment.