ใส่หมายเลขที่เพิ่มขึ้นในแต่ละบรรทัดในการเลือกหรือการจับคู่


10

ฉันมีปัญหาฉันสามารถคิดถึงวิธีการทั่วไปสองวิธีในการแก้ปัญหา แต่ฉันไม่ทราบวิธีที่เฉพาะเจาะจงสำหรับทั้งสองวิธี

...
Level 1:    cũng    also
Level 1:    và      and
Level 1:    như     like; such as
Level 2:    các     plural marker
Level 2:    của     belonging to
...

สำหรับแต่ละบรรทัดที่ขึ้นต้นด้วย "ระดับ n" ฉันต้องการใส่ตัวเลขโดยเริ่มต้นด้วย "01" เพื่อความง่ายเรามาเติมตัวเลขกัน

วิธีที่ 1: เลือกทุกบรรทัดด้วยตนเองในระดับเดียวกัน วิงวอนขอใช้เวทมนตร์ฉันจะเรียนรู้เร็ว ๆ นี้

วิธีที่ 2: เขียนการค้นหาและแทนที่ที่ตรงกับทุกบรรทัดด้วยระดับที่กำหนดซึ่งในแต่ละการแข่งขันรวมถึงตัวเลขในข้อความแทนที่ซึ่งเพิ่มขึ้นทีละหนึ่งด้วยการแข่งขันแต่ละ

ฉันได้พบคำถามที่คล้ายกันใน StackOverflowหรือไซต์ Vim อื่น ๆ แต่ดูเหมือนว่าจะมีปัญหาอย่างใดอย่างหนึ่งต่อไปนี้:

  1. เป็นเรื่องเกี่ยวกับการแทรกหมายเลขบรรทัดปัจจุบันแทนที่จะเป็นหมายเลขที่กำหนดเอง แต่เพิ่มขึ้น
  2. ไม่ทำให้ตัวเลขเป็นศูนย์
  3. ใช้งานไม่ได้กับการเลือกใน Vim 7.4 ที่ทำงานบน Windows 7 (สิ่งเหล่านี้ส่งผลให้เกิดข้อผิดพลาดE481: No range allowed)

ฉันใช้ gVim ใน Windows ด้วย mswin.vim แต่วิธีแก้ปัญหาที่ใช้ได้กับการติดตั้ง vanilla Vim ทั้งหมดโดยไม่ต้องปรับแต่งการตั้งค่าอาจจะดีที่สุด


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

1
ปลั๊กอินนี้จะไม่ได้เป็นโซลูชั่นที่สมบูรณ์สำหรับปัญหาของคุณ แต่มันจะมีประโยชน์อย่างมากสำหรับการเพิ่มคอลัมน์ของตัวเลข: VisIncr เอกสารที่นี่ FWIW
lcd047

คำตอบ:


17

คล้ายกับคำตอบที่https://vi.stackexchange.com/a/818/227คุณสามารถใช้คำสั่งโกลบอล

ด้วยคุณสามารถสั่งให้เป็นกลุ่มเพื่อค้นหาบรรทัดที่ตรงกับรูปแบบแล้วดำเนินการคำสั่งกับมัน

ในกรณีของคุณคุณต้องการเพิ่มข้อความลงในบรรทัดที่ขึ้นต้นด้วย "ระดับ N:" ดังนั้นคำสั่งทั่วโลกของเราอาจเป็นได้

:g/^Level \d:/{COMMANDS}

การใช้คำสั่ง replace (การแทนที่นิพจน์ปกติ) สำหรับคำสั่ง

คำสั่งนั้นสนุกกว่า ฉันมักจะต้องการเปลี่ยนนิพจน์ปกติสำหรับสิ่งเช่นนี้เนื่องจากมันง่ายต่อการใช้ตัวแปร

ตัวอย่างคำถามของคุณ

:let i = 1 | g/^Level \d:/s/^/\=printf("%02d ", i)/ | let i = i+1

มันทำงานอย่างไร

ในส่วนการแทนที่ของคำสั่งการทดแทนสามารถเป็นนิพจน์

สิ่งแรกที่เราจะทำคือตั้งค่าตัวแปรiให้เป็นหมายเลขเริ่มต้น ฉันเลือก 1 แต่หมายเลขใด ๆ ที่จะทำlet i = 1

จากนั้นเราก็เรียกใช้คำสั่งระดับโลกของเราซึ่งกำหนดให้เราดำเนินการในบรรทัดที่ตรงกัน g/^Level \d:/

เราจะให้คำสั่งระดับโลกของเราแทรกค่าและเพิ่มตัวนับของเราโดยใช้คำสั่งการแทนที่และคำสั่ง lets/^/\=printf("%02d ", i)/ | let i = i+1

นิพจน์ปกติของคำสั่งการแทนที่ค้นหาจุดเริ่มต้นของบรรทัด^และแทนที่ด้วยนิพจน์และนิพจน์ของเราจะเป็นผลลัพธ์ของการพิมพ์ที่จัดรูปแบบ เช่นเดียวกับในภาษา C printf ของ vim ใช้การจัดรูปแบบพารามิเตอร์ %02dวิธีการแปลงข้อโต้แย้งราวกับว่ามันเป็นตัวเลขทศนิยมdครอบครองอย่างน้อย 2 คัน2และแผ่นด้วย 00 สำหรับรายละเอียดและตัวเลือกการแปลงอื่น ๆ (รวมถึงการจัดรูปแบบจุดลอย) :help printfดู เราให้ตัวแปรการนับของเรา printf iและมันให้เรา01เป็นครั้งแรกครั้ง02ที่สอง ฯลฯ สิ่งนี้ถูกใช้โดยคำสั่งการแทนที่เพื่อแทนที่จุดเริ่มต้นของบรรทัดการแทรกผลลัพธ์ของ printf ที่จุดเริ่มต้นได้อย่างมีประสิทธิภาพ

โปรดทราบว่าฉันใส่ช่องว่างหลังจาก "%02d "D: คุณไม่ได้ถามคำถาม (และฉันไม่เห็นตัวอย่างผลลัพธ์) แต่ฉันสงสัยว่าคุณต้องการแยกตัวเลขออกจากคำว่า "ระดับ" ลบช่องว่างออกจากสายอักขระที่กำหนดให้ printf เพื่อให้มีหมายเลขแทรกอยู่ข้าง L ในระดับ

ท้ายที่สุดนั่นเป็นการlet i = i + 1เพิ่มตัวนับของเราหลังจากการเปลี่ยนตัวแต่ละครั้ง

สิ่งนี้สามารถนำไปใช้โดยทั่วไปเพื่อแทนที่ส่วนต่าง ๆ ของเส้นที่ถูกจับคู่โดยเกณฑ์อื่นด้วยข้อมูลการทำงานโดยพลการ

ใช้คำสั่งปกติรวมกัน

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

ตัวอย่างคำถามของคุณ

:let i = 1 | g/^Level \d:/execute "normal! I" . printf("%02d ", i) | let i = i+1

มันทำงานอย่างไร

ค่าที่ใช้นั้นคล้ายกับตัวทดแทน (เรายังคงใช้ printf เพื่อจัดรูปแบบหมายเลขของเราเพื่อให้เป็น 0 padded ด้วยตัวเลข 2 หลัก) แต่การดำเนินการแตกต่างกัน

ที่นี่เราใช้คำสั่ง execute ซึ่งรับสตริงและรันสตริงเป็นคำสั่ง ex ( :help :exe) เราสร้างสตริงที่รวม "ปกติ! ฉัน" กับข้อมูลของเราซึ่งจะเป็น "ปกติ! I01" ในครั้งแรกและ "ปกติ! I02" ครั้งที่สอง ฯลฯ

normalการดำเนินงานคำสั่งดำเนินการเช่นถ้าในโหมดปกติ ในตัวอย่างนี้คำสั่งปกติของเราคือIซึ่งแทรกที่จุดเริ่มต้นของบรรทัด ถ้าเราใช้ddมันจะลบบรรทัดoจะเปิดบรรทัดใหม่หลังจากบรรทัดที่ตรงกัน มันเหมือนกับว่าคุณพิมพ์I(หรือปฏิบัติการอื่น ๆ ) ตัวเองในโหมดปกติ เราใช้!after normalเพื่อให้แน่ใจว่าไม่มีการแมปเข้ามาในทางของเรา :help :normalดู

สิ่งที่ใส่เข้าไปแล้วคือค่าของ printf ของเราดังเช่นในตัวอย่างแรกของการใช้ทดแทน

วิธีการนี้จะมีนักเล่นกว่า regex เพราะคุณสามารถทำสิ่งที่ต้องการexecute "normal! ^2wy" . i . "th$p"ซึ่งจะไปที่จุดเริ่มต้นของข้อความ^ก้าวไปข้างหน้า 2 คำ2wงัดจนบอด 'H' ตัวละครy" . i . "thย้ายไปยังจุดสิ้นสุดของเส้นและวาง$p

นี่เป็นเหมือนการเรียกใช้แมโคร แต่ไม่ได้ใช้การลงทะเบียนและสามารถรวมสตริงจากนิพจน์ใด ๆ ฉันพบว่าสิ่งนี้มีพลังมาก

วิธีการที่แต่ละระดับมีเคาน์เตอร์ของตัวเอง

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

