Telescope: tìm file và grep trong dự án
Mục tiêu của bài này
Sau bài này, bạn sẽ:
- Khai báo Telescope trong
lua/plugins/telescope.luavà hiểu vì sao cầnplenary.nvim,ripgrep,fd. - Gắn keybinding cho 5 picker dùng nhiều nhất:
find_files,live_grep,buffers, symbol,resume. - Đọc kết quả trong picker nhanh bằng preview và các phím mở file (
<CR>,<C-v>,<C-x>,<C-t>). - Cấu hình
file_ignore_patternsđểnode_moduleskhông làm nhiễu kết quả.
Bài trước bạn đã có LSP chạy với vim.lsp.config và diagnostics. Telescope không phụ thuộc LSP, nhưng hai thứ cộng lại mới thành vòng lặp điều hướng khép kín: LSP báo lỗi ở đâu, Telescope đưa bạn tới đó.
Dựng Telescope trong cấu hình capstone
Bước 1: Cài binary mà Telescope gọi ra
Telescope chỉ là UI. Phần tìm kiếm thật do binary bên ngoài làm:
live_grepgọirg(ripgrep). Không córgthì picker báo lỗi ngay khi mở.find_filesưu tiênfd, không có thì fallback sang tự quét thư mục (chậm hơn nhiều trong repo lớn).
Trên Windows 11, mở PowerShell:
winget install BurntSushi.ripgrep.MSVC
winget install sharkdp.fd
Đóng hẳn terminal rồi mở lại để PATH cập nhật, sau đó kiểm tra trong Neovim:
:lua print(vim.fn.exepath("rg"))
:lua print(vim.fn.exepath("fd"))
Ra đường dẫn tuyệt đối là xong. Ra chuỗi rỗng nghĩa là Neovim chưa thấy binary, thường vì bạn khởi động Neovim từ terminal cũ.
Bước 2: Khai báo plugin
-- lua/plugins/telescope.lua
return {
"nvim-telescope/telescope.nvim",
tag = "0.1.8",
dependencies = { "nvim-lua/plenary.nvim" },
cmd = "Telescope",
keys = {
{ "<leader>ff", "<cmd>Telescope find_files<cr>", desc = "Tìm file" },
{ "<leader>fg", "<cmd>Telescope live_grep<cr>", desc = "Grep trong dự án" },
{ "<leader>fb", "<cmd>Telescope buffers<cr>", desc = "Buffer đang mở" },
{ "<leader>fs", "<cmd>Telescope lsp_dynamic_workspace_symbols<cr>", desc = "Symbol toàn workspace" },
{ "<leader>fr", "<cmd>Telescope resume<cr>", desc = "Mở lại picker trước" },
},
opts = {
defaults = {
file_ignore_patterns = { "node_modules", "%.git/", "dist" },
sorting_strategy = "ascending",
layout_config = { prompt_position = "top" },
},
},
}
Lý do từng dòng:
tag = "0.1.8": ghim phiên bản để cấu hình không vỡ sau một lần:Lazy updatetrên máy công ty.cmd = "Telescope": chỉ nạp plugin khi gọi lệnh hoặc bấm key, giữ thời gian khởi động thấp.keyskhai báo cảdesc, nênwhich-key(nếu bạn có) hiện mô tả tiếng Việt thay vì dấu hỏi.file_ignore_patternsdùng Lua pattern, không phải regex:%.git/chứ không phải\.git/. Đây là lỗi phổ biến nhất khiến filter không có tác dụng.prompt_position = "top": ô nhập nằm trên, mắt không phải nhảy xuống dưới rồi ngược lên khi gõ.
Bước 3: Dùng thử
Mở repo capstone, bấm <leader>ff, gõ một phần tên file, rồi:
| Phím | Tác dụng |
|---|---|
<C-n> / <C-p> |
Xuống / lên trong danh sách kết quả |
<CR> |
Mở ở cửa sổ hiện tại |
<C-v> |
Mở vertical split |
<C-x> |
Mở horizontal split |
<C-t> |
Mở tab mới |
? |
Hiện bảng mapping của picker đang mở |
Preview bên phải cho bạn thấy nội dung file hoặc dòng match trước khi quyết định mở. Đó chính là chỗ tiết kiệm thời gian: không mở file sai rồi đóng lại.
flowchart TD
Start["🔍 Cần nhảy tới đâu?"] --> Q1{"Biết tên file?"}
Q1 -->|Có| FF["📁 find_files"]
Q1 -->|Không| Q2{"Nhớ nội dung code?"}
Q2 -->|Có| FG["🔎 live_grep"]
Q2 -->|Không| Q3{"File đang mở rồi?"}
Q3 -->|Có| FB["🗂️ buffers"]
Q3 -->|Không| FS["🧩 workspace symbols"]
Thực hành trên repo capstone
Làm trên chính repo bạn sẽ dùng cho công việc, không dùng repo mẫu.
Bài 1. Mở Neovim ở thư mục gốc repo, bấm <leader>fg, gõ TODO. Ghi lại số kết quả và kiểm tra xem có dòng nào trong node_modules lọt vào không.
Bài 2. Bấm <leader>fg, gõ tên một hàm bạn biết là có trong repo, nhưng chỉ gõ 3 ký tự đầu. Dùng <C-n> chọn kết quả thứ ba, rồi <C-v> để mở vertical split. Sau đó bấm <leader>fb xem buffer đó có trong danh sách không.
Bài 3. Đặt con trỏ lên một hàm, bấm <leader>fs, gõ tên hàm. So sánh với :Telescope lsp_document_symbols: cái nào chỉ quét file hiện tại?
Gợi ý và đáp án
Bài 1. Nếu vẫn thấy node_modules, gần như chắc chắn file_ignore_patterns viết sai kiểu pattern (\.git/ thay vì %.git/), hoặc bạn đang mở Neovim ở thư mục cha chứ không phải gốc repo. live_grep cũng tôn trọng .gitignore, nên nếu node_modules đã nằm trong .gitignore thì không cần khai báo lại, nhưng khai báo vẫn an toàn.
Bài 2. <C-v> mở file ở split dọc, buffer cũ vẫn còn, nên <leader>fb phải liệt kê cả hai. Đây là lý do buffers nhanh hơn find_files: nó không chạm đĩa.
Bài 3. lsp_dynamic_workspace_symbols quét toàn workspace (mọi file project), còn lsp_document_symbols chỉ quét file hiện tại. Dùng cái đầu khi bạn chỉ nhớ tên hàm, dùng cái sau khi đã ở đúng file và muốn nhảy trong file.
Lỗi thường gặp khi dùng Telescope trên Windows 11
live_grepbáo "rg not found": binary chưa có trong PATH của tiến trình Neovim. Kiểm tra bằng:lua print(vim.fn.exepath("rg")), đừng đoán.find_fileschậm bất thường:fdchưa cài, Telescope đang tự quét. Càifdlà hết.- Filter không ăn: nhớ
file_ignore_patternslà Lua pattern.*và\không hoạt động như regex quen thuộc. - Không gõ được dấu tiếng Việt trong prompt: đó là giới hạn của input method tại cửa sổ prompt, không phải lỗi Telescope. Cứ gõ không dấu để tìm, tên file và code vẫn khớp bình thường.
- Cài
telescope-fzf-native.nvimrồi build lỗi: extension này cầnmakevà trình biên dịch. Trên Windows 11 đây là nguồn lỗi lớn mà lợi ích không tương xứng. Bỏ qua nó ở giai đoạn này.
Mẹo dùng picker như người có nghề
- Một picker, một câu hỏi. Trước khi bấm key, tự hỏi "mình đang tìm tên file, nội dung, hay symbol?". Trả lời sai câu hỏi là mở sai picker, gõ lại từ đầu.
resumelà phím quý nhất trong 5 phím trên. Bạn vừa tìm xong, mở file, đọc một đoạn, cần quay lại danh sách cũ:<leader>fr. Không gõ lại pattern.- Grep bằng từ dưới con trỏ khi đang đọc code:
:Telescope grep_string. Nhanh hơn copy tên hàm rồi dán vàolive_grep. - Đừng mở quá 6-7 buffer.
<leader>fbchỉ hữu ích khi danh sách còn ngắn. Nhiều hơn thì dùng:bddọn bớt. - Ghim tag phiên bản Telescope. Plugin này thay đổi mapping giữa các bản; ghim tag giúp cấu hình của bạn tái lập được trên máy thứ hai.
Tóm lại
Telescope = UI của Neovim + binary tìm kiếm của hệ điều hành. Cài ripgrep và fd trước, khai báo plugin với cmd và keys để giữ startup nhanh, rồi luyện 5 picker (ff, fg, fb, fs, fr) cho tới khi phản xạ. Nhớ hai chi tiết dễ sai: file_ignore_patterns dùng Lua pattern, và <leader>fr đưa bạn về picker vừa dùng.
Bài tiếp theo chuyển sang Treesitter: bật parser để có highlight chính xác theo cú pháp và folding theo cấu trúc code, thay vì dựa vào regex.
Kiểm tra nhanh: live_grep cần binary nào? _____. find_files ưu tiên binary nào? _____. Phím mở kết quả ở vertical split là _____. Phím mở lại picker vừa dùng là _____. file_ignore_patterns dùng cú pháp _____ chứ không phải regex.
Front: Telescope dùng binary nào cho live_grep? Back: ripgrep (rg), nên phải cài và có trong PATH.
Front: Telescope dùng binary nào cho find_files? Back: fd; nếu không có, nó fallback sang tự quét thư mục nên chậm hơn.
Front: Plugin nào là dependency bắt buộc của telescope.nvim? Back: plenary.nvim.
Front: Phím di chuyển xuống và lên trong danh sách kết quả?
Back:
Front: Mở kết quả ở vertical split, horizontal split và tab mới?
Back:
Front: Phím nào hiện bảng mapping của picker đang mở? Back: Dấu ? mở which_key của picker.
Front: file_ignore_patterns dùng cú pháp gì? Back: Lua pattern, ví dụ %.git/ chứ không phải .git/.
Front: builtin nào tìm symbol trên toàn workspace? Back: lsp_dynamic_workspace_symbols.
Front: builtin nào chỉ tìm symbol trong file hiện tại? Back: lsp_document_symbols.
Front: builtin nào mở lại picker vừa dùng mà không gõ lại pattern?
Back: resume, gắn với
Front: Vì sao khai báo cmd = "Telescope" trong spec lazy.nvim? Back: Để plugin chỉ nạp khi gọi lệnh, giữ thời gian khởi động thấp.
Front: Vì sao nên ghim tag phiên bản telescope.nvim? Back: Mapping và API thay đổi giữa các bản, ghim tag giúp cấu hình tái lập được.
Front: Telescope có giúp ích gì khi LSP đã báo lỗi? Back: Đưa bạn tới đúng file và dòng có lỗi bằng find_files, live_grep hoặc workspace symbols.
Front: Vì sao tạm bỏ qua telescope-fzf-native.nvim trên Windows 11? Back: Nó cần make và trình biên dịch để build, dễ lỗi mà lợi ích chưa tương xứng.