Vim Script: วิธีส่ง Varargs ไปยัง lambda ใน timer_start
ฉันกำลังกำหนดค่ากลุ่มของฉันด้วย Vim Script
ฉันได้ตั้งค่าฟังก์ชัน grep ที่กำหนดเองไว้ด้านล่าง:
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>)
รหัสด้านบนทำงานได้ตามที่คาดไว้ ฉันสามารถดำเนินการ:Grep xxxเพื่อทำ grep ที่กำหนดเองในกลุ่มของฉัน
ตอนนี้ฉันต้องการใช้คุณสมบัติ vim 8: timer_startเพื่อทำให้ grep async ที่กำหนดเองของฉันตามความเข้าใจของฉันฉันต้องเขียนโค้ดใหม่ให้กับฟังก์ชัน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
แต่ฉันมักจะได้รับข้อผิดพลาด: Invalid argument.
จะแก้ไขได้อย่างไร?
นอกจากนี้อย่างที่คุณเห็นว่าฉันใช้timer_startมันฉันรู้ว่ามันจะสร้างรหัสงานซึ่งทำให้เราสามารถหยุดงานชั่วคราว / หยุดงานได้ ในกรณีของฉันฉันต้องหยุดงานอย่างชัดเจนหรือไม่?
เพิ่มอีกหนึ่งสิ่ง
มีปัญหาอีกอย่างหนึ่งคือวิธีที่จะผ่าน varargs จากแลมบ์ดาในฟังก์ชั่นการทำงานCustomGrepCustomGrepCore
ฉันพบลิงค์นี้: https://www.reddit.com/r/vim/comments/3761po/vimscript_question_passing_arguments_from_a/
แต่เรามีกรณีที่แตกต่างกันที่นี่ เพราะการที่เรานำมาใช้ในการทำงานexecute() CustomGrepเป็นไปได้ไหมที่จะส่ง varargs จากexecute()ไปยังฟังก์ชันCustomGrepCore? พารามิเตอร์ของexecute()เป็นสตริง แต่ถ้าเราแปลงa:000สตริง ( string(a:000)) ผมต้องเปลี่ยนฟังก์ชั่นCustomGrepCoreเพราะถ้าฉันผ่านstring(a:000)ไปCustomGrepCoreค่าของการจะเป็นa:01
ดังนั้นเป็นไปได้ไหมที่จะส่ง varargs จากexecute()ฟังก์ชันอื่น CustomGrepCoreถ้าไม่ดีผมต้องเปลี่ยนฟังก์ชั่น
คำตอบ
คำถามนี้ส่วนใหญ่เป็นการตอบคำถามติดตามผลที่ OP มีเพื่อตอบสนองต่อคำตอบที่ฉันให้กับคำถามอื่นที่พวกเขาโพสต์ สิ่งนี้ยังพูดถึงโซลูชันที่เกี่ยวข้องกับฟังก์ชันแลมบ์ดาและการปิดที่ถูกต้อง แต่ซับซ้อนกว่าที่จำเป็นสำหรับกรณีการใช้งานส่วนใหญ่ ด้วยเหตุผลทั้งสองนี้ฉันขอแนะนำให้คุณตรวจสอบถาม& ตอบก่อน: วิธีเริ่มฟังก์ชัน async ใน Vim 8 หากคุณต้องการข้อมูลเชิงลึกเพิ่มเติมเล็กน้อยเกี่ยวกับหัวข้อ (และหัวข้อต่อพ่วง) คุณสามารถกลับมาที่หัวข้อนี้ได้ในภายหลัง
ฉันสงสัยว่าทำไมคุณกำลังใช้ FuncRef CustomGrepCore()ในรหัสเดิมที่จะทำให้การเรียกร้องให้ ดูเหมือนจะไม่มีความจำเป็นอย่างชัดเจนสำหรับมัน ในโค้ดใหม่มันทำให้สิ่งต่าง ๆ ซับซ้อนเนื่องจากคุณต้องจัดการกับสองระดับของทิศทาง (FuncRef + lambda) แทนที่จะเป็นเพียงระดับเดียวสำหรับแลมด้า
ดังนั้นคำแนะนำแรกของฉันคือใช้บางอย่างเช่นcall CustomGrepCore(param)ก่อนที่จะแตะสิ่งจับเวลา
ไม่ว่าข้างต้นจะเป็นอย่างไรลองดูอาร์กิวเมนต์ที่สองของ timer_start สิ่งนี้ยุ่งยากไม่ต้องสงสัยเลย แต่คุณหลงทางจากสิ่งที่ฉันเขียนจริงๆ มีสามสิ่งที่ไม่ตรงกันระหว่างสิ่งที่คุณมี
{param -> execute("call F(param)")}
และสิ่งที่ฉันมี
{-> execute("call LongRunningFun('" . a:patt . "')", "")}
ประการแรกparamก่อนหน้านี้->มาจากไหน? โยนมัน
ประการที่สองฉันกำลังผ่านสอง args เพื่อดำเนินการและคุณกำลังผ่านไปหนึ่งอัน คุณอาจจะเข้าใจในสิ่งที่หมายถึงนี้และได้ทำเพื่อจงใจ แต่เพียงในกรณีที่ไม่ได้กรณีที่ ... :silentออกนอกพระรามสองจะเทียบเท่ากับการเรียกใช้คำสั่งที่ไม่ได้รับสิทธิในการโต้แย้งครั้งแรกกับ ผ่านสตริงที่ว่างเปล่าเป็นพารามิเตอร์ที่สองคือเทียบเท่ากับการทำงานพวกเขาโดยไม่ต้อง :silentในตอนแรกที่เขียนโค้ดนี้ฉันจะไม่ใช้ความเงียบเป็นการส่วนตัว หลังจากทำงานเสร็จแล้วฉันอาจเพิ่มมัน
สุดท้ายพารามิเตอร์แรกของคุณคือสตริงเดี่ยวแบบคงที่ในขณะที่ของฉันเป็นการต่อกันของสตริงคงที่สองสตริงและนิพจน์ ( a:patt) ในขณะที่คุณตั้งใจจะส่งผ่านค่าที่มีอยู่ในตัวแปรโลคัลparamไปยังexecute()สิ่งที่คุณกำลังทำอยู่จริงๆคือการส่งสตริงตัวอักษร "param" สิ่งนี้ใช้กับทุกสิ่งที่อยู่ในเครื่องหมายคำพูด
อย่างไรก็ตามเพื่อหลีกเลี่ยงการลากสิ่งนี้ออกไปฉันจะแสดงให้คุณเห็นว่าฉันจะทำอย่างไร อย่างแรกฉันจะไม่ใช้-nargs=?ในคำสั่งของคุณ ฉันจะใช้+หรือ*แทนที่?ขึ้นอยู่กับว่าศูนย์ args ( :Grep) เป็นการโทรที่ถูกต้องหรือไม่ จากนั้นจะใส่แต่ละอาร์กิวเมนต์ในช่องแยกในรายการพารามิเตอร์ นอกจากนี้อย่าใช้ varargs ( ...) เว้นแต่คุณจะต้องการจริงๆ พวกเขาเพิ่มระดับของทิศทางที่ทำให้สิ่งต่างๆยุ่งยาก อาจเป็นไปไม่ได้ในกรณีนี้ ดังนั้นเปลี่ยน CustomGrepCore เพื่อให้ยอมรับอาร์กิวเมนต์เดียวซึ่งจะเป็นรายการ นี่คือการสาธิตวิธีการทำงานเมื่อเราได้ชิ้นtimer_startส่วนที่ถูกต้อง
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'
ด้วยสิ่งที่เราจะได้รับสิ่งที่ทำงาน ...
" 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 . ")", "")})
ส่วนที่ไม่ใช่ที่เห็นได้ชัดของที่นี่คือความจำเป็นในการที่จะผ่านการ varags รายการผ่านa:000 รับเป็นสตริงพารามิเตอร์แรก (ซึ่งประเมินเป็นนิพจน์) คุณไม่สามารถเชื่อมสตริงและรายการเข้าด้วยกัน คุณจะได้รับข้อผิดพลาดหากคุณลอง ดังนั้นเราจำเป็นต้องแปลงเป็นการแสดงสตริงของรายการแล้วเชื่อมต่อกันstring()execute()
หมายเหตุเพิ่มเติม: หากไม่จำเป็นต้องทำอย่างอื่นอย่างชัดเจนให้พิจารณาเพิ่ม "ยกเลิก" ที่ส่วนท้ายของลายเซ็นฟังก์ชันของคุณเพื่อให้ฟังก์ชันของคุณ "ล้มเหลวอย่างรวดเร็ว" แต่จะดำเนินต่อไปแม้ว่าคำสั่งภายในจะล้มเหลวก็ตาม
อัปเดต: OP ถามว่าเป็นไปได้ไหมที่จะเก็บ varargs CustomGrepCore()ไว้ นี่เป็นการท้าทาย แต่ ... แต่ไม่มีอะไร อ่านการอัปเดตคำตอบที่อ้างถึงในย่อหน้าแรกของคำถามนี้ วิธีนี้เป็นเส้นทางที่ง่ายที่สุดในการจัดการกรณีการใช้งานของการส่ง varargs จากผู้เรียกของ timer_start ไปยังฟังก์ชันเรียกกลับที่เรียกใช้โดยตัวจับเวลาเมื่อฟังก์ชันนั้นรับ varargs ด้วย