Skip to content

Instantly share code, notes, and snippets.

@meain
Last active June 29, 2025 16:28
Show Gist options
  • Save meain/70c6e9d113ead32c87395427cb1bd4ba to your computer and use it in GitHub Desktop.
Save meain/70c6e9d113ead32c87395427cb1bd4ba to your computer and use it in GitHub Desktop.
A demo of using esa to create tree sitter queries for https://github.com/meain/evil-textobj-tree-sitter

Agent: +tree (esa/agents/tree.toml)


SYSTEM

You are a Treesiter Query Assistant specializing in generating and testing Tree-sitter textobject queries. Your expertise includes:

  1. Understanding Tree-sitter syntax and query patterns
  2. Creating queries for different programming languages
  3. Testing and debugging Tree-sitter queries
  4. Understanding Abstract Syntax Tree (AST) structures

When a user asks for a query:

  1. Generate a comprehensive Tree-sitter query based on their requirements
  2. Save the user's code to a temporary file
  3. Use tsg to view the Tree-sitter parse tree of the code
  4. Test the query against the code using tsg
  5. Provide the results along with explanations

Supported languages:

  • bash
  • c
  • cpp
  • css
  • go
  • html
  • java
  • javascript
  • js
  • php
  • py
  • python
  • ruby
  • rust
  • ts
  • tsx
  • typescript
  • yaml
  • yml

Example: <user_query> I need a query to select conditional statement in golang </user_query>

(if_statement consequence: (block) @conditional.inner) @conditional.outer

(if_statement alternative: (block) @conditional.inner)? @conditional.outer

(expression_switch_statement (expression_case) @conditional.inner) @conditional.outer

(type_switch_statement (type_case) @conditional.inner) @conditional.outer

(select_statement (communication_case) @conditional.inner) @conditional.outer

Always remember to:

  • View the tree structure first to understand the syntax nodes
  • Test queries thoroughly before presenting results
  • Explain the query and how it matches the code
  • Suggest improvements or alternatives if the query doesn't work as expected
  • Keep the query simple and generic, do not be too specific. See the examples above
  • Only provide simple inner and outer textobjects

USER

golang loop textobj

ASSISTANT

I'll help you create Tree-sitter textobject queries for Golang loops. Let's first understand the structure of different loop types in Go by examining a sample code with loops.

Tool Calls

Tool: show_tree_structure (ID: tooluse_t9kxixmsSwOuNTEtQ0oLwQ)

language: go

content

package main

func main() {
    // For loop
    for i := 0; i < 10; i++ {
        println(i)
    }
    
    // For-range loop
    nums := []int{1, 2, 3}
    for i, num := range nums {
        println(i, num)
    }
    
    // While-style for loop
    j := 0
    for j < 5 {
        j++
    }
    
    // Infinite loop
    for {
        break
    }
}

TOOL

Tool Result (for call tooluse_t9kxixmsSwOuNTEtQ0oLwQ, tool show_tree_structure):

Command: tsg --lang go

Output: 
(source_file
  (package_clause
    "package"
    (package_identifier "main")
  )
  "\n\n"
  (function_declaration
    "func"
    (identifier "main")
    (parameter_list
      "("
      ")"
    )
    (block
      "{"
      (comment "// For loop")
      (for_statement
        "for"
        (for_clause
          (short_var_declaration
            (expression_list
              (identifier "i")
            )
            ":="
            (expression_list
              (int_literal "0")
            )
          )
          ";"
          (binary_expression
            (identifier "i")
            "<"
            (int_literal "10")
          )
          ";"
          (inc_statement
            (identifier "i")
            "++"
          )
        )
        (block
          "{"
          (expression_statement
            (call_expression
              (identifier "println")
              (argument_list
                "("
                (identifier "i")
                ")"
              )
            )
          )
          "\n"
          "}"
        )
      )
      "\n"
      (comment "// For-range loop")
      (short_var_declaration
        (expression_list
          (identifier "nums")
        )
        ":="
        (expression_list
          (composite_literal
            (slice_type
              "["
              "]"
              (type_identifier "int")
            )
            (literal_value
              "{"
              (literal_element
                (int_literal "1")
              )
              ","
              (literal_element
                (int_literal "2")
              )
              ","
              (literal_element
                (int_literal "3")
              )
              "}"
            )
          )
        )
      )
      "\n"
      (for_statement
        "for"
        (range_clause
          (expression_list
            (identifier "i")
            ","
            (identifier "num")
          )
          ":="
          "range"
          (identifier "nums")
        )
        (block
          "{"
          (expression_statement
            (call_expression
              (identifier "println")
              (argument_list
                "("
                (identifier "i")
                ","
                (identifier "num")
                ")"
              )
            )
          )
          "\n"
          "}"
        )
      )
      "\n"
      (comment "// While-style for loop")
      (short_var_declaration
        (expression_list
          (identifier "j")
        )
        ":="
        (expression_list
          (int_literal "0")
        )
      )
      "\n"
      (for_statement
        "for"
        (binary_expression
          (identifier "j")
          "<"
          (int_literal "5")
        )
        (block
          "{"
          (inc_statement
            (identifier "j")
            "++"
          )
          "\n"
          "}"
        )
      )
      "\n"
      (comment "// Infinite loop")
      (for_statement
        "for"
        (block
          "{"
          (break_statement
            "break"
          )
          "\n"
          "}"
        )
      )
      "\n"
      "}"
    )
  )
)

ASSISTANT

Now I'll create a query for loop textobjects in Go and test it on the sample code:

Tool Calls

Tool: run_query (ID: tooluse_rVEvqr5DQSyiWOzTmoEwvw)

language: go

content

package main

func main() {
    // For loop
    for i := 0; i < 10; i++ {
        println(i)
    }
    
    // For-range loop
    nums := []int{1, 2, 3}
    for i, num := range nums {
        println(i, num)
    }
    
    // While-style for loop
    j := 0
    for j < 5 {
        j++
    }
    
    // Infinite loop
    for {
        break
    }
}

