org-menu.el (28739B)
1 ;;; org-menu.el --- A discoverable menu for org-mode using transient -*- lexical-binding: t; coding: utf-8 -*- 2 ;; 3 ;; Copyright 2021 Jan Rehders 4 ;; 5 ;; Author: Jan Rehders <nospam@sheijk.net> 6 ;; Version: 0.1alpha 7 ;; Package-Requires: ((emacs "26.1") (transient "0.1")) 8 ;; URL: https://github.com/sheijk/org-menu 9 ;; 10 ;; This file is free software; you can redistribute it and/or modify 11 ;; it under the terms of the GNU General Public License as published by 12 ;; the Free Software Foundation; either version 2, or (at your option) 13 ;; any later version. 14 ;; 15 ;; This file is distributed in the hope that it will be useful, 16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 ;; GNU General Public License for more details. 19 ;; 20 ;; You should have received a copy of the GNU General Public License 21 ;; along with GNU Emacs; see the file COPYING. If not, write to 22 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 23 ;; Boston, MA 02111-1307, USA. 24 ;; 25 ;;; Commentary: 26 ;; 27 ;; Usage: 28 ;; 29 ;; Add this to your ~/.emacs to bind the menu to `C-c m': 30 ;; 31 ;; (with-eval-after-load 'org 32 ;; (require 'org-menu) ;; not needed if installing by package manager 33 ;; (define-key org-mode-map (kbd "C-c m") 'org-menu)) 34 ;; 35 ;; The menu should be pretty self-explanatory. It is context dependent and 36 ;; offers different commands for headlines, tables, timestamps, etc. 37 ;; The task menu provides entry points for task that work from anywhere. 38 ;; 39 ;;; Code: 40 41 (require 'org) 42 (require 'transient) 43 44 (defgroup org-menu nil 45 "Options for org-menu" 46 :group 'org) 47 48 (defcustom org-menu-use-q-for-quit t 49 "Whether to add a q binding to quit to all menus. 50 51 Use this if you prefer to be consistent with magit. It will also 52 change some other bindings to use Q instead of q." 53 :group 'org-menu 54 :type 'boolean) 55 56 (defcustom org-menu-global-toc-depth 10 57 "The number of heading levels to show when displaying the global content." 58 :group 'org-menu 59 :type 'integer) 60 61 (defun org-menu-heading-navigate-items (check-for-heading &optional cycle-function) 62 "Items to navigate headings. 63 64 These will be added to most sub menus. If `CHECK-FOR-HEADING' is 65 true the items will only be added if on a heading. `CYCLE-FUNCTION' is the 66 function to be used to cycle visibility of current element." 67 (setq cycle-function (or cycle-function #'org-cycle)) 68 `(["Navigate" 69 ,@(when check-for-heading '(:if org-at-heading-p)) 70 ("p" "prev" org-previous-visible-heading :transient t) 71 ("n" "next" org-next-visible-heading :transient t) 72 ("c" "cycle" ,cycle-function :transient t) 73 ("u" "parent" outline-up-heading :transient t) 74 ("M-p" "prev (same level)" org-backward-heading-same-level :transient t) 75 ("M-n" "next (same level)" org-forward-heading-same-level :transient t) 76 ("M-w" "store link" org-store-link :transient t :if-not region-active-p) 77 ("C-_" "undo" undo :transient t)])) 78 79 (defun org-menu-show-headline-content () 80 "Will show the complete content of the current headline and it's children." 81 (interactive) 82 (save-excursion 83 (outline-hide-subtree) 84 (org-show-children 4) 85 (org-goto-first-child) 86 (org-reveal '(4)))) 87 88 ;;;###autoload (autoload 'org-menu-visibility "org-menu" nil t) 89 (transient-define-prefix org-menu-visibility () 90 "A menu to control visibility of org-mode items" 91 ["dummy"]) 92 93 (transient-insert-suffix 'org-menu-visibility (list 0) 94 `["Visibility" 95 ,@(org-menu-heading-navigate-items nil) 96 ["Visibility" 97 ("a" "all" org-show-subtree :if-not org-at-block-p :transient t) 98 ("a" "all" org-hide-block-toggle :if org-at-block-p :transient t) 99 ("t" "content" org-menu-show-headline-content :if-not org-at-block-p :transient t) 100 ("h" "hide" outline-hide-subtree :if-not org-at-block-p :transient t) 101 ("h" "hide" org-hide-block-toggle :if org-at-block-p :transient t) 102 ("r" "reveal" (lambda () (interactive) (org-reveal t)) :if-not org-at-block-p :transient t)] 103 ["Global" 104 ("C" "cycle global" org-global-cycle :transient t) 105 ("go" "overview" org-overview) 106 ("gt" "content" (lambda () (interactive) (org-content org-menu-global-toc-depth))) 107 ("ga" "all" org-show-all) 108 ("gd" "default" (lambda () (interactive) (org-set-startup-visibility)))] 109 ["Quit" 110 :if-non-nil org-menu-use-q-for-quit 111 ("q" "quit" transient-quit-all)]]) 112 113 (defun org-menu-eval-src-items () 114 "Return the items to evaluate a source block." 115 (list 116 ["Source" 117 :if org-in-src-block-p 118 ("e" "run block" org-babel-execute-src-block) 119 ("c" "check headers" org-babel-check-src-block) 120 ("k" "clear results" org-babel-remove-result-one-or-many) 121 ("'" "edit" org-edit-special)])) 122 123 ;;;###autoload (autoload 'org-menu-eval "org-menu" nil t) 124 (transient-define-prefix org-menu-eval () 125 "A menu to evaluate buffers, tables, etc. in org-mode" 126 ["dummy"]) 127 128 (defun org-menu-run-gnuplot () 129 "Will call `org-plot/gnuplot' and update inline images." 130 (interactive) 131 (org-plot/gnuplot) 132 (when org-inline-image-overlays 133 (org-redisplay-inline-images))) 134 135 (transient-insert-suffix 'org-menu-eval (list 0) 136 `["Evaluation" 137 ["Table" 138 :if org-at-table-p 139 ("e" "table" (lambda () (interactive) (org-table-recalculate 'iterate))) 140 ("1" "one iteration" (lambda () (interactive) (org-table-recalculate t))) 141 ("l" "line" (lambda () (interactive) (org-table-recalculate nil))) 142 ("f" "format" org-table-align :if org-at-table-p)] 143 ,@(org-menu-eval-src-items) 144 ["Heading" 145 :if-not org-in-src-block-p 146 ("c" "update checkbox count" org-update-checkbox-count)] 147 ["Plot" 148 ("p" "gnuplot" org-menu-run-gnuplot)] 149 ["Quit" 150 :if-non-nil org-menu-use-q-for-quit 151 ("q" "quit" transient-quit-all)]]) 152 153 (defun org-menu-insert-block (str) 154 "Insert an org mode block of type `STR'." 155 (interactive) 156 (insert (format "#+begin_%s\n#+end_%s\n" str str))) 157 158 (defun org-menu-expand-snippet (snippet) 159 "Will expand the given snippet named `SNIPPET'." 160 (interactive) 161 (if (require 'yasnippet nil 'noerror) 162 (progn 163 (insert snippet) 164 (yas-expand)) 165 (message "error: yasnippet not installed, could not expand %s" snippet))) 166 167 ;;;###autoload (autoload 'org-menu-insert-blocks "org-menu" nil t) 168 (transient-define-prefix org-menu-insert-blocks () 169 "A menu to insert new blocks in org-mode" 170 [["Insert block" 171 ("s" "source" (lambda () (interactive) (org-menu-insert-block "src"))) 172 ("e" "example" (lambda () (interactive) (org-menu-insert-block "example"))) 173 ("v" "verbatim" (lambda () (interactive) (org-menu-insert-block "verbatim"))) 174 ("a" "ascii" (lambda () (interactive) (org-menu-insert-block "ascii"))) 175 ("q" "quote" (lambda () (interactive) (org-menu-insert-block "quote")) :if-nil org-menu-use-q-for-quit) 176 ("Q" "quote" (lambda () (interactive) (org-menu-insert-block "quote")) :if-non-nil org-menu-use-q-for-quit) 177 ("d" "dynamic block" org-dynamic-block-insert-dblock)] 178 ["Quit" 179 :if-non-nil org-menu-use-q-for-quit 180 ("q" "quit" transient-quit-all)]]) 181 182 ;;;###autoload (autoload 'org-menu-insert-heading "org-menu" nil t) 183 (transient-define-prefix org-menu-insert-heading () 184 "A menu to insert new headings in org-mode" 185 [["Heading" 186 ("h" "heading" org-insert-heading) 187 ("H" "heading (after)" org-insert-heading-after-current) 188 ("T" "todo" org-insert-todo-heading)] 189 ["Items" 190 ("d" "drawer" org-insert-drawer)] 191 ["Quit" 192 :if-non-nil org-menu-use-q-for-quit 193 ("q" "quit" transient-quit-all)]]) 194 195 ;;;###autoload (autoload 'org-menu-insert-template "org-menu" nil t) 196 (transient-define-prefix org-menu-insert-template () 197 "A menu to insert new templates in org-mode" 198 [["Templates" 199 ("S" "structure template" org-insert-structure-template) 200 ("B" "yas blocks" (lambda () (interactive) (org-menu-expand-snippet "beg"))) 201 ("O" "yas options" (lambda () (interactive) (org-menu-expand-snippet "opt")))] 202 ["Quit" 203 :if-non-nil org-menu-use-q-for-quit 204 ("q" "quit" transient-quit-all)]]) 205 206 ;;;###autoload (autoload 'org-menu-insert-timestamp "org-menu" nil t) 207 (transient-define-prefix org-menu-insert-timestamp () 208 "A menu to insert timestamps in org-mode" 209 [["Timestamp" 210 ("." "active" org-time-stamp) 211 ("!" "inactive" org-time-stamp-inactive)] 212 ["Now" 213 ("n" "active" (lambda () (interactive) (org-insert-time-stamp (current-time) t))) 214 ("N" "inactive" (lambda () (interactive) (org-insert-time-stamp (current-time) t t)))] 215 ["Today" 216 ("t" "active" (lambda () (interactive) (org-insert-time-stamp (current-time) nil))) 217 ("T" "inactive" (lambda () (interactive) (org-insert-time-stamp (current-time) nil t)))] 218 ["Quit" 219 :if-non-nil org-menu-use-q-for-quit 220 ("q" "quit" transient-quit-all)]]) 221 222 (defun org-menu-table-insert-row-below () 223 "Insert a new table column below point." 224 (interactive) 225 (org-table-insert-row '4)) 226 227 (defun org-menu-table-insert-column-left () 228 "Insert a new column to the left of point." 229 (interactive) 230 (org-table-insert-column) 231 (org-table-move-column-right)) 232 233 ;;;###autoload (autoload 'org-menu-insert-table "org-menu" nil t) 234 (transient-define-prefix org-menu-insert-table () 235 "A menu to insert table items in org-mode" 236 [["Table" 237 ("t" "table" org-table-create-or-convert-from-region :if-not org-at-table-p) 238 ("i" "import" org-table-import :if-not org-at-table-p)] 239 ["Rows/columns" 240 :if org-at-table-p 241 ("r" "row above" org-table-insert-row :transient t) 242 ("R" "row below" org-menu-table-insert-row-below :transient t) 243 ("c" "column right" org-table-insert-column :transient t) 244 ("C" "column left" org-menu-table-insert-column-left :transient t) 245 ("-" "horiz. line" org-table-insert-hline :transient t)] 246 ["Quit" 247 :if-non-nil org-menu-use-q-for-quit 248 ("q" "quit" transient-quit-all)]]) 249 250 (defun org-menu-insert-superscript () 251 "Insert a text with superscript." 252 (interactive) 253 (if (require 'yasnippet nil 'noerror) 254 (yas-expand-snippet "${1:text}^{${2:super}}") 255 (insert "a^b"))) 256 257 (defun org-menu-insert-subscript () 258 "Insert a text with subscript." 259 (interactive) 260 (if (require 'yasnippet nil 'noerror) 261 (yas-expand-snippet "${1:text}_{${2:sub}}") 262 (insert "a_b"))) 263 264 (defun org-menu-parse-formatting (format-char) 265 "Will return the bounds of the format markup `FORMAT-CHAR'." 266 (let ((original-point (point)) 267 start end) 268 (ignore-errors 269 (save-excursion 270 (save-restriction 271 (save-match-data 272 (org-narrow-to-element) 273 (goto-char (search-backward (format "%c" format-char))) 274 (setq start (point)) 275 (goto-char original-point) 276 (goto-char (search-forward (format "%c" format-char))) 277 (setq end (point)) 278 (cons start end))))))) 279 280 (defun org-menu-toggle-format (format-char) 281 "Will add/remove the given format wrapped in `FORMAT-CHAR' form the region (or point)." 282 (let ((range (org-menu-parse-formatting format-char)) 283 (format-string (format "%c" format-char))) 284 (if (null range) 285 (org-menu-insert-text format-string format-string t) 286 (goto-char (cdr range)) 287 (delete-backward-char 1) 288 (goto-char (car range)) 289 (delete-char 1)))) 290 291 ;;;###autoload (autoload 'org-menu-insert-list "org-menu" nil t) 292 (transient-define-prefix org-menu-insert-list () 293 "A menu to insert lists" 294 [["List" 295 ("-" "item" (lambda () (interactive) (insert "- "))) 296 ("+" "+" (lambda () (interactive) (insert "+ "))) 297 ("*" "*" (lambda () (interactive) (insert "* "))) 298 ("." "1." (lambda () (interactive) (insert "1. "))) 299 (")" "1)" (lambda () (interactive) (insert "1) ")))] 300 ["Todo" 301 ("t" "todo" (lambda () (interactive) (insert "- [ ] "))) 302 ("d" "done" (lambda () (interactive) (insert "- [X] "))) 303 ("p" "partial" (lambda () (interactive) (insert "- [-] ")))] 304 ["Quit" 305 :if-non-nil org-menu-use-q-for-quit 306 ("q" "quit" transient-quit-all)]]) 307 308 (defun org-menu-insert-plot () 309 "Insert a small example plot for `gnu-plot'." 310 (interactive) 311 (beginning-of-line 1) 312 (if (require 'yasnippet nil 'noerror) 313 (yas-expand-snippet 314 "#+plot: type:${1:2d} file:\"${2:plot.svg}\" 315 | A | B | 316 |---+----| 317 | 1 | 10 | 318 | 2 | 8 | 319 | 3 | 9 | 320 321 #+attr_org: :width ${3:400px} 322 [[file:$2]] 323 ") 324 (insert 325 "#+plot: type:2d file:\"plot.svg\" 326 | A | B | 327 |---+----| 328 | 1 | 10 | 329 | 2 | 8 | 330 | 3 | 9 | 331 332 #+attr_org: :width 400px 333 [[file:plot.svg]] 334 "))) 335 336 ;;;###autoload (autoload 'org-menu-insert "org-menu" nil t) 337 (transient-define-prefix org-menu-insert () 338 "A menu to insert new items in org-mode" 339 [["Insert" 340 ("." "time" org-menu-insert-timestamp) 341 ("t" "table" org-menu-insert-table) 342 ("h" "heading" org-menu-insert-heading) 343 ("b" "block" org-menu-insert-blocks) 344 ("T" "templates" org-menu-insert-template) 345 ("l" "link (new)" org-insert-link) 346 ("L" "link (stored)" org-insert-last-stored-link :transient t) 347 ("-" "list" org-menu-insert-list) 348 ("p" "plot" org-menu-insert-plot)] 349 ["Format" 350 ("^" "superscript" org-menu-insert-superscript) 351 ("_" "subscript" org-menu-insert-subscript)] 352 ["Quit" 353 :if-non-nil org-menu-use-q-for-quit 354 ("q" "quit" transient-quit-all)]]) 355 356 (defun org-menu-comment-line () 357 "Toggle line comment w/o moving cursor." 358 (interactive) 359 (save-excursion (comment-line 1))) 360 361 (defun org-menu-insert-text (left right &optional surround-whitespace) 362 "Will insert left|right and put the curser at |. 363 364 If region is active it will be surrounded by `LEFT' and `RIGHT' and 365 the point will be at end of region. Will add spaces before/after text if 366 `SURROUND-WHITESPACE' is true and it's needed." 367 368 (let ((start (point)) 369 (end (point))) 370 (when (region-active-p) 371 (setq start (region-beginning) 372 end (region-end)) 373 (deactivate-mark)) 374 (when (> start end) 375 ;; swap variables w/o importing cl-lib 376 (setq start (prog1 end (setq end start)))) 377 378 (goto-char start) 379 (when (and surround-whitespace 380 (not (bolp)) 381 (not (looking-back " +"))) 382 (insert " ")) 383 (insert left) 384 385 (forward-char (- end start)) 386 387 (save-excursion 388 (insert right) 389 (when (and surround-whitespace 390 (not (eolp)) 391 (not (looking-at " +"))) 392 (insert " "))))) 393 394 (defun org-menu-in-time-p () 395 "Return whether we're at a time stamp or similar. 396 397 Adapted from `org-goto-calendar'" 398 (or (org-at-timestamp-p 'lax) 399 (org-match-line (concat ".*" org-ts-regexp)))) 400 401 ;;;###autoload (autoload 'org-menu-goto "org-menu" nil t) 402 (transient-define-prefix org-menu-goto () 403 "Menu to go to different places by name" 404 [["Go to" 405 ("h" "heading" imenu) 406 ("s" "source block" org-babel-goto-named-src-block) 407 ("r" "result block" org-babel-goto-named-result) 408 ("." "calendar" org-goto-calendar :if org-menu-in-time-p)] 409 ["Quit" 410 :if-non-nil org-menu-use-q-for-quit 411 ("q" "quit" transient-quit-all)]]) 412 413 (defun org-menu-at-text-p () 414 "Return whether point is at text." 415 (not (or (org-at-heading-p) 416 (org-at-table-p) 417 (org-in-item-p) 418 (org-in-src-block-p)))) 419 420 (defun org-menu-text-format-items (check-for-table) 421 "Items to format text. 422 423 Will add an ':if org-menu-at-text-p' criteria if `CHECK-FOR-TABLE' is true." 424 (list 425 `["Navigate" 426 ,@(when check-for-table '(:if org-menu-at-text-p)) 427 ("p" "up" previous-line :transient t) 428 ("n" "down" next-line :transient t) 429 ("b" "left" backward-word :transient t) 430 ("f" "right" forward-word :transient t) 431 ("u" "parent" org-up-element :transient t) 432 ("M-w" "store link" org-store-link :transient t :if-not region-active-p) 433 ("C-_" "undo" undo :transient t) 434 ("SPC" "mark" set-mark-command :transient t) 435 ("C-x C-x" "exchange" exchange-point-and-mark :transient t)] 436 `["Formatting" 437 ,@(when check-for-table '(:if org-menu-at-text-p)) 438 ("*" "Bold" (lambda nil (interactive) (org-menu-toggle-format ?*)) :transient t) 439 ("/" "italic" (lambda nil (interactive) (org-menu-toggle-format ?/)) :transient t) 440 ("_" "underline" (lambda nil (interactive) (org-menu-toggle-format ?_)) :transient t) 441 ("+" "strikethrough" (lambda nil (interactive) (org-menu-toggle-format ?+)) :transient t)] 442 `["Source" 443 ,@(when check-for-table '(:if org-menu-at-text-p)) 444 ("~" "code" (lambda nil (interactive) (org-menu-toggle-format ?~)) :transient t) 445 ("=" "verbatim" (lambda nil (interactive) (org-menu-toggle-format ?=)) :transient t)])) 446 447 ;;;###autoload (autoload 'org-menu-text-in-element "org-menu" nil t) 448 (transient-define-prefix org-menu-text-in-element () 449 "Add formatting for text inside other elements like lists and tables" 450 ["dummy"]) 451 452 (transient-insert-suffix 'org-menu-text-in-element (list 0) 453 `[,@(org-menu-text-format-items nil) 454 ["Quit" 455 :if-non-nil org-menu-use-q-for-quit 456 ("q" "quit" transient-quit-all)]]) 457 458 ;;;###autoload (autoload 'org-menu-options "org-menu" nil t) 459 (transient-define-prefix org-menu-options () 460 "A menu to toggle options" 461 [["Display" 462 ("l" "show links" org-toggle-link-display) 463 ("i" "inline images" org-toggle-inline-images) 464 ("p" "pretty entities" org-toggle-pretty-entities) 465 ("I" "indent by level" org-indent-mode) 466 ("t" "timestamp overlay" org-toggle-time-stamp-overlays) 467 ("n" "numbered headings" org-num-mode)] 468 ["Quit" 469 :if-non-nil org-menu-use-q-for-quit 470 ("q" "quit" transient-quit-all)]]) 471 472 (defun org-menu-in-link () 473 "Return whether we are inside a link. 474 475 Conditions have been adapted from `org-insert-link'" 476 (or 477 ;; Use variable from org-compat to support Emacs 26 478 (org-in-regexp org-bracket-link-regexp 1) 479 (when (boundp 'org-link-angle-re) 480 (org-in-regexp org-link-angle-re)) 481 (when (boundp 'org-link-plain-re) 482 (org-in-regexp org-link-plain-re)))) 483 484 (defun org-menu-toggle-has-checkbox () 485 "Toggle whether the current list item has a checkbox." 486 (interactive) 487 (save-excursion 488 (back-to-indentation) 489 (if (not (looking-at "- ")) 490 (message "Not at list item") 491 (end-of-line 1) 492 (org-ctrl-c-ctrl-c '(4))))) 493 494 (defun org-menu-is-timer-running () 495 "Return whether a timer is currently running." 496 (and org-timer-start-time 497 (not org-timer-countdown-timer) 498 (not org-timer-pause-time))) 499 500 (defun org-menu-is-timer-paused () 501 "Return whether a timer has been started and is paused." 502 (and org-timer-start-time 503 (not org-timer-countdown-timer) 504 org-timer-pause-time)) 505 506 ;;;###autoload (autoload 'org-menu-clock "org-menu" nil t) 507 (transient-define-prefix org-menu-clock () 508 "Time management using org-modes clock" 509 [["Clock" 510 ("<tab>" "in" org-clock-in :if-not org-clock-is-active) 511 ("TAB" "in" org-clock-in :if-not org-clock-is-active) 512 ("o" "out" org-clock-out :if org-clock-is-active) 513 ("j" "goto" org-clock-goto :if org-clock-is-active) 514 ("q" "cancel" org-clock-cancel 515 :if (lambda () (and (not org-menu-use-q-for-quit) 516 (org-clock-is-active)))) 517 ("Q" "cancel" org-clock-cancel 518 :if (lambda () (and org-menu-use-q-for-quit 519 (org-clock-is-active)))) 520 ("d" "display" org-clock-display :if org-clock-is-active) 521 ("x" "in again" org-clock-in-last :if-not org-clock-is-active) 522 ("z" "resolve" org-resolve-clocks)] 523 ["Timer" 524 ("0" "start" org-timer-start :if-nil org-timer-start-time) 525 ("_" "stop" org-timer-stop :if-non-nil org-timer-start-time) 526 ("." "insert" org-timer :if-non-nil org-timer-start-time) 527 ("-" "... item" org-timer-item :if-non-nil org-timer-start-time) 528 ("," "pause" org-timer-pause-or-continue :if org-menu-is-timer-running) 529 ("," "continue" org-timer-pause-or-continue :if org-menu-is-timer-paused) 530 (";" "countdown" org-timer-set-timer :if-nil org-timer-start-time)] 531 ["Quit" 532 :if-non-nil org-menu-use-q-for-quit 533 ("q" "quit" transient-quit-all)]]) 534 535 (transient-define-prefix org-menu-search-and-filter () 536 "A menu to search and filter org-mode documents" 537 ["Search and filter" 538 ["Filter" 539 ("/" "only matching" org-sparse-tree) 540 ("q" "tags" org-tags-sparse-tree :if-nil org-menu-use-q-for-quit) 541 ("Q" "tags" org-tags-sparse-tree :if-non-nil org-menu-use-q-for-quit) 542 ("t" "todos" org-show-todo-tree) 543 ("d" "deadlines" org-check-deadlines) 544 ("b" "before date" org-check-before-date) 545 ("a" "after date" org-check-after-date) 546 ("D" "dates range" org-check-dates-range)] 547 ["Agenda" 548 ("A" "open" org-agenda)] 549 ["Quit" 550 :if-non-nil org-menu-use-q-for-quit 551 ("q" "quit" transient-quit-all)]]) 552 553 (transient-define-prefix org-menu-attachments () 554 "A menu to manage attachments" 555 ["Attachments" 556 ["Add" 557 ("a" "file" org-attach-attach) 558 ("c" "copy" org-attach-attach-cp) 559 ("m" "move" org-attach-attach-mv) 560 ("l" "link" org-attach-attach-ln) 561 ("y" "symlink" org-attach-attach-lns) 562 ("u" "download" org-attach-url) 563 ("b" "buffer" org-attach-buffer) 564 ("n" "new" org-attach-new)] 565 ["Open" 566 ("o" "attachment" org-attach-open) 567 ("O" "in Emacs" org-attach-open-in-emacs) 568 ("f" "directory" org-attach-reveal) 569 ("F" "in Emacs" org-attach-reveal-in-emacs)] 570 ["Delete" 571 ("d" "delete" org-attach-delete-one) 572 ("D" "all" org-attach-delete-all)] 573 ["More" 574 ("s" "set directory" org-attach-set-directory) 575 ("S" "unset" org-attach-unset-directory) 576 ("z" "synchronize" org-attach-sync)]]) 577 578 (transient-define-prefix org-menu-archive () 579 "A menu to archive items" 580 ["dummy"]) 581 582 (transient-insert-suffix 'org-menu-archive (list 0) 583 `["Archive" 584 ,@(org-menu-heading-navigate-items nil #'org-force-cycle-archived) 585 ["Archive to" 586 ("t" "tree" org-archive-subtree :transient t) 587 ("s" "sibling" org-archive-to-archive-sibling :transient t) 588 ("Q" "tag" org-toggle-archive-tag :transient t)]]) 589 590 (defun org-menu-insert-todo-heading-after-current () 591 "Insert a new todo heading with same level as current, after subtree." 592 (interactive) 593 (org-insert-todo-heading '(16))) 594 595 ;;;###autoload (autoload 'org-menu "org-menu" nil t) 596 (transient-define-prefix org-menu () 597 "A discoverable menu to edit and view org-mode documents" 598 ["dummy"]) 599 600 (transient-insert-suffix 'org-menu (list 0) 601 `["Org mode" 602 ;; Items for headings 603 ,@(org-menu-heading-navigate-items t) 604 605 ["Move heading" 606 :if org-at-heading-p 607 ("P" "up" org-metaup :transient t) 608 ("N" "down" org-metadown :transient t) 609 ("B" "left" org-shiftmetaleft :transient t) 610 ("F" "right" org-shiftmetaright :transient t) 611 ("b" "left (line)" org-metaleft :transient t) 612 ("f" "right (line)" org-metaright :transient t) 613 ("r" "refile" org-refile :transient t)] 614 ["Change heading" 615 :if org-at-heading-p 616 ("*" "toggle" org-ctrl-c-star :if-not org-at-table-p :transient t) 617 ("t" "todo" org-todo :transient t) 618 ("q" "tags" org-set-tags-command :transient t :if-nil org-menu-use-q-for-quit) 619 ("Q" "tags" org-set-tags-command :transient t :if-non-nil org-menu-use-q-for-quit) 620 ("y" "property" org-set-property :transient t) 621 ("," "priority" org-priority :transient t) 622 ("A" "archive" org-menu-archive :transient t) 623 ("D" "deadline" org-deadline :transient t) 624 ("S" "schedule" org-schedule :transient t) 625 ("/" "comment" org-toggle-comment :transient t) 626 ("C-w" "cut tree" org-cut-special :transient t) 627 ("C-y" "yank tree" org-paste-special :transient t)] 628 ["Make new/delete" 629 :if org-at-heading-p 630 ("mh" "make heading (before)" org-insert-heading) 631 ("mH" "make heading (after)" org-insert-heading-after-current) 632 ("mt" "make todo (before)" org-insert-todo-heading) 633 ("mT" "make todo (after)" org-menu-insert-todo-heading-after-current) 634 ("dh" "delete heading" org-cut-subtree :transient t) 635 ("dy" "delete property" org-delete-property :transient t) 636 ("a" "attachments" org-menu-attachments :transient t)] 637 638 ;; Items for tables 639 ["Navigate" 640 :if org-at-table-p 641 ("p" "up" previous-line :transient t) 642 ("n" "down" next-line :transient t) 643 ("b" "left" org-table-previous-field :transient t) 644 ("f" "right" org-table-next-field :transient t) 645 ("u" "parent" outline-up-heading :transient t) 646 ("M-w" "store link" org-store-link :transient t :if-not region-active-p) 647 ("C-_" "undo" undo :transient t)] 648 ["Move r/c" 649 :if org-at-table-p 650 ("P" "up" org-table-move-row-up :transient t) 651 ("N" "down" org-table-move-row-down :transient t) 652 ("B" "left" org-table-move-column-left :transient t) 653 ("F" "right" org-table-move-column-right :transient t)] 654 ["Field" 655 :if org-at-table-p 656 ("'" "edit" org-table-edit-field) 657 ("SPC" "blank" org-table-blank-field :transient t) 658 ("RET" "from above" org-table-copy-down :transient t) 659 ("t" "text formatting" org-menu-text-in-element)] 660 ["Formulas" 661 :if org-at-table-p 662 ("E" "edit all" org-table-edit-formulas :transient t) 663 ("=" "field" (lambda () (interactive) (org-table-eval-formula '(4))) :transient t) 664 ("+" "in place" (lambda () (interactive) (org-table-eval-formula '(16)))) 665 ("c" "column" org-table-eval-formula :transient t) 666 ("h" "coordinates" org-table-toggle-coordinate-overlays :transient t) 667 ("D" "debug" org-table-toggle-formula-debugger :transient t)] 668 ["Table" 669 :if org-at-table-p 670 ("dr" "delete row" org-shiftmetaup :transient t) 671 ("dc" "delete column" org-shiftmetaleft :transient t) 672 ("m" "make" org-menu-insert-table) 673 ,@(when (fboundp (function org-table-toggle-column-width)) 674 (list '("S" "shrink column" org-table-toggle-column-width :transient t))) 675 ("r" "sort" org-table-sort-lines :transient t) 676 ("M-w" "copy rect" org-table-copy-region :transient t :if region-active-p) 677 ("C-w" "cut rect" org-table-cut-region :transient t :if region-active-p) 678 ("C-y" "yank rect" org-table-paste-rectangle :transient t)] 679 680 ;; Items for lists 681 ["Navigate" 682 :if org-in-item-p 683 ("p" "prev" previous-line :transient t) 684 ("n" "next" next-line :transient t) 685 ("c" "cycle" org-cycle :transient t) 686 ("u" "parent" org-up-element :transient t) 687 ("M-p" "prev (same level)" org-backward-element :transient t) 688 ("M-n" "next (same level)" org-forward-element :transient t) 689 ("M-w" "store link" org-store-link :transient t :if-not region-active-p) 690 ("C-_" "undo" undo :transient t)] 691 ["Move list" 692 :if org-in-item-p 693 ("P" "up" org-metaup :transient t) 694 ("N" "down" org-metadown :transient t) 695 ("B" "left" org-shiftmetaleft :transient t) 696 ("F" "right" org-shiftmetaright :transient t) 697 ("b" "left (line)" org-metaleft :transient t) 698 ("f" "right (line)" org-metaright :transient t)] 699 ["List" 700 :if org-in-item-p 701 ("R" "repair" org-list-repair) 702 ("*" "turn into tree" org-list-make-subtree) 703 ("S" "sort" org-sort-list :transient t) 704 ("t" "text formatting" org-menu-text-in-element)] 705 ["Toggle" 706 :if org-in-item-p 707 ("-" "list item" org-toggle-item :if-not org-at-table-p :transient t) 708 ("+" "list style" org-cycle-list-bullet :if-not org-at-table-p :transient t) 709 ("d" "done" org-toggle-checkbox :transient t) 710 ("m" "checkbox" org-menu-toggle-has-checkbox :transient t)] 711 712 ;; Items for text 713 ,@(org-menu-text-format-items t) 714 ["Line" 715 :if org-menu-at-text-p 716 (":" "fixed width" org-toggle-fixed-width :transient t) 717 (";" "comment" org-menu-comment-line :transient t) 718 ("--" "list" org-toggle-item :transient t) 719 ("-*" "heading" org-ctrl-c-star :transient t)] 720 721 ;; Items for source blocks 722 ,@(org-menu-eval-src-items) 723 724 ["Link" 725 :if org-menu-in-link 726 ("e" "edit" org-insert-link :transient t)] 727 728 ["Timestamp" 729 :if org-menu-in-time-p 730 ("." "type" org-toggle-timestamp-type :transient t) 731 ("e" "edit" org-time-stamp :transient t)] 732 733 ["Tasks" 734 ("v" "visibility" org-menu-visibility) 735 ("x" "evaluation" org-menu-eval) 736 ("i" "insert" org-menu-insert) 737 ("g" "go to" org-menu-goto) 738 ("s" "search" org-menu-search-and-filter) 739 ("o" "options" org-menu-options) 740 ("C" "clock (active)" org-menu-clock :if org-clock-is-active) 741 ("C" "clock" org-menu-clock :if-not org-clock-is-active) 742 ,@(when (fboundp #'org-capture-finalize) 743 (list '("C-c C-c" "confirm capture" org-capture-finalize :if-non-nil org-capture-mode))) 744 ,@(when (fboundp #'org-capture-kill) 745 (list '("C-c C-k" "abort capture" org-capture-kill :if-non-nil org-capture-mode))) 746 ("" "" transient-noop) 747 ("q" "quit" transient-quit-all :if-non-nil org-menu-use-q-for-quit) 748 ]]) 749 750 (provide 'org-menu) 751 ;;; org-menu.el ends here