Skip to content

Commit

Permalink
Use new REPL notebook API (#24029)
Browse files Browse the repository at this point in the history
  • Loading branch information
amunger committed Sep 20, 2024
1 parent 63280be commit f9bb1f1
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 52 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 9 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@
"terminalDataWriteEvent",
"terminalExecuteCommandEvent",
"contribIssueReporter",
"notebookVariableProvider",
"codeActionAI"
"codeActionAI",
"notebookReplDocument",
"notebookVariableProvider"
],
"author": {
"name": "Microsoft Corporation"
Expand All @@ -47,7 +48,7 @@
"theme": "dark"
},
"engines": {
"vscode": "^1.94.0-20240913"
"vscode": "^1.94.0-20240918"
},
"enableTelemetry": false,
"keywords": [
Expand Down Expand Up @@ -1168,6 +1169,11 @@
{
"command": "python.execInREPLEnter",
"key": "enter",
"when": "!config.interactiveWindow.executeWithShiftEnter && activeEditor == 'workbench.editor.repl' && !inlineChatFocused"
},
{
"command": "python.execInInteractiveWindowEnter",
"key": "enter",
"when": "!config.interactiveWindow.executeWithShiftEnter && activeEditor == 'workbench.editor.interactive' && !inlineChatFocused"
},
{
Expand Down
1 change: 1 addition & 0 deletions src/client/common/application/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export interface ICommandNameArgumentTypeMapping extends ICommandNameWithoutArgu
[Commands.Start_Native_REPL]: [undefined | Uri];
[Commands.Exec_In_REPL]: [undefined | Uri];
[Commands.Exec_In_REPL_Enter]: [undefined | Uri];
[Commands.Exec_In_IW_Enter]: [undefined | Uri];
[Commands.Exec_In_Terminal]: [undefined, Uri];
[Commands.Exec_In_Terminal_Icon]: [undefined, Uri];
[Commands.Debug_In_Terminal]: [Uri];
Expand Down
1 change: 1 addition & 0 deletions src/client/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export namespace Commands {
export const Exec_In_REPL = 'python.execInREPL';
export const Exec_Selection_In_Django_Shell = 'python.execSelectionInDjangoShell';
export const Exec_In_REPL_Enter = 'python.execInREPLEnter';
export const Exec_In_IW_Enter = 'python.execInInteractiveWindowEnter';
export const Exec_Selection_In_Terminal = 'python.execSelectionInTerminal';
export const GetSelectedInterpreterPath = 'python.interpreterPath';
export const InstallJupyter = 'python.installJupyter';
Expand Down
2 changes: 1 addition & 1 deletion src/client/repl/nativeRepl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export class NativeRepl implements Disposable {
this.replController.updateNotebookAffinity(this.notebookDocument, NotebookControllerAffinity.Default);
await selectNotebookKernel(notebookEditor, this.replController.id, PVSC_EXTENSION_ID);
if (code) {
await executeNotebookCell(this.notebookDocument, code);
await executeNotebookCell(notebookEditor, code);
}
}
}
Expand Down
46 changes: 21 additions & 25 deletions src/client/repl/replCommandHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
workspace,
} from 'vscode';
import { getExistingReplViewColumn } from './replUtils';
import { PVSC_EXTENSION_ID } from '../common/constants';

/**
* Function that opens/show REPL using IW UI.
Expand All @@ -23,29 +24,24 @@ export async function openInteractiveREPL(
notebookController: NotebookController,
notebookDocument: NotebookDocument | undefined,
): Promise<NotebookEditor> {
let notebookEditor: NotebookEditor | undefined;
let viewColumn = ViewColumn.Beside;

// Case where NotebookDocument (REPL document already exists in the tab)
if (notebookDocument) {
const existingReplViewColumn = getExistingReplViewColumn(notebookDocument);
const replViewColumn = existingReplViewColumn ?? ViewColumn.Beside;
notebookEditor = await window.showNotebookDocument(notebookDocument!, { viewColumn: replViewColumn });
viewColumn = existingReplViewColumn ?? viewColumn;
} else if (!notebookDocument) {
// Case where NotebookDocument doesnt exist, open new REPL tab
const interactiveWindowObject = (await commands.executeCommand(
'interactive.open',
{
preserveFocus: true,
viewColumn: ViewColumn.Beside,
},
undefined,
notebookController.id,
'Python REPL',
)) as { notebookEditor: NotebookEditor };
notebookEditor = interactiveWindowObject.notebookEditor;
notebookDocument = interactiveWindowObject.notebookEditor.notebook;
// Case where NotebookDocument doesnt exist, create a blank one.
notebookDocument = await workspace.openNotebookDocument('jupyter-notebook');
}
return notebookEditor!;
const editor = window.showNotebookDocument(notebookDocument!, { viewColumn, asRepl: 'Python REPL' });
await commands.executeCommand('notebook.selectKernel', {
editor,
id: notebookController.id,
extension: PVSC_EXTENSION_ID,
});

return editor;
}

/**
Expand Down Expand Up @@ -73,13 +69,14 @@ export async function selectNotebookKernel(
* @param code
* @return Promise<void>
*/
export async function executeNotebookCell(notebookDocument: NotebookDocument, code: string): Promise<void> {
const { cellCount } = notebookDocument;
await addCellToNotebook(notebookDocument, code);
export async function executeNotebookCell(notebookEditor: NotebookEditor, code: string): Promise<void> {
const { notebook, replOptions } = notebookEditor;
const cellIndex = replOptions?.appendIndex ?? notebook.cellCount;
await addCellToNotebook(notebook, cellIndex, code);
// Execute the cell
commands.executeCommand('notebook.cell.execute', {
ranges: [{ start: cellCount, end: cellCount + 1 }],
document: notebookDocument.uri,
ranges: [{ start: cellIndex, end: cellIndex + 1 }],
document: notebook.uri,
});
}

Expand All @@ -89,11 +86,10 @@ export async function executeNotebookCell(notebookDocument: NotebookDocument, co
* @param code
*
*/
async function addCellToNotebook(notebookDocument: NotebookDocument, code: string): Promise<void> {
async function addCellToNotebook(notebookDocument: NotebookDocument, index: number, code: string): Promise<void> {
const notebookCellData = new NotebookCellData(NotebookCellKind.Code, code as string, 'python');
const { cellCount } = notebookDocument!;
// Add new cell to interactive window document
const notebookEdit = NotebookEdit.insertCells(cellCount, [notebookCellData]);
const notebookEdit = NotebookEdit.insertCells(index, [notebookCellData]);
const workspaceEdit = new WorkspaceEdit();
workspaceEdit.set(notebookDocument!.uri, [notebookEdit]);
await workspace.applyEdit(workspaceEdit);
Expand Down
56 changes: 35 additions & 21 deletions src/client/repl/replCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,29 +98,43 @@ export async function registerReplExecuteOnEnter(
): Promise<void> {
disposables.push(
commandManager.registerCommand(Commands.Exec_In_REPL_Enter, async (uri: Uri) => {
const interpreter = await interpreterService.getActiveInterpreter(uri);
if (!interpreter) {
commands.executeCommand(Commands.TriggerEnvironmentSelection, uri).then(noop, noop);
return;
}
await onInputEnter(uri, 'repl.execute', interpreterService, disposables);
}),
);
disposables.push(
commandManager.registerCommand(Commands.Exec_In_IW_Enter, async (uri: Uri) => {
await onInputEnter(uri, 'interactive.execute', interpreterService, disposables);
}),
);
}

const nativeRepl = await getNativeRepl(interpreter, disposables);
const completeCode = await nativeRepl?.checkUserInputCompleteCode(window.activeTextEditor);
const editor = window.activeTextEditor;
async function onInputEnter(
uri: Uri,
commandName: string,
interpreterService: IInterpreterService,
disposables: Disposable[],
): Promise<void> {
const interpreter = await interpreterService.getActiveInterpreter(uri);
if (!interpreter) {
commands.executeCommand(Commands.TriggerEnvironmentSelection, uri).then(noop, noop);
return;
}

if (editor) {
// Execute right away when complete code and Not multi-line
if (completeCode && !isMultiLineText(editor)) {
await commands.executeCommand('interactive.execute');
} else {
insertNewLineToREPLInput(editor);
const nativeRepl = await getNativeRepl(interpreter, disposables);
const completeCode = await nativeRepl?.checkUserInputCompleteCode(window.activeTextEditor);
const editor = window.activeTextEditor;

// Handle case when user enters on blank line, just trigger interactive.execute
if (editor && editor.document.lineAt(editor.selection.active.line).text === '') {
await commands.executeCommand('interactive.execute');
}
}
if (editor) {
// Execute right away when complete code and Not multi-line
if (completeCode && !isMultiLineText(editor)) {
await commands.executeCommand(commandName);
} else {
insertNewLineToREPLInput(editor);

// Handle case when user enters on blank line, just trigger interactive.execute
if (editor && editor.document.lineAt(editor.selection.active.line).text === '') {
await commands.executeCommand(commandName);
}
}),
);
}
}
}
2 changes: 1 addition & 1 deletion src/client/repl/replController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function createReplController(
const server = createPythonServer([interpreterPath], cwd);
disposables.push(server);

const controller = vscode.notebooks.createNotebookController('pythonREPL', 'interactive', 'Python REPL');
const controller = vscode.notebooks.createNotebookController('pythonREPL', 'jupyter-notebook', 'Python REPL');
controller.supportedLanguages = ['python'];
controller.supportsExecutionOrder = true;

Expand Down
33 changes: 33 additions & 0 deletions types/vscode.proposed.notebookReplDocument.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

declare module 'vscode' {

export interface NotebookDocumentShowOptions {
/**
* The notebook should be opened in a REPL editor,
* where the last cell of the notebook is an input box and the other cells are the read-only history.
* When the value is a string, it will be used as the label for the editor tab.
*/
readonly asRepl?: boolean | string | {
/**
* The label to be used for the editor tab.
*/
readonly label: string;
};
}

export interface NotebookEditor {
/**
* Information about the REPL editor if the notebook was opened as a repl.
*/
replOptions?: {
/**
* The index where new cells should be appended.
*/
appendIndex: number;
};
}
}

0 comments on commit f9bb1f1

Please sign in to comment.