Spacemacs에서 문자열 검색 또는 변경하기

문자열 검색과 치환은 리팩토링에 반드시 필요한 기능이다. 그러나 Spacemacs는 여러 패키지에서 동일한 기능을 제공하고 있고 저마다 사용방법이 조금씩 달라서, 익숙하지 않을 때는 오히려 생산성이 크게 감소된다. 마음이 급하거나 바쁠 때는 Emasc를 버리고 VScode로 작업하거나 vim에서 처리하는 불상사가 반복되기도 한다.  또한 아무리 익숙해졌다고 생각해도 얼마간 사용하지 않으면 여지없이 키배열이 생각나지 않는 골치아픈 영역이기도 하다.  (그리하야 내 이를 어여삐여겨 새로 3개의 테이블을 맹가노니… 부러 잊음없이 제때 잘 쓰고자 할 따람이니라.)

 

문자열 검색
  helm-project-smart-do-search helm-ag helm-swoop
검색  SPC    /   SPC →  v  SPC   s →  s 
검색 범위 버퍼 및 프로젝트내 모든 파일 버퍼 및 프로젝트내 모든 파일 현재 작업 버퍼

패턴간  이동

다른 버퍼에서

시각적  선택

영역(버퍼/파일/프로젝트) 지정후

시각적 선택

다른 버퍼에서

시각적  선택

검색 문자열

블록잡힌 문자열  또는 

커서 위치의 문자열

블록잡힌 문자열  또는 

커서 위치의 문자열

블록잡힌 문자열

없으면 입력받음

파일정보  표시

표시됨

표시됨

제외됨

사용 문맥

 

검색후 iedit로 문자열 변경을 할 때 사용.

변경없이

검색만 할 때 사용

요구사항

 

외부 실행파일 설치필요 (ag)
Ubuntu: apt-get install silversearcher-ag 

macOS: brew install the_silver_searcher 

 

 

현재 버퍼내 문자열 치환
  iedit evil-mc multiple-cursors

다중 커서  지원 여부

X O O
패턴 지정 SPC  s  e  C-n 
C-p 
n 
p 
(region-bindings-mode 패키지 필요)
패턴간 이동

  (다음) 

 p  (이전)

M-n 
M-p 

 

특정 위치 제외 가능: TAB 키로 토글 C-t  
(토글기능은 아니며
다음번 매칭된 패턴으로 이동)
N 
P 
(region-bindings-mode 패키지 필요)

검색후 문자열 변경

 S 키를 눌러 변경 시작
 패턴입력   
   ↳ ESC  ESC

VIM 표준 키입력      
패턴입력          
g   r   q 
VIM 표준 키입력      
패턴입력          
C-g             
요구사항  

~/.spacemacs 파일에

레이어 설정

~/.spacemacs 파일에

레이어 설정

~/.spacemacs 레이어 설정

