Browse Source

Add a dispatcher command for inserting dynamic blocks

* lisp/org.el (org-dynamic-block-insert-dblock,
  org-dynamic-block-alist, org-dynamic-block-functions,
  org-dynamic-block-types, org-dynamic-block-define,
  org-dynamic-block-function): New variables, New functions.

* lisp/org-keys.el (org-dynamic-block-insert-dblock): Add binding for
  the function.
(org-clock-report, org-columns-insert-dblock): Remove function
keybindings. Mark them as obsolete.

* doc/org-manual.org (Dynamic Blocks): : Add manual for dispatch
command `org-dynamic-block-insert-dblock'.

* testing/lisp/test-org-clock.el: New test.
stardiviner 1 year ago
parent
commit
34b71a0ca9
8 changed files with 94 additions and 8 deletions
  1. 8 3
      doc/org-manual.org
  2. 13 0
      etc/ORG-NEWS
  3. 3 0
      lisp/org-clock.el
  4. 3 1
      lisp/org-colview.el
  5. 6 0
      lisp/org-compat.el
  6. 2 4
      lisp/org-keys.el
  7. 35 0
      lisp/org.el
  8. 24 0
      testing/lisp/test-org-clock.el

+ 8 - 3
doc/org-manual.org

@@ -19975,9 +19975,14 @@ users mailing list, at mailto:emacs-orgmode@gnu.org.
 
 Org supports /dynamic blocks/ in Org documents.  They are inserted
 with begin and end markers like any other code block, but the contents
-are updated automatically by a user function.  For example, {{{kbd(C-c
-C-x C-r)}}} inserts a dynamic table that updates the work time (see
-[[*Clocking Work Time]]).
+are updated automatically by a user function.
+
+#+kindex: C-c C-x x
+#+findex: org-dynamic-block-insert-dblock
+You can insert a dynamic block with ~org-dynamic-block-insert-dblock~,
+which is bound to {{{kbd(C-c C-x x)}}} by default.  For example,
+{{{kbd(C-c C-x x c l o c k t a b l e RET)}}} inserts a table that
+updates the work time (see [[*Clocking Work Time]]).
 
 Dynamic blocks can have names and function parameters.  The syntax is
 similar to source code block specifications:

+ 13 - 0
etc/ORG-NEWS

@@ -40,6 +40,13 @@ arguments no longer imply a "file" result is expected.
 See [[git:3367ac9457]] for details.
 
 ** New features
+*** Add a dispatcher command to insert dynamic blocks
+
+You can add dynamic block into ~org-dynamic-block-alist~ with function
+~org-dynamic-block-define~.  All dynamic blocks in
+~org-dynamic-block-define~ can be used by
+~org-dynamic-block-insert-dblock~ command.
+
 *** Babel
 **** Add LaTeX output support in PlantUML
 *** New minor mode to display headline numbering
@@ -85,6 +92,12 @@ system than the main Org document.  For example:
 the corresponding direction by swapping with the adjacent cell.
 
 ** New functions
+*** ~org-dynamic-block-insert-dblock~
+
+Use default keybinding =<C-c C-x x>= to run command
+~org-dynamic-block-insert-dblock~.  It will prompt user to select
+dynamic block in ~org-dynamic-block-alist~.
+
 *** ~org-table-cell-up~
 *** ~org-table-cell-down~
 *** ~org-table-cell-left~

+ 3 - 0
lisp/org-clock.el

@@ -36,6 +36,7 @@
 (declare-function org-element-property "org-element" (property element))
 (declare-function org-element-type "org-element" (element))
 (declare-function org-table-goto-line "org-table" (n))
+(declare-function org-dynamic-block-define "org" (type func))
 
 (defvar org-frame-title-format-backup frame-title-format)
 (defvar org-time-stamp-formats)
@@ -2052,6 +2053,8 @@ in the buffer and update it."
     (start (goto-char start)))
   (org-update-dblock))
 
+(org-dynamic-block-define "clocktable" #'org-clock-report)
+
 (defun org-day-of-week (day month year)
   "Returns the day of the week as an integer."
   (nth 6

+ 3 - 1
lisp/org-colview.el

@@ -41,6 +41,7 @@
 (declare-function org-element-property "org-element" (property element))
 (declare-function org-element-restriction "org-element" (element))
 (declare-function org-element-type "org-element" (element))
+(declare-function org-dynamic-block-define "org" (type func))
 
 (defvar org-agenda-columns-add-appointments-to-effort-sum)
 (defvar org-agenda-columns-compute-summary-properties)
@@ -1237,7 +1238,7 @@ When PRINTF is non-nil, use it to format the result."
   "Summarize CHECK-BOXES with a check-box cookie."
   (format "[%d/%d]"
 	  (cl-count-if (lambda (b) (or (equal b "[X]")
-				  (string-match-p "\\[\\([1-9]\\)/\\1\\]" b)))
+				   (string-match-p "\\[\\([1-9]\\)/\\1\\]" b)))
 		       check-boxes)
 	  (length check-boxes)))
 
@@ -1537,6 +1538,7 @@ PARAMS is a property list of parameters:
 		     (id)))))
   (org-update-dblock))
 
+(org-dynamic-block-define "columnview" #'org-columns-insert-dblock)
 
 
 ;;; Column view in the agenda

+ 6 - 0
lisp/org-compat.el

@@ -464,6 +464,12 @@ use of this function is for the stuck project list."
 (define-obsolete-function-alias 'org-babel-strip-quotes
   'org-strip-quotes "Org 9.2")
 
+(define-obsolete-function-alias 'org-clock-report
+  'org-dynamic-block-insert-dblock "Org 9.3")
+
+(define-obsolete-function-alias 'org-columns-insert-dblock
+  'org-dynamic-block-insert-dblock "Org 9.3")
+
 ;;;; Obsolete link types
 
 (eval-after-load 'org

+ 2 - 4
lisp/org-keys.el

@@ -49,10 +49,8 @@
 (declare-function org-clock-in "org" (&optional select start-time))
 (declare-function org-clock-in-last "org" (&optional arg))
 (declare-function org-clock-out "org" (&optional switch-to-state fail-quietly at-time))
-(declare-function org-clock-report "org" (&optional arg))
 (declare-function org-clone-subtree-with-time-shift "org" (n &optional shift))
 (declare-function org-columns "org" (&optional global columns-fmt-string))
-(declare-function org-columns-insert-dblock "org" ())
 (declare-function org-comment-dwim "org" (arg))
 (declare-function org-copy "org" ())
 (declare-function org-copy-special "org" ())
@@ -67,6 +65,7 @@
 (declare-function org-cycle "org" (&optional arg))
 (declare-function org-cycle-agenda-files "org" ())
 (declare-function org-date-from-calendar "org" ())
+(declare-function org-dynamic-block-insert-dblock "org" (&optional arg))
 (declare-function org-dblock-update "org" (&optional arg))
 (declare-function org-deadline "org" (arg1 &optional time))
 (declare-function org-decrease-number-at-point "org" (&optional inc))
@@ -638,7 +637,7 @@ COMMANDS is a list of alternating OLDDEF NEWDEF command names."
 (org-defkey org-mode-map (kbd "C-c C-x C-j") #'org-clock-goto)
 (org-defkey org-mode-map (kbd "C-c C-x C-q") #'org-clock-cancel)
 (org-defkey org-mode-map (kbd "C-c C-x C-d") #'org-clock-display)
-(org-defkey org-mode-map (kbd "C-c C-x C-r") #'org-clock-report)
+(org-defkey org-mode-map (kbd "C-c C-x x") #'org-dynamic-block-insert-dblock)
 (org-defkey org-mode-map (kbd "C-c C-x C-u") #'org-dblock-update)
 (org-defkey org-mode-map (kbd "C-c C-x C-l") #'org-toggle-latex-fragment)
 (org-defkey org-mode-map (kbd "C-c C-x C-v") #'org-toggle-inline-images)
@@ -650,7 +649,6 @@ COMMANDS is a list of alternating OLDDEF NEWDEF command names."
 (org-defkey org-mode-map (kbd "C-c C-x e") #'org-set-effort)
 (org-defkey org-mode-map (kbd "C-c C-x E") #'org-inc-effort)
 (org-defkey org-mode-map (kbd "C-c C-x o") #'org-toggle-ordered-property)
-(org-defkey org-mode-map (kbd "C-c C-x i") #'org-columns-insert-dblock)
 (org-defkey org-mode-map (kbd "C-c C-,") #'org-insert-structure-template)
 (org-defkey org-mode-map (kbd "C-c C-x .") #'org-timer)
 (org-defkey org-mode-map (kbd "C-c C-x -") #'org-timer-item)

+ 35 - 0
lisp/org.el

@@ -11518,6 +11518,41 @@ If COMMAND is not given, use `org-update-dblock'."
 	(unless (re-search-forward org-dblock-end-re nil t)
 	  (error "Dynamic block not terminated"))))))
 
