My custom DOOM EMACS Config

Table of Contents

Initialize

Load Config from files

우선 한곳에 모든 설정들을 모아두면 알아보기 힘드니 대략적인 기능별로 파일을 나누어 관리한다.

(load! "+custom_funcs")
;; (load! "+private")

+private.el 파일에는 공개하기 곤란한 개인 토큰이나 패스워드등이 담겨 있다.

Packages

(package! tramp)
(package! rainbow-delimiters)
(package! expand-region)
(package! dotenv-mode)
(package! gitlab-ci-mode)
(package! rg)
(package! evil-matchit)
(package! drag-stuff)
(package! evil-textobj-anyblock)
(package! git-link)
(package! w3m)
(package! prettier-js)
(package! nyan-mode)
(package! emacs-async)
(package! company-tabnine :recipe (:host github :repo "TommyX12/company-tabnine"))
;; (package! code-review)
;; (package! dash)
;; (package! org-jira)
;; (package! ejira
;; :recipe
;; (:host github
;; :repo "nyyManni/ejira"
;; :files ("*.el")
;; ))

Custom Setup

Startup Setup

이맥스 관련 일반적인 사항들을 지정한다. 주로 특정 모드가 아닌 이맥스에 글로벌 설정들이 포함된다.

;; 사용자 이름 설정
(setq user-full-name "Jaejin Park"
      user-mail-address "jjpark78@gmail.com")
