เคอร์เซอร์หลายตำแหน่งที่ต้องการ


14

ฉันใช้ปลั๊กอินvim-multiple-cursors

ฉันต้องการวางเคอร์เซอร์ตรงที่ฉันต้องการ ตัวอย่างเช่น ( [x]คือตำแหน่งเคอร์เซอร์):

Lorem ipsum dolor sit amet[1], consectetur adipiscing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna[2]
aliqua.

ในข้อความประเสริฐฉันมักจะวางเคอร์เซอร์แรกจากนั้นไปที่ตำแหน่งถัดไปด้วยปุ่มลูกศรและวางที่สอง

มีอะไรที่คล้ายกันใน VIM? ด้วยปลั๊กอินนี้หรืออื่น ๆ

แก้ไข

การติดตามความคิดเห็นความสนใจของฉันในการทำเช่นนั้นปรากฏขึ้นเมื่อพยายามเขียน
\section[My first section in this book]{My first section in this book}
ใน.texไฟล์ ปฏิกิริยาแรกของฉันคือการเขียน\section[]{}แล้วใส่สองเคอร์เซอร์เพื่อที่จะเขียนในสิ่งเดียวกันภายในและ[]{}

ตัวอย่างอื่น ๆ คือการเพิ่ม_someStuffหลังจากชื่อตัวแปรต่าง ๆ ตัวอย่างเช่นเปิดสิ่งนี้:

variable1 = 2
my_variable2 = 12
var3 = 14

เป็นนี้

variable1_someStuff = 2
my_variable2_someStuff = 12
var3_someStuff = 14

ด้วยเคอร์เซอร์หลายตัวฉันสามารถทำได้โดยเลือกเคอร์เซอร์คอลัมน์จากนั้นไปที่ส่วนท้ายของคำจากนั้นแทรก ฯลฯ แต่ฉันคิดว่าฉันจะเลือกตำแหน่งที่ฉันต้องการให้เคอร์เซอร์อยู่ตรงไหนได้ง่าย



1
ฉันคิดว่าคำถามสำคัญที่ต้องถามคือทำไมคุณถึงต้องการทำเช่นนั้น? คุณพยายามจะทำอะไร? การใช้เคอร์เซอร์หลาย ๆ อันนั้นไม่ได้ "ตามวิธี Vim" จริงๆแล้วอาจมีวิธีที่แตกต่างในการทำสิ่งที่คุณต้องการอย่างน้อยก็มีประสิทธิภาพด้วยการใช้ฟีเจอร์ในตัว
statox

@statox ดูการแก้ไข ฉันยกสองตัวอย่างที่อยู่ในใจของฉัน อันที่สองที่ฉันสามารถทำได้ด้วยปลั๊กอิน vim-multiple-cursors แต่อย่างแรกที่ฉันทำไม่ได้อย่างน้อยก็ในวิธีที่ง่าย
tomasyany

@ Nobe4 ขอบคุณสคริปต์ที่สองสร้างขึ้น
tomasyany

คำตอบ:


30

การใช้เคอร์เซอร์หลายตัวไม่ใช่สิ่งที่มีความจำเป็น

ดังที่ฉันได้กล่าวไว้ในความคิดเห็นโดยใช้เคอร์เซอร์หลายตัว (แม้จะมีปลั๊กอิน) ไม่ใช่ "ตามวิธี Vim" ฉันเข้าใจโดยสิ้นเชิงว่ามันน่าสนใจสำหรับคนที่มาจาก Sublime-Text แต่คุณมักจะพบทางเลือกที่อย่างน้อย มีประสิทธิภาพด้วยคุณสมบัติในตัวของ Vim

แน่นอนว่าการหาโซลูชันทางเลือกเหล่านี้ไม่ใช่เรื่องง่ายเสมอไปและบางครั้งก็ต้องใช้เวลา แต่มันจะง่ายขึ้นเมื่อใช้ Vim และคุณจะเห็นว่าเมื่อเวลาผ่านไป

เยี่ยมมาก แต่ฉันจะหาวิธีอื่นได้อย่างไร

ไม่มีคำตอบสากลเนื่องจากขึ้นอยู่กับสิ่งที่คุณพยายามจะทำฉันจะลองให้คำแนะนำเกี่ยวกับสิ่งแรกที่จะลอง:


