JuliaEditorSupport / julia-ts-mode

Experimental Emacs Julia major mode using tree-sitter
MIT License
37 stars 8 forks source link

Auto indentation of blocks #17

Open hexaeder opened 11 months ago

hexaeder commented 11 months ago

Hello,

I just switched from julia-mode to julia-ts-mode on emacs 29.1. One thing I noticed is that my editor does not indent correctly anymore. I.e., when typing

function foo()<ret>

the point gets placed at column 0 in the next row rather than column 4. This works "correctly" when I switch back to julia-mode.

Is this a problem with julia-ts-mode? Might be related to something else in my config, so feel free to close if this feature has nothing to do with julia-ts-mode, my emacs knowledge is rather limited and I am never quite certain which mode provides which feature anyway 🙃

ggebbie commented 11 months ago

Discussion of the issue here:

https://discourse.julialang.org/t/help-testing-julia-tree-sitter-mode-in-emacs/92819/29?page=2

My understanding: it is a tree-sitter issue. If you have the enclosing end block then the indenting should work.

ronisbr commented 11 months ago

Yes, exactly like @ggebbie said. Before adding end, tree-sitter does not create a new node. Hence, emacs cannot know that it must apply indentation.

hexaeder commented 11 months ago

Thanks for the link! However, M-x indent-according-to-mode works as expected, even without the end keyword:

function foo()
bar <M-x indent-according-to-mode>
#noend

EDIT: does not work at the end of file 🤔

On the other hand

function foo() <ret>
end

will put the point at column 0 within the function body, even with end keyword.

This feels inconsistent with the explanation about treesitter nodes...

ronisbr commented 11 months ago

will put the point at column 0 within the function body, even with end keyword.

If you have no text at all, it also does not create a node. That's why you do not see the indentation. Take a look at the explorer information:

function teste()

end
(function_definition function name: (identifier)
 (parameter_list ( ))
 \n end)

Notice that we do not have a "function body".

Thanks for the link! However, M-x indent-according-to-mode works as expected, even without the end keyword:

It does not work here. You probably are seeing some strange indentation due to the previous text in the file. I tested with this snippet:

function foo()
bar

function teste()
end

This feels inconsistent with the explanation about treesitter nodes...

No. Check the explorer when you partially define a function:

function teste()
a = 2
(ERROR function (identifier)
 (parameter_list ( ))
 \n
 (assignment (identifier) (operator) (integer_literal))
 \n)

It marks as an error. That's why the indentation cannot work.

natrys commented 2 weeks ago

If you have no text at all, it also does not create a node. That's why you do not see the indentation.

I think that's only partially true. Whether or not there is a node at point shouldn't materially affect our indentation condition since in this case it only relies on parent (e.g. (parent_is "_definition")).

The first two parameters of treesit-simple-indent function is node and parent. Given our particular condition here, so long as the parent is function_definition, we should indent regardless of whether node is nil or something valid. If you trace that function, then I think the problem becomes apparent. At this position if you press tab:

function test()
|
end

The trace-output gives you:

======================================================================
1 -> (treesit-simple-indent nil #<treesit-node "
" in 20-22> 21)
1 <- treesit-simple-indent: (1 . 0)

So here Emacs considers us to be inside the anonymous newline node, and therefore considers that to be the parent. I am not sure why Emacs can't consider node to be newline, I think that's because the start of the newline happened before the bol of current line. That would make sense, if the reason writing something else suddenly makes it work is because then that newline node terminates and becomes a sibling node, and the new node is actually considered a node because it started after the bol of current line, which means function_definition finally becomes parent:

======================================================================
1 -> (treesit-simple-indent #<treesit-node assignment in 23-37> #<treesit-node function_definition in 1-41> 23)
1 <- treesit-simple-indent: (1 . 2)

What I am getting to is that it might be possible to fix the problem here if we make our indentation rules that newline aware. However that might be very tricky to do, it might be worthwhile asking upstream to simply remove the newlines from showing up in the syntax tree, then the rules here will work as is (without needing to create a new node).