diff --git a/after/queries/rust/matchup.scm b/after/queries/rust/matchup.scm new file mode 100644 index 0000000..40a8745 --- /dev/null +++ b/after/queries/rust/matchup.scm @@ -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_ + diff --git a/lua/treesitter-matchup/internal.lua b/lua/treesitter-matchup/internal.lua index 2f89605..37a0263 100644 --- a/lua/treesitter-matchup/internal.lua +++ b/lua/treesitter-matchup/internal.lua @@ -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 diff --git a/test/vader/ts_py_motion.vader b/test/vader/ts_py_motion.vader index 57ea1c5..5b07158 100644 --- a/test/vader/ts_py_motion.vader +++ b/test/vader/ts_py_motion.vader @@ -10,11 +10,16 @@ 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 @@ -22,6 +27,8 @@ Given python (A python script): Execute (Logs): Log b:matchup_active_engines +# ----- outer if/elif/else ----- + Before (Cursor): normal! 2gg^ @@ -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): %%% @@ -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)