We need to handle comments. There are a few tricky issues.
Problems
Comments vs. Doc comments
Doc comments apply to a node, such as a function definition or an enum variant. Regular comments typically do not. For example, a comment in the middle of a function body might be meant to apply to all following lines; commented-out code does not apply to whatever happens to come next; and regular comments can appear at the end of a file.
Therefore, doc comments should be an optional child of the node they apply to. In Rust, they're placed above a function, or sometimes at the top of a module; in Python they're in doc strings before the function body; in other languages they might be allowed to be at the end of a line.
Regular comments, by contrast, exist between nodes. Usually they're between listy nodes, but we may want to support them appearing between fixed arity nodes as well. (The only real reason to support them between fixed arity nodes is to accurately represent code written in a text editor that has comments in strange places.)
Commented-out code
You should be able to
- Comment some code with a hotkey
- Uncomment some code with a hotkey
- Maybe uncomment code that came from a text editor?
- Navigate to the commented out code and edit it just like uncommented code.
End of line comments
What should happen if there's a comment like:
foobar(bizzazzle, boozazzle) // foobar the bezazzles
Is that equivalent to a comment on the previous line?
// foobar the bezazzles
foobar(bizzazzle, boozazzle)
Inline block comments
What should we do with inline block comments like these?
let x = /* one */ 1 /* plus */ + /* two */ 2;
Comments and commas
How is this JSON list displayed?
[
one,
// comment1
two
// comment2
// comment3
]
Assuming that we're using "sibling comments" (see "Comments vs. Doc Comments"), this list has five elements, three of which are whitespace elements. Commas must be placed only after the element one
, not after comments or two
(because JSON). So the rule is that you put a comment after every non-last, non-whitespace element.
Proposed Solutions
Commented-out code
When you comment out code, it's placed in a special "code block" node in the comment. Or alternatively, it's placed in a "commented code" node (as distinguished from "comment" nodes). Either way, it marks what sort of code was commented. For example, commented-out code when saved to a text file might appear as:
// ``` rust statement
// let x = 1;
// ```
Commented-out code from a text editor appears as regular comments, and cannot be un-commented. (Unless you manually add the "commented code" node syntax.)
End of line comments
End of line comments can be parsed, but are always converted to before-line comments. This is due to an interaction of rules we really want to hold:
- Navigation order equals visual order. A before-line comment would have to be the first child, while and end of line comment would have to be the last child, so that when you navigate to the next sibling you don't jump around.
- Fixed navigation order: if you edit a comment, it absolutely cannot move to a different location in the tree. However, an end of line comment can only be so long while fitting on the line and needing to be moved. (And in screen reader mode, line width isn't even defined).
Inline block comments
At least for now, turn inline block comments into before-line comments. Thus this code:
let x = /* one */ 1 /* plus */ + /* two */ 2;
gets converted into:
// one
// plus
// two
let x = 1 + 2;
Comments vs. Doc comments
Doc comments are optional children of the node they annotate.
Regular comments can appear only in listy nodes, where they can be interspersed with regular children.
Comments and commas
The notation language can handle this with two new predicates:
IsEmptyText
: is the current node a text node with empty text?
IsLast
: is the next fold child the last non-whitespace element?
IsWhitespace
: is the next fold child a whitespace element?
There's a new kind of notation called If
that takes a Predicate
, which is made of the base predicates above joined by Not
, And
, Or
. These predicates are implemented by PrettyDoc
, just like IsEmptyText
is currently.