+(defcustom org-dynamic-block-alist nil
+  "Alist defining all the Org dynamic blocks.
+The key is the dynamic block type name, as a string.  The value
+is the function used to insert the dynamic block."
+  :group 'org-block
+  :package-version '(Org . "9.3")
+  :type '(alist :tag "Dynamic block name"
+		:key-type string
+                :value-type function)
+  :safe #'listp)
+
+(defun org-dynamic-block-function (type)
+  "Return function associated to a given dynamic block type.
+TYPE is the dynamic block type, as a string."
+  (cdr (assoc type org-dynamic-block-alist)))
+
+(defun org-dynamic-block-types ()
+  "List all defined dynamic block types."
+  (mapcar #'car org-dynamic-block-alist))
+
+(defun org-dynamic-block-define (type func)
+  "Define dynamic block TYPE with FUNC."
+  (push (cons type func) org-dynamic-block-alist))
+
+(defun org-dynamic-block-insert-dblock (type)
+  "Insert a dynamic block of type TYPE.
+When used interactively, select the dynamic block types among
+defined types, per `org-dynamic-block-define'."
+  (interactive (list (completing-read "Dynamic block: "
+				      (org-dynamic-block-types))))
+  (pcase (org-dynamic-block-function type)
+    (`nil (error "No such dynamic block: %S" type))
+    ((and f (pred functionp)) (funcall f))
+    (_ (error "Invalid function for dynamic block %S" type))))
+
 (defun org-dblock-update (&optional arg)
   "User command for updating dynamic blocks.
 Update the dynamic block at point.  With prefix ARG, update all dynamic

+ 24 - 0
testing/lisp/test-org-clock.el

@@ -273,6 +273,30 @@ the buffer."
 
 ;;; Clocktable
 
+(ert-deftest test-org-clock/clocktable/insert ()
+  "Test insert clocktable dynamic block with `org-dynamic-block-insert-dblock'."
+  (should
+   (equal
+    "| Headline     | Time   |      |
+|--------------+--------+------|
+| *Total time* | *1:00* |      |
+|--------------+--------+------|
+| \\_  H2       |        | 1:00 |"
+    (org-test-with-temp-text "** H1\n\n** H2\n<point>"
+      (insert (org-test-clock-create-clock ". 1:00" ". 2:00"))
+
+      (goto-line 2)
+      (require 'org-clock)
+      (org-dynamic-block-insert-dblock "clocktable")
+
+      (goto-line 1)
+      (unwind-protect
+	  (save-excursion
+	    (when (search-forward "#+CAPTION:") (forward-line))
+	    (buffer-substring-no-properties
+	     (point) (progn (search-forward "#+END:") (line-end-position 0))))
+	(delete-region (point) (search-forward "#+END:\n")))))))
+
 (ert-deftest test-org-clock/clocktable/ranges ()
   "Test ranges in Clock table."
   ;; Relative time: Previous two days.