12 poin oleh GN⁺ 2025-08-13 | 1 komentar | Bagikan ke WhatsApp
  • Memperkenalkan cara mengimplementasikan fitur pelengkapan tab di Bash dan Zsh yang menampilkan deskripsi bahkan untuk kata yang sudah lengkap
  • Bash dan Zsh menggunakan API pelengkapan tab yang berbeda, dan hanya Zsh yang secara bawaan menyediakan fitur menampilkan deskripsi pada pelengkapan
  • Mengimplementasikan struktur yang menghasilkan kandidat dengan _generate_foo_completions lalu mengembalikannya melalui COMPREPLY di Bash dan compadd di Zsh
  • Untuk menerapkan fitur deskripsi Zsh di Bash juga, digunakan pendekatan menyertakan deskripsi dalam string kandidat lalu menghapus deskripsi itu hanya saat kandidatnya tunggal
  • Bahkan saat kandidatnya tunggal, perilaku ini ditingkatkan dengan sengaja menambahkan ambiguitas agar deskripsi tetap ditampilkan saat menekan <TAB>
  • Skrip akhirnya memberikan pengalaman pengguna yang sama di kedua shell, serta mendukung pelengkapan parsial, pelengkapan penuh, dan tampilan deskripsi kandidat

Latar belakang masalah

  • Pelengkapan tab (tab-completion) berguna saat menjelajahi perintah atau flag, terutama ketika baru pertama kali menggunakan API atau alat CLI
  • Secara bawaan, Zsh hanya menampilkan deskripsi ketika ada beberapa kandidat, sedangkan Bash hanya bisa melakukannya lewat konfigurasi tambahan
  • Namun, untuk kata yang sudah lengkap, kedua shell tidak menampilkan deskripsi
  • Akibatnya, pengguna harus melalui proses merepotkan seperti berikut untuk melihat deskripsi
    • Menghapus beberapa karakter agar beberapa kandidat cocok
    • Menekan tombol <TAB> untuk melihat daftar kandidat
    • Mencari deskripsi yang diinginkan secara visual
    • Mengetik ulang karakter yang tadi dihapus untuk menjalankan perintah

Ringkasan solusi

  • Bahkan jika hanya ada satu kandidat, tambahkan kandidat dummy (dummy completion) agar kandidat menjadi ambigu
  • Dengan begitu, baik Bash maupun Zsh akan menampilkan daftar kandidat beserta deskripsinya
  • Namun, harus diperhatikan agar teks deskripsi tidak ikut disisipkan ke perintah sebenarnya

Konsep dasar

  • Pelengkapan tab bekerja dengan menerima kata saat ini dan posisi kursor ketika <TAB> ditekan, lalu mengembalikan daftar kandidat yang mungkin
  • Bash: menetapkan kandidat ke array COMPREPLY
  • Zsh: mendaftarkan kandidat dengan perintah compadd
  • Fungsi _generate_foo_completions mencetak string kandidat, dan dalam penggunaan nyata bisa dibuat secara dinamis berdasarkan status CLI

Mendukung Bash dan Zsh sekaligus

  • Mengimplementasikan fungsi _complete_foo_bash dan _complete_foo_zsh sesuai shell masing-masing
  • Membedakannya dengan if [ -n "${ZSH_VERSION:-}" ]; then ... elif [ -n "${BASH_VERSION:-}" ]; then ... fi
  • Pengguna menerapkan skrip ini setelah mendaftarkannya di .bashrc atau .zshrc

Menampilkan deskripsi di Zsh

  • Gunakan format nama: deskripsi pada string kandidat
  • Zsh: kirim nama dan deskripsi sebagai array paralel lewat compadd -d raw -- $trimmed
  • Bash: hanya kirim kandidat tanpa bagian deskripsi ke COMPREPLY (secara bawaan tidak mendukung deskripsi)

Mengimplementasikan deskripsi di Bash

  • Jika ada beberapa kandidat, tampilkan apa adanya string yang sudah berisi deskripsi
  • Jika kandidatnya tunggal, hapus deskripsinya
  • Manfaatkan perilaku pelengkapan Bash yang hanya menyisipkan prefiks bersama agar deskripsi tidak benar-benar masuk ke input

Menampilkan deskripsi bahkan untuk kandidat tunggal

  • Agar deskripsi tetap terlihat saat menekan <TAB> pada kata yang sudah lengkap, tambahkan kandidat dummy untuk menciptakan ambiguitas
  • Zsh: tambahkan kandidat dummy ke kedua array paralel (raw, trimmed)
  • Bash: jika kandidatnya tunggal, tambahkan hanya namanya ke trimmed

Hasil akhir

  • Saat ada banyak kandidat, nama+deskripsi semuanya ditampilkan
  • Saat kandidat tunggal pun, deskripsi tetap bisa dilihat dengan <TAB>
  • Bash dan Zsh sama-sama memberikan pengalaman yang identik
  • Contoh penerapan:
    $ foo <TAB>  
    apple: a common fruit banana: starchy and high in potassium  
    apricot: sour fruit... cherry: small and sweet...  
    

1 komentar

 
GN⁺ 2025-08-13
Opini Hacker News
  • Di fish, jika program yang diminati menyediakan halaman man, cukup jalankan fish_update_completions
    • Perintah ini mem-parsing semua halaman man di sistem dan membuat file autocompletion di ~/.cache/fish/generated_completions/
    • Jika halaman man kurang baik atau tidak ada, kita bisa menulisnya sendiri dan berkontribusi ke upstream
    • Format fish sederhana, jadi cukup melihat dokumentasi resminya
    • Contoh: curl untuk -L → 'Follow redirects', -O → 'Write output to file named as remote file'
    • Saat berbagi layar, orang-orang mengira saya memakai zsh dan berbagai plugin, padahal sebenarnya saya hanya memakai fish yang cantik dengan pengaturan bawaan
    • Namun akan lebih baik jika perilaku autocompletion non-prefiks yang membuat car TAB meluas menjadi blkdiscard, sehingga cargo salah terdeteksi meski tidak ada di PATH, bisa diperbaiki
    • Untuk program yang hanya menyediakan --help tanpa halaman man, saya penasaran apakah fish punya fungsi seperti _gnu_generic di zsh atau complete -F _longopt di bash
    • Di zsh juga ada skrip yang membuat autocompletion berbasis halaman man → zsh-manpage-completion-generator
    • Di OpenSUSE, saat mengetik zypper search fish-completion, muncul lebih dari 200 paket sehingga terasa agak mencurigakan
  • Semakin autocompletion bash menjadi “pintar”, semakin tidak nyaman karena ia memblokir penyelesaian file/direktori jika menilai nama file tidak cocok untuk posisi kursor saat ini
    • Menurut saya, lebih baik selalu fallback ke penyelesaian nama file
    • Karena ini, saya sempat mempertimbangkan untuk mematikan semua skrip completion
    • Di bash ada fungsi seperti complete-filename yang dibind ke M-/, yang menyelesaikan nama file sambil mengabaikan konteks
    • Completion file untuk perintah tertentu bisa rusak total, sehingga saya menyiasatinya dengan menyelesaikan lewat ls dulu lalu mengganti perintahnya
    • Jika nama file ada tetapi tidak bisa dieksekusi, pesan seperti “file foo.exe exists but it isn't executable” akan terasa lebih tidak membingungkan
    • Fakta bahwa setelah menjalankan complete -r semuanya bekerja sesuai keinginan membuat saya curiga masalahnya ada di skrip bash-completion
    • Ini mirip UX yang menjengkelkan seperti validasi frontend pada formulir web yang menampilkan error “Invalid email!” bahkan ketika baru mengetik huruf pertama
  • Ini tulisan saya, dan saya harap orang lain juga membacanya dengan tertarik
    • Alih-alih memuat skrip completion zsh setiap kali, memasangnya di $fpath membuatnya di-cache sehingga kecepatan startup menjadi lebih cepat
    • Saat mendistribusikan lewat Homebrew, kita bisa memasang completions secara otomatis
    • Completion zsh cukup besar skalanya sehingga sulit dipelajari, tetapi di kantor saya terus merapikannya sambil menambahkan opsi per-playbook, autocompletion tag, dan lain-lain ke skrip pembungkus ansible
  • Belakangan ini saya mulai memakai library usage untuk pengembangan CLI → terintegrasi dengan clap, dan bisa menghasilkan completions, argparse, serta halaman man
    • Saya masih mempertimbangkan apakah layak mengganti blok argparse di skrip fish, tetapi jelas jauh lebih baik daripada optparse
    • Saya juga sedang membuat alat definisi spesifikasi CLI serupa, dan baru saja menambahkan dukungan fish
  • Memperkenalkan fx.wtf yang mendukung autocompletion field JSON di bash/zsh
    • Lebih ringan daripada ijq, tetapi bisa berguna tergantung situasinya
  • Tutorial membuat completer dengan fungsi bawaan zsh → zsh-completions-howto
  • Saya penasaran apakah ada flag standar yang memungkinkan program tidak perlu menulis skrip bash sendiri
    • Di zsh, _gnu_generic memungkinkan completion sederhana berbasis --help
    • Ada contoh seperti clap_complete di Rust, opsi --generate milik ripgrep, dan pembuatan completion saat runtime di PHP Symfony
    • Akan bagus jika ada standar --completion yang otomatis diimplementasikan oleh library parser umum
  • Di ksh, completion sederhana bisa diimplementasikan hanya dengan definisi array
    • Contoh: jika nama sinyal dimasukkan ke array complete_kill_1, maka akan tersedia completion untuk argumen pertama perintah kill
    • Saya penasaran apakah ini sintaks ksh93 atau sesuatu yang di-backport ke oksh
  • Contoh snippet completion zsh sederhana yang saya buat: memakai _describe di fungsi set-java-home agar daftar ~/apps/java/* dilengkapi sebagai versi
    • Strukturnya sangat sederhana, nyaris satu baris
  • Karena fungsi utama LLM adalah autocompletion teks, GitHub Copilot tampaknya cukup bagus memberikan completion bash dan zsh di terminal vscode