Skip to content
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

Improve treesitter indentation #1440

Closed
Triton171 opened this issue Jan 4, 2022 · 2 comments · Fixed by #1562
Closed

Improve treesitter indentation #1440

Triton171 opened this issue Jan 4, 2022 · 2 comments · Fixed by #1562
Labels
A-tree-sitter Area: Tree-sitter C-enhancement Category: Improvements

Comments

@Triton171
Copy link
Contributor

I'm opening this to continue the discussion from #1293. The current indentation configuration is somewhat limited and could be improved upon. For example, there is currently no way to write an indents.toml for C that handles:

if (condition)
   do_smth();

and:

if (condition)
{
   do_smth();
}

correctly without breaking the rest of the indentation.

To make writing indent queries easier and more intuitive (right now, indent and outdent work differently which might be confusing), we could borrow some ideas from emacs as @archseer suggested.
This still wouldn't be enough to fix the above issue, as that requires some sort of context-dependence. My suggestion is to also allow specifying a list of nodes in an indent query. The corresponding indentation will then only be applied if all of these nodes are directly nested in the given order. The indents.toml file might then look like this (for C):

[indent]
# This indents the whole node one level
all = []
# This indents everything except for the first and last child one level
body = [
   "compound_statement",
# ...
]
# This indents everything except for the first child one level
tail = [
   "init_declarator",
   "case_statement",
   # We need this to correctly indent if statements without braces (the first case in the above example)
   "if_statement",
# ...
]

[outdent]
all = [
   # This means that every compound statement (statements inside braces) inside an if statement is outdented one level.
   # Together with the indentation for the if_statement in indents.tail and the indentation for the compound statement in
   # indents.body, this yields the correct indentation
   ["if_statement", "compound_statement"]
]
body = [
# ...
]

If we want to go in that direction, the current indent queries could easily be adjusted for this and eventually be updated to take advantage of the new features.

@kirawi kirawi added A-tree-sitter Area: Tree-sitter C-enhancement Category: Improvements labels Jan 4, 2022
@dead10ck
Copy link
Member

dead10ck commented Jan 8, 2022

Thanks for thinking about this, I like some of the ideas here about making use of tree-sitter to make auto indent more intelligent.

[outdent]
all = [
   # This means that every compound statement (statements inside braces) inside an if statement is outdented one level.
   # Together with the indentation for the if_statement in indents.tail and the indentation for the compound statement in
   # indents.body, this yields the correct indentation
   ["if_statement", "compound_statement"]
]
body = [
# ...
]

Am I understanding that this case covers

if (thing)
{
    something();
}

Isn't this a stylistic choice? What happens if the brace is on the same line, does it outdent the whole if expression? e.g.

void foo() {
if (thing) {
        something();
    }
}

@Triton171
Copy link
Contributor Author

[outdent]
all = [
   # This means that every compound statement (statements inside braces) inside an if statement is outdented one level.
   # Together with the indentation for the if_statement in indents.tail and the indentation for the compound statement in
   # indents.body, this yields the correct indentation
   ["if_statement", "compound_statement"]
]

This means that we outdent every compound statement that's directly inside an if statement (note that the indentation of the if statement as a whole is not changed).
It only prevents the if-statement from being indented twice (once because the tail of an "if_statement" is indented and once because the body of a "compound_statement" is indented). This is never an issue in the second case anyways, because even we never indent more than one level from one line to the next. The double-indentation is always a problem when the 'if' and the opening brace are on different lines.

My solution also works if the braces are on the same line (we obviously want to support all reasonable code styles). In your example, this looks like:

// As it is a compound statement, the function body is indented one level
void foo() {
    // The tail of the if statement should be indented one level, this would apply to the second line of the if statement.
    // Because of the "outdent" definition, this is essentially nullified by the outdent of the compound statement.
    // Even if they didn't cancel out, both of these would only apply starting from the second line
    if (thing) {
        // This is inside the body of another compound statement, so it's indented one more level
    }
}

The ["if_statement", "compound_statement"] syntax is a bit confusing. Maybe something like { node = "compound_statement", if_parent_in = ["if_statement"] } would be better. It's a bit less general because it doesn't allow more distant ancestors but it's easier to read and allows specifying multiple possible parents.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-tree-sitter Area: Tree-sitter C-enhancement Category: Improvements
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants