<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.8.7">Jekyll</generator><link href="https://ohyecloudy.com/emacsian/feed.xml" rel="self" type="application/atom+xml" /><link href="https://ohyecloudy.com/emacsian/" rel="alternate" type="text/html" /><updated>2026-03-14T13:25:04+09:00</updated><id>https://ohyecloudy.com/emacsian/feed.xml</id><title type="html">(emacsian ohyecloudy)</title><subtitle>emacs를 배우면서 남기는 조각들</subtitle><author><name>Jongbin Oh</name><email>ohyecloudy@gmail.com</email></author><entry><title type="html">Jekyll 블로그 포스트를 Org-mode로 쓰기 - org-ruby, ox-jekyll-lite</title><link href="https://ohyecloudy.com/emacsian/2026/03/14/writing-jekyll-blog-posts-in-org-mode/" rel="alternate" type="text/html" title="Jekyll 블로그 포스트를 Org-mode로 쓰기 - org-ruby, ox-jekyll-lite" /><published>2026-03-14T00:00:00+09:00</published><updated>2026-03-14T00:00:00+09:00</updated><id>https://ohyecloudy.com/emacsian/2026/03/14/writing-jekyll-blog-posts-in-org-mode</id><content type="html" xml:base="https://ohyecloudy.com/emacsian/2026/03/14/writing-jekyll-blog-posts-in-org-mode/"><![CDATA[<p>Naver, Egloos, Tistory, Textcube, Wordpress 블로깅 서비스를 거쳐서 지금은 <a href="https://jekyllrb-ko.github.io/">Jekyll</a>을 사용하고 있다. Jekyll과 같은 정적 사이트 생성기는 내가 원하는 에디터로 글을 써서 관리 비용이 적은 정적 블로그를 생성할 수 있는 매력적인 툴이다.</p>

<p>Jekyll은 <a href="https://ko.wikipedia.org/wiki/%EB%A7%88%ED%81%AC%EB%8B%A4%EC%9A%B4">Markdown</a>으로 글을 써서 HTML을 생성하는 콘텐트 가공하는 프로세스를 가지고 있다. 이게 마음에 안 든다. 내 First-class 마크업 언어는 <a href="https://orgmode.org/">Org-mode</a> 이기 때문이다. Org-mode로 블로그 포스트를 쓸 방법을 연구했다.</p>

<h1 id="org-ruby-라이브러리로-org-파일을-html로-컨버팅---폐기">org-ruby 라이브러리로 org 파일을 html로 컨버팅 - 폐기</h1>

<p><a href="https://jekyllrb.com/docs/plugins/converters/">Jekyll Converters</a>를 사용하면 된다. <code class="highlighter-rouge">org</code> 확장자를 가진 파일일 때, <code class="highlighter-rouge">html</code> 로 변환하는 Converter를 추가하면 Org-mode로 글을 쓸 수 있다.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">module</span> <span class="nn">Jekyll</span>
  <span class="c1"># Convert org-mode files.</span>
  <span class="nb">require</span> <span class="s1">'org-ruby'</span>
  <span class="k">class</span> <span class="nc">OrgConverter</span> <span class="o">&lt;</span> <span class="no">Converter</span>
    <span class="n">safe</span> <span class="kp">true</span>

    <span class="k">def</span> <span class="nf">setup</span>
      <span class="c1"># No-op</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">matches</span><span class="p">(</span><span class="n">ext</span><span class="p">)</span>
      <span class="n">ext</span> <span class="o">=~</span> <span class="sr">/org$/i</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">output_ext</span><span class="p">(</span><span class="n">ext</span><span class="p">)</span>
      <span class="s2">".html"</span>
    <span class="k">end</span>

    <span class="k">def</span> <span class="nf">convert</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
      <span class="n">setup</span>
      <span class="no">Orgmode</span><span class="o">::</span><span class="no">Parser</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">content</span><span class="p">).</span><span class="nf">to_html</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>위와 같은 컨버터를 만들어서 <code class="highlighter-rouge">_plugins</code> 디렉터리에 만들면 사이트 생성 파이프라인에서 실행한다. <a href="https://github.com/wallyqs/org-ruby">wallyqs/org-ruby - github.com</a> 라이브러리를 사용했다.</p>

<p>별도 컨버터를 만들어서 html로 변환하니 테마가 지원하는 기능을 활용하지 못한다. <a href="https://github.com/mmistakes/minimal-mistakes">minimal mistakes 테마</a>에서 지원하는 TOC 기능을 사용하지 못한다. org-ruby는 소스 블럭에 <code class="highlighter-rouge">pre</code> 태그를 사용하는데 테마에서는 <code class="highlighter-rouge">code</code> 태그를 사용해야 예쁘게 보여준다. 하나하나 고쳐나가기보다는 미래를 위해 <code class="highlighter-rouge">org-ruby</code> 라이브러리를 사용한 컨버터를 사용하지 않기로 했다.</p>

<h1 id="org-문서를-md로-export---ox-jekyll-lite">org 문서를 md로 export - ox-jekyll-lite</h1>

<p>방법을 바꿔서 org 문서를 md로 export 하기로 했다. md로 원하는 대로만 export하면 여기서부터는 Jekyll 사이트 생성 파이프라인에 수정 없이 태울 수 있다.</p>

<p>다행히 같은 고민을 한 사람이 있다. <a href="https://github.com/peterewills/ox-jekyll-lite">peterewills/ox-jekyll-lite - github.com</a> 라이브러리를 찾았다.</p>

<pre><code class="language-org">#+begin_src elisp :tangle packages.el
  (package! example
    :recipe (:host github :repo "peterewills/ox-jekyll-lite"))
#+end_src

#+begin_src elisp
  (require 'ox-jekyll-lite)
#+end_src
</code></pre>

<p><a href="https://ohyecloudy.com/emacsian/2022/09/03/welcome-doom-emacs/">Doom Emacs</a> 설정에 패키지 사용을 추가했다.</p>

<p>org-mode 문서의 Front matter(전문) 역할은 property가 한다. 하지만 나는 그동안 써 온 글이 너무나 많아서 컨버팅하지 않고 yaml 전문을 그대로 사용하게 세팅했다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">org-jekyll-lite-include-yaml-front-matter</span> <span class="no">nil</span><span class="p">)</span>
</code></pre></div></div>

<p>포스팅 글에 아래와 같은 전문을 앞에 추가해서 해결했다.</p>

<pre><code class="language-org">#+begin_export html
---
title: "$title"
date: $now
categories: post
tags:
-
header:
  image: /assets/$now-$title-00.jpg
---
#+end_export
</code></pre>

<p>이제 준비가 끝났으니 org 파일에서 Jekyll md로 익스포트하면 된다.</p>

<p><code class="highlighter-rouge">M-x org-export-dispatch j j</code></p>

<p><code class="highlighter-rouge">org</code> 파일이 사이트 생성 결과물에 포함되지 않게 Jekyll <code class="highlighter-rouge">_config.yml</code> 파일 <code class="highlighter-rouge">exclude</code> 에 포함시킨다.</p>

<h1 id="기존에-작성한-org-파일을-bulk로-변환">기존에 작성한 org 파일을 bulk로 변환</h1>

<p>하나하나씩 언제 하냐. 디렉터리에 모든 <code class="highlighter-rouge">org</code> 파일을 찾아서 export하는 함수를 만들어서 실행했다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defvar</span> <span class="nv">my-org-bulk-export-jekyll--log-buffer-name</span> <span class="s">"*My Org Bulk Jekyll Export Log*"</span><span class="p">)</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">my-org-bulk-export-jekyll</span> <span class="p">()</span>
  <span class="p">(</span><span class="nv">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="k">let*</span> <span class="p">((</span><span class="nv">posts-dir</span> <span class="p">(</span><span class="nv">my-org-bulk-export-jekyll-locate-posts-dir</span><span class="p">))</span>
         <span class="p">(</span><span class="nv">org-files</span> <span class="p">(</span><span class="nv">directory-files-recursively</span> <span class="nv">posts-dir</span> <span class="s">"\\.org$"</span><span class="p">))</span>
         <span class="p">(</span><span class="nv">total</span> <span class="p">(</span><span class="nb">length</span> <span class="nv">org-files</span><span class="p">))</span>
         <span class="p">(</span><span class="nv">counter</span> <span class="mi">0</span><span class="p">))</span>

    <span class="p">(</span><span class="nv">my-org-bulk-export-jekyll--log</span> <span class="s">"Found %d .org files to export."</span> <span class="nv">total</span><span class="p">)</span>

    <span class="p">(</span><span class="nb">dolist</span> <span class="p">(</span><span class="nv">file</span> <span class="nv">org-files</span><span class="p">)</span>
      <span class="p">(</span><span class="k">setq</span> <span class="nv">counter</span> <span class="p">(</span><span class="nb">1+</span> <span class="nv">counter</span><span class="p">))</span>
      <span class="p">(</span><span class="nv">my-org-bulk-export-jekyll--log</span> <span class="s">"Exporting %d/%d: %s"</span> <span class="nv">counter</span> <span class="nv">total</span> <span class="nv">file</span><span class="p">)</span>
      <span class="p">(</span><span class="nv">with-current-buffer</span> <span class="p">(</span><span class="nv">find-file-noselect</span> <span class="nv">file</span><span class="p">)</span>
        <span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">org-export-with-broken-links</span> <span class="ss">'mark</span><span class="p">))</span>
          <span class="p">(</span><span class="nv">org-export-to-file</span>
              <span class="ss">'jekyll</span>
              <span class="p">(</span><span class="nv">concat</span> <span class="p">(</span><span class="nv">file-name-sans-extension</span> <span class="nv">file</span><span class="p">)</span> <span class="s">".md"</span><span class="p">)))))))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">my-org-bulk-export-jekyll-locate-posts-dir</span> <span class="p">()</span>
  <span class="s">"Locate the _posts directory at the root of the current Git repository, if any."</span>
  <span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">root</span> <span class="p">(</span><span class="nv">vc-git-root</span> <span class="nv">default-directory</span><span class="p">)))</span>
    <span class="p">(</span><span class="nb">when</span> <span class="nv">root</span> <span class="p">(</span><span class="nv">concat</span> <span class="nv">root</span> <span class="s">"_posts/"</span><span class="p">))))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">my-org-bulk-export-jekyll--log</span> <span class="p">(</span><span class="nv">format-string</span> <span class="k">&amp;rest</span> <span class="nv">args</span><span class="p">)</span>
  <span class="s">"Log a message to the dedicated export log buffer."</span>
  <span class="p">(</span><span class="nv">with-current-buffer</span> <span class="p">(</span><span class="nv">get-buffer-create</span> <span class="nv">my-org-bulk-export-jekyll--log-buffer-name</span><span class="p">)</span>
    <span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">inhibit-read-only</span> <span class="no">t</span><span class="p">))</span> <span class="c1">;; allow writing to read-only buffer</span>
      <span class="p">(</span><span class="nv">goto-char</span> <span class="p">(</span><span class="nv">point-max</span><span class="p">))</span>
      <span class="p">(</span><span class="nv">insert</span> <span class="p">(</span><span class="nb">apply</span> <span class="nf">#'</span><span class="nb">format</span> <span class="nv">format-string</span> <span class="nv">args</span><span class="p">)</span> <span class="s">"\n"</span><span class="p">))))</span>

<span class="p">(</span><span class="nb">provide</span> <span class="ss">'my-org-bulk-export-jekyll</span><span class="p">)</span>
</code></pre></div></div>

<h1 id="마무리">마무리</h1>

<p>컨버터를 내가 ownership을 가지고 수정할 마음이 없었다면 사용하면 안 된다. Jekyll 사이트 생성 파이프라인을 그대로 탈 수 있는 방법을 찾았어야 했다. 사제를 쓰니 마음에 드는 테마를 바꿀 때, 문제가 생겼다.</p>

<p><code class="highlighter-rouge">C-x C-s C-x C-c</code></p>

<h1 id="링크">링크</h1>

<ul>
  <li><a href="https://github.com/mmistakes/minimal-mistakes">mmistakes/minimal-mistakes - github.com</a></li>
  <li><a href="https://github.com/peterewills/ox-jekyll-lite">peterewills/ox-jekyll-lite - github.com</a></li>
  <li><a href="https://github.com/wallyqs/org-ruby">wallyqs/org-ruby - github.com</a></li>
  <li><a href="https://jekyllrb-ko.github.io/">Jekyll • 심플한, 블로그 지향적, 정적 사이트 - 평범한 텍스트 파일을 정적 웹사이트 또는 블로그로 변신시켜 보세요. - jekyl…</a>(<a href="http://web.archive.org/web/20260314040457/https://jekyllrb-ko.github.io/">archive</a>)</li>
  <li><a href="https://jekyllrb.com/docs/plugins/converters/">Converters - Jekyll • Simple, blog-aware, static sites - jekyllrb.com</a>(<a href="http://web.archive.org/web/20260314040459/https://jekyllrb.com/docs/plugins/converters/">archive</a>)</li>
  <li><a href="https://ko.wikipedia.org/wiki/%EB%A7%88%ED%81%AC%EB%8B%A4%EC%9A%B4">마크다운 - ko.wikipedia.org</a></li>
  <li><a href="https://ohyecloudy.com/emacsian/2022/09/03/welcome-doom-emacs/">Doom Emacs 전환 후기 - (emacsian ohyecloudy) - ohyecloudy.com</a>(<a href="http://web.archive.org/web/20260314040456/https://ohyecloudy.com/emacsian/2022/09/03/welcome-doom-emacs/">archive</a>)</li>
  <li><a href="https://orgmode.org/">Org mode for GNU Emacs - orgmode.org</a>(<a href="https://web.archive.org/web/20260314041947/https://orgmode.org/">archive</a>)</li>
</ul>

<!----- Footnotes ----->]]></content><author><name>Jongbin Oh</name><email>ohyecloudy@gmail.com</email></author><category term="uncategorized" /><category term="blogging" /><category term="jekyll" /><category term="org-mode" /><summary type="html"><![CDATA[Naver, Egloos, Tistory, Textcube, Wordpress 블로깅 서비스를 거쳐서 지금은 Jekyll을 사용하고 있다. Jekyll과 같은 정적 사이트 생성기는 내가 원하는 에디터로 글을 써서 관리 비용이 적은 정적 블로그를 생성할 수 있는 매력적인 툴이다.]]></summary></entry><entry><title type="html">Org-drill - Emacs Org-mode로 구성하는 Flashcard</title><link href="https://ohyecloudy.com/emacsian/2026/01/17/org-drill/" rel="alternate" type="text/html" title="Org-drill - Emacs Org-mode로 구성하는 Flashcard" /><published>2026-01-17T00:00:00+09:00</published><updated>2026-01-17T00:00:00+09:00</updated><id>https://ohyecloudy.com/emacsian/2026/01/17/org-drill</id><content type="html" xml:base="https://ohyecloudy.com/emacsian/2026/01/17/org-drill/"><![CDATA[<p><a href="https://ohyecloudy.com/pnotes/archives/book-the-programmer-brain/">프로그래머의 뇌 (펠리너 헤르만스, 2022)</a>를 보고 <a href="https://en.wikipedia.org/wiki/Long-term_memory">LTM(long-term memory)</a>에 정보를 넣는 것과 인출 강도를 높여 쉽게 떠올리는 능력을 강화하는 <a href="https://en.wikipedia.org/wiki/Spaced_repetition">Spaced repetition(간격 반복 학습)</a>에 관심이 생겼다.</p>

<p>당연히 다음 질문을 하게 된다. Emacs에서는 어떻게 할 수 있는가?</p>

<h1 id="org-drill-설치"><code class="highlighter-rouge">org-drill</code> 설치</h1>

<p>패키지 매니저에 등록되어 있다. <a href="https://ohyecloudy.com/emacsian/2016/11/20/package-use-package/">Use-package</a> 패키지를 사용하면 쉽게 설치할 수 있다. <a href="https://ohyecloudy.com/emacsian/2022/09/03/welcome-doom-emacs/">Doom Emacs</a>에서는 아래와 같이 설치할 수 있다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">package!</span> <span class="nv">org-drill</span><span class="p">)</span>
</code></pre></div></div>

<h1 id="간격-반복-학습-단위-drill을-정의하는-방법">간격 반복 학습 단위 drill을 정의하는 방법</h1>

<p>Org-mode의 tag를 사용한다. 구획 제목(heading)에 <code class="highlighter-rouge">:drill</code> 태그를 붙이면 된다.</p>

<p>대답을 한 번에 보여주는 타입</p>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>* Item                                   :drill:
What is the capital city of Estonia?

** The Answer
Tallinn.
</code></pre></div></div>

<p>빈칸 메우는 클로즈(cloze) 방식</p>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>,* Item                                   :drill:
The capital city of Estonia is [Tallinn].
</code></pre></div></div>

<p>이외에도 다양한 타입을 지원한다.</p>

<h1 id="drill-세션-시작">drill 세션 시작</h1>

<p><code class="highlighter-rouge">:drill</code> 태그가 있는 문서에서 아래 명령을 실행하면 시작한다</p>

<p><code class="highlighter-rouge">M-x org-drill</code></p>

<h1 id="메타-정보는-어디에-저장될까">메타 정보는 어디에 저장될까?</h1>

<p>메타 정보를 저장해야 한다. 언제 마지막으로 풀었는지 답은 맞췄는지와 같은 정보이다. 구획 제목에 붙는 속성(property)으로 저장한다.</p>

<pre><code class="language-org">:PROPERTIES:
:ID:       bd0713e6-6c6a-4085-8001-6089edbad15d
:DRILL_LAST_INTERVAL: 3.86
:DRILL_REPEATS_SINCE_FAIL: 2
:DRILL_TOTAL_REPEATS: 1
:DRILL_FAILURE_COUNT: 0
:DRILL_AVERAGE_QUALITY: 3.0
:DRILL_EASE: 2.36
:DRILL_LAST_QUALITY: 3
:DRILL_LAST_REVIEWED: [Y-08-29 Fri 09:%]
:END:
</code></pre>

<p>Org-mode 컨벤션을 따르면 메타 정보를 담을 적당한 곳이 거저 생긴다.</p>

<h1 id="마치며">마치며</h1>

<p>역시나 간격 반복 학습 패키지를 Emacs에 추가할 수 있다. 사용법도 쉽고 설정도 이만하면 충분하다.</p>

<p>Emacs에서만 돌아가는 게 문제다. 간격 반복 학습은 데스크탑보다는 모바일에서 사용하고 싶다. 짬이 날 때 보기 좋기 때문이다. 그래서 <code class="highlighter-rouge">org-drill</code> 사용은 보류한다. 모바일 지원이 안 되기 때문이다.</p>

<p><a href="https://apps.ankiweb.net/">Anki</a> 앱처럼 모바일을 잘 지원하는 앱을 메인으로 사용하고 Emacs는 데이터를 동기화하는 보조적인 수단으로 쓰면 어떨까 고민하고 있다.</p>

<p><code class="highlighter-rouge">C-x C-s C-x C-c</code></p>

<h1 id="링크">링크</h1>

<ul>
  <li><a href="https://apps.ankiweb.net/">Anki - powerful, intelligent flashcards - apps.ankiweb.net</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Long-term_memory">Long-term memory - en.wikipedia.org</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Spaced_repetition">Spaced repetition - en.wikipedia.org</a></li>
  <li><a href="https://ohyecloudy.com/emacsian/2016/11/20/package-use-package/">use-package 패키지로 패키지 관리를 더 간단하게 - (emacsian ohyecloudy) - ohyecloudy.com</a></li>
  <li><a href="https://ohyecloudy.com/emacsian/2022/09/03/welcome-doom-emacs/">Doom Emacs 전환 후기 - (emacsian ohyecloudy) - ohyecloudy.com</a></li>
  <li><a href="https://ohyecloudy.com/pnotes/archives/book-the-programmer-brain/">프로그래머의 뇌 (펠리너 헤르만스, 2022) 독후감 - ohyecloudy’s pnotes - ohyecloudy.com</a></li>
</ul>

<!----- Footnotes ----->]]></content><author><name>Jongbin Oh</name><email>ohyecloudy@gmail.com</email></author><category term="uncategorized" /><category term="org" /><category term="spaced-repetition" /><category term="flashcard" /><summary type="html"><![CDATA[프로그래머의 뇌 (펠리너 헤르만스, 2022)를 보고 LTM(long-term memory)에 정보를 넣는 것과 인출 강도를 높여 쉽게 떠올리는 능력을 강화하는 Spaced repetition(간격 반복 학습)에 관심이 생겼다.]]></summary></entry><entry><title type="html">]b, [b 키바인딩을 추가해 org block 단위로 점프</title><link href="https://ohyecloudy.com/emacsian/2025/08/30/keybindings-for-org-next-block/" rel="alternate" type="text/html" title="]b, [b 키바인딩을 추가해 org block 단위로 점프" /><published>2025-08-30T00:00:00+09:00</published><updated>2025-08-30T00:00:00+09:00</updated><id>https://ohyecloudy.com/emacsian/2025/08/30/keybindings-for-org-next-block</id><content type="html" xml:base="https://ohyecloudy.com/emacsian/2025/08/30/keybindings-for-org-next-block/"><![CDATA[<p><a href="https://ohyecloudy.com/emacsian/2022/09/03/welcome-doom-emacs/">Doom Emacs</a>는 org-mode 일 때, 소스코드 블록 이동을 <code class="highlighter-rouge">]c</code>, <code class="highlighter-rouge">[c</code> 키에 바인딩한다. <code class="highlighter-rouge">]c</code> 키를 누르면 문서에서 <code class="highlighter-rouge">#+begin_src</code> 문자열을 찾는다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">use-package!</span> <span class="nv">evil-org</span>
  <span class="p">(</span><span class="nv">let-alist</span> <span class="nv">evil-org-movement-bindings</span>
    <span class="p">(</span><span class="nv">map!</span> <span class="ss">:map</span> <span class="nv">evil-org-mode-map</span>
          <span class="ss">:m</span> <span class="s">"]c"</span>  <span class="nf">#'</span><span class="nv">org-babel-next-src-block</span>
          <span class="ss">:m</span> <span class="s">"[c"</span>  <span class="nf">#'</span><span class="nv">org-babel-previous-src-block</span><span class="p">)))</span>
</code></pre></div></div>

<p>소스코드 블록뿐만 아니라 <code class="highlighter-rouge">#+begin_example</code>, <code class="highlighter-rouge">#+begin_quote</code> 이런 다른 블록들도 이동할 수 있는 키바인딩이 있으면 편할 것 같다. <code class="highlighter-rouge">org-babel-next-src-block</code> 코드를 살펴보니 <code class="highlighter-rouge">org-next-block</code> 함수를 호출한다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">org-next-block</span> <span class="p">(</span><span class="nv">arg</span> <span class="k">&amp;optional</span> <span class="nv">backward</span> <span class="nv">block-regexp</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">interactive</span> <span class="s">"p"</span><span class="p">)</span>
  <span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">re</span> <span class="p">(</span><span class="nb">or</span> <span class="nv">block-regexp</span> <span class="s">"^[ \t]*#\\+BEGIN"</span><span class="p">))</span>
        <span class="p">(</span><span class="nv">case-fold-search</span> <span class="no">t</span><span class="p">)</span>
        <span class="p">(</span><span class="nv">search-fn</span> <span class="p">(</span><span class="k">if</span> <span class="nv">backward</span> <span class="nf">#'</span><span class="nv">re-search-backward</span> <span class="nf">#'</span><span class="nv">re-search-forward</span><span class="p">))</span>
</code></pre></div></div>

<p>기본값인 <code class="highlighter-rouge">#+begin</code> 을 그대로 사용하면 된다. <code class="highlighter-rouge">]b</code>, <code class="highlighter-rouge">[b</code> 키에 바인딩했다. 버퍼 이동 함수에 바인딩된 키인데 사용하지 않아서 뺏었다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">after!</span> <span class="nv">org</span>
  <span class="p">(</span><span class="nv">map!</span> <span class="ss">:map</span> <span class="nv">org-mode-map</span>
        <span class="ss">:n</span> <span class="s">"]b"</span> <span class="nf">#'</span><span class="nv">org-next-block</span>
        <span class="ss">:n</span> <span class="s">"[b"</span> <span class="nf">#'</span><span class="nv">org-previous-block</span><span class="p">))</span>
</code></pre></div></div>

<p>조금 더 이동이 민첩해졌다.</p>

<p><code class="highlighter-rouge">C-x C-s C-x C-c</code></p>

<!----- Footnotes ----->]]></content><author><name>Jongbin Oh</name><email>ohyecloudy@gmail.com</email></author><category term="uncategorized" /><category term="evil" /><category term="org" /><category term="doom-emacs" /><summary type="html"><![CDATA[Doom Emacs는 org-mode 일 때, 소스코드 블록 이동을 ]c, [c 키에 바인딩한다. ]c 키를 누르면 문서에서 #+begin_src 문자열을 찾는다.]]></summary></entry><entry><title type="html">f/t 모션으로 한글도 검색하는 evil-snipe 설정</title><link href="https://ohyecloudy.com/emacsian/2025/06/21/evil-snipe-hangul-f-t-motion/" rel="alternate" type="text/html" title="f/t 모션으로 한글도 검색하는 evil-snipe 설정" /><published>2025-06-21T00:00:00+09:00</published><updated>2025-06-21T00:00:00+09:00</updated><id>https://ohyecloudy.com/emacsian/2025/06/21/evil-snipe-hangul-f-t-motion</id><content type="html" xml:base="https://ohyecloudy.com/emacsian/2025/06/21/evil-snipe-hangul-f-t-motion/"><![CDATA[<p>Emacs evil-mode를 사용하면 vim에서 사용하는 <code class="highlighter-rouge">f/F</code>, <code class="highlighter-rouge">t/T</code> 키를 사용할 수 있다. 예를 들어 <code class="highlighter-rouge">fa</code> 키를 누르면 현재 라인에 있는 <code class="highlighter-rouge">a</code> 문자를 찾는다. <a href="https://ohyecloudy.com/emacsian/2025/04/27/evil-snipe/">Evil-snipe</a> 패키지를 사용하면 현재 라인뿐만 아니라 전체 문서로 검색 범위를 넓힐 수도 있고 연속된 두 글자를 검색할 수도 있다. 이걸 한글로도 넓혀보고 싶었다. <code class="highlighter-rouge">evil-snipe-aliases</code> 변수가 있어서 손쉽게 한글도 지원할 수 있었다.</p>

<h1 id="두벌식-초성만-지원"><a href="https://ko.wikipedia.org/wiki/%EB%91%90%EB%B2%8C%EC%8B%9D_%EC%9E%90%ED%8C%90">두벌식</a> 초성만 지원</h1>

<p>두벌식 초성만 지원한다. 모음과 종성은 뺐다. 구현상의 이유다. 정규식을 사용해서 검색할 건데, 초성처럼 범위로 나타낼 수 없기 때문이다. 좀 더 생각해 보니 이렇게 정밀도를 높이면 오히려 검색에 방해가 되겠다 싶다. 구현하기 어려워서 빨리 포기해서 다행이다.</p>

<p><code class="highlighter-rouge">fr</code> 키를 누르면 <code class="highlighter-rouge">r</code> 도 찾고 <code class="highlighter-rouge">ㄱ</code>, <code class="highlighter-rouge">가-낗</code> 까지 찾게 한다. 한글로 찾기 영문으로 찾기 이런 모드는 따로 안 나누고 같이 찾게 했다.</p>

<h1 id="evil-snipe-aliases-변수-설정"><code class="highlighter-rouge">evil-snipe-aliases</code> 변수 설정</h1>

<p><code class="highlighter-rouge">evil-snipe</code> 패키지에 key를 정규식으로 매핑할 수 있는 변수가 있다. 기대를 안 하고 <a href="https://ohyecloudy.com/emacsian/2023/02/18/advice-basic/">Advice</a>를 생각으로 소스 코드를 보다가 발견했다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">add-hook</span> <span class="ss">'c++-mode-hook</span>
          <span class="p">(</span><span class="k">lambda</span> <span class="p">()</span>
            <span class="p">(</span><span class="nv">make-local-variable</span> <span class="ss">'evil-snipe-aliases</span><span class="p">)</span>
            <span class="p">(</span><span class="nb">push</span> <span class="o">'</span><span class="p">(</span><span class="nv">?\[</span> <span class="nv">\"[[{</span><span class="p">(</span><span class="nv">]\"</span><span class="p">)</span> <span class="nv">evil-snipe-aliases</span><span class="p">)))</span><span class="err">"</span>
</code></pre></div></div>

<p>주석을 보니 C++ 모드에서 <code class="highlighter-rouge">f[</code> 키를 누르면 <code class="highlighter-rouge">[</code> 문자뿐만 아니라 <code class="highlighter-rouge">(</code>, <code class="highlighter-rouge">{</code> 문자도 찾도록 정의할 수 있다. 이렇게 써도 편하겠다. 이 변수를 쓰면 한글 검색을 할 수 있다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">evil-snipe-aliases</span> <span class="o">'</span><span class="p">(</span>
                           <span class="p">(</span><span class="nv">?r</span> <span class="s">"[rㄱ가-낗]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?R</span> <span class="s">"[Rㄲ까-낗]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?s</span> <span class="s">"[sㄴ나-닣]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?e</span> <span class="s">"[eㄷ다-딯]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?E</span> <span class="s">"[Eㄸ따-띻]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?f</span> <span class="s">"[fㄹ라-맇]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?a</span> <span class="s">"[aㅁ마-밓]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?q</span> <span class="s">"[qㅂ바-빟]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?Q</span> <span class="s">"[Qㅃ빠-삫]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?t</span> <span class="s">"[tㅅ사-싷]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?T</span> <span class="s">"[Tㅆ싸-앃]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?d</span> <span class="s">"[dㅇ아-잏]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?w</span> <span class="s">"[wㅈ자-짛]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?W</span> <span class="s">"[Wㅉ짜-찧]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?c</span> <span class="s">"[cㅊ차-칳]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?z</span> <span class="s">"[zㅋ카-킿]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?x</span> <span class="s">"[xㅌ타-팋]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?v</span> <span class="s">"[vㅍ파-핗]"</span><span class="p">)</span>
                           <span class="p">(</span><span class="nv">?g</span> <span class="s">"[gㅎ하-힣]"</span><span class="p">)</span>
                           <span class="p">))</span>
</code></pre></div></div>

<p>하나만 치니깐 나머지는 GitHub Copilot이 채워줬다. 친절하다</p>

<h1 id="테스트">테스트</h1>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The 비행기는 quick 이륙 brown 준비를 fox 마쳤지만 jumped 갑자기 over 폭우가 the 쏟아졌다 lazy dog.
^
</code></pre></div></div>

<p><code class="highlighter-rouge">fT</code> 키를 누르면 <code class="highlighter-rouge">[Tㅆ싸-앃]</code> 를 찾는다. <code class="highlighter-rouge">쏟</code> 으로 이동한다.</p>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The 비행기는 quick 이륙 brown 준비를 fox 마쳤지만 jumped 갑자기 over 폭우가 the 쏟아졌다 lazy dog.
                                                                              ^
</code></pre></div></div>

<p><code class="highlighter-rouge">Fc</code> 키를 누르면 <code class="highlighter-rouge">[cㅊ차-칳]</code> 를 찾는다. <code class="highlighter-rouge">쳤</code> 으로 이동한다.</p>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The 비행기는 quick 이륙 brown 준비를 fox 마쳤지만 jumped 갑자기 over 폭우가 the 쏟아졌다 lazy dog.
                                          ^
</code></pre></div></div>

<p><code class="highlighter-rouge">;</code> 키를 눌러 반복 찾기를 누르면 <code class="highlighter-rouge">[cㅊ차-칳]</code> 를 한 번 더 찾는다. <code class="highlighter-rouge">c</code> 로 이동한다.</p>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The 비행기는 quick 이륙 brown 준비를 fox 마쳤지만 jumped 갑자기 over 폭우가 the 쏟아졌다 lazy dog.
               ^
</code></pre></div></div>

<p>잘 된다.</p>

<h1 id="마치며">마치며</h1>

<p>정말 Emacs에서 evil-mode를 쓴다면 evil-snipe 패키지를 안 쓸 이유가 없다.</p>

<p>한글 검색을 계속 미루다가 이제야 했다. <code class="highlighter-rouge">f/t</code> 모션 활용도가 훨씬 더 높아졌고 이동이 가벼워졌다.</p>

<p><code class="highlighter-rouge">C-x C-s C-x C-c</code></p>

<!----- Footnotes ----->]]></content><author><name>Jongbin Oh</name><email>ohyecloudy@gmail.com</email></author><category term="uncategorized" /><category term="doom-emacs" /><category term="evil" /><category term="evil-snipe" /><summary type="html"><![CDATA[Emacs evil-mode를 사용하면 vim에서 사용하는 f/F, t/T 키를 사용할 수 있다. 예를 들어 fa 키를 누르면 현재 라인에 있는 a 문자를 찾는다. Evil-snipe 패키지를 사용하면 현재 라인뿐만 아니라 전체 문서로 검색 범위를 넓힐 수도 있고 연속된 두 글자를 검색할 수도 있다. 이걸 한글로도 넓혀보고 싶었다. evil-snipe-aliases 변수가 있어서 손쉽게 한글도 지원할 수 있었다.]]></summary></entry><entry><title type="html">f/t 모션을 확장하는 evil-snipe 패키지 - 두 문자 검색 지원 및 버퍼로 확장 찾기</title><link href="https://ohyecloudy.com/emacsian/2025/04/27/evil-snipe/" rel="alternate" type="text/html" title="f/t 모션을 확장하는 evil-snipe 패키지 - 두 문자 검색 지원 및 버퍼로 확장 찾기" /><published>2025-04-27T00:00:00+09:00</published><updated>2025-04-27T00:00:00+09:00</updated><id>https://ohyecloudy.com/emacsian/2025/04/27/evil-snipe</id><content type="html" xml:base="https://ohyecloudy.com/emacsian/2025/04/27/evil-snipe/"><![CDATA[<p><a href="https://github.com/hlissner/evil-snipe">hlissner/evil-snipe</a> 패키지를 Henrik Lissner가 만들었다. <a href="https://ohyecloudy.com/emacsian/2022/09/03/welcome-doom-emacs/">Doom Emacs</a>를 만든 사람이다. 그래서인지 Doom Emacs에 디폴트로 포함되어 있다. 익숙해지면 편리할 것 같아서 친해질 겸 정리해 본다.</p>

<p>제공하는 기능은 다음과 같다. 현재 줄 찾기 후 확장. <code class="highlighter-rouge">s</code>, <code class="highlighter-rouge">S</code> 키로 연속된 두 문자 포함 찾기. <code class="highlighter-rouge">x</code>, <code class="highlighter-rouge">X</code> 키로 연속된 두 문자 앞까지 operator 모드에서 사용.</p>

<h1 id="찾기-범위----">찾기 범위 - <code class="highlighter-rouge">;</code>, <code class="highlighter-rouge">,</code></h1>

<p><code class="highlighter-rouge">f/F</code>, <code class="highlighter-rouge">t/T</code> 키로 현재 라인에서 문자를 찾을 수 있다. 찾고 난 뒤에는 <code class="highlighter-rouge">;</code> 키로 앞으로 반복해서 찾고 <code class="highlighter-rouge">,</code> 키로 뒤로 반복해서 찾을 수 있다. 찾기와 반복 찾기 범위를 설정할 수 있다.</p>

<p>현재 줄에서 찾고 보이는 범위로 확장해서 반복 찾기를 하는 디폴트 값을 그대로 사용하고 있다. <code class="highlighter-rouge">evil-snipe-scope</code> 변수와 <code class="highlighter-rouge">evil-snipe-repeat-scope</code> 변수로 범위를 변경할 수 있다.</p>

<p>첫 번째 문장 제일 앞 문자에 커서가 있다.</p>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The quick Brown fox jumps over the lazy dog.
^
The five boxing wizards jump quickly.
</code></pre></div></div>

<p><code class="highlighter-rouge">fu</code> 키를 누른다.</p>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The quick Brown fox jumps over the lazy dog.
     ^
The five boxing wizards jump quickly.
</code></pre></div></div>

<p><code class="highlighter-rouge">;</code> 키를 누른다.</p>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The quick Brown fox jumps over the lazy dog.
                     ^
The five boxing wizards jump quickly.
</code></pre></div></div>

<p>한 번 더 <code class="highlighter-rouge">;</code> 키를 누른다. vim 기본 패키지 상태에서는 line 단위로만 찾기 때문에 더 이상 커서 이동을 하지 않는다. 하지만 evil-snipe 패키지를 사용하기 때문에 범위를 확장해서 찾는다.</p>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The quick Brown fox jumps over the lazy dog.

The five boxing wizards jump quickly.
                         ^
</code></pre></div></div>

<p><code class="highlighter-rouge">F</code> 는 반대로 찾는다. <code class="highlighter-rouge">Fi</code></p>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The quick Brown fox jumps over the lazy dog.

The five boxing wizards jump quickly.
                 ^
</code></pre></div></div>

<p><code class="highlighter-rouge">t</code> 는 입력한 문자 바로 이전으로 이동한다. <code class="highlighter-rouge">T</code> 는 뒤로 검색한다. 아래는 <code class="highlighter-rouge">tu</code> 를 입력한 결과다.</p>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The quick Brown fox jumps over the lazy dog.

The five boxing wizards jump quickly.
                        ^
</code></pre></div></div>

<p>찾는 글자 수와 관계없이 버퍼 대상으로 찾을 때, <code class="highlighter-rouge">/</code> 키를 눌렀다. 이제 범위를 확장할 수 있으니 <code class="highlighter-rouge">f/F</code>, <code class="highlighter-rouge">t/T</code> 키 활용도가 증가했다.</p>

<h1 id="두-문자-매칭-이동---ss">두 문자 매칭 이동 - <code class="highlighter-rouge">s/S</code></h1>

<p><code class="highlighter-rouge">s/S</code> 키를 누르면 키 입력을 추가로 두 개를 받는다. 문자 하나만 받는 <code class="highlighter-rouge">f/F</code> 키를 두 문자를 받게 한 기능이다. 아직까진 완전히 손에 익진 않았고 가끔 생각날 때마다 한 번씩 쓰기는 한다.</p>

<p>커서가 첫 번째 문장 제일 앞에 글자인 <code class="highlighter-rouge">T</code> 에 있을 때, <code class="highlighter-rouge">sum</code> 키를 누르면</p>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The quick Brown fox jumps over the lazy dog.
                     ^
The five boxing wizards jump quickly.
</code></pre></div></div>

<p><code class="highlighter-rouge">;</code> 반복 찾기 키를 누르면</p>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The quick Brown fox jumps over the lazy dog.

The five boxing wizards jump quickly.
                         ^
</code></pre></div></div>

<p><code class="highlighter-rouge">S</code> 는 반대로 이동한다.</p>

<h1 id="두-문자-이전까지---operator-mode에서-xx">두 문자 이전까지 - operator mode에서 <code class="highlighter-rouge">x/X</code></h1>

<p><code class="highlighter-rouge">d</code> 키를 누르면 이후에 지울 범위를 받는다. operator mode로 변환되는 것이다. <code class="highlighter-rouge">x/X</code> 키는 연속된 두 글자 이전까지를 범위로 잡는다.</p>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The quick Brown fox jumps over the lazy dog.
           ^
The five boxing wizards jump quickly.
</code></pre></div></div>

<p>위처럼 <code class="highlighter-rouge">r</code> 문자에 커서가 있을 때, <code class="highlighter-rouge">dxov</code> 키를 누르면</p>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The quick Bover the lazy dog.
           ^
The five boxing wizards jump quickly.
</code></pre></div></div>

<p>처음으로 찾은 <code class="highlighter-rouge">ov</code> 문자 앞까지 지운다. <code class="highlighter-rouge">X</code> 키는 <code class="highlighter-rouge">x</code> 키와 반대로 커서 이전을 찾는다.</p>

<h1 id="마치며">마치며</h1>

<p>찾기 범위 변수인 <code class="highlighter-rouge">evil-snipe-scope</code> 와 반복 찾기 변수인 <code class="highlighter-rouge">evil-snipe-repeat-scope</code> 를 분리한 건 좋은 디자인 결정이다. 현재 줄에서 찾는 키 기능을 그대로 유지하면서 반복했을 때만 범위를 넓히는 확장을 할 수 있게 해준다. <code class="highlighter-rouge">f/F</code>, <code class="highlighter-rouge">t/T</code> 키를 눌렀을 때, 갑자기 먼 곳으로 이동해서 놀라는 걸 막아준다.</p>

<p>그러고 보니 반복 찾기가 현재 줄을 넘어가서 찾는데, evil 기능이겠거니 하면서 쓰고 있었다. 확실히 편하다. <code class="highlighter-rouge">s/S</code>, <code class="highlighter-rouge">x/X</code> 키가 유용하지만 아직은 손에 익지 않았다. 존재 자체를 매번 까먹게 된다. 흔한 글자를 찾을 때, 두 글자로 찾을 수 있는 <code class="highlighter-rouge">s/S</code> 키를 요긴하게 쓸 수 있다.</p>

<p><code class="highlighter-rouge">C-x C-s C-x C-c</code></p>

<!----- Footnotes ----->]]></content><author><name>Jongbin Oh</name><email>ohyecloudy@gmail.com</email></author><category term="uncategorized" /><category term="evil" /><category term="evil-snipe" /><category term="doom-emacs" /><summary type="html"><![CDATA[hlissner/evil-snipe 패키지를 Henrik Lissner가 만들었다. Doom Emacs를 만든 사람이다. 그래서인지 Doom Emacs에 디폴트로 포함되어 있다. 익숙해지면 편리할 것 같아서 친해질 겸 정리해 본다.]]></summary></entry><entry><title type="html">왜 C-d 키를 눌러 스크롤하면 절반 이상이 넘을까?</title><link href="https://ohyecloudy.com/emacsian/2025/03/30/evil-scroll-down-up/" rel="alternate" type="text/html" title="왜 C-d 키를 눌러 스크롤하면 절반 이상이 넘을까?" /><published>2025-03-30T00:00:00+09:00</published><updated>2025-03-30T00:00:00+09:00</updated><id>https://ohyecloudy.com/emacsian/2025/03/30/evil-scroll-down-up</id><content type="html" xml:base="https://ohyecloudy.com/emacsian/2025/03/30/evil-scroll-down-up/"><![CDATA[<p><a href="https://www.gnu.org/software/emacs/">Emacs</a>에서 <a href="https://github.com/emacs-evil/evil">Evil</a> 모드를 사용하고 있다. <a href="id:3e83ada6-edba-47c4-bd34-dd068f1b6789">Vim</a> 키바인딩을 Emacs에서 사용하기 위해서다. Emacs와 Vim을 같이 쓰는 대화합의 장이다.</p>

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

<p>왜 이런 걸까?</p>

<h1 id="c-d-키에-바인딩한-evil-scroll-down-함수-주요-코드"><code class="highlighter-rouge">C-d</code> 키에 바인딩한 <code class="highlighter-rouge">evil-scroll-down</code> 함수 주요 코드</h1>

<p>왜 화면 절반보다 더 많은 라인을 스크롤 하는지 함수를 살펴보자. <code class="highlighter-rouge">C-d</code> 키를 누르면 <code class="highlighter-rouge">evil-scroll-down</code> 함수를 실행한다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">evil-define-command</span> <span class="nv">evil-scroll-down</span> <span class="p">(</span><span class="nb">count</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">interactive</span> <span class="s">"&lt;c&gt;"</span><span class="p">)</span>
  <span class="c1">;; ...</span>
  <span class="p">(</span><span class="k">setq</span> <span class="nb">count</span> <span class="p">(</span><span class="nv">evil--get-scroll-count</span> <span class="nb">count</span><span class="p">))</span>
  <span class="c1">;; ...</span>
  <span class="p">)</span>
</code></pre></div></div>

<p><code class="highlighter-rouge">evil--get-scroll-count</code> 함수가 얼마만큼 스크롤 할 것인지를 계산하는 함수다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">evil--get-scroll-count</span> <span class="p">(</span><span class="nb">count</span><span class="p">)</span>
  <span class="s">"Given a user-supplied COUNT, return scroll count."</span>
  <span class="p">(</span><span class="nv">cl-flet</span> <span class="p">((</span><span class="nv">posint</span> <span class="p">(</span><span class="nv">x</span><span class="p">)</span> <span class="p">(</span><span class="nb">and</span> <span class="p">(</span><span class="nv">natnump</span> <span class="nv">x</span><span class="p">)</span> <span class="p">(</span><span class="nb">&lt;</span> <span class="mi">0</span> <span class="nv">x</span><span class="p">)</span> <span class="nv">x</span><span class="p">)))</span>
    <span class="p">(</span><span class="nb">or</span> <span class="p">(</span><span class="nv">posint</span> <span class="nb">count</span><span class="p">)</span>
        <span class="p">(</span><span class="nv">posint</span> <span class="nv">evil-scroll-count</span><span class="p">)</span>
        <span class="p">(</span><span class="nb">/</span> <span class="p">(</span><span class="nv">window-body-height</span><span class="p">)</span> <span class="mi">2</span><span class="p">))))</span>
</code></pre></div></div>

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

<p>그렇다면 이상하다. Emacs 기본 라이브러리 함수인 <code class="highlighter-rouge">window-body-height</code> 가 이상한 값을 리턴할리는 없잖아?</p>

<h1 id="window-body-height-값이-왜-이런가"><code class="highlighter-rouge">window-body-height</code> 값이 왜 이런가?</h1>

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

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

<p>’<a href="https://lists.libreplanet.org/archive/html/bug-gnu-emacs/2022-04/msg00598.html">bug#54894: 28.1; window-body-height returns wrong result if line-spacing</a>’ 글에서 원인을 찾았다. <code class="highlighter-rouge">line-spacing</code> 값이 nil이 아닐 때, <code class="highlighter-rouge">window-body-height</code> 함수 리턴 값이 텍스트 줄 수와 다르다는 버그 리포트다.</p>

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

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">set-default</span> <span class="ss">'line-spacing</span> <span class="mf">0.2</span><span class="p">)</span>
</code></pre></div></div>

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

<blockquote>
  <p>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.</p>

  <p><a href="https://lists.libreplanet.org/archive/html/bug-gnu-emacs/2022-04/msg00598.html">bug#54894: 28.1; window-body-height returns wrong result if line-spacing - li…</a></p>
</blockquote>

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

<p><code class="highlighter-rouge">line-spacing</code> 값이 nil이 아닐 때, 텍스트 라인 수를 구하는 방법을 찾아야 한다. 혹은 <code class="highlighter-rouge">line-spacing</code> 값을 nil로 설정하고 눈이 적응하는 방법도 있다.</p>

<h1 id="evil--get-scroll-count-함수-override로-해결"><code class="highlighter-rouge">evil--get-scroll-count</code> 함수 override로 해결?</h1>

<p><code class="highlighter-rouge">line-spacing</code> 값이 nil이 아닐 때, 텍스트 라인 수를 구하는 방법은 없는 걸까? 픽셀로 연산하는 방법이 있다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">/</span> <span class="p">(</span><span class="nv">window-body-height</span> <span class="no">nil</span> <span class="no">t</span><span class="p">)</span> <span class="p">(</span><span class="nv">default-line-height</span><span class="p">))</span>
</code></pre></div></div>

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

<p>이제 적용할 차례다. <a href="https://ohyecloudy.com/emacsian/2023/02/18/advice-basic/">Advice</a>를 사용해 <code class="highlighter-rouge">evil--get-scroll-count</code> 함수를 덮어쓰자.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">after!</span> <span class="nv">evil</span>
  <span class="p">(</span><span class="nb">defun</span> <span class="nv">my-evil-get-scroll-count</span> <span class="p">(</span><span class="nb">count</span><span class="p">)</span>
    <span class="s">"Given a user-supplied COUNT, return scroll count."</span>
    <span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">line-count</span> <span class="p">(</span><span class="nb">/</span> <span class="p">(</span><span class="nv">window-body-height</span> <span class="no">nil</span> <span class="no">t</span><span class="p">)</span>
                         <span class="p">(</span><span class="nv">default-line-height</span><span class="p">))))</span>
      <span class="p">(</span><span class="nv">cl-flet</span> <span class="p">((</span><span class="nv">posint</span> <span class="p">(</span><span class="nv">x</span><span class="p">)</span> <span class="p">(</span><span class="nb">and</span> <span class="p">(</span><span class="nv">natnump</span> <span class="nv">x</span><span class="p">)</span> <span class="p">(</span><span class="nb">&lt;</span> <span class="mi">0</span> <span class="nv">x</span><span class="p">)</span> <span class="nv">x</span><span class="p">)))</span>
        <span class="p">(</span><span class="nb">or</span> <span class="p">(</span><span class="nv">posint</span> <span class="nb">count</span><span class="p">)</span>
            <span class="p">(</span><span class="nv">posint</span> <span class="nv">evil-scroll-count</span><span class="p">)</span>
            <span class="p">(</span><span class="nb">/</span> <span class="nv">line-count</span> <span class="mi">2</span><span class="p">)))))</span>
  <span class="p">(</span><span class="nv">advice-add</span> <span class="ss">'evil--get-scroll-count</span> <span class="ss">:override</span> <span class="nf">#'</span><span class="nv">my-evil-get-scroll-count</span><span class="p">))</span>
