Junk emacs lisp

あまり役に立たない emacs lisp のコード。あるいは、新しいコードを書いて obsolete になったコード。


Contents


.emacs.el のバイトコンパイルをし忘れる

.emacs.el が大きくなってくると、バイトコンパイルしてちょっとでも速くしたくなってきますが、Emacs は初期設定ファイルを.emacs.elc → .emacs.el OR .emacsの順に調べるので、.emacs.el を変更しても(バイトコンパイルし忘れて)変更が反映されない、てなことが起きます。

というわけで、

;; Emacs-lisp モードで M-s でバイトコンパイルできるようにキーマップを設定
(add-hook 'emacs-lisp-mode-hook
          (function
           (lambda ()
             (local-set-key "\M-s" 'emacs-lisp-byte-compile))))

とかしてみたくなりますが、それでも忘れるときには忘れるので、終了時に自動バイトコンパイルするように設定([meadow-users-jp: 3800] Re: Meadow 終了時に byte-compile

(add-hook 'kill-emacs-query-functions
          (function
           (lambda ()
             (if (file-newer-than-file-p "~/.emacs.el" "~/.emacs.elc")
                 (byte-compile-file "~/.emacs.el"))
             t)))

してみたり、起動時に自動バイトコンパイルするように設定

(if (file-newer-than-file-p "~/.emacs.el" "~/.emacs.elc")
    (save-excursion
      (byte-compile-file "~/.emacs.el")))

してみたり、保存時に自動バイトコンパイルするように設定

(add-hook 'after-save-hook
          (function (lambda ()
                      (if (string= (expand-file-name "~/.emacs.el")
                                   (buffer-file-name))
                          (save-excursion
                            (byte-compile-file "~/.emacs.el"))))))

します。追記: 保存時に自動バイトコンパイルするように設定する場合、emacs lisp なら全部バイトコンパイルするようにするには、上の代わりに以下のように書けば良いでしょう。

(add-hook 'after-save-hook
          (function (lambda ()
                      (if (eq major-mode 'emacs-lisp-mode)
                          (save-excursion
                            (byte-compile-file buffer-file-name))))))

バッファ移動を簡単に

バッファ移動を簡単に,C-,(降順)とC-.(昇順)で出来るようにします。

(defun my-select-visible (blst n)
  (let ((buf (nth n blst)))
    (cond ((= n (- (length blst) 1)) (other-buffer))
          ;; bufferを一つしか開いていなければ other-buffer に任せる
          ((not (= (aref (buffer-name buf) 0) ? )) buf)
          ;; mini-bufは要らない
          (t (my-select-visible blst (+ n 1))))))

(defun my-grub-buffer ()
  (interactive)
  (let ((blst (if (< emacs-major-version 20)
                  (reverse (buffer-list))
                (reverse (cdr (assq 'buffer-list (frame-parameters)))))))
    ;; frameのbuffer-listってどうやって返すんだっけ?
    (switch-to-buffer (my-select-visible blst 0))))

(global-set-key [?\C-,] 'my-grub-buffer)
(global-set-key [?\C-.] 'bury-buffer)

Emacs-19ベースのmuleは(frame-parameters)にbuffer-listがないので(buffer-list)を使っています。これだと*Help*とかにいかないようにできないのが悲しい。そこで少し拡張したもの。特殊バッファで移動しないものは、my-ignore-buffer-list に並べてください。

(defvar my-ignore-buffer-list
  '("*Help*" "*Compile-Log*" "*Mew completions*" "*Completions*"
    "*Shell Command Output*" "*Apropos*" "*Buffer List*"))

(defun my-visible-buffer (blst)
  (let ((bufn (buffer-name (car blst))))
    (if (or (= (aref bufn 0) ? ) (member bufn my-ignore-buffer-list))
        (my-visible-buffer (cdr blst)) (car blst))))

(defun my-grub-buffer ()
  (interactive)
  (switch-to-buffer (my-visible-buffer (reverse (buffer-list)))))

(defun my-bury-buffer ()
  (interactive)
  (let ((nbuf (my-visible-buffer (cdr (buffer-list)))))
    (bury-buffer)
    (switch-to-buffer nbuf)))

(global-set-key [?\C-,] 'my-grub-buffer)
(global-set-key [?\C-.] 'my-bury-buffer)

こんがらがったバッファをソート

色々なファイルを編集しているとバッファリストの順番がこんがらがっていてわけが分からなくなります。上のバッファ移動を使っても根本的な問題は解決されないので、ここでは今開いているバッファの順番を名前順でソートしてみます。

(defun my-rotate-buffer (cbuf slst)
  ;; bury-bufferを使って、カレントバッファが先頭に来るように回転
  (cond ((eq cbuf (car slst)) t)
        (t (bury-buffer (car slst))
           (my-rotate-buffer cbuf (cdr slst)))))

(defun my-sort-buffer-by (x y) ; 特殊バッファ→通常バッファでファイル名順
  (if (and (buffer-file-name x) (buffer-file-name y))
      (string< (buffer-file-name x) (buffer-file-name y))
    (string<< (buffer-name x) (buffer-name y))))

(defun my-sort-buffer ()
  "Sort buffer list by name"
  (interactive)
  (let ((cbuf (current-buffer))
        (slst (sort (buffer-list) 'my-sort-buffer-by)))
    (message (prin1-to-string slst))
    (my-rotate-buffer cbuf (append (cdr (memq cbuf slst)) slst))))

それほど使用頻度は高くないので、キーバインディングは割り当てません。M-x my-sort-bufferとでもしてください。ソートの関数を増やせば色々な基準でのソートができますが、もっと複雑なことがしたければ ibuffer.el を使うと良いでしょう。

keyboard-quit で連続実行する種類のコマンドの結果を元に戻す

連続して実行する種類のコマンド(yank(-pop)/dabbrev-expand/undo/my-grub-buffer など)を実行していると、ふとそれらのコマンドを最初に実行したときの状態に戻りたくなることがあります。例えば、yank (C-y) の後の yank-pop (M-y) は便p利なコマンドですが、コピーしたと思っていた文字列を実はコピーし忘れているときがあります。しかし、コマンド実行中のような気分で keyboard-quit (C-g) しても yank していたテキストが消えず、また元に戻す気分で undo (C-/) しても前の yank-pop が表示されるのが始末が悪いです。これは dabbrev-expand のときも同じです。さらに、yank-pop してるときには、カットした文字列を保存しているリストである kill-ring の先頭から、次の候補を順に取り出す(kill-ring-yank-pointer を変更することで実現されている)わけですが、keyboard-quit した時に kill-ring-yank-pointer の状態が元に戻らないため、最初に yank した時の候補に戻ってくれません。

そこで、yank-pop の定義を参考に、keyboard-quit を wrap して yank(-pop)/dabbrev-expand の後なら挿入されたテキストを消すようにしてみました。さらに、yank 時に kill-ring-yank-pointer を保存しておき、keyboard-quit の時にリストアするようにして、完全に「yank/dabbrev-expand 前の状態」に戻れるようにしました。

追記: undo していてやっぱりいいやという時にも undo し始めたときに戻してほしいので、redo が読み込まれていたら redo できるだけするようにしてみました。

;; dabbrev-expand 用 marker
(defvar my-dabbrev-marker nil)
(defadvice dabbrev-expand-by-category (before my-mark activate)
  (unless (eq last-command 'dabbrev-expand)
    (setq my-dabbrev-marker (point))))

;; kill-ring-yank-pointer の backup 用の変数
(defvar my-kill-ring-yank-pointer-backup nil)
;; yank するときには kill-ring-pointer の位置を覚えておく
(defadvice yank (before my-kill-ring-yank-pointer-backup activate)
  (setq my-kill-ring-yank-pointer-backup kill-ring-yank-pointer))

(defun my-keyboard-quit ()
  "Wrapped keyboard-quit for yank(-pop)/dabbrev-expand/undo.
   This command executes keyboard-quit, it deletes the inserted text when
   the last-command is yank(-pop) or dabbrev-expand, recovers
   kill-ring-yank-pointer when yank(-pop), and repeats redo as possible."
  (interactive)
  (cond ((eq last-command 'yank)
         (let ((inhibit-read-only t))
           (delete-region (point) (mark t)))
         ;; yank/yank-pop したテキストを消す
         (setq kill-ring-yank-pointer my-kill-ring-yank-pointer-backup)
         ;; kill-ring-yank-pointer の位置を yank 前に戻す
         )
        ((eq last-command 'dabbrev-expand)
         ;; dabbrev-expand したテキストを消す
         (delete-region (point) my-dabbrev-marker))
        ((and (featurep 'redo) (eq last-command 'undo))
         ;; undo している最中なら redo できるだけする
         (while 1 (redo 1))))
  (keyboard-quit))

これで、やっと願っていた通りの振る舞いをしてくれるようになりました。

kill-ring はテキスト属性を保存しなくていい

コピーした文字列は emacs の kill-ring と呼ばれる循環リストに保存されますが、テキスト属性(色情報など)も一緒についてきます。例えば、メールで、コメント文をコピーするとそのまま色がついていて気味が悪いです。そこで、テキスト属性をコピーしないようにします。

(defadvice kill-new (around my-kill-ring-disable-text-property activate)
  (let ((new (ad-get-arg 0)))
    (set-text-properties 0 (length new) nil new)
    ad-do-it))

フレームのサイズと位置を初期設定に戻す

Meadow/Emacs を使っていると、フレームのサイズを時々変更することがあるのですが、ときどき元のサイズに戻したくなります。そういうときには、

(defun resize-frame-to-default ()
  (interactive)
  (let ((width (cdr (assq 'width default-frame-alist)))
        (height (cdr (assq 'height default-frame-alist)))
        (left (cdr (assq 'left default-frame-alist)))
        (top (cdr (assq 'top default-frame-alist))))
    (set-frame-size (selected-frame) width height)
    (set-frame-position (selected-frame) left top)))

みたいに書いて、M-x resize-frame-to-defaultなどとやれば良いでしょう。

yank 時に次の yank 候補を minibuffer に表示

yank (C-y) のあと yank-pop (M-y) でコピーする候補を切り替えていると、今表示している buffer 中で yank した文字列が切り替わるので既存の buffer 内容と混ざってごちゃごちゃになってどうも気持ち悪くて慣れません。browse-kill-ring.elyank-pop-summary.el でもいいんですが、個人的には minibuffer に yank 内容が出るだけでいいや、と思ったので出力するようにしてみました。

ここでは、yank-pop の逆がやりにくいことも考慮して(勢い行き過ぎることがある)、yank した内容自体ではなく次の yank 候補を表示しています。

(defun my-yank-display ()
  (unless (or (eq kill-ring-yank-pointer nil)
              ;; kill-ringが空だったり
              (= (aref (buffer-name) 0) ? ))
    ;; minibuf で yank しようとしていなければ
    (if (eq (cdr kill-ring-yank-pointer) nil)
        (message "end of kill-ring")
      (message (car (cdr kill-ring-yank-pointer))))))

(defun my-yank (arg)
  "Yank with displaying next"
  (interactive "*P")
  (yank arg)
  (my-yank-display)
  (setq this-command 'yank))

(defun my-yank-pop (arg)
  "Yank-pop with displaying next"
  (interactive "*p")
  (yank-pop arg)
  (my-yank-display)
  (setq this-command 'yank))

(global-set-key "\C-y" 'my-yank)
(global-set-key "\M-y" 'my-yank-pop)

Emacs20以前な場合は、(resize-minibuffer-mode)でminibufferを可変にしておくと良いかもしれません。と思ったけど、改行が^Jになってしまうなあ。

対応する括弧にジャンプ

対応する括弧にジャンプするコマンドは forward-list (M-C-n)backward-list (M-C-p) がありますが、これらは実際には交互に使う(対応する括弧のあたりを眺めて戻ってくる)ことが多いです。そこで交互にコマンドを打ちかえる手間を省くために、EMACS FAQに載っていたのを少しいじって現在のメジャーモードでの括弧類全てでジャンプするようにしたもの。コメントアウトしてある部分を外せば(全ての % で処理が走るのが気持ち悪いものの) % でジャンプします。閉じ括弧と開き括弧が連続している場合は閉じ括弧優先。

;; コメント行はスキップ
(setq parse-sexp-ignore-comments t)

(defun my-match-paren (&optional arg)
  "Go to the matching parenthesis if on parenthesis."
  (interactive "p")
  (or arg (setq arg 1))
  (cond ((and (not (bobp)) (equal 41 (char-syntax (char-before))))
         (backward-list arg))
        ((and (not (eobp)) (equal 40 (char-syntax (char-after))))
         (forward-list arg))
        (t (self-insert-command arg))))

(global-set-key "\C-x%" 'my-match-paren)
;; (global-set-key "%" 'my-match-paren)

プログラムでデバッグのprint文を挿入

プログラムを書いているとよく、デバッグのために print 文を挿入しますが、その時に、どこで出力したかをメモするために identifier を入れることがあります。ここではそのような identifier として print 文のある行数を採用して、行数を出力する print 文を挿入する emacs lisp を作ってみました。

;; 各 progmodes (major-mode) の print 文のフォーマット
;; (mode-name . (BEGIN  . END))
(defvar print-format-alist
  '((c++-mode . ("std::cerr << \"" .  "\\n\";"))
    (emacs-lisp-mode . ("(message \"" . "\")"))
    (lisp-interaction-mode . ("(message \"" . "\")"))))

(defun my-print-debug ()
  "Insert print-sentence with line number and file name"
  (interactive)
  (let* ((format-pair (assq major-mode print-format-alist))
         (format-begin (cadr format-pair))
         (format-end (cddr format-pair)))
    (cond ((eq (buffer-file-name) nil)
           (message "save buffer with a file name"))
          ((or format-begin format-end)
           (insert (concat
                    format-begin
                    "line: "
                    (number-to-string
                     (or (and (bolp) (+ (count-lines (point-min) (point)) 1))
                         (count-lines (point-min) (point))))
                    " in "
                    (file-name-nondirectory (buffer-file-name)))
                   format-end
                   "\n"))
          (t (message (concat "no format for " (prin1-to-string major-mode)))))))
(global-set-key [?\C-\;] 'my-print-debug)

print-format-alist に好きなように追加すれば任意の major-mode に対応させることが出来ます。

ispell で LaTeX 文書などを校正するときに固まらないようにする

ispell で LaTeX で書いた論文などの校正をしていると結構固まります。これは、ispell に長すぎる文字列を渡すときに起きる問題らしいです。段落中は改行しないポリシーというわけでないのであれば(単にいちいち ESC-q: fill-region するのが面倒なだけなのであれば)、以下のようにispell-buffer に入る前に適当に折り返しておいてやれば良いでしょう。まぁ、auto-fill で書いているそばから改行させればいいんですが、書いてるときは煩いんだよなぁ。

(defadvice ispell-buffer (before my-text-fill-for-ispell activate)
  (let ((justify nil))  ;; latex-mode で文章の右端を揃えたければ t
    (cond ((eq major-mode 'latex-mode)
           ;; latex-mode では LaTeX-fill-buffer
           (LaTeX-fill-buffer justify))
          ((eq major-mode 'mew-draft-mode)
           ;; mail-draft ではヘッダ部は整形しない
           ;; というか素直に isearch-region 使えという話もあり。
           (save-excursion
             (goto-char (point-min))
             (search-forward-regexp "^----$")
             (fill-region (point) (point-max) justify)))
          (t (save-excursion
               (fill-region (point-min) (point-max) justify))))))

[Emacs 21] ミニバッファでプロンプトの扱いがおかしい

Emacs 21 ではミニバッファのプロンプトがクリッカブルになっていて、プロンプトの上にもカーソルを合わせる事は出来ますが、デフォルトでは beginning-of-line はプロンプトの直後に移動してくれるようになっています。しかし、emacs をカスタマイズしているうちに、気がついたら、C-a で移動するとプロンプトの先頭にマウスが移動して嫌な感じな人いませんか。調べてみたところ、physical-line の仕様でこういうことにになってしまうようです。というわけで、physical-line を読み込んで↑のようなことになってイライラしている人向け。

;; ミニバッファのプロンプトの属性。デフォルトでは (read-only t) のみ。
;; これで emacs 20 と同じになる
(setq minibuffer-prompt-properties
      '(read-only t point-entered minibuffer-avoid-prompt))
    

ミニバッファだけ physical-line をオフに出来ればいいのですが、そういう機能は無いようなので、ミニバッファの beginning-of-line をさらに別関数で置き換えるのは、以下のようにしておけばよいでしょう。

(defun beginning-of-minibuffer ()
  (interactive)
  (goto-char (minibuffer-prompt-end)))

(when (= emacs-major-version 21)
  (add-hook 'minibuffer-setup-hook
            (lambda () (local-set-key "\C-a" 'beginning-of-minibuffer))))

素直に、physical-line に手を加えるなら

(defun physical-line-active-p ()
  (and (not (eq physical-line -1))
       (or  physical-line
            physical-line-mode)
       (null (window-minibuffer-p (selected-window))) ;; <- この行を追加
       (null (member major-mode physical-line-ignoring-mode-list))))

追記: physical-line が beginning-of-line をいじっているのが問題なことが分かったので文章差し替え。

複数のネットワーク環境で Meadow を使いたい

しょうもない tips ですが、職場では unix のファイルサーバを HOME として、それ以外ではローカルに HOME を持つなど、複数のネットワーク上で Emacs を使いたいときには、基本的にはローカルにデータを全て持つことにします。

複数のネットワークで共有したいファイルとそうでないファイルがある場合は、まず、kill-emacs-hook を eval (C-x C-e) して、Emacs 終了時に呼び出される関数を調べて、それらの関数が HOME 以下に保存するファイルを調べます。ファイルが分かれば、各ファイルごとに、以下のような設定をしておけばよいです。

;; ローカルが windows の場合
(defconst running-on-windows (equal system-type 'windows-nt))

;; ミニバッファの履歴を保存する save-history
(when (locate-library "save-history")
  (load "save-history")
  (when running-on-windows
    (setq save-history-file "c:/cygwin/home/administrator/.emacs-histories")))

こうしておけば、Emacs の起動スピード自体も速くなります。

複数の home で session.el や tramp を使うためのファイル名変換

session は、開いたファイル名の履歴やコマンド履歴などを保存してくれるため、起動し直すたび同じコマンドやファイル名を打ち直さなくて良くなる、非常に便利な emacs lisp です。それなりに emacs を使いこなしている人なら、session は必ず入れていると思いますが、職場や家など、2つの異なる home で meadow を起動している人からすると、変数 file-name-history に保存されるファイル名の履歴がごっちゃごちゃに保存されるため、補完時に混乱します。そこで、file-name-history は基本的に絶対パスで保存し、読み込み・保存時に "~/" と環境変数 HOME で指定されるパスを相互変換する emacs lisp を書いてみました。

;; ファイル名を相互変換
(defun my-replace-file-name-history (dirs orig rep)
  (if (eq dirs '()) '()
    (let ((dir (car dirs)))
      (cons
       (if (string-match (concat "^" orig) dir)
           (replace-match rep t t dir) dir)
       (my-replace-file-name-history (cdr dirs) orig rep)))))

;; 相対パス -> 絶対パス e.g., ~/ -> c:/cygwin/home/administrator
(defun my-set-absolute-file-name-history ()
  (let ((home (expand-file-name (concat (getenv "HOME") "\\"))))
    (setq file-name-history
          (my-replace-file-name-history file-name-history "~/" home))))

;; 絶対パス -> 相対パス e.g., c:/cygwin/home/administrator -> ~/
(defun my-set-relative-file-name-history ()
  (let ((home (expand-file-name (concat (getenv "HOME") "\\"))))
    (setq file-name-history
          (my-replace-file-name-history file-name-history home "~/"))))

;; * after-init-hook より後で読み込まれる window-setup-hook で、
;;   file-name-history に含まれるファイル名を相対パスに変換する。
;; * session-initialize で kill-emacs-hook に追加される session 保存関数より
;;   前に、file-name-history に含まれるファイル名を絶対パスに変換する。
(add-hook 'window-setup-hook
          (lambda ()
            (my-set-relative-file-name-history)
      (add-hook 'kill-emacs-hook 'my-set-absolute-file-name-history)))

うまく動かない場合は、(expand-file-name (concat (getenv "HOME") "\\"))が、c:/cygwin/home/administrator/みたいな/で終わる HOME ディレクトリになっているか調べてください。起動してる最中に、(setenv "HOME" "...")などで、HOME を変更すると(普通はそんなことしないだろうけど)ファイル名が壊れるので注意。

tramp 使っているなら、tramp パスも自動変換したくなるかもしれません。その場合は以下を追加。これで家でも大学でもまったくストレス無くファイルが開けます。

;; tramp パスも変換する
;; tramp で接続するサーバにログインしているときのホーム
(defvar tramp-home "z:/")
;; tramp 経由でファイルを開くときのヘッダ
;; e.g. /yoshinag@camplin.is.s.u-tokyo.ac.jp:
(defvar tramp-header "/username@host:path")

;; tramp ヘッダつきファイルパス -> 元のホームから辿れるファイルパス
(defun my-set-absolute-tramp-file-name-history ()
  (setq file-name-history
        (my-replace-file-name-history
         file-name-history tramp-header tramp-home)))

;; 元のホームから辿れるファイルパス -> tramp ヘッダつきファイルパス
(defun my-set-relative-tramp-file-name-history ()
  (setq file-name-history
        (my-replace-file-name-history
         file-name-history tramp-home tramp-header)))

(add-hook 'window-setup-hook
          (lambda ()
            (my-set-relative-file-name-history)
            (add-hook 'kill-emacs-hook 'my-set-absolute-file-name-history)
            ;; 以下は tramp を使っている場合
            (unless (eq (expand-file-name (getenv "HOME")) "z:/") 
              (my-set-relative-tramp-file-name-history)
(add-hook 'kill-emacs-hook 'my-set-absolute-tramp-file-name-history))))

ファイル履歴の長さを長くしている場合は、(setq max-specpdl-size 1000) みたいな感じで関数の繰り返し処理の上限を上げないといけないかもしれない。

Cygwin などの外部のアプリと組み合わせて

開いているファイルのあるディレクトリを explorer で開く

バッファでファイルを開いている場合、そのファイルのあるディレクトリを explorer で開きたい場合があります。dired に慣れている人はそちらでも良いのですが、慣れていない人も多いと思います。そういう場合、

(defun my-explorer-open ()
          (interactive)
          (shell-command "explorer /e,.")
          )

          (global-set-key "\C-x\C-@" 'my-explorer-open)

これで C-x C-@ で explorer で今いるディレクトリを開けます。開くディレクトリを変更したければ、次のようにしておけば ok です。

(defun my-explorer-open ()
  (interactive)
  (shell-command
   (concat "start " ;; shell が cygwin bash なら cygstart
           (file-name-directory
            (read-file-name "open by explorer.exe: " default-directory)))))

(global-set-key "\C-x\C-@" 'my-explorer-open)

ディレクトリの取得に read-file-name を使っているのが気持ち悪いですが、補完は使いたいし、こういう場合どうするもんなんでしょうね。

LaTeX コンパイル簡略化 powered by latexmk

Emacs 上で LaTeX で論文を書いていると、変更結果をすぐコンパイルして preview したくなります。大規模なものなら Makefile を作ってM-x compileでもいいだろうし、そうでなくても AUC TeX とか YaTeX を使っていれば,何か他にも方法はあるのだと思いますが,latexmk を利用することにして,適当な関数を書いてみました。

(defun my-latex (tex)
  "Compile tex file using latexmk"
  (interactive "fTeX file: ")
  (let ((file (file-name-nondirectory tex)))
    (when (get-process "Shell")
      (delete-process "Shell"))
    (let ((latexmk "c:/cygwin/usr/local/bin/platexmk.prl")
          ;; オプション は適当に
          (optstr "-r /cygdrive/c/cygwin/home/administrator/.platexmkrc -ps -f -g -pv"))
      (save-window-excursion
        (shell-command
         (concat latexmk " " optstr " " file " &"))))
    (let* ((window (selected-window))
           (height (window-height (selected-window))))
      (when (> height 10) ;; 下10行にコンパイルログを表示
        (let ((output-window (split-window window (- height 10) nil)))
          (set-window-buffer output-window "*Async Shell Command*")
          ;; 10秒以内にログバッファに移動していればログを残す
          (run-at-time 5 nil 
                       (lambda (window)
                         (unless (eq (selected-window) window)
                           (delete-window window)))
                       output-window))))))

;; LaTeX-mode で M-o でコンパイル(デフォルトは編集中の tex ファイル)
(add-hook 'LaTeX-mode-hook
          (lambda () (local-set-key "\M-o" 'my-latex)))

普段は、コンパイルして gsview で表示させるようにしています。

どうも、Meadow では非同期プロセスはプロセスが残って嫌な感じ。start-process の問題を解決するわけではないけど、shell-command を使うものに差し替えておこう。直しついでに、コンパイルログもちょっと出してみよう。

SSH 経由で WEB サーバに編集している html/css ファイルをアップロード

自分用 Web サーバアップロードスクリプト。コマンドを打つのがだるくて。

(defun my-webup ()
  (interactive)
  (let ((home (getenv "HOME"))
        (ldir "WWW/")                                 ;; local www directory
        (rdir "public_html/")                         ;; remote www directory
        (us "yoshinag@www-tsujii.is.s.u-tokyo.ac.jp") ;; username@server
        (f buffer-file-name))
    (when (integerp (string-match "\\([^/]+\\)$" f))
      (let ((src (match-string 1 f)))
        (if (eq 0 (string-match (concat home ldir "\\(.+\\)") f))
            (let* ((w3f (match-string 1 f))
                   (dest (concat us ":" rdir w3f)))
              (call-process "scp" nil 0 nil "-i" identity src dest))
          (message "This file is not in the target directory."))))))

(add-hook 'html-helper-mode-hook
          (lambda () (local-set-key "\C-z" 'my-webup)))
(add-hook 'css-mode-hook
          (lambda () (local-set-key "\C-z" 'my-webup)))

html-helper-mode, css-mode で C-z でアップロード。そうでないファイルは M-x my-webup と叩けばよい。あと、web server に RSA/DSA 認証でパスワード無しでログインできるようになっていないと駄目です。そのようにするためには、SSH の鍵管理辺りを参考に。

追記: 同じようなことを考える人はいるもので、 増山さんの EMACSmyftp.pl というのがありました。

追記2: buffer-name はあくまでバッファにつけられた名前であって、ファイル名と一致するとは限らず、src の値として buffer-name を使う適当でないため、その辺りをちょいちょいと書き換えました。

追記3: Meadow の start-process は時々プロセスが残ったり不安定のようなので、call-process + destination => 0 に書き換えてみました。これでプロセスが残ったりしないかな?

Html previewer powered by w3m

以下を.emacsに追加すれば、html-helper-modeで編集しているHTMLファイルをw3mを使って見ることができます。

(defun my-w3m-preview ()
  (interactive)
  (w3m (concat "file:/" buffer-file-name)))
;; html-helper-mode のときに\M-sでプレビュー
(add-hook 'html-helper-mode-hook
          (function (lambda ()
                      (local-set-key "\M-s" 'my-w3m-preview))))

しかしw3m.elで現在のバッファを見るというのがあるような気も・・・(ぱっと見見つからなかったんだけど)。→ありました。が、browse-url.el の browse-url-of-buffer を使っていて、ファイル指定辺りでうまく動かないようです。

[Emacs21] emacs-w3m でタブ移動

emacs-w3mはEmacs上で動くタブブラウザ・・・と言いたい所ですが実際はw3mのラッパープログラムです。w3mおよびemacs-w3mのインストールなどについては、Emacs 上で w3m を動かそうを参考にしてください。このemacs-w3m, 最近のmozillaのようにリンク上でshift真ん中クリックすると仮想的な「タブ」が出るのですが、Meadow2やベースのEmacs21では本当にbufferの最上部にタブが表示されます。そこで、せっかくなのでタブ間を移動するEmacs Lispを書いてみました。


(defun my-w3m-tab (blst tlst)
  (if (eq blst '()) (sort tlst 'string<)
    (if (string-match "\\*w3m\\*" (buffer-name (car blst)))
        (my-w3m-tab (cdr blst) (cons (buffer-name  (car blst)) tlst))
      (my-w3m-tab (cdr blst) tlst))))

(defun my-w3m-left-tab ()
  "switch left tab in emacs-w3m"
  (interactive)
  (let* ((ctab (buffer-name))
         (tlst (my-w3m-tab (cdr (assq 'buffer-list (frame-parameters))) '()))
         (clst (member ctab (reverse tlst))))
    (cond ((= (length clst) 1)
           ;; 一番左のタブだったら一番右のタブを返す
           (switch-to-buffer (car (reverse tlst))))
          ;; そうでなければ左のタブを帰す。
          (t (switch-to-buffer (car (cdr clst)))))))

(defun my-w3m-right-tab ()
  "switch right tab in emacs-w3m"
  (interactive)
  (let* ((ctab (buffer-name))
         (tlst (my-w3m-tab (cdr (assq 'buffer-list (frame-parameters))) '()))
          (clst (member ctab tlst)))
    (cond ((= (length clst) 1)
           ;; 一番右のタブだったら一番左のタブを返す
           (switch-to-buffer (car tlst)))
          ;; そうでなければ右のタブを返す。
          (t (switch-to-buffer (car (cdr clst)))))))

(add-hook 'w3m-mode-hook
          (function
          (lambda ()
            (local-set-key [?\M-,] 'my-w3m-left-tab))))
(add-hook 'w3m-mode-hook
          (function
           (lambda ()
             (local-set-key [?\M-.] 'my-w3m-right-tab))))

w3mのバッファ内でAlt-,で左のタブ、Alt-.で右のタブ移動します。ちょっと動かした限りだとうまくいっているみたいです。Emacs21じゃなくても使えますが、タブが出ないのであんまり楽しくは無いです。

追記: w3m-previous-buffer, w3m-next-bufferという関数がw3m.el用意されていて同じ動作をするようです。というわけで、このスクリプトは用無しですが、一応残しておきます。


後細かいところはProgramming in Emacs Lisp: A simple introductionを見る。更に細かいところはGNU Emacs Lisp Reference Manualを見る。

▲ ホーム


Copyright © 1998 - 2010 Naoki Yoshinaga, All right Reserved.
$ Last modified at Thu Aug 07 10:53:49 2008 $