Created
April 12, 2026 08:52
-
-
Save olimorris/6493844cb2b0a970992e1804d83b6eaf to your computer and use it in GitHub Desktop.
Tree-sitter in Neovim without nvim-treesitter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env ruby | |
| # frozen_string_literal: true | |
| require "fileutils" | |
| require "tmpdir" | |
| TREESITTER_DIR = File.expand_path("~/.local/share/nvim/treesitter") | |
| PARSER_DIR = File.join(TREESITTER_DIR, "parser") | |
| QUERIES_DIR = File.join(TREESITTER_DIR, "queries") | |
| PARSERS = { | |
| "html" => "tree-sitter/tree-sitter-html", | |
| "lua" => "tree-sitter-grammars/tree-sitter-lua", | |
| "python" => "tree-sitter/tree-sitter-python", | |
| "query" => "nvim-treesitter/tree-sitter-query", | |
| "ruby" => "tree-sitter/tree-sitter-ruby", | |
| "toml" => "tree-sitter-grammars/tree-sitter-toml", | |
| "tsx" => "tree-sitter/tree-sitter-typescript:tsx", | |
| "typescript" => "tree-sitter/tree-sitter-typescript:typescript", | |
| "vimdoc" => "neovim/tree-sitter-vimdoc", | |
| "yaml" => "tree-sitter-grammars/tree-sitter-yaml" | |
| }.freeze | |
| def run(*cmd) | |
| system(*cmd) || abort("error: command failed: #{cmd.join(" ")}") | |
| end | |
| def check_tree_sitter! | |
| return if system("command", "-v", "tree-sitter", out: File::NULL, err: File::NULL) | |
| abort("error: tree-sitter-cli not found -- run: brew install tree-sitter") | |
| end | |
| def build_parser(lang, force: false) | |
| existing = Dir.glob(File.join(PARSER_DIR, "#{lang}.{dylib,so}")).first | |
| if existing && !force | |
| puts(" parser -> #{existing} (cached)") | |
| return | |
| end | |
| entry = PARSERS.fetch(lang, "tree-sitter/tree-sitter-#{lang}") | |
| repo, subdir = entry.split(":", 2) | |
| Dir.mktmpdir do |tmp| | |
| src = File.join(tmp, lang) | |
| run("git", "clone", "--depth=1", "https://github.com/#{repo}", src, "-q") | |
| build_dir = subdir ? File.join(src, subdir) : src | |
| Dir.chdir(build_dir) { run("tree-sitter", "build") } | |
| parser_file = Dir.glob(File.join(build_dir, "*.{dylib,so}")).first | |
| abort("error: no parser file found after build for '#{lang}'") unless parser_file | |
| dest = File.join(PARSER_DIR, "#{lang}#{File.extname(parser_file)}") | |
| FileUtils.cp(parser_file, dest) | |
| puts(" parser -> #{dest}") | |
| end | |
| end | |
| def install(langs, force: false) | |
| abort("usage: nvim-treesitter install <lang> [<lang>...]") if langs.empty? | |
| check_tree_sitter! | |
| FileUtils.mkdir_p([QUERIES_DIR, PARSER_DIR]) | |
| puts("Fetching queries...") | |
| Dir.mktmpdir do |tmp| | |
| clone_dir = File.join(tmp, "nvim-treesitter") | |
| paths = langs.map { |l| "runtime/queries/#{l}" } | |
| run( | |
| "git", | |
| "clone", | |
| "--depth=1", | |
| "--filter=blob:none", | |
| "--sparse", | |
| "https://github.com/nvim-treesitter/nvim-treesitter", | |
| clone_dir, | |
| "-q" | |
| ) | |
| run("git", "-C", clone_dir, "sparse-checkout", "set", *paths) | |
| langs.each do |lang| | |
| src = File.join(clone_dir, "runtime/queries", lang) | |
| dest = File.join(QUERIES_DIR, lang) | |
| if !Dir.exist?(src) | |
| warn(" warning: no queries found for '#{lang}' in nvim-treesitter") | |
| elsif !Dir.exist?(dest) || force | |
| FileUtils.cp_r(src, dest) | |
| puts(" + #{lang}") | |
| end | |
| end | |
| end | |
| puts("Building parsers...") | |
| langs.each do |lang| | |
| puts(" #{lang}") | |
| build_parser(lang, force: force) | |
| end | |
| puts("\nDone!") | |
| end | |
| def sync(force: false) | |
| install(PARSERS.keys, force: force) | |
| end | |
| case ARGV.shift | |
| when "sync" | |
| sync(force: ARGV.delete("--force")) | |
| when "install" | |
| install(ARGV) | |
| else | |
| puts( | |
| <<~USAGE | |
| nvim-treesitter -- manage tree-sitter parsers and queries for Neovim | |
| Usage: | |
| nvim-treesitter sync [--force] fetch all queries + build parsers | |
| nvim-treesitter install <lang>... fetch queries + build parser for specific language(s) | |
| Add to init.lua: | |
| vim.opt.runtimepath:prepend(vim.fn.expand("~/.local/share/nvim/treesitter")) | |
| USAGE | |
| ) | |
| end |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This script downloads the parsers from their respective repos and pulls in the queries from the archived nvim-treesitter, building them with Tree-sitter.
Ensure the parsers are available in your Neovim config with:
and ensure you're starting Tree-sitter in Neovim with: