KUR Creative


kill-current-sexp의 Emacs, VSCode 구현

나만 쓰는 명령어 kill-current-sexp

kill-current-sexp은 나만 쓰는 명령으로, 말 그대로 현재 커서가 있는 S-expression을 kill(잘라내기)하는 명령이다.

문제는 몇몇 코너 케이스인데, 커서가 붉은색에 위치할 때, kill-current-sexp의 결과를 나타내면 다음과 같다.

(s (expre (ssion)) sexp) => (s (expre (ssion)) sexp) => (s ((ssion)) sexp)
(s (expre (ssion)) sexp) => (s (expre (ssion)) sexp) => (s sexp)
(s (expre (ssion)) sexp) => (s (expre (ssion)) sexp) => (s sexp)
(s (expre (ssion)) sexp) => (s (expre (ssion) sexp)) => 

그런데 vim과 일반적인 에디터의 커서 표시 차이 때문에, Paredit에는 kill-current-sexp과 같은 명령이 존재하지 않는다.

왜 Paredit에는 없나?

Emacs나 VScode에서 Vim바인딩의 커서는 실제 커서의 오른쪽에 있는 문자에 음영을 입혀서 구현한다. 그러니까 위 예시들에서 실제 에디터의 커서 위치는 다음과 같다.

(s (expre (ssion)) sexp) = (s (|expre (ssion)) sexp)
(s (expre (ssion)) sexp) = (s |(expre (ssion)) sexp)
(s (expre (ssion)) sexp) = (s (expre (ssion)|) sexp)
(s (expre (ssion)) sexp) = (s (expre (ssion) sexp)|)

Paredit은 vim처럼 커서가 문자를 가리키는 게 아니라 "문자의 사이"를 가리키는 일반적인 에디터를 가정하고 만들어졌다. 그래서 "현재의 sexp"은 보통 커서의 뒤에 등장하는 sexp을 의미하며, "현재의 sexp을 삭제한다"고 가정하면 다음과 같은 결과가 나온다. (3,4번째 결과에 주목)

(s (|expre (ssion)) sexp) => (s (|expre (ssion)) sexp) => (s ((ssion)) sexp)
(s |(expre (ssion)) sexp) => (s |(expre (ssion)) sexp) => (s sexp)
(s (expre (ssion)|) sexp) => (s (expre (ssion)|) sexp) => (s (expre (ssion)) sexp)
(s (expre (ssion)) sexp|) => (s (expre (ssion) sexp)|) => (s (expre (ssion)) sexp)

꼬우면... 아시죠?

하지만 나 같은 vim틀딱에게 kill-current-sexp은 매우 편리한 기능이다. 써보면 안다... 그래서 내가 직접 만들었다.

resource/꼬우면아시죠.jpg
구현 자체는 코너 케이스만 잘 처리하면 된다.

  1. 커서를 한 칸 앞으로 옮기고
  2. 커서를 바로 이전 sexp으로 가거나 상위 sexp으로 올라가게 한 후
  3. 현재 커서에서 앞쪽을 kill하면 된다.

1,2를 거치면 커서는 항상 sexp의 첫번째 문자에 위치하게 된다.

Emacs의 설정 elisp 파일

(defun kill-current-sexp () (interactive)
  (forward-char) (paredit-backward) (kill-sexp))

(evil-define-key 'normal clojure-mode-map 
                 (kbd "X") 'kill-current-sexp)

[VSCode의 settings.json]

// Vim Keybindings
"vim.normalModeKeyBindings": [
  // X: Kill current sexp
  { "before": ["X"], 
    "commands": [
      {"command":"cursorMove", "args": {"to": "right"}}, // [a,b]
      "paredit.backwardSexpOrUp", 
      "paredit.killSexpForward"]},
],
// Use killing as cutting like emacs
"vim.useSystemClipboard": true, // [c]
"calva.paredit.killAlsoCutsToClipboard": true,

단축키는 간단히 vim 노말 모드에서 대문자 X를 쓰도록 맵핑했다. commands에 명령을 하나씩 넣고 저장한 후 X를 눌러 작동을 확인하는 걸 반복하는 방식으로 코딩했다.

맺음말

이게 얼마나 편한지는 써 본 사람만 안다 진짜 ㄹㅇ루다가
다만 Vim 바인딩과 Lisp과 Paredit을 모두 쓰는 변태한테만 의미 있다는 게.. 좀 심하게 매니악하긴 하다.

아무튼 난 잘 쓰고 있다. 님들도 쓰쉴?

2022.06.17 업데이트

resource/Pasted image 20220617113443.png
위 기능을 쓰다 보면 가끔씩 이런 창이 뜨는데 기능에는 아무 문제가 없다. 왜 뜨는 거지? 어떻게 하면 없앨 수 있지 이거?


또 다른 심하게 매니악한 설정: 한자키 -> ESC

참조한 문서
[a] https://code.visualstudio.com/api/references/commands#:~:text=an issue on-,cursorMove,-- Move cursor to
[b] https://github.com/VSCodeVim/Vim#key-remapping
[c] https://github.com/VSCodeVim/Vim#quick-example
[c] https://github.com/VSCodeVim/Vim

#dev-env개발환경#lang/lisp
kur2206020921Archivekur2302031338