あさた研メモ

主に私が気づいたこととか困った時のメモとか書き留めとく用。

Emacs を自分でビルドしよう (MacOS)

この記事は Emacs Advent Calendar 2023 の 7 日目の記事です。

qiita.com

はじめに

みなさんは自分の好きなエディタはありますか?

私はなんだかんだいってEmacsを愛用しています。

自分で使うツールは自分でビルドしたいですよね?

ということで、Emacsをパッケージマネージャなどを使わずに自分でビルドして入れる方法を紹介します。

私の環境

必要なものを揃える

XCode の Command line tools と、Homebrew は用意しておきましょう (大体の人は入れてると思いますが)。

libgccjit が必要になるので入れます。

$ brew install libgccjit

ビルドの仕方

まずは git プロジェクトの clone

$ git clone git://git.sv.gnu.org/emacs.git

次に、自分が使いそうなオプションを追加して configure します。
git switch などで特定のリリースをビルドしましょう。

私は master ブランチのままビルドしています。

configure するときにいくつか引数を渡すことで、特定のビルドオプションを ON / OFF できます。 詳しくはここに書いています。

github.com

絶対に入れておくべきなのは、--with-native-compilation です。 Emacs 28 以降、Native Compile にすることで高速に使うことができます。

$ cd ./emacs
$ git pull
$ ./autogen.sh
$ ./configure --with-native-compilation \
            --without-x \
            --with-ns \
            --with-imagemagick \
            --with-json

ここまできたら、あとは build のみです。

$ make bootstrap
$ make install

configure や bootstrap 中にエラー終了することがありますが、気づきにくいのでよくログを見ておきましょう。

たいていはなにかのパッケージが足りてないので、都度エラーの内容から入れるべきパッケージを入れましょう。

make install が終わると、 nextstep/ 配下に Emacs.app ができているので、これを /Application 内に移動して完了です。

注意点

結構ビルドが失敗する要因として、libgccjitopenssl のバージョンです。

Emacs のバージョンによって最低要件のバージョンなどがあるので、コケたらよく調べておきましょう。

さいごに

「ビルドがコケたら」の話は人それぞれなのと、Emacs を自分でビルドして使いたい人はビルド環境などを調べるのも好きな人が多いと思うので、
解決方法を根気よく探しましょう。

Emacsオープンソースでビルドが簡単なので、ぜひ思い思いのオプションをつけてビルドしてみましょう!

お世話になった記事

はじめてビルドするとき、大変お世話になりました。

blog.tomoya.dev

SendGrid の Dynamic Template では equals~else equals 構文も使える

SendGrid の Dynamic Template では、簡易的に if 文などの構文が使える。

docs.sendgrid.com

このドキュメントにおいて、if ~ else if の構文が使えることが紹介されている。

実はドキュメントに書いてないが、 equals や notEquals でも同様の構文が使える。

