2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Emacs で文字数をカウントしたいとき(または私用のキーバインドとメニュー設定)

Last updated at Posted at 2020-09-05

バッファ文字数のカウント

web フォームで文字数が決まっているものがときどきありますが、 たまにしかない場面なので Emacs 上でのカウントのしかたが身に着いていなくて、 その都度ググったり info をひっくり返したりしていました。 でも毎回やっていたんじゃ時間や手間が結構馬鹿になりません。

結局 count-words というのを使えばカレントバッファの文字数、 もしくはリージョンがあればリージョンの文字数が判るんですが、 キーバインドだけしておいてもまたすぐ忘れるので、メニューに登録しておくことによってすぐ判るようにしました。

init.el
(define-key-after global-map [menu-bar tools cwords]
  '("Count Words" . count-words)
  'spell)

これで Menu -> Tools の中の Spell Checking の下に件のコマンドが登録されます。

文字数カウントについては以上です。
以下はキーやメニュー(初稿からアップデートされています)の拡張の仕方なので興味のある方だけどうぞ。

私用キーマップ

ついでにキーにバインドするなら、マイマップを用意しておくと追加が便利になります。

Emacs の掟で C-cアルファベット1からなるシーケンスはユーザー用に予約解放済みなのでそれを例に説明します。

下準備(init.el等)
(define-prefix-command 'priv-map)
(define-key global-map "\C-c" 'priv-map)

priv-map の箇所はお好きな名前で、上記のようにして個人用プレフィクス・マップを作ってバインドしておくと

init.el
(define-key global-map "\C-cw" 'count-words)

などと毎回頭からフルストロークで指定しないでも

init.el
(define-key priv-map "w" 'count-words)

などと 1キャラ指定で個人用バインドをシンプルに追加していけて整理管理も楽になります。

ちなみに自分は whch-key-mode を使っていて、バインドを忘れていてもこれがヘルプになるのですが、 入れていないデフォルト状態でもプレフィクスの後に C-h を押すとバインドの一覧が出るので覚えていると素の Emacs を使う場面等で役に立ちます。

私用メニューマップ

メニューは easy-menu-add-item を使うと設定も追加もこれだけでできます。標準添付関数ですが init.el で使うにはロードが必要かもしれません。

init.el
(require 'easymenu)
(easy-menu-add-item
 global-map
 '(menu-bar)
 '("Private" ; これがそのままマップのシンボルになる.
   ["Count Words" count-words]
   ["imenu"       imenu-add-menubar-index]
   ["Remember"    remember]
   ["Dup Line"    dup-line]
   ["Follow Win"  follow-delete-other-windows-and-split]
   ("TimeClock"  
    ["In"  timeclock-in]
    ["Out" timeclock-out]
    ["Change" timeclock-change]
    ["Log" timeclock-visit-timelog])))

こんな感じで初期化なしでいきなりまとめて設定でき、入れ子にすれば階層構造の表現もできるようです。
グローバル・マップとメジャー・モード等の並びの間の追加メニューとしては理想的な位置に出現します。

メニュー名がそのままキーマップの名前(ダミーファンクションキーの名前)になるようなので、
このメニューに後からコマンドを追加したい場合は以下の要領で書くだけでできます。

(easy-menu-add-item global-map '(menu-bar Private) ["テスト"  version])

オプションの4番目の引数をつけると位置の指定もでき、そのメニュー項目の上に挿入されます。何も指定しないと最後になります。

(easy-menu-add-item global-map '(menu-bar Private) ["テスト2" version] "TimeClock")

最初の count-words メニュー追加案件は(位置は変わりますが)以下一発でも OK だということです。

(easy-menu-add-item global-map '(menu-bar Tools) '["Count Word" count-words] "Spell Cheking")

メニューへのショートカット

メニューのキーボード・ショートカットがないか調べてみると menu-bar-opentmm-menubar (Meta + BackQuart) の2種をとりあえず発見。

前者はフルキーからいけないぽいので使いやすそうなのは後者。
それに後者はデフォルト・バインドもされていて時々誤操作で開いて何だか判らずそっ閉じしていたもの。

これをしばらく使ってみようと思いましたが、デフォルトだと視認性がイマイチなので which-key に似せた色付けをしてみました。 ちなみに頭の長い英語のヘルプメッセは変数 tmm-completion-prompt で変更できるようです。

init.el
(add-hook 'completion-list-mode-hook
          '(lambda ()
             (let ((prompt (if (boundp 'tmm-mid-prompt) tmm-mid-prompt "xxx")))
               (font-lock-add-keywords
                nil
                `((" ---[-0-9A-Za-z(/*+,#)]+\\([ ]?[-0-9A-Za-z(/*+,#)]+\\)*" . 'dired-ignored)
                  (,(concat
                     "\\(?1:[0-9A-Za-z%]\\)\\(?2:" prompt
                     "\\)\\(?3:[-0-9A-Za-z(</*+,.#>)]+\\(?:[ ]?[-0-9A-Za-z(</*+,.#>)]+\\)*\\)")
                   (1 font-lock-constant-face)
                   (2 font-lock-doc-face)
                   (3 font-lock-function-name-face)))))))

tmmbar.png

dired 等多機能なモードのとき起動すると何気にいいヘルプになりますね。

「私的メニュー追加」に間する初稿

[この項 はeasy-menu-add-item の項の追記で要らなくなった先に書いてあったメニュー追加記事です。 が、こちらも知っておくとメニュー設定の仕組が見えてきます。]

更なるついでにメニューの追加法です。普段そんなに使わないものや自作マクロ等なら私用メニューを作って詰めこんでおくといいでしょう。これも少々指定が増えますがほぼ同じ要領で追加できます。

下準備(init.el等)
(define-prefix-command 'priv-menu-map nil "Private")
(define-key-after global-map [menu-bar private] '("Private" . priv-menu-map))

ブラケットの引数がダミーのファンクションキーで、 (private の箇所はこの名前でなくてもいいのですが判りやすいようにします) そのキーに priv-menu-map を割り振り Private というメニュー・ラベルを貼り付けます。ラベルは勿論 日本語 でも大丈夫です。

define-key ではなく define-key-after ですが、こちらにしないとメニュー先頭(左端)に追加されてしまいます。23

あとは以下のように随時 priv-menu-map に割り当てれば メニュー Private の中にコマンドを追加していけます。

init.el
(define-key priv-menu-map [imenu]   '("Imenu Index"    . imenu-add-menubar-index))
(define-key priv-menu-map [dupline] '("Duplicate Line" . dup-line))

オマケ

拙作 の Mew 用の拡張を Mew のメニューに追加 [Menu -> Mew -> Select の中]

.mew.el
(define-key-after ;; "1日以内のメール" 
  (lookup-key mew-summary-mode-map [menu-bar Mew Select])
  [day]
  '("Within 1 Day" . mew-indays))
(define-key-after ;; "全文検索"
  (lookup-key mew-summary-mode-map [menu-bar Mew Select])
  [pic]
  '("Full Text Search" . mew-summary-selection-by-search))

関連リンク

count-words-region

他のことを調べていてcount-words-region として M-= に標準バインドされていた事に気づきました。
scratchC-h b してバインド一覧をとって count で occur してみると見事にマッチ。
記事を書く前に確認したと思ったんですがそう思い込んでいたようです。お恥かしや。

7 matches for "count" in buffer: *Help*
    312:<f3>            kmacro-start-macro-or-insert-counter
    493:C-x l           count-lines-page
    557:M-=             count-words-region
    841:C-c M =         count-words
    870:C-x C-k C-a     kmacro-add-counter
    871:C-x C-k C-c     kmacro-set-counter
    875:C-x C-k TAB     kmacro-insert-counter

設定も掘ってみると見事にデフォルトバインドされています。

bindings.el
3 matches for "count" in buffer: bindings.el
    753:	buffer-display-count buffer-display-time
    911:(define-key esc-map "=" 'count-words-region)
   1360:(define-key ctl-x-map "l" 'count-lines-page)

デフォルト・バインドされていれば、基本的に Emacs 流の伝統的規則性があって覚えられるので、 メニューに当てておくこともないんですが、 menu にもマッピングされているのか見てみましたが、こちらにはセットされていませんでした。

menu-bar.el
4 matches in 3 lines for "count" in buffer: menu-bar.el
   2078:	(count 0))
   2081:	  (setq count (1+ count)))
   2083:    (> count 1)))
  1. 普通の大文字もしくは小文字のアルファベットのいずれかでコントロール+アルファベットはアルファベットではなくコントロールコードなので含まれない。ちなみに自分は私的バインド空間を C-cM にしていて、これなら衝突を気にせずコントロール・コードでも記号でも自由にバインドできるようになります。

  2. easy-menu-define とかいうどこが easy なんだか判らないマクロがありますが、これで global-map に追加すると先頭に追加されてしまい回避法が判りませんでした。3

  3. easy-menu-add-item を使えば回避できることが判りました。 2

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?