query

;; Standard for loop
(for_statement
  (for_clause)
  (block) @loop.inner) @loop.outer

;; For-range loop
(for_statement
  (range_clause)
  (block) @loop.inner) @loop.outer

;; Condition-only for loop (while-style)
(for_statement
  (binary_expression)
  (block) @loop.inner) @loop.outer

;; Infinite for loop
(for_statement
  (block) @loop.inner) @loop.outer

TOOL

Tool Result (for call tooluse_rVEvqr5DQSyiWOzTmoEwvw, tool run_query):

Command: tsg --lang go --query ';; Standard for loop (for_statement (for_clause) (block) @loop.inner) @loop.outer ;; For-range loop (for_statement (range_clause) (block) @loop.inner) @loop.outer ;; Condition-only for loop (while-style) (for_statement (binary_expression) (block) @loop.inner) @loop.outer ;; Infinite for loop (for_statement (block) @loop.inner) @loop.outer'

Output: 
No matches found

ASSISTANT

It seems the query formatting might have issues. Let's try a simplified version:

Tool Calls

Tool: run_query (ID: tooluse_F_M20Ev2Rc2vuw3ucNvWRw)

content

package main

func main() {
    // For loop
    for i := 0; i < 10; i++ {
        println(i)
    }
    
    // For-range loop
    nums := []int{1, 2, 3}
    for i, num := range nums {
        println(i, num)
    }
    
    // While-style for loop
    j := 0
    for j < 5 {
        j++
    }
    
    // Infinite loop
    for {
        break
    }
}

query

(for_statement
  (block) @loop.inner) @loop.outer

language: go

TOOL

Tool Result (for call tooluse_F_M20Ev2Rc2vuw3ucNvWRw, tool run_query):

Command: tsg --lang go --query '(for_statement (block) @loop.inner) @loop.outer'

Output: 
@loop.outer
start: 5:4
end: 7:5
content:
for i := 0; i < 10; i++ {
        println(i)
    }

@loop.inner
start: 5:28
end: 7:5
content:
{
        println(i)
    }


@loop.outer
start: 11:4
end: 13:5
content:
for i, num := range nums {
        println(i, num)
    }

@loop.inner
start: 11:29
end: 13:5
content:
{
        println(i, num)
    }


@loop.outer
start: 17:4
end: 19:5
content:
for j < 5 {
        j++
    }

@loop.inner
start: 17:14
end: 19:5
content:
{
        j++
    }


@loop.outer
start: 22:4
end: 24:5
content:
for {
        break
    }

@loop.inner
start: 22:8
end: 24:5
content:
{
        break
    }

ASSISTANT

Perfect! The basic query works for all types of Go loops. Now let's refine it to be clearer and more specific for the different loop types in Go:

Tool Calls

Tool: run_query (ID: tooluse_8Cwybz-SRIOLpTZtEZ4TTg)

language: go

content

package main

func main() {
    // For loop
    for i := 0; i < 10; i++ {
        println(i)
    }
    
    // For-range loop
    nums := []int{1, 2, 3}
    for i, num := range nums {
        println(i, num)
    }
    
    // While-style for loop
    j := 0
    for j < 5 {
        j++
    }
    
    // Infinite loop
    for {
        break
    }
}

query

;; All for loops (captures all types)
(for_statement
  (block) @loop.inner) @loop.outer

TOOL

Tool Result (for call tooluse_8Cwybz-SRIOLpTZtEZ4TTg, tool run_query):

Command: tsg --lang go --query ';; All for loops (captures all types) (for_statement (block) @loop.inner) @loop.outer'

Output: 
No matches found

ASSISTANT

Let's try again with a clean query without comments:

Tool Calls

Tool: run_query (ID: tooluse_1xXyzr5lTdm_pkersUXRYA)

language: go

content

package main

func main() {
    // For loop
    for i := 0; i < 10; i++ {
        println(i)
    }
    
    // For-range loop
    nums := []int{1, 2, 3}
    for i, num := range nums {
        println(i, num)
    }
    
    // While-style for loop
    j := 0
    for j < 5 {
        j++
    }
    
    // Infinite loop
    for {
        break
    }
}

query

(for_statement
  (block) @loop.inner) @loop.outer

TOOL

Tool Result (for call tooluse_1xXyzr5lTdm_pkersUXRYA, tool run_query):

Command: tsg --lang go --query '(for_statement (block) @loop.inner) @loop.outer'

Output: 
@loop.outer
start: 5:4
end: 7:5
content:
for i := 0; i < 10; i++ {
        println(i)
    }

@loop.inner
start: 5:28
end: 7:5
content:
{
        println(i)
    }


@loop.outer
start: 11:4
end: 13:5
content:
for i, num := range nums {
        println(i, num)
    }

@loop.inner
start: 11:29
end: 13:5
content:
{
        println(i, num)
    }


@loop.outer
start: 17:4
end: 19:5
content:
for j < 5 {
        j++
    }

@loop.inner
start: 17:14
end: 19:5
content:
{
        j++
    }


@loop.outer
start: 22:4
end: 24:5
content:
for {
        break
    }

@loop.inner
start: 22:8
end: 24:5
content:
{
        break
    }

ASSISTANT

Great! Here's the Tree-sitter query for Go loop textobjects:

(for_statement
  (block) @loop.inner) @loop.outer

This query works for all types of Go loops:

  1. Standard for loops with initialization, condition, and increment:

    for i := 0; i < 10; i++ { ... }
  2. For-range loops:

    for i, num := range nums { ... }
  3. While-style loops (condition-only for loops):

    for j < 5 { ... }
  4. Infinite loops:

    for { ... }

The query captures:

  • @loop.outer: The entire for statement including the loop keyword, any conditions/clauses, and the block
  • @loop.inner: Just the block contents (the body of the loop)

This is a simple but effective textobject query that handles all Go loop variants with a single pattern.

USER

do the same for rust

ASSISTANT

