1 Introduction
본 포스트에서는 리눅스에서 emacs 에디터를 통해 C++/Python 개발환경을 구축하는 방법에 대하여 설명한다. emacs는 40년이 넘는 오랜 역사를 가지고 있는 에디터이므로 매우 다양한 코드 네비게이션 방법들이 존재하지만 본 포스트에서는 lsp-mode 를 사용하여 코드 네비게이션하는 방법에 대해 설명한다.
emacs는 매우 복잡하고 많은 키바인딩으로 유명하다. 이러한 복잡한 키바인딩을 단순화시켜서 편하게 사용하기 위해 필자는 evil 모드를 설치하여 vim 키바인딩을 사용하였으며 대부분의 키바인딩을 Space 키 기반으로 변경하여 손에 부담이 덜 가도록 설정하였다. spacemacs 같이 이미 많은 사람들이 사용하는 Space 키 기반의 emacs가 존재하지만 use-package 기반의 환경설정을 사용할 수 없어서 필자만의 커스텀 Space 키바인딩을 사용하였다.
emacs는 C++ 에디터 외에도 org-mode, latex-mode 등과 같은 강력한 패키지들을 사용하여 일정 관리 및 논문 작성, 블로그 포스팅 등 여러 기능들을 사용할 수 있으나 본 포스트에서는 C++/Python 개발환경설정에 초점을 맞춘다. 그리고 emacs는 GUI, CLI 모두를 지원하지만 본 포스트에서는 GUI 환경에 대해서만 설명한다.
포스트를 작성하는 시점(2020.09 -> 2022.12 updated)을 기준으로 emacs 에디터의 버전은 emacs 28.1 을 사용하였으며 폰트는 Hack 을 사용하였고 테마는 doom-dark+ 테마를 사용하였다. 포스트에서 설명하는 모든 내용들은 우분투 18.04 LTS 환경에서 테스트하였다. 설정 파일은 다음 링크에서 다운받을 수 있다. emacs(dev)
2 How to setup
본 포스트에서는 필자의 설치 방법을 소개한다. 우선 Documents 디렉토리로 이동한다.
$ cd ~/Documents
다음으로 아래 명령을 통해 설치 스크립트를 다운로드한다. wget이 없다면 해당 링크 에서 스크립트를 Documents 폴더로 복사한다.
$ wget https://raw.githubusercontent.com/edward0im/lockdpwn/master/shell_scripts/install_emacs.sh
해당 스크립트를 열어서 readonly version 변수가 28.1 으로 되어 있는지 확인한다. 다음으로 해당 스크립트에 실행 권한을 준 후 실행하여 emacs을 설치한다.
$ chmod 755 ./install_emacs.sh
$ ./install_emacs.sh
다음으로 emacs(dev) 환경설정 파일을 다운로드 받아서 홈폴더로 이동시킨다. 이 때, emacs 파일 이름 앞에 점(.)을 추가해야 정상적으로 emacs에서 인식한다.
# Move .emacs to home folder.
$ mv ~/Downloads/emacs_dev ~/.emacs
그리고 다음 경로로 이동하여 emacs를 실행한다. emacs를 실행하면 .emacs 파일에서 패키지 목록을 읽어서 패키지들을 자동으로 설치한다.
$ cd ~/Documents/emacs-28.1/src/
# Run emacs 28.1
$ ./emacs
이 때, 아래와 같이 TLS 관련 질문이 나오면 always 를 선택하여 모든 패키지들을 설치한다.
여기까지 완료되었으면 lsp-mode 를 제외하고 모든 emacs 기능들이 정상적으로 설치되었다.
2.1 How to use lsp-mode
lsp-mode 는 emacs의 코드 네비게이션(함수 이동, 에러 체크) 및 자동완성을 수행하는 패키지이다. lsp-mode 가 설치된 상태에서 맨 처음 파일을 여는 경우 아래와 같이 cmake 프로젝트의 최상단을 입력하라는 창이 뜬다. 제안된 경로가 맞는 경우 i 키를 누르면 되고 임의로 경로를 변경하고 싶은 경우 I 키를 눌러서 변경하면 된다.
2.1.1 C++
lsp-mode 는 clangd-9 버전에서 정상적으로 작동한다. clangd-9는 다음 명령을 통해 설치할 수 있다.
# Install clangd-9.
$ sudo apt install clangd-9
임의로 작성한 함수 및 클래스의 자동완성을 하고자 하는 경우 cmake 프로젝트의 최상단 경로에 compile_commands.json 링크 파일이 존재해야 한다. 다음과 같이 진행한다.
- cmake 프로젝트에서 빌드를 수행할 때 -DCMAKE_EXPORT_COMPILE_COMMANDS=1 옵션을 추가하여 build 폴더에 compile_commands.json 파일이 생성되도록 한다. 코드를 반드시 빌드하지(make) 않아도 해당 json 파일만 생성되면 네비게이션이 된다.
# In cmake project, make build folder and go to the folder.
$ mkdir build
$ cd build
# This option creates 'compile_commands.json' file.
$ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 ..
- 앞서 생성한 compile_commands.json 파일을 프로젝트 최상단에 링크 파일로 세팅한다.
# Create a soft link of compile_commands.json file to the cmake project root path.
$ cd /path/to/cmake/project/
$ ln -s ./build/compile_commands.json ./
- emacs에서 파일을 열 때 lsp 경로를 묻는 창이 나오면 cmake 프로젝트의 최상단 디렉토리를 설정한다.
모든 설정이 완료되면 임의로 작성한 클래스와 함수에 대해서도 위와 같이 코드의 자동완성 및 네비게이션 기능을 사용할 수 있다.
2.1.2 Python
파이썬의 경우 pyls 프로그램을 설치해야 코드 자동완성 및 네비게이션이 가능하다. pyls 는 다음 명령어를 통해 쉽게 설치할 수 있다.
# Install pyls
$ pip install python-language-server --user
# Check pyls is installed successfully
$ pyls --help
pyls 가 정상적으로 설치되었다면 이후에는 compile_commands.json 파일없이도 정상적으로 코드 네비게이션 및 자동완성이 가능하다.
3 My emacs configuration
해당 섹션에서는 필자가 사용하는 emacs의 다양한 기본 기능 및 플러그인 기능들에 대해 설명한다. 사용하는 .emacs 파일은 emacs(dev) 에서 다운로드 받을 수 있다. 해당 키바인딩은 필자가 임의로 설정한 키바인딩이므로 원한다면 얼마든지 바꿔서 사용할 수 있다.
3.1 Emacs keybindings
emacs는 기본 키바인딩들이 대부분 Control로 시작하여 왼쪽 새끼손가락에 무리가 많이 가기 때문에 기본 키바인딩을 거의 대부분 Space 키부터 시작하도록 변경하였다. Custom keybindings 섹션의 경우 emacs 자체의 기본 키바인딩을 변경한 필자의 키바인딩에 대해 설명하였다. emacs에는 수많은 기능 및 키바인딩들이 존재하지만 이를 다 설명하지 않고 실제로 자주 사용하는 키바인딩 위주로 설명하였다.
Control 키바인딩은 <C {...}> 과 같이 표현하고 Alt 키바인딩은 <A {...}> 으로 표현하였다. 그리고 Space 키바인딩은 <SPC {...}> 로 표현하였으며 Shift 키바인딩은 <S {...}> 로 표현하였다.
3.1.1 General keybindings
기본 키바인딩은 evil 모드를 사용하여 대부분 키바인딩이 vim 키바인딩과 동일하다. 해당 섹션에서는 vim의 기본 키바인딩과 동일하지 않은 키바인딩들을 설명한다.
3.1.1.1 <, .>: forward-paragraph, backward-paragraph
코드를 빈 칸 단위로 빠르게 네비게이션한다.
3.1.1.2 <C-f>: newline-without-break-of-line
현재 커서 아랫줄에 빈 줄을 추가한다.
3.1.1.3 <C-]>: match-paren
커서 위에 괄호가 있는 경우 반대편 괄호로 이동한다.
3.1.1.4 <A-w>: kill-whitespace
현재 커서의 앞에 존재하는 Whitespace를 제거한다.
3.1.1.5 <A-u>: xah-toggle-letter-case
커서 위 단어의 대소문자를 변경한다. A-u를 반복해서 누르면 모두 소문자, 맨 앞글자만 대문자, 모두 대문자 순서로 변경된다.
3.1.1.6 <A-Enter>: open-line
현재 커서를 기준으로 새로운 라인을 만든다.
3.1.1.7 <C-e>, <A-e> : move-end-of-line, move-beginning-of-line
현재 라인의 맨 끝과 맨 처음으로 이동한다.
3.1.1.8 <C-n>, <C-S-n>: make-frame-command, delete-frame
새로운 emacs 창을 열거나 닫는다. C-n으로 새로운 창을 열고 C-S-n으로 닫는다.
3.1.1.9 <C-->, <C-=>: zoom-frame-out, zoom-frame-in
폰트의 크기를 키우거나 줄인다.
3.1.1.10 <A-'>, <A-;>: comment-dwim, select-current-line-and-comment
A-'로 현재 커서에 주석을 추가하며 Visual Block 모드에서 A-;를 통해 여러줄을 주석 처리한다.
3.1.1.11 <A-h,j,k,l>: next-multiframe-window, previous-multiframe-window
분할된 여러 창들을 네비게이션한다. Alt + 방향키를 통해서도 이동할 수 있다.
3.1.1.12 <C-q>, <A-q>: delete-other-windows, delete-window
C-q는 현재 커서가 놓인 창을 제외하고 나머지 창들을 전부 닫으며, A-q는 현재 커서가 놓인 창만 닫는다.
3.1.2 Space keybindings
3.1.2.1 <SPC-b-b>: bookmark-set
북마크를 등록한다.
3.1.2.2 <SPC-b-v>: bookmark-jump
등록된 북마크를 보여준다.
3.1.2.3 <SPC-b-d>: bookmark-delete
등록된 북마크를 삭제한다.
3.1.2.4 <SPC-c-k>: my-kill-this-buffer
현재 버퍼를 닫는다.
3.1.2.5 <SPC-c-p>: pwd
현재 버퍼의 디렉토리 경로를 하단 미니버퍼에 출력한다.
3.1.2.6 <SPC-c-g>: grep-find
현재 경로에서 grep 명령을 수행한다.
3.1.2.7 <SPC-z-m>: suspend-frame
emacs 창을 최소화한다
3.1.2.8 <SPC-z-f>: toggle-frame-fullscreen
emacs 창을 최대화한다
3.1.2.9 <SPC-s>: create-scratch-buffer
Scratch 버퍼로 이동한다. Scratch 버퍼는 emacs가 시작하면 가장 처음으로 열리는 창이며 emacs lisp 언어 모드로 되어있다.
3.1.2.10 <SPC-->, <SPC-\>: split-window-vertically, split-window-horizontally
현재 창을 분할한다. SPC-\는 세로로 분할하고 SPC–는 가로로 분할한다.
3.1.2.11 <SPC-]>: window-split-toggle
분할된 창을 세로 <==> 가로로 토글한다.
3.2 Packages keybindings
3.2.1 evil
vim 키바인딩을 사용할 수 있는 패키지. vim의 키바인딩과 동일하다.
3.2.1.1
3.2.2 lsp-mode
코드 네비게이션(함수 이동, 에러 체크) 및 자동완성을 수행하는 패키지
3.2.3 magit
git 관련 명령을 수행할 수 있는 패키지
3.2.3.1 <SPC-m-\>: magit-status
magit 관리 상태창을 연다.
3.2.3.2 <SPC-m-e>: magit-ediff-dwim
ediff를 수행한다. 현재 파일(A)과 다른 파일(B)를 비교할 수 있다. 다른 파일(B)은 다른 브랜치의 파일 또는 이전 커밋 파일 등을 선택할 수 있다.
3.2.3.3 <SPC-m-d>: magit-diff-dwim
diff를 수행한다. 현재 파일(A)과 다른 파일(B)를 비교할 수 있다. 다른 파일(B)은 다른 브랜치의 파일 또는 이전 커밋 파일 등을 선택할 수 있다.
3.2.3.4 <SPC-m-l>: magit-log-buffer-file
현재 파일의 commit 기록을 확인한다.
3.2.3.5 <SPC-m-f>: magit-find-file
다른 브랜치 내의 파일을 검색한다.
3.2.3.6 <Tab>: magit-section-toggle
파일의 변경 내역을 자세히 확인한다.
3.2.3.7 <s, u>: magit-statge-file, magit-unstage-file
변경된 파일을 Stage, Unstage한다.
3.2.3.8 <d-d>: magit-diff
변경된 파일의 diff 창을 연다.
3.2.3.9 <c-c>: magit-commit
Stage된 파일들을 Commit한다.
3.2.3.10 <F-u>, <P-u>
: magit-pull, magit-push
F-u로 pull 명령을 사용하고 P-u로 push 명령을 사용한다.
3.2.4 git-gutter
git 프로젝트의 경우 코드의 추가/변경/삭제 내역을 줄번호 옆에 표시해주는 패키지
3.2.4.1 <SPC-m n,p,r>: git-gutter:next(prev)-hunk, git-gutter:revert-hunk
SPC-m-n,p 키로 변경된 내역을 빠르게 네비게이션한다. SPC-n-r 키를 통해 현재 변경내역을 제거한다.
3.2.5 helm
emacs 전용 GUI 패키지. 주로 버퍼창을 쉽게 네비게이션하는데 사용한다.
3.2.5.1 <SPC-f>: helm-for-files
현재 열려있는 버퍼들의 목록을 표시한다. 추가적으로 가장 최근에 열었던 버퍼들도 확인할 수 있다.
3.2.5.2 <SPC-w>: helm-find-files
특정 경로의 파일을 연다.
3.2.5.3 <SPC-a>: helm-semantic
코드 상에서 함수와 변수의 정보를 표시한다. 엔터키를 눌러서 바로 이동할 수 있다.
3.2.5.4 <A-x>: helm-M-x
emacs의 다양한 명령들의 목록을 표시한다.
3.2.6 projectile
프로젝트를 관리해주는 패키지. cmake를 통해 관리되는 프로젝트들은 자동으로 인식된다. 임의의 프로젝트를 등록하고 싶은 경우 최상단 경로에 .projectile 이름의 빈파일을 생성하면 projectile에서 자동으로 인식하여 프로젝트에 추가한다.
3.2.6.1 <SPC-,>: helm-projectile-switch-project
현재 등록된 프로젝트 목록을 표시한다.
3.2.6.2 <SPC-p-p>: add-current-project-to-projectile
현재 경로를 새로운 프로젝트 경로로 추가한다.
3.2.6.3 <SPC-p-r>: projectile-remove-known-project
등록된 프로젝트들을 제거한다.
3.2.7 yasnippet
특정 키워드를 입력 후 Tab을 누르면 미리 저장된 Snippet들이 나오는 패키지
3.2.8 dired
(emace 기본패키지) 디렉토리 탐색 패키지
3.2.8.1 <SPC-d>: open-dired-mode
현재 버퍼의 디렉토리 경로를 dired 모드로 연다.
3.2.8.2 <d>: dired-flag-file-deletion
커서 위의 파일을 삭제하기 위해 Flag 설정한다.
3.2.8.3 <m, u, U>: dired-mark, dired-unmark, dired-unmark-all-marks
커서 위의 파일들을 마킹하거나 해제할 수 있다. 마킹한 여러 파일들은 복사(C), 이름변경(R), 삭제(d) 등에 사용된다.
3.2.8.4 <X>: dired-do-flagged-delete
삭제 Flag되어 있는 파일들을 삭제한다.
3.2.8.5 <a>: goto-parent-directory
부모 디렉토리로 이동한다.
3.2.8.6 <g>: revert-buffer
버퍼를 새로고침한다.
3.2.8.7 <C>: dired-do-copy
커서 위의 파일을 복사한다.
3.2.8.8 <R>: dired-do-rename
커서 위의 파일을 이름을 변경하거나 위치를 이동한다.
3.2.8.9 <+>: dired-create-directory
새로운 디렉토리를 생성한다.
3.2.9 eldoc
emacs 하단 상태창에 커서 위의 있는 함수 또는 변수의 정보를 표시하는 패키지
3.2.10 flycheck
코드의 에러 구문을 체크하는 패키지
3.2.11 gud
(emacs 기본패키지) gdb 디버깅을 수행하는 패키지. gcc, g++ 컴파일 수행 시 -g 옵션을 추가해줘야 정상적으로 디버깅할 수 있다. 또는 cmake 프로젝트에서 RelWithDebInfo 또는 Debug 모드로 컴파일해야 정상적으로 디버깅할 수 있다.
3.2.11.1 <SPC-g-d>: gdb
gdb를 실행한다. args 뒤에 실행파일의 경로를 입력해준다.
3.2.11.2 <SPC-g-w>: gdb-many-windows
gdb의 다양한 정보를 보기 위해 창을 토글한다.
3.2.11.3 <SPC-g-b>: gud-break
특정 라인의 브레이크포인트를 설정한다.
3.2.11.4 <SPC-g-s>: gud-run
해당 바이너리를 실행한다.
3.2.12 neotree
vim의 NerdTree와 같이 현재 파일의 디렉토리 구조를 볼 수 있는 패키지
3.2.12.1 <SPC-n-n>: neotree-toggle
neotree 모드를 토글한다.
3.2.12.2 <SPC-n-a>, <SPC-n-r>: neotree-go-to-upper-directory, neotree-refresh
SPC-n-a로 부모 디렉토리로 이동하며 SPC-n-r로 현재 파일의 위치를 neotree에 표시한다.
3.2.13 eyebrowse
하나의 emacs 창에서 여러 workspace를 사용하여 여러 코드들을 편집할 수 있는 패키지
3.2.13.1 <SPC-0,1,2,3>, <SPC-c-">: eyebrowse-switch-to-window-config-#, eyebrowse-close-window-config
SPC-#를 통해 특정 워크스페이스를 만들 수 있다. 화면의 좌측 하단 번호 참조. SPC-c-" 키로 현재 워크스페이스를 제거한다.
3.2.14 smerge
3.2.14.1 <C-c n,p>, <C-c a,b>: smerge-next(previous), smerge-keep-mine(other)
Merge Conflict가 발생한 경우 C-c-n,p 키를 통해 빠르게 네비게이션할 수 있다. C-c-a,b 키를 통해 두 변경 내역 중 하나를 선택한다. C-c a는 위의 변경내역을 선택하고 C-c b는 아래 변경내역을 선택한다.
3.2.15 centaur-tabs
emacs 상단의 Tab 기능을 사용할 수 있는 패키지
3.2.15.1 <SPC-c-t>: centaur-tabs-mode
centaur-tabs 모드를 토글한다.
3.2.15.2 <J, K>, <SPC-c Left, Right>: centaur-tabs-forward(backward)-tab, centaur-tabs-move-current-tab-to-left(right)
J,K 키로 탭을 네비게이션할 수 있으며 SPC-c-방향키로 탭의 위치를 변경할 수 있다.
3.2.15.3 <C-w>: centaur-tabs--kill-this-buffer-dont-ask
현재 탭을 닫는다.
3.2.16 which-key
특정 키바인딩을 입력 중인 경우 사용 가능한 키바인딩을 팝업창으로 보여주는 패키지
3.2.16.1 <SPC-x-k>: which-key-mode
which-key 모드를 토글한다.
3.2.17 which-func
현재 커서 위에 놓여진 함수의 이름을 modeline에 표시하는 패키지
3.2.18 multi-term
emacs 내에서 터미널(bash, zsh)를 사용할 수 있는 패키지
3.2.18.1 <SPC-m-m>: multi-term
새로운 터미널을 생성한다. 여러 터미널을 동시에 띄울 수 있다.
3.2.18.2 <SPC-m-t>: my-multi-term
이미 열려있는 터미널의 버퍼로 이동한다. 자동으로 화면을 분할하여 터미널로 이동한다.
3.2.19 iedit
특정 단어를 코드 전체에 걸쳐서 일괄적으로 변경할 수 있는 패키지
3.2.19.1 <A-[>: iedit-mode
커서 위의 코드와 동일한 다른 코드를 iedit 모드로 편집한다.
3.2.19.2 <Tab, ESC>: iedit-next-occurence, iedit-toggle-selection
Tab으로 같은 단어들을 네비게이션할 수 있으며 ESC 키를 통해 변경을 원치않는 단어들을 뺄 수 있다.
3.2.20 wgrep
grep 명령어를 통해 동일한 단어를 검색한 경우 이를 일괄적으로 수정할 수 있는 패키지
3.2.20.1 <C-c C-p>, <C-x C-s>: wgrep-change-to-wgrep-mode, wgrep-finish-edit
grep 모드에서 C-c C-p키를 통해 wgrep 모드를 실행한다. wgrep 모드에서 단어를 변경한 후 C-x C-s 키를 통해 변경사항을 저장한다.
3.2.21 avy
코드 간 빠른 네비게이션 기능을 수행할 수 있는 패키지
3.2.21.1 <SPC-`>: evil-avy-goto-word-0
코드를 빠르게 네비게이션한다.
3.2.22 ecb
코딩에 필요한 현재 디렉토리, 현재 파일, 함수 또는 클래스 정보를 표시해주는 GUI 패키지
3.2.22.1 <SPC-c-e>: ech-minor-mode
ecb 모드를 토글한다.
3.2.23 highlight-symbol
특정 코드를 하이라이팅하는 기능, 같은 단어를 빠르게 네비게이션하는 기능을 제공하는 패키지
3.2.23.1 <SPC-c m,n>: highlight-symbol, highlight-symbol-remove-all
커서 위의 코드를 하이라이팅한다. 현재는 커서 위에 단어를 올리면 자동으로 동일한 단어가 하이라이팅되기 때문에 SPC-c-m 키를 연속해서 두 번 눌러야 정상적으로 하이라이팅된다. SPC-m-n 키로 모든 하이라이팅을 제거한다.
3.2.23.2 <[, ]>: highlight-symbol-next(prev)
커서 위의 단어와 동일한 단어를 빠르게 네비게이션한다.
3.2.24 hideshow
(emacs 기본패키지) 긴 코드를 접고 펼치는 패키지
3.2.24.1 <A-g>: hs-toggle-hiding
해당 코드의 괄호를 접었다 펼쳤다 토글한다.
3.2.24.2 <SPC-x l,s>: hs-hide-level, hs-show-all
코드 전체의 괄호를 접었다 펼친다. SPC-x-l로 모든 코드의 괄호를 접으며 SPC-x-s로 모든 코드를 펼친다.
3.2.25 undo-tree
코드의 작업취소(undo), 복구(redo)를 수행해주는 패키지. 시각적으로 변경 정보를 한 눈에 볼 수 있는 visual undo-tree 기능도 제공한다.
3.2.25.1 <SPC-c-z>: undo-tree-visualize
작업취소 내역을 시각적으로 보여준다.
3.2.25.2 <U>: undo-tree-redo
작업취소한 내역을 다시 복구한다.
3.2.26 highlight-indentation
코드 들여쓰기를 시각적으로 표현해주는 패키지
3.2.26.1 <C-S-l>: highlight-indentation-mode
코드의 들여쓰기를 시각적으로 보여준다.
3.2.27 mic-paren
괄호에 커서를 올리면 반대편 괄호를 하이라이팅해주는 패키지
3.2.28 smartparens
괄호를 입력하면 닫는괄호도 자동으로 입력해주는 패키지
3.2.29 google-c-style
구글의 C 코딩 스타일 들여쓰기를 적용해주는 패키지
3.2.30 doom-modeline
doom 테마의 modeline을 사용할 수 있는 패키지. 하단의 보라색 라인을 modeline이라고 하는데 여기에 표시되는 정보들을 설정할 수 있다. 색상은 테마를 변경하면 같이 변경된다.
4 References
'Software' 카테고리의 다른 글
gdb 디버거 사용법 및 다양한 기능 설명 (0) | 2022.01.06 |
---|---|
tmux 다중터미널 프로그램 사용법 설명 및 환경설정 (0) | 2022.01.06 |
Vim 에디터 사용법 설명 및 C++/Python 개발환경설정 (0) | 2022.01.06 |
cmake 사용법 및 다양한 옵션 정리 (0) | 2022.01.06 |
x11vnc 원격 접속 서버 설치 및 사용법 정리 (0) | 2022.01.04 |