You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Over in jolars/panache#115 we have been discussing an idea like this where the quarto comments are removed before styling, but when they are added back in an empty line should be added unconditionally between the quarto comments and the rest of the code block. That prevents cases like the above from looking so confusing. Can we make this change in Quarto? It seems like it would be an easy tweak.
So concretely:
If we start here
#| label: my label
## my R comment 1
a <- 1
## my R comment 2
b <- 2
Then we stay there.
If we start here
#| label: my label
## my R comment 1
a <- 1
## my R comment 2
b <- 2
then we add a line and end here
#| label: my label
## my R comment 1
a <- 1
## my R comment 2
b <- 2
if we start here
#| label: my label
a <- 1
b <- 2
we add a line and end here
#| label: my label
a <- 1
b <- 2
i.e. that empty line is unconditionally added to split the quarto directives from the rest of the code, even when there isnt a comment in the code itself
Reacted by zz77zz, Lorenzo Gaborini and Daniel Vartanian
A user reported in the Air R formatter repo that they feel that the lack of newlines after formatted quarto comments hinders readability. @mcanouil would you be interested to update the quarto comment formatting to always have a newline after the comments? I ask based on your recent work in: #655
I'll update with a more thorough test on typst, HTML, Reveal.js with code-annotation, code-line-numbers, and few others code related features to get a more comprehensive view of current status.
This will then be used for unit tests.
One more philosophical question is that okay to force all users to adopt the empty line after code cells options?
The initial report mentioned basically the spurious diff that the code cell preservation change caused because air deletes leading empty lines.
Here we are talking about causing the opposite spurious diff for users that do not have empty lines.
The options are (regardless of checking possible breakage for features using line numbers):
enforce all the way the stylistic convention of empty line after code cells options.
preserve (one) empty line if any after the code cells options or do nothing (no addition of empty line).
Option 1 is not followed at all on quarto-web for example, so enforcing this rule will lead to many diff in a lot of projects.
Option 1 is also far simpler than option 2 (although option 2 is "only" extending my previous change).
I'm personally leaning towards option 2 (with the cursor move to one extra line), but extra line added when there is none in the first place.
I was on several tools using development version and active extensions, so most likely a conflict somewhere.
Before making changes, I still need a test file sufficiently comprehensive to avoid breakage or unintended behaviour.
By the way, Python formatters do not strip leading empty lines in code cells context (it does in plain Python), so I need to understand why.
@vezwork yes, the behaviour between R and Python is different, even the code path is a as well.
Note that in your example # | has no support at all outside of Python.
Quarto implemented # | as a valid code cells comment because Black refused to leave #| alone.
On the R side, the story is different, styler and air leave #| be.
@mcanouil that R result looks wrong to me. There should be a full blank line after formatting between #| echo: true and # some comment if we are doing what we suggested in this issue
@mcanouil that R result looks wrong to me. There should be a full blank line after formatting between #| echo: true and # some comment if we are doing what we suggested in this issue
like
#| echo: true
# some comment
@DavisVaughan Yes, current is wrong for R (every empty lines are stripped), but before doing anything I need to clarify/understand.
Here are the options, and Option 1 is to enforce a new rule that will add one extra empty line while for Q1 or Q2 this empty line changes nothing (@cscheid to confirm I'm not mistaken).
Option 1 - What "you" are suggesting is that the below code always have an empty line inserted (I don't think that's right)
I'm currently looking at which part of the codepath differs between R and Python as both formatters (Air/Ruff) used in my setup should strip all leading empty lines, but somehow that's not the case in code cells. Air behave "as expected".
I'll see if there is a simple way (not refactoring the whole extension) to harmonise the code paths for easier maintenance of this part.
Python virtual document used to format the code always has content as the first line, so the leading empty lines are actually never at the beginning in the virtual doc.
IMO if quarto is already stripping out the quarto code cell options before calling the formatter, then it should also strip out all blank lines between the code cell options and the start of the code, rather than relying on the formatter to do it, because formatters differ in behavior here and we can't rely on them to help provide a consistent experience.
It is also feels more maintainable and easy to explain:
Strip all code cell options and any blank lines between code cell options and start of the code
Format remaining code
Reinsert all code cell options and exactly 1 blank line
The alternate algorithm you seem to prefer could be:
Strip all code cell options and any blank lines between code cell options and start of the code. Set any_blank_lines = true if we removed at least 1 blank line.
Format remaining code
Reinsert all code cell options. Insert exactly 1 blank line if any_blank_lines == true.
I agree. I've started playing around those ideas, especially because the "inject" thing makes formatters behave differently.
This injection should not produce different results in my opinion.
I have something working which adds a normalisation step for the leading empty lines (independent and prior to any formatters).
I'll add some tests and ask an LLM to review my code.
With this, I should be able to make a proper PR soon.
Here are the options, and Option 1 is to enforce a new rule that will add one extra empty line while for Q1 or Q2 this empty line changes nothing (@cscheid to confirm I'm not mistaken).
I apologize, but continue to not understand what Q1 vs Q2 has to do with the external formatter discussion happening here.
The parser doesn't actually know anything about the content of the cell. We have logic to extract the YAML option from the content, but that's not part of the qmd tree-sitter parser; that happens in a Rust filter. (After inspecting the code, I now realize this is currently fairly broken in Quarto 2, but the code is in crates/quarto-core/src/stage/stages/pre_engine_sugaring.rs over at quarto-dev/q2).
Activity
vezwork commentedon Apr 16, 2026
A user reported in the Air R formatter repo that they feel that the lack of newlines after formatted quarto comments hinders readability. @mcanouil would you be interested to update the quarto comment formatting to always have a newline after the comments? I ask based on your recent work in: #655
mcanouil commentedon Apr 16, 2026
Does it mean the cursor should also be moved one line below when quitting code cells options completion mode?
In my opinion, formatting is last resort. The tool should help getting it right in the first place.
vezwork commentedon Apr 16, 2026
That makes sense to me to change in addition to changing the formatting!
mcanouil commentedon Apr 16, 2026
I can take a look.
vezwork commentedon Apr 16, 2026
cool :)
mcanouil commentedon Apr 21, 2026
About the empty line after some testing:
echo: true) would display a code cell where line 1 is an empty line.I'm not sure the rendering aspect was taken into account here.
So, I would suggest to only preserve starting empty lines (likely as part of the options block), thus keep the code as provided by users.
DavisVaughan commentedon Apr 21, 2026
Can you provide a concrete example?
mcanouil commentedon Apr 21, 2026
I'll update with a more thorough test on typst, HTML, Reveal.js with code-annotation, code-line-numbers, and few others code related features to get a more comprehensive view of current status.
This will then be used for unit tests.
mcanouil commentedon Apr 21, 2026
One more philosophical question is that okay to force all users to adopt the empty line after code cells options?
The initial report mentioned basically the spurious diff that the code cell preservation change caused because air deletes leading empty lines.
Here we are talking about causing the opposite spurious diff for users that do not have empty lines.
The options are (regardless of checking possible breakage for features using line numbers):
Option 1 is not followed at all on quarto-web for example, so enforcing this rule will lead to many diff in a lot of projects.
Option 1 is also far simpler than option 2 (although option 2 is "only" extending my previous change).
I'm personally leaning towards option 2 (with the cursor move to one extra line), but extra line added when there is none in the first place.
vezwork commentedon Apr 21, 2026
It looks to me like rendering with
echo: truestrips out the code cell options and leading newline:edit: ah, but for R it is somehow different:
mcanouil commentedon Apr 21, 2026
I was on several tools using development version and active extensions, so most likely a conflict somewhere.
Before making changes, I still need a test file sufficiently comprehensive to avoid breakage or unintended behaviour.
By the way, Python formatters do not strip leading empty lines in code cells context (it does in plain Python), so I need to understand why.
mcanouil commentedon Apr 21, 2026
@vezwork yes, the behaviour between R and Python is different, even the code path is a as well.
Note that in your example
# |has no support at all outside of Python.Quarto implemented
# |as a valid code cells comment because Black refused to leave#|alone.On the R side, the story is different,
stylerandairleave#|be.vezwork commentedon Apr 21, 2026
Oh! In R, I see

