forked from microsoft/vscode-python
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor Native REPL code (microsoft#23550)
Refactor Native REPL code. Resolves: microsoft#23632 --------- Co-authored-by: Aaron Munger <[email protected]>
- Loading branch information
1 parent
6af3d03
commit 9abfd30
Showing
4 changed files
with
362 additions
and
162 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// Native Repl class that holds instance of pythonServer and replController | ||
|
||
import { NotebookController, NotebookControllerAffinity, NotebookDocument, TextEditor, workspace } from 'vscode'; | ||
import { Disposable } from 'vscode-jsonrpc'; | ||
import { PVSC_EXTENSION_ID } from '../common/constants'; | ||
import { PythonEnvironment } from '../pythonEnvironments/info'; | ||
import { createPythonServer, PythonServer } from './pythonServer'; | ||
import { executeNotebookCell, openInteractiveREPL, selectNotebookKernel } from './replCommandHandler'; | ||
import { createReplController } from './replController'; | ||
|
||
export class NativeRepl implements Disposable { | ||
private pythonServer: PythonServer; | ||
|
||
private interpreter: PythonEnvironment; | ||
|
||
private disposables: Disposable[] = []; | ||
|
||
private replController: NotebookController; | ||
|
||
private notebookDocument: NotebookDocument | undefined; | ||
|
||
// TODO: In the future, could also have attribute of URI for file specific REPL. | ||
constructor(interpreter: PythonEnvironment) { | ||
this.interpreter = interpreter; | ||
|
||
this.pythonServer = createPythonServer([interpreter.path as string]); | ||
this.replController = this.setReplController(); | ||
|
||
this.watchNotebookClosed(); | ||
} | ||
|
||
dispose(): void { | ||
this.disposables.forEach((d) => d.dispose()); | ||
} | ||
|
||
/** | ||
* Function that watches for Notebook Closed event. | ||
* This is for the purposes of correctly updating the notebookEditor and notebookDocument on close. | ||
*/ | ||
private watchNotebookClosed(): void { | ||
this.disposables.push( | ||
workspace.onDidCloseNotebookDocument((nb) => { | ||
if (this.notebookDocument && nb.uri.toString() === this.notebookDocument.uri.toString()) { | ||
this.notebookDocument = undefined; | ||
} | ||
}), | ||
); | ||
} | ||
|
||
/** | ||
* Function that check if NotebookController for REPL exists, and returns it in Singleton manner. | ||
* @returns NotebookController | ||
*/ | ||
public setReplController(): NotebookController { | ||
if (!this.replController) { | ||
return createReplController(this.interpreter.path, this.disposables); | ||
} | ||
return this.replController; | ||
} | ||
|
||
/** | ||
* Function that checks if native REPL's text input box contains complete code. | ||
* @param activeEditor | ||
* @param pythonServer | ||
* @returns Promise<boolean> - True if complete/Valid code is present, False otherwise. | ||
*/ | ||
public async checkUserInputCompleteCode(activeEditor: TextEditor | undefined): Promise<boolean> { | ||
let completeCode = false; | ||
let userTextInput; | ||
if (activeEditor) { | ||
const { document } = activeEditor; | ||
userTextInput = document.getText(); | ||
} | ||
|
||
// Check if userTextInput is a complete Python command | ||
if (userTextInput) { | ||
completeCode = await this.pythonServer.checkValidCommand(userTextInput); | ||
} | ||
|
||
return completeCode; | ||
} | ||
|
||
/** | ||
* Function that opens interactive repl, selects kernel, and send/execute code to the native repl. | ||
* @param code | ||
*/ | ||
public async sendToNativeRepl(code: string): Promise<void> { | ||
const notebookEditor = await openInteractiveREPL(this.replController, this.notebookDocument); | ||
this.notebookDocument = notebookEditor.notebook; | ||
|
||
if (this.notebookDocument) { | ||
this.replController.updateNotebookAffinity(this.notebookDocument, NotebookControllerAffinity.Default); | ||
await selectNotebookKernel(notebookEditor, this.replController.id, PVSC_EXTENSION_ID); | ||
await executeNotebookCell(this.notebookDocument, code); | ||
} | ||
} | ||
} | ||
|
||
let nativeRepl: NativeRepl | undefined; // In multi REPL scenario, hashmap of URI to Repl. | ||
|
||
/** | ||
* Get Singleton Native REPL Instance | ||
* @param interpreter | ||
* @returns Native REPL instance | ||
*/ | ||
export function getNativeRepl(interpreter: PythonEnvironment, disposables: Disposable[]): NativeRepl { | ||
if (!nativeRepl) { | ||
nativeRepl = new NativeRepl(interpreter); | ||
disposables.push(nativeRepl); | ||
} | ||
return nativeRepl; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { | ||
commands, | ||
window, | ||
NotebookController, | ||
NotebookEditor, | ||
ViewColumn, | ||
NotebookDocument, | ||
NotebookCellData, | ||
NotebookCellKind, | ||
NotebookEdit, | ||
WorkspaceEdit, | ||
workspace, | ||
} from 'vscode'; | ||
import { getExistingReplViewColumn } from './replUtils'; | ||
|
||
/** | ||
* Function that opens/show REPL using IW UI. | ||
* @param notebookController | ||
* @param notebookEditor | ||
* @returns notebookEditor | ||
*/ | ||
export async function openInteractiveREPL( | ||
notebookController: NotebookController, | ||
notebookDocument: NotebookDocument | undefined, | ||
): Promise<NotebookEditor> { | ||
let notebookEditor: NotebookEditor | undefined; | ||
|
||
// 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 }); | ||
} 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; | ||
} | ||
return notebookEditor!; | ||
} | ||
|
||
/** | ||
* Function that selects notebook Kernel. | ||
* @param notebookEditor | ||
* @param notebookControllerId | ||
* @param extensionId | ||
* @return Promise<void> | ||
*/ | ||
export async function selectNotebookKernel( | ||
notebookEditor: NotebookEditor, | ||
notebookControllerId: string, | ||
extensionId: string, | ||
): Promise<void> { | ||
await commands.executeCommand('notebook.selectKernel', { | ||
notebookEditor, | ||
id: notebookControllerId, | ||
extension: extensionId, | ||
}); | ||
} | ||
|
||
/** | ||
* Function that executes notebook cell given code. | ||
* @param notebookDocument | ||
* @param code | ||
* @return Promise<void> | ||
*/ | ||
export async function executeNotebookCell(notebookDocument: NotebookDocument, code: string): Promise<void> { | ||
const { cellCount } = notebookDocument; | ||
await addCellToNotebook(notebookDocument, code); | ||
// Execute the cell | ||
commands.executeCommand('notebook.cell.execute', { | ||
ranges: [{ start: cellCount, end: cellCount + 1 }], | ||
document: notebookDocument.uri, | ||
}); | ||
} | ||
|
||
/** | ||
* Function that adds cell to notebook. | ||
* This function will only get called when notebook document is defined. | ||
* @param code | ||
* | ||
*/ | ||
async function addCellToNotebook(notebookDocument: NotebookDocument, 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 workspaceEdit = new WorkspaceEdit(); | ||
workspaceEdit.set(notebookDocument!.uri, [notebookEdit]); | ||
await workspace.applyEdit(workspaceEdit); | ||
} |
Oops, something went wrong.