Hatena::Groupcadr

kozima の日記

2011-04-03

emacs から StumpWM の eval を呼ぶ

| 11:28

.stumpwmrc を編集しているときに,とりあえず書いてみたものを評価したいと思うことがあったのでできるようにしました。

文字列を投げつける部分

stumpish を使うとよいようなので shell-command で呼び出してみました。結果は短ければミニバッファに,長ければ *Shell Command Output* バッファに現れるようです。

(defun mylib-stumpwmrc-eval-region (start end)
  (interactive "r")
  (let ((string (buffer-substring-no-properties start end)))
    (shell-command (format "echo %S | stumpish -e eval"
                           (format "(progn %s)" string)))))

(defun mylib-stumpwmrc-eval-last-sexp ()
  (interactive)
  (save-excursion
    (let ((start (progn (backward-sexp 1) (point)))
          (end (progn (forward-sexp 1) (point))))
      (mylib-stumpwmrc-eval-region start end))))

あんまりテストしてないのですが,C-x C-e で直前の式の評価, C-c C-r でリージョンの評価ができるつもり。

モード作った

わざわざ M-x からコマンド名を打ち込むのも手間なので,おざなりながらモードを作りました。

最初はマイナーモードとして作っておいて lisp-mode-hook に引っかければいいと思ったんですが,それだと slime とキーバインディングが衝突しました。slime-mode を呼ぶかどうかをファイル名を見て判断するように修正するとか,slime-mode の後で呼ばれるように順序を調整するという手も考えられますが,それもあんまりきれいな解決方法じゃない気がするし,それよりもメジャーモードにしてしまうのが早いだろうということでメジャーモードに。

(defvar mylib-stumpwmrc-mode-map (make-sparse-keymap))
(define-key mylib-stumpwmrc-mode-map "\C-x\C-e" 'mylib-stumpwmrc-eval-last-sexp)
(define-key mylib-stumpwmrc-mode-map "\C-c\C-r" 'mylib-stumpwmrc-eval-region)

(defun mylib-stumpwmrc-mode ()
  (interactive)
  (let ((lisp-mode-hook nil))
    (lisp-mode))
  (use-local-map mylib-stumpwmrc-mode-map))

(add-to-list 'auto-mode-alist '("\\.stumpwmrc$" . mylib-stumpwmrc-mode))

r_takaishir_takaishi2011/05/02 08:51StumpWMでswankサーバを起動して,slimeで接続すればいいと思うのですが,何か問題があるのでしょうか.もしあるなら教えて頂きたいです.

2011-04-02

StumpWM で起動時にウィンドウの配置を復元したい

| 15:19

起動してからグループを作って分割してウィンドウを移動させて,という作業が面倒なので勝手にやってくれないかなーという試み。やりたいことは http://cadr.g.hatena.ne.jp/g000001/20100507/1273145273 とだいたい同じです。

グループの分割状態の復元

これは g000001 さんと同じですが,

dump-desktop .stummpwm.d/default-desktop

などとやってファイルに書き出しておいて,restore-from-file で復元します。

ところで restore したときに Default っていうグループがファイルに書いてなくても消えない気がするんですが,そういうものでしょうか。

ウィンドウの場所の復元

本当は前回の終了時に起動していたものをあった場所に置くのがよいのだと思いますが,どうやればできるかすぐにはわかりそうになかったので,とりあえず代替手段を探していたら Rule Based window Placement というのがマニュアルにあったので試してみることに。

特定の条件を満たすウィンドウが,指定したグループの指定したフレームに送られるようにするみたいです。適当なファイルに

;; (group-name frame-number raise lock ...)

(("Default" 0 NIL T :CLASS "Emacs")

("Default" 1 NIL T :CLASS "Gnome-terminal")

("Default" 1 NIL T :CLASS "Firefox")

("skype" 0 NIL T :CLASS "Skype")

)

とか書いて restore-window-placement-rules で設定します。:CLASS にプログラム名を capitalize して指定すればうまくいくことが多いみたいです。

ウィンドウの振り分けルールを設定するだけなので,自動でいろんなのが立ち上がったりするわけではありません。

まとめ

全体の流れとしては,

  1. dump-desktop する (下の例では .stumpwm.d/default-desktop へ出力)
  2. window placement rule を書く (下の例では .stumpwm.d/default-placement に記述)
  3. .stumpwmrc で restore する

という感じになります。

ということで,.stupwmrc にこんな設定を追加してみました。フックを使ってるのは :loadrc したときには何もしないほうがいいかなと思うので。

(defun stumpwm-default-startup ()
  (restore-from-file (data-dir-file "default-desktop"))
  (restore-window-placement-rules (data-dir-file "default-placement"))

  ;; 必要に応じていろいろする
  (run-shell-command "xrdb ~/.Xresources")
  (run-shell-command "gnome-settings-daemon")
  (run-shell-command "gnome-terminal")
  ;; (run-shell-command "firefox")
  ;; (run-shell-command "emacs")
  ;; (run-shell-command "skype")
  )

(add-hook *start-hook* 'stumpwm-default-startup)

詳しいことはわからない部分もあるのですが,とりあえず適当にやったらそれっぽく動くようになった感じです。もっと頑張って調べれば,よりいい感じになるのかもしれません。

2011-02-25

最近あなごるを見てて思うこと

| 10:13

anarchy golf - Numbering などを見て,だんだん狂気の世界に足を踏み入れ始めたなあ,などと思っています。

それにしても,この問題に限らず,別々の人が独立にほとんど同じ方針を立てているというのは,ちょっと不思議な感じがします。見かけの普通でないコードがその感想を強めるのでしょうか。

2011-02-23

FizzBuzz 84bytes

| 19:46

特にこのタイミングである意味はないんですけど,そのうち晒そうと思って忘れてたのを思い出したので。

(dotimes'101(format(> .'0)"~[Fizz~[Buzz~]~:;~[Buzz~:;~A~]~]
"(rem .'3)(rem .'5).'t))

2010-12-16

map vs. loop

| 00:11

xyzzy の session.l の一部を elisp で書いてみたのですが,そのときに感じたこと。どうも複雑な処理は無名関数にして map するより loop で書いたほうが短いし楽だし読みやすい(気がする)。

ということで具体的にコードを並べてみることにしました。

session.l は,開いていたファイルの情報やバッファのポイント位置,ウィンドウの状態などをファイルに書き出しておいて後で復元する機能を提供しています。その中から,現在開いているファイルの情報を集める部分と,それを復元する部分のコードを比べてみます。

まず xyzzy のソースから。

(defun list-buffer-info ()
  (save-excursion
    (let ((info nil))
      (mapc #'(lambda (buffer)
                (when (file-visited-p buffer)
                  (set-buffer buffer)
                  (push (list (get-buffer-file-name buffer)
                              (point)
                              (cons buffer-mode
                                    (mapcan #'(lambda (mode)
                                                (let ((var (and (consp mode) (car mode))))
                                                  (and (symbolp var)
                                                       (boundp var)
                                                       (symbol-value var)
                                                       (list var))))
                                            *minor-mode-alist*))
                              (mapcar #'(lambda (var)
                                          (and (symbolp var)
                                               (local-variable-p var)
                                               (cons var (symbol-value var))))
                                      *buffer-info-variable-list*))
                        info)))
            (buffer-list :buffer-bar-order t))
      (nreverse info))))