ก่อนอื่นให้ฟรี i ในกรณีที่เราใช้มันเป็นจำนวนเต็ม เราไม่สามารถแปลง i เป็นรายการเราต้องสร้างมันขึ้นมาแบบนั้น

:unlet! i

ถัดไปให้ตั้งเป็นรายการที่มีจำนวนระดับ คุณแสดงคำถาม 2 ข้อ แต่ให้สมมติ 10 เพื่อความสนุก เนื่องจากการจัดทำดัชนีรายการเป็นแบบ 0 และฉันไม่ต้องการแก้ไขการเรียงลำดับแบบ 1 เช่นรายการของคุณเราจะสร้างองค์ประกอบมากพอ (11) และไม่เคยใช้ดัชนี 0

:let j = 0
:let i = []
:while j < 11 | let i += [1] | let j += 1 | endwhile

ต่อไปเราต้องการวิธีในการรับหมายเลขระดับ โชคดีที่มีฟังก์ชั่นทดแทนเป็นฟังก์ชั่นเช่นกันดังนั้นเราจะให้สายของเราและแยกหมายเลขระดับsubstitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")

เนื่องจากฉันเป็นรายการ 11 1วินาที (แต่ละดัชนีเป็นตัวนับสำหรับระดับของเรา) ตอนนี้เราสามารถปรับตัวอย่างใดตัวอย่างหนึ่งข้างต้นเพื่อใช้ผลลัพธ์ของการทดแทนนี้:

ผ่านคำสั่งทดแทน:

