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


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.