(defun restore-buffer-info (info)
  (let ((obuffer (selected-buffer)))
    (mapc #'(lambda (i)
              (let ((file (pop i)))
                (when (file-exist-p file)
                  (handler-case
                      (let ((point (pop i))
                            (mode (pop i))
                            (minor nil))
                        (when (listp mode)
                          (setq minor (cdr mode))
                          (setq mode (car mode)))
                        (let ((*find-file-auto-mode-function* mode))
                          (find-file file))
                        (goto-char point)
                        (mapc #'(lambda (f) (and (fboundp f) (funcall f))) minor)
                        (mapc #'(lambda (x)
                                  (when (and (car x) (symbolp (car x)))
                                    (make-local-variable (car x))
                                    (set (car x) (cdr x))))
                              (pop i)))
                    (file-error (c)
                      (si:*print-condition c))))))
          info)
    (set-buffer obuffer)))

エラーチェックの部分を削ってますが,基本的に同じことをやる elisp のコード。ついでにちょっとコメント入り。

(defun mylib-session-list-buffer-info ()
  (loop for buffer in (buffer-list)
        as file = (buffer-file-name buffer)
        if file collect
        (with-current-buffer buffer
          (list file
                (point)
                ;; collect minor mode info
                (loop for mode in minor-mode-alist
                      as var = (and (consp mode) (car mode))
                      if (and (symbolp var) (boundp var) (symbol-value var))
                      collect var into vars
                      finally (return (cons major-mode vars)))
                ;; collect local variable info
                (loop for var in mylib-session-buffer-info-variable-list
                      if (and (symbolp var) (local-variable-p var))
                      collect (cons var (symbol-value var)))))))

(defun mylib-session-restore-buffer-info (info)
  (save-excursion
    (loop for (file point (mode . minor) local-vars) in info do
          (if (file-exists-p file)
              (ignore-errors
                ;; open visited file,
                ;; (find-file-noselect file)
                (find-file file)   ; 修正 (2010/12/17)
                (when (and (fboundp mode) (not (eq mode major-mode)))
                  (funcall mode))
                ;; move point to the original place,
                (goto-char point)
                ;; restore minor modes,
                (dolist (f minor) (if (fboundp f) (funcall f)))
                ;; and local variables
                (loop for (var value) in local-vars do
                      (make-local-variable var)
                      (set var value)))
            (message "file `%s' does not exist!" file)
            (sit-for 0.5)))))

map 系は無名関数(特に大きめのもの)と一緒に使うとあまり読みやすくない,ということは以前から思っていましたが,それが顕著に表れた気がします。たぶん,第二引数が離れてしまうせいで「何に対して map するのか」がぱっと見てわかりづらいことが,読みにくさを感じさせる一因になっているのではないかと思います。

可読性ともいくらか関係しますが,コードの行数もやや多くなりがちです。lambda で無名関数を書くと,情報量のわりに場所をとる気がします。特に無名関数がよく使われる map の第一引数のようなところでは,lambda と書いてあってもテンプレート通りという感じで,それほど情報量が多くないのではないでしょうか。そういうところに場所をとるものがあると,余計にコードサイズがかさんでいるように感じられるのかもしれません。

それからもう一つありました。map に限らず,高階関数に無名関数を渡すというスタイルは,インデントが深くなりやすいですね。これは可読性にはそれほど関係しないような気もしますが,書いているときにだんだん右に寄ってくるとなんとなく窮屈に感じます。