org-tempo: Various improvements

* lisp/org-tempo.el (org-tempo-keywords-alist): Improve docstring.
(org-tempo--update-maybe):
(org-tempo--keys): New function.
(org-tempo-complete-tag):
(org-tempo-setup):
(org-tempo-add-templates): Use new functions.
(org-tempo-add-block): Smarter position of point.
* testing/lisp/test-org-tempo.el (test-org-tempo/cursor-placement):
 (test-org-tempo/space-first-line): New tests.
* testing/lisp/test-org-tempo.el (test-org-tempo/completion): Adapt
  test to changes.

Org Tempo more carefully checks for new definitions.  When inserting
blocks point will differ depending on whether it is source block.
This commit is contained in:
Rasmus 2017-12-21 12:59:36 +01:00
parent b56df737b7
commit e5f6cb6c8b
2 changed files with 67 additions and 12 deletions

View File

@ -34,7 +34,7 @@
;;
;; `tempo' can also be used to define more sophisticated keywords
;; completions. See the section "Additional keywords" below for
;; additional details.
;; examples.
;;
;;; Code:
@ -65,7 +65,9 @@ and KEYWORD. The tempo snippet \"<KEY\" is expand to the KEYWORD
value.
For example \"<l\" at the beginning of a line is expanded to
#+latex:"
\"#+latex:\".
Note: the tempo function for \"#+include\" is defined elsewhere."
:group 'org-tempo
:type '(repeat (cons (string :tag "Key")
(string :tag "Keyword")))
@ -76,23 +78,35 @@ For example \"<l\" at the beginning of a line is expanded to
;;; Org Tempo functions and setup.
(defun org-tempo-setup ()
(org-tempo-add-templates)
(org-tempo--update-maybe)
(tempo-use-tag-list 'org-tempo-tags)
(setq-local tempo-match-finder "^ *\\(<[[:word:]]+\\)\\="))
(defun org-tempo--keys ()
"Return a list of all Org Tempo expansion strings, like \"<s\"."
(mapcar (lambda (pair) (format "<%s" (car pair)))
(append org-structure-template-alist
org-tempo-keywords-alist)))
(defun org-tempo--update-maybe ()
"Check and add new Org Tempo templates if necessary.
In particular, if new entries were added to
`org-structure-template-alist' or `org-tempo-keywords-alist', new
Tempo templates will be added."
(unless (cl-every (lambda (key) (assoc key org-tempo-tags))
(org-tempo--keys))
(org-tempo-add-templates)))
(defun org-tempo-add-templates ()
"Update all Org Tempo templates.
Goes through `org-structure-template-alist' and
`org-tempo-keywords-alist'."
(let ((keys (mapcar (lambda (pair) (format "<%c" (car pair)))
(append org-structure-template-alist
org-tempo-keywords-alist))))
(let ((keys (org-tempo--keys)))
;; Check for duplicated snippet keys and warn if any are found.
(when (> (length keys) (length (delete-dups keys)))
(warn
"Duplicated keys in `org-structure-template-alist' and `org-tempo-keywords-alist'"))
;; Remove any keys already defined in case they have been updated.
(setq org-tempo-tags
(cl-remove-if (lambda (tag) (member (car tag) keys)) org-tempo-tags))
@ -102,9 +116,11 @@ Goes through `org-structure-template-alist' and
(defun org-tempo-add-block (entry)
"Add block entry from `org-structure-template-alist'."
(let* ((key (format "<%s" (car entry)))
(name (cdr entry)))
(name (cdr entry))
(special (member name '("src" "export"))))
(tempo-define-template (format "org-%s" (replace-regexp-in-string " " "-" name))
`(,(format "#+begin_%s " name) p '> n n
`(,(format "#+begin_%s%s" name (if special " " ""))
,(when special 'p) '> n '> ,(unless special 'p) n
,(format "#+end_%s" (car (split-string name " ")))
>)
key
@ -126,10 +142,12 @@ Goes through `org-structure-template-alist' and
Unlike to `tempo-complete-tag', do not give a signal if a partial
completion or no match at all is found. Return nil if expansion
didn't succeed."
(org-tempo--update-maybe)
;; `tempo-complete-tag' returns its SILENT argument when there is no
;; completion available at all.
(not (eq 'fail (tempo-complete-tag 'fail))))
;;; Additional keywords
(defun org-tempo--include-file ()
@ -160,8 +178,6 @@ didn't succeed."
(add-hook 'org-mode-hook 'org-tempo-setup)
(add-hook 'org-tab-before-tab-emulation-hook 'org-tempo-complete-tag)
(org-tempo-add-templates)
;; Enable Org Tempo in all open Org buffers.
(dolist (b (org-buffer-list 'files))
(with-current-buffer b (org-tempo-setup)))

View File

@ -41,7 +41,7 @@
(org-tempo-setup)
(call-interactively 'org-cycle)
(buffer-string))
"#+begin_export latex \n\n#+end_export"))
"#+begin_export latex\n\n#+end_export"))
;; Tab should work for expansion.
(should
(equal (org-test-with-temp-text "<L<point>"
@ -59,6 +59,45 @@
(buffer-string))
"<k"))
(ert-deftest test-org-tempo/space-first-line ()
"Test space on first line after expansion."
;; Normal blocks should have no space at the end of the first line.
(should (zerop
(org-test-with-temp-text "<l<point>"
(org-tempo-setup)
(tempo-complete-tag)
(goto-char (point-min))
(end-of-line)
(skip-chars-backward " "))))
;; src blocks, export blocks and keywords should have one space at
;; the end of the first line.
(should (cl-every (apply-partially 'eq 1)
(mapcar (lambda (s)
(org-test-with-temp-text (format "<%s<point>" s)
(org-tempo-setup)
(tempo-complete-tag)
(goto-char (point-min))
(end-of-line)
(abs (skip-chars-backward " "))))
'("s" "E" "L")))))
(ert-deftest test-org-tempo/cursor-placement ()
"Test the placement of the cursor after tempo expand"
;; Normal blocks place point "inside" block.
(should
(eq (org-test-with-temp-text "<l<point>"
(org-tempo-setup)
(tempo-complete-tag)
(point))
(length "#\\+begin_export latex\n")))
;; Special block stop at end of #+begin line.
(should
(eq (org-test-with-temp-text "<s<point>"
(org-tempo-setup)
(tempo-complete-tag)
(point))
(length "#\\+begin_src "))))
(ert-deftest test-org-tempo/add-new-templates ()
"Test that new structures and keywords are added correctly."
;; New blocks should be added.