</code></pre></div></div>

<p><code class="highlighter-rouge">line-spacing</code> 널 지키려고 내가 이렇게까지 한다.</p>

<p>하지만</p>

<h1 id="결론---line-spacing-을-nil-로-설정하다">결론 - <code class="highlighter-rouge">line-spacing</code> 을 <code class="highlighter-rouge">nil</code> 로 설정하다</h1>

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

<p>발견할 때마다 고쳐가며 <code class="highlighter-rouge">line-spacing</code> 값을 지켜야 할까? 아니면 <code class="highlighter-rouge">line-spacing</code> 을 <code class="highlighter-rouge">nil</code> 값으로 설정하고 이 문제에서 자유로워질 것인가?</p>

<p><code class="highlighter-rouge">line-spacing</code> 을 <code class="highlighter-rouge">nil</code> 로 설정했다. 처음엔 눈에 안 들어오지만 금새 눈이 적응한다. 눈을 적응시키고 <code class="highlighter-rouge">line-spacing</code> 문제에서 자유로워지기로 했다. 지켜주지 못해 미안해.</p>

<p><code class="highlighter-rouge">C-x C-s C-x C-c</code></p>

<!----- Footnotes ----->]]></content><author><name>Jongbin Oh</name><email>ohyecloudy@gmail.com</email></author><category term="uncategorized" /><category term="evil" /><category term="window-body-height" /><summary type="html"><![CDATA[Emacs에서 Evil 모드를 사용하고 있다. Vim 키바인딩을 Emacs에서 사용하기 위해서다. Emacs와 Vim을 같이 쓰는 대화합의 장이다.]]></summary></entry><entry><title type="html">팝업을 2등 시민으로 만드는 Doom Emacs popup 모듈</title><link href="https://ohyecloudy.com/emacsian/2025/03/01/doom-emacs-popup-module/" rel="alternate" type="text/html" title="팝업을 2등 시민으로 만드는 Doom Emacs popup 모듈" /><published>2025-03-01T00:00:00+09:00</published><updated>2025-03-01T00:00:00+09:00</updated><id>https://ohyecloudy.com/emacsian/2025/03/01/doom-emacs-popup-module</id><content type="html" xml:base="https://ohyecloudy.com/emacsian/2025/03/01/doom-emacs-popup-module/"><![CDATA[<blockquote>
  <p>Not all windows are created equally. Some are less important. Some I want gone once they have served their purpose, like code output or a help buffer. Others I want to stick around, like a scratch buffer or org-capture popup.</p>

  <p>More than that, popups ought to be the second class citizens of my editor; spawned off to the side, discarded with the push of a button (e.g. ESC or C-g), and easily restored if I want to see them again. Of course, this system should clean up after itself and kill off buffers I mark as transient.</p>

  <p>모든 창이 똑같이 생성되는 것은 아닙니다. 일부는 덜 중요합니다. 코드 출력이나 도움말 버퍼처럼 용도가 끝나면 사라졌으면 하는 창도 있습니다. 스크래치 버퍼나 org-capture 팝업처럼 계속 유지하고 싶은 팝업도 있습니다.</p>

  <p>그보다 팝업은 에디터에서 2등 시민이 되어야 하며, 버튼(예: ESC 또는 C-g)을 누르면 사라지고, 다시 보고 싶을 때 쉽게 복원할 수 있어야 합니다. 물론 이 시스템은 스스로 정리하고 사용자가 일시적인 것으로 표시한 버퍼를 없애야 합니다.</p>

  <p><a href="https://docs.doomemacs.org/v21.12/modules/ui/popup/">:ui popup - Doom Emacs v21.12 documentation - docs.doomemacs.org</a></p>
</blockquote>

<p>어디서 본 적이 있는 것 같은데, <a href="https://ohyecloudy.com/emacsian/2022/09/03/welcome-doom-emacs/">doom emacs</a>에서는 직접 구현한 모듈(module)이 있다.</p>

<h1 id="설정-방법">설정 방법</h1>

<blockquote>
  <ul>
    <li><code class="highlighter-rouge">+all</code>
      <ul>
        <li>Enable fallback rules to ensure all temporary/special buffers (whose name begins with a space or asterix) are treated as popups.</li>
      </ul>
    </li>
    <li><code class="highlighter-rouge">+defaults</code>
      <ul>
        <li>Enable reasonable default popup rules for a variety of buffers.</li>
      </ul>
    </li>
  </ul>
</blockquote>

<p>빈칸이나 <code class="highlighter-rouge">*</code> 문자로 시작하는 건 모두 임시 버퍼 취급하는 <code class="highlighter-rouge">+all</code> 옵션과 적당히 타협하는 <code class="highlighter-rouge">+default</code> 옵션이 있다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">doom!</span> <span class="ss">:ui</span>
       <span class="p">(</span><span class="nv">popup</span> <span class="nv">+defaults</span><span class="p">)</span>   <span class="c1">; tame sudden yet inevitable temporary windows</span>
       <span class="p">)</span>
