Skip to content

Instantly share code, notes, and snippets.

@olimorris
Created April 12, 2026 08:52
Show Gist options
  • Select an option

  • Save olimorris/6493844cb2b0a970992e1804d83b6eaf to your computer and use it in GitHub Desktop.

Select an option

Save olimorris/6493844cb2b0a970992e1804d83b6eaf to your computer and use it in GitHub Desktop.
Tree-sitter in Neovim without nvim-treesitter
#!/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
@olimorris
Copy link
Copy Markdown
Author

olimorris commented Apr 12, 2026

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:

vim.opt.runtimepath:prepend(vim.fn.expand("~/.local/share/nvim/treesitter"))

and ensure you're starting Tree-sitter in Neovim with:

vim.api.nvim_create_autocmd("FileType", {
  callback = function(args)
    pcall(vim.treesitter.start, args.buf)
  end,
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment