Skip to content

Show all R expression outputs in Quarto cells#14185

Closed
metehankaygsz wants to merge 6 commits into
posit-dev:mainfrom
metehankaygsz:feature/quarto-r-multiple-outputs
Closed

Show all R expression outputs in Quarto cells#14185
metehankaygsz wants to merge 6 commits into
posit-dev:mainfrom
metehankaygsz:feature/quarto-r-multiple-outputs

Conversation

@metehankaygsz

Copy link
Copy Markdown
Contributor

Summary

  • split primary R Quarto cell code into line-aligned complete expressions before execution
  • execute those expressions sequentially so every implicit result or plot is appended to the cell's inline output
  • fall back to the original whole-cell execution when a runtime cannot determine expression completeness
  • preserve existing behavior for other languages and non-primary-language console execution
  • add regression coverage for a multiline R expression followed by multiple visible results

This makes separate outputs from an R chunk appear together below the chunk instead of showing only the final implicit result.

Related to #5272 and #13505.

Testing

  • npm run compile-check-ts-native
  • npx vitest run src/vs/workbench/contrib/positronQuarto/test/browser/quartoExecutionManager.vitest.ts
  • targeted ESLint on the two changed files: 0 errors

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@metehankaygsz

Copy link
Copy Markdown
Contributor Author

I have read the CLA Document and I hereby sign the CLA

@jmcphers jmcphers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This approach is going to be too slow to be practical in many situations. It's going to create an RPC round trip for every single line of code in the cell, and those can be very slow e.g. over remote SSH or on loaded systems.

To make this efficient, we should find statement boundaries via a single RPC to the back end (probably going through to Ark on the R side), or by precomputing them via a new LSP method during edits.

We do appreciate the contribution and are aware that lots of people are interested in this feature!

@metehankaygsz

Copy link
Copy Markdown
Contributor Author

I updated this to use a single backend boundary query through Ark (inputBoundaries) and then split the R code locally from that response, so we avoid the per-line RPC cost. I also added a test to confirm we only query once and still keep each output under the chunk. I believe this is much closer to the approach you were asking for, but I’d appreciate your confirmation.

@metehankaygsz
metehankaygsz requested a review from jmcphers June 11, 2026 17:25
@jmcphers

Copy link
Copy Markdown
Collaborator

I updated this to use a single backend boundary query through Ark

I see the update in this PR to ask Ark for the boundaries, but none on the Ark side to implement the back end of the RPC. Do you have the accompanying Ark changes? If so, can you open a PR for them as well?

@metehankaygsz

Copy link
Copy Markdown
Contributor Author

I implemented this on Ark side too, opening the PR right now.

@lionel- lionel- left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I like your current approach that calls the LSP request we implemented in posit-dev/ark#533. We don't want to go through the kernel via the UI comm (cf your recent Ark-side PR) because that would necessarily make the input boundaries unavailable while R is busy. In practice this will also be the case with the Ark LSP (already the case on Windows, soon on Unix platforms too, see posit-dev/ark#1222). In the future though, this request will live in the standalone Oak LSP which will make it fully independent of the state of the kernel.

So what this PR implements is the right shape overall: positron-r dispatches that call to whatever backend is suitable (Ark LSP, Ark kernel, Oak LSP, ...). I'm just not sure we should do it from callMethod which is meant to execute code in the kernel.

The one potential advantage of callMethod is that it would keep input boundaries more private, and would not tie us to a particular interface. We have never used it anywhere yet so it's kind of experimental (in particular until we do #5272). So we could use it temporarily.

That said, I'd still expose the input boundaries more cleanly. A better model than callMethod for this feature is the provider-like interface of StatementRangeProvider in positron.d.ts. WDYT @jmcphers?

Comment thread extensions/positron-r/src/session.ts Outdated
}

callMethod(method: string, ...args: any[]): Thenable<any> {
async callMethod(method: string, ...args: any[]): Promise<any> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Interesting, I've never had the thought that callMethod() could dispatch outside the kernel. It's not what it was made for (cf doc in positron.d.ts).

Comment thread extensions/positron-r/src/session.ts Outdated
if (method === 'inputBoundaries') {
// Quarto asks for R input boundaries through the LSP so it can split
// expressions without paying for a completeness check per line.
const text = typeof args[0] === 'string' ? args[0] : '';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Probably should throw if malformed

});

describe('Output Handling', () => {
it('queries input boundaries once and keeps each R result', async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It would be reassuring to have tests with incomplete and parse-error sections.

@metehankaygsz

Copy link
Copy Markdown
Contributor Author

Thank you, I addressed these problems in 94de970e.

I’m still new to this codebase, so I really appreciate the guidance and explanations.

I updated the PR to follow the approach you suggested instead of using callMethod(). I also added the extra test coverage you asked for, including incomplete R code and parse-error cases.

@metehankaygsz
metehankaygsz marked this pull request as ready for review June 18, 2026 15:10
@metehankaygsz
metehankaygsz marked this pull request as draft June 18, 2026 15:54
@metehankaygsz
metehankaygsz marked this pull request as ready for review June 18, 2026 17:04
@metehankaygsz
metehankaygsz requested a review from lionel- June 19, 2026 09:53
@lionel-
lionel- removed their request for review June 23, 2026 08:39

@jmcphers jmcphers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This approach looks much better!

A few more things to tweak:

  • The new behavior wherein we split up the statements can be great for people who expect it, but others will expect it to work more like notebooks do. We should have an option to gate this behavior (default to on is okay).
  • As written this is hardcoded to R. We should avoid language-specific paths in the Quarto execution manager. Instead, it should be gated on whether an input boundary provider exists for the language (see the statement range provider for examples). This will allow third party language packs to implement boundary splitting if they desire without us needing to add special casing here.

@metehankaygsz

Copy link
Copy Markdown
Contributor Author

I updated the PR to address both points.

Added positron.quarto.inlineOutput.splitStatements, defaulting to true. When disabled, Quarto inline execution sends the full code range once.

Removed the R-specific branching from the Quarto execution manager. It now creates the boundary model using the cell language and only splits when an input boundary provider matches that language. If no provider matches or the boundaries are malformed, it falls back to whole-range execution.

I also updated the R provider selector and added tests for disabled splitting, no matching provider, non-R provider splitting, malformed boundaries, and the existing R behavior.

@jmcphers

jmcphers commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Teşekkürler, very much appreciate the contribution! I am going to move this to #14615 so our CI can run on it, follow updates there.

@metehankaygsz

Copy link
Copy Markdown
Contributor Author

You are welcome, I want to thank you too for your assistance throughout the way. I will track it from that PR.

jmcphers added a commit that referenced this pull request Jul 2, 2026
Supersedes #14185. 

### Release Notes

#### New Features

- Show inline output of each individual R expression when running Quarto
cells (#14185; thanks to @metehankaygsz)


#### Bug Fixes

- N/A

Test tags: @:quarto @:console @:editor

---------

Co-authored-by: Metehan Kaygısız <metehankaygsz@yahoo.com.tr>
@jmcphers

jmcphers commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

This is all looking great and working well for me. I've merged #14615 into Positron so will close this PR. Thanks again!

@jmcphers jmcphers closed this Jul 2, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants