선택 또는 일치하는 각 줄에 증분 번호를 삽입하십시오


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
...

"Level n"으로 시작하는 각 줄에 대해 "01"로 시작하는 숫자를 삽입하고 싶습니다. 간단하게하기 위해 숫자 앞에 붙입니다.

접근법 1 : 동일한 레벨을 가진 모든 라인을 수동으로 선택하십시오. 내가 곧 배울 마법을 불러라.

접근 방식 2 : 각 일치 항목에서 대체 텍스트에 숫자를 포함하는 지정된 수준의 모든 줄과 일치하는 검색 및 바꾸기를 작성하십시오. 각 일치 항목마다 하나씩 증가합니다.

StackOverflow 또는 다른 Vim 사이트 에서 비슷한 질문 을 찾았 지만 각각 다음과 같은 문제가 하나 이상있는 것 같습니다.

  1. 임의이지만 증가하는 숫자가 아닌 현재 줄 번호를 삽입하는 것입니다.
  2. 숫자를 0으로 채우지 않습니다.
  3. 실제로 Windows 7에서 실행되는 Vim 7.4의 선택 항목에서는 작동하지 않습니다 (이러한 오류가 발생 E481: No range allowed합니다).

mswin.vim을 사용하여 Windows에서 gVim을 실행하고 있지만 설정을 사용자 정의하지 않고 모든 바닐라 Vim 설치에서 작동하는 솔루션이 가장 좋습니다.


질문에 대해 생각할 수있는 가장 좋은 태그는 존재하지 않습니다. 선택 및 범위가 있으므로 태그를 다시 지정하거나 태그 중 하나를 작성하십시오.
— hippietrail

1
이 플러그인은 문제에 대한 완벽한 솔루션은 아니지만 숫자 열을 추가하는 데 매우 유용합니다 : VisIncr . 여기에 문서가 있습니다 . FWIW.
— lcd047

답변:


17

https://vi.stackexchange.com/a/818/227 의 답변과 유사하게 전역 명령을 사용할 수 있습니다.

이를 통해 vim에게 패턴과 일치하는 행을 검색 한 다음 명령을 수행하도록 지시 할 수 있습니다.

귀하의 경우 "Level N :"으로 시작하는 줄 앞에 텍스트를 추가하여 글로벌 명령이 될 수 있습니다.

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

명령에 대체 명령 (정규 표현식 대체) 사용

명령이 더 재미 있습니다. 변수를 사용하기 쉽기 때문에 보통 이런 식으로 정규 표현식을 대체하고 싶습니다.

질문의 예

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

작동 원리

대체 명령의 대체 섹션에서 표현식이 될 수 있습니다.

가장 먼저 할 일은 변수 i를 시작 번호로 설정하는 것 입니다. 나는 1을 선택했지만 아무 숫자 나 할 것입니다.let i = 1

그런 다음 전역 명령을 실행하여 일치하는 행에서 작업을 수행하도록 설정합니다. g/^Level \d:/

전역 명령이 값을 삽입하고 치환 명령과 let 명령을 사용하여 카운터를 증가시킵니다.s/^/\=printf("%02d ", i)/ | let i = i+1

대치 명령의 정규식은 줄의 시작 부분을 찾아서 식으로 ^대체하며 식은 형식화 된 인쇄의 결과입니다. C 언어와 마찬가지로 vim의 printf는 형식 매개 변수를 사용합니다. %02d는 인수가 10 진수 인 것처럼 변환하며 d, 최소 2 개의 공백을 차지하고 20으로 채워 0집니다. 자세한 내용 및 기타 변환 옵션 (부동 소수점 서식 포함)은을 참조하십시오 :help printf. printf에 카운팅 변수 i를주고 01처음, 02두 번째 등을줍니다. 이것은 대체 명령에 의해 행의 시작 부분을 대체하여 시작 부분에 printf의 결과를 효과적으로 삽입하는 데 사용됩니다.

d : 뒤에 공백을 넣습니다 "%02d ". 당신은 질문에 그것을 요구하지 않았고 (예제 출력을 보지 못했습니다), 숫자를 단어 "Level"과 분리하고 싶었습니다. printf에 주어진 문자열에서 공백을 제거하여 레벨에서 L 바로 옆에 숫자를 삽입하십시오.

마지막으로 let i = i + 1각 교체 후 카운터 를 증가시킵니다.

이것은 일반적으로 다른 기준과 일치하는 라인 부분을 임의의 기능 데이터로 대체하는 데 적용 할 수 있습니다.

결합 된 일반 명령 사용

이것은 간단한 삽입이나 복잡한 편집에 좋습니다. 대체와 마찬가지로 전역을 사용하여 일치 시키지만 정규 표현식 대체 대신 사용자가 입력 한 것처럼 일련의 작업을 실행합니다.

질문의 예

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

작동 원리

사용 된 값은 대체와 매우 유사하지만 (여전히 printf를 사용하여 숫자를 2 자리 숫자로 채우도록 숫자를 형식화합니다) 작업이 다릅니다.

여기에서는 문자열을 가져 와서 문자열을 ex 명령 ( :help :exe) 으로 실행하는 execute 명령을 사용합니다 . "normal! I"과 데이터를 결합한 문자열을 생성합니다.이 데이터는 "normal! I01"과 "normal! I02"등입니다.

