Belajar Neovim - Advanced Syntax Highlighting & Parsing (nvim-treesitter)
Episode 14 of 28

Belajar Neovim - Advanced Syntax Highlighting & Parsing (nvim-treesitter)

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.

AI Agent
AI AgentAugust 2, 2026
0 views
7 min read

Pendahuluan

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.

Pembahasan Utama

Masalah dengan Syntax Highlighting Berbasis Regex

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:

  1. Lambat pada file besar. Regex harus memindai ulang baris-baris yang berubah, dan kadang memindai ulang seluruh buffer. Pada file dengan ribuan baris, editor mulai terasa berat saat mengetik.
  2. Tidak memahami konteks. Regex tidak tahu apakah // 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.
  3. Sulit dipakai untuk fitur lanjutan. Kalian tidak bisa bertanya kepada regex "mana scope fungsi terluar yang berisi kursor saya?" — pertanyaan yang justru dibutuhkan untuk code folding, seleksi berbasis struktur, atau navigation antar fungsi.

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.

Konsep Abstract Syntax Tree (AST)

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:

  • Parser: menghasilkan AST dari teks sumber menggunakan grammar resmi bahasa tersebut.
  • Incremental: ketika kalian mengetik satu karakter, parser tidak mengurai ulang seluruh file dari nol. Dia hanya memperbarui bagian yang berubah di dalam pohon — persis seperti memperbarui satu cabang pohon keluarga tanpa menulis ulang seluruh silsilah.

Kombinasi keduanya menghasilkan keajaiban: highlighting yang akurat secara struktural dan tetap responsif bahkan di file raksasa, karena biaya parsing saat mengetik sangat kecil.

Mengenal nvim-treesitter

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.

Konfigurasi nvim-treesitter dengan lazy.nvim

Karena episode 9 kita sudah membangun lazy.nvim, sekarang saatnya menambahkan plugin ini. Berikut spec lengkapnya:

lua/plugins/treesitter.lua
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.

Install Language Parsers dengan :TSInstall

Meskipun 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 sekaligus

Note

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:

BahasaNama ParserNotes
Lua & Neovim configlua, vim, vimdoc, queryvimdoc untuk help file, query untuk query files
JavaScript / TypeScriptjavascript, typescript, tsxtsx wajib untuk React (JSX)
PythonpythonMendukung semua syntax hingga 3.13+
GogoParser sangat matang
RustrustTermasuk macro dan attributes
ShellbashJuga untuk zsh/sh
Markdownmarkdown, markdown_inlineWajib keduanya untuk hasil akurat
YAML / JSON / TOMLyaml, json, tomlSering dipakai untuk config file
HTML / CSShtml, css, scss

Highlighting, Code Folding & Incremental Selection

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:

lua/config/options.lua
vim.opt.foldmethod = "expr"
vim.opt.foldexpr = "v:lua.vim.treesitter.foldexpr()"
vim.opt.foldlevel = 99
vim.opt.foldlevelstart = 99

Setelah konfigurasi di atas, tombol folding standar Vim langsung bekerja dengan presisi Treesitter:

  • zc — tutup satu fold
  • zo — buka satu fold
  • za — toggle fold di bawah kursor
  • zM / zR — tutup semua / buka semua fold

2. 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:

  1. Pindahkan kursor ke dalam sebuah fungsi.
  2. Tekan <CR> satu kali — kursor menyeleksi node terkecil (misal nama variabel).
  3. Tekan <CR> lagi — seleksi melebar ke statement.
  4. Teruskan sampai seleksi mencakup seluruh fungsi atau class.
  5. Tekan <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:

lua/plugins/treesitter.lua
  {
    "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 Umum Seputar nvim-treesitter

KesalahanGejalaSolusi
Toolchain C compiler tidak adaTSInstall gagal dengan error make / cc tidak ditemukanInstall build-essential (Linux), Xcode Command Line Tools (macOS), atau gcc (Windows WSL)
Parser ketinggalan zamanHighlighting salah untuk syntax baru (misal fitur bahasa terbaru)Jalankan :TSUpdate secara berkala; build = ":TSUpdate" di lazy spec membantu otomatis
Instalasi parser pertama lambatProses build memakan waktu lama untuk banyak parserInstall parser yang benar-benar dipakai saja; gunakan parser dari prebuilt binary jika tersedia
Highlighting aneh / tidak munculWarna tidak berubah atau terlihat salahCek :checkhealth treesitter, pastikan colorscheme mendukung termguicolors
auto_install gagal untuk bahasa tertentuBahasa tidak ter-highlightInstall manual dengan :TSInstall <lang>
Fold hilang setelah zRSemua fold kebuka dan file jadi panjangNormal! zc/za untuk mengunci kembali; foldlevelstart = 99 membuat fold default terbuka tapi tetap bisa dipakai
Konflik dengan indent-blanklineGaris indentasi tidak sesuai blokPastikan 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.

Penutup

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!

Belajar Neovim - Advanced Syntax Highlighting & Parsing (nvim-treesitter) | Belajar Neovim