I'll help you create Tree-sitter textobject queries for Rust loops. Let's first understand the structure of different loop types in Rust by examining a sample code with loops.

Tool Calls

Tool: show_tree_structure (ID: tooluse_KM0RC8GETj6EJ4uT8RQqEQ)

language: rust

content

fn main() {
    // For loop
    for i in 0..10 {
        println!("{}", i);
    }
    
    // While loop
    let mut j = 0;
    while j < 5 {
        j += 1;
    }
    
    // Loop (infinite)
    loop {
        break;
    }
    
    // While let loop
    let mut optional = Some(0);
    while let Some(i) = optional {
        if i > 3 {
            optional = None;
        } else {
            optional = Some(i + 1);
        }
    }
    
    // For loop with pattern
    let pairs = vec![(1, 2), (3, 4)];
    for (x, y) in pairs {
        println!("({}, {})", x, y);
    }
}

TOOL

Tool Result (for call tooluse_KM0RC8GETj6EJ4uT8RQqEQ, tool show_tree_structure):

Command: tsg --lang rust

Output: 
(source_file
  (function_item
    "fn"
    (identifier "main")
    (parameters
      "("
      ")"
    )
    (block
      "{"
      (line_comment
        "//"
      )
      (expression_statement
        (for_expression
          "for"
          (identifier "i")
          "in"
          (range_expression
            (integer_literal "0")
            ".."
            (integer_literal "10")
          )
          (block
            "{"
            (expression_statement
              (macro_invocation
                (identifier "println")
                "!"
                (token_tree
                  "("
                  (string_literal
                    """
                    (string_content "{}")
                    """
                  )
                  ","
                  (identifier "i")
                  ")"
                )
              )
              ";"
            )
            "}"
          )
        )
      )
      (line_comment
        "//"
      )
      (let_declaration
        "let"
        (mutable_specifier "mut")
        (identifier "j")
        "="
        (integer_literal "0")
        ";"
      )
      (expression_statement
        (while_expression
          "while"
          (binary_expression
            (identifier "j")
            "<"
            (integer_literal "5")
          )
          (block
            "{"
            (expression_statement
              (compound_assignment_expr
                (identifier "j")
                "+="
                (integer_literal "1")
              )
              ";"
            )
            "}"
          )
        )
      )
      (line_comment
        "//"
      )
      (expression_statement
        (loop_expression
          "loop"
          (block
            "{"
            (expression_statement
              (break_expression
                "break"
              )
              ";"
            )
            "}"
          )
        )
      )
      (line_comment
        "//"
      )
      (let_declaration
        "let"
        (mutable_specifier "mut")
        (identifier "optional")
        "="
        (call_expression
          (identifier "Some")
          (arguments
            "("
            (integer_literal "0")
            ")"
          )
        )
        ";"
      )
      (expression_statement
        (while_expression
          "while"
          (let_condition
            "let"
            (tuple_struct_pattern
              (identifier "Some")
              "("
              (identifier "i")
              ")"
            )
            "="
            (identifier "optional")
          )
          (block
            "{"
            (expression_statement
              (if_expression
                "if"
                (binary_expression
                  (identifier "i")
                  ">"
                  (integer_literal "3")
                )
                (block
                  "{"
                  (expression_statement
                    (assignment_expression
                      (identifier "optional")
                      "="
                      (identifier "None")
                    )
                    ";"
                  )
                  "}"
                )
                (else_clause
                  "else"
                  (block
                    "{"
                    (expression_statement
                      (assignment_expression
                        (identifier "optional")
                        "="
                        (call_expression
                          (identifier "Some")
                          (arguments
                            "("
                            (binary_expression
                              (identifier "i")
                              "+"
                              (integer_literal "1")
                            )
                            ")"
                          )
                        )
                      )
                      ";"
                    )
                    "}"
                  )
                )
              )
            )
            "}"
          )
        )
      )
      (line_comment
        "//"
      )
      (let_declaration
        "let"
        (identifier "pairs")
        "="
        (macro_invocation
          (identifier "vec")
          "!"
          (token_tree
            "["
            (token_tree
              "("
              (integer_literal "1")
              ","
              (integer_literal "2")
              ")"
            )
            ","
            (token_tree
              "("
              (integer_literal "3")
              ","
              (integer_literal "4")
              ")"
            )
            "]"
          )
        )
        ";"
      )
      (expression_statement
        (for_expression
          "for"
          (tuple_pattern
            "("
            (identifier "x")
            ","
            (identifier "y")
            ")"
          )
          "in"
          (identifier "pairs")
          (block
            "{"
            (expression_statement
              (macro_invocation
                (identifier "println")
                "!"
                (token_tree
                  "("
                  (string_literal
                    """
                    (string_content "({}, {})")
                    """
                  )
                  ","
                  (identifier "x")
                  ","
                  (identifier "y")
                  ")"
                )
              )
              ";"
            )
            "}"
          )
        )
      )
      "}"
    )
  )
)

ASSISTANT

Now I'll create a query for loop textobjects in Rust and test it on the sample code:

Tool Calls

Tool: run_query (ID: tooluse_2Hm2hAiiTSGMf-i2H5YgCA)

language: rust

content

fn main() {
    // For loop
    for i in 0..10 {
        println!("{}", i);
    }
    
    // While loop
    let mut j = 0;
    while j < 5 {
        j += 1;
    }
    
    // Loop (infinite)
    loop {
        break;
    }
    
    // While let loop
    let mut optional = Some(0);
    while let Some(i) = optional {
        if i > 3 {
            optional = None;
        } else {
            optional = Some(i + 1);
        }
    }
    
    // For loop with pattern
    let pairs = vec![(1, 2), (3, 4)];
    for (x, y) in pairs {
        println!("({}, {})", x, y);
    }
}

query

(for_expression
  (block) @loop.inner) @loop.outer

(while_expression
  (block) @loop.inner) @loop.outer

(loop_expression
  (block) @loop.inner) @loop.outer

TOOL

Tool Result (for call tooluse_2Hm2hAiiTSGMf-i2H5YgCA, tool run_query):

Command: tsg --lang rust --query '(for_expression (block) @loop.inner) @loop.outer (while_expression (block) @loop.inner) @loop.outer (loop_expression (block) @loop.inner) @loop.outer'

Output: 
@loop.outer
start: 3:4
end: 5:5
content:
for i in 0..10 {
        println!("{}", i);
    }

@loop.inner
start: 3:19
end: 5:5
content:
{
        println!("{}", i);
    }


@loop.outer
start: 9:4
end: 11:5
content:
while j < 5 {
        j += 1;
    }

@loop.inner
start: 9:16
end: 11:5
content:
{
        j += 1;
    }


@loop.outer
start: 14:4
end: 16:5
content:
loop {
        break;
    }

@loop.inner
start: 14:9
end: 16:5
content:
{
        break;
    }


@loop.outer
start: 20:4
end: 26:5
content:
while let Some(i) = optional {
        if i > 3 {
            optional = None;
        } else {
            optional = Some(i + 1);
        }
    }

@loop.inner
start: 20:33
end: 26:5
content:
{
        if i > 3 {
            optional = None;
        } else {
            optional = Some(i + 1);
        }
    }


@loop.outer
start: 30:4
end: 32:5
content:
for (x, y) in pairs {
        println!("({}, {})", x, y);
    }

@loop.inner
start: 30:24
end: 32:5
content:
{
        println!("({}, {})", x, y);
    }

ASSISTANT

Perfect! The query works for all types of Rust loops. Let's finalize the query definition:

Tool Calls

Tool: run_query (ID: tooluse_WCS2bdseTcyvPMe0Zt7WNw)

language: rust

content

fn main() {
    // For loop
    for i in 0..10 {
        println!("{}", i);
    }
    
    // While loop
    let mut j = 0;
    while j < 5 {
        j += 1;
    }
    
    // Loop (infinite)
    loop {
        break;
    }
    
    // While let loop
    let mut optional = Some(0);
    while let Some(i) = optional {
        if i > 3 {
            optional = None;
        } else {
            optional = Some(i + 1);
        }
    }
}

query

(for_expression
  (block) @loop.inner) @loop.outer

(while_expression
  (block) @loop.inner) @loop.outer

(loop_expression
  (block) @loop.inner) @loop.outer

TOOL

Tool Result (for call tooluse_WCS2bdseTcyvPMe0Zt7WNw, tool run_query):

Command: tsg --lang rust --query '(for_expression (block) @loop.inner) @loop.outer (while_expression (block) @loop.inner) @loop.outer (loop_expression (block) @loop.inner) @loop.outer'

Output: 
@loop.outer
start: 3:4
end: 5:5
content:
for i in 0..10 {
        println!("{}", i);
    }

@loop.inner
start: 3:19
end: 5:5
content:
{
        println!("{}", i);
    }


@loop.outer
start: 9:4
end: 11:5
content:
while j < 5 {
        j += 1;
    }

@loop.inner
start: 9:16
end: 11:5
content:
{
        j += 1;
    }


@loop.outer
start: 14:4
end: 16:5
content:
loop {
        break;
    }

@loop.inner
start: 14:9
end: 16:5
content:
{
        break;
    }


@loop.outer
start: 20:4
end: 26:5
content:
while let Some(i) = optional {
        if i > 3 {
            optional = None;
        } else {
            optional = Some(i + 1);
        }
    }

@loop.inner
start: 20:33
end: 26:5
content:
{
        if i > 3 {
            optional = None;
        } else {
            optional = Some(i + 1);
        }
    }

ASSISTANT

Great! Here's the Tree-sitter query for Rust loop textobjects:

(for_expression
  (block) @loop.inner) @loop.outer

(while_expression
  (block) @loop.inner) @loop.outer

(loop_expression
  (block) @loop.inner) @loop.outer

This query works for all types of Rust loops:

  1. For loops:

    for i in 0..10 { ... }

    Including for loops with patterns:

    for (x, y) in pairs { ... }
  2. While loops:

    while j < 5 { ... }

    Including while-let loops:

    while let Some(i) = optional { ... }
  3. Infinite loops (using the loop keyword):

    loop { ... }

The query captures:

  • @loop.outer: The entire loop expression including the loop keyword, any conditions/iterators, and the block
  • @loop.inner: Just the block contents (the body of the loop)

This query effectively handles all Rust loop variants with three simple patterns, giving you inner and outer text objects for each loop type.

USER

now for python

ASSISTANT

I'll help you create Tree-sitter textobject queries for Python loops. Let's first understand the structure of different loop types in Python by examining a sample code with loops.

Tool Calls

Tool: show_tree_structure (ID: tooluse_SNmidszwRQa7Z-puPteApw)

language: python

content

def main():
    # For loop
    for i in range(10):
        print(i)
    
    # For loop with else
    for i in range(5):
        print(i)
    else:
        print("Loop completed")
    
    # While loop
    j = 0
    while j < 5:
        j += 1
        print(j)
    
    # While loop with else
    k = 0
    while k < 3:
        k += 1
    else:
        print("While loop completed")
    
    # Nested loops
    for i in range(3):
        for j in range(2):
            print(i, j)
    
    # For loop with comprehension
    squares = [x*x for x in range(10)]
    
    # Dictionary comprehension
    square_dict = {x: x*x for x in range(5)}

if __name__ == "__main__":
    main()

TOOL

Tool Result (for call tooluse_SNmidszwRQa7Z-puPteApw, tool show_tree_structure):

Command: tree-sitter-debugger --lang python

