複数候補を選択して補完できる動的略語展開 dabbrev-expand-multiple (未完成)


新バージョン, そして, エントリ作成.

今度は, 上記 URL で更新してく.


まとまったエントリを作るべきか迷い中.
まあ, ある程度の状態になるまでは, 毎回エントリ作る形式で行こうと思う.
まだまだ問題点は山積みなわけやし.



plus さんが, メールで前回のものの更新版を送ってくれました.
変数にすべき部分や, 似たような処理を繰り返して部分が綺麗にまとめられていました.
ありがとうございます.
今回は, その plus さんが改良してくれたものに, tooltip を使えるようにしました.
けっこう良い感じです.
まだまだ完成にはほど遠いですが.


とりあえず, ソースをば.

;; -*- Coding: iso-2022-7bit -*-

(autoload 'dabbrev--reset-global-variables "dabbrev" nil t)
;; (require 'dabbrev nil t)

(defvar dabbrev-expand-multiple-select-key
  ;; '("^A" "^S" "^D" "^F" "^G")
  ;; キーを増やしていくと C-g に当たったためコントロール文字は避ける
  '("a" "s" "d"))
(defvar dabbrev-expand-multiple-timeout 5)

(defvar dabbrev-expand-multiple-tooltip-params nil)

;; ------------------------------------------------------------
;; 設定例
;; ------------------------------------------------------------
;;
;; (setq dabbrev-expand-multiple-tooltip-params
;;       '((foreground-color . "white")
;;         (background-color . "navy blue")
;;         (border-color . "royal blue")))
;; (setq dabbrev-expand-multiple-select-key
;;   '("a" "s" "d" "f" "g"))
;;
;; ------------------------------------------------------------

(defvar dabbrev-expand-multiple-use-tooltip t
  "ツールチップを使って表示するかどうか.")

(defun dabbrev-expand-multiple ()
  "動的略語展開において, 補完候補を複数掲示する関数.
ミニバッファに候補を表示したまま,
dabbrev-expand-multiple-timeout 秒間何もしないと,
タイムアウトとなり, 関数の実行が終了する."
  (interactive)
  (when dabbrev-expand-multiple-use-tooltip
    (require 'tooltip))
  (dabbrev--reset-global-variables)
  (let* ((target (dabbrev--abbrev-at-point))
         (key (reverse dabbrev-expand-multiple-select-key))
         (i (length key))
         (prompt "")
         abbrev-list abbrev action sel)
    (while (and (> i 0)
                (setq abbrev (dabbrev--find-expansion
                              target 0 dabbrev-case-fold-search)))
      (add-to-list 'abbrev-list abbrev t)
      (if dabbrev-expand-multiple-use-tooltip
          (setq prompt (format "%s(%s): %s\n" prompt (nth (1- i) key) abbrev))
        (setq prompt (format "%s(%s): %s " prompt (nth (1- i) key) abbrev)))
      (setq i (1- i)))
    (cond
     (abbrev-list
      (if dabbrev-expand-multiple-use-tooltip
          (let* ((P (dabbrev-expand-multiple-mouse-position))
                 (frame (car P)) (x  (cadr P)) (y (cddr P))
                 (oP (mouse-position))
                 (oframe (car oP))
                 (ox (cadr oP)) (oy (cddr oP)))
            (set-mouse-position frame x y)
            (dabbrev-expand-multiple-show-tooltip prompt)
            (setq action (read-char "" nil dabbrev-expand-multiple-timeout))
            ;; 外部プログラムである unclutter を使ってマウスカーソルを隠していると,
            ;; 元のマウス位置が取得できず, エラーが出るので, その対策.
            (when (eq ox nil) (setq ox 0))
            (when (eq oy nil) (setq oy 0))
            (tooltip-hide)
            (set-mouse-position oframe ox oy))
        (setq action (read-char prompt nil dabbrev-expand-multiple-timeout)))
      (setq sel (length (member (string (or action 0)) key)))
      (cond
       ;; 時間切れ
       ((not action)
        (message "Timeout"))
       ;; 独自拡張
       ;; スペースで挿入
;;       ((eq action ? )
;;        (insert
;;         (substring (nth 1 abbrev-list) (string-width target))))
       ;; 独自拡張されたキー, dabbrev-expand-multiple-select-key 以外のキーが押された
       ((or (= sel 0) (> sel (length abbrev-list)))
        (message "Quit"))
       ;; 選択された文字を挿入
       (t
        (insert
         (substring (nth (1- sel) abbrev-list) (string-width target))))))
     (t
      (error "No dynamic expansion for `%s' found" target)))))

(defun dabbrev-expand-multiple-show-tooltip (text)
  "ほぼ SKK の skk-tooltip-show-1 のコピー.
変数設定を dabbrev-expand-multiple-tooltip-params にしたのみ."
  (condition-case error
      (let ((params (copy-sequence tooltip-frame-parameters))
            fg bg)
        (if dabbrev-expand-multiple-tooltip-params
            ;; ユーザが独自に tooltip 表示設定する
            (dolist (cell dabbrev-expand-multiple-tooltip-params)
              (setq params (tooltip-set-param params
                                              (car cell)
                                              (cdr cell))))
          ;; tooltip のデフォルトの設定をする
          (setq fg (face-attribute 'tooltip :foreground))
          (setq bg (face-attribute 'tooltip :background))
          (when (stringp fg)
            (setq params (tooltip-set-param params 'foreground-color fg))
            (setq params (tooltip-set-param params 'border-color fg)))
          (when (stringp bg)
            (setq params (tooltip-set-param params 'background-color bg))))
        (unless (ignore-errors
                  (or (get-text-property 0 'face text)
                      (get-text-property 2 'face text)))
          (setq text (propertize text 'face 'tooltip)))
        (x-show-tip text
                    (selected-frame)
                    params
                    dabbrev-expand-multiple-timeout
                    tooltip-x-offset
                    tooltip-y-offset))
    (error
     (message "Error while displaying tooltip: %s" error)
     (sit-for 1)
     (message "%s" text))))

(defun dabbrev-expand-multiple-mouse-position ()
  "Return the position of point as (FRAME X . Y).
Analogous to mouse-position.
SKK の skk-e21-mouse-position をほぼ流用.
ミニバッファを振り分けていたのを消し,
コメントを削除してしまったぐらい."
  (let* ((w (selected-window))
         (edges (window-edges w))
         (list
          (compute-motion
           (max (window-start w) (point-min))
           '(0 . 0)
           (point)
           (cons (window-width w) (window-height w))
           (1- (window-width w))
           (cons (window-hscroll w) 0)
           w)))
    (cons (selected-frame)
          (cons (+ (car edges)       (car (cdr list)))
                (+ (car (cdr edges)) (car (cdr (cdr list))))))))

;; dabbrev.el ends here

候補選択に使うキーが, C-a, C-s, C-d ... から, a, s, d, f になっています.
これは, C-a, C-s ... と増やしていくと, いずれ, C-g にぶちあたるためです.
この件は, plus さんにより御指摘頂きました. ありがとうございます.
また, いくつか変数が追加されています.
代表的なものは, dabbrev-expand-multiple-use-tooltip で,
これが t だと tooltip を使用します.
他の変数については, 上記に設定例があります.
timeout は, 秒数です. デフォルトでは, 5秒でタイムアウトとなります.



使用例のスクリーンショットは, このようになります.
これは, 設定例の設定を適用している場合です.

このように表示されます.
tooltip は, マウスカーソルがある部分に表示されるので,
元のカーソル位置を記憶し, ポイント付近に移動, そして, 記憶しておいた元の場所に戻すという処理をしています.
これは, SKK が行っているもので, その処理をほぼそのまま持ってきました.


今後の予定は, 以下のようなものです.

  • defvar を defcustom にする
  • インライン表示を実装する
  • 漢字変換のような補完候補のサイクルを実現する
  • 補完元のハイライトをする.

インライン表示を優先的に実装したいと考えています.
これは, SKK によるものと同じです.
サンプルとして, SKKによる例を下にスクリーンショットで上げておきます.
これは, tooltip も同時に表示させています.

これが実装できれば,
tooltip が使えない環境でも使えるようになりので, より便利になりと思います.



それにしても, SKK ってやっぱりものすごくよく出来てるなあと思った. ほんと.

更新時刻

2007年, 7月29日, 2:53