[vim/vim] feat: treesitter integration (PR #18869)

10 views
Skip to first unread message

Foxe Chen

unread,
Dec 5, 2025, 10:21:30 PM (2 days ago) Dec 5
to vim/vim, Subscribed

This PR doesn't actually do anything useful for the end user, all it does is just expose the treesitter C API through vimscript, which is what Neovim does except they use Lua. I suppose the end game plan is to create a vim9 script pack plugin that uses the API to actually do useful things.

To expose the treesitter API to vimscript, I created a new type called "opaque", which is similar to Lua's userdata if you know what that is. All they do is just bring reference counting to a block of memory + I also added properties to them (which are essentially the same as class members). They have full type checking at both runtime and compile time (for vim9 script), including the properties.

I haven't added tests yet so be mindful that it may be a little buggy/segfaulty.

Here's a demo script that highlights string literals using the C parser (just run g:RunParse(<buffer number>), also ignore the red highlighting on the function names, the syntax files have not been updated):

vim9script

ts_load("c", "/usr/lib/tree_sitter/c.so") # Update to where the parser library is

var parser: opaque<TSParser> = tsparser_new()

tsparser_set_language(parser, "c")

prop_type_add('literal', {'highlight': 'ErrorMsg'})

def g:RunParse(buf: number): opaque<TSQueryCursor>
    var res: opaque<TSTree>

    # There is a timeout limit for parsing (here 3 milliseconds), meaning we can implement async parsing: 
    while true
        var tree: opaque<TSTree> = tsparser_parse_buf(parser, buf, 3)

        if tree == null_opaque
            continue
        endif

        res = tree
        break
    endwhile

    var node: opaque<TSNode> = tstree_root_node(res)

    var querystr: list<string> =<< trim END
    (string_literal) @string
    END

    var query: opaque<TSQuery> = tsquery_new("c", join(querystr, "\n"))
    var cursor: opaque<TSQueryCursor> = tsquerycursor_new()

    tsquerycursor_exec(cursor, query, node)

    while tsquerycursor_next_match(cursor)
        for i in range(0, len(cursor.match_captures) - 1)
            var capture: opaque<TSNode> = cursor.match_captures[i][0]
            var start: tuple<number, number> = capture.start_point
            var end: tuple<number, number> = capture.end_point

            prop_add(start[0] + 1, start[1] + 1, {
                type: 'literal',
                end_lnum: end[0] + 1,
                end_col: end[1] + 1
            })
        endfor
    endwhile

    return cursor
enddef

TODO:

  • Add ability to load WASM parser modules
  • Add tests
  • Add more API functions

You can view, comment on, or merge this pull request online at:

  https://github.com/vim/vim/pull/18869

Commit Summary

File Changes

(41 files)

Patch Links:


Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/pull/18869@github.com>

Reply all
Reply to author
Forward
0 new messages