diff --git a/extensions/positron-python/pythonFiles/positron/positron_ipykernel/ui_comm.py b/extensions/positron-python/pythonFiles/positron/positron_ipykernel/ui_comm.py index cbbaa0a3fd2..c21823933fd 100644 --- a/extensions/positron-python/pythonFiles/positron/positron_ipykernel/ui_comm.py +++ b/extensions/positron-python/pythonFiles/positron/positron_ipykernel/ui_comm.py @@ -116,6 +116,20 @@ class Selection(BaseModel): ) +class Range(BaseModel): + """ + Selection range + """ + + start: Position = Field( + description="Start position of the selection", + ) + + end: Position = Field( + description="End position of the selection", + ) + + @enum.unique class UiBackendRequest(str, enum.Enum): """ @@ -195,6 +209,15 @@ class UiFrontendEvent(str, enum.Enum): # Execute a Positron command ExecuteCommand = "execute_command" + # Open a workspace + OpenWorkspace = "open_workspace" + + # Set the selections in the editor + SetEditorSelections = "set_editor_selections" + + # Show a URL in Positron's Viewer pane + ShowUrl = "show_url" + class BusyParams(BaseModel): """ @@ -256,6 +279,20 @@ class ShowQuestionParams(BaseModel): ) +class ShowDialogParams(BaseModel): + """ + Show a dialog + """ + + title: str = Field( + description="The title of the dialog", + ) + + message: str = Field( + description="The message to display in the dialog", + ) + + class PromptStateParams(BaseModel): """ New state of the primary and secondary prompts @@ -300,6 +337,54 @@ class ExecuteCommandParams(BaseModel): ) +class OpenWorkspaceParams(BaseModel): + """ + Open a workspace + """ + + path: str = Field( + description="The path for the workspace to be opened", + ) + + new_window: bool = Field( + description="Should the workspace be opened in a new window?", + ) + + +class SetEditorSelectionsParams(BaseModel): + """ + Set the selections in the editor + """ + + selections: List[Range] = Field( + description="The selections (really, ranges) to set in the document", + ) + + +class ModifyEditorSelectionsParams(BaseModel): + """ + Modify selections in the editor with a text edit + """ + + selections: List[Range] = Field( + description="The selections (really, ranges) to set in the document", + ) + + values: List[str] = Field( + description="The text values to insert at the selections", + ) + + +class ShowUrlParams(BaseModel): + """ + Show a URL in Positron's Viewer pane + """ + + url: str = Field( + description="The URL to display", + ) + + EditorContext.update_forward_refs() TextDocument.update_forward_refs() @@ -308,6 +393,8 @@ class ExecuteCommandParams(BaseModel): Selection.update_forward_refs() +Range.update_forward_refs() + CallMethodParams.update_forward_refs() CallMethodRequest.update_forward_refs() @@ -320,6 +407,8 @@ class ExecuteCommandParams(BaseModel): ShowQuestionParams.update_forward_refs() +ShowDialogParams.update_forward_refs() + PromptStateParams.update_forward_refs() WorkingDirectoryParams.update_forward_refs() @@ -327,3 +416,11 @@ class ExecuteCommandParams(BaseModel): DebugSleepParams.update_forward_refs() ExecuteCommandParams.update_forward_refs() + +OpenWorkspaceParams.update_forward_refs() + +SetEditorSelectionsParams.update_forward_refs() + +ModifyEditorSelectionsParams.update_forward_refs() + +ShowUrlParams.update_forward_refs() diff --git a/extensions/positron-r/package.json b/extensions/positron-r/package.json index 0928e7c1086..85191ee8fe2 100644 --- a/extensions/positron-r/package.json +++ b/extensions/positron-r/package.json @@ -533,7 +533,7 @@ }, "positron": { "binaryDependencies": { - "ark": "0.1.72" + "ark": "0.1.73" } } } diff --git a/positron/comms/ui-frontend-openrpc.json b/positron/comms/ui-frontend-openrpc.json index 9af706bd604..13e9b703e10 100644 --- a/positron/comms/ui-frontend-openrpc.json +++ b/positron/comms/ui-frontend-openrpc.json @@ -229,6 +229,51 @@ } } }, + { + "name": "set_editor_selections", + "summary": "Set the selections in the editor", + "description": "Use this to set the selection ranges/cursor in the editor", + "params": [ + { + "name": "selections", + "description": "The selections (really, ranges) to set in the document", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/range" + } + } + } + ] + }, + { + "name": "modify_editor_selections", + "summary": "Modify selections in the editor with a text edit", + "description": "Use this to edit a set of selection ranges/cursor in the editor", + "params": [ + { + "name": "selections", + "description": "The selections (really, ranges) to set in the document", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/range" + } + } + }, + { + "name": "values", + "description": "The text values to insert at the selections", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "result": {} + }, { "name": "last_active_editor_context", "summary": "Context metadata for the last editor", @@ -383,6 +428,24 @@ "end", "text" ] + }, + "range": { + "type": "object", + "description": "Selection range", + "properties": { + "start": { + "description": "Start position of the selection", + "$ref": "#/components/schemas/position" + }, + "end": { + "description": "End position of the selection", + "$ref": "#/components/schemas/position" + } + }, + "required": [ + "start", + "end" + ] } } } diff --git a/src/vs/workbench/api/browser/positron/mainThreadLanguageRuntime.ts b/src/vs/workbench/api/browser/positron/mainThreadLanguageRuntime.ts index bc4ef78fe4f..56429f264b1 100644 --- a/src/vs/workbench/api/browser/positron/mainThreadLanguageRuntime.ts +++ b/src/vs/workbench/api/browser/positron/mainThreadLanguageRuntime.ts @@ -28,8 +28,10 @@ import { IPositronHelpService } from 'vs/workbench/contrib/positronHelp/browser/ import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IRuntimeClientEvent } from 'vs/workbench/services/languageRuntime/common/languageRuntimeUiClient'; import { URI } from 'vs/base/common/uri'; -import { BusyEvent, UiFrontendEvent, OpenEditorEvent, OpenWorkspaceEvent, PromptStateEvent, WorkingDirectoryEvent, ShowMessageEvent, ExecuteCommandEvent } from 'vs/workbench/services/languageRuntime/common/positronUiComm'; +import { BusyEvent, UiFrontendEvent, OpenEditorEvent, OpenWorkspaceEvent, PromptStateEvent, WorkingDirectoryEvent, ShowMessageEvent, ExecuteCommandEvent, SetEditorSelectionsEvent } from 'vs/workbench/services/languageRuntime/common/positronUiComm'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditor } from 'vs/editor/common/editorCommon'; +import { Selection } from 'vs/editor/common/core/selection'; import { ITextResourceEditorInput } from 'vs/platform/editor/common/editor'; import { IPositronDataExplorerService } from 'vs/workbench/services/positronDataExplorer/browser/interfaces/positronDataExplorerService'; import { ObservableValue } from 'vs/base/common/observableInternal/base'; @@ -190,6 +192,13 @@ class ExtHostLanguageRuntimeSessionAdapter implements ILanguageRuntimeSession { // Update busy state const busy = ev.data as BusyEvent; this.dynState.busy = busy.busy; + } else if (ev.name === UiFrontendEvent.SetEditorSelections) { + // Set the editor selections + const sel = ev.data as SetEditorSelectionsEvent; + const selections = sel.selections.map(s => + new Selection(s.start.line, s.start.character, s.end.line, s.end.character)); + const editor = this._editorService.activeTextEditorControl as IEditor; + editor.setSelections(selections); } else if (ev.name === UiFrontendEvent.OpenEditor) { // Open an editor const ed = ev.data as OpenEditorEvent; diff --git a/src/vs/workbench/api/common/positron/extHostMethods.ts b/src/vs/workbench/api/common/positron/extHostMethods.ts index 974a8cfca85..925cee80cf2 100644 --- a/src/vs/workbench/api/common/positron/extHostMethods.ts +++ b/src/vs/workbench/api/common/positron/extHostMethods.ts @@ -6,7 +6,8 @@ import * as extHostProtocol from './extHost.positron.protocol'; import { ExtHostEditors } from '../extHostTextEditors'; import { ExtHostModalDialogs } from '../positron/extHostModalDialogs'; import { ExtHostWorkspace } from '../extHostWorkspace'; -import { UiFrontendRequest, EditorContext } from 'vs/workbench/services/languageRuntime/common/positronUiComm'; +import { Range } from 'vs/workbench/api/common/extHostTypes'; +import { UiFrontendRequest, EditorContext, Range as UIRange } from 'vs/workbench/services/languageRuntime/common/positronUiComm'; import { JsonRpcErrorCode } from 'vs/workbench/services/languageRuntime/common/positronBaseComm'; import { EndOfLine } from '../extHostTypeConverters'; @@ -64,6 +65,18 @@ export class ExtHostMethods implements extHostProtocol.ExtHostMethodsShape { result = await this.lastActiveEditorContext(); break; } + case UiFrontendRequest.ModifyEditorSelections: { + if (!params || + !Object.keys(params).includes('selections') || + !Object.keys(params).includes('values')) { + return newInvalidParamsError(method); + } + const sel = params.selections as UIRange[]; + const selections = sel.map(s => + new Range(s.start.line, s.start.character, s.end.line, s.end.character)); + result = await this.modifyEditorLocations(selections, params.values as string[]); + break; + } case UiFrontendRequest.WorkspaceFolder: { if (params && Object.keys(params).length > 0) { return newInvalidParamsError(method); @@ -179,6 +192,21 @@ export class ExtHostMethods implements extHostProtocol.ExtHostMethodsShape { }; } + async modifyEditorLocations(locations: Range[], values: string[]): Promise { + const editor = this.editors.getActiveTextEditor(); + if (!editor) { + return null; + } + + editor.edit(editBuilder => { + locations.map((location, i) => { + editBuilder.replace(location, values[i]); + }); + }); + + return null; + } + async workspaceFolder(): Promise { const folders = this.workspace.getWorkspaceFolders(); if (folders && folders.length > 0) { diff --git a/src/vs/workbench/services/languageRuntime/common/languageRuntimeUiClient.ts b/src/vs/workbench/services/languageRuntime/common/languageRuntimeUiClient.ts index a5ed2f1ee22..8e5aaed4d5b 100644 --- a/src/vs/workbench/services/languageRuntime/common/languageRuntimeUiClient.ts +++ b/src/vs/workbench/services/languageRuntime/common/languageRuntimeUiClient.ts @@ -5,7 +5,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { IRuntimeClientInstance } from 'vs/workbench/services/languageRuntime/common/languageRuntimeClientInstance'; -import { BusyEvent, ClearConsoleEvent, UiFrontendEvent, OpenEditorEvent, OpenWorkspaceEvent, PositronUiComm, PromptStateEvent, ShowMessageEvent, WorkingDirectoryEvent, ExecuteCommandEvent, ShowUrlEvent } from './positronUiComm'; +import { BusyEvent, ClearConsoleEvent, UiFrontendEvent, OpenEditorEvent, OpenWorkspaceEvent, PositronUiComm, PromptStateEvent, ShowMessageEvent, WorkingDirectoryEvent, ExecuteCommandEvent, ShowUrlEvent, SetEditorSelectionsEvent } from './positronUiComm'; /** @@ -63,6 +63,7 @@ export class UiClientInstance extends Disposable { /** Emitters for events forwarded from the UI comm */ onDidBusy: Event; onDidClearConsole: Event; + onDidSetEditorSelections: Event; onDidOpenEditor: Event; onDidOpenWorkspace: Event; onDidShowMessage: Event; @@ -86,6 +87,7 @@ export class UiClientInstance extends Disposable { this._comm = new PositronUiComm(this._client); this.onDidBusy = this._comm.onDidBusy; this.onDidClearConsole = this._comm.onDidClearConsole; + this.onDidSetEditorSelections = this._comm.onDidSetEditorSelections; this.onDidOpenEditor = this._comm.onDidOpenEditor; this.onDidOpenWorkspace = this._comm.onDidOpenWorkspace; this.onDidShowMessage = this._comm.onDidShowMessage; diff --git a/src/vs/workbench/services/languageRuntime/common/positronUiComm.ts b/src/vs/workbench/services/languageRuntime/common/positronUiComm.ts index 4704b5105b1..6775dcc567c 100644 --- a/src/vs/workbench/services/languageRuntime/common/positronUiComm.ts +++ b/src/vs/workbench/services/languageRuntime/common/positronUiComm.ts @@ -138,6 +138,22 @@ export interface Selection { } +/** + * Selection range + */ +export interface Range { + /** + * Start position of the selection + */ + start: Position; + + /** + * End position of the selection + */ + end: Position; + +} + /** * Event: Change in backend's busy/idle status */ @@ -241,6 +257,17 @@ export interface OpenWorkspaceEvent { } +/** + * Event: Set the selections in the editor + */ +export interface SetEditorSelectionsEvent { + /** + * The selections (really, ranges) to set in the document + */ + selections: Array; + +} + /** * Event: Show a URL in Positron's Viewer pane */ @@ -320,6 +347,24 @@ export interface DebugSleepRequest { export interface WorkspaceFolderRequest { } +/** + * Request: Modify selections in the editor with a text edit + * + * Use this to edit a set of selection ranges/cursor in the editor + */ +export interface ModifyEditorSelectionsRequest { + /** + * The selections (really, ranges) to set in the document + */ + selections: Array; + + /** + * The text values to insert at the selections + */ + values: Array; + +} + /** * Request: Context metadata for the last editor * @@ -338,6 +383,7 @@ export enum UiFrontendEvent { WorkingDirectory = 'working_directory', ExecuteCommand = 'execute_command', OpenWorkspace = 'open_workspace', + SetEditorSelections = 'set_editor_selections', ShowUrl = 'show_url' } @@ -346,6 +392,7 @@ export enum UiFrontendRequest { ShowDialog = 'show_dialog', DebugSleep = 'debug_sleep', WorkspaceFolder = 'workspace_folder', + ModifyEditorSelections = 'modify_editor_selections', LastActiveEditorContext = 'last_active_editor_context' } @@ -360,6 +407,7 @@ export class PositronUiComm extends PositronBaseComm { this.onDidWorkingDirectory = super.createEventEmitter('working_directory', ['directory']); this.onDidExecuteCommand = super.createEventEmitter('execute_command', ['command']); this.onDidOpenWorkspace = super.createEventEmitter('open_workspace', ['path', 'new_window']); + this.onDidSetEditorSelections = super.createEventEmitter('set_editor_selections', ['selections']); this.onDidShowUrl = super.createEventEmitter('show_url', ['url']); } @@ -434,6 +482,12 @@ export class PositronUiComm extends PositronBaseComm { * Use this to open a workspace in Positron */ onDidOpenWorkspace: Event; + /** + * Set the selections in the editor + * + * Use this to set the selection ranges/cursor in the editor + */ + onDidSetEditorSelections: Event; /** * Show a URL in Positron's Viewer pane * diff --git a/src/vs/workbench/services/runtimeSession/common/runtimeSession.ts b/src/vs/workbench/services/runtimeSession/common/runtimeSession.ts index 09079b90ef6..acc4775b12c 100644 --- a/src/vs/workbench/services/runtimeSession/common/runtimeSession.ts +++ b/src/vs/workbench/services/runtimeSession/common/runtimeSession.ts @@ -1051,6 +1051,15 @@ export class RuntimeSessionService extends Disposable implements IRuntimeSession } }); })); + this._register(uiClient.onDidSetEditorSelections(event => { + this._onDidReceiveRuntimeEventEmitter.fire({ + session_id: session.sessionId, + event: { + name: UiFrontendEvent.SetEditorSelections, + data: event + } + }); + })); this._register(uiClient.onDidOpenEditor(event => { this._onDidReceiveRuntimeEventEmitter.fire({ session_id: session.sessionId,