</code></pre></div></div>

<p><code class="highlighter-rouge">init.el</code> 파일에 설정한다. 적당한 옵션으로 세팅해서 사용하고 있다.</p>

<h1 id="사용-방법">사용 방법</h1>

<p>주로 사용하는 키는 다음과 같다. 팝업창을 일반 창으로 승격시키는 기능도 있다. 자신이 쓰기 위해 만든 모듈은 이렇게 기능이 디테일하다.</p>

<ul>
  <li><code class="highlighter-rouge">C-g</code> - 닫기</li>
  <li><code class="highlighter-rouge">C-x p</code> - <code class="highlighter-rouge">+popup/other</code> - 열린 팝업창으로 이동</li>
  <li><code class="highlighter-rouge">SPC ~</code> - <code class="highlighter-rouge">+popup/toggle</code> - 팝업창을 여닫는다</li>
  <li><code class="highlighter-rouge">C-~</code> - <code class="highlighter-rouge">+popup/raise</code> - 팝업창을 일반 창으로 승격</li>
</ul>

<h1 id="현재-사용-예">현재 사용 예</h1>

<p>popup 모듈 확장성이 좋다. 패키지에 팝업으로 띄웠으면 좋겠다고 하는 window에 적용해서 쓰고 있다.</p>

<p><a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Programming-Language-Doc.html#FOOT15">ElDoc</a> 패키지에서 사용하는 window에 팝업을 적용했다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">after!</span> <span class="nv">eldoc</span>
  <span class="c1">;; =M-x eldoc-doc-buffer= 함수 호출로 표시하는 buffer 크기 조절</span>
  <span class="p">(</span><span class="nv">set-popup-rule!</span> <span class="s">"^\\*eldoc for"</span> <span class="ss">:size</span> <span class="mf">0.2</span> <span class="ss">:vslot</span> <span class="mi">-1</span><span class="p">))</span>
</code></pre></div></div>

<p><a href="https://ohyecloudy.com/emacsian/2024/05/11/web-archive/">Web Archive</a> 링크를 만드는 패키지에도 결과를 보여줄 때, 팝업으로 보여준다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">when</span> <span class="p">(</span><span class="nb">functionp</span> <span class="nf">#'</span><span class="nv">set-popup-rule!</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">set-popup-rule!</span> <span class="nv">my/web-archive-result-buffer-name</span> <span class="ss">:size</span> <span class="mf">0.25</span> <span class="ss">:ttl</span> <span class="no">nil</span> <span class="ss">:vslot</span> <span class="mi">-1</span><span class="p">))</span>
</code></pre></div></div>

<p><code class="highlighter-rouge">size</code>, <code class="highlighter-rouge">ttl</code>, <code class="highlighter-rouge">vslot</code> 등 옵션도 많다.</p>

<h1 id="마치며">마치며</h1>

<p>맞다. 모든 윈도우가 같은 취급을 받으면 안 된다. 어떤 윈도우는 계속 한자리를 차지하고 있어야 하지만 어떤 윈도우는 잠시 보였다가 손쉽게 사라져야 한다. 만약 Doom Emacs를 떠나 다른 곳으로 간다면 그리울 패키지다. 비슷한 기능을 가진 걸 찾거나 Doom Emacs를 벗어나더라도 사용할 수 있게 분리하는 작업을 시작할 것 같다.</p>

<p><code class="highlighter-rouge">C-x C-s C-x C-c</code></p>

<h1 id="링크">링크</h1>