(setq-default frame-title-format '("DOOM EMACS"))
(setq auth-sources '("~/.authinfo"))

(setq avy-all-windows t)

(setq confirm-kill-emacs nil)
(setq which-key-idle-delay 0.5)
(setq which-key-allow-multiple-replacements t)
(after! which-key
  (pushnew!
   which-key-replacement-alist
   '(("" . "\\`+?evil[-:]?\\(?:a-\\)?\\(.*\\)") . (nil . "\\1"))
   '(("\\`g s" . "\\`evilem--?motion-\\(.*\\)") . (nil . "\\1"))
   ))

;; 수동으로 직접 프레임 위치를 지정해줘 본다.
;; 언제부터 이맥스에 픽셀단위로 크기 지정이 가능해졌지 ?? 찾아보니 2015년 이후란다. 이맥스를 사랑하는 개발자로서
;; 이런 중요한 업데이트를 모르고 있다뉘...
(when (display-graphic-p)
  (setq frame-resize-pixelwise t)
  (set-frame-position (selected-frame) 1128 32)
  (set-frame-size (selected-frame) 1230 1350 t))

(global-evil-matchit-mode)
(global-auto-revert-mode t)

;; 스나이프를 화면에 보이는 영역으로 한다.
(setq evil-snipe-scope 'whole-visible)
(setq evil-snipe-repeat-scope 'whole-visible)

;; just for fun
(nyan-mode)
(nyan-start-animation)

;; for very long src-block fontify
(setq jit-lock-stealth-time 0.5)

;; melpa나 패키지 지원을 하지 않는 커스텀 lisp들을 추가로 로드한다
;; gendoxy
(add-to-list 'load-path (expand-file-name "~/.doom.d/elisps/gendoxy"))

;; (when (and (file-exists-p "~/.doom.d/emacs-dash.png"))
;;   (setq +doom-dashboard-banner-padding '(8 . 4)
;;         +doom-dashboard-banner-file "emacs-dash.png"
;;         +doom-dashboard-banner-dir "~/.doom.d"))

UTF & Korean Input

한글 관련 설정

;; 한글 입력기 on
(setq default-input-method "korean-hangul")
(set-language-environment "Korean")
(setq locale-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(prefer-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(require 'ucs-normalize)
(set-file-name-coding-system 'utf-8-hfs)

;; 편집모드에서 빠져나갈때 한글이 켜져있으면 이후에 노멀 모드에서 커맨드가 안먹히는 짜증이 발생한다.
;; 그냥 편집모드에서 빠져나갈때 무조건 리셋시킨다. 엄청 편리함
(add-hook 'evil-insert-state-exit-hook (lambda ()
                                         (setq evil-input-method nil)))

UIs

이맥스의 ui설정에 관련된 사항.

;; 이것 저것 많이 바꿔봤지만 역시 기본 테마가 젤 이쁘다
(setq doom-theme 'doom-one)

;; ;; 노안이 왔는지 이제는 이정도 폰트 크기는 되어야 잘 보임
(setq doom-font (font-spec :family "FiraMono NF" :size 16))
;; (setq doom-unicode-font (font-spec :family "FiraMono NF" :size 16))

;; ;; 한글 관련 폰트 스케일링 설정
;; (set-fontset-font "fontset-default" 'hangul (font-spec :family "AppleGothic"))
(set-fontset-font "fontset-default" 'hangul (font-spec :family "MaruBuri" :size 18))
;; (set-fontset-font t 'japanese-jisx0213.2004-1 (font-spec :name "AppleGothic"))
;; (set-fontset-font t 'katakana-jisx0201 (font-spec :name "AppleGothic"))
;; (setq face-font-rescale-alist '(("AppleGothic" . 1.3007692307692308) ))
Popup Rules

이맥스에서 몇가지 자주 쓰는 버퍼들이 정해진 위치에 표시되도록 해서 레이아웃을 망치지 않고 작업 효율을 높이도록 한다. DOOM 이맥스는 기본 키 바인딩인 ‘SPC ~’ 조합을 사용해서 해당 버퍼의 윈도우를 빠르게 토글 시킬 수 있어서 정말 편하다.

(set-popup-rule! "^\\*Messages\\*" :ttl t :side 'bottom :height 12 :quit t)
(set-popup-rule! "^\\*doom:vterm*" :ttl t :side 'bottom :height 20 :quit t)
(set-popup-rule! "^\\*npm*" :ttl t :side 'bottom :height 20 :quit t)
(set-popup-rule! "^\\*Flycheck*" :ttl t :side 'bottom :height 20 :quit t)

Doom Modeline

Doom ModeLine 관련 수정

(after! doom-modeline
  (setq doom-modeline-major-mode-icon t
        doom-modeline-buffer-encoding nil
        doom-modeline-persp-name t
        doom-modeline-mu4e nil
        doom-modeline-buffer-file-name-style 'truncate-with-project))

Fine-tune

이맥스를 맥과 고해상도(FHD이상의 해상도)에서 쓰다보면 스크롤이 엄청 느려지는 경우가 있다. 여러 이슈들을 확인한 결과 어쩔수 없다 함. 해당 현상을 완화하는데 조금이나마 도움이 된다하여서 구글링을 통해 찾은 각종 튜닝들을 여기다가 넣어 놓았다.

;;gccemacs로 넘어 오기 전에는 라인넘버를 껐었는데
;;넘어오니까 완전 빨라져서 다시 라인넘버를 사용하기 시작함.
(setq display-line-numbers-type 'relative)

;; 더블버퍼링이 동작하도록 설정한다.
(add-to-list 'default-frame-alist '(inhibit-double-buffering . t))

;; lsp 관련 설정 메뉴들
;; 이맥스를 느리게 만드는 범인중 십중팔구 LSP가 관련되어져 있다고 함.
;; 해당 튜닝도 구글링을 통해서 찾았다.
(setq lsp-file-watch-threshold (* 1024 1024))
(setq read-process-output-max (* 10 1024 1024))

Useful Bindings

단축키 설정

(define-key evil-normal-state-map (kbd "C-j") 'evil-window-down)
(define-key evil-normal-state-map (kbd "C-k") 'evil-window-up)
(define-key evil-normal-state-map (kbd "C-h") 'evil-window-left)
(define-key evil-normal-state-map (kbd "C-l") 'evil-window-right)

(map! :leader :prefix "g" :desc "ediff style diff from working-tree" "d" #'magit-ediff-show-working-tree)
;; go back, go references
(map! :n "gr" #'+lookup/references)
;; 검색할때 브라우저를 찾는 수고를 줄여 준다.
(map! :leader :prefix "s" :desc "Search Google.com" "g" #'google-search)
(map! :leader :prefix "s" :desc "Search StackOverFlow" "v" #'stackoverflow-search)
(map! :leader :prefix "s" :desc "Search Github" "h" #'github-search)
(map! :leader :prefix "s" :desc "Search Online Watchtower Library" "w" #'jw-wol-search)

(map! :leader :prefix "s" :desc "Search Buffers" "b" #'swiper-all)
(map! :leader :prefix "s" :desc "new project search file" "p" #'rg-project)
;; 가끔씩 즐겨보는 블로그들의 rss를 피드로 받아와서 읽을때 사용한다.
(map! :leader :prefix "o" :desc "Open news form RSS with ELfeed" "n" #'elfeed)
(map! :leader :prefix "o" :desc "Open mu4e to current window" "m" #'mu4e)
;; 버퍼끼리 화면 전환할때 프로젝트를 벗어 나지 않도록 강제한다.
;; (map! :leader :desc "workspace buffer list" "," '+vertico/switch-workspace-buffer)
(map! :leader :desc "workspace buffer list" "," 'counsel-projectile-switch-to-buffer)
;; ORG 모드에서 쓰는 단축키들
(map! :leader :desc "Tangle Export" "ee" #'org-babel-tangle)
;; 커스텀 함수로 정의해둔 설정 파일불러오는 함수에게 단축기를 할당했음.
;; 자주 쓰지는 않는데 있어보이는 척 할때 아주 좋다.
(map! :leader :prefix "f" :desc "Open Shell init file on other windows" "gs" #'my/find-shell-init-file)
;; (map! :leader :prefix "f" :desc "Open alacritty init file on other windows" "ga" #'my/find-alacritty-init-file)
(map! :leader :prefix "f" :desc "Edit Tmuxinator Session File" "gi" 'my/find-tmuxinator-file)
(map! :leader :prefix "f" :desc "Edit Tmux Configuration File" "gt" 'my/find-tmuxconfig-file)
;; 코드를 입력받아서 이쁜 화면으로 만들어주는 패키지에 단축기를 할당했다.
(map! :leader :prefix "t" :desc "Capture Code with Carbon now" "t" #'carbon-now-sh)
;; change window split mode
;; 이맥스를 넓게 쓰다가 길게 쓰다가 할때마다 자주 쓰이는 레이아웃 번경 맛집 함수
(map! :leader :prefix "t" :desc "Toggle Window Split Style" "s" #'toggle-window-split)
;; ace-window
(map! :leader :prefix "w" :desc "open ace window to select window" "a" #'ace-window)
;; evil 에서 라인 처음과 마지막으로 더 빨리 점프할 수 있도록 한다.
(map! :leader :prefix "c" :desc "run npm script" "n" #'npm-mode-npm-run)

(map! :leader :prefix "q" :desc "quit frame without prompt" "f" #'delete-frame)
(map! :leader :prefix "q" :desc "quit frame without prompt" "q" #'delete-frame)

(define-key evil-visual-state-map (kbd "H") 'beginning-of-line-text)
(define-key evil-visual-state-map (kbd "L") 'evil-end-of-line)
(define-key evil-normal-state-map (kbd "H") 'beginning-of-line-text)
(define-key evil-normal-state-map (kbd "L") 'evil-end-of-line)
;; evil multi edit recommanded setting
(define-key evil-visual-state-map (kbd "C-M-m") 'evil-multiedit-match-all)
(define-key evil-normal-state-map (kbd "C-M-m") 'evil-multiedit-match-all)
(define-key evil-insert-state-map (kbd "C-M-m") 'evil-multiedit-match-all)
;; 블럭 단위로 한번에 선택하고 싶을때 사용하면 좋다.
;; 기본 단축키가 너무 불편해서 변경했다.
(define-key evil-normal-state-map (kbd "C-M-k") #'er/expand-region)
(define-key evil-normal-state-map (kbd "C-M-j") #'er/contract-region)
(define-key evil-insert-state-map (kbd "C-M-k") #'er/expand-region)
(define-key evil-insert-state-map (kbd "C-M-j") #'er/contract-region)

;;ivy 미니 버퍼에서 컨트롤 키로 아이템을 선택하는건 새끼손가락에 죄를 짓는 일이다.
(map! :after ivy :map ivy-minibuffer-map "TAB" 'next-line)

;;vertico로 둠이 변경되어서 같은 바인딩을 추가 한다.
;; (map! :after vertico :map vertico-map "TAB" 'vertico-next)

;; ORG 모드에서 헤더 레벨 설정할때 쓰기 편한 단축키
(map! :after org-mode :map org-mode-map ">" 'org-cycle-level)

;; <SPC> w C-o 는 너무 누르기 힘들지만 이게 의외로 많이 쓰인다. 쓰이지 않는 키 바인딩에 할당해서 더 간단히 만든다.
(map! :leader :prefix "w" :desc "Close Other Windows Fast Binding" "O" 'delete-other-windows)

(drag-stuff-global-mode t)

(define-key evil-normal-state-map (kbd "M-k") 'drag-stuff-up)
(define-key evil-visual-state-map (kbd "M-k") 'drag-stuff-up)
(define-key evil-normal-state-map (kbd "M-j") 'drag-stuff-down)
(define-key evil-visual-state-map (kbd "M-j") 'drag-stuff-down)

(defun execute-gitkraken ()
  (interactive)
  (call-process-shell-command "gitkraken&" nil 0))

(defun execute-chrome ()
  (interactive)
  (call-process-shell-command "run-window-chrome&" nil 0))

(defun execute-alacritty ()
  (interactive)
  (call-process-shell-command "/mnt/c/alacritty.exe -e \"wsl.exe tmux\"" nil 0))

; 편리하게 외부 프로그램을 실행한다.
(map! :leader :prefix "r" :desc "Run Command - Gitkraken" "gk" 'execute-gitkraken)
(map! :leader :prefix "r" :desc "Run Command - Google Chrome" "gh" 'execute-chrome)
(map! :leader :prefix "r" :desc "Run Command - alacritty" "ga" 'execute-alacritty)

Programming

CC-Mode & CCLS Setting

(defun custom-cc-mode ()
  "Custom cc-mode make support platfomio, qml, qmake etc."
  (interactive)
  ;; (lsp)
  (setq lsp-prefer-flymake nil
        lsp-ui-peek-fontify 'always
        lsp-ui-doc-include-signature nil  ; don't include type signature in the child fram
        lsp-ui-sideline-show-symbol nil)  ; don't show symbol on the right of info
  (setq-default flycheck-disabled-checkers '(c/c++-clang c/c++-cppcheck c/c++-gcc)))

(use-package ccls
  :config '(ccls-initialization-options (quote (compilationDatabaseDirectory :build)))
    :hook ((c-mode c++-mode objc-mode) . (lambda () (require 'ccls) (lsp))))

LSP / Language Mode Hooks

주로 사용하는 언어들 관련 설정. lsp관련 설정들을 모아 놓았다.

;; 뷰모드가 느리게 동작하고 아직 버그가 많아서 웹 모드로 바꾼다.
(add-to-list 'auto-mode-alist '("\\.vue$" . web-mode))
(add-to-list 'auto-mode-alist '("\\.env$" . dotenv-mode))
(add-to-list 'auto-mode-alist '("\\.ino$" . cpp-mode))
(add-to-list 'auto-mode-alist '("\\.js$" . js2-mode))
(add-to-list 'auto-mode-alist '("\\.jsx$" . js2-mode))
(add-to-list 'auto-mode-alist '("\\.ts$" . typescript-mode))
(add-to-list 'auto-mode-alist '("\\.tsx$" . typescript-mode))

(add-hook 'web-mode-hook 'my/custom-web-mode)
(add-hook 'web-mode-hook 'prettier-js-mode)
(add-hook 'js2-mode-hook 'prettier-js-mode)
(add-hook 'js2-mode-hook 'my/custom-js-mode)
(add-hook 'typescript-mode-hook 'my/custom-ts-mode)
(add-hook 'typescript-mode-hook 'prettier-js-mode)
(add-hook 'typescript-tsx-mode-hook 'my/custom-ts-mode)
(add-hook 'typescript-tsx-mode-hook 'prettier-js-mode)
(add-hook 'cc-mode-hook 'custom-cc-mode)
(add-hook 'cpp-mode-hook 'custom-cc-mode)

(setq lsp-auto-guess-root t)

(set-company-backend! 'typescript-mode '(company-capf))
(setq flycheck-global-modes '(not conf-colon-mode gfm-mode forge-post-mode gitlab-ci-mode dockerfile-mode Org-mode org-mode))

(setq lsp-ui-sideline-show-code-actions nil
      lsp-ui-sideline-show-diagnostics t
      lsp-modeline-diagnostics-mode nil
      lsp-modeline-diagnostics-enable nil
      lsp-signature-render-all t)

;; 린트 에러 버퍼를 오픈하면 포커스가 자동으로 이동하지 않는다.
;; 이거 없으면 생각보다 귀찮아진다.
(defun my/custom-diagnostics ()
    (interactive)
    (flycheck-list-errors)
    (switch-to-buffer-other-window "*Flycheck errors*"))

(map! :leader :prefix "c" :desc "Display LSP Errors" "x" 'my/custom-diagnostics)

;; (add-hook 'flycheck-error-list-mode-hook (lambda () (progn
;;                                                       (message "called lambda function")
;;                                                       (switch-to-buffer-other-window "*Flycheck errors*"))))

Vue & Typescript Custom Setting

Vue와 타입스크립트를 위한 커스텀 설정 모드.

(defun my/custom-ts-mode ()
  (if (not (equal buffer-file-name 'nil))
      (let ((extname (file-name-extension buffer-file-name)))
        (when (or (string-equal "tsx" extname)
                  (string-equal "ts" extname))
          (setup-custom-jsts-mode)))))
          ;; (flycheck-select-checker 'javascript-eslint)))))

(defun my/custom-js-mode ()
  (if (not (equal buffer-file-name 'nil))
      (let ((extname (file-name-extension buffer-file-name)))
        (when (or (string-equal "js" extname)
                  (string-equal "jsx" extname))
          (setup-custom-jsts-mode)
          (setq js2-strict-missing-semi-warning nil)))))
          ;; (flycheck-select-checker 'javascript-eslint)))))

(defun my/custom-web-mode ()
  "Custom hooks for vue-mode"
  (if (not (equal buffer-file-name 'nil))
      (let ((extname (file-name-extension buffer-file-name)))
        (when (string-equal "vue" extname)
          (setup-custom-jsts-mode)
          ;; (flycheck-select-checker 'javascript-eslint)
          ))))

(defun setup-custom-jsts-mode ()
  ;; 기본 인덴테이션을 설정한다.
  (lsp!)
  (setq typescript-indent-level 2)
  (setq emmet-indentation 2)
  (setq js-indent-level 2)
  ;; (setq global-git-gutter-mode t)
  (setq web-mode-code-indent-offset 2)
  (setq web-mode-css-indent-offset 2)
  (setq web-mode-markup-indent-offset 2)
  (flycheck-mode +1)
  ;; (my/use-eslint-from-node-modules)
  ;; (flycheck-add-mode 'javascript-eslint 'web-mode)
  ;; (flycheck-add-mode 'javascript-eslint 'typescript-mode)
  ;; (flycheck-add-mode 'javascript-eslint 'js2-mode)
  (setq lsp-ui-peek-fontify 'always)
  (setq flycheck-check-syntax-automatically '(save mode-enabled))
  )

;; (defun my/use-eslint-from-node-modules ()
;;   (let* ((root (locate-dominating-file
;;                 (or (buffer-file-name) default-directory)
;;                 "node_modules"))
;;          (eslint (and root
;;                       (expand-file-name "node_modules/eslint/bin/eslint.js"
;;                                         root))))
;;     (when (and eslint (file-exists-p eslint))
;;       (setq-local flycheck-javascript-eslint-executable eslint))))

Tabnine

제대로 설정할 수 있게 되었기 때문에 다시 사용한다.

(after! company
  (setq +lsp-company-backends '(company-tabnine :separate company-capf company-yasnippet))
  (setq company-show-numbers t)
  (setq company-idle-delay 0)
)

Programming ETC

개발관련 기타 설정들

;; 1초라도 빨리 팝업 띄우고 싶어서, 그러나 실제 체감속도 향상은 없음
(setq company-idle-delay 0)

(drag-stuff-mode t)
;; ;; persp 모드에서 터미널도 지원하도록 한다.
;; (persp-def-buffer-save/load
;;   :mode 'eshell-mode :tag-symbol 'def-eshell-buffer
;;   :save-vars '(major-mode default-directory))

Interact Persp-mode and Tmux

이맥스에서 Persp모드를 많이 활용하는데 Tmux의 window와 동기화를 시키면 매우 편리하다. 이맥스에서 직접 터미널을 만져도 되지만, 가끔 이유없이 터미널 버퍼가 사라지기도 하고, 터미널 버퍼를 여러개 사용하면, 이맥스가 무거워지고 또 Persp-mode에서 버퍼를 포함해서 세선파일로 저장하는 방법을 아직 몰라서 코딩과 문서는 이맥스에서 하고 빌드나 스크립트 실행은 Tmux에서 수행하는 워크플로우가 익숙하다.

(defun my/persp-tmux-sync (file hash name)
  (let ((tmux-command (concat "tmux " "switch-client " "-t " (file-name-nondirectory file) " > /dev/null 2>&1")))
        (shell-command tmux-command nil nil)))
(add-hook! 'persp-after-load-state-functions 'my/persp-tmux-sync)

Magit & Forge

magit이나 dired등과 같이 유틸리티 관련 설정들을 모아 놓았다.

;; vc & magit 관련 설정
(setq vc-follow-symlinks t)
(setq find-file-visit-truename t)
(setq magit-refresh-status-buffer 'switch-to-buffer)
(setq magit-rewrite-inclusive 'ask)
(setq magit-save-some-buffers t)
(setq magit-set-upstream-on-push 'askifnotset)
(setq magit-diff-refine-hunk 'all)

;; (magit-delta-mode)
;; (magit-todos-mode)
(setq forge-topic-list-limit '(200 . 10))

;; ediff를 닫을때 항상 물어보는 거 금지!!
(defadvice! shut-up-ediff-quit (orig-fn &rest args)
  :around #'ediff-quit
  (letf! (defun y-or-n-p (&rest _) t)
    (apply orig-fn args)))
(after! git-link
  (setq git-link-default-remote "upstream"
        git-link-default-branch "develop"
        git-link-open-in-browser nil
  )
  (map! :leader :prefix "g" :desc "get remote link using git-link"  "k" #'git-link)
)

Magit의 Forge를 사용하면 깃랩 이슈나 머지리퀘스트를 이맥스에서 편하게 생성할 수 있다. 하는 김에 단축기도 좀 편하게 evil스타일로 변경해본다.

(after! forge
  ;; (setq auth-sources '("~/.authinfo"))
  (add-to-list 'forge-alist '("gitlab.com" "gitlab.com/api/v4" "gitlab.com" forge-gitlab-repository))
  ;; O-T (Open This)바인딩으로 브라우저에서 링크를 열 수 있도록 지원한다.
  (define-key forge-topic-title-section-map (kbd "ot") 'forge-custom-open-url)
  (define-key forge-topic-marks-section-map (kbd "ot") 'forge-custom-open-url)
  (define-key forge-topic-state-section-map (kbd "ot") 'forge-custom-open-url)
  (define-key forge-topic-labels-section-map (kbd "ot") 'forge-custom-open-url)
  (define-key forge-topic-milestone-section-map (kbd "ot") 'forge-custom-open-url)
  (define-key forge-topic-assignees-section-map (kbd "ot") 'forge-custom-open-url)
  (define-key forge-post-section-map (kbd "ot") 'forge-custom-open-url)
  ;; Y-T (Yank This)바인딩으로 이슈와 커멘트들의 링크를 복사한다.
  (define-key forge-topic-title-section-map (kbd "yt") 'forge-copy-url-at-point-as-kill)
  (define-key forge-topic-marks-section-map (kbd "yt") 'forge-copy-url-at-point-as-kill)
  (define-key forge-topic-state-section-map (kbd "yt") 'forge-copy-url-at-point-as-kill)
  (define-key forge-topic-labels-section-map (kbd "yt") 'forge-copy-url-at-point-as-kill)
  (define-key forge-topic-milestone-section-map (kbd "yt") 'forge-copy-url-at-point-as-kill)
  (define-key forge-topic-assignees-section-map (kbd "yt") 'forge-copy-url-at-point-as-kill)
  (define-key forge-post-section-map (kbd "yt") 'forge-copy-url-at-point-as-kill)
  ;; E-T i(Edit This)바인딩으로 간편하게 모든걸 수정하자
  (define-key forge-topic-title-section-map (kbd "et") 'forge-edit-topic-title)
  (define-key forge-topic-marks-section-map (kbd "et") 'forge-edit-topic-marks)
  (define-key forge-topic-state-section-map (kbd "et") 'forge-edit-topic-state)
  (define-key forge-topic-labels-section-map (kbd "et") 'forge-edit-topic-labels)
  (define-key forge-topic-milestone-section-map (kbd "et") 'forge-edit-topic-milestone)
  (define-key forge-topic-assignees-section-map (kbd "et") 'forge-edit-topic-assignees)
  (define-key forge-post-section-map (kbd "et") 'forge-edit-post)
  (define-key forge-post-section-map (kbd "dt") 'forge-delete-comment)
  (define-key forge-topic-mode-map (kbd "ar") 'forge-create-post)
  ;; 팝업을 별도의 버퍼로 띄우도록 한다.
  ;; (setq magit-display-buffer-function #'+magit-my-display-buffer-fn)
  (setq markdown-display-remote-images t)

  ;;section visibility
  (setq magit-section-initial-visibility-alist
        '((stashes . show)
          (untracked . show)
          (unstaged . show)
          (staged . show)
          (unpushed . show)
          ;; (todos . show)
          (issues . show)
          (pullreqs . show)))
  )

RSS Feed

Custom Elfeed Search Column

기본 피드 목록 화면은 한글 제목의 문자열 길이 계산에 버그가 있는지 컬럼 정렬이 뒤죽박죽이다. 그래서 컬럼 순서에서 제목 부분을 제일 뒤로 두어 깔끔하게 정렬되도록 한다. 구글링 해서 찾았음.

(defun feed-reader/search-print (entry)
      "Print ENTRY to the buffer."
      (let* ((feed-width 16)
              (tags-width 8)
              (title (or (elfeed-meta entry :title) (elfeed-entry-title entry) ""))
              (title-faces (elfeed-search--faces (elfeed-entry-tags entry)))
              (feed (elfeed-entry-feed entry))
              (feed-title
              (when feed
              (or (elfeed-meta feed :title) (elfeed-feed-title feed))))
              (tags (mapcar #'symbol-name (elfeed-entry-tags entry)))
              (tags-str (concat "[" (mapconcat 'identity tags ",") "]"))
              (title-width (- (window-width) feed-width tags-width 4))
              (title-column (elfeed-format-column
                              title (elfeed-clamp
                              elfeed-search-title-min-width
                              title-width
                              elfeed-search-title-max-width)
                              :left))
              (tag-column (elfeed-format-column
                      tags-str (elfeed-clamp (length tags-str) tags-width tags-width)
                      :left))
              (feed-column (elfeed-format-column
                      feed-title (elfeed-clamp feed-width feed-width feed-width)
                      :left)))
      (insert (propertize feed-column 'face 'elfeed-search-feed-face) " ")
      (insert (propertize tag-column 'face 'elfeed-search-tag-face) " ")
      (insert (propertize title 'face title-faces 'kbd-help title))))

 (setq elfeed-search-print-entry-function #'feed-reader/search-print)

이맥스에서 RSS피드를 받아 보기에 편하다.

(setq elfeed-feeds '(
                     "http://www.bloter.net/feed"
                     "https://d2.naver.com/d2.atom"
                     "https://engineering.linecorp.com/ko/feed/"
                     "http://sachachua.com/blog/category/emacs/feed"
                     "https://blog.rust-embedded.org/rss.xml"
                     ))
(map! :leader :map elfeed-show-map "U" 'elfeed-update)
(setq elfeed-search-filter "@6-month-ago +unread")

Utils

Persp-mode and Special Buffers

(persp-def-buffer-save/load
  :mode 'vterm-mode :tag-symbol 'def-vterm-buffer
  :save-vars '(major-mode default-directory))

Web-browser for WSL

; browse-url-function용 설정 함수
(defun execute-chrome-with-args (url &optional args)
  ;; 만약 WSL내부의 파일을 접근하는 것이라면 윈도우애서부터 접근할 수 있도록 URL을 수정한다.
  (if (equal "file://" (substring url 0 7))
      (progn
        (setq wsl-latest-md-preview-url (string-replace "file://" "file://wsl%24/Manjaro" url))
        (call-process-shell-command (concat "run-window-chrome " wsl-latest-md-preview-url " &") nil 0))
      (call-process-shell-command (concat "run-window-chrome " url " &") nil 0)))
(setq browse-url-browser-function #'execute-chrome-with-args)

run-window-chrome은 적당한 shell script 파일인데 크롬을 좀 더 편하게 실행 할 수 있게 도와주는 스크립트이다. 대략 다음과 같이 생겼다.

#!/bin/zsh
# execute windows chrome
/mnt/c/Program\ Files/Google/Chrome/Application/chrome.exe $1

Ripgrep

rg.el 관련 설정.

(use-package rg
  :config
  (setq rg-group-result t
        rg-hide-command t
        rg-show-columns nil
        rg-show-header t
        rg-custom-type-aliases nil
        rg-default-alias-fallback "all")
  ;; 버퍼가 열리면 포커스를 그쪽으로 이동시킨다.
  ;; 이거 없으면 생각보다 귀찮아진다.
  (add-hook 'rg-mode-hook (lambda () (switch-to-buffer-other-window "*rg*"))))

Google, StackOverFlow Search

구글 검색, 각종 사이트 검색을 편리하게 하기 위한 간단한 유틸리티 함수들 구글링으로 찾았다.

(defun stackoverflow-search ()
"search keyword in google code search and stackoverflow.com"
    (interactive)
    (require 'w3m)
    (let ((keyword (w3m-url-encode-string (read-string "Enter Search Text: "))))
      (execute-chrome-with-args (concat "https://www.google.com/search\\?q=" keyword "+site:stackoverflow.com")))
)

(defun google-search ()
"search word under cursor in google code search and google.com"
    (interactive)
    (require 'w3m)
    (let ((keyword (w3m-url-encode-string (read-string "Enter Search Text: "))))
      (execute-chrome-with-args (concat "https://www.google.com/search\\?q=" keyword "")))
)

(defun github-search ()
"search word under cursor in google code search and google.com"
    (interactive)
    (require 'w3m)
    (let ((keyword (w3m-url-encode-string (read-string "Enter Search Text: "))))
      (execute-chrome-with-args (concat "https://www.google.com/search\\?q=" keyword "+site:github.com")))
)

(defun jw-wol-search ()
"과연 이맥스에서 온라인 라이브러리 검색을 익숙하게 할 수 있을까 ?? org-protocol을 활용한 브라우저와의 연동을 시험해본다"
   (interactive)
   (require 'w3m)
   (let ((keyword (w3m-url-encode-string (read-string "Enter Search Text:"))))
     (execute-chrome-with-args (concat "https://www.google.com/search\\?q=" keyword "+site:wol.jw.org")))
)

Forge Custom Open Link

Forge에서 브라우저로 바로 열수 있는 함수를 사용한다. 역시 사전에 만들어 놓은 ‘execute-chrome-with-args’ 함수를 사용해서 외부 윈도우의 크롬브라우저를 열도록 수정한다.

(defun forge-custom-open-url ()
  (interactive)
  (if-let ((url (forge-get-url (or (forge-post-at-point)
                                   (forge-current-topic)))))
      (progn
        (execute-chrome-with-args url)))
  )

Toggle Window Layout

윈도우를 두개로 나누었을때 가로, 세로 나누기로 변경하는 함수.

(defun toggle-window-split ()
  (interactive)
  (if (= (count-windows) 2)
      (let* ((this-win-buffer (window-buffer))
             (next-win-buffer (window-buffer (next-window)))
             (this-win-edges (window-edges (selected-window)))
             (next-win-edges (window-edges (next-window)))
             (this-win-2nd (not (and (<= (car this-win-edges)
                                         (car next-win-edges))
                                     (<= (cadr this-win-edges)
                                         (cadr next-win-edges)))))
             (splitter
              (if (= (car this-win-edges)
                     (car (window-edges (next-window))))
                  'split-window-horizontally
                'split-window-vertically)))
        (delete-other-windows)
        (let ((first-win (selected-window)))
          (funcall splitter)
          (if this-win-2nd (other-window 1))
          (set-window-buffer (selected-window) this-win-buffer)
          (set-window-buffer (next-window) next-win-buffer)
          (select-window first-win)
          (if this-win-2nd (other-window 1))))))

ETC

Shell Utils

쉘 설정 파일을 바로 불어 올 수 있는 함수. 단축기와 연동하여 사용한다. zsh관련 설정 파일을 만질 일이 있을때 요긴하게 잘 사용한다.

(defun my/find-alacritty-init-file ()
  "Edit the shell init file in another window."
  (interactive)
    (find-file-other-window (expand-file-name ".config/alacritty/alacritty.yml" (getenv "HOME"))))

(defun my/find-tmuxconfig-file ()
  "Edit the shell init file in another window."
  (interactive)
    (find-file-other-window (expand-file-name ".tmux.conf" (getenv "HOME"))))

(defun my/find-tmuxinator-file ()
  "Brows tmuxinator session definition"
  (interactive)
  (find-file-other-window "~/.config/tmuxinator")
  )

(defun my/find-shell-init-file ()
  "Edit the shell init file in another window."
  (interactive)
  (let* ((shell (car (reverse (split-string (getenv "SHELL") "/"))))
         (shell-init-file (cond
                           ((string-equal "zsh" shell) ".zshrc")
                           ((string-equal "bash" shell) ".bashrc")
                           (t (error "Unknown shell")))))
    (find-file-other-window (expand-file-name shell-init-file (getenv "HOME")))))