Output: 
(module
  (function_definition
    "def"
    (identifier "main")
    (parameters
      "("
      ")"
    )
    ":"
    (comment "# For loop")
    (block
      (for_statement
        "for"
        (identifier "i")
        "in"
        (call
          (identifier "range")
          (argument_list
            "("
            (integer "10")
            ")"
          )
        )
        ":"
        (block
          (expression_statement
            (call
              (identifier "print")
              (argument_list
                "("
                (identifier "i")
                ")"
              )
            )
          )
        )
      )
      (comment "# For loop with else")
      (for_statement
        "for"
        (identifier "i")
        "in"
        (call
          (identifier "range")
          (argument_list
            "("
            (integer "5")
            ")"
          )
        )
        ":"
        (block
          (expression_statement
            (call
              (identifier "print")
              (argument_list
                "("
                (identifier "i")
                ")"
              )
            )
          )
        )
        (else_clause
          "else"
          ":"
          (block
            (expression_statement
              (call
                (identifier "print")
                (argument_list
                  "("
                  (string
                    (string_start """)
                    (string_content "Loop completed")
                    (string_end """)
                  )
                  ")"
                )
              )
            )
          )
        )
      )
      (comment "# While loop")
      (expression_statement
        (assignment
          (identifier "j")
          "="
          (integer "0")
        )
      )
      (while_statement
        "while"
        (comparison_operator
          (identifier "j")
          "<"
          (integer "5")
        )
        ":"
        (block
          (expression_statement
            (augmented_assignment
              (identifier "j")
              "+="
              (integer "1")
            )
          )
          (expression_statement
            (call
              (identifier "print")
              (argument_list
                "("
                (identifier "j")
                ")"
              )
            )
          )
        )
      )
      (comment "# While loop with else")
      (expression_statement
        (assignment
          (identifier "k")
          "="
          (integer "0")
        )
      )
      (while_statement
        "while"
        (comparison_operator
          (identifier "k")
          "<"
          (integer "3")
        )
        ":"
        (block
          (expression_statement
            (augmented_assignment
              (identifier "k")
              "+="
              (integer "1")
            )
          )
        )
        (else_clause
          "else"
          ":"
          (block
            (expression_statement
              (call
                (identifier "print")
                (argument_list
                  "("
                  (string
                    (string_start """)
                    (string_content "While loop completed")
                    (string_end """)
                  )
                  ")"
                )
              )
            )
          )
        )
      )
      (comment "# Nested loops")
      (for_statement
        "for"
        (identifier "i")
        "in"
        (call
          (identifier "range")
          (argument_list
            "("
            (integer "3")
            ")"
          )
        )
        ":"
        (block
          (for_statement
            "for"
            (identifier "j")
            "in"
            (call
              (identifier "range")
              (argument_list
                "("
                (integer "2")
                ")"
              )
            )
            ":"
            (block
              (expression_statement
                (call
                  (identifier "print")
                  (argument_list
                    "("
                    (identifier "i")
                    ","
                    (identifier "j")
                    ")"
                  )
                )
              )
            )
          )
        )
      )
      (comment "# For loop with comprehension")
      (expression_statement
        (assignment
          (identifier "squares")
          "="
          (list_comprehension
            "["
            (binary_operator
              (identifier "x")
              "*"
              (identifier "x")
            )
            (for_in_clause
              "for"
              (identifier "x")
              "in"
              (call
                (identifier "range")
                (argument_list
                  "("
                  (integer "10")
                  ")"
                )
              )
            )
            "]"
          )
        )
      )
      (comment "# Dictionary comprehension")
      (expression_statement
        (assignment
          (identifier "square_dict")
          "="
          (dictionary_comprehension
            "{"
            (pair
              (identifier "x")
              ":"
              (binary_operator
                (identifier "x")
                "*"
                (identifier "x")
              )
            )
            (for_in_clause
              "for"
              (identifier "x")
              "in"
              (call
                (identifier "range")
                (argument_list
                  "("
                  (integer "5")
                  ")"
                )
              )
            )
            "}"
          )
        )
      )
    )
  )
  (if_statement
    "if"
    (comparison_operator
      (identifier "__name__")
      "=="
      (string
        (string_start """)
        (string_content "__main__")
        (string_end """)
      )
    )
    ":"
    (block
      (expression_statement
        (call
          (identifier "main")
          (argument_list
            "("
            ")"
          )
        )
      )
    )
  )
)

ASSISTANT

Now I'll create a query for loop textobjects in Python and test it on the sample code:

Tool Calls

Tool: run_query (ID: tooluse_l7NsioBkQzCsPNn2ObLCXQ)

language: python

content

def main():
    # For loop
    for i in range(10):
        print(i)
    
    # For loop with else
    for i in range(5):
        print(i)
    else:
        print("Loop completed")
    
    # While loop
    j = 0
    while j < 5:
        j += 1
        print(j)
    
    # While loop with else
    k = 0
    while k < 3:
        k += 1
    else:
        print("While loop completed")
    
    # Nested loops
    for i in range(3):
        for j in range(2):
            print(i, j)
    
    # For loop with comprehension
    squares = [x*x for x in range(10)]
    
    # Dictionary comprehension
    square_dict = {x: x*x for x in range(5)}

if __name__ == "__main__":
    main()

query

(for_statement
  (block) @loop.inner) @loop.outer

(while_statement
  (block) @loop.inner) @loop.outer

(list_comprehension
  (for_in_clause) @loop.inner) @loop.outer

(dictionary_comprehension
  (for_in_clause) @loop.inner) @loop.outer

TOOL

Tool Result (for call tooluse_l7NsioBkQzCsPNn2ObLCXQ, tool run_query):

Command: tree-sitter-debugger --lang python --query '(for_statement (block) @loop.inner) @loop.outer (while_statement (block) @loop.inner) @loop.outer (list_comprehension (for_in_clause) @loop.inner) @loop.outer (dictionary_comprehension (for_in_clause) @loop.inner) @loop.outer'

Output: 
@loop.outer
start: 3:4
end: 4:16
content:
for i in range(10):
        print(i)

@loop.inner
start: 4:8
end: 4:16
content:
print(i)


@loop.outer
start: 7:4
end: 10:31
content:
for i in range(5):
        print(i)
    else:
        print("Loop completed")

@loop.inner
start: 8:8
end: 8:16
content:
print(i)


@loop.outer
start: 14:4
end: 16:16
content:
while j < 5:
        j += 1
        print(j)

@loop.inner
start: 15:8
end: 16:16
content:
j += 1
        print(j)


@loop.outer
start: 20:4
end: 23:37
content:
while k < 3:
        k += 1
    else:
        print("While loop completed")

@loop.inner
start: 21:8
end: 21:14
content:
k += 1


@loop.outer
start: 26:4
end: 28:23
content:
for i in range(3):
        for j in range(2):
            print(i, j)

@loop.inner
start: 27:8
end: 28:23
content:
for j in range(2):
            print(i, j)


@loop.outer
start: 27:8
end: 28:23
content:
for j in range(2):
            print(i, j)

@loop.inner
start: 28:12
end: 28:23
content:
print(i, j)


@loop.outer
start: 31:14
end: 31:38
content:
[x*x for x in range(10)]

@loop.inner
start: 31:19
end: 31:37
content:
for x in range(10)


@loop.outer
start: 34:18
end: 34:44
content:
{x: x*x for x in range(5)}

@loop.inner
start: 34:26
end: 34:43
content:
for x in range(5)

ASSISTANT

The query is almost correct, but there are some adjustments needed, especially for comprehensions. Let me refine the query:

Tool Calls

Tool: run_query (ID: tooluse_TzT4zap7RQGSwTOk6CuFzg)

content

def main():
    # For loop
    for i in range(10):
        print(i)
    
    # For loop with else
    for i in range(5):
        print(i)
    else:
        print("Loop completed")
    
    # While loop
    j = 0
    while j < 5:
        j += 1
        print(j)
    
    # While loop with else
    k = 0
    while k < 3:
        k += 1
    else:
        print("While loop completed")
    
    # Nested loops
    for i in range(3):
        for j in range(2):
            print(i, j)

if __name__ == "__main__":
    main()

query

(for_statement
  body: (block) @loop.inner) @loop.outer

(while_statement
  body: (block) @loop.inner) @loop.outer

language: python

TOOL

Tool Result (for call tooluse_TzT4zap7RQGSwTOk6CuFzg, tool run_query):

Command: tree-sitter-debugger --lang python --query '(for_statement body: (block) @loop.inner) @loop.outer (while_statement body: (block) @loop.inner) @loop.outer'

Output: 
@loop.outer
start: 3:4
end: 4:16
content:
for i in range(10):
        print(i)

@loop.inner
start: 4:8
end: 4:16
content:
print(i)


@loop.outer
start: 7:4
end: 10:31
content:
for i in range(5):
        print(i)
    else:
        print("Loop completed")

@loop.inner
start: 8:8
end: 8:16
content:
print(i)


@loop.outer
start: 14:4
end: 16:16
content:
while j < 5:
        j += 1
        print(j)

@loop.inner
start: 15:8
end: 16:16
content:
j += 1
        print(j)


@loop.outer
start: 20:4
end: 23:37
content:
while k < 3:
        k += 1
    else:
        print("While loop completed")

@loop.inner
start: 21:8
end: 21:14
content:
k += 1


@loop.outer
start: 26:4
end: 28:23
content:
for i in range(3):
        for j in range(2):
            print(i, j)

@loop.inner
start: 27:8
end: 28:23
content:
for j in range(2):
            print(i, j)


@loop.outer
start: 27:8
end: 28:23
content:
for j in range(2):
            print(i, j)

@loop.inner
start: 28:12
end: 28:23
content:
print(i, j)

ASSISTANT

The query works correctly for the regular for and while loops. Let's try another approach to simplify the query:

Tool Calls

Tool: run_query (ID: tooluse_xe4jIkimSvWPYDVj8H7qbg)

language: python

content

def main():
    # For loop
    for i in range(10):
        print(i)
    
    # For loop with else
    for i in range(5):
        print(i)
    else:
        print("Loop completed")
    
    # While loop
    j = 0
    while j < 5:
        j += 1
        print(j)
    
    # While loop with else
    k = 0
    while k < 3:
        k += 1
    else:
        print("While loop completed")
    
    # Nested loops
    for i in range(3):
        for j in range(2):
            print(i, j)
    
    # For loop with comprehension
    squares = [x*x for x in range(10)]
    
    # Dictionary comprehension
    square_dict = {x: x*x for x in range(5)}

if __name__ == "__main__":
    main()

query

(for_statement
  (block) @loop.inner) @loop.outer

(while_statement
  (block) @loop.inner) @loop.outer

TOOL

Tool Result (for call tooluse_xe4jIkimSvWPYDVj8H7qbg, tool run_query):

Command: tree-sitter-debugger --lang python --query '(for_statement (block) @loop.inner) @loop.outer (while_statement (block) @loop.inner) @loop.outer'

Output: 
@loop.outer
start: 3:4
end: 4:16
content:
for i in range(10):
        print(i)

@loop.inner
start: 4:8
end: 4:16
content:
print(i)


@loop.outer
start: 7:4
end: 10:31
content:
for i in range(5):
        print(i)
    else:
        print("Loop completed")

@loop.inner
start: 8:8
end: 8:16
content:
print(i)


@loop.outer
start: 14:4
end: 16:16
content:
while j < 5:
        j += 1
        print(j)

@loop.inner
start: 15:8
end: 16:16
content:
j += 1
        print(j)


@loop.outer
start: 20:4
end: 23:37
content:
while k < 3:
        k += 1
    else:
        print("While loop completed")

@loop.inner
start: 21:8
end: 21:14
content:
k += 1


@loop.outer
start: 26:4
end: 28:23
content:
for i in range(3):
        for j in range(2):
            print(i, j)

@loop.inner
start: 27:8
end: 28:23
content:
for j in range(2):
            print(i, j)


@loop.outer
start: 27:8
end: 28:23
content:
for j in range(2):
            print(i, j)

@loop.inner
start: 28:12
end: 28:23
content:
print(i, j)

ASSISTANT

Let's add a separate query for comprehensions:

Tool Calls

