Skip to content

Commit fdd757e

Browse files
trungleducjtpio
andauthored
Support cell deletion suggestions (#15)
Co-authored-by: Jeremy Tuloup <[email protected]>
1 parent bc16dbd commit fdd757e

File tree

26 files changed

+914
-208
lines changed

26 files changed

+914
-208
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"@types/json-schema": "^7.0.11",
5858
"@types/react": "^18.0.26",
5959
"@types/react-addons-linked-state-mixin": "^0.14.22",
60+
"@types/react-dom": "^18.0.26",
6061
"@typescript-eslint/eslint-plugin": "^6.1.0",
6162
"@typescript-eslint/parser": "^6.1.0",
6263
"css-loader": "^6.7.1",

packages/base/src/baseSuggestionsManager.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {
66
IAllSuggestionData,
77
ISuggestionChange,
88
ISuggestionData,
9-
ISuggestionsManager
9+
ISuggestionsManager,
10+
SuggestionType
1011
} from './types';
1112
import { User } from '@jupyterlab/services';
1213

@@ -56,6 +57,7 @@ export abstract class BaseSuggestionsManager implements ISuggestionsManager {
5657
notebook: NotebookPanel;
5758
cell: Cell<ICellModel>;
5859
author?: User.IIdentity | null;
60+
type: SuggestionType;
5961
}): Promise<string>;
6062

