Di episode ini kita akan membongkar cara kerja syntax highlighting modern berbasis Abstract Syntax Tree (AST) dengan nvim-treesitter, mulai dari instalasi parser, highlighting, incremental selection, code folding, hingga textobjects berbasis grammar.

Setelah di episode 13 sebelumnya kita membahas akselerasi navigasi dengan flash.nvim berbasis Treesitter, menandai file favorit dengan harpoon, hingga manajemen session — pada episode kali ini kita akan membahas fondasi yang sebenarnya membuat semua itu bisa berjalan: nvim-treesitter, mesin parsing kode berkecepatan tinggi yang menjadi otak dari semua fitur "pintar" di Neovim modern.
Sekarang bayangkan kalian bekerja di tim yang menggunakan banyak bahasa: TypeScript untuk frontend, Go untuk backend, Python untuk data pipeline. Setiap kali kalian menekan satu tombol di editor, editor harus mengenali apakah sebuah baris adalah deklarasi fungsi, pemanggilan method, atau hanya komentar — dalam hitungan milidetik, di tengah file yang mungkin puluhan ribu baris. Kemampuan editor untuk mengenali struktur kode secara akurat inilah yang membedakan editor "buta" (yang sekadar mewarnai teks) dengan editor "pintar" (yang memahami grammar bahasa). Episode ini akan membawa kalian memahami mekanisme di balik layar tersebut, lalu membangun fondasi untuk episode-episode selanjutnya seperti LSP, autocompletion, dan formatting.
Sebelum Treesitter, Neovim (dan Vim) mewarnai kode dengan pendekatan yang jauh lebih primitif: regex (regular expression). Ide dasarnya sederhana — cari pola teks yang cocok, lalu beri warna. Tampaknya masuk akal, tapi cara ini punya tiga kelemahan fatal di dunia nyata:
// adalah komentar, URL di dalam string, atau pembagi dalam regex itu sendiri. Akibatnya, highlighting sering salah — misalnya string diwarnai hijau padahal sudah tertutup, atau komentar tidak berhenti di tempat yang benar.Bayangkan regex itu seperti seorang penerjemah yang hanya tahu kata per kata tanpa memahami tata bahasa. Dia bisa menerjemahkan kosakata, tapi akan salah menangkap makna ketika kalimatnya kompleks.
Solusi yang dipakai semua IDE modern (VS Code, JetBrains, bahkan editor web seperti Monaco) adalah parsing nyata: mengurai kode menjadi struktur pohon yang disebut Abstract Syntax Tree (AST).
AST adalah representasi hierarkis dari kode kalian. Setiap konstruksi bahasa — function, if, for, class, ekspresi, literal string — menjadi sebuah node di dalam pohon, lengkap dengan posisi awal dan akhirnya di dalam file. Analogikan dengan dokumen peraturan hukum: daripada membaca satu kalimat demi satu kalimat, kalian punya bagan yang menunjukkan "Pasal 1 ayat 3 memiliki beberapa poin, dan poin itu berisi sub-poin". Editor yang memahami AST bisa menjawab pertanyaan struktural dengan sangat presisi: "apa fungsi terluar yang berisi kursor saya?" atau "di mana blok if ini dimulai dan berakhir?"
Nah, tree-sitter adalah sebuah incremental parser. Ini dua kata kunci yang wajib kalian pahami:
Kombinasi keduanya menghasilkan keajaiban: highlighting yang akurat secara struktural dan tetap responsif bahkan di file raksasa, karena biaya parsing saat mengetik sangat kecil.
Sejak Neovim 0.5, Treesitter terintegrasi langsung di dalam kernel (API vim.treesitter). Yang dilakukan plugin nvim-treesitter adalah: mengelola parser (grammar bahasa) yang dikompilasi sebagai library native, lalu menyediakan modul-modul fitur di atasnya — highlighting, incremental selection, indent, folding, dst.
Setiap bahasa punya parser terpisah. Inilah kenapa ada perintah :TSInstall <language> — kalian menginstal grammar bahasa tertentu satu per satu. Parser dikompilasi dari source grammar (yang ditulis dalam C) menjadi file .so saat instalasi pertama, jadi toolchain C compiler wajib ada di sistem kalian — kita akan bahas ini di bagian kesalahan umum.
Karena episode 9 kita sudah membangun lazy.nvim, sekarang saatnya menambahkan plugin ini. Berikut spec lengkapnya:
return {
{
"nvim-treesitter/nvim-treesitter",
version = false,
build = ":TSUpdate",
event = { "BufReadPre", "BufNewFile" },
config = function()
require("nvim-treesitter").setup({
ensure_installed = {
"lua", "vim", "vimdoc", "query",
"javascript", "typescript", "tsx",
"python", "go", "rust", "bash",
"markdown", "markdown_inline", "yaml", "json", "toml",
"html", "css", "scss",
},
auto_install = true,
highlight = { enable = true },
indent = { enable = true },
incremental_selection = {
enable = true,
keymaps = {
init_selection = "<CR>",
node_incremental = "<CR>",
scope_incremental = "<S-CR>",
node_decremental = "<BS>",
},
},
})
end,
},
}Poin-poin yang perlu kalian perhatikan:
build = ":TSUpdate" — setelah plugin selesai di-clone oleh lazy.nvim, perintah :TSUpdate otomatis dijalankan untuk mengkompilasi parser yang terdaftar di ensure_installed. Ini memperbaiki masalah klasik "parser belum terinstall setelah bootstrap config baru".event = { "BufReadPre", "BufNewFile" } — plugin hanya dimuat ketika kalian benar-benar membuka buffer. Ini adalah lazy-loading event-based yang dibahas di episode 9, menjaga startup time tetap rendah.highlight = { enable = true } — mengaktifkan highlighting berbasis Treesitter sebagai pengganti regex.indent = { enable = true } — indentasi berbasis struktur; gg=G atau menekan = pada selection akan menyesuaikan indentasi sesuai grammar, bukan sekadar perhitungan whitespace.Tip
Jika kalian sering membuka file dengan bahasa yang belum ada parser-nya, set auto_install = true seperti contoh di atas. Saat file dengan bahasa tak dikenal dibuka, parser otomatis diunduh dan dikompilasi. Tidak semua grammar mendukung instalasi otomatis ini, tapi mayoritas populer sudah mendukung.
:TSInstallMeskipun ensure_installed sudah mencakup bahasa umum, kalian pasti akan menemukan bahasa baru di proyek berikutnya. Ini command set yang wajib dihapal:
:TSInstall rust " satu bahasa
:TSInstall go python " beberapa bahasa sekaligusNote
Nama parser tidak selalu sama dengan nama bahasa. Contoh: TypeScript dengan JSX memakai parser tsx (terpisah dari typescript), dan Markdown memerlukan dua parser — markdown untuk blok dan markdown_inline untuk teks di dalamnya. Cek :TSInstallInfo untuk melihat daftar lengkap.
Berikut tabel parser untuk bahasa-bahasa paling umum yang dipakai tim engineering:
| Bahasa | Nama Parser | Notes |
|---|---|---|
| Lua & Neovim config | lua, vim, vimdoc, query | vimdoc untuk help file, query untuk query files |
| JavaScript / TypeScript | javascript, typescript, tsx | tsx wajib untuk React (JSX) |
| Python | python | Mendukung semua syntax hingga 3.13+ |
| Go | go | Parser sangat matang |
| Rust | rust | Termasuk macro dan attributes |
| Shell | bash | Juga untuk zsh/sh |
| Markdown | markdown, markdown_inline | Wajib keduanya untuk hasil akurat |
| YAML / JSON / TOML | yaml, json, toml | Sering dipakai untuk config file |
| HTML / CSS | html, css, scss |
Dengan highlighting aktif, kalian bisa langsung merasakan perbedaannya. String di dalam komentar tidak lagi diwarnai hijau; keyword di dalam template literal JS tidak lagi dikira keyword. Selain itu, kita bisa memanfaatkan struktur AST untuk tiga fitur produktivitas:
1. Code Folding berbasis struktur. Dengan AST, fold memakai batas blok yang sebenarnya (fungsi, class, blok if), bukan sekedar level indentasi:
vim.opt.foldmethod = "expr"
vim.opt.foldexpr = "v:lua.vim.treesitter.foldexpr()"
vim.opt.foldlevel = 99
vim.opt.foldlevelstart = 99Setelah konfigurasi di atas, tombol folding standar Vim langsung bekerja dengan presisi Treesitter:
zc — tutup satu foldzo — buka satu foldza — toggle fold di bawah kursorzM / zR — tutup semua / buka semua fold2. Incremental Selection. Ini fitur andalan untuk menyeleksi blok kode secara bertahap — dimulai dari node terkecil lalu melebar mengikuti struktur AST. Dengan keymaps yang kita definisikan di spec tadi:
<CR> satu kali — kursor menyeleksi node terkecil (misal nama variabel).<CR> lagi — seleksi melebar ke statement.<BS> untuk menyempitkan kembali jika kebanyakan.Bayangkan ini seperti meminjam kaca pembesar yang bisa di-zoom out secara bertahap: seleksi selalu mengikuti batas struktur yang benar, tidak pernah memotong di tengah-tengah ekspresi.
3. Textobjects berbasis grammar. Di episode 3 kita belajar textobjects klasik seperti ci" dan dap. Textobjects bawaan itu hanya mengerti karakter pembatas dan kata. Dengan nvim-treesitter, kita bisa memilih fungsi, class, blok, parameter sebagai object — jauh lebih bermakna bagi programmer.
Plugin treesitter-textobjects menambahkan keymaps a/i untuk objek-objek tersebut:
{
"nvim-treesitter/nvim-treesitter-textobjects",
dependencies = { "nvim-treesitter/nvim-treesitter" },
config = function()
require("nvim-treesitter-textobjects").setup({
select = {
enable = true,
lookahead = true,
keymaps = {
["af"] = "@function.outer",
["if"] = "@function.inner",
["ac"] = "@class.outer",
["ic"] = "@class.inner",
["ab"] = "@block.outer",
["ib"] = "@block.inner",
["aa"] = "@parameter.outer",
["ia"] = "@parameter.inner",
},
},
move = {
enable = true,
goto_next_start = { ["]f"] = "@function.outer", ["]c"] = "@class.outer" },
goto_previous_start = { ["[f"] = "@function.outer", ["[c"] = "@class.outer" },
},
swap = {
enable = true,
swap_next = { [">a"] = "@parameter.inner" },
swap_previous = { ["<a"] = "@parameter.inner" },
},
})
end,
},Contoh penggunaan yang akan mengubah hidup kalian:
yaf — yank (copy) seluruh fungsi tempat kursor berada.daf — hapus seluruh fungsi sekaligus.ci( — sudah ada bawaan, tapi cia memilih argumen dengan presisi Treesitter.]f — lompat ke awal fungsi berikutnya; [f kembali ke fungsi sebelumnya.>a — geser parameter fungsi satu posisi ke kanan (swap).Important
Perhatikan detail lookahead = true pada select: fitur ini membuat daf bekerja bahkan ketika kursor berada di dalam fungsi, bukan harus tepat di garis fungsi tersebut. Tanpa lookahead, textobject hanya dikenali jika kursor berada pada baris yang sama dengan awal objek — sering membingungkan pengguna baru.
| Kesalahan | Gejala | Solusi |
|---|---|---|
| Toolchain C compiler tidak ada | TSInstall gagal dengan error make / cc tidak ditemukan | Install build-essential (Linux), Xcode Command Line Tools (macOS), atau gcc (Windows WSL) |
| Parser ketinggalan zaman | Highlighting salah untuk syntax baru (misal fitur bahasa terbaru) | Jalankan :TSUpdate secara berkala; build = ":TSUpdate" di lazy spec membantu otomatis |
| Instalasi parser pertama lambat | Proses build memakan waktu lama untuk banyak parser | Install parser yang benar-benar dipakai saja; gunakan parser dari prebuilt binary jika tersedia |
| Highlighting aneh / tidak muncul | Warna tidak berubah atau terlihat salah | Cek :checkhealth treesitter, pastikan colorscheme mendukung termguicolors |
auto_install gagal untuk bahasa tertentu | Bahasa tidak ter-highlight | Install manual dengan :TSInstall <lang> |
Fold hilang setelah zR | Semua fold kebuka dan file jadi panjang | Normal! zc/za untuk mengunci kembali; foldlevelstart = 99 membuat fold default terbuka tapi tetap bisa dipakai |
Konflik dengan indent-blankline | Garis indentasi tidak sesuai blok | Pastikan indent-blankline membaca vim.treesitter (versi terbaru sudah otomatis) |
Warning
Satu kesalahan yang paling sering membuat frustrasi: setelah menambahkan bahasa baru di ensure_installed, parser tidak otomatis terkompilasi pada config yang sudah ada — karena lazy.nvim hanya menjalankan build saat plugin baru di-install. Solusi: jalankan :TSInstall <language> sekali untuk bahasa baru, atau gunakan :TSUpdate untuk memastikan semua parser di ensure_installed terinstall.
Pada episode 14 ini kita telah membedah mengapa regex highlighting sudah menjadi solusi usang, memahami konsep AST dan incremental parser yang membuat Treesitter begitu cepat, lalu membangun konfigurasi lengkap nvim-treesitter: instalasi parser per bahasa, highlighting struktural, incremental selection, code folding berbasis grammar, hingga textobjects seperti yaf dan ]f yang memahami fungsi dan class. Semua ini adalah panggung bagi fitur yang jauh lebih besar: kemampuan editor untuk menyadari kode secara kontekstual.
Di episode 15 selanjutnya, kita akan membangun di atas fondasi ini topik yang paling ditunggu: Built-in LSP (Language Server Protocol) dengan mason.nvim — menghubungkan Neovim kalian ke language server untuk mendapatkan go-to-definition, rename symbol, code action, dan hover documentation yang setara IDE komersial. Pastikan tetap semangat!