Tool: run_query (ID: tooluse_hE5SHVuYTC6xbmlMYd3Mkw)

language: python

content

def main():
    # For loop with comprehension
    squares = [x*x for x in range(10)]
    
    # Dictionary comprehension
    square_dict = {x: x*x for x in range(5)}

if __name__ == "__main__":
    main()

query

(list_comprehension) @loop.outer
(dictionary_comprehension) @loop.outer

TOOL

Tool Result (for call tooluse_hE5SHVuYTC6xbmlMYd3Mkw, tool run_query):

Command: tree-sitter-debugger --lang python --query '(list_comprehension) @loop.outer (dictionary_comprehension) @loop.outer'

Output: 
@loop.outer
start: 3:14
end: 3:38
content:
[x*x for x in range(10)]


@loop.outer
start: 6:18
end: 6:44
content:
{x: x*x for x in range(5)}

ASSISTANT

Great! Now I have all the pieces for a complete Python loop textobject query. Let me put it all together:

Tool Calls

Tool: run_query (ID: tooluse_-HP7c0kOSBiUA-Ru8w-T1Q)

language: python

content

def main():
    # For loop
    for i in range(10):
        print(i)
    
    # For loop with else
    for i in range(5):
        print(i)
    else:
        print("Loop completed")
    
    # While loop
    j = 0
    while j < 5:
        j += 1
        print(j)
    
    # While loop with else
    k = 0
    while k < 3:
        k += 1
    else:
        print("While loop completed")
    
    # Nested loops
    for i in range(3):
        for j in range(2):
            print(i, j)
            
    # For loop with comprehension
    squares = [x*x for x in range(10)]
    
    # Dictionary comprehension
    square_dict = {x: x*x for x in range(5)}

if __name__ == "__main__":
    main()

query

(for_statement
  (block) @loop.inner) @loop.outer

(while_statement
  (block) @loop.inner) @loop.outer

(list_comprehension) @loop.outer

(dictionary_comprehension) @loop.outer

TOOL

Tool Result (for call tooluse_-HP7c0kOSBiUA-Ru8w-T1Q, tool run_query):

Command: tree-sitter-debugger --lang python --query '(for_statement (block) @loop.inner) @loop.outer (while_statement (block) @loop.inner) @loop.outer (list_comprehension) @loop.outer (dictionary_comprehension) @loop.outer'

Output: 
@loop.outer
start: 3:4
end: 4:16
content:
for i in range(10):
        print(i)

@loop.inner
start: 4:8
end: 4:16
content:
print(i)


@loop.outer
start: 7:4
end: 10:31
content:
for i in range(5):
        print(i)
    else:
        print("Loop completed")

@loop.inner
start: 8:8
end: 8:16
content:
print(i)


@loop.outer
start: 14:4
end: 16:16
content:
while j < 5:
        j += 1
        print(j)

@loop.inner
start: 15:8
end: 16:16
content:
j += 1
        print(j)


@loop.outer
start: 20:4
end: 23:37
content:
while k < 3:
        k += 1
    else:
        print("While loop completed")

@loop.inner
start: 21:8
end: 21:14
content:
k += 1


@loop.outer
start: 26:4
end: 28:23
content:
for i in range(3):
        for j in range(2):
            print(i, j)

@loop.inner
start: 27:8
end: 28:23
content:
for j in range(2):
            print(i, j)


@loop.outer
start: 27:8
end: 28:23
content:
for j in range(2):
            print(i, j)

@loop.inner
start: 28:12
end: 28:23
content:
print(i, j)


@loop.outer
start: 31:14
end: 31:38
content:
[x*x for x in range(10)]


@loop.outer
start: 34:18
end: 34:44
content:
{x: x*x for x in range(5)}

ASSISTANT

Now let's define inner nodes for comprehensions:

Tool Calls

Tool: run_query (ID: tooluse_BBTods4fSCWlSbK1KGoIHA)

query

(list_comprehension
  (for_in_clause) @loop.inner) @loop.outer

(dictionary_comprehension
  (for_in_clause) @loop.inner) @loop.outer

language: python

content

def main():
    # For loop with comprehension
    squares = [x*x for x in range(10)]
    
    # Dictionary comprehension
    square_dict = {x: x*x for x in range(5)}

if __name__ == "__main__":
    main()

TOOL

Tool Result (for call tooluse_BBTods4fSCWlSbK1KGoIHA, tool run_query):

Command: tree-sitter-debugger --lang python --query '(list_comprehension (for_in_clause) @loop.inner) @loop.outer (dictionary_comprehension (for_in_clause) @loop.inner) @loop.outer'

Output: 
@loop.outer
start: 3:14
end: 3:38
content:
[x*x for x in range(10)]

@loop.inner
start: 3:19
end: 3:37
content:
for x in range(10)


@loop.outer
start: 6:18
end: 6:44
content:
{x: x*x for x in range(5)}

@loop.inner
start: 6:26
end: 6:43
content:
for x in range(5)

ASSISTANT

Perfect! Now I have all the necessary components for a comprehensive Python loop textobject query. Let's finalize it:

Tool Calls

Tool: run_query (ID: tooluse_hzN14UKQQdKTz97Qz5EDMA)

language: python

content

def main():
    # For loop
    for i in range(10):
        print(i)
    
    # For loop with else
    for i in range(5):
        print(i)
    else:
        print("Loop completed")
    
    # While loop
    j = 0
    while j < 5:
        j += 1
        print(j)
    
    # While loop with else
    k = 0
    while k < 3:
        k += 1
    else:
        print("While loop completed")
    
    # Nested loops
    for i in range(3):
        for j in range(2):
            print(i, j)
            
    # For loop with comprehension
    squares = [x*x for x in range(10)]
    
    # Dictionary comprehension
    square_dict = {x: x*x for x in range(5)}

if __name__ == "__main__":
    main()

query

(for_statement
  (block) @loop.inner) @loop.outer

(while_statement
  (block) @loop.inner) @loop.outer

(list_comprehension
  (for_in_clause) @loop.inner) @loop.outer

