Skip to content

Commit 9b926ce

Browse files
committed
Added missing segments for copy
1 parent 46976d5 commit 9b926ce

File tree

1 file changed

+140
-27
lines changed

1 file changed

+140
-27
lines changed

src/overridecopy.ts

Lines changed: 140 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
JupyterFrontEndPlugin
44
} from '@jupyterlab/application';
55
import { Clipboard } from '@jupyterlab/apputils';
6-
import { INotebookTracker, Notebook, NotebookActions } from '@jupyterlab/notebook';
6+
import { INotebookTracker, Notebook } from '@jupyterlab/notebook';
77
import * as nbformat from '@jupyterlab/nbformat';
88
import { JSONObject } from '@lumino/coreutils';
99

@@ -40,38 +40,153 @@ function selectedCellsWithoutOutputs(notebook: Notebook): nbformat.ICell[] {
4040
}
4141

4242
/**
43-
* Copy or cut the selected cell data to the clipboard without outputs.
44-
*
45-
* This is based on the private `copyOrCut()` function from JupyterLab's
46-
* notebook actions.
47-
*
48-
* @param notebook - The target notebook widget.
49-
* @param cut - True if the cells should be cut, false if they should be copied.
50-
*/
51-
function copyOrCut(notebook: Notebook, cut: boolean): void {
52-
if (!notebook.model || !notebook.activeCell) {
53-
return;
43+
* The interface for a widget state.
44+
*/
45+
export interface IState {
46+
/**
47+
* Whether the widget had focus.
48+
*/
49+
wasFocused: boolean;
50+
51+
/**
52+
* The active cell id before the action.
53+
*
54+
* We cannot rely on the Cell widget or model as it may be
55+
* discarded by action such as move.
56+
*/
57+
activeCellId: string | null;
5458
}
5559

56-
const clipboard = Clipboard.getInstance();
60+
/**
61+
* Get the state of a widget before running an action.
62+
*/
63+
export function getState(notebook: Notebook): IState {
64+
return {
65+
wasFocused: notebook.node.contains(document.activeElement),
66+
activeCellId: notebook.activeCell?.model.id ?? null
67+
};
68+
}
5769

58-
notebook.mode = 'command';
59-
clipboard.clear();
70+
/**
71+
* Handle the state of a widget after running an action.
72+
*/
73+
export async function handleState(
74+
notebook: Notebook,
75+
state: IState,
76+
scrollIfNeeded = false
77+
): Promise<void> {
78+
const { activeCell, activeCellIndex } = notebook;
79+
if (scrollIfNeeded && activeCell) {
80+
await notebook.scrollToItem(activeCellIndex, 'auto', 0).catch(reason => {
81+
// no-op
82+
});
83+
}
84+
if (state.wasFocused || notebook.mode === 'edit') {
85+
notebook.activate();
86+
}
87+
}
6088

61-
// Get selected cells without outputs
62-
const data = selectedCellsWithoutOutputs(notebook);
63-
console.log(data)
89+
/**
90+
* Delete the selected cells.
91+
*
92+
* @param notebook - The target notebook widget.
93+
*
94+
* #### Notes
95+
* The cell after the last selected cell will be activated.
96+
* If the last cell is deleted, then the previous one will be activated.
97+
* It will add a code cell if all cells are deleted.
98+
* This action can be undone.
99+
*/
100+
export function deleteCells(notebook: Notebook): void {
101+
const model = notebook.model!;
102+
const sharedModel = model.sharedModel;
103+
const toDelete: number[] = [];
64104

65-
clipboard.setData(JUPYTER_CELL_MIME, data);
105+
notebook.mode = 'command';
66106

67-
if (cut) {
68-
NotebookActions.deleteCells(notebook);
69-
notebook.lastClipboardInteraction = 'cut';
70-
} else {
107+
// Find the cells to delete.
108+
notebook.widgets.forEach((child, index) => {
109+
const deletable = child.model.getMetadata('deletable') !== false;
110+
111+
if (notebook.isSelectedOrActive(child) && deletable) {
112+
toDelete.push(index);
113+
notebook.model?.deletedCells.push(child.model.id);
114+
}
115+
});
116+
117+
// If cells are not deletable, we may not have anything to delete.
118+
if (toDelete.length > 0) {
119+
// Delete the cells as one undo event.
120+
sharedModel.transact(() => {
121+
// Delete cells in reverse order to maintain the correct indices.
122+
toDelete.reverse().forEach(index => {
123+
sharedModel.deleteCell(index);
124+
});
125+
126+
// Add a new cell if the notebook is empty. This is done
127+
// within the compound operation to make the deletion of
128+
// a notebook's last cell undoable.
129+
if (sharedModel.cells.length == toDelete.length) {
130+
sharedModel.insertCell(0, {
131+
cell_type: notebook.notebookConfig.defaultCell,
132+
metadata:
133+
notebook.notebookConfig.defaultCell === 'code'
134+
? {
135+
// This is an empty cell created in empty notebook, thus is trusted
136+
trusted: true
137+
}
138+
: {}
139+
});
140+
}
141+
});
142+
// Select the *first* interior cell not deleted or the cell
143+
// *after* the last selected cell.
144+
// Note: The activeCellIndex is clamped to the available cells,
145+
// so if the last cell is deleted the previous cell will be activated.
146+
// The *first* index is the index of the last cell in the initial
147+
// toDelete list due to the `reverse` operation above.
148+
notebook.activeCellIndex = toDelete[0] - toDelete.length + 1;
149+
}
150+
151+
// Deselect any remaining, undeletable cells. Do this even if we don't
152+
// delete anything so that users are aware *something* happened.
71153
notebook.deselectAll();
72-
notebook.lastClipboardInteraction = 'copy';
73154
}
74-
}
155+
156+
/**
157+
* Copy or cut the selected cell data to the clipboard without outputs.
158+
*
159+
* @param notebook - The target notebook widget.
160+
*
161+
* @param cut - True if the cells should be cut, false if they should be copied.
162+
*/
163+
export function copyOrCut(notebook: Notebook, cut: boolean): void {
164+
if (!notebook.model || !notebook.activeCell) {
165+
return;
166+
}
167+
168+
const state = getState(notebook);
169+
const clipboard = Clipboard.getInstance();
170+
171+
notebook.mode = 'command';
172+
clipboard.clear();
173+
174+
const data = selectedCellsWithoutOutputs(notebook);
175+
console.log(data)
176+
177+
clipboard.setData(JUPYTER_CELL_MIME, data);
178+
if (cut) {
179+
deleteCells(notebook);
180+
} else {
181+
notebook.deselectAll();
182+
}
183+
if (cut) {
184+
notebook.lastClipboardInteraction = 'cut';
185+
} else {
186+
notebook.lastClipboardInteraction = 'copy';
187+
}
188+
void handleState(notebook, state);
189+
}
75190

76191
/**
77192
* Duplicate selected cells without outputs.
@@ -194,8 +309,6 @@ export const overrideCopyPlugin: JupyterFrontEndPlugin<void> = {
194309
duplicateWithoutOutputs(notebook);
195310
}
196311
});
197-
198-
console.log('Copy/cut/duplicate commands overridden to exclude outputs');
199312
});
200313
}
201314
};

0 commit comments

Comments
 (0)