Ivy/Counsel으로 바꾸기, 기능들 만들기 (w/ C++지원, ++rmsbolt)

Posted on Feb 26, 2019

최근에 이맥스 설정을 Helm기반에서 Ivy/Counsel으로 전부 바꿨다.

더 가볍고, 적당히 잘 동작하고, Ripgrep이랑 기본적으로 설정이 가능해서 편함.

거기에 C-c C-o (ivy-occur) + wgrep이 Helm에선 동작할때가 그때그때 기능에 따라 달라서 짜증나고, 심지어 플러그인을 설정해서 써야하거나 해서 정말 좋아하는 기능인데 우울하게 만들었는데, Ivy이 훨씬 깔끔하게 동작하고 일관성있게 잘 동작함.

처음에는 Helm에서 하는 기능들을 그대로 옮기려고만 생각하다가, 오히려 Ivy에 맞춰서 내가 익숙해지고, 더 낫게 할 방법들이 있어서 그냥 내가 adopt해서 편안해졌다.

CMake + compile_commands.json

이전에는 RTags을 사용해서 ‘‘정말로’’ C++ 프로젝트를 Clang등을 이용해서 파싱하고, 그걸 인덱싱한걸로 정확한 navigation, code completion을 구성해 사용하려고 했었었다. 그리고 타입 정보다 그런거 다 있으니 제대로 굴러가서 꽤 쓸만하고.

다 좋은데 그냥 이렇게까지 필요한가 싶은 생각이 들어서, 그냥 ‘‘전통적인 방법’‘에1 가깝게 GNU Global을 이용해서 C/C++ IDE 설정을 만들었다.

적당히 인덱싱 필요할때해서, 태그 끼리 Jump나 되고 그런 정도지만, 그래도 소스코드 매번 ripgrep하는것보다는 편하니까.

사실 어차피 타입이나 컴파일 가능 체크는 Flycheck + GCC/Clang으로 가능하게 만들어서 딱히 무거운 인덱서를 계속 돌리지 않아도 되니까.

여튼 이런것들을 하기 위해서 Elisp + Perl5을 이용해서 CMake/GCC/Clang이 생성하는 compile_commands.json을 파싱하는걸 작성해봤다.2

펄은 Text::ParseWordsshellwords을 적당히 Elisp에서 구현한걸 찾지도 못했고3, 구현할바엔 그냥 잘 굴러가는 펄코드를 원라이너로 불러서 써도 될거 같아서.

compile_commands.json에 있는 gcc/clang을 호출하는 커맨드라인을 파싱해서, 지정한 Include directory등을 추출해서, 다시 그걸 Flycheck에 지정해서 적절하게 동작하도록 만들고 싶어서 이런 작업들을 했다.4

그냥 이맥스/Elisp/리눅스/펄에 익숙해지고, 이들 생태계에도 익숙해지면서 어떤걸 선택해서 어떻게 연결해서 사용하면 될거 같다는걸 알게되면서 좋아지는거 같다. 그리고 반대로 어떤 편집기를 사용해도 이런걸 어느정도 알고 설정하고 확장하며 쓰게 될텐데, Lisp처럼 이맥스는 정말 그런 툴킷을 주는쪽에 더 가까운거 같다.5

Ivy와의 연동

Ivy와 연동해서, 빌드한 실행파일을 골라서 realgud 디버거나 테스트케이스 실행이 가능하도록 연동하는걸 작성했다.

Ivy을 사용한 이유는 어떤 실행파일인지 고르고6, 실행인자들도 지정해주고 하면 좋으니까.

다음과 같이 동작하도록 만들어봤다.

  1. 빌드 디렉토리의 실행파일 목록을,
  2. ‘‘현재 수정하고 있는 파일이름’’ 와 Levenshtein 거리가 가까운 순서로 정렬.
  3. 선택한 파일을 compilation-mode7이나 realgud으로 실행/디버그.
  4. https://github.com/ageldama/configs/commit/46e7478adb8588b7a948b2ec74cf2ca570fbec00#diff-844fd039f1d7faf1317fa95b88940eb7

이전에도 Helm + Rtags으로 동일한 기능으로 Elisp 구현해서 사용했었는데, 훨씬 Ivy쪽이 작성하기 편한거 같다. 그리고 나도 Elisp등에 익숙해진것도 큰거 같지만.

++rmsbolt

https://github.com/emacsmirror/rmsbolt 연동도 해봤다.

코드 수정하고 저장하면, 이 모드를 켜놓으면, 자동으로 컴파일러 실행해서 Disassemble 덤프를 보여주고, 현재 포인트의 C/C++에 해당하는 어셈블리를 하일라이팅해서 보여준다.

이것도 동작하는데 compile_commands.json 파싱해서 컴파일러 적당히 동작하도록 필요한 include directories이나 $CFLAGS 지정해주고 하도록해서, 정말 빌드만 CMake + compile_commands.json만 잘되어 있으면 아무것도 신경 안써도 바로 동작하도록했다.

https://github.com/ageldama/configs/commit/a8f6c9b0c9e5172aaa507e2c765d059ea40b56c8#diff-844fd039f1d7faf1317fa95b88940eb7

점점 더 Emacs/Elisp 이용해 내가 쓰는데 적합한 환경을 만드는데 익숙해지고 편안한거 같다. 그냥 적당한 패키지 있으면 확장하고, 사용하기 좋게 만들기도 재밌는거 같고.

그냥 이맥스에서 만족하고 살고, 계속 확장하면 된다는 마인드가 되어가는거 같기도 해서…



  1. Ctags/Etags/Cscope/Xref… 다만 Semantic이나 CEDET 은 여전히 쓰고 싶지 않았다. ↩︎

  2. https://github.com/ageldama/configs/blob/master/emacs/compile-commands-json.el ↩︎

  3. split-string-and-unquote도 딱히 마음에 들게 동작하지는 않았고, 찾은 한 Elisp라이브러리는 동작하는것처럼 보였지만, 조금만 파싱할 커맨드라인이 길어지면 스택 오버플로우. 구현을 고쳐서 Merge request할바엔 그냥 안쓰는게 나을거 같았다. ↩︎

  4. Flycheck + GCC/Clang은 심지어 그냥 Flycheck에 내장된 체커를 사용하고, 이들이 제대로 동작하게 연동만 해주는 작업. ↩︎

  5. 완성된 결과품을 쥐어주고, 수정도 못하게 만들어놓기보다는. ↩︎

  6. 현재 파일이름을 갖고 CMakeLists.txt까지 파싱해서 매칭하면 되겠지만.. ↩︎

  7. M-x compile이 좋은게, 한번 실행한 다음에 g 눌러서 변경점을 반영해서 다시 실행하면서 고쳐나가거나, C-u g 눌러서 실행 커맨드를 조금 바꾸거나 하면서 계속 실행하기 좋아서. 원래는 comint-mode 이용해서 직접 실행 버퍼를 구현했었는데, 그것보다 더 편해졌다. ↩︎