;; set 'evil-mc' backend. (or 'mc' for 'multiple-cursors' backend)
(setq-default dotspacemacs-configuration-layers '(
  (multiple-cursors :variables multiple-cursors-backend 'evil-mc))

 

프로젝트내 파일에 대해 문자열 검색&치환

agptack 등이 설치되어 있으면, 파일 간에 매칭된 패턴변경은 helm-ag에 의해 처리된다.

1 SPC / 키입력으로 Helm Smart Search를 시작한다. (이때 바꾸고자 하는 패턴을 미리 블록잡아두면 편리하다)

2 C-c C-e 키입력으 helm-ag-edit 편집모드로 전환한다. (나타나는 내용은 helm-ag가 검색한 것들이다)

3 SPC s e 키입력으 iedit-mode 상태로 전환한다음, 표준 VIM 키를 사용해 패턴을 선택한다.

4 C-c C-c 키입력으로 적용하거나, C-c C-k 키입력으로 취소한다.

5 ESC ESC 두번 입력으로 helm-ag 검색모드에서 normal 모드로 전환한다.

 

 

요약

  검색만 할 경우

  ↳ 현재 버퍼: helm-swoop   SPC  s s 
   프로젝트 전체 파일: helm-ag    SPC v  

  검색 및 치환 할 경우
   현재 버퍼: iedit   SPC s e
   프로젝트 전체 파일: helm smart search  SPC  / iedit 기능 사용  

 

 

다중 커서 (multiple-cursor)

아래 보다시피 거지같이 복잡하다. 다중 커서 기능을 지원하는 두개의 패키지가 완벽하질 않다. 그나마 쓰라고 한다면 evil-mc 패키지인데 이 패키지의 가장 큰 아쉬운 점은 블록설정된 영역의 첫 부분에서 커서를 만드는 기능이 없다는거다. 결론적으로 다중커서 백엔드로 evil-mc를 지정해서 메인으로 삼고 multiple-cursors 패키지는  ~/.spacemacs  dotspacemacs-additional-packages  설정에 추가해서 두개 모두 쓰고 있는 중이다.

  evil-mc multi-cursors

현재 패턴 위치에  커서 만들고, 
매칭되는 다음 패턴위치로  이동

evil-mc-make-and-goto-next-match
C-n
mc/mark-next-like-this
n
(require ‘region-bindings-mode)

현재 패턴 위치에  커서 만들고, 
매칭되는 이전 패턴위치로  이동

evil-mc-make-and-goto-preview-match

C-p 

mc/mark-previous-like-this
p
(require ‘region-bindings-mode)

현재 패턴 위치에  커서 만들고, 
다음 커서위치로  이동

evil-mc-make-and-goto-next-cursor
M-n 
mc/skip-to-next-like-thise
N
(require ‘region-bindings-mode)

현재 패턴 위치에  커서 만들고,
이전 커서위치로  이동

evil-mc-make-and-goto-prev-cursor

M-p 

mc/skip-to-previous-like-this
P
(require ‘region-bindings-mode)

다중커서  상태 해제

evil-mc-undo-all-cursors

g r  q 

C-g 

모든 패턴에 대해  커서 만들기

evil-mc-make-all-cursors
g  r  m 
mc/mark-all-like-this

SPC  s  m  b 

선택영역의 모든

  첫 컬럼에 커서 만들기

  mc/edit-lines
C-S-c   C-S-c 
or
SPC  s  m  r 

 

빡침주의

아무리 많은 돈을 준다해도 살수없는 시간을 이딴 설정때문에 복잡하게 살지 말자! 

어지간하면 VScode 쓰면 된다. 그러나 X-Window 없는 서버에서 코딩해야하는데 VIM로는 열불 날때 Spacemacs가 적합할 수도 있다.

NCurses 기반 Space Invader 게임을 웹으로 포팅하기

지난번 PacVim 포팅1http://andrwj.com/post/2019/09/porting-pacvim-with-emscripten/ 실패후에 찝찝함이 남아서 다른 패키지를 포팅해보았다.  흔해빠진 Invader 게임인데 ncurses 기반이다. 2http://ninvaders.sourceforge.net/ 둘다 ncurses v6.1 이지만 이번 nInvader 게임은 PacVim 보다 화면제어가 잘되고 있다. 그러나 역시나 어쩔 수 없는 점도 많이 보인다.

 

 

nInvaders 게임 코드 수정사항

1  pthread를 사용하지 않기 때문에 간단히 window.requestAnimationFrame()를 사용해서 브라우저가 뻗지 않도록 적절하게 업데이트하게 했다. iTerm2 에서 동작할 때와 별반 다른 점을 찾기 힘들정도로 부드럽게 움직였다. (뭐 당연한 얘기)

2  역시나 main()exit() 을 업애버리고 시작과 종료를 적절히 웹에서 조절하게 했다. 

3  장애물이 이상하게 표시되고 미사일 발사 주변이 말려올라가고 … 등등은 iTerm2에서 실행할 때는 전혀 문제없다. PacVim에서보다 훨씬 화면 제어가 잘되는 걸로 봐서 xterm.js에서 제대로 표시하려면 특정 ncurses API를 WebAssembly로 변환된 후의 상황을 고려해서 보정해줘야하는게 있다고 추측한다. 그게 뭔지 알려면 시간이 필요할 뿐이다. xterm.js 에는 문제가 없는 것 같다. (여전히 추측)

4  종료후 GPL 라이센스 출력을 하지 않게했다. 

5  ‘q’를 눌러도 exit() 하지 않고 다시 게임 대기화면으로 돌아가게 했다.

 

 

별 수정을 가하지 않아도 제대로 포팅되는 ncurses 기반 프로젝트를 발견할 때까지 쉬엄 쉬엄 포팅을 해볼 요량이다. 더불어 xterm.js 를 Client/Server 방식으로 돌려보며 문제해결채을 찾게되면 다시 업데이트 할 예정. 

References   [ + ]

커맨드라인 게임 PacVim을 Emscripten으로 웹으로 포팅하기

Vim 키맵을 사용하기 때문에 이런류의 게임1https://github.com/jmoon018/PacVim 좋아라 한다.  Emscripten을 사용한지도 오래됐고 WebAssembly Tool의 현재 상태도 알아보고 싶기도해서 겸사겸사 포팅을 시작했더랬다.  React Hooks를 씌워서 그럴싸한 UI까지 만드는게 목표였는데, ncurses + xterm.js 조합에 해결하기엔 시간이 너무 들어갈 것으로 보이는 문제 때문에 잠시 묵혀두기로 결정한다.

 

컴파일환경
  • Emscripten v1.38.41-upstream2https://emscripten.org/docs/getting_started/downloads.html, Target: WASM
  • Developing OS: Ubuntu
  • ncurses v6.13https://github.com/mirror/ncurses
  • xterm.js4https://xtermjs.org/ 
  • PacVim5https://github.com/jmoon018/PacVim 

 

중단원인

아래 두 그림에서 보듯 iTerm2 에서의 게임내 레벨 표시에 비해 xterm.js 에서 표시는 중간 부분이 완전히 사라져보이는데, 해당 원인은 Emscripten으로 컴파일된 ncurses에서 제어문자열이 도중에 많은 부분이 제거된채  xterm.js으로 보내지기 때문이다. (비교를 위해 이미지 높이를 동일하게 했더니 왼쪽이 좀 좁아 보인다)

Level 8 in iTerm2 Level 8 in xterm.js

ncurses 에서 문자하나를 현재 위치에 출력하는 addch() 함수호출 뒤에 refresh() 호출 여부가 JS 쪽 출력에 영향을 주는 것이 확인됐다. ncurses API에서 내놓는 escape sequence code를 xterm.js 쪽에서 해석을 하지 못하는 게 아닌가 의심스럽지만,  ncurses API쪽에서 적절한 단위로 코드를 나눠 전송하지 않는 문제일수도 있다.  문자열이 아닌 ASCII code 31 이하의 특수문자는  xterm.js 에서 제대로 해석하는 걸로 확인됐다. 따라서 현재 테스트해봐야할 부분은 ncurses API쪽에서 보낸 데이터와 브라우저 JS에서 받은 코드를 비교함으로써 데이터 누락 유무를 확인 하는 것이다.  제대로 전달되지 않는다면 Emscripten으로 컴파일된 ncurses 쪽 문제이고, 제대로 전달된다면  ncurses 쪽에서 내놓는 코드가 xterm.js 에서 제대로 해석되는지 확인해야 한다. 이래저래 시간 엄청 들어가므로 여기서 멈춘다. 

 

PacVim 코드 수정사항

1  게임내 Player 움직임을 제어하기 위해 ncurses getch() 로 입력받는 부분을 제거하고 브라우저 JS 쪽에서 입력받고 C++ API를 호출하는 방식으로 변경했다.

void EMSCRIPTEN_KEEPALIVE player_move(const char *key) {
        if(!READY) return;
        if(*key) {
                onKeystroke(player, *key);
                move(player.getY(), player.getX());
        }
        refresh();
}
 

 

Emscripten으로 포팅된 getch()는 동기함수라서 매번 입력받는 팝업이 게임 흐름을 정지시키므로 도저히 사용할 수가  없다. 앞으로 ncurses API로 데이터 입력받는 일은 없을 듯. 입력된 키 전달은  브라우저 JS에서  C++ API 호출로 전송한다. 

function onKeyUp(event) {
   if(event.key === 'Enter') {
      running = !running;
      Module._toggle_game_state(running);
      return;
   }
   Module._player_move(event.key);
}

output.addEventListener("keyup", onKeyUp, true);

 

2  game.cpp init() 함수에서 while()루프를 돌며 게임이 진행되지만  emscripten_set_main_loop()로 변환하지 않았다. Ghost는 while 루프와 무관하게 pthread로 동작하기 때문이다. pthread상의 Ghost는 READY 변수로 동작을 pause/resume 시킬 수 있다. 이 부분도 브라우저에서 조절하는 게 훨씬 조작성을 좋게한다.  pthreads 때문에  -s ALLOW_MEMORY_GROWTH=1 설정과 -u USE_PTHREADS=1 설정을 동시에 사용하면 귀찮은 메모리 설정 문제가 발생하므로  전체 메모리를 48MB, 스택을 16MB로 지정하고 컴파일했다.

3  게임속 player life 갯수가 0 이하가 될 때까지 루프를 돌게되어 있지만,  단순히 게임 한판을 종료하면 끝내게 해서  main()을 사용하지 않는다.  사용자입력 검증및 후처리를 ncurses API로 받아 하는 것보다 React Hooks 에서 조절하는게 훨씬 보기 좋기 때문이다. 

4  TERM 설정 값 외에는 다른 terminfo 파일이 필요없으므로 embed 방식으로 포함하였다:

   ↳  --embed-file /opt/emscriptened/share/terminfo/x/xterm@/home/web_user/.terminfo/x/xterm

5  PacVim 레벨 데이터 파일은 preload 로 포함시킨다:

    --preload-file maps@/usr/local/share/pacvim-maps

 

ncurses 기반 앱 활용가능성

SDL6https://www.libsdl.org/ 사용하면 이런 종류의 문제는 없겠지만 개인적으로 콘솔형 어플을 선호하기 때문에 가능한 ncurses + xterm.js 조합이 성공하길 기대하고 있다. ncurses를 사용하는 다른 오픈소스가운데 몇가지 Emscripten으로 컴파일해서 살펴보며 추후 수정여부를 새 글로 올릴 예정이다.

 

References   [ + ]

1. https://github.com/jmoon018/PacVim
2. https://emscripten.org/docs/getting_started/downloads.html
3. https://github.com/mirror/ncurses
4. https://xtermjs.org/
5. https://github.com/jmoon018/PacVim
6. https://www.libsdl.org/