(dictionary_comprehension
  (for_in_clause) @loop.inner) @loop.outer

TOOL

Tool Result (for call tooluse_hzN14UKQQdKTz97Qz5EDMA, tool run_query):

Command: tree-sitter-debugger --lang python --query '(for_statement (block) @loop.inner) @loop.outer (while_statement (block) @loop.inner) @loop.outer (list_comprehension (for_in_clause) @loop.inner) @loop.outer (dictionary_comprehension (for_in_clause) @loop.inner) @loop.outer'

Output: 
@loop.outer
start: 3:4
end: 4:16
content:
for i in range(10):
        print(i)

@loop.inner
start: 4:8
end: 4:16
content:
print(i)


@loop.outer
start: 7:4
end: 10:31
content:
for i in range(5):
        print(i)
    else:
        print("Loop completed")

@loop.inner
start: 8:8
end: 8:16
content:
print(i)


@loop.outer
start: 14:4
end: 16:16
content:
while j < 5:
        j += 1
        print(j)

@loop.inner
start: 15:8
end: 16:16
content:
j += 1
        print(j)


@loop.outer
start: 20:4
end: 23:37
content:
while k < 3:
        k += 1
    else:
        print("While loop completed")

@loop.inner
start: 21:8
end: 21:14
content:
k += 1


@loop.outer
start: 26:4
end: 28:23
content:
for i in range(3):
        for j in range(2):
            print(i, j)

@loop.inner
start: 27:8
end: 28:23
content:
for j in range(2):
            print(i, j)


@loop.outer
start: 27:8
end: 28:23
content:
for j in range(2):
            print(i, j)

@loop.inner
start: 28:12
end: 28:23
content:
print(i, j)


@loop.outer
start: 31:14
end: 31:38
content:
[x*x for x in range(10)]

@loop.inner
start: 31:19
end: 31:37
content:
for x in range(10)


@loop.outer
start: 34:18
end: 34:44
content:
{x: x*x for x in range(5)}

@loop.inner
start: 34:26
end: 34:43
content:
for x in range(5)

ASSISTANT

Here's the Tree-sitter query for Python loop textobjects:

(for_statement
  (block) @loop.inner) @loop.outer

(while_statement
  (block) @loop.inner) @loop.outer

(list_comprehension
  (for_in_clause) @loop.inner) @loop.outer

(dictionary_comprehension
  (for_in_clause) @loop.inner) @loop.outer

This query works for all types of Python loops:

  1. Regular for loops:

    for i in range(10):
        print(i)

    Including those with else clauses:

    for i in range(5):
        print(i)
    else:
        print("Loop completed")
  2. While loops:

    while j < 5:
        j += 1

    Including those with else clauses:

    while k < 3:
        k += 1
    else:
        print("While loop completed")
  3. List comprehensions:

    squares = [x*x for x in range(10)]
  4. Dictionary comprehensions:

    square_dict = {x: x*x for x in range(5)}

The query captures:

  • For regular loops (for and while):

    • @loop.outer: The entire loop statement including keywords and the body
    • @loop.inner: Just the block contents (the body of the loop)
  • For comprehensions (list and dictionary):

    • @loop.outer: The entire comprehension expression
    • @loop.inner: The for_in_clause part of the comprehension

This query effectively handles all Python loop variants with clear inner and outer textobjects for each loop type.

# Tree sitter esa agent config
name = "Treesiter Query Assistant"
description = "An agent that helps generate textobject queries using tree-sitter-debugger"
system_prompt = """
You are a Treesiter Query Assistant specializing in generating and testing Tree-sitter textobject queries. Your expertise includes:
1. Understanding Tree-sitter syntax and query patterns
2. Creating queries for different programming languages
3. Testing and debugging Tree-sitter queries
4. Understanding Abstract Syntax Tree (AST) structures
When a user asks for a query:
1. Generate a comprehensive Tree-sitter query based on their requirements
2. Save the user's code to a temporary file
3. Use tree-sitter-debugger to view the Tree-sitter parse tree of the code
4. Test the query against the code using tree-sitter-debugger
5. Provide the results along with explanations
{{$tree-sitter-debugger --list-languages}}
Example:
<user_query>
I need a query to select conditional statement in golang
</user_query>
<response>
(if_statement
consequence: (block) @conditional.inner) @conditional.outer
(if_statement
alternative: (block) @conditional.inner)? @conditional.outer
(expression_switch_statement
(expression_case) @conditional.inner) @conditional.outer
(type_switch_statement
(type_case) @conditional.inner) @conditional.outer
(select_statement
(communication_case) @conditional.inner) @conditional.outer
</response>
Always remember to:
- View the tree structure first to understand the syntax nodes
- Test queries thoroughly before presenting results
- Explain the query and how it matches the code
- Suggest improvements or alternatives if the query doesn't work as expected
- Keep the query simple and generic, do not be too specific. See the examples above
- Only provide simple inner and outer textobjects
"""
[[functions]]
name = "show_tree_structure"
description = "Display the Tree-sitter parse tree for a file"
command = "tree-sitter-debugger --lang {{language}}"
stdin = "{{content}}"
safe = true
[[functions.parameters]]
name = "language"
type = "string"
description = "Programming language of the file (e.g., python, javascript, rust)"
required = true
[[functions.parameters]]
name = "content"
type = "string"
description = "Code to show the tree structure of. Always provide valid code"
required = true
[[functions]]
name = "run_query"
description = "Run a Tree-sitter query on a file"
command = "tree-sitter-debugger --lang {{language}} --query '{{query}}'"
stdin = "{{content}}"
safe = true
[[functions.parameters]]
name = "language"
type = "string"
description = "Programming language of the file (e.g., python, javascript, rust)"
required = true
[[functions.parameters]]
name = "query"
type = "string"
description = "Tree-sitter query to run"
required = true
[[functions.parameters]]
name = "content"
type = "string"
description = "Code to show the tree structure of. Always provide valid code"
required = true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment