Skip to content

Commit

Permalink
Support Ctrl+A in notebook outputs (#207548)
Browse files Browse the repository at this point in the history
* Support Ctrl+A in notebook outputs

* Better handling of outputs

* oops

* Better determination of outputId
  • Loading branch information
DonJayamanne authored Mar 17, 2024
1 parent aefe7e8 commit 8c7cf6a
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { localize, localize2 } from 'vs/nls';
import { Disposable } from 'vs/base/common/lifecycle';
import { WorkbenchPhase, registerWorkbenchContribution2 } from 'vs/workbench/common/contributions';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { NOTEBOOK_CELL_EDITABLE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys';
import { NOTEBOOK_CELL_EDITABLE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys';
import { cellRangeToViewCells, expandCellRangesWithHiddenCells, getNotebookEditorFromEditorPane, ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/browser/clipboard';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
Expand All @@ -16,7 +16,7 @@ import { CellEditType, ICellEditOperation, ISelectionState, SelectionStateType }
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import * as platform from 'vs/base/common/platform';
import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
import { CellOverflowToolbarGroups, INotebookActionContext, INotebookCellActionContext, NotebookAction, NotebookCellAction, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT } from 'vs/workbench/contrib/notebook/browser/controller/coreActions';
import { CellOverflowToolbarGroups, INotebookActionContext, INotebookCellActionContext, NotebookAction, NotebookCellAction, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT, NOTEBOOK_OUTPUT_WEBVIEW_ACTION_WEIGHT } from 'vs/workbench/contrib/notebook/browser/controller/coreActions';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys';
Expand All @@ -41,7 +41,7 @@ function _log(loggerService: ILogService, str: string) {
}
}

function getFocusedWebviewDelegate(accessor: ServicesAccessor): IWebview | undefined {
function getFocusedEditor(accessor: ServicesAccessor) {
const loggerService = accessor.get(ILogService);
const editorService = accessor.get(IEditorService);
const editor = getNotebookEditorFromEditorPane(editorService.activeEditorPane);
Expand All @@ -65,8 +65,15 @@ function getFocusedWebviewDelegate(accessor: ServicesAccessor): IWebview | undef
return;
}

const webview = editor.getInnerWebview();
_log(loggerService, '[Revive Webview] Notebook editor backlayer webview is focused');
return { editor, loggerService };
}
function getFocusedWebviewDelegate(accessor: ServicesAccessor): IWebview | undefined {
const result = getFocusedEditor(accessor);
if (!result) {
return;
}
const webview = result.editor.getInnerWebview();
_log(result.loggerService, '[Revive Webview] Notebook editor backlayer webview is focused');
return webview;
}

Expand All @@ -79,6 +86,11 @@ function withWebview(accessor: ServicesAccessor, f: (webviewe: IWebview) => void
return false;
}

function withEditor(accessor: ServicesAccessor, f: (editor: INotebookEditor) => boolean) {
const result = getFocusedEditor(accessor);
return result ? f(result.editor) : false;
}

const PRIORITY = 105;

UndoCommand.addImplementation(PRIORITY, 'notebook-webview', accessor => {
Expand All @@ -101,7 +113,6 @@ CutAction?.addImplementation(PRIORITY, 'notebook-webview', accessor => {
return withWebview(accessor, webview => webview.cut());
});


export function runPasteCells(editor: INotebookEditor, activeCell: ICellViewModel | undefined, pasteCells: {
items: NotebookCellTextModel[];
isCopy: boolean;
Expand Down Expand Up @@ -573,3 +584,37 @@ registerAction2(class extends Action2 {
}
}
});


registerAction2(class extends NotebookCellAction {
constructor() {
super(
{
id: 'notebook.cell.output.selectAll',
title: localize('notebook.cell.output.selectAll', "Select All"),
keybinding: {
primary: KeyMod.CtrlCmd | KeyCode.KeyA,
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey), NOTEBOOK_OUTPUT_FOCUSED),
weight: NOTEBOOK_OUTPUT_WEBVIEW_ACTION_WEIGHT
}
});
}

async runWithContext(accessor: ServicesAccessor, _context: INotebookCellActionContext) {
withEditor(accessor, editor => {
if (!editor.hasEditorFocus()) {
return false;
}
if (editor.hasEditorFocus() && !editor.hasWebviewFocus()) {
return true;
}
const cell = editor.getActiveCell();
if (!cell || !cell.outputIsFocused || !editor.hasWebviewFocus()) {
return true;
}
editor.selectOutputContent(cell);
return true;
});

}
});
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const CELL_TITLE_CELL_GROUP_ID = 'inline/cell';
export const CELL_TITLE_OUTPUT_GROUP_ID = 'inline/output';

export const NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT = KeybindingWeight.EditorContrib; // smaller than Suggest Widget, etc
export const NOTEBOOK_OUTPUT_WEBVIEW_ACTION_WEIGHT = KeybindingWeight.WorkbenchContrib + 1; // higher than Workbench contribution (such as Notebook List View), etc

export const enum CellToolbarOrder {
EditCell,
Expand Down
5 changes: 5 additions & 0 deletions src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,11 @@ export interface INotebookEditor {
* Copy the image in the specific cell output to the clipboard
*/
copyOutputImage(cellOutput: ICellOutputViewModel): Promise<void>;
/**
* Select the contents of the first focused output of the cell.
* Implementation of Ctrl+A for an output item.
*/
selectOutputContent(cell: ICellViewModel): void;

readonly onDidReceiveMessage: Event<INotebookWebviewMessage>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1976,6 +1976,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
}
}

selectOutputContent(cell: ICellViewModel) {
this._webview?.selectOutputContents(cell);
}

onWillHide() {
this._isVisible = false;
this._editorFocus.set(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1681,6 +1681,18 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Themable {
this.webview?.focus();
}

selectOutputContents(cell: ICellViewModel) {
if (this._disposed) {
return;
}
const output = cell.outputsViewModels.find(o => o.model.outputId === cell.focusedOutputId);
const outputId = output ? this.insetMapping.get(output)?.outputId : undefined;
this._sendMessageToWebview({
type: 'select-output-contents',
cellOrOutputId: outputId || cell.id
});
}

focusOutput(cellOrOutputId: string, alternateId: string | undefined, viewFocused: boolean) {
if (this._disposed) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,11 @@ export interface IReturnOutputItemMessage {
readonly output: OutputItemEntry | undefined;
}

export interface ISelectOutputItemMessage {
readonly type: 'select-output-contents';
readonly cellOrOutputId: string;
}

export interface ILogRendererDebugMessage extends BaseToWebviewMessage {
readonly type: 'logRendererDebugMessage';
readonly message: string;
Expand Down Expand Up @@ -555,7 +560,8 @@ export type ToWebviewMessage = IClearMessage |
IFindHighlightCurrentMessage |
IFindUnHighlightCurrentMessage |
IFindStopMessage |
IReturnOutputItemMessage;
IReturnOutputItemMessage |
ISelectOutputItemMessage;


export type AnyMessage = FromWebviewMessage | ToWebviewMessage;
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,21 @@ async function webviewPreloads(ctx: PreloadContext) {
postNotebookMessage<webviewMessages.IOutputFocusMessage>('outputFocus', outputFocus);
}
};
const selectOutputContents = (cellOrOutputId: string) => {
const selection = window.getSelection();
if (!selection) {
return;
}
const cellOutputContainer = window.document.getElementById(cellOrOutputId);
if (!cellOutputContainer) {
return;
}
selection.removeAllRanges();
const range = document.createRange();
range.selectNode(cellOutputContainer);
selection.addRange(range);

};

const handleDataUrl = async (data: string | ArrayBuffer | null, downloadName: string) => {
postNotebookMessage<webviewMessages.IClickedDataUrlMessage>('clicked-data-url', {
Expand Down Expand Up @@ -1609,6 +1624,9 @@ async function webviewPreloads(ctx: PreloadContext) {
case 'focus-output':
focusFirstFocusableOrContainerInOutput(event.data.cellOrOutputId, event.data.alternateId);
break;
case 'select-output-contents':
selectOutputContents(event.data.cellOrOutputId);
break;
case 'decorations': {
let outputContainer = window.document.getElementById(event.data.cellId);
if (!outputContainer) {
Expand Down

0 comments on commit 8c7cf6a

Please sign in to comment.