{{#equals var "hoge"}}
  ホゲだよ
{{else equals var "fuga"}}
  フガでござる
{{else}}
  知らんがな
{{/equals}}

上記の例では、

  • {"var": "hoge"} のとき => ホゲだよ
  • {"var": "fuga"} のとき => フガでござる
  • {"var": "piyo"} のとき => 知らんがな

という置換がされる。

なお注意として、これもifの項でしかドキュメントに書いてないのだが equals スコープ内で変数置換する場合、その参照の仕方に注意しないといけない。

# 渡すjson
{"weather": "rain", "date": "2021-12-31"}

# テンプレート
{{#equals weather "rain"}}
  雨量が少なければ様子を見て開催
{{else equals weather "snow"}}
  中止
{{else}}
  開催 ({{@root.date}})
{{/equals}}

lsp-mode の設定を晒す (2021-02-06 時点)

自分の設定を晒す。

(lsp-document-sync-method . 'lsp--sync-incremental)コメントアウトされているが、他の記事に書いてあったこの設定を入れていたらlspがちゃんと動かなかったので、指定を外した。

こういう設定系は誰かが最新の環境で動くものを晒さないと構成のためにならないと思うので、自分も書いておく。

(leaf lsp-mode
  :doc "LSP mode"
  :req "emacs-26.1" "dash-2.14.1" "dash-functional-2.14.1" "f-0.20.0" "ht-2.0" "spinner-1.7.3" "markdown-mode-2.3" "lv-0"
  :tag "languages" "emacs>=26.1"
  :added "2020-08-27"
  :url "https://github.com/emacs-lsp/lsp-mode"
  :emacs>= 26.1
  :ensure t
  :after spinner markdown-mode lv
  :custom
  `((lsp-keymap-prefix                  . "C-c l")
    (lsp-inhibit-message                . t)
    (lsp-message-project-root-warning   . t)
    (create-lockfiles                   . nil)
    (lsp-signature-auto-activate        . t)
    (lsp-signature-doc-lines            . 1)
    (lsp-print-performance              . t)
    (lsp-log-io                         . t)
    (lsp-eldoc-render-all               . t)
    (lsp-enable-completion-at-point     . t)
    (lsp-enable-xref                    . t)
    (lsp-keep-workspace-alive           . nil)
    (lsp-enable-snippet                 . t)
    (lsp-server-trace                   . nil)
    (lsp-auto-guess-root                . nil)
    ;; (lsp-document-sync-method           . 'lsp--sync-incremental)
    (lsp-document-sync-method           . 2)
    (lsp-diagnostics-provider           . :flycheck)
    (lsp-response-timeout               . 5)
    (lsp-idle-delay                     . 0.500)
    (lsp-enable-file-watchers           . nil)
    (lsp-completion-provider            . :capf)
    (lsp-headerline-breadcrumb-segments . '(symbols)))
  :commands
  (lsp lsp-deferred)
  :hook
  (prog-major-mode . lsp-prog-major-mode-enable)
  (lsp-mode-hook . lsp-ui-mode)
  (lsp-mode-hook . lsp-headerline-breadcrumb-mode)
  
  :init
  (leaf lsp-ui
    :doc "UI modules for lsp-mode"
    :req "emacs-26.1" "dash-2.14" "dash-functional-1.2.0" "lsp-mode-6.0" "markdown-mode-2.3"
    :tag "tools" "languages" "emacs>=26.1"
    :added "2020-08-27"
    :url "https://github.com/emacs-lsp/lsp-ui"
    :emacs>= 26.1
    :ensure t
    :after lsp-mode markdown-mode
    :custom
    ((lsp-ui-doc-enable            . t)
     (lsp-ui-doc-deley             . 0.5)
     (lsp-ui-doc-header            . t)
     (lsp-ui-doc-include-signature . t)
     (lsp-ui-doc-position          . 'at-point)
     (lsp-ui-doc-max-width         . 150)
     (lsp-ui-doc-max-height        . 30)
     (lsp-ui-doc-use-childframe    . nil)
     (lsp-ui-doc-use-webkit        . nil)
     (lsp-ui-flycheck-enable       . t)
     (lsp-ui-peek-enable           . t)
     (lsp-ui-peek-peek-height      . 20)
     (lsp-ui-peek-list-width       . 50)
     (lsp-ui-peek-fontify          . 'on-demand) ;; never, on-demand, or always
     )
    :hook ((lsp-mode-hook . lsp-ui-mode))
    )
  (leaf lsp-treemacs
    :doc "LSP treemacs"
    :req "emacs-26.1" "dash-2.14.1" "dash-functional-2.14.1" "f-0.20.0" "ht-2.0" "treemacs-2.5" "lsp-mode-6.0"
    :tag "languages" "emacs>=26.1"
    :added "2020-10-22"
    :url "https://github.com/emacs-lsp/lsp-treemacs"
    :emacs>= 26.1
    :ensure t
    :after treemacs lsp-mode)
  )

なお動作環境は Emacs 28.0.50。

起動時に Warning (comp): el-get-status.el:22:1: Warning: Package cl is deprecated みたいなのが出まくるんだけど。
init.el に

(setq byte-compile-warnings '(not cl-functions obsolete))

を書いているのに直らない。

Emacs-27 では出なかったんだけど、既知の問題なんだろうか?
鬱陶しいだけなのだが、鬱陶しいのでなんとかしたい…。
だれか教えてください。

quick search を Go で書く

タイトル通り。
参考記事を元に書いたけど、正しく実装できてるのか自信ない。
愚直な実装も一緒に書いておいて、結果の比較をできるようにしてある。

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

const SIGMA = 256

func native(ts, ps []string) {
    n := len(ts)
    m := len(ps)

    if n == 0 || m == 0 {
        return
    }

    for i := 0; i <= n-m; i++ {
        j := 0
        for j = 0; j < m && ps[j] == ts[i+j]; {
            j++
        }
        if j == m {
            fmt.Println(i)
        }
    }
}

func quickSearch(ts, ps []string) {
    n := len(ts)
    m := len(ps)

    if n == 0 || m == 0 {
        return
    }

    // 要素数+1を参照するため
    ts = append(ts, "")

    // 移動表
    shift := make(map[string]int)
    for i := 0; i < m; i++ {
        shift[ps[i]] = m - i
    }

    for i := 0; i <= n-m; {
        j := 0
        for j = 0; j < m && ps[j] == ts[i+j]; j++ {
        }

        if j == m {
            fmt.Println(i)
        }

        if shift[ts[i+m]] != 0 {
            i += shift[ts[i+m]]
        } else {
            i += m
        }
    }
}

func main() {
    var t, p string
    var sc = bufio.NewScanner(os.Stdin)
    if sc.Scan() {
        t = sc.Text()
    }
    if sc.Scan() {
        p = sc.Text()
    }

    ts := strings.Split(t, "")
    ps := strings.Split(p, "")

    fmt.Println("===== NATIVE =====")
    native(ts, ps)

    fmt.Println("===== QS =====")
    quickSearch(ts, ps)
}

Gist に残した: quick search · GitHub

参考記事

www.m3tech.blog

www.nct9.ne.jp

Doom Emacs から Vanilla Emacs に乗り換えた

もう数年間 Emacs を使っているものの、ちょくちょく環境を変えてきた。

最初は Spacemacs を使っていたが、起動が重くて Doom Emacs に切り替えて使ってきた。

しかし、leaf.el の登場で設定が結構簡単に書けるようになるので、これを期に Vanilla の Emacs に乗り換えることにした。

使っているパッケージを整理する

新しく設定を書くにあたって、 Spacemacs や Doom Emacs でサラッと利用していたパッケージがあるので、よく使う機能を整理した。

中には、ディストリビューションが初期状態で搭載しているものもあったので、ここが結構大変だった。

  • ivy
  • swiper
  • flycheck
  • company
  • magit
  • lsp, lsp-ui
  • visual-regexp
  • doom-themes
  • doom-modeline
  • undo-tree

などなど…

他にも、使う言語に関連するパッケージ (tide とか) も整理した。

設定を書く

init.el に leaf.el の例のインストールのやつを書いて、その後各パッケージの設定を書く。

init.el を更新したら、以下のコマンドでバイトコンパイルする。

$ cd ~/.emacs.d
$ emacs --batch -f batch-byte-compile init.el

built-in のパッケージの設定

特に、当たり前に付いてるけど忘れがちなパッケージの設定は結構ハマった。

ちなみに、各設定は、パッケージが登録されてれば leaf-convert-insert-template でパッケージ名を入れれば、デフォルト設定が簡単に入れられる。

  • 行番号の表示を linum で表示
(leaf linum
  :doc "display line numbers in the left margin"
  :tag "builtin"
  :added "2020-08-27"
  :init (global-linum-mode 1))
  • 対応する括弧の自動挿入
(leaf electric
  :doc "window maker and Command loop for `electric' modes"
  :tag "builtin"
  :added "2020-08-27"
  :init (electric-pair-mode 1))
  • ispell を aspell で使う
(leaf ispell
  :doc "interface to spell checkers"
  :tag "builtin"
  :added "2020-08-27"
  :setq-default (ispell-program-name . "aspell"))

追加パッケージの設定

exec-path-from-shell を入れないとシェルの環境変数が読み込まれず、これが一番ハマった。

具体的には、typescript の lspを使うにあたって npm が読み込めず、解決するまでめちゃくちゃ時間かかった。

(leaf exec-path-from-shell
  :doc "Get environment variables such as $PATH from the shell"
  :req "emacs-24.1"
  :tag "environment" "unix" "emacs>=24.1"
  :added "2020-08-27"
  :url "https://github.com/purcell/exec-path-from-shell"
  :emacs>= 24.1
  :ensure t)

なお、exec-path-from-shellserver-start を、init.el の最後の方に追加した。

...
(provide 'init)

(exec-path-from-shell-initialize)
(server-start)

;; Local Variables:
;; indent-tabs-mode: nil
;; byte-compile-warnings: (not cl-functions obsolete)
;; End:

その他

なんか見づらいな〜と思ってたら、フォントの設定を忘れてた。

設定の仕方は、シンプルに

(setq default-frame-alist
      '(
        (font . "Cica 16")))

って感じ。

あと、キーバインドの設定も忘れずに。

(leaf-keys (("C-h"     . backward-delete-char)
            ("C-c M-a" . align-regexp)
            ("C-c ;"   . comment-region)
            ("C-c M-;" . uncomment-region)
            ("C-/"     . undo)
            ("C-C M-R" . replace-regexp)
            ("C-c r"   . replace-string)
            ("<home>"  . beginning-of-buffer)
            ("C-c M-l" . toggle-truncate-lines)
            ("C-M-%"   . vr/query-replace)
            ("C-c v g" . magit-status)
            ("C-c m e" . emoji-cheat-sheet-plus-insert)))

やってみた感想

起動がめっちゃ早くなった。バイトコンパイルの影響もあるのかな?

当たり前だけど、必要なパッケージだけを入れたので、シンプルになった。

個人的に leaf.el のいいな〜と思った点は、 init.el に設定を詰め込んでも leaf-tree-mode で見たい設定のところまで飛べるところ。 大変便利だった。

まだまだ lsp のところが不安。

リモートワークなので tramp-mode を活用したいが、lsp over tramp-mode がどうなるのか分からなくて、そこでまた色々苦戦しそうだなと思っている。


今の Emacs 事情は、viper とか使わないなら変にディストリを使わなくてよくなったなあと思う。

leaf.el 、偉大だなぁ。