2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Emacsを使う理由(4): SKK

Last updated at Posted at 2025-12-14

はじめに

  • 最近のIMEは賢いですか?
    昔はATOK最強と言われていましたが、長文をいれての一発変換に満足できず、文節を短かく工夫しながら変換していくのは、いや、変換結果を添削して次に進む って実は面倒で脳に余計な負担をかけてませんか?
  • skkは「今から漢字をかくぞ!」と都度気合をいれる必要がありますが、それ以外はそのまま確定入力されていくので、手書きと同じような感覚で快適です
  • 手書きゆえに 漢字や送りがなを把握していないと使えない のですが、「あ〜そういう変換してほしくなかった」ということがほぼないです

SKK, ddskkとは

SKK(1987〜 佐藤雅彦氏) を元に、Emacsではddskkを使います(Linux上ではfcitx5-skk)。

つまり:

  • Emacsユーザは Ctrlキー を多用し、
  • SKKを使うことによってさらに Shiftキー を多用するので、
  • 左手小指をさらに酷使することになります(が、特段強くなっている感はないですね。。。:sweat_smile:)

基本設定

(package-install 'ddskk)
(global-set-key "\C-xj" 'skk-mode)
(setq skk-echo t)

;; 辞書設定
(setq skk-jisyo-code 'utf-8)
(setq skk-large-jisyo "~/somewhere/SKK-JISYO.L.utf8")
(setq skk-extra-jisyo-file-list
      (list "~/somewhere/SKK-JISYO.my.utf8"
            "~/somewhere/SKK-JISYO.emoji.utf8"))
(setq skk-face-proportional-p nil)
(setq skk-indicator-use-cursor-color nil)
(setq skk-cursor-hiragana-color "firebrick")
(setq skk-cursor-katakana-color "LimeGreen")
(setq skk-cursor-latin-color "SteelBlue")
(setq skk-status-indicator 'left)
(setq skk-latin-mode-string "[aA]")
(setq skk-hiragana-mode-string "[あ]")
(setq skk-katakana-mode-string "[ア]")
(setq skk-jisx0208-latin-mode-string "[A]")

ローマ字入力カスタマイズ

残念な全角文字まじりの文章にならないように以下の設定は必須です

(add-hook 'skk-mode-hook
          '(lambda ()
             (setq skk-rom-kana-rule-list
                   (append skk-rom-kana-rule-list
                           '(
                             ("z," nil ",")
                             ("z." nil ".")
                             ("[" nil "「")
                             ("]" nil "」")
                             ("z[" nil "[")
                             ("z]" nil "]")
                             (":" nil ":")
                             (";" nil ";")
                             ("z:" nil ":")
                             ("z;" nil ";")
                             ("z-" nil "-")
                             ("z~" nil "~")
                             ("~" nil "〜")
                             ("?" nil "?")
                             ("z?" nil "?")
                             ("!" nil "!")
                             ("z!" nil "!")
                             ("zt" nil "- [ ] ")
                             ("zs" nil "□")
                             ("zk" nil "×")
                             ("zw" nil "÷")
                             )))
             (setq skk-rule-tree
                   (skk-compile-rule-list
                    skk-rom-kana-base-rule-list skk-rom-kana-rule-list))
             (setq skk-rom-kana-rule-list
                   (cons '("/" nil "/") skk-rom-kana-rule-list))
             (setq skk-rule-tree
                   (skk-compile-rule-list
                    skk-rom-kana-base-rule-list skk-rom-kana-rule-list))
             ))

最近 z- → 〜z- → -~ → 〜 に変えましたが慣れてません。

  1. 「わーー」(wa--)
  2. 「わ - -」(wa z- z-)
  3. 「わ〜〜」(wa~~)

SKK辞書を使った単語移動・削除

この記事を書いてる最中にふと思いたって、AI君と会話してたら長年悩まされてた日本語の単語移動、削除が解決しました(恐しい時代だ、、)

Emacs標準

  1. M-fM-b ... 単語単位で前後移動だが、当然英語前提
  2. M-dM-h ... 単語単位で前後に切り取りするけどこれも当然英語前提

以下のコピペで なんとなく日本語の単語単位での移動、削除 が可能に! :joy:

(defvar skk-word-hash (make-hash-table :test 'equal))

(defun skk-load-word-dictionary ()
  "SKK 辞書ファイルから表記語をハッシュに登録する"
  (clrhash skk-word-hash)
  (dolist (file (cons skk-large-jisyo skk-extra-jisyo-file-list))
    (when (and file (file-readable-p file))
      (with-temp-buffer
        (insert-file-contents file)
        (goto-char (point-min))
        (while (re-search-forward "/\\([^/]+\\)/" nil t)
          (let* ((entries (split-string (match-string 1) "/"))
                 (word (car entries)))
            (puthash word t skk-word-hash)))))))

(add-hook 'after-init-hook #'skk-load-word-dictionary)

(defconst skk-word-max-length 12
  "SKK 辞書で単語として見る最大文字数")

(defun skk--longest-match-forward ()
  (let* ((limit (min (+ (point) skk-word-max-length) (point-max)))
         (text (buffer-substring-no-properties (point) limit))
         (i (length text))
         found)
    (while (> i 0)
      (when (gethash (substring text 0 i) skk-word-hash)
        (setq found i)
        (setq i 0))   ;; ループ終了
      (setq i (1- i)))
    found))

(defun skk--longest-match-backward ()
  (let* ((start (max (- (point) skk-word-max-length) (point-min)))
         (text (buffer-substring-no-properties start (point)))
         (len (length text))
         (i 0)
         found)
    (while (< i len)
      (when (gethash (substring text i) skk-word-hash)
        (setq found (- len i))
        (setq i len))
      (setq i (1+ i)))
    found))

(defun skk--ascii-or-space-p (char)
  (or (<= char 32)
      (and (<= char 126) (>= char 33))))

(defun skk--punct-p (char)
  (member char
          (string-to-list ".,;:,.、。:")))

(defun skk--particle-p (char)
  ;; 超割り切り:よく使う1文字助詞
  (memq char '(? ? ? ? ? ? ? ? ? ?)))

(defun skk-kill-word-forward ()
  "SKK 辞書を使った前方向 kill-word(実用版)"
  (interactive)
  (cond
   ;; バッファ末尾
   ((eobp) nil)

   ;; 空白・ASCII
   ((skk--ascii-or-space-p (char-after))
    (kill-word 1))

   ;; 句読点
   ((skk--punct-p (char-after))
    (kill-region (point) (1+ (point))))

   ;; 助詞(1文字)
   ((skk--particle-p (char-after))
    (kill-region (point) (1+ (point))))

   ;; SKK 辞書最長一致
   (t
    (let ((len (skk--longest-match-forward)))
      (if len
          (kill-region (point) (+ (point) len))
        ;; フォールバック:1文字
        (kill-region (point) (1+ (point))))))))

(defun skk-kill-word-backward ()
  "SKK 辞書を使った後ろ方向 kill-word(実用版)"
  (interactive)
  (cond
   ;; バッファ先頭
   ((bobp) nil)

   ;; 空白・ASCII
   ((skk--ascii-or-space-p (char-before))
    (backward-kill-word 1))

   ;; 句読点
   ((skk--punct-p (char-before))
    (kill-region (1- (point)) (point)))

   ;; 助詞
   ((skk--particle-p (char-before))
    (kill-region (1- (point)) (point)))

   ;; SKK 辞書最長一致
   (t
    (let ((len (skk--longest-match-backward)))
      (if len
          (kill-region (- (point) len) (point))
        (kill-region (1- (point)) (point)))))))

(global-set-key (kbd "M-d") #'skk-kill-word-forward)
(global-set-key (kbd "M-h") #'skk-kill-word-backward)


(defun skk-forward-word-end ()
  "ポイント位置から次の『単語境界』位置を返す。"
  (cond
   ((eobp) (point))

   ;; ASCII / space
   ((skk--ascii-or-space-p (char-after))
    (save-excursion
      (forward-word 1)
      (point)))

   ;; punctuation
   ((skk--punct-p (char-after))
    (1+ (point)))

   ;; particle
   ((skk--particle-p (char-after))
    (1+ (point)))

   ;; SKK 辞書最長一致
   (t
    (let ((len (skk--longest-match-forward)))
      (if len
          (+ (point) len)
        (1+ (point)))))))

(defun skk-backward-word-start ()
  "ポイント位置から前の『単語境界』位置を返す。"
  (cond
   ((bobp) (point))

   ;; ASCII / space
   ((skk--ascii-or-space-p (char-before))
    (save-excursion
      (backward-word 1)
      (point)))

   ;; punctuation
   ((skk--punct-p (char-before))
    (1- (point)))

   ;; particle
   ((skk--particle-p (char-before))
    (1- (point)))

   ;; SKK 辞書最長一致
   (t
    (let ((len (skk--longest-match-backward)))
      (if len
          (- (point) len)
        (1- (point)))))))

(defun skk-forward-word ()
  "SKK 辞書ベース forward-word"
  (interactive)
  (goto-char (skk-forward-word-end)))

(defun skk-backward-word ()
  "SKK 辞書ベース backward-word"
  (interactive)
  (goto-char (skk-backward-word-start)))

(global-set-key (kbd "M-f") #'skk-forward-word)
(global-set-key (kbd "M-b") #'skk-backward-word)

形態素解析しなくちゃ! めかぶだ!すだちか!とか考えなくてよかったです。

おまけ

fcitx5-skk(Linux)のキーマップを変更

/usr/share/libskk/rules/default/keymap/default.json

{
    "define": {
        "keymap": {
            "Escape": "abort",
            "C-g": "abort",
            "\n": "commit-unhandled",
            "C-m": "commit-unhandled",
            "\b": "delete",
            "C-h": "delete",
            "\\": "kuten",
            " ": "next-candidate",
            "\t": "complete",
            "C-i": "complete",
            ">": "special-midasi",
            "x": "previous-candidate",
            "X": "purge-candidate",
            "C-f": "expand-preedit",
            "C-b": "shrink-preedit",
            "Right": "expand-preedit",
            "Left": "shrink-preedit"
        }
    }
}

まとめ

別に普段長文を書くわけでもない一般人なので、そこまで日本語入力にこだわる必要はないのですが、昔はターミナル越しによる唯一の日本語入力方法でした。

エンジニアとしては半角英数字のソースコードで日本語入力を頻繁に行き来するにあたっては、今でも最も最適なIMEだと信じているので、興味を持った方はぜひ試してみてください :bow_tone1:

2
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?