<ul>
  <li><a href="https://docs.doomemacs.org/v21.12/modules/ui/popup/">:ui popup - Doom Emacs v21.12 documentation - docs.doomemacs.org</a>(<a href="http://web.archive.org/web/20250301092121/https://docs.doomemacs.org/v21.12/modules/ui/popup/">archive</a>)</li>
  <li><a href="https://ohyecloudy.com/emacsian/2024/05/11/web-archive/">URL로부터 Title을 가져오고 Web Archive 링크를 생성하는 Emacs 함수 - (emacsian ohyecloudy) - oh…</a>(<a href="http://web.archive.org/web/20250301092122/https://ohyecloudy.com/emacsian/2024/05/11/web-archive/">archive</a>)</li>
  <li><a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Programming-Language-Doc.html#FOOT15">Programming Language Doc (GNU Emacs Manual) - gnu.org</a>(<a href="http://web.archive.org/web/20250301092131/https://www.gnu.org/software/emacs/manual/html_node/emacs/Programming-Language-Doc.html">archive</a>)</li>
  <li><a href="https://ohyecloudy.com/emacsian/2022/09/03/welcome-doom-emacs/">Doom Emacs 전환 후기 - (emacsian ohyecloudy) - ohyecloudy.com</a>(<a href="http://web.archive.org/web/20250301092123/https://ohyecloudy.com/emacsian/2022/09/03/welcome-doom-emacs/">archive</a>)</li>
</ul>

<!----- Footnotes ----->]]></content><author><name>Jongbin Oh</name><email>ohyecloudy@gmail.com</email></author><category term="uncategorized" /><category term="doom-emacs" /><category term="window" /><category term="popup" /><summary type="html"><![CDATA[Not all windows are created equally. Some are less important. Some I want gone once they have served their purpose, like code output or a help buffer. Others I want to stick around, like a scratch buffer or org-capture popup. More than that, popups ought to be the second class citizens of my editor; spawned off to the side, discarded with the push of a button (e.g. ESC or C-g), and easily restored if I want to see them again. Of course, this system should clean up after itself and kill off buffers I mark as transient. 모든 창이 똑같이 생성되는 것은 아닙니다. 일부는 덜 중요합니다. 코드 출력이나 도움말 버퍼처럼 용도가 끝나면 사라졌으면 하는 창도 있습니다. 스크래치 버퍼나 org-capture 팝업처럼 계속 유지하고 싶은 팝업도 있습니다. 그보다 팝업은 에디터에서 2등 시민이 되어야 하며, 버튼(예: ESC 또는 C-g)을 누르면 사라지고, 다시 보고 싶을 때 쉽게 복원할 수 있어야 합니다. 물론 이 시스템은 스스로 정리하고 사용자가 일시적인 것으로 표시한 버퍼를 없애야 합니다. :ui popup - Doom Emacs v21.12 documentation - docs.doomemacs.org]]></summary></entry><entry><title type="html">GitLab merge request 정보를 Emacs Org-mode 문서에 삽입</title><link href="https://ohyecloudy.com/emacsian/2025/01/18/gitlab-merge-request-org-mode/" rel="alternate" type="text/html" title="GitLab merge request 정보를 Emacs Org-mode 문서에 삽입" /><published>2025-01-18T00:00:00+09:00</published><updated>2025-01-18T00:00:00+09:00</updated><id>https://ohyecloudy.com/emacsian/2025/01/18/gitlab-merge-request-org-mode</id><content type="html" xml:base="https://ohyecloudy.com/emacsian/2025/01/18/gitlab-merge-request-org-mode/"><![CDATA[<p>org-mode 문서에 링크를 추가할 일이 있으면 <a href="https://ohyecloudy.com/emacsian/2022/03/17/package-org-cliplink/">Org-cliplink</a> 패키지를 사용한다. 로그인해야 접근할 수 있는 <a href="https://ohyecloudy.com/emacsian/2024/05/18/confluence-api-org-cliplink/">Confluence</a>와 <a href="https://ohyecloudy.com/emacsian/2024/11/02/jira-issue-org-mode/">Jira</a> 페이지도 Org-cliplink를 사용할 수 있게 확장하는 작업을 했다. 특정 주소로 시작하면 API를 호출해서 페이지 정보를 가져오는 방식으로 처리했다.</p>

<p>같은 방식은 아니지만 <a href="https://ohyecloudy.com/emacsian/2018/03/11/elisp-gitlab-inserting-links-and-titles-in-an-issue-or-a-merge-request/">GitLab issue와 merge request를 org-mode 문서에 링크로 추가할 수 있는 코드를 짰었다</a>. Org-cliplink를 알기 전이라 함수를 호출하고 issue나 merge request 번호를 입력해야 한다. GitLab merge request 링크도 Org-cliplink로 인터페이스를 통일하는 작업을 했다.</p>

<h1 id="url을-파싱해서-정보를-가져오기">URL을 파싱해서 정보를 가져오기</h1>

<p>GitLab URL structure에 <code class="highlighter-rouge">-</code> 구획 문자(delimiter)를 사용한다. <a href="https://slack.com/">Slack</a> 봇인 <a href="https://ohyecloudy.com/pnotes/archives/side-project-slab/">Slab</a>을 만들 때, 구획 문자가 추가된 GitLab 버전을 반영하느라 투덜거렸던 기억이 난다. 그때는 group 정보를 따로 빼낼 필요가 없어서 몰랐다. group, subgroup, project 정보와 page 정보를 파싱하는데 <code class="highlighter-rouge">-</code> 구획 문자가 있으니 편하다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">my/gitlab--extract-groups-and-mr-id</span> <span class="p">(</span><span class="nv">url</span> <span class="nv">end_point</span><span class="p">)</span>
  <span class="p">(</span><span class="k">let*</span> <span class="p">((</span><span class="nv">url-without-endpoint</span> <span class="p">(</span><span class="nv">replace-regexp-in-string</span> <span class="p">(</span><span class="nv">concat</span> <span class="s">"^"</span> <span class="nv">end_point</span><span class="p">)</span> <span class="s">""</span> <span class="nv">url</span><span class="p">))</span>
         <span class="p">(</span><span class="nv">url_parts</span> <span class="p">(</span><span class="nv">split-string</span> <span class="nv">url-without-endpoint</span> <span class="s">"/-/"</span><span class="p">))</span>
         <span class="p">(</span><span class="nv">mr-id</span> <span class="p">(</span><span class="nv">my/gitlab--merge-request-id</span> <span class="p">(</span><span class="nb">cadr</span> <span class="nv">url_parts</span><span class="p">))))</span>
    <span class="p">(</span><span class="nb">list</span> <span class="ss">:groups</span> <span class="p">(</span><span class="nv">my/gitlab-remove-leading-slash</span> <span class="p">(</span><span class="nb">car</span> <span class="nv">url_parts</span><span class="p">))</span> <span class="ss">:mr-id</span> <span class="nv">mr-id</span><span class="p">)))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">my/gitlab--merge-request-id</span> <span class="p">(</span><span class="nv">url</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">save-match-data</span>
    <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nv">string-match</span> <span class="s">"/?merge_requests/\\([0-9]+\\)"</span> <span class="nv">url</span><span class="p">)</span>
        <span class="p">(</span><span class="nv">string-to-number</span> <span class="p">(</span><span class="nv">match-string</span> <span class="mi">1</span> <span class="nv">url</span><span class="p">)))))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">my/gitlab-remove-leading-slash</span> <span class="p">(</span><span class="nv">url</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">replace-regexp-in-string</span> <span class="s">"^/"</span> <span class="s">""</span> <span class="nv">url</span><span class="p">))</span>

<span class="p">(</span><span class="nv">my/gitlab--extract-groups-and-mr-id</span> <span class="s">"https://gitlab.com/abc/def/ghi/-/merge_requests/123"</span> <span class="s">"https://gitlab.com"</span><span class="p">)</span>
</code></pre></div></div>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(:groups "abc/def/ghi" :mr-id 123)
</code></pre></div></div>

<p>group 정보와 merge request id는 <code class="highlighter-rouge">https://gitlab.com/api/v4/projects/abc%2Fdef%2Fghi/merge_requests/123</code> 처럼 호출 API URL을 만들 때, 사용한다.</p>

<h1 id="org-cliplink와-연동할-mygitlab-merge-request-title-함수">Org-cliplink와 연동할 <code class="highlighter-rouge">my/gitlab-merge-request-title</code> 함수</h1>

<p>함수 인자로 URL을 받는다. 설정으로 추가한 host 정보와 일치하면 <a href="https://ohyecloudy.com/emacsian/2023/04/22/auth-source-easypg/">Auth-source</a>에 저장한 private token을 사용해서 GitLab API를 호출한다. 만약 실패하면 <code class="highlighter-rouge">nil</code> 을 리턴한다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">my/gitlab-merge-request-title</span> <span class="p">(</span><span class="nv">url</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">when-let</span> <span class="p">(</span><span class="nv">content</span> <span class="p">(</span><span class="nv">my/gitlab-merge-request</span> <span class="nv">url</span><span class="p">))</span>
    <span class="p">(</span><span class="nv">concat</span> <span class="p">(</span><span class="nv">plist-get</span> <span class="nv">content</span> <span class="ss">:reference</span><span class="p">)</span> <span class="s">" "</span> <span class="p">(</span><span class="nv">plist-get</span> <span class="nv">content</span> <span class="ss">:title</span><span class="p">))))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">my/gitlab-merge-request</span> <span class="p">(</span><span class="nv">url</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">when-let*</span> <span class="p">((</span><span class="nv">host</span> <span class="p">(</span><span class="nv">my/gitlab--find-host</span> <span class="nv">url</span> <span class="nv">my/gitlab-hosts</span><span class="p">))</span>
              <span class="p">(</span><span class="nv">request-url</span> <span class="p">(</span><span class="nv">my/gitlab--merge-request-url</span> <span class="nv">url</span> <span class="nv">host</span><span class="p">))</span>
              <span class="p">(</span><span class="nv">header</span> <span class="p">(</span><span class="nv">my/gitlab--auth</span> <span class="p">(</span><span class="nv">plist-get</span> <span class="nv">host</span> <span class="ss">:url</span><span class="p">)))</span>
              <span class="p">(</span><span class="nv">content</span> <span class="p">(</span><span class="nv">my/gitlab--get-merge-request</span> <span class="nv">request-url</span> <span class="nv">header</span><span class="p">)))</span>
    <span class="nv">content</span><span class="p">))</span>
</code></pre></div></div>

<p>full reference를 사용해서 <code class="highlighter-rouge">gitlab-org/gitlab!23066 Move merge request routes under /-/ scope</code> 와 같이 title을 구성한다.</p>

<p><a href="https://ohyecloudy.com/emacsian/2024/05/18/confluence-api-org-cliplink/">Org-cliplink를 확장하려고 추가한 hook 변수</a>에 <code class="highlighter-rouge">my/gitlab-merge-request-title</code> 함수를 추가한다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">my/org-cliplink-custom-retrieve-title-hook</span>
      <span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nv">url</span><span class="p">)</span>
        <span class="p">(</span><span class="nb">or</span> <span class="p">(</span><span class="nv">my/org-cliplink-confluence-title</span> <span class="nv">url</span><span class="p">)</span>
            <span class="p">(</span><span class="nv">my/jira-title</span> <span class="nv">url</span><span class="p">)</span>
            <span class="p">(</span><span class="nv">my/gitlab-merge-request-title</span> <span class="nv">url</span><span class="p">))))</span>
</code></pre></div></div>

<h1 id="org-mode-heading으로-삽입할-mygitlab-insert-heading-content">Org-mode heading으로 삽입할 <code class="highlighter-rouge">my/gitlab-insert-heading-content</code></h1>

<p>’<a href="https://ohyecloudy.com/emacsian/2024/11/02/jira-issue-org-mode/">Jira 이슈 정보를 Emacs Org-mode 문서에 삽입</a>’ 글에서 짠 함수와 비슷하게 키를 바인딩할 함수를 짰다. 클립보드에 URL이 있으면 그걸 사용하고 없으면 URL 입력 프롬프트를 띄우게 했다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">my/gitlab-insert-heading-content</span> <span class="p">()</span>
  <span class="p">(</span><span class="nv">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="k">let*</span> <span class="p">((</span><span class="nv">url</span> <span class="p">(</span><span class="nv">my/gitlab--url-clipboard-or-prompt</span><span class="p">))</span>
         <span class="p">(</span><span class="nv">content</span> <span class="p">(</span><span class="nv">my/gitlab-merge-request</span> <span class="nv">url</span><span class="p">))</span>
         <span class="p">(</span><span class="nv">reference</span> <span class="p">(</span><span class="nv">plist-get</span> <span class="nv">content</span> <span class="ss">:reference</span><span class="p">))</span>
         <span class="p">(</span><span class="nv">title</span> <span class="p">(</span><span class="nv">plist-get</span> <span class="nv">content</span> <span class="ss">:title</span><span class="p">)))</span>
    <span class="p">(</span><span class="nv">org-insert-heading</span><span class="p">)</span>
    <span class="p">(</span><span class="nv">insert</span> <span class="p">(</span><span class="nb">format</span> <span class="s">"%s %s [/]"</span> <span class="nv">reference</span> <span class="nv">title</span><span class="p">))</span>
    <span class="p">(</span><span class="nv">org-update-statistics-cookies</span> <span class="no">nil</span><span class="p">)</span>
    <span class="p">(</span><span class="nv">org-set-property</span> <span class="s">"URL"</span> <span class="nv">url</span><span class="p">)))</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">my/gitlab--url-clipboard-or-prompt</span> <span class="p">()</span>
  <span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">clipboard-content</span> <span class="p">(</span><span class="nv">gui-get-selection</span> <span class="ss">'CLIPBOARD</span><span class="p">)))</span>
    <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nb">and</span> <span class="nv">clipboard-content</span> <span class="p">(</span><span class="nv">string-match-p</span> <span class="s">"^http"</span> <span class="nv">clipboard-content</span><span class="p">))</span>
        <span class="nv">clipboard-content</span>
      <span class="p">(</span><span class="nv">read-string</span> <span class="s">"Enter URL: "</span><span class="p">))))</span>
</code></pre></div></div>

<p><a href="https://ohyecloudy.com/emacsian/2022/09/03/welcome-doom-emacs/">Doom Emacs</a> 함수를 사용해서 <code class="highlighter-rouge">SPC m z g</code> 키로 바인딩했다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">map!</span> <span class="ss">:map</span> <span class="nv">org-mode-map</span>
      <span class="ss">:localleader</span>
      <span class="p">(</span><span class="ss">:prefix</span> <span class="p">(</span><span class="s">"z"</span> <span class="o">.</span> <span class="s">"insert"</span><span class="p">)</span>
               <span class="s">"g"</span> <span class="nf">#'</span><span class="nv">my/gitlab-insert-heading-content</span><span class="p">))</span>
</code></pre></div></div>

<h1 id="마치며">마치며</h1>

<p>Org-cliplink와 <code class="highlighter-rouge">SPC m z</code> 키바인딩으로 org-mode 문서 링크 추가 인터페이스를 통일했다. 어떤 URL이던지 clipboard에 복사하고 <code class="highlighter-rouge">SPC m l c</code> 키를 누르면 링크가 추가되니 편하다. org-mode 문서 heading을 추가하는 건 <code class="highlighter-rouge">SPC m z</code> 키바인딩으로 통일해서 이제 손에도 익숙하다. 전체 코드는 <a href="https://github.com/ohyecloudy/dotfiles/commit/94028ad00ac82f4d0d29f9d1061dcb046c5290d2">ohyecloudy/dotfiles/commit/94028ad00ac</a> 커밋으로 볼 수 있다.</p>

<p><code class="highlighter-rouge">C-x C-s C-x C-c</code></p>

<h1 id="링크">링크</h1>

<ul>
  <li><a href="https://ohyecloudy.com/emacsian/2018/03/11/elisp-gitlab-inserting-links-and-titles-in-an-issue-or-a-merge-request/">#gitlab issue 또는 merge request의 링크와 제목 삽입하기 - (emacsian ohyecloudy) - ohyeclo…</a>(<a href="http://web.archive.org/web/20250118081229/https://ohyecloudy.com/emacsian/2018/03/11/elisp-gitlab-inserting-links-and-titles-in-an-issue-or-a-merge-request/">archive</a>)</li>
  <li><a href="https://ohyecloudy.com/emacsian/2022/09/03/welcome-doom-emacs/">Doom Emacs 전환 후기 - (emacsian ohyecloudy) - ohyecloudy.com</a>(<a href="http://web.archive.org/web/20250118081228/https://ohyecloudy.com/emacsian/2022/09/03/welcome-doom-emacs/">archive</a>)</li>
  <li><a href="https://ohyecloudy.com/emacsian/2023/04/22/auth-source-easypg/">auth-source와 EasyPG 기본 제공 패키지를 사용한 비밀 번호를 포함한 계정 정보 관리 - (emacsian ohyecloudy…</a>(<a href="http://web.archive.org/web/20250118081229/https://ohyecloudy.com/emacsian/2023/04/22/auth-source-easypg/">archive</a>)</li>
  <li><a href="https://ohyecloudy.com/emacsian/2024/05/18/confluence-api-org-cliplink/">Confluence API로 가져온 제목으로 org-mode 마크업 링크 삽입 - org-cliplink 기능 확장 - (emacsian …</a>(<a href="http://web.archive.org/web/20250118081229/https://ohyecloudy.com/emacsian/2024/05/18/confluence-api-org-cliplink/">archive</a>)</li>
  <li><a href="https://ohyecloudy.com/emacsian/2024/11/02/jira-issue-org-mode/">Jira 이슈 정보를 Emacs org-mode 문서에 삽입 - (emacsian ohyecloudy) - ohyecloudy.com</a>(<a href="http://web.archive.org/web/20250118081228/https://ohyecloudy.com/emacsian/2024/11/02/jira-issue-org-mode/">archive</a>)</li>
  <li><a href="https://slack.com/">AI Work Management &amp; Productivity Tools - Slack - slack.com</a>(<a href="http://web.archive.org/web/20250118081228/https://slack.com/">archive</a>)</li>
  <li><a href="https://github.com/ohyecloudy/dotfiles/commit/94028ad00ac82f4d0d29f9d1061dcb046c5290d2">ohyecloudy/dotfiles/commit/94028ad00ac82f4d0d29f9d1061dcb046c5290d2 - github.com</a>(<a href="http://web.archive.org/web/20250118081228/https://github.com/ohyecloudy/dotfiles/commit/94028ad00ac82f4d0d29f9d1061dcb046c5290d2">archive</a>)</li>
  <li><a href="https://ohyecloudy.com/pnotes/archives/side-project-slab/">#side_project #elixirlang slab 후기 - gitlab API를 사용해 귀찮은 일을 덜어주는 slack 봇 - ohy…</a>(<a href="http://web.archive.org/web/20250118081228/https://ohyecloudy.com/pnotes/archives/side-project-slab/">archive</a>)</li>
  <li><a href="https://ohyecloudy.com/emacsian/2022/03/17/package-org-cliplink/">org-cliplink 패키지로 title과 url을 편하게 삽입 - (emacsian ohyecloudy) - ohyecloudy.com</a>(<a href="http://web.archive.org/web/20250118081229/https://ohyecloudy.com/emacsian/2022/03/17/package-org-cliplink/">archive</a>)</li>
</ul>

<!----- Footnotes ----->]]></content><author><name>Jongbin Oh</name><email>ohyecloudy@gmail.com</email></author><category term="uncategorized" /><category term="gitlab" /><category term="merge-request" /><category term="org" /><category term="org-cliplink" /><summary type="html"><![CDATA[org-mode 문서에 링크를 추가할 일이 있으면 Org-cliplink 패키지를 사용한다. 로그인해야 접근할 수 있는 Confluence와 Jira 페이지도 Org-cliplink를 사용할 수 있게 확장하는 작업을 했다. 특정 주소로 시작하면 API를 호출해서 페이지 정보를 가져오는 방식으로 처리했다.]]></summary></entry><entry><title type="html">Emacs를 위한 트리 레이아웃 파일 탐색기 Treemacs 기본 사용법</title><link href="https://ohyecloudy.com/emacsian/2024/12/28/treemacs-basic/" rel="alternate" type="text/html" title="Emacs를 위한 트리 레이아웃 파일 탐색기 Treemacs 기본 사용법" /><published>2024-12-28T00:00:00+09:00</published><updated>2024-12-28T00:00:00+09:00</updated><id>https://ohyecloudy.com/emacsian/2024/12/28/treemacs-basic</id><content type="html" xml:base="https://ohyecloudy.com/emacsian/2024/12/28/treemacs-basic/"><![CDATA[<p>Emacs에서 프로그래밍하니깐 트리 레이아웃 파일 탐색기가 그리워진다. <a href="https://visualstudio.microsoft.com/ko/">Visual Studio</a> 같은 <a href="https://ko.wikipedia.org/wiki/%ED%86%B5%ED%95%A9_%EA%B0%9C%EB%B0%9C_%ED%99%98%EA%B2%BD">IDE(integrated development environment)</a>에서는 기본으로 제공하는 솔루션 탐색기(solution explorer) 같은 탐색기 말이다. <a href="https://ohyecloudy.com/emacsian/2022/09/03/welcome-doom-emacs/">Doom Emacs</a>에 Treemacs 파일 탐색기 패키지가 세팅되어 있어서 사용하기 시작했다.</p>

<h1 id="설치">설치</h1>

<p>Doom Emacs에서는 <code class="highlighter-rouge">init.el</code> 파일에 주석 처리가 되어 있는 <code class="highlighter-rouge">treemacs</code> 를 찾아서 주석을 풀고 세팅을 해주면 된다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">doom!</span> <span class="ss">:ui</span>
       <span class="p">(</span><span class="nv">treemacs</span> <span class="nv">+lsp</span><span class="p">))</span>
</code></pre></div></div>

<p>Doom Emacs를 사용하지 않는다면 <a href="https://github.com/Alexander-Miller/treemacs">Alexander-Miller/treemacs</a> 페이지를 보고 설치하면 된다. <a href="https://melpa.org/">MELPA</a>에 등록되어 있으니 <a href="https://ohyecloudy.com/emacsian/2016/11/20/package-use-package/">use-package</a>로 쉽게 설치할 수 있다.</p>

<h1 id="사용">사용</h1>

<p>키바인딩은 Doom Emacs 기준이다.</p>

<ul>
  <li><code class="highlighter-rouge">M-x treemacs (SPC o p)</code> - 파일 탐색기를 연다/닫는다</li>
  <li><code class="highlighter-rouge">M-x treemacs-find-file (SPC o P)</code> - 파일 탐색기를 열고 현재 파일에 포커스를 둔다</li>
  <li><code class="highlighter-rouge">M-x treemacs-common-helpful-hydra (?)</code> - 도움말</li>
  <li><code class="highlighter-rouge">M-x treemacs-advanced-helpful-hydra (C-?)</code> - 부가 기능 도움말</li>
</ul>

<p><code class="highlighter-rouge">SPC o p/P</code> 로 파일 탐색기를 열고 <code class="highlighter-rouge">?</code> 키를 누르면 사용할 수 있는 도움말을 볼 수 있다. <a href="https://github.com/abo-abo/hydra">abo-abo/hydra</a> 패키지를 사용해 보여주는 도움말이 보기 좋다. 기본 도움말과 부가 기능 도움말을 분리한 것도 좋은 결정이다. 많은 키바인딩을 한 번에 보여줘서 가시성을 떨어뜨리는 걸 막는다.</p>

<p>다음은 열기 메뉴다.</p>