:unlet! i | unlet! j | let j = 0 | let i = [] | while j < 11 | let i += [1] | let j += 1 | endwhile
:g/^Level \d:/let ind=str2nr(substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")) | s/^/\=printf("%02d ", i[ind])/ | let i[ind] += 1

ผ่านคำสั่งปกติ:

:unlet! i | unlet! j | let j = 0 | let i = [] | while j < 11 | let i += [1] | let j += 1 | endwhile
:g/^Level \d:/let ind=str2nr(substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")) | execute "normal! I" . printf("%02d ", i[ind]) | let i[ind] += 1

อินพุตตัวอย่าง:

Level 1: stuff

Level 1: Stuff

Some text
Level 3: Other

Level 1: Meh

Level 2: More

ตัวอย่างผลลัพธ์:

01 Level 1: stuff

02 Level 1: Stuff

Some text
01 Level 3: Other

03 Level 1: Meh

01 Level 2: More

ว้าวสารานุกรมมาก ฉันอ่านเฉพาะตอนแรกและรู้สึกว่าฉันได้เรียนรู้เกี่ยวกับ Vim มากขึ้นกว่าเดิมในช่วงไม่กี่ปีที่ผ่านมา นี่คือคำตอบชนิดหนึ่งที่ทำให้การแลกเปลี่ยนแบบสแต็คดีกว่าเว็บไซต์ถาม - ตอบทั่วไปและยังแสดงให้เห็นถึงประโยชน์ของการมีกลุ่ม SE ที่เป็นกลุ่ม
hippietrail

3

คุณสามารถสร้างจากhttps://stackoverflow.com/a/4224454/15934เพื่อเป็นศูนย์ตัวเลขของคุณ

" A reminder: how to start numbers at the first line
:'<,'>s/^\s*\zs/\=(line('.') - line("'<")+1).'. '

แต่เพื่อให้การดำเนินการของตัวเลขง่ายขึ้นฉันจะใช้ฟังก์ชันและคำสั่งคู่หนึ่ง:

command!  -range=% -nargs=? PrependNumbers <line1>,<line2>call s:Prepend(<args>)

function! s:ReplExpr(nb_digits, number)
  return repeat('0', a:nb_digits - strlen(a:number)).a:number
endfunction

function! s:Prepend(...) range
  let pattern = a:0 > 0 ? '\ze'. a:1 : '^'
  let nb_values = (a:lastline - a:firstline) + 1
  let nb_digits = strlen(nb_values)
  exe ':'.a:firstline.','a:lastline.'s#'.pattern.'#\=s:ReplExpr(nb_digits, 1+ line(".")-'.a:firstline.')." "#'
endfunction

กว่าให้เลือกบรรทัดและประเภทของ:PrependNumberคุณ (คุณจะเห็น:'<,'>PrependNumber[หมายเหตุ: คำสั่งใช้พารามิเตอร์ที่เป็นทางเลือก: รูปแบบก่อนที่จะใส่หมายเลข]


แต่ไม่ได้line(".")หมายความว่า "ใช้หมายเลขบรรทัดปัจจุบัน" ใช่หรือไม่ นั่นคือปัญหาของฉัน1.ด้วยคำตอบก่อนหน้านี้ที่ฉันสามารถหาได้
hippietrail

1
ใช่แล้ว. นั่นคือสาเหตุที่ฉันลบหมายเลขบรรทัดของบรรทัดแรกในช่วง
Luc Hermitte

อ่าฉันยังไม่ได้ทดสอบเพราะฉันจำไม่ได้ว่าจะใส่สคริปต์ใน Vim ได้ยังไงต้องอ่านก่อนนะ (-:
hippietrail

1
$HOME/vimfiles/plugin/whatevernameyouwish.vimในขณะที่คุณอยู่ภายใต้หน้าต่างคัดลอกวางโค้ดลง หรือแม้กระทั่งของคุณ$HOME/_vimrc(ชื่อไฟล์ windows) หากคุณต้องการ (ในครั้งแรก) หากคุณไม่แน่ใจว่า $ HOME คืออะไรในเครื่องของคุณให้ถามเป็นกลุ่มว่ามันคิดว่ามันคืออะไร ->:echo $HOME
Luc Hermitte

1
คุณไม่จำเป็นต้องใช้เลย:sourceหากสคริปต์ vim ติดตั้งอย่างถูกต้องในไดเรกทอรีที่ถูกต้อง ({rtp} / plugin หรือ {rtp} / ftplugin / {filetype} / ftplugin สำหรับปลั๊กอินเฉพาะประเภทไฟล์) :sourceคือสิ่งที่เราต้องเล่นกับเมื่อสิบกว่าปีที่แล้ว
Luc Hermitte

2

คุณสามารถเข้าใกล้มันได้โดยการบันทึกมาโคร เริ่มต้นด้วยไฟล์ต้นฉบับของคุณเติมอินสแตนซ์แรกด้วยหมายเลข

01 Level 1:    cũng    also
Level 1:    và      and
Level 1:    như     like; such as
Level 2:    các     plural marker
Level 2:    của     belonging to

เลื่อนเคอร์เซอร์ของคุณเพื่อเลือกและงัดมันด้วย01 yiwตอนนี้คุณต้องการบันทึกการกระทำของคุณจากจุดนี้

qq/^Level 1<CR>P<C-A>A<space><esc>0yiwq

  • qq เริ่มต้นแมโครใน register q
  • /^Level 1<CR> ค้นหาบรรทัดที่ขึ้นต้นด้วย "ระดับ 1"
  • P วางก่อนเคอร์เซอร์ (มีหมายเลขของคุณ)
  • <C-A> เพิ่มจำนวน
  • A<space><esc> แทรกช่องว่างหลังหมายเลข
  • 0 ย้ายไปที่จุดเริ่มต้น
  • yiw ดึงหมายเลขปัจจุบัน
  • q จบแมโคร

@qจากนั้นทำซ้ำแมโครนี้โดยใช้


1
คือ<C-A>การควบคุม + หรือไม่? ที่เลือกทุกอย่างใน Vim บน Windows
hippietrail

มันคือการควบคุม + a ในกลุ่มควรเพิ่มจำนวน หากคุณเปลี่ยนการโยงคีย์ของคุณเพื่อทำการกระทำของ Windows แทนการกระทำเป็นกลุ่มดังนั้นคุณจะไม่สามารถทำสิ่งที่คนส่วนใหญ่โพสต์ได้ที่นี่
jecxjo

ไม่มีเวอร์ชั่น Vim สำหรับ Windows ที่มาพร้อมกับการผูกที่แตกต่างกันเนื่องจากภูมิปัญญาที่ไม่มีที่สิ้นสุดของใครบางคน ฉันแค่ใช้การผูกวานิลลานอกกล่อง ฉันไม่เคยเปลี่ยนคีย์ Vim ในยี่สิบปีที่ฉันจำได้ (-:
hippietrail

ไม่ควรเป็นอย่างนั้นการติดตั้ง Windows ของฉันมีการเชื่อมโยงเป็นกลุ่มแบบปกติ เป็นไปได้ไหมที่คุณจะติดตั้ง vim ของคนอื่น? หรือคุณสามารถเรียกใช้กลุ่มในโหมด "ง่าย"? ฉันเชื่อว่า windows ติดตั้งไอคอนเดสก์ท็อปด้วยตัวเลือกโหมดปกติและใช้งานง่าย
jecxjo

3
ไม่เป็นเพราะการติดตั้งเริ่มต้นใน windows โหลด mswin.vim หากคุณสร้าง vimrc ของคุณเองและไม่โหลด mswin.vim คุณจะได้รับการผูก vim ปกติ ฉันใช้ vimrc เพียงครั้งเดียวสำหรับการติดตั้งทั้งหมดของฉัน (Linux, Mac และ Windows) และไม่เคยจัดการกับ mswin.vim สำหรับข้อมูลเพิ่มเติมเกี่ยวกับปัญหานี้โปรดดูstackoverflow.com/questions/289681/…
jecxjo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.