คำสั่ง dot .

คำสั่ง Dot น่าจะเป็นหนึ่งในเครื่องมือที่ทรงพลังที่สุดใน Vim มันช่วยให้เราสามารถทำซ้ำการเปลี่ยนแปลงครั้งล่าสุดได้ ฉันไม่สามารถอธิบายได้ดีไปกว่า Drew Neil ในกลุ่ม Vim ของเขา ฉันคิดว่า Vimmer ทุกคนควรพิจารณาอ่านหนังสือเล่มนี้

จุดแข็งของคำสั่งนี้คือการเปลี่ยนแปลงครั้งล่าสุดอาจเป็นการกระทำที่ทำงานกับตัวอักษรบรรทัดหรือไฟล์ทั้งหมด ตัวอย่างเช่นการเปลี่ยนแปลงสามารถคั่นด้วยช่วงเวลาที่คุณเข้าสู่โหมดแทรกและช่วงเวลาที่คุณกลับไปที่โหมดปกติ

โดยที่ในใจมันเป็นเรื่องง่ายที่จะทำสิ่งที่คุณต้องการจะทำกับ multicursor:

  • ก่อนอื่นมาตั้งสภาพแวดล้อมของเรากันเถอะเขียนตามที่คุณแนะนำ

    \section[]{}

  • จากนั้นทำการเปลี่ยนแปลงที่ทำซ้ำได้ เคอร์เซอร์เปิดอยู่}กดF[เพื่อกลับไปที่[อักขระ จากนั้นเข้าสู่โหมดแทรกด้วยiและพิมพ์My first section in this bookแล้วกลับสู่โหมดปกติด้วยESC:

    \section[My first section in this book]{}

  • และนี่คือส่วนเวทย์มนตร์: ลองพิมพ์f{เพื่อวางเคอร์เซอร์บน{ตัวละครและกด.เพื่อทำการเปลี่ยนแปลงล่าสุดซ้ำ:

    \section[My first section in this book]{My first section in this book}

ความท้าทายทั้งหมดของคำสั่ง dot คือการเรียนรู้วิธีการเปลี่ยนแปลงที่ทำซ้ำได้: มันจะมาพร้อมกับ Vim grokking แต่พื้นฐานคือการเข้าใจวิธีการเปลี่ยนแปลงของคุณในทางที่ทำซ้ำได้

ยกตัวอย่างเช่นการแทรกลำไส้ใหญ่กึ่งที่ท้ายบรรทัดที่คุณจะชอบใช้ แทนA; $a;ทำไม?

เนื่องจากA;สร้างการกระทำแบบอะตอมมิกดังนั้นเมื่อคุณใช้.ในบรรทัดอื่นไม่ว่าคุณจะอยู่ที่ไหนในบรรทัดคุณจะแทรกเครื่องหมายโคลอนกึ่งท้ายที่ท้าย โดยที่เมื่อ$a;คุณใช้คุณแบ่งการเปลี่ยนแปลงออกเป็นสองส่วน$aและการแทรก;ดังนั้นหากคุณใช้การเปลี่ยนแปลง.จะเป็นการแทรกเครื่องหมายโคลอนกึ่งบนตำแหน่งปัจจุบันของเคอร์เซอร์

หมายเหตุn.สูตรวิเศษในกลุ่มคือ เวิร์กโฟลว์ที่ยอดเยี่ยมจริงๆคือ:

  • ค้นหาสถานที่ที่คุณต้องการแก้ไข /pattern
  • ทำการแก้ไขที่ทำซ้ำได้ของคุณ
  • ใช้nเพื่อไปยังสถานที่ถัดไปเพื่อแก้ไข
  • ใช้.เพื่อแก้ไขซ้ำ
  • ทำซ้ำสองขั้นตอนสุดท้าย: คุณคือราชาแห่งโลก (หรืออย่างน้อยก็มีการแก้ไข)

แมโคร

มาโครเป็นอีกหนึ่งเครื่องมือที่มีความสำคัญอย่างยิ่งใน Vim เนื่องจากช่วยให้คุณสามารถบันทึกลำดับการกดแป้นและทำซ้ำราวกับว่าคุณพิมพ์อีกครั้ง

ฉันจะใช้กรณีการใช้งานครั้งที่สองของคุณ:

variable1 = 2
my_variable2 = 12
var3 = 14

สิ่งสำคัญคือการเรียนรู้วิธีทำให้มาโครของคุณมีประสิทธิภาพ (ฉันจะยกตัวอย่างการนับหลังจาก):

  • วางเคอร์เซอร์บนคำและเริ่มมีการบันทึกแมโครของคุณด้วยvariable1 qqซึ่งหมายความว่า "เริ่มบันทึกการกดแป้นในอนาคตของฉันทั้งหมดในการลงทะเบียนชื่อq"

  • เริ่มทำการพิมพ์แก้ไขของคุณ:

    • 0 ไปที่จุดเริ่มต้นของบรรทัด
    • e ไปที่ท้ายคำแรก
    • a เพื่อต่อท้ายเคอร์เซอร์ของคุณ
    • .someStuff เพื่อต่อท้ายข้อความที่ต้องการ
    • <Esc> เพื่อหยุดการแทรก
    • j เพื่อไปที่บรรทัดถัดไป
    • q เพื่อหยุดการบันทึกแมโคร
  • คุณจะได้รับ:

variable1.someStuff = 2
my_variable2 = 12
var3 = 14
  • ตอนนี้คุณสามารถใช้แมโครเพื่อทำซ้ำการแก้ไขของคุณ @qในขณะที่คุณอยู่ในสายที่เหมาะสมในการแก้ไขคุณก็สามารถดำเนินการแมโครด้วย ตามที่เราต้องการเรียกใช้งานสองครั้งคุณสามารถใช้2@qและคุณจะได้รับผลลัพธ์ต่อไปนี้:
variable1.someStuff = 2
my_variable2.someStuff = 12
var3.someStuff = 14

หมายเหตุ 1อย่างที่คุณอาจสังเกตเห็นการใช้0eaที่จุดเริ่มต้นของแมโครนั้นสำคัญมาก แน่นอนถ้าคุณวางเคอร์เซอร์ที่ท้ายคำแรกก่อนที่จะบันทึกแมโครและดำเนินการอีกครั้งผลลัพธ์ของคุณจะเป็นดังนี้:

variable1.someStuff = 2
my_variable2 = 12.someStuff
var3 = 14.someStuff

เมื่อเคอร์เซอร์ของคุณข้อความจะถูกแทรกที่ตำแหน่งเคอร์เซอร์หลังจากเปลี่ยนบรรทัด (เช่นจุดสิ้นสุดของบรรทัดในกรณีนี้)

หมายเหตุ 2มาโครนั้นทรงพลังอย่างยิ่งและคุณสามารถสร้างมาโครแบบเรียกซ้ำเมื่อคุณพอใจกับมัน มาโครที่นี่อาจเป็น:

`0ea.someStuff<Esc>j@q`

สุดท้าย@qจะได้เรียกมาโครด้วยตัวเองแทนการใช้2@q; คุณเพิ่งได้ใช้@qและงานทั้งหมดจะเสร็จสิ้น


บล็อกภาพ

นี่คือเคล็ดลับอื่นที่ไม่ได้นำไปใช้โดยตรงกับกรณีการใช้งานของคุณ แต่อาจมีประโยชน์มากในการแก้ไขบรรทัดจำนวนมากในเวลาเดียวกัน มารับสารสกัดของรหัส CSS นี้:

li.one   a{ background-image: url('/images/sprite.png'); }
li.two   a{ background-image: url('/images/sprite.png'); }
li.three a{ background-image: url('/images/sprite.png'); }

เกิดอะไรขึ้นถ้าคุณย้ายไปรต์จากimagesไปcomponents?

ดีที่คุณสามารถวางเคอร์เซอร์บนiของและกดimages <C-v>นี่จะเป็นการเริ่มโหมดบล็อกภาพซึ่งอนุญาตให้เลือกบล็อกได้ ตอนนี้คุณสามารถพิมพ์t/ เพื่อเลือกคำที่คุณต้องการเปลี่ยนและ2jเพื่อเลือกคำที่เกิดขึ้นทั้งหมด

หลังจากนั้นคุณก็ต้องพิมพ์ที่จะเปลี่ยนคำแล้วc componentsเมื่อคุณออกจากโหมดแทรกคุณจะเห็น:

li.one   a{ background-image: url('/components/sprite.png'); }
li.two   a{ background-image: url('/components/sprite.png'); }
li.three a{ background-image: url('/components/sprite.png'); }

คำสั่งระดับโลก

คำสั่งโกลบอลเป็นเครื่องมือที่อนุญาตให้ใช้คำสั่ง ex mode บนบรรทัดที่จับคู่รูปแบบอีกครั้งซึ่งเป็นวิธีที่ดีในการใช้การเปลี่ยนแปลงแบบเดียวกันในสถานที่ที่แตกต่างกันโดยไม่ต้องใช้เคอร์เซอร์หลายตัว

ไวยากรณ์มีดังต่อไปนี้:

:[range] g / pattern / command

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับพารามิเตอร์โปรดดู[range] :h :rangeฉันจะไม่บอกรายละเอียดที่นี่ฉันจะเตือนว่ามัน%หมายถึงไฟล์ทั้งหมด'<,'> แสดงถึงสิ่งที่เลือกล่าสุดและ1,5แสดงบรรทัดที่ 1 ถึง 5 ของไฟล์

พารามิเตอร์นี้กำหนดบรรทัดที่จะถูกใช้โดยคำสั่งโกลบอล หากไม่ได้ระบุช่วงไว้คำสั่งโกลบอลจะใช้%ตามค่าเริ่มต้น

อาร์กิวเมนต์ [pattern] เป็นรูปแบบการค้นหาตามที่คุณคุ้นเคยกับเครื่องมือค้นหา เนื่องจากมันรวมประวัติการค้นหาคุณสามารถปล่อยให้ฟิลด์นี้ว่างและคำสั่งโกลบอลจะใช้รูปแบบการค้นหาล่าสุดในประวัติการค้นหา

ในที่สุดพารามิเตอร์ [command] คือคำสั่ง ex ตามที่คุณอาจคุ้นเคย

ตอนนี้พฤติกรรมของคำสั่งทั่วโลกนั้นค่อนข้างง่าย:

  • ทำซ้ำผ่านบรรทัดทั้งหมดที่กำหนดไว้ในพารามิเตอร์ [range]
  • หากบรรทัดปัจจุบันตรงกับรูปแบบที่กำหนดให้ใช้คำสั่ง

เนื่องจากพารามิเตอร์ [command] เป็นคำสั่ง ex คุณสามารถทำสิ่งต่างๆมากมาย ลองใช้โค้ดหลอกต่อไปนี้ซึ่งไม่ค่อยน่าสนใจและมีข้อความดีบั๊กมากมาย:

var myList  = null
var i       = 0

myList = new List()
echo "List instantiated"

for (i=0; i<10; i++)
    myList.Add(i)
    echo i . " added to the list"

echo "end of for loop"

ตอนนี้สมมติว่าคุณแน่ใจว่ารหัสนี้ใช้งานได้และคุณต้องการลบechoข้อความที่ไร้ประโยชน์เหล่านี้:

  • คุณสามารถใช้คำสั่งโกลบอลของคุณกับไฟล์ทั้งหมดดังนั้นคุณจะต้องเสริมคำสั่งด้วย%(หรือไม่มีสิ่งใดนับ%เป็นช่วงเริ่มต้น)

  • คุณรู้ว่าบรรทัดที่คุณต้องการลบทั้งหมดตรงกับรูปแบบ echo

  • คุณต้องการลบบรรทัดเหล่านี้ดังนั้นคุณจะต้องใช้คำสั่ง:delete ที่สามารถย่อเป็นd

ดังนั้นคุณจะต้องใช้ฟังก์ชั่นต่อไปนี้:

:%global/echo/delete

ซึ่งยังสามารถย่อได้ดังนี้

:g/echo/d

โปรดทราบว่า%หายไปglobalจะถูกย่อเป็นgและเป็นdelete dตามที่คุณอาจจินตนาการถึงผลลัพธ์คือ:

var myList  = null
var i       = 0

myList = new List()

for (i=0; i<10; i++)
    myList.Add(i)

หมายเหตุ 1จุดสำคัญที่ต้องใช้เวลาพอสมควรในการตระหนักคือ normalคำสั่งนั้นเป็นคำสั่ง ex ซึ่งหมายความว่าคุณสามารถใช้มันกับคำสั่งทั่วโลก ที่จะมีประสิทธิภาพจริงๆ: n.ขอบอกว่าผมต้องการที่จะทำซ้ำทุกสายที่มีก้องฉันไม่จำเป็นต้องแมโครหรือแม้กระทั่งสูตรมายากล ฉันสามารถใช้

:g/echo/normal YP

และ voila:

var myList  = null
var i       = 0

myList = new List()
echo "List instantiated"
echo "List instantiated"

for (i=0; i<10; i++)
    myList.Add(i)
    echo i . " added to the list"
    echo i . " added to the list"

echo "end of for loop"
echo "end of for loop"

หมายเหตุ 2 "เฮ้ถ้าฉันต้องการใช้คำสั่งของฉันในบรรทัดที่ไม่ตรงกับรูปแบบเฉพาะ"

globalมีคำสั่งตรงข้ามvglobalยากvที่ทำงานเหมือนกันglobalยกเว้นว่าคำสั่งจะถูกนำมาใช้ในสายที่ไม่ตรงกับ [รูปแบบ] พารามิเตอร์ วิธีนี้ถ้าเราสมัคร

:v/echo/d

ในตัวอย่างก่อนหน้าของเราเราได้รับ:

echo "List instantiated"
    echo i . " added to the list"
echo "end of for loop"

คำสั่งได้ถูกนำมาใช้บนเส้นซึ่งไม่ได้มีdeleteecho


ที่นี่ฉันหวังว่าคำแนะนำเล็กน้อยเหล่านั้นจะทำให้คุณมีความคิดเกี่ยวกับวิธีการกำจัดปลั๊กอินเคอร์เซอร์หลายของคุณและใช้เป็นกลุ่มในวิธี Vim ;-)

ดังที่คุณสามารถจินตนาการได้ว่าตัวอย่างเหล่านี้ค่อนข้างเรียบง่ายและถูกสร้างขึ้นมาเพื่อแสดงให้เห็นว่าเมื่อคุณทำตามวิธีการที่เป็นกลุ่มคุณไม่ค่อยต้องการเคอร์เซอร์หลายตัว คำแนะนำของฉันจะเป็นเมื่อคุณพบสถานการณ์ที่คุณคิดว่ามันจะมีประโยชน์เขียนลงและใช้เวลาในภายหลังเพื่อหาทางออกที่ดีกว่า 99% ของเวลาที่คุณจะได้พบกับวิธีที่เร็ว / มีประสิทธิภาพมากขึ้นในที่สุด

นอกจากนี้ฉันจะทำซ้ำตัวเองอีกครั้ง แต่ฉันสนับสนุนให้คุณอ่าน Practical Vimโดย Drew Neil เพราะหนังสือเล่มนี้ไม่เกี่ยวกับ "วิธีการทำสิ่งนั้นหรือสิ่งนี้ใน Vim" เป็นเรื่องเกี่ยวกับ "วิธีการเรียนรู้ที่จะคิดด้วยวิธี Vim" ซึ่งจะช่วยให้คุณสร้างโซลูชันของคุณเองเพื่อแก้ไขปัญหาในอนาคตของคุณในทางที่ดี


ป.ล.ขอบคุณเป็นพิเศษสำหรับ @Alex Stragies สำหรับงานแก้ไขและการแก้ไขที่เขาทำกับโพสต์อันยาวนานนี้


คำตอบที่สมบูรณ์มาก จริง ๆ แล้วฉันรู้คำสั่งเหล่านั้นทั้งหมด แต่ไม่เคยคิดที่จะใช้มันอย่างนั้น ... ฉันเดาว่าฉันต้องฝึกฝนมากกว่านี้ อย่างไรก็ตามฉันต้องบอกว่าบางครั้ง 'เสียงเรียกเข้า' ใช้เวลานานกว่าการใช้ปลั๊กอินบางตัว (ตัวอย่างเช่นตัวอย่าง CSS ของคุณสามารถทำได้เร็วกว่าปลั๊กอินที่ฉันกล่าวถึง) และบางครั้งก็ใช้เวลาสั้นกว่ามาก เสียงส่วนใหญ่จะเร็วกว่าตัวแก้ไขข้อความใด ๆ หากใช้อย่างเหมาะสม)
tomasyany

Btw ฉันคิดว่าคำตอบของคุณค่อนข้างสมบูรณ์แล้ว ฉันจะยอมรับ แต่ถ้าคุณต้องการเพิ่มรายละเอียดโปรดทำและฉันจะดีใจที่ได้อ่าน
tomasyany

1
@ Tomasyany: บางครั้งการใช้ "วิธี vim" อาจดูนานกว่านี้ (ฉันไม่สามารถนับได้ว่าฉันใช้เวลามากขึ้นในการสร้างมาโครการทำงานมากกว่าที่ฉันได้ทำการเปลี่ยนแปลงด้วยตนเอง) แต่ด้วยการฝึกฝนในที่สุดมันก็เร็วขึ้น ฉันใช้ Vim เพียงหนึ่งปีและฉัน (มาก) อยู่ไกลจากการเป็นผู้เชี่ยวชาญ แต่ฉันรู้สึกได้ว่าการแก้ไขของฉันดีขึ้นอย่างต่อเนื่องในขณะที่ฉันฝึกฝน
statox

1
ควรพูดถึงgnวัตถุข้อความด้วยเช่นกัน มันกล่าวถึงกรณีการใช้งานของเคอร์เซอร์หลายตัว ค้นหาด้วย/แล้วdgnหรือแล้วทำซ้ำกับcgn .
Justin M. Keyes

@ จัสตินฉันไม่คุ้นเคยกับgnฉันฉันจะลองและเพิ่มหลังจาก ขอบคุณสำหรับหัวขึ้น!
statox

5

การใช้งานที่ได้รับมาของคุณให้ฉันฟังดูเหมือนอยากรู้.คำสั่งคีย์ (ทำซ้ำคำสั่งแก้ไขล่าสุด)

  • ไปที่ตำแหน่งแรกกดiแทรกข้อความของคุณ<ESC>
  • ย้ายไปที่ตำแหน่ง 2 กด.(จุด) ==> แทรกข้อความเหมือนกัน
  • หากต้องการเลื่อนลงหนึ่งบรรทัดที่นี่และแทรกอีกครั้ง (เมื่อแทรกเข้าไปในบรรทัดที่ต่อเนื่องกัน) กด <ALT>-j.

ตัวอย่างข้อความของคุณสำหรับฉันได้รับการแก้ไขโดย:

I\section[]{}<ESC>hhiMy first Sec...<ESC>lll.


มันเป็นทางออกที่ดีงาม ฉันรู้.คำสั่งที่สำคัญอย่างสมบูรณ์แบบแต่ไม่เคยคิดที่จะใช้มันอย่างนั้น ฉันเห็นด้วยมันเป็นวิธีที่ดี แต่คำตอบของคุณไม่ได้อ้างถึงคำถามจริงดังนั้นจึงไม่เหมาะสมที่จะยอมรับว่าฉันเดา
tomasyany

1
คำถามของคุณ (หลังจากที่คุณเพิ่มกรณีการใช้งานที่ต้องการ) ทำให้ฉันนึกถึง "ฉันชอบกรรไกรของฉันมากและต้องการขันสกรูให้แน่นด้วยพวกเขาบอกฉันว่า" และฉันชี้ให้คุณใช้ไขควงอย่างดี;) (ฉันเพิ่มเป็นคำตอบเพราะฉันไม่รู้วิธีการจัดรูปแบบในความคิดเห็น)
Alex Stragies

4

วิธีการเป็นกลุ่มเพิ่มเติม

งานแรก:

\section[foobar baz]{foobar baz}

ขั้นตอนการทำงาน:

\section[]{}
hh
ifoobar baz<Esc>
$.

ภารกิจที่สอง:

variable1 = 2
my_variable2 = 12
var3 = 14

คำสั่ง:

3:norm ea_somestuff

ผลลัพธ์:

variable1_somestuff = 2
my_variable2_somestuff = 12
var3_somestuff = 14
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.