3 minute read

Emacs에서 Evil 모드를 사용하고 있다. Vim 키바인딩을 Emacs에서 사용하기 위해서다. Emacs와 Vim을 같이 쓰는 대화합의 장이다.

현재 편집 영역의 절반을 아래로 스크롤 하는 C-d 키 동작이 이상하다. C-f 키가 한 페이지를 아래로 스크롤 한다면 C-d 키는 절반을 아래로 스크롤해야 한다. 하지만 어떨 때는 C-f 키를 누른 것처럼 한 페이지 정도를 아래로 스크롤 한다.

왜 이런 걸까?

C-d 키에 바인딩한 evil-scroll-down 함수 주요 코드

왜 화면 절반보다 더 많은 라인을 스크롤 하는지 함수를 살펴보자. C-d 키를 누르면 evil-scroll-down 함수를 실행한다.

(evil-define-command evil-scroll-down (count)
  (interactive "<c>")
  ;; ...
  (setq count (evil--get-scroll-count count))
  ;; ...
  )

evil--get-scroll-count 함수가 얼마만큼 스크롤 할 것인지를 계산하는 함수다.

(defun evil--get-scroll-count (count)
  "Given a user-supplied COUNT, return scroll count."
  (cl-flet ((posint (x) (and (natnump x) (< 0 x) x)))
    (or (posint count)
        (posint evil-scroll-count)
        (/ (window-body-height) 2))))

window-body-height 함수 리턴 값을 2로 나눈다. 즉 window-body-height 함수 리턴 값이 현재 화면에 보이는 총 라인 수다. 그걸 반으로 나눠서 화면 절반만 스크롤할 값을 구한다.

그렇다면 이상하다. Emacs 기본 라이브러리 함수인 window-body-height 가 이상한 값을 리턴할리는 없잖아?

window-body-height 값이 왜 이런가?

M-: 키에 바인딩된 eval-expression 함수를 호출한 후 (window-body-height) 를 입력해서 지금 포커스가 있는 위도의 body height를 구해봤다.

찾았다. window-body-height 함수 리턴 값이 더 크게 나오고 있다. 49줄을 표시하고 있는 윈도에서 실행하니 63이 나온다. 이걸 반으로 나누고 있으니 기대와 다르게 절반 이상이 스크롤된 것이다. 한편으로는 이 함수를 엄청나게 많이 쓰고 있을 텐데, 어떻게 고칠지 막막하다.

bug#54894: 28.1; window-body-height returns wrong result if line-spacing’ 글에서 원인을 찾았다. line-spacing 값이 nil이 아닐 때, window-body-height 함수 리턴 값이 텍스트 줄 수와 다르다는 버그 리포트다.

줄 간격이 있으면 읽기 편해서 아래처럼 줄 간격을 세팅해서 쓰고 있다.

(set-default 'line-spacing 0.2)

해결 방법이 있을 것 같아서 버그 리포트 글을 읽어보았다.

It does handle that correctly. Like with most (if not all) functions that return window and frame dimensions, the return value is in unit of a canonical line height for the frame. It doesn’t count the actual text lines. So it isn’t affected by stuff like line-spacing, the actual height of the lines due to changes in fonts, etc.

bug#54894: 28.1; window-body-height returns wrong result if line-spacing - li…

리턴 값이 실제 텍스트 라인 수가 아니라 프레임의 표준 줄 높이다(unit of a canonical line height for the frame). 읽어도 이해가 안 되는 답변이다. 프레임의 표준 줄 높이는 또 뭐람. 잘 모르겠지만 확실한 건 window-body-height 함수 구현이 절대 바뀔 리가 없다는 것이다.

line-spacing 값이 nil이 아닐 때, 텍스트 라인 수를 구하는 방법을 찾아야 한다. 혹은 line-spacing 값을 nil로 설정하고 눈이 적응하는 방법도 있다.

evil--get-scroll-count 함수 override로 해결?

line-spacing 값이 nil이 아닐 때, 텍스트 라인 수를 구하는 방법은 없는 걸까? 픽셀로 연산하는 방법이 있다.

(/ (window-body-height nil t) (default-line-height))

window-body-height 함수 두 번째 인자로 t 넘겨서 pixel 단위로 현재 포커스가 있는 윈도 크기를 구한다. 거기에 default-line-heightline-spacing 값이 반영된 줄 높이를 나눠서 구한다.

이제 적용할 차례다. Advice를 사용해 evil--get-scroll-count 함수를 덮어쓰자.

(after! evil
  (defun my-evil-get-scroll-count (count)
    "Given a user-supplied COUNT, return scroll count."
    (let ((line-count (/ (window-body-height nil t)
                         (default-line-height))))
      (cl-flet ((posint (x) (and (natnump x) (< 0 x) x)))
        (or (posint count)
            (posint evil-scroll-count)
            (/ line-count 2)))))
  (advice-add 'evil--get-scroll-count :override #'my-evil-get-scroll-count))

line-spacing 널 지키려고 내가 이렇게까지 한다.

하지만

결론 - line-spacingnil 로 설정하다

line-spacing 을 고려해 텍스트 줄 수를 구하게 evil--get-scroll-count 함수를 덮어써서 한동안 신나 하며 잘 썼다. 스크롤 관련 동작들이 잘 동작하다가 한 번씩 튀는 현상을 경험했다. 예전처럼 그런 현상들이 나오는 것이다. line-spacingnil 일 때 잘 동작하는 어떤 함수를 어디에선가 사용하고 있다!

발견할 때마다 고쳐가며 line-spacing 값을 지켜야 할까? 아니면 line-spacingnil 값으로 설정하고 이 문제에서 자유로워질 것인가?

line-spacingnil 로 설정했다. 처음엔 눈에 안 들어오지만 금새 눈이 적응한다. 눈을 적응시키고 line-spacing 문제에서 자유로워지기로 했다. 지켜주지 못해 미안해.

C-x C-s C-x C-c