#|(shown here) results in different behaviour than# |(shown in my above comment screenshot):But in Python it does not seem to make a difference.
3 remaining items
cscheid commentedon Apr 21, 2026
Can you clarify what you mean? I'm not sure I understand what you're asking.
DavisVaughan commentedon Apr 21, 2026
@mcanouil that R result looks wrong to me. There should be a full blank line after formatting between
#| echo: trueand# some commentif we are doing what we suggested in this issuelike
mcanouil commentedon Apr 21, 2026
@DavisVaughan Yes, current is wrong for R (every empty lines are stripped), but before doing anything I need to clarify/understand.
Here are the options, and Option 1 is to enforce a new rule that will add one extra empty line while for Q1 or Q2 this empty line changes nothing (@cscheid to confirm I'm not mistaken).
mcanouil commentedon Apr 21, 2026
I'm currently looking at which part of the codepath differs between R and Python as both formatters (Air/Ruff) used in my setup should strip all leading empty lines, but somehow that's not the case in code cells. Air behave "as expected".
I'll see if there is a simple way (not refactoring the whole extension) to harmonise the code paths for easier maintenance of this part.
mcanouil commentedon Apr 21, 2026
The difference of behaviour:
quarto/apps/vscode/src/vdoc/languages.ts
Lines 43 to 44 in 5224848
Python virtual document used to format the code always has content as the first line, so the leading empty lines are actually never at the beginning in the virtual doc.
DavisVaughan commentedon Apr 21, 2026
IMO if quarto is already stripping out the quarto code cell options before calling the formatter, then it should also strip out all blank lines between the code cell options and the start of the code, rather than relying on the formatter to do it, because formatters differ in behavior here and we can't rely on them to help provide a consistent experience.
It is also feels more maintainable and easy to explain:
The alternate algorithm you seem to prefer could be:
any_blank_lines = trueif we removed at least 1 blank line.any_blank_lines == true.mcanouil commentedon Apr 21, 2026
I agree. I've started playing around those ideas, especially because the "inject" thing makes formatters behave differently.
This injection should not produce different results in my opinion.
mcanouil commentedon Apr 21, 2026
Side question: with the development version (in test mode):
#some commentis not formatted to.# some comment. Do you observe the same thing?mcanouil commentedon Apr 21, 2026
I have something working which adds a normalisation step for the leading empty lines (independent and prior to any formatters).
I'll add some tests and ask an LLM to review my code.
With this, I should be able to make a proper PR soon.
Screen.Recording.2026-04-21.at.18.45.46.mov
cscheid commentedon Apr 21, 2026
I apologize, but continue to not understand what Q1 vs Q2 has to do with the external formatter discussion happening here.
mcanouil commentedon Apr 21, 2026
Sorry, I wasn't clear. Is the parser "care" (I know Q1 does not) if there is an empty new line between code cell options and code cell content?
cscheid commentedon Apr 21, 2026
The parser doesn't actually know anything about the content of the cell. We have logic to extract the YAML option from the content, but that's not part of the qmd tree-sitter parser; that happens in a Rust filter. (After inspecting the code, I now realize this is currently fairly broken in Quarto 2, but the code is in
crates/quarto-core/src/stage/stages/pre_engine_sugaring.rsover atquarto-dev/q2).vezwork commentedon Apr 22, 2026
@mine-cetinkaya-rundel said that adding an empty line after code cell option comments can break line numbering stuff in practice.
mcanouil commentedon Apr 22, 2026
PR is up.