複数のブリットを一括操作する
org-mode
のブリッツは非常に便利です.ちょっとしたリストを作成したり,手順を記したり,様々な用途に使えます.また,ブリッツにチェックボックスを加えれば,タスクの実施済みと未実施を簡単に区別できます.
さて,私が org-mode
を使ってメモや情報を集めるときには,基本的に箇条書きで記しています.ブリッツはレベルを簡単に変更( M-<right>/<left>
)でき,入れ子にできるので,思考の構造化にピッタリです.
そのようなフローですと,とりあえず5つくらいのブリッツを生成したあとで「これらはチェックリストだな」とか「いや順番が重要だから,ハイフンのブリッツじゃなくて,数値のブリッツに変更したいな」と考えるようになります.ブリッツの種類を一括で変更する機能( Shift-<right>/<left>
)はデフォルトで実装されていますが,チェックボックスを一括で加えたり削除する機能は がありません C-u M-x org-toggle-checkbox (C-u C-c C-x C-b)
と長いので,自分用に実装してみました.
地味実装だけど、意外と使うことある。 pic.twitter.com/Xj9Lp3No5O
— Takaaki Ishikawa (@takaxp) February 16, 2021
以下のコマンドは,処理したい領域を選択してから, M-x
で呼び出しても良いですし, seleted.el
と組み合わせて,設定した任意のシングルキー押下でもOKです.便利!
- 後からチェックボックス化
- 後からリスト化
- チェックボックス付きリストの操作
selected.el
でシングルキー実行
後からチェックボックス化
M-x my-org-list-insert-checkbox-into-items
でリストにチェックボックスを追加, M-x my-org-list-delete-checkbox-from-items
でチェックボックスを削除.
;;;###autoload
(defun my-org-list-insert-checkbox-into-items (begin end)
(interactive "r")
(when mark-active
(let* ((checkbox "[ ] ")
(len (string-width checkbox)))
(goto-char begin)
(while (re-search-forward
(concat "\\(^[ \t]*[-\\+\\*][ \t]+\\|"
"^[ \t]*[0-9]*[\\.)][ \t]+\\)") end t)
(replace-match (concat "\\1" checkbox) nil nil)
(setq end (+ end len)))
(goto-char begin))))
;;;###autoload
(defun my-org-list-delete-checkbox-from-items (begin end)
(interactive "r")
(when mark-active
(let ((len (string-width "[ ] ")))
(goto-char begin)
(while (re-search-forward
(concat "\\(^[ \t]*[-\\+\\*][ \t]\\|^[ \t]*[0-9]*[\\.)][ \t]\\)"
"\\[.\\][ \t]+")
end t)
(replace-match "\\1" nil nil)
(setq end (- end len)))
(goto-char begin))))
ところが,記事を公開してから @ak10i さんに「それあるよ」と教えていただきました.コマンドとしては C-u M-x org-toggle-checkbox (C-u C-c C-x C-b)
になります.上に実装したコマンドのトグル版ですね.ただ,選択した時にどの行をトグル対象にするのか(デフォルト関数の方が広く取られる)と,コマンド実行後にカーソルが元の位置に戻らない(上の実装では戻る)のが,私の実装と異なります.
チェックボックスをリストに加えたり削除したりするのは C-u C-c C-x C-b ?
— ak10i (@ak10i) February 20, 2021
入力するにはちょっと長いので,次のようにすると,短縮して使えるようになります.新しい関数を selected.el
に噛ませば,シングルキーでトグルできます.
(defun my-org-toggle-checkbox ()
(interactive)
(let ((current-prefix-arg '(4)))
(call-interactively 'org-toggle-checkbox)))
(org-defkey org-mode-map (kbd "<f3>") #'my-org-toggle-checkbox)
トグルになっているので,こっちの方が便利ですね…
ということで,トグル版も作りました.
(defvar my-org-list-with-checkbox-regexp
(concat "\\(^[ \t]*[-\\+\\*][ \t]\\|^[ \t]*[0-9]*[\\.)][ \t]\\)"
"\\[.\\][ \t]+"))
;;;###autoload
(defun my-org-list-toggle-checkbox (begin end)
(interactive "r")
(when mark-active
(goto-char begin)
(if (re-search-forward
my-org-list-with-checkbox-regexp (point-at-eol) t)
(my-org-list-delete-checkbox-from-items begin end)
(my-org-list-insert-checkbox-into-items begin end))))
後からリスト化
選択領域を後からリスト化することもできます. M-x my-org-list-insert-items
で行頭に " - "
を追加, M-x my-org-list-delete-items
で削除.
また,文章を書き始めて,その当該行をすぐにブリッツ化したいときには, M-x my-cycle-bullet-at-heading
を実行すると,カーソル位置を保持したまま,行頭にブリッツを挿入できます.領域選択は不要です.同じ行でもう一度実行すると,チェックボックスに変更できます.
;;;###autoload
(defun my-org-list-insert-items (begin end)
(interactive "r")
(when mark-active
(let* ((bullet " - ")
(len (string-width bullet)))
(goto-char begin)
(while (and (re-search-forward (concat "\\(^[ \t]*\\)") end t)
(not (equal (point) end)))
(replace-match (concat "\\1" bullet) nil nil)
(setq end (+ end len)))
(goto-char begin))))
;;;###autoload
(defun my-org-list-delete-items (begin end)
(interactive "r")
(when mark-active
(let* ((bullet "- ")
(len (string-width bullet)))
(goto-char begin)
(while (re-search-forward
(concat "\\(^[ \t]*\\)" bullet) end t)
(replace-match "" nil nil)
(setq end (- end len)))
(goto-char begin))))
;;;###autoload
(defun my-cycle-bullet-at-heading (arg)
"Add a bullet of \" - \" if the line is NOT a bullet line."
(interactive "P")
(save-excursion
(beginning-of-line)
(let ((bullet "- ")
(point-at-eol (point-at-eol)))
(cond
((re-search-forward
(concat "\\(^[ \t]*\\)" bullet "\\[.\\][ \t]+") point-at-eol t)
(replace-match (if arg "" (concat "\\1" bullet)) nil nil))
((re-search-forward
(concat "\\(^[ \t]*\\)" bullet) point-at-eol t)
(replace-match (if arg "" (concat "\\1" bullet "[ ] ")) nil nil))
((re-search-forward
(concat "\\(^[ \t]*\\)") point-at-eol t)
(replace-match
(concat "\\1 " bullet) nil nil))
(t nil)))))
(global-set-key (kbd "C-M--") 'my-cycle-bullet-at-heading)
チェックボックス付きリストの操作
M-x my-org-list-insert-itms-with-checkbox
でチェックボックス付きリスト化, M-x my-org-list-delete-items-with-checkbox
でチェックボックスごとブリッツを削除できます.
;;;###autoload
(defun my-org-list-insert-itms-with-checkbox (begin end)
(interactive "r")
(when mark-active
(let* ((bullet " - ")
(checkbox "[ ] ")
(blen (string-width bullet))
(clen (string-width checkbox)))
(goto-char begin)
(while (and (re-search-forward (concat "\\(^[ \t]*\\)") end t)
(not (equal (point) end)))
(replace-match (concat "\\1" bullet checkbox) nil nil)
(setq end (+ end blen clen)))
(goto-char begin))))
;;;###autoload
(defun my-org-list-delete-items-with-checkbox (begin end)
(interactive "r")
(when mark-active
(let* ((bullet "- ")
(checkbox "[ ] ")
(blen (string-width bullet))
(clen (string-width checkbox)))
(goto-char begin)
(while (re-search-forward
(concat "\\(^[ \t]*[-\\+\\*][ \t]\\|^[ \t]*[0-9]*[\\.)][ \t]\\)"
"\\[.\\][ \t]+") end t)
(replace-match "" nil nil)
(setq end (- end blen clen)))
(goto-char begin))))
selected.el でシングルキー実行
selected.el と組み合わせると,選択領域に対してシングルキー押下で一括したチェックボックス化などが実行できて,さらに便利になります.以下の例では,選択して "-"
押下と, 選択して "_"
にコマンドを割り当てています.
使い方の詳細は selected.el で「選択して右クリック」的な概念を にまとめてあります.参照ください.
(when (require 'selected nil t)
(define-key selected-keymap (kbd "-") #'my-org-list-insert-items)
(define-key selected-keymap (kbd "_")
#'my-org-list-insert-checkbox-into-items)
;; (define-key selected-keymap (kbd "_") #'my-org-toggle-checkbox)
(selected-global-mode 1))