본문 바로가기

Software

Emacs 에디터 사용법 설명 및 C++/Python 개발환경설정

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 링크 파일이 존재해야 한다. 다음과 같이 진행한다.

  1. 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 ..
  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 ./
  1. 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

  1. Naver blog 포스트
  2. 이맥스와 함께하는 개발환경 - TOAST Meetup