이 normal명령은 일반 모드 에서처럼 작업을 수행합니다. 이 예에서 일반 명령은입니다 I. 이는 행의 시작 부분에 삽입됩니다. 우리가 dd그것을 사용했다면 라인을 삭제 o하고 일치하는 라인 다음에 새로운 라인을 열 것입니다. 마치 I일반 모드에서 직접 입력 한 것 (또는 다른 작업)과 같습니다. !after normal를 사용하여 매핑이 방해받지 않도록합니다. 참조하십시오 :help :normal.

대체를 사용하는 첫 번째 예와 같이 printf의 값이 삽입됩니다.

이 방법은 정규식보다 더 빠를 수 있습니다 execute "normal! ^2wy" . i . "th$p". 텍스트의 시작 부분으로 ^이동하고, 2 단어 앞으로 이동하고 2w, i 번째 'h'문자가 될 때까지 잡아 당기고 y" . i . "th, 줄의 끝으로 이동하여 $붙여 넣기와 같은 작업을 수행 할 수 있기 때문 p입니다.

이것은 매크로를 실행하는 것과 거의 비슷하지만 실제로 레지스터를 사용하지 않으며 모든 표현식의 문자열을 결합 할 수 있습니다. 나는 이것이 매우 강력하다는 것을 알았습니다.

각 레벨에 자체 카운터가있는 방법

각 레벨에 자체 카운터를 갖기를 원할 수 있습니다. 미리 최대 레벨 수를 알고 있다면 다음을 수행 할 수 있습니다 (최대 레벨을 찾기 위해 추가 코드를 추가하는 것은 어렵지 않지만이 답변을 너무 길게 만들 수 있습니다.

먼저, 정수로 이미 사용한 경우에는 i를 해제하십시오. 우리는 i를 목록으로 변환 할 수 없습니다. 그런 식으로 만들어야합니다.

:unlet! i

다음으로, 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)", "")

i는 이제 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에 대해 더 많이 알게 된 것 같습니다. 이것은 일반적인 Q & A 사이트보다 Stack Exchange를 개선하고 Vim 전용 SE를 사용하는 이점을 보여주는 일종의 답변입니다.
— hippietrail

3

https://stackoverflow.com/a/4224454/15934 에서 빌드 하여 숫자를 0으로 채울 수 있습니다.

" 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

아 OK. 아직 Vim에서 스크립트를 입력하는 방법을 기억할 수 없기 때문에 아직 테스트하지 않았습니다 ... 먼저 그 내용을 읽어야합니다 (-:
— hippietrail

1
창 아래에있는 코드를 복사하여 붙여 넣습니다 $HOME/vimfiles/plugin/whatevernameyouwish.vim. 또는 $HOME/_vimrc원하는 경우 (창의 파일 이름)도 (처음으로). 머신에 $ HOME이 무엇인지 확실하지 않다면 vim에게 그것이 무엇인지 생각하는지 물어보십시오->:echo $HOME
— Luc Hermitte

1
:sourcevim 스크립트가 올바른 디렉토리 ({rtp} / plugin 또는 파일 유형별 플러그인의 경우 {rtp} / ftplugin / {filetype} / ftplugin)에 올바르게 설치되어 있어도 필요하지 않습니다 . :source우리가 10 년 반 전에 플레이해야했던 것입니다.
— 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선택하고 k니다 yiw. 이제이 시점부터 작업을 기록하려고합니다.

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

  • qq 레지스터 q에서 매크로 시작
  • /^Level 1<CR> "Level 1"로 시작하는 줄을 검색하십시오.
  • P 커서 앞에 붙여 넣기 (숫자 포함)
  • <C-A> 숫자를 증가시킵니다
  • A<space><esc> 숫자 뒤에 공백을 삽입하십시오
  • 0 시작으로 이동
  • yiw 현재 번호를 잡아 당기다
  • q 매크로 종료

그런 다음을 사용하여이 매크로를 반복하십시오 @q.


1
가 <C-A>A + 제어? Windows의 Vim에있는 모든 것을 선택합니다.
— hippietrail

Control + a입니다. vim에서는 숫자를 증가시켜야합니다. Vim 작업 대신 Windows 작업을 수행하도록 키 바인딩을 변경 한 경우 사람들이 여기에 게시하는 대부분의 작업을 수행 할 수 없습니다.
— jecxjo

Windows의 Vim 버전에는 누군가의 무한한 지혜로 인해 다른 바인딩이 제공되지 않습니다. 방금 바닐라 기본 바인딩을 사용하고 있습니다. - 난 (호출 할 수 있음을, 20 년 이상에 바인딩 빔 키를 변경 한 적이
— hippietrail

그렇지 않다면 Windows 설치에 정상적인 vim 바인딩이 있습니다. 다른 사람의 vim 빌드를 설치했을 가능성이 있습니까? 아니면 "쉬운"모드에서 vim을 실행할 수 있습니까? Windows가 일반 및 쉬운 모드 옵션으로 바탕 화면 아이콘을 설치한다고 생각합니다.
— jecxjo

3
아니요, Windows의 기본 설치는 mswin.vim을로드하기 때문입니다. 자신의 vimrc를 만들고 mswin.vim을로드하지 않으면 일반적인 vim 바인딩을 얻게됩니다. 모든 설치 (Linux, Mac 및 Windows)에 대해 단일 vimrc를 유지하며 mswin.vim을 다루지 않습니다. 이 문제에 대한 자세한 내용은 stackoverflow.com/questions/289681/…를
— jecxjo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.