Skip to content

Add rust support via tree-sitter #140

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

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions after/queries/rust/matchup.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
; --------------- fn/return ---------------

(function_item
"fn" @open.function) @scope.function
(closure_expression parameters: (closure_parameters . "|" @open.function "|" .) body: (block)) @scope.function
(return_expression
"return" @mid.function.1)

; --------------- async/await ---------------

; give async_block the same scope name, because .await always
; suspends to the innermost async scope
(function_item (function_modifiers "async" @open.async)) @scope.async
(async_block "async" @open.async) @scope.async
(await_expression "await" @mid.async.1)

; --------------- if/else ---------------

; the rust grammar is like (else_clause "else" (if_expression "if"))
; so the else and the if can't be given a single name covering both of them
; hence just highlighting the else. close enough
(else_clause "else" @mid.if_.1 (if_expression) @else_if)
(else_clause "else" @mid.if_.1 (if_let_expression) @else_if)
(else_clause "else" @mid.if_.2 (block))

; ideally neovim would support more predicates like is-not?, which would mean
; that you could recognise an if-expression being the if in an "else if" and
; use that capture @else_if to indicate that it shouldn't form its own
; @scope.if_. I actually don't know if this would work, because predicates
; are not documented anywhere that I can find.
;
; ((if_expression "if" @open.if_) @scope.if_ (is-not? @else_if))
; ((if_let_expression "if" @open.if_) @scope.if_ (is-not? @else_if))
;
; for now, this will suffice to prevent such "else if" if_expressions from
; introducing an inner scope, but won't match "let x = if {} else {}" at all.
(block (if_expression "if" @open.if_) @scope.if_)
(block (if_let_expression "if" @open.if_) @scope.if_)

; --------------- while/loop/for + break/continue ---------------

; the . matches an end, so we can explicitly refuse to handle break 'label; and
; 'label loop {}
(for_expression . "for" @open.loop) @scope.loop
(while_let_expression . "while" @open.loop) @scope.loop
(loop_expression . "loop" @open.loop) @scope.loop

; unfortunately we can't exclude only `break 'label;` but not `break {expression};`
; as _expression is a supernode/meta node or whatever TS calls it, so you can't
; match on (expression)
(break_expression "break" @mid.loop.1 .)
(break_expression "break" @mid.loop.1 .)
(continue_expression "continue" @mid.loop.1 .)

; this strategy would maybe work if matchup were modified to support string
; matches on scopes
; (break_expression (loop_label (identifier) @mid.looplabel_.1))
; (loop_expression (loop_label (identifier) @open.looplabel_)) @scope.looplabel_

; --------------- match/arms ---------------

; this is fun, but lots of match expressions are complex enough that this would
; be too annoying because of firstly the lost syntax highlighting of the arms
; and secondly the fact that many match arms have braces in them, and those
; braces take priority.

; (match_expression
; "match" @open.match_
; body: (match_block
; (match_arm
; pattern: (match_pattern)? @mid.match_.1
; pattern: (macro_invocation)? @mid.match_.1
; )*
; (match_arm pattern: (match_pattern) @mid.match_.2)
; .
; )
; ) @scope.match_

13 changes: 7 additions & 6 deletions lua/treesitter-matchup/internal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -248,13 +248,14 @@ function M.get_matching(delim, down, bufnr)
and (row >= info.search_range[1]
and row <= info.search_range[3]) then

local scope = M.containing_scope(node, bufnr, info.key)
local target_scope = M.containing_scope(node, bufnr, info.key)
if info.scope == target_scope then
local text = ts_utils.get_node_text(node, bufnr)[1]
table.insert(matches, {text, row + 1, col + 1})

local text = ts_utils.get_node_text(node, bufnr)[1]
table.insert(matches, {text, row + 1, col + 1})

if side == 'close' then
got_close = true
if side == 'close' then
got_close = true
end
end
end
end
Expand Down
38 changes: 35 additions & 3 deletions test/vader/ts_py_motion.vader
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,25 @@ Execute (Helper):
endif

Given python (A python script):
def F(x):
def F(x, y):
if x == 1:
return 1
elif x == 2:
pass
if y == 5:
pass
elif y == 7:
return 9
else:
return x
else:
return 3
return 2

Execute (Logs):
Log b:matchup_active_engines

# ----- outer if/elif/else -----

Before (Cursor):
normal! 2gg^

Expand All @@ -33,7 +40,7 @@ Then (Verify line):
Do (Move % twice):
%%
Then (Verify line):
Assert line('.') == (TSActive() ? 6 : 2)
Assert line('.') == (TSActive() ? 11 : 2)

Do (Move % 3 times):
%%%
Expand All @@ -44,3 +51,28 @@ Do (Move % 4 times):
%%%%
Then (Verify line):
Assert line('.') == (TSActive() ? 4 : 2)

# ----- inner if/elif/else -----

Before (Cursor):
normal! 5gg^

Do (Inner: Move %):
%
Then (Verify line):
Assert line('.') == (TSActive() ? 7 : 5)

Do (Inner: % 2 times):
%%
Then (Verify line):
Assert line('.') == (TSActive() ? 9 : 5)

Do (Inner: % 3 times):
%%%
Then (Verify line):
Assert line('.') == (TSActive() ? 5 : 5)

Do (Inner: % 4 times):
%%%%
Then (Verify line):
Assert line('.') == (TSActive() ? 7 : 5)