Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js';
7
import { localize } from '../../../../../nls.js';
8
import { Action2, MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js';
9
import { IClipboardService } from '../../../../../platform/clipboard/common/clipboardService.js';
10
import { IOpenerService } from '../../../../../platform/opener/common/opener.js';
11
import { INotebookOutputActionContext, NOTEBOOK_ACTIONS_CATEGORY } from './coreActions.js';
12
import { NOTEBOOK_CELL_HAS_HIDDEN_OUTPUTS, NOTEBOOK_CELL_HAS_OUTPUTS } from '../../common/notebookContextKeys.js';
13
import * as icons from '../notebookIcons.js';
14
import { ILogService } from '../../../../../platform/log/common/log.js';
15
import { copyCellOutput } from '../viewModel/cellOutputTextHelper.js';
16
import { IEditorService } from '../../../../services/editor/common/editorService.js';
17
import { ICellOutputViewModel, ICellViewModel, INotebookEditor, getNotebookEditorFromEditorPane } from '../notebookBrowser.js';
18
import { CellKind, CellUri } from '../../common/notebookCommon.js';
19
import { CodeCellViewModel } from '../viewModel/codeCellViewModel.js';
20
import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js';
21
import { INotebookEditorModelResolverService } from '../../common/notebookEditorModelResolverService.js';
22
23
export const COPY_OUTPUT_COMMAND_ID = 'notebook.cellOutput.copy';
24
25
registerAction2(class ShowAllOutputsAction extends Action2 {
26
constructor() {
27
super({
28
id: 'notebook.cellOuput.showEmptyOutputs',
29
title: localize('notebookActions.showAllOutput', "Show Empty Outputs"),
30
menu: {
31
id: MenuId.NotebookOutputToolbar,
32
when: ContextKeyExpr.and(NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_HAS_HIDDEN_OUTPUTS)
33
},
34
f1: false,
35
category: NOTEBOOK_ACTIONS_CATEGORY
36
});
37
}
38
39
run(accessor: ServicesAccessor, context: INotebookOutputActionContext): void {
40
const cell = context.cell;
41
if (cell && cell.cellKind === CellKind.Code) {
42
43
for (let i = 1; i < cell.outputsViewModels.length; i++) {
44
if (!cell.outputsViewModels[i].visible.get()) {
45
cell.outputsViewModels[i].setVisible(true, true);
46
(cell as CodeCellViewModel).updateOutputHeight(i, 1, 'command');
47
}
48
}
49
}
50
}
51
});
52
53
registerAction2(class CopyCellOutputAction extends Action2 {
54
constructor() {
55
super({
56
id: COPY_OUTPUT_COMMAND_ID,
57
title: localize('notebookActions.copyOutput', "Copy Cell Output"),
58
menu: {
59
id: MenuId.NotebookOutputToolbar,
60
when: NOTEBOOK_CELL_HAS_OUTPUTS
61
},
62
category: NOTEBOOK_ACTIONS_CATEGORY,
63
icon: icons.copyIcon,
64
});
65
}
66
67
private getNoteboookEditor(editorService: IEditorService, outputContext: INotebookOutputActionContext | { outputViewModel: ICellOutputViewModel } | undefined): INotebookEditor | undefined {
68
if (outputContext && 'notebookEditor' in outputContext) {
69
return outputContext.notebookEditor;
70
}
71
return getNotebookEditorFromEditorPane(editorService.activeEditorPane);
72
}
73
74
async run(accessor: ServicesAccessor, outputContext: INotebookOutputActionContext | { outputViewModel: ICellOutputViewModel } | undefined): Promise<void> {
75
const notebookEditor = this.getNoteboookEditor(accessor.get(IEditorService), outputContext);
76
77
if (!notebookEditor) {
78
return;
79
}
80
81
let outputViewModel: ICellOutputViewModel | undefined;
82
if (outputContext && 'outputId' in outputContext && typeof outputContext.outputId === 'string') {
83
outputViewModel = getOutputViewModelFromId(outputContext.outputId, notebookEditor);
84
} else if (outputContext && 'outputViewModel' in outputContext) {
85
outputViewModel = outputContext.outputViewModel;
86
}
87
88
if (!outputViewModel) {
89
// not able to find the output from the provided context, use the active cell
90
const activeCell = notebookEditor.getActiveCell();
91
if (!activeCell) {
92
return;
93
}
94
95
if (activeCell.focusedOutputId !== undefined) {
96
outputViewModel = activeCell.outputsViewModels.find(output => {
97
return output.model.outputId === activeCell.focusedOutputId;
98
});
99
} else {
100
outputViewModel = activeCell.outputsViewModels.find(output => output.pickedMimeType?.isTrusted);
101
}
102
}
103
104
if (!outputViewModel) {
105
return;
106
}
107
108
const mimeType = outputViewModel.pickedMimeType?.mimeType;
109
110
if (mimeType?.startsWith('image/')) {
111
const focusOptions = { skipReveal: true, outputId: outputViewModel.model.outputId, altOutputId: outputViewModel.model.alternativeOutputId };
112
await notebookEditor.focusNotebookCell(outputViewModel.cellViewModel as ICellViewModel, 'output', focusOptions);
113
notebookEditor.copyOutputImage(outputViewModel);
114
} else {
115
const clipboardService = accessor.get(IClipboardService);
116
const logService = accessor.get(ILogService);
117
118
copyCellOutput(mimeType, outputViewModel, clipboardService, logService);
119
}
120
}
121
122
});
123
124
export function getOutputViewModelFromId(outputId: string, notebookEditor: INotebookEditor): ICellOutputViewModel | undefined {
125
const notebookViewModel = notebookEditor.getViewModel();
126
if (notebookViewModel) {
127
const codeCells = notebookViewModel.viewCells.filter(cell => cell.cellKind === CellKind.Code) as CodeCellViewModel[];
128
for (const cell of codeCells) {
129
const output = cell.outputsViewModels.find(output => output.model.outputId === outputId || output.model.alternativeOutputId === outputId);
130
if (output) {
131
return output;
132
}
133
}
134
}
135
136
return undefined;
137
}
138
139
export const OPEN_OUTPUT_COMMAND_ID = 'notebook.cellOutput.openInTextEditor';
140
141
registerAction2(class OpenCellOutputInEditorAction extends Action2 {
142
constructor() {
143
super({
144
id: OPEN_OUTPUT_COMMAND_ID,
145
title: localize('notebookActions.openOutputInEditor', "Open Cell Output in Text Editor"),
146
f1: false,
147
category: NOTEBOOK_ACTIONS_CATEGORY,
148
icon: icons.copyIcon,
149
});
150
}
151
152
private getNoteboookEditor(editorService: IEditorService, outputContext: INotebookOutputActionContext | { outputViewModel: ICellOutputViewModel } | undefined): INotebookEditor | undefined {
153
if (outputContext && 'notebookEditor' in outputContext) {
154
return outputContext.notebookEditor;
155
}
156
return getNotebookEditorFromEditorPane(editorService.activeEditorPane);
157
}
158
159
async run(accessor: ServicesAccessor, outputContext: INotebookOutputActionContext | { outputViewModel: ICellOutputViewModel } | undefined): Promise<void> {
160
const notebookEditor = this.getNoteboookEditor(accessor.get(IEditorService), outputContext);
161
const notebookModelService = accessor.get(INotebookEditorModelResolverService);
162
163
if (!notebookEditor) {
164
return;
165
}
166
167
let outputViewModel: ICellOutputViewModel | undefined;
168
if (outputContext && 'outputId' in outputContext && typeof outputContext.outputId === 'string') {
169
outputViewModel = getOutputViewModelFromId(outputContext.outputId, notebookEditor);
170
} else if (outputContext && 'outputViewModel' in outputContext) {
171
outputViewModel = outputContext.outputViewModel;
172
}
173
174
const openerService = accessor.get(IOpenerService);
175
176
if (outputViewModel?.model.outputId && notebookEditor.textModel?.uri) {
177
// reserve notebook document reference since the active notebook editor might not be pinned so it can be replaced by the output editor
178
const ref = await notebookModelService.resolve(notebookEditor.textModel.uri);
179
await openerService.open(CellUri.generateCellOutputUriWithId(notebookEditor.textModel.uri, outputViewModel.model.outputId));
180
ref.dispose();
181
}
182
}
183
});
184
185
export const OPEN_OUTPUT_IN_OUTPUT_PREVIEW_COMMAND_ID = 'notebook.cellOutput.openInOutputPreview';
186
187
registerAction2(class OpenCellOutputInNotebookOutputEditorAction extends Action2 {
188
constructor() {
189
super({
190
id: OPEN_OUTPUT_IN_OUTPUT_PREVIEW_COMMAND_ID,
191
title: localize('notebookActions.openOutputInNotebookOutputEditor', "Open in Output Preview"),
192
menu: {
193
id: MenuId.NotebookOutputToolbar,
194
when: ContextKeyExpr.and(NOTEBOOK_CELL_HAS_OUTPUTS, ContextKeyExpr.equals('config.notebook.output.openInPreviewEditor.enabled', true))
195
},
196
f1: false,
197
category: NOTEBOOK_ACTIONS_CATEGORY,
198
});
199
}
200
201
private getNotebookEditor(editorService: IEditorService, outputContext: INotebookOutputActionContext | { outputViewModel: ICellOutputViewModel } | undefined): INotebookEditor | undefined {
202
if (outputContext && 'notebookEditor' in outputContext) {
203
return outputContext.notebookEditor;
204
}
205
return getNotebookEditorFromEditorPane(editorService.activeEditorPane);
206
}
207
208
async run(accessor: ServicesAccessor, outputContext: INotebookOutputActionContext | { outputViewModel: ICellOutputViewModel } | undefined): Promise<void> {
209
const notebookEditor = this.getNotebookEditor(accessor.get(IEditorService), outputContext);
210
if (!notebookEditor) {
211
return;
212
}
213
214
let outputViewModel: ICellOutputViewModel | undefined;
215
if (outputContext && 'outputId' in outputContext && typeof outputContext.outputId === 'string') {
216
outputViewModel = getOutputViewModelFromId(outputContext.outputId, notebookEditor);
217
} else if (outputContext && 'outputViewModel' in outputContext) {
218
outputViewModel = outputContext.outputViewModel;
219
}
220
221
if (!outputViewModel) {
222
return;
223
}
224
225
const genericCellViewModel = outputViewModel.cellViewModel;
226
if (!genericCellViewModel) {
227
return;
228
}
229
230
// get cell index
231
const cellViewModel = notebookEditor.getCellByHandle(genericCellViewModel.handle);
232
if (!cellViewModel) {
233
return;
234
}
235
const cellIndex = notebookEditor.getCellIndex(cellViewModel);
236
if (cellIndex === undefined) {
237
return;
238
}
239
240
// get output index
241
const outputIndex = genericCellViewModel.outputsViewModels.indexOf(outputViewModel);
242
if (outputIndex === -1) {
243
return;
244
}
245
246
if (!notebookEditor.textModel) {
247
return;
248
}
249
250
// craft rich output URI to pass data to the notebook output editor/viewer
251
const outputURI = CellUri.generateOutputEditorUri(
252
notebookEditor.textModel.uri,
253
cellViewModel.id,
254
cellIndex,
255
outputViewModel.model.outputId,
256
outputIndex,
257
);
258
259
const openerService = accessor.get(IOpenerService);
260
openerService.open(outputURI, { openToSide: true });
261
}
262
});
263
264