Skip to content

Fix multiconsole hyperlink support by tracking active sessions in MainThreadConsoleService #8151

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

DavisVaughan
Copy link
Contributor

@DavisVaughan DavisVaughan commented Jun 17, 2025

Addresses #7322
Closes #8148 (an alternative approach)

As I was writing up #8148, I was irked by two things:

  • I do think the current approach of exposing a "real" Console object at the API level feels right and useful, and it mimics the TextEditor API very closely.
  • I re-found Console: API to get the active console #1507, which is all about having an API for accessing the active Console

In light of that, I no longer want to do the approach in #8148, because I think we should keep the Console API around, even if right now we only use it for pasteText().


This PR uses the new-ish onDidChangeActivePositronConsoleInstance() event to allow MainThreadConsoleService to track the active console per language. This allows positron.window.getActiveConsoleForLanguage() to correctly retrieve the active console given a particular languageId. That addresses #7322 by allowing us to paste into the active console, rather than the first one the user ever created for that language.

I also see that we now have onDidDeletePositronConsoleInstance(), so I've used that to also remove some TODOs about cleaning up when consoles are deleted.

example.mov

Copy link

github-actions bot commented Jun 17, 2025

E2E Tests 🚀
This PR will run tests tagged with: @:critical

readme  valid tags

Copy link
Collaborator

@jmcphers jmcphers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not work after a window reload. It doesn't look like _mainThreadConsolesBySessionId is populated after a reload?

image

*
* @param languageId The runtime language id to retrieve a `Console` for, i.e. 'r' or 'python'.
*
* @returns A Thenable that resolves to a `Console` or `undefined` if no `Console` for
* that `languageId` exists.
*/
export function getConsoleForLanguage(languageId: string): Thenable<Console | undefined>;
export function getActiveConsoleForLanguage(languageId: string): Thenable<Console | undefined>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is clearer, but do you think there's any chance someone is using the current name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -48,7 +48,7 @@ function handleNotRunnable(code: string) {
}

async function handleManuallyRunnable(_runtime: RSession, code: string) {
const console = await positron.window.getConsoleForLanguage('r');
const console = await positron.window.getActiveConsoleForLanguage('r');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your change is fine, but since we have an RSession, we could do this w/o any additional accounting by having an API to get a console by session ID instead of trying to track the active console

@DavisVaughan
Copy link
Contributor Author

Plan for when I'm back from vacation:

  • RSession gains a hacky pasteText(text: string) method
  • That calls RSession's onMessage() with a hackily hand crafted positron.LanguageRuntimeMessage
    • id can be a uuid
    • parent_id can be empty string
    • when can be current date and time in iso form
    • type should be CommData
    • The actual interface it implements should be LanguageRuntimeCommMessage
      • This needs the comm_id which should be the UI comm (how?). I think this comm_id is required so that emitDidReceiveClientMessage() doesn't just drop it.
      • This is where we'd set data to be a struct of { method: string, params: { text: string } }
  • That propagates RSession's onDidReceiveRuntimeMessage
  • PositronUiComm would gain this.onDidPasteText = super.createEventEmitter('paste_text', ['text']);, which would be an event that gets fired by PositronBaseComm in onDidReceiveData()
  • UiClientInstance relays onDidPasteText and refires it
  • startUiClientImpl() listens for onDidPasteText() and enhances with the sessionId and fires _onDidReceiveRuntimeEventEmitter (i.e. onDidReceiveRuntimeEvent) with new UiFrontendEvent.PasteText event.
  • Relays through RuntimeSessionService's onDidReceiveRuntimeEvent
  • ExtHostLanguageRuntimeSessionAdapter's constructor already does this._runtimeSessionService.onDidReceiveRuntimeEvent where it listens for all UiFrontendEvents and actually handles them. We are on the main thread here, so at this point use the sessionId to find the console to paste the text to. It's likely the constructor() will need to gain access to the _positronConsoleService, but the MainThreadLanguageRuntime can provide that. To find the right console, use this._positronConsoleService.positronConsoleInstances.find((console) => console.sessionId === sessionId) and then console.pasteText()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants