Giới thiệu
Tôi là tác giả của tree-sitter-unreal-cpp, một parser Tree-sitter tùy chỉnh cho ngôn ngữ C++ của Unreal Engine. Gần đây, tôi đã quyết định cập nhật thiết lập Neovim của mình lên nhánh main mới nhất của nvim-treesitter. Tôi đã chuẩn bị cho một số trục trặc, nhưng điều tôi nhận được là một sự cố hệ thống hoàn toàn: tất cả các đánh dấu cú pháp mà tôi đã cẩn thận tạo ra cho các macro của Unreal Engine đã biến mất. 😱
Bài viết này ghi lại hành trình gỡ lỗi của tôi và những phát hiện quan trọng mà tôi đã thực hiện, dẫn đến một quá trình di chuyển thành công. Nếu bạn là tác giả plugin hoặc sử dụng các parser tùy chỉnh, tôi hy vọng kinh nghiệm của tôi có thể giúp bạn tiết kiệm thời gian.
Hành Trình Gỡ Lỗi
Mục tiêu của tôi là khôi phục các đánh dấu tùy chỉnh cho các macro như UCLASS và các chỉ định như Blueprintable. Tôi bắt đầu bằng cách kiểm tra từng phần của chuỗi một cách có hệ thống.
1. Xác Minh Tệp Truy Vấn
Trước tiên, tôi cần xác nhận xem tệp highlights.scm tùy chỉnh của mình có được tải hay không. Trên nhánh main, các công cụ gỡ lỗi đã thay đổi, và cách đáng tin cậy nhất là sử dụng API tích hợp của Neovim trực tiếp.
Tôi đã chạy lệnh này trong Neovim:
:lua print(vim.inspect(vim.treesitter.query.get_files(vim.bo.filetype, "highlights")))
Kết quả xác nhận rằng tệp truy vấn tùy chỉnh của tôi (.../queries/cpp/highlights_unreal.scm) thực sự nằm trong danh sách. Vì vậy, tệp đã được tìm thấy. Bước đầu tiên: thành công.
2. Kiểm Tra Các Đánh Dấu Đã Áp Dụng
Tiếp theo, tôi đặt con trỏ của mình lên một từ khóa đáng lẽ phải được đánh dấu, như Blueprintable, và sử dụng lệnh :Inspect để xem những nhóm cú pháp nào đang được áp dụng.
Kết quả? Không có gì. Hay nói đúng hơn, không có gì từ Tree-sitter. Rõ ràng là ngay cả khi parser của tôi hoạt động (điều mà tôi đã xác nhận với :InspectTree), không có quy tắc đánh dấu nào từ tệp .scm của tôi được áp dụng.
3. Bí Ẩn Sâu Sắc Hơn
Đây là phần gây bối rối. Parser của tôi là chính xác, tệp truy vấn của tôi đã được tải, và cú pháp của nó là hợp lệ theo tài liệu mới nhất của Neovim. Tại sao lại không có kết nối nào giữa hai cái này?
Tôi bắt đầu nghi ngờ rằng có điều gì đó cơ bản đã thay đổi. Đó là khi tôi quyết định đọc lại README của nhánh main của nvim-treesitter từ đầu đến cuối.
Đột Phá: Đây Là Một Plugin Hoàn Toàn Mới
README đã tiết lộ sự thật: nhánh main không phải là một bản cập nhật; đây là một viết lại hoàn toàn, không tương thích.
Cấu hình cũ của tôi hoàn toàn sai vì triết lý cốt lõi của plugin đã thay đổi. Dưới đây là những điểm chính:
setuplà tối thiểu: Bảngsetupkhổng lồ mà bạn từng sử dụng để cấu hình mọi thứ (highlight,indent,ensure_installed) đã biến mất.ensure_installedkhông còn: Bạn không còn cung cấp danh sách các parser để tự động cài đặt. Thay vào đó, bạn phải gọi một hàm để cài đặt chúng.- Kích hoạt tính năng giờ đây là thủ công: Các công tắc như
highlight = { enable = true }đã được loại bỏ. Plugin không tự động kích hoạt các tính năng nữa. Nó chỉ cung cấp các parser và truy vấn; bây giờ là trách nhiệm của người dùng để kích hoạt đánh dấu, thụt lề và gập bằng cách sử dụng các API cốt lõi của Neovim.
nvim-treesitter đã tiến hóa từ một framework all-in-one thành một quản lý parser tập trung hơn. "Công tắc nguồn" cho việc đánh dấu đã tắt vì tôi là người phải bật nó lên.
Cấu Hình Cuối Cùng
Với sự hiểu biết mới này, tôi đã viết cấu hình cuối cùng của mình. Mã này giờ đây phản ánh đúng triết lý mới, rõ ràng của nhánh main.
Dưới đây là thiết lập của tôi sử dụng lazy.nvim:
-- lazy.nvim spec
-- lazy.nvim spec trong tệp plugin của bạn (ví dụ: lua/plugins/treesitter.lua)
return {
{
'nvim-treesitter/nvim-treesitter',
branch = 'main',
-- Bước xây dựng là rất quan trọng để tự động cài đặt/cập nhật parser
build = ':TSUpdate',
-- Plugin này phụ thuộc vào việc parser tùy chỉnh có sẵn
dependencies = {
'taku25/tree-sitter-unreal-cpp',
},
config = function()
-- === 1. Cấu hình nvim-treesitter để sử dụng parser tùy chỉnh của chúng tôi ===
-- Chúng tôi hook vào sự kiện 'TSUpdate' để ghi đè cấu hình parser cho "cpp".
-- Điều này đảm bảo rằng bất cứ khi nào `:TSUpdate` chạy, nó biết sử dụng repo của bạn.
vim.api.nvim_create_autocmd('User', {
pattern = 'TSUpdate',
callback = function()
-- Ghi đè định nghĩa parser 'cpp'
require('nvim-treesitter.parsers').cpp = {
install_info = {
url = 'https://github.com/taku25/tree-sitter-unreal-cpp',
-- Ghi chú vào một commit cụ thể là một thực tiễn tốt cho sự ổn định
revision = 'd5673330c80033dfbf6ac868c7bbbfb16d53b5f6',
},
-- Quan trọng: Vô hiệu hóa parser C++ mặc định là cần thiết
-- nếu parser tùy chỉnh của bạn không bao phủ tất cả cú pháp C++ tiêu chuẩn.
-- Nếu parser của bạn hoàn toàn hỗ trợ C++, bạn có thể xóa điều này.
--
-- Bằng cách thiết lập điều này, parser C++ mặc định vẫn sẽ được sử dụng
-- cho việc đánh dấu, và parser tùy chỉnh của bạn sẽ được xếp lớp lên trên
-- thông qua tệp truy vấn `;; extends` của bạn.
-- maintainers = {},
-- files = {}
}
end
})
-- === 2. Kích hoạt các tính năng cho các ngôn ngữ cụ thể ===
-- Đây là cách mới, chính xác để kích hoạt đánh dấu và thụt lề.
local langs = { "c", "cpp", "c_sharp", "lua", "vim", "vimdoc", "h" }
local group = vim.api.nvim_create_augroup('MyTreesitterSetup', { clear = true })
vim.api.nvim_create_autocmd('FileType', {
group = group,
pattern = langs,
callback = function(args)
-- Kích hoạt đánh dấu cho buffer
vim.treesitter.start(args.buf)
-- Kích hoạt thụt lề cho buffer
vim.bo[args.buf].indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
end,
})
end,
},
}
Với cấu hình này, tất cả các đánh dấu tùy chỉnh của Unreal Engine đã sống lại và hòa hợp một cách yên bình với các mã thông báo ngữ nghĩa LSP. Chiến thắng! 🚀
Kết Luận
Việc di chuyển sang nhánh main của nvim-treesitter yêu cầu bạn quên những gì bạn đã biết về nhánh master cũ. Chìa khóa là hiểu sự thay đổi trong trách nhiệm: plugin quản lý các công cụ (parser và truy vấn), nhưng bạn, người dùng, giờ đây sử dụng các API của Neovim để quyết định khi nào và cách sử dụng chúng.
Hãy luôn đọc README, đặc biệt là trên một nhánh main! Tôi hy vọng bài viết này có thể giúp ai đó tránh được cơn đau đầu gỡ lỗi tương tự. Chúc bạn lập trình vui vẻ!