6163
abstract acceptSuggestion(options: {
@@ -77,8 +79,6 @@ export abstract class BaseSuggestionsManager implements ISuggestionsManager {
7779
newSource: string;
7880
}): Promise<void>;
7981

80-
protected _suggestionsMap = new Map<string, IAllSuggestionData>();
81-
8282
protected _notebookAdded(tracker: INotebookTracker, panel: NotebookPanel) {
8383
panel.disposed.connect(p => {
8484
const localPath = p.context.localPath;
@@ -88,6 +88,8 @@ export abstract class BaseSuggestionsManager implements ISuggestionsManager {
8888
});
8989
}
9090

91+
protected _suggestionsMap = new Map<string, IAllSuggestionData>();
92+
9193
protected _suggestionChanged = new Signal<
9294
ISuggestionsManager,
9395
ISuggestionChange

packages/base/src/icons.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import hintStr from '../style/icon/hint.svg';
33
import minimizeStr from '../style/icon/minimize.svg';
44
import expandStr from '../style/icon/expand.svg';
55
import collapseStr from '../style/icon/collapse.svg';
6+
import locationStr from '../style/icon/location.svg';
67

78
export const hintIcon = new LabIcon({
89
name: 'jupyter-suggestions:hintIcon',
@@ -20,3 +21,8 @@ export const collapseIcon = new LabIcon({
2021
name: 'jupyter-suggestions:collapseIcon',
2122
svgstr: collapseStr
2223
});
24+
25+
export const locationIcon = new LabIcon({
26+
name: 'jupyter-suggestions:locationIcon',
27+
svgstr: locationStr
28+
});

packages/base/src/index.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
export * from './types';
2-
export * from './suggestionsPanel';
1+
export * from './baseSuggestionsManager';
32
export * from './icons';
4-
export * from './tokens';
53
export * from './localSuggestionsManager';
6-
export * from './baseSuggestionsManager';
74
export * from './registry';
5+
export * from './suggestionCellMenu';
6+
export * from './suggestionsPanel';
7+
export * from './tokens';
88
export * from './tools';
9+
export * from './types';

packages/base/src/localSuggestionsManager/localSuggestionsManager.ts

Lines changed: 82 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,25 @@
1-
import {
2-
Cell,
3-
CellModel,
4-
CodeCellModel,
5-
ICellModel,
6-
MarkdownCellModel,
7-
RawCellModel
8-
} from '@jupyterlab/cells';
1+
import { Cell, ICellModel } from '@jupyterlab/cells';
92
import { ICell } from '@jupyterlab/nbformat';
103
import { NotebookPanel } from '@jupyterlab/notebook';
4+
import { User } from '@jupyterlab/services';
115
import { UUID } from '@lumino/coreutils';
126

137
import { BaseSuggestionsManager } from '../baseSuggestionsManager';
8+
import { cloneCellModel, deleteCellById } from '../tools';
149
import {
1510
IAllSuggestionData,
1611
IDict,
1712
ISuggestionData,
1813
ISuggestionMetadata,
19-
ISuggestionsManager
14+
ISuggestionsManager,
15+
SuggestionType
2016
} from '../types';
21-
import { User } from '@jupyterlab/services';
2217

2318
export interface ISerializedSuggessionData {
2419
originalCellId: string;
2520
newSource: string;
2621
metadata: ISuggestionMetadata;
22+
type: SuggestionType;
2723
}
2824

2925
const METADATA_KEY = 'jupyter_suggestion';
@@ -96,37 +92,50 @@ export class LocalSuggestionsManager
9692
notebook: NotebookPanel;
9793
cell: Cell<ICellModel>;
9894
author?: User.IIdentity | null;
95+
type: SuggestionType;
9996
}): Promise<string> {
100-
const { notebook, cell, author } = options;
101-
const path = notebook.context.localPath;
102-
if (!this._suggestionsMap.has(path)) {
103-
this._suggestionsMap.set(path, new Map());
104-
}
105-
const currentSuggestions = this._suggestionsMap.get(path)!;
106-
const cellId = cell.model.id;
107-
if (!currentSuggestions.has(cellId)) {
108-
currentSuggestions.set(cellId, {});
109-
}
110-
const cellSuggesions = currentSuggestions.get(cellId)!;
11197
const suggestionId = UUID.uuid4();
112-
const suggestionContent: ISuggestionData = {
113-
originalCellId: cellId,
114-
cellModel: this._cloneCellModel(cell.model),
115-
metadata: { author }
116-
};
117-
cellSuggesions[suggestionId] = suggestionContent;
118-
await this._saveSuggestionToMetadata({
119-
notebook,
120-
cellId,
121-
suggestionId,
122-
suggestionContent
123-
});
124-
this._suggestionChanged.emit({
125-
notebookPath: path,
126-
cellId,
127-
suggestionId,
128-
operator: 'added'
129-
});
98+
switch (options.type) {
99+
case SuggestionType.delete:
100+
case SuggestionType.change: {
101+
const { notebook, cell, author } = options;
102+
const path = notebook.context.localPath;
103+
if (!this._suggestionsMap.has(path)) {
104+
this._suggestionsMap.set(path, new Map());
105+
}
106+
const currentSuggestions = this._suggestionsMap.get(path)!;
107+
const cellId = cell.model.id;
108+
if (!currentSuggestions.has(cellId)) {
109+
currentSuggestions.set(cellId, {});
110+
}
111+
const cellSuggesions = currentSuggestions.get(cellId)!;
112+
113+
const suggestionContent: ISuggestionData = {
114+
originalCellId: cellId,
115+
cellModel: cloneCellModel(cell.model),
116+
metadata: { author },
117+
type: options.type
118+
};
119+
cellSuggesions[suggestionId] = suggestionContent;
120+
await this._saveSuggestionToMetadata({
121+
notebook,
122+
cellId,
123+
suggestionId,
124+
suggestionContent
125+
});
126+
this._suggestionChanged.emit({
127+
notebookPath: path,
128+
cellId,
129+
suggestionId,
130+
operator: 'added'
131+
});
132+
break;
133+
}
134+
135+
default:
136+
break;
137+
}
138+
130139
return suggestionId;
131140
}
132141

@@ -137,20 +146,38 @@ export class LocalSuggestionsManager
137146
}): Promise<boolean> {
138147
const { notebook, cellId, suggestionId } = options;
139148
const notebookPath = notebook.context.localPath;
140-
141149
const currentSuggestion = await this.getSuggestion({
142150
notebookPath,
143151
cellId,
144152
suggestionId
145153
});
146154
if (currentSuggestion && notebook.content.model?.cells) {
147-
const newSource = currentSuggestion.cellModel.sharedModel.getSource();
148-
for (const element of notebook.content.model.cells) {
149-
if (element.id === cellId) {
150-
element.sharedModel.setSource(newSource);
151-
await this.deleteSuggestion(options);
152-
return true;
155+
switch (currentSuggestion.type) {
156+
case SuggestionType.change: {
157+
// In case of a change suggestion. the cell model is always defined
158+
const newSource =
159+
currentSuggestion.cellModel!.sharedModel.getSource();
160+
for (const element of notebook.content.model.cells) {
161+
if (element.id === cellId) {
162+
element.sharedModel.setSource(newSource);
163+
await this.deleteSuggestion(options);
164+
return true;
165+
}
166+
}
167+
break;
168+
}
169+
case SuggestionType.delete: {
170+
const currentNotebook = notebook.content;
171+
const { defaultCell } = currentNotebook.notebookConfig;
172+
const deleted = await deleteCellById({
173+
currentNotebook,
174+
cellId,
175+
defaultCell
176+
});
177+
return deleted;
153178
}
179+
default:
180+
break;
154181
}
155182
}
156183
return false;
@@ -219,12 +246,14 @@ export class LocalSuggestionsManager
219246
suggestionContent: ISuggestionData;
220247
}) {
221248
const { notebook, cellId, suggestionId, suggestionContent } = options;
249+
const { originalCellId, cellModel, metadata, type } = suggestionContent;
222250
const currentSuggestions: IDict<IDict<ISerializedSuggessionData>> =
223251
notebook.context.model.getMetadata(METADATA_KEY) ?? {};
224252
const serializedData: ISerializedSuggessionData = {
225-
originalCellId: suggestionContent.originalCellId,
226-
newSource: suggestionContent.cellModel.sharedModel.getSource(),
227-
metadata: suggestionContent.metadata
253+
originalCellId,
254+
newSource: cellModel?.sharedModel?.getSource() ?? '',
255+
metadata: metadata,
256+
type
228257
};
229258
const newData = {
230259
...currentSuggestions,
@@ -288,50 +317,18 @@ export class LocalSuggestionsManager
288317
await this._saveNotebook(notebook);
289318
}
290319

291-
private _cloneCellModel(
292-
cellModel: ICellModel,
293-
newSource?: string
294-
): ICellModel {
295-
let copiedCellModel: CellModel | undefined;
296-
const mimeType = cellModel.mimeType;
297-
switch (cellModel.type) {
298-
case 'code': {
299-
copiedCellModel = new CodeCellModel();
300-
break;
301-
}
302-
case 'markdown': {
303-
copiedCellModel = new MarkdownCellModel();
304-
break;
305-
}
306-
case 'raw': {
307-
copiedCellModel = new RawCellModel();
308-
break;
309-
}
310-
default:
311-
break;
312-
}
313-
314-
if (!copiedCellModel) {
315-
throw new Error('Invalid cell type');
316-
}
317-
copiedCellModel.mimeType = mimeType;
318-
copiedCellModel.sharedModel.setSource(
319-
newSource ?? cellModel.sharedModel.getSource()
320-
);
321-
return copiedCellModel;
322-
}
323-
324320
private _deserializedSuggestion(
325321
serializedData: ISerializedSuggessionData,
326322
cellMap: IDict<ICellModel>
327323
): ISuggestionData {
328-
const { originalCellId, newSource, metadata } = serializedData;
324+
const { originalCellId, newSource, metadata, type } = serializedData;
329325
const originalCellModel = cellMap[serializedData.originalCellId];
330-
const newCellModel = this._cloneCellModel(originalCellModel, newSource);
326+
const newCellModel = cloneCellModel(originalCellModel, newSource);
331327
return {
332328
originalCellId,
333329
cellModel: newCellModel,
334-
metadata
330+
metadata,
331+
type
335332
};
336333
}
337334
}

0 commit comments

Comments
 (0)