Skip to content

Add empty line after quarto comments #950

Closed

Description

@vezwork

Via @DavisVaughan in posit-dev/air#487:

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

Activity

  1. vezwork commented on Apr 16, 2026

    @vezwork
    MemberAuthor

    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

  2. mcanouil commented on Apr 16, 2026

    @mcanouil
    Contributor

    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.

  3. vezwork commented on Apr 16, 2026

    @vezwork
    MemberAuthor

    That makes sense to me to change in addition to changing the formatting!

  4. mcanouil commented on Apr 16, 2026

    @mcanouil
    Contributor

    I can take a look.

  5. vezwork commented on Apr 16, 2026

    @vezwork
    MemberAuthor

    cool :)

  6. mcanouil commented on Apr 21, 2026

    @mcanouil
    Contributor

    About the empty line after some testing:

    • a code cell echoing its code (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.

  7. DavisVaughan commented on Apr 21, 2026

    @DavisVaughan
    Collaborator

    Can you provide a concrete example?

  8. mcanouil commented on Apr 21, 2026

    @mcanouil
    Contributor

    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.

  9. mcanouil commented on Apr 21, 2026

    @mcanouil
    Contributor

    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):

    1. enforce all the way the stylistic convention of empty line after code cells options.
    2. 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.

  10. vezwork commented on Apr 21, 2026

    @vezwork
    MemberAuthor

    About the empty line after some testing:

    • a code cell echoing its code (echo: true) would display a code cell where line 1 is an empty line.

    It looks to me like rendering with echo: true strips out the code cell options and leading newline:

    Image

    edit: ah, but for R it is somehow different:

    Image
  11. mcanouil commented on Apr 21, 2026

    @mcanouil
    Contributor

    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.

  12. mcanouil commented on Apr 21, 2026

    @mcanouil
    Contributor

    @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.

  13. vezwork commented on Apr 21, 2026

    @vezwork
    MemberAuthor

    Oh! In R, I see #| (shown here) results in different behaviour than # | (shown in my above comment screenshot):
    Image

    But in Python it does not seem to make a difference.

  14. 3 remaining items

  15. cscheid commented on Apr 21, 2026

    @cscheid
    Member

    @cscheid from a Q1/Q2 point of view, any thoughts?

    Can you clarify what you mean? I'm not sure I understand what you're asking.

  16. DavisVaughan commented on Apr 21, 2026

    @DavisVaughan
    Collaborator

    @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
    
  17. mcanouil commented on Apr 21, 2026

    @mcanouil
    Contributor

    @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)
    BeforeAfter
    ```{r}
    #| echo: true
    
    #some comment
    x<-1
    ```
    ```{r}
    #| echo: true
    
    # some comment
    x <- 1
    ```
    ```{r}
    #| echo: true
    #some comment
    x<-1
    ```
    ```{r}
    #| echo: true
    
    # some comment
    x <- 1
    ```
    ```{r}
    #| echo: true
    
    
    
    
    #some comment
    x<-1
    ```
    ```{r}
    #| echo: true
    
    # some comment
    x <- 1
    ```
    • Option 2
    BeforeAfter
    ```{r}
    #| echo: true
    
    #some comment
    x<-1
    ```
    ```{r}
    #| echo: true
    
    # some comment
    x <- 1
    ```
    ```{r}
    #| echo: true
    #some comment
    x<-1
    ```
    ```{r}
    #| echo: true
    # some comment
    x <- 1
    ```
    ```{r}
    #| echo: true
    
    
    
    
    #some comment
    x<-1
    ```
    ```{r}
    #| echo: true
    
    # some comment
    x <- 1
    ```
  18. mcanouil commented on Apr 21, 2026

    @mcanouil
    Contributor

    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.

  19. mcanouil commented on Apr 21, 2026

    @mcanouil
    Contributor

    The difference of behaviour:

    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.

  20. DavisVaughan commented on Apr 21, 2026

    @DavisVaughan
    Collaborator

    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.
  21. mcanouil commented on Apr 21, 2026

    @mcanouil
    Contributor

    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.

  22. mcanouil commented on Apr 21, 2026

    @mcanouil
    Contributor

    Side question: with the development version (in test mode): #some comment is not formatted to. # some comment. Do you observe the same thing?

  23. mcanouil commented on Apr 21, 2026

    @mcanouil
    Contributor

    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
  24. cscheid commented on Apr 21, 2026

    @cscheid
    Member

    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.

  25. mcanouil commented on Apr 21, 2026

    @mcanouil
    Contributor

    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?

  26. cscheid commented on Apr 21, 2026

    @cscheid
    Member

    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).

  27. vezwork commented on Apr 22, 2026

    @vezwork
    MemberAuthor

    @mine-cetinkaya-rundel said that adding an empty line after code cell option comments can break line numbering stuff in practice.

  28. mcanouil commented on Apr 22, 2026

    @mcanouil
    Contributor

    PR is up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions