Vim Script: Cách chuyển Varargs tới lambda trong timer_start
Tôi đang định cấu hình vim của mình bằng Vim Script.
Tôi đã đặt chức năng grep tùy chỉnh của riêng mình như sau:
function! CustomGrepCore(...)
if a:0 == 0
" do something
else if a:0 == 1
" do something2
else
" do something3
endfunction
function! CustomGrep(...)
let param = a:000
let F = {p -> call(function('CustomGrepCore'), p)}
call F(param)
endfunction
command! -nargs=? Grep :call CustomGrep(<f-args>)
Đoạn mã trên đã hoạt động như mong đợi. Tôi có thể thực thi :Grep xxxgrep tùy chỉnh của mình trong vim của mình.
Bây giờ tôi muốn sử dụng tính năng vim 8: timer_startđể làm cho grep không đồng bộ tùy chỉnh của tôi, theo hiểu biết của tôi, tôi chỉ cần mã lại hàm CustomGrep:
function! CustomGrep(...)
let param = a:000
let F = {p -> call(function('CustomGrepCore'), p)}
call timer_start(10, {param -> execute("call F(param)")}, "") " ERROR: Invalid argument
endfunction
Nhưng tôi luôn có những lỗi: Invalid argument.
Làm thế nào để khắc phục điều này?
Ngoài ra, như bạn thấy, tôi đã sử dụng timer_start, tôi biết rằng nó sẽ tạo ra một ID công việc, cho phép chúng tôi tạm dừng / dừng công việc. Trong trường hợp của tôi, tôi có cần phải dừng công việc một cách rõ ràng không?
Thêm một điều nữa
Còn một vấn đề nữa: làm thế nào để truyền các varargs từ lambda trong hàm CustomGrepsang hàm CustomGrepCore.
Tôi đã tìm thấy liên kết này: https://www.reddit.com/r/vim/comments/3761po/vimscript_question_passing_arguments_from_a/
Nhưng chúng ta có một trường hợp khác ở đây. Bởi vì chúng tôi đã sử dụng execute()trong hàm CustomGrep. Vì vậy, nó là nó có thể truyền varargs từ execute()cho chức năng CustomGrepCore? Tham số của execute()là một chuỗi, nhưng nếu chúng tôi chuyển đổi a:000thành chuỗi ( string(a:000)), tôi phải thay đổi hàm CustomGrepCore, bởi vì nếu tôi chuyển string(a:000)sang CustomGrepCore, giá trị của a:0sẽ luôn là 1.
Vì vậy, nó là nó có thể để truyền các varargs từ execute()một chức năng khác? Nếu không, tốt, tôi phải thay đổi chức năng CustomGrepCore.
Trả lời
Câu hỏi này chủ yếu giải quyết một số câu hỏi tiếp theo OP đã trả lời câu trả lời mà tôi đã đưa ra cho một câu hỏi khác mà họ đã đăng. Phần này cũng nói về một giải pháp liên quan đến các hàm lambda và các bao đóng hợp lệ nhưng phức tạp hơn mức cần thiết cho hầu hết các trường hợp sử dụng. Vì cả hai lý do đó, tôi khuyên bạn nên xem phần Hỏi & Đáp đó trước: Cách khởi động chức năng không đồng bộ trong Vim 8 . Tất nhiên, nếu bạn muốn hiểu thêm một chút về chủ đề (và một số chủ đề ngoại vi), bạn có thể quay lại chủ đề này sau đó.
Tôi tự hỏi tại sao bạn đang sử dụng FuncRef trong mã gốc để thực hiện cuộc gọi tới CustomGrepCore(). Dường như không có nhu cầu rõ ràng cho nó. Trong mã mới, nó làm cho mọi thứ trở nên phức tạp vì bạn phải đối phó với hai cấp độ chuyển hướng (FuncRef + lambda) thay vì chỉ một cấp cho lambda.
Vì vậy, đề xuất đầu tiên của tôi là sử dụng một cái gì đó giống như call CustomGrepCore(param)trước khi chạm vào công cụ hẹn giờ.
Mặc dù vậy, bất kể điều gì ở trên, hãy xem đối số thứ hai cho timer_start. Công cụ này là khó khăn, không có nghi ngờ, nhưng bạn thực sự đi lạc với những gì tôi đã viết. Có ba điểm không phù hợp giữa những gì bạn có
{param -> execute("call F(param)")}
và những gì tôi có
{-> execute("call LongRunningFun('" . a:patt . "')", "")}
Đầu tiên, cái paramtrước ->đến từ đâu? Quăng nó.
Thứ hai, tôi đang chuyển hai args để thực thi và bạn đang vượt qua một args. Bạn có thể hiểu điều này có nghĩa là gì và đã cố ý làm như vậy nhưng đề phòng trường hợp đó không phải vậy ... Bỏ đi tham số thứ hai tương đương với việc chạy các lệnh Ex trong đối số đầu tiên với :silent. Truyền chuỗi rỗng làm tham số thứ hai tương đương với việc chạy chúng mà không có :silent . Khi lần đầu tiên viết mã này, cá nhân tôi sẽ không sử dụng im lặng. Sau khi mọi thứ đang hoạt động, tôi có thể thêm nó.
Cuối cùng, tham số đầu tiên của bạn là một chuỗi tĩnh , duy nhất trong khi của tôi là một ghép của hai chuỗi tĩnh và một biểu thức ( a:patt). Trong khi bạn dự định chuyển (các) giá trị có trong biến cục bộ paramcho execute()những gì bạn thực sự đang làm là truyền chuỗi ký tự "param". Điều này áp dụng cho bất kỳ thứ gì có trong dấu ngoặc kép.
Dù sao, để tránh kéo điều này ra, tôi sẽ chỉ cho bạn những gì tôi sẽ làm. Đầu tiên, tôi sẽ không sử dụng -nargs=?lệnh của bạn. Tôi sẽ sử dụng +hoặc *thay thế ?tùy thuộc vào việc zero args ( :Grep) có phải là một cuộc gọi hợp lệ hay không. Điều này sau đó sẽ đặt mỗi đối số vào một vị trí riêng biệt trong danh sách tham số. Ngoài ra, không sử dụng varargs ( ...) trừ khi bạn thực sự cần chúng. Chúng thêm một mức độ chuyển hướng khiến mọi thứ trở nên phức tạp. Có lẽ không thể trong trường hợp cụ thể này. Vì vậy, hãy thay đổi CustomGrepCore để nó chấp nhận một đối số duy nhất sẽ là một danh sách. Đây là một minh chứng về cách nó có thể hoạt động khi chúng tôi làm timer_startđúng phần.
function! CustomGrepCore(args) abort
if len(a:args)
echom "First item in args list is " . a:args[0]
endif
endfunction
command! -nargs=* Grep :call CustomGrep(<f-args>)
Grep hello " prints 'First item in args list is hello'
Với tất cả những gì chúng ta có thể có được một thứ hoạt động ...
" You can't use string(a:000) directly in param expression. Not yet sure why.
let arglist = string(a:000)
call timer_start(50, { -> execute("call CustomGrepCore(" . arglist . ")", "")})
Một phần không rõ ràng của điều này là sự cần thiết phải vượt qua danh sách kỳ đà a:000, thông qua string(). execute()lấy tham số đầu tiên của nó là một chuỗi (mà nó đánh giá là một biểu thức). Bạn không thể nối một chuỗi và một danh sách. Bạn sẽ gặp lỗi nếu cố gắng. Vì vậy, chúng ta cần chuyển đổi thành một biểu diễn chuỗi của danh sách và sau đó nối.
Lưu ý thêm: Nếu không có nhu cầu rõ ràng để làm khác, hãy cân nhắc thêm "hủy bỏ" vào cuối chữ ký hàm của bạn để các hàm của bạn "không thành công nhanh" thay vì tiếp tục ngay cả khi một lệnh bên trong bị lỗi.
Cập nhật: OP đã hỏi liệu có thể giữ kỳ đà không CustomGrepCore(). Điều này đặt ra một số thách thức nhưng ... Nhưng không có gì. Đọc bản cập nhật cho câu trả lời được tham chiếu trong đoạn đầu tiên của câu hỏi này. Phương thức có đường dẫn đơn giản nhất để xử lý trường hợp sử dụng chuyển varargs từ trình gọi của timer_start đến hàm gọi lại được gọi bởi bộ định thời khi hàm đó cũng nhận varargs.