(Vim) 필요없는 공백제거하기

왠만한 편집기들은 파일을 저장하기 전에 각 라인의 뒤에 들어있는 쓸데없는 공백들을 제거해주는 기능을 제공한다. (이러한 trailing space들의 해악에 대해서는 따로 말할 필요가 없을 것 같다.) 물론 vim에서도 이걸 사용할 수 있다. :s 명령을 사용하면 각 줄에서 맨 뒤쪽 공백을 제거할 수 있다.

:%s/\s\+$//

간단히 설명하면 이 명령은 다음과 같이 동작한다.

  1. %으로 전체 라인에 대해서 반복한다.
  2. 패턴은 /\s\+$/ 으로 공백문자가 이어지다가 라인의 끝을 만나는 것이다.
  3. 이 패턴을 지운다.

이걸 :autocmd에 끼얹으면 손쉽게 파일 저장 이전에 실행될 수 있도록 할 수 있다. * 는 모든 파일이라는 의미이므로, *.py,*.js 처럼 바꾸면 파이썬, 자바스크립트 파일에 대해서만 실행할 수도 있다.

:autocmd BufWritePre * %s/\s\+$//

이렇게하면 파일을 저장하려 할 때마다 저 치환 동작을 수행해서 불필요한 공백을 제거할 수 있다. 극히 간단하게 적용할 수 있는 방법이고, 사실 인터넷을 뒤져봐도 워낙 간단한 방법이라 많이 소개된다. 다음과 같은 변형으로 키맵에 연결하는 경우도 있다. (<C-u>는 명령모드에서 프롬프트에 입력된 내용을 모두 지우는 키 동작이다.)

:nnoremap <F8> :<C-u>%s/\s\+$//<CR>

좀 더 들여다보기

그런데 이 방법은 엄청 간단한 대신에 몇 가지 부작용을 가지고 있다. 첫째로 가장 근본적인 문제는 %s 명령을 사용한다는 것 때문에 발생한다. 범위에 %를 주는 경우, 모든 라인을 따라 돌아가면서 명령을 반복하게 되는데, 이 때 커서가 같이 따라 움직이게 된다. 따라서 저장을 마치고 나면 편집하던 위치가 아니라 항상 파일의 맨 끝으로 가게 된다. (물론 이건 <C-o>로 되돌아가면 되긴 하다)

다른 문제는 :s 명령은 검색 레지스터를 사용한다는 것이다. 따라서 특정 단어를 검색하고, 해당 단어가 하이라이트 된 중에 파일을 저장했다면 찾고 있던 내역이 사라지는 것이다. 다만 autocmd는 항상 실행 전후에 검색 레지스터를 백업했다가 복구한다. 이 문제는 키맵으로 설정하는 경우에 다르게 처리해야 한다.

셋째로 이미 끝자리 공백이 모두 제거된 파일에 대해서는 치환이 실패할 것이므로 매번 E486: Pattern not found: \s\+$ 이라는 에러 메시지가 표시될 것이다.

이를 해결할 수 있는 실마리들은 다음과 같다.

  1. 치환 후 원래위치를 복원하기 : 현재 커서 위치의 라인과, 스크롤 상태 등을 따로 기억했다가 복원하는 여러 팁들이 있는데, 가장 간단한 방법은 winsaveview() 함수를 통해서 현재 화면의 상태를 얻고 나중에 winrestview() 함수를 통해서 복원하는 것이다.
  2. 역시 검색 레지스터는 스크립트에서 쓰기/읽기가 가능하므로 백업/복원이 가능하다.
  3. :s 명령의 플래그중에는 치환실패 메시지를 출력하지 않게하는 e 옵션이 있다.

이를 토대로 다음과 같이 코드를 수정할 수 있다. 다만 map 명령에서 키 시퀀스 부분을 여러 줄로 분할하여 입력할 수는 없으므로 엄청 긴 키맵을 입력해야하는 부담이 있다.

nnoremap <F8> :<C-u>let _x = @/<Bar>let _w = winsaveview()<Bar>%s/\s\+$//e<Bar>let @/ = _x<Bar>call winrestview(_w)<CR>

또 키맵이나 autocmd의 경우 변수를 추가로 노출하기 때문에 깔끔한 방법이 아니다. 따라서 다음과 같이 함수로 정의해서 저장 할 때, 키를 눌렀을 때 작동할 수 있도록 하자.

