2015年を振り返る
ある日, 古い Emacsパッケージを使おうとしたとき全然動かなくて, 動くための PRを送ったんですが, そのとき他のも動くのかなと古いやつを片っ端からダウンロードし, 試してみたらいろいろ問題が見つかり PRしまくっていました. それで妙に草が生えてしまっています. それに伴い githubのリポジトリ数も 1000を超えてしまうということになってしまいました.
あとはひたすらメンテナンスですかね. 今年は新しい物を作らなすぎたと思います. 最後の方は Emacs dynamic moduleのテストのためにあれこれ書いてみましたが, どれも中途半端な感じになってしまいました. 来年は新しいものを作れるようにしたいですね.
自分のやる気のなさが原因としか言えませんが, 早い内になんとかしたいです.
参考
ejectで学ぶ Dynamic module機能
Dynamic module機能は 2015年 12月 12日現在開発中の機能です(だいぶ落ち着いてはきていますが). なので以下の内容は最新版では正しくない可能性があります. 動かない場合等はソースコード及び modules以下に含まれるテストコードを確認してみてください.
リポジトリ
https://github.com/syohex/emacs-eject
リビジョンは 20151212 とします.
ヘッダファイル
関連するヘッダファイルは emacs-module.hだけです. Cファイルではこのヘッダのインクルードが必須です. その他は実装するものに応じて適宜 includeしてください.
型
emacs-module.hに定義される型で重要になるのが, emacs_envと emacs_valueです.
emacs_env
Version 25ではその実体は struct emacs_env_25
です. フィールドはほとんど関数ポインタで, Emacs Lisp側から渡された値を Cの型に変換したり, その逆を行うときに必要となります. データのやりとり, 返還等でわからなくなったらとにかくこの構造体のフィールドにそれらしいものがないか確認すると良いでしょう.
C言語側で Emacs Lispから渡された整数値を受け取るときは以下のようになります.
// envは emacs_env*型, eint_valは emacs_value型 intmax_t cval = env->extract_integer(env, eint_val);
逆に C言語側から Emacs Lisp側に整数値を返したい場合は以下のようになります.
// envは emacs_env*型, cint_valは int型. emacs_value eval = env->make_integer(env, (intmax_t)cint_val);
このように C <=> Emacs Lisp間での値の変換はすべて emacs_env型を使って変換します.
emacs_value
Emacs Lispでの値を表現する型です. Emacs Lisp側から渡ってきた値はすべて emacs_value
型です. また C言語から Emacs Lisp側に値を返すときは emacs_value
型に変換する必要があります.
コードを見ていく
ライセンスの宣言
https://github.com/syohex/emacs-eject/blob/20151212/eject.c#L28
コードが GPL互換のライセンスでライセンスされていないとリンクできません. なのでそのことを示すためのシンボル plugin_is_GPL_compatible
を宣言します.
追記
GPLでなく, GPL互換のライセンスでした. 失礼しました.
エントリポイント
https://github.com/syohex/emacs-eject/blob/20151212/eject.c#L55-L56
各モジュールでエントリポイントなる関数は emacs_module_init
です. ここで C言語の関数の登録, パッケージの登録を行います.
関数の登録
関数の登録の流れは以下のとおりです.
- C言語の関数を関数オブジェクト(
emacs_value
型)に変換 fset
関数を呼び出し, Emacs Lisp側で利用する関数名と1.
で作成した関数オブジェクトを関連付ける
それでは順を追って見ていきましょう.
https://github.com/syohex/emacs-eject/blob/20151212/eject.c#L60
emacs_value ejectfn = env->make_function(env, 0, 1, Feject, "Control CD tray", NULL);
make_function
の引数は以下の通りです.
make_functionに渡せる C言語関数のシグネチャは以下のとおりです.
emacs_value func(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data);
引数はそれぞれ以下のとおりです.
emacs_env*
型変数- 引数の個数
- 引数
- 関数登録時に設定した引数
https://github.com/syohex/emacs-eject/blob/20151212/eject.c#L62-L65
次に作成した関数を登録します. 登録には fset
関数を使います. ここで C言語から Emacs Lisp側の関数を呼び出す必要があります. C言語側から Emacs Lisp側の関数を呼び出す方法はひとつだけで, シンボルを internし, 引数を設定, それらを使い funcall
を呼び出します.
// (fset 'eject ejectfn)と等価 emacs_value Qfset = env->intern(env, "fset"); emacs_value Qeject = env->intern(env, "eject"); emacs_value fset_args[] = { Qeject, ejectfn }; env->funcall(env, Qfset, 2, fset_args);
これで関数の登録ができました. (eject)
と呼び出せば, C言語の関数を呼び出すことができます.
ロードできるようにする
これで関数は呼び出せる準備は整いましたが, ロードできないので provide
を呼び出し, require
でロードできるようにします. これも fset
と同じ流れで provide
呼び出します.
https://github.com/syohex/emacs-eject/blob/20151212/eject.c#L67-L69
// (provide 'eject) と等価 emacs_value Qprovide = env->intern(env, "provide"); emacs_value provide_args[] = { Qeject }; // 先ほど作成した 'eject env->funcall(env, Qprovide, 1, provide_args);
これで require
を使ってライブラリをロードできるようになりました.
関数の登録, provideは定形なので, Emacs本体の modules/以下にあるサンプルコードではDEFUN
マクロや provide
関数が定義されています. (それも面倒なので本体側に何か用意して欲しいところだが...)
eject本体
本体は以下の通りで, 戻り値の部分を除き普通の Cです. 戻り値は emacs_value
である必要があるので, emacs_env
の適切な関数フィールドを使い変換します. ここでは成功か失敗かだけわかればいいので, シンボルを返しています.
static emacs_value Feject(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data) { int fd = open("/dev/cdrom", O_RDONLY|O_NONBLOCK); if (fd < 0) { perror("open /dev/cdrom"); return env->intern(env, "nil"); } int cmd = CDROMEJECT; if (!env->is_not_nil(env, args[0])) { if (ioctl(fd, CDROM_DRIVE_STATUS, 0) == CDS_TRAY_OPEN) cmd = CDROMCLOSETRAY; else cmd = CDROMEJECT; } if (ioctl(fd, cmd, 0) < 0) { perror("ioctl"); return env->intern(env, "nil"); } return env->intern(env, "t"); }
おわりに
理解を深めるためにもっとコードを書いていこうと思います.
Suppress warning of exec-path-from-shell
Sorry I removed old entry about this thing by mistake. This entry is updated version of it.
Add following configuration in your init.el. Then exec-path-from-shell does not warn about environment variables.
(custom-set-variables '(exec-path-from-shell-check-startup-files nil))
Recently exec-path-from-shell warn if environment variable in interactive mode is different from them in non-interactive mode. I suppose this is right. We should set environment variables in .bash_profile
or .zshenv
, not .bashrc
, .zshrc
. However this is not easy for some configurations such as rbenv. rbenv initialization eval 'rbenv init -'
generates some environment variables setting and function definitions. We should not define functions in .bash_profile
, .zsh_profile
so I think such setting should not be moved. And I want to keep shell configuration in only one file :-)
color-theme-modern
I ported all color-theme themes(over 100 themes) to Emacs theme framework 2 years ago. Now those themes are available on MELPA. You can download themes with package.el. Package name is color-theme-modern.
Repository
All sreenshots are here
https://github.com/emacs-jp/replace-colorthemes
Installation
All themes are available on MELPA and MELPA stable
You can install color-theme-modern with the following command.
M-x package-install color-theme-modern
Enjoy Emacs themes.
Indent like python-mode in coffee-mode
https://github.com/defunkt/coffee-mode/pull/323
I have implemented indentation like python-mode in coffee-mode. You can enable this feature by setting coffee-indent-like-python-mode
to non-nil.
(custom-set-variables '(coffee-indent-like-python-mode t))
If you don't like default coffee-mode indent-command, please try this. And I suppose this is useful for Evil users. o
and O
commands works well as you expect with this indentation mode.
migemo対応の helm sourceを書く(helm-migemo不要)
昔 helmは migemo対応していたのですが(anything時代から), それが除去され, migemo機能を有効にする helm-migemoというパッケージが作成されさました. しかし最近 helmが再度 migemo対応しました. これにより helmだけで migemo対応の sourceが書けるようになりました. その手順を示します(作者の方が主導的に行われたので再度外される可能性はないとはいえませんが, 高くはないと思います).
バージョン
1.7.9以上を使う必要があります.
事前準備
migemoの設定を行い(以下は cmigemo), helm-modeを有効にします. (NOTE helm-migemo-modeは autoloadできないので, 事前に helmをロードしています)
(require 'migemo) (setq migemo-command "migemo") (setq migemo-options '("-q" "--emacs")) ;; Set your installed path (setq migemo-dictionary (expand-file-name "/usr/local/share/migemo/utf-8/migemo-dict")) (setq migemo-user-dictionary nil) (setq migemo-regex-dictionary nil) (setq migemo-coding-system 'utf-8-unix) (migemo-init) (require 'helm) (helm-migemo-mode +1)
サンプル
source作成マクロで migemo
属性を non-nilに設定します.
(defvar sample-source (helm-build-sync-source "Sample helm-migemo" :candidates '("りんご" "みかん" "メロン") :action 'message :migemo t)) (defun sample-helm-migemo () (interactive) (helm :sources '(sample-source) :buffer "*helm-sample*"))
イメージ
上記の実行結果(M-x sample-helm-migemo)は以下のようになります.
Delete line in edit mode
I have implemented delete line feature in helm-ag edit mode. We can put delete line mark by C-c C-d
, and remove delete mark by C-c C-u
by default. Marking some lines and commit(C-c C-c
by default), then these lines are deleted.
Please report me if you have issues or suggestions via github issues