<ul>
  <li><code class="highlighter-rouge">M-x treemacs-RET-action (RET) (l)</code> - 열기</li>
  <li><code class="highlighter-rouge">M-x treemacs-visit-node-vertical-split (o s)</code> - vertical split</li>
  <li><code class="highlighter-rouge">M-x treemacs-visit-node-horizontal-split (o v)</code> - horizontal split</li>
</ul>

<p><code class="highlighter-rouge">RET</code> 키로 열어도 되고 <code class="highlighter-rouge">l</code> 키로 열어도 된다. <code class="highlighter-rouge">l</code> 키는 <a href="https://www.vim.org/">Vim</a> 이동 키인 <code class="highlighter-rouge">hjkl</code> 에서 따온 것 같다. 사이드바는 왼쪽에 있고 오른쪽에 해당 파일을 여니깐 <code class="highlighter-rouge">l</code> 키가 말이 된다.</p>

<p>treemacs에 포커스를 유지하고 파일을 빠르게 살펴볼 수 있는 모드를 지원한다.</p>

<ul>
  <li><code class="highlighter-rouge">M-x treemacs-peek-mode (P)</code> - Peek 모드 toggle</li>
  <li><code class="highlighter-rouge">M-x treemacs-next-line-other-window (M-J)</code> - Peek 모드에서 윈도우 스크롤</li>
  <li><code class="highlighter-rouge">M-x treemacs-previous-line-other-window (M-K)</code> - Peek 모드에서 윈도우 스크롤</li>
</ul>

<h1 id="프로젝트-컬렉션인-워크스페이스">프로젝트 컬렉션인 워크스페이스</h1>

<blockquote>
  <p>If you’ve previously used a different explorer like NeoTree or NerdTree - or an earlier version of treemacs for that matter - you are probably used to a display system wherein you see exactly a single file tree whose exact root you can arbitrarily change. This system makes it difficult to work on and switch between multiple projects. Treemacs used to (and still does) remedy that limitation by making every treemacs buffer unique to its frame, but it has now been redesigned to be able to display multiple file trees - projects - at once.</p>

  <p>NeoTree나 NerdTree 같은 다른 탐색기나 이전 버전의 Treemacs를 사용해 본 적이 있다면, 정확한 루트를 임의로 변경할 수 있는 단일 파일 트리가 표시되는 디스플레이 시스템에 익숙하실 것입니다. 이 시스템에서는 여러 프로젝트에서 작업하고 전환하기가 어렵습니다. Treemacs는 예전에는 모든 Treemacs 버퍼를 해당 프레임에 고유하게 만들어 이러한 한계를 해결했지만, 이제는 여러 파일 트리(프로젝트)를 한 번에 표시할 수 있도록 재설계되었습니다.</p>

  <p><a href="https://github.com/Alexander-Miller/treemacs">Alexander-Miller/treemacs - github.com</a></p>
</blockquote>

<p>Visual Studio를 다뤄봤다면 여러 프로젝트를 담는 솔루션(solution) 파일이 연상될지도 모르겠다. 단일 프로젝트가 아니라 여러 프로젝트를 워크스페이스로 묶어서 한 번에 보여줄 수 있게 설계했다.</p>

<p><code class="highlighter-rouge">M-x treemacs-edit-workspaces</code> 함수를 호출하면 <a href="https://orgmode.org/">Org-mode</a>로 된 버퍼를 볼 수 있다.</p>

<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#+TITLE: Edit Treemacs Workspaces &amp; Projects
# Call ~treemacs-finish-edit~ or press ~C-c C-c~ when done.
# [[https://github.com/Alexander-Miller/treemacs#conveniently-editing-your-projects-and-workspaces][Click here for detailed documentation.]]
# To cancel you can simply kill this buffer.

* Perspective main
** project_a
   - path :: ~/src/project_a
** COMMENT project_b
   - path :: ~/src/project_b
</code></pre></div></div>

<p>Org-mode 마크업 언어로 세팅을 편집하는 건 처음이다. Emacs에서는 지원이 확실하니 좋은 경험이었다. Org-mode에서 지원하는 코멘트 기능으로 워크스페이스에 안 보여줄 프로젝트를 세팅하는 게 재미있었다.</p>

<p><a href="https://ohyecloudy.com/emacsian/2017/10/29/package-projectile-basic/">Projectile</a>을 사용한다면 <code class="highlighter-rouge">M-x treemacs-projectile</code> 함수로 projectile로 등록한 프로젝트를 워크스페이스에 손쉽게 추가할 수 있다.</p>

<h1 id="추가-세팅">추가 세팅</h1>

<p>Doom Emacs에서 <code class="highlighter-rouge">follow-mode</code> 를 꺼놔서 나는 켜는 추가 세팅을 했다. 현재 버퍼에 있는 파일을 treemacs 버퍼에서 포커싱하는 기능이다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">after!</span> <span class="nv">treemacs</span>
  <span class="p">(</span><span class="nv">treemacs-follow-mode</span> <span class="no">t</span><span class="p">))</span>
</code></pre></div></div>

<h1 id="마치며">마치며</h1>

<p>현재 내게 Emacs는 <a href="https://elixir-lang.org/">Elixir</a> 메인 프로그래밍 에디터이다. Elixir 프로그래밍을 할 때 <code class="highlighter-rouge">SPC o p</code> 키를 눌러 treemacs 사이드바를 열어 놓고 시작한다. 여러 프로젝트를 한 번에 보여주고 전환을 쉽게 할 수 있는 워크스페이스는 아직 잘 활용하고 있진 않다.</p>

<p><code class="highlighter-rouge">C-x C-s C-x C-c</code></p>

<h1 id="링크">링크</h1>

<ul>
  <li><a href="https://ohyecloudy.com/emacsian/2022/09/03/welcome-doom-emacs/">Doom Emacs 전환 후기 - (emacsian ohyecloudy) - ohyecloudy.com</a></li>
  <li><a href="https://orgmode.org/">Org mode for GNU Emacs - orgmode.org</a>(<a href="http://web.archive.org/web/20241228121202/https://orgmode.org/">archive</a>)</li>
  <li><a href="https://www.vim.org/">welcome home : vim online - vim.org</a>(<a href="http://web.archive.org/web/20241228121202/https://www.vim.org/">archive</a>)</li>
  <li><a href="https://visualstudio.microsoft.com/ko/">Visual Studio: 소프트웨어 개발자 및 Teams용 IDE 및 코드 편집기 - visualstudio.microsoft.com</a>(<a href="http://web.archive.org/web/20241228121202/https://visualstudio.microsoft.com/ko/">archive</a>)</li>
  <li><a href="https://ohyecloudy.com/emacsian/2016/11/20/package-use-package/">use-package 패키지로 패키지 관리를 더 간단하게 - (emacsian ohyecloudy) - ohyecloudy.com</a></li>
  <li><a href="https://ohyecloudy.com/emacsian/2017/10/29/package-projectile-basic/">projectile - 프로젝트 단위로 파일 이름 찾고 문자열 검색하고 바꾸고… - (emacsian ohyecloudy) - ohyecl…</a></li>
  <li><a href="https://melpa.org/">MELPA - melpa.org</a>(<a href="http://web.archive.org/web/20241228120948/https://melpa.org/#/">archive</a>)</li>
  <li><a href="https://github.com/abo-abo/hydra">abo-abo/hydra - github.com</a>(<a href="http://web.archive.org/web/20241228120948/https://github.com/abo-abo/hydra">archive</a>)</li>
  <li><a href="https://ko.wikipedia.org/wiki/%ED%86%B5%ED%95%A9_%EA%B0%9C%EB%B0%9C_%ED%99%98%EA%B2%BD">통합 개발 환경 - ko.wikipedia.org</a>(<a href="http://web.archive.org/web/20241228120948/https://ko.wikipedia.org/wiki/%ED%86%B5%ED%95%A9_%EA%B0%9C%EB%B0%9C_%ED%99%98%EA%B2%BD">archive</a>)</li>
  <li><a href="https://elixir-lang.org/">The Elixir programming language - elixir-lang.org</a>(<a href="http://web.archive.org/web/20241228120950/https://elixir-lang.org/">archive</a>)</li>
  <li><a href="https://github.com/Alexander-Miller/treemacs">Alexander-Miller/treemacs - github.com</a>(<a href="http://web.archive.org/web/20241228121001/https://github.com/Alexander-Miller/treemacs">archive</a>)</li>
</ul>

<!----- Footnotes ----->]]></content><author><name>Jongbin Oh</name><email>ohyecloudy@gmail.com</email></author><category term="uncategorized" /><category term="doom-emacs" /><category term="treemacs" /><category term="file-explorer" /><summary type="html"><![CDATA[Emacs에서 프로그래밍하니깐 트리 레이아웃 파일 탐색기가 그리워진다. Visual Studio 같은 IDE(integrated development environment)에서는 기본으로 제공하는 솔루션 탐색기(solution explorer) 같은 탐색기 말이다. Doom Emacs에 Treemacs 파일 탐색기 패키지가 세팅되어 있어서 사용하기 시작했다.]]></summary></entry><entry><title type="html">evil-mode 편집 모드를 나갈 때, input method를 초기화한다</title><link href="https://ohyecloudy.com/emacsian/2024/12/07/evil-mode-initialize-input-method/" rel="alternate" type="text/html" title="evil-mode 편집 모드를 나갈 때, input method를 초기화한다" /><published>2024-12-07T00:00:00+09:00</published><updated>2024-12-07T00:00:00+09:00</updated><id>https://ohyecloudy.com/emacsian/2024/12/07/evil-mode-initialize-input-method</id><content type="html" xml:base="https://ohyecloudy.com/emacsian/2024/12/07/evil-mode-initialize-input-method/"><![CDATA[<p><a href="https://github.com/emacs-evil/evil">evil</a>은 편집 모드에서 세팅한 <a href="https://ohyecloudy.com/emacsian/2013/10/26/input-method/">입력기(input method)</a>를 유지해 준다. 한글을 입력하다가 노멀 모드로 갔다가 다시 편집 모드로 돌아가서 한글을 계속 입력할 수 있다.</p>

<p>한글과 영문을 오가며 써보니 헷갈린다. 편집 모드로 돌아가서 한글을 입력하려고 입력기를 변경하면 이전 한글 입력기 선택을 기억하고 있어 영문이 입력되고 이런 일이 빈번하게 발생했다. <a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Mode-Line.html">모드 라인(mode line)</a>에 표시된 입력기 상태를 보면 되긴 하지만 편집에 집중하고 있다 보면 눈에 잘 안 들어온다.</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">after!</span> <span class="nv">evil</span>
  <span class="p">(</span><span class="nv">add-hook</span> <span class="ss">'evil-insert-state-exit-hook</span>
            <span class="p">(</span><span class="k">lambda</span> <span class="p">()</span>
              <span class="p">(</span><span class="k">setq</span> <span class="nv">evil-input-method</span> <span class="no">nil</span><span class="p">)))</span>
  <span class="p">)</span>
</code></pre></div></div>

<p>그래서 편집 모드를 나가면 무조건 영문으로 돌리는 세팅을 했다. <a href="https://github.com/jjpark78/my_doom_emacs_config?utm_source=pocket_mylist">jjpark78/my_doom_emacs_config</a> 세팅을 참고했다. 다시 약간의 적응이 필요하겠지만 하나로 고정되어 있으니 익숙해지면 더 편해지겠지?</p>

<p><code class="highlighter-rouge">C-x C-s C-x C-c</code></p>

<!----- Footnotes ----->]]></content><author><name>Jongbin Oh</name><email>ohyecloudy@gmail.com</email></author><category term="uncategorized" /><category term="evil" /><category term="input-method" /><summary type="html"><![CDATA[evil은 편집 모드에서 세팅한 입력기(input method)를 유지해 준다. 한글을 입력하다가 노멀 모드로 갔다가 다시 편집 모드로 돌아가서 한글을 계속 입력할 수 있다.]]></summary></entry></feed>