function! s:remove_trailing_spaces() abort
	let x = @/
	let w = winsaveview()
	silent! %s/\s\+$//e  "silent 명령으로 치환 결과를 표시 안함
	let @/ = x
	call winrestview(w)
endfunction

이 함수를 호출하는 사용자 정의 Ex 명령과 키맵을 작성한다. 그리고 적절한 키와 자동명령에서 호출되도록 해주면 된다.

" 명령으로 정의
command! -nargs=0 RemoveTrailingSpaces
			\ call <SID>remove_trailing_spaces()
" 저장시마다 발동
augroup TS
  au!
  au BufWritePre * RemoveTrailingSpaces
augroup END
" 키맵으로 정의해서 원할때마다 실행하려면
noremap <script><silent> <Plug>(remove_trailing_spaces)
			\ :<C-u>RemoveTrailingSpaces()<CR>
nmap <F8> <Plug>(remove_trailing_spaces)

이상의 코드를 vim파일로 작성해놓고 :source 명령으로 읽어들이면 자동으로 혹은 수동으로 실행할 수 있게 된다.

보너스 – 파일 끝의 빈줄 제거하기

줄 끝의 공백만큼이나 많이 생기는 것이 파일 끝의 빈 줄이다. 이것도 간단히 처리할 수 있다. 파일끝 공백은 개행문자로부터 시작하여 공백-개행-공백-개행-…-공백이 계속해서 이어진다. 결국 (개행공백*)의 반복이다. 그리고 그 끝은 파일의 끝이다. vim 검색 패턴에서 \%$는 파일의 끝을 의미하므로 다음과 같은 명령으로 파일 끝 빈 줄을 제거할 수 있다.

:s/\(\n\s\+\)\+\%$//e

참고로, 파일 끝 빈줄은 파일 내에서 한 번만 나올 것이기 때문에 %를 붙이지 않는다. (붙여도 상관은 없다만…) slient s/\(\n\s\+\)\+\%$//e 를 위에서 작성한 함수에 추가해주면 깔끔하게 정리 성공.

Read more

워드프레스에서 고스트로 이전

워드프레스에서 고스트로 이전

이 글을 쓰면서도 믿기 힘든 사실인데, 블로그라는 걸 처음 시작한지가 20년이 되었습니다. 이글루스에서 처음 시작했다가, SK컴즈가 인수한다고 발표함과 동시에 워드프레스로 플랫폼을 옮겼죠. 워드프레스오 옮긴 이후에는 호스팅 환경을 이리 저리 옮기긴 했지만 거의 18년 가까이 워드프레스를 사용해온 것 같습니다. 그 동안 워드프레스는 블로깅 툴에서 명실상부한 범용CMS로 발전했습니다. 사실 웬만한 홈페이지들은 이제

By sooop
띄어쓰기에 대한 생각

띄어쓰기에 대한 생각

업무 메일을 쓸 때 가장 많이 쓰는 말 중에 하나가 메일 말미에 ‘업무에 참고 부탁 드립니다.‘인데요, 어느 날부터 아웃룩에서 이 ‘부탁 드립니다’가 틀렸다고 맞춤법 지적을 하기 시작했습니다. 맞는 말은 ‘부탁드립니다’라고 붙여 쓰는 거라고. 사실 아래아한글 시절부터 이전의 MS워드까지, 워드프로세서들의 한국어 맞춤법 검사 실력은 거의 있으나 마나 한

By sooop

구글 포토에서 아이클라우드로 탈출한 후기

한 때 구글 포토가 백업 용량을 무제한으로 제공해 주겠다고해서, 구글 포토를 사용해서 사진을 백업해왔습니다. 물론 이 이야기의 결말은 저나 이 글을 읽고 있는 여러분이나 모두 알고 있습니다. 사실 AI에게 학습 시킬 이미지 데이터를 모으기 위한 것일 뿐이라거나 하는 이야기는 그 당시에도 있었습니다만, 에이 그래도 구글인데 용량은 넉넉하게 주겠지…하는 순진한

By sooop

Julia의 함수 사용팁

연산자의 함수적 표기 Julia의 연산자는 기본적으로 함수이며, 함수 호출 표기와 같은 방식으로 호출하는 것이 가능합니다. 또한 그 자체로 함수이기 때문에 filter(), map() 과 같이 함수를 인자로 받는 함수에도 연산자를 그대로 적용하는 것이 가능합니다. 특히 + 연산자는 sum() 함수와 같이 여러 인자를 받아 인자들의 합을 구할 수 있습니다. 2 + 3 # = 5 +(2,

By sooop