From b56df737b7392845c6e00d4cc52801e64c105f8b Mon Sep 17 00:00:00 2001 From: Rasmus Date: Thu, 21 Dec 2017 12:55:35 +0100 Subject: [PATCH 3/6] org: org-structure-template-alist uses string keys * lisp/org-tempo.el (org-tempo-keywords-alist): (org-tempo-setup): (org-tempo-add-templates): * testing/lisp/test-org-tempo.el (test-org-tempo/add-new-templates): * lisp/org.el (org-structure-template-alist): Use string keys. (org--insert-structure-template-mks): (org--insert-structure-template-unique-keys): New functions for block selection. (org-insert-structure-template): Use new functions. * etc/ORG-NEWS: * doc/org-manual.org: Reflect changes. --- doc/org-manual.org | 7 +- etc/ORG-NEWS | 4 +- lisp/org-tempo.el | 16 ++-- lisp/org.el | 140 ++++++++++++++++++++++++++------- testing/lisp/test-org-tempo.el | 9 ++- 5 files changed, 130 insertions(+), 46 deletions(-) diff --git a/doc/org-manual.org b/doc/org-manual.org index d787e5da4..82639445c 100644 --- a/doc/org-manual.org +++ b/doc/org-manual.org @@ -18174,9 +18174,10 @@ text in such a block. Prompt for a type of block structure, and insert the block at point. If the region is active, it is wrapped in the block. - First prompts the user for a key, which is used to look up - a structure type from the values below. If the key is - {{{kbd(TAB)}}}, the user is prompted to enter a type. + First prompts the user for keys, which are used to look up a + structure type from the variable below. If the key is + {{{kbd(TAB)}}}, {{{kbd(RET)}}}, or {{{kbd(SPC)}}}, the user is + prompted to enter a block type. #+vindex: org-structure-template-alist Available structure types are defined in diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index 0edd77115..bfb5a2dc2 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -65,8 +65,8 @@ details. *** Change ~org-structure-template-alist~ value With the new template expansion mechanism (see -[[*~org-insert-structure-template~]]), the variable changed its data type. -See docstring for details. +[[*~org-insert-structure-template~]] and =org-tempo.el=), the variable +changed its data type. See docstring for details. *** Change ~org-set-effort~ signature See docstring for details. diff --git a/lisp/org-tempo.el b/lisp/org-tempo.el index 047c4cb4a..a41c99465 100644 --- a/lisp/org-tempo.el +++ b/lisp/org-tempo.el @@ -54,10 +54,10 @@ "Tempo tags for Org mode") (defcustom org-tempo-keywords-alist - '((?L . "latex") - (?H . "html") - (?A . "ascii") - (?i . "index")) + '(("L" . "latex") + ("H" . "html") + ("A" . "ascii") + ("i" . "index")) "Keyword completion elements. Like `org-structure-template-alist' this alist of KEY characters @@ -67,7 +67,7 @@ value. For example \" n n @@ -113,7 +113,7 @@ Goes through `org-structure-template-alist' and (defun org-tempo-add-keyword (entry) "Add keyword entry from `org-tempo-keywords-alist'." - (let* ((key (format "<%c" (car entry))) + (let* ((key (format "<%s" (car entry))) (name (cdr entry))) (tempo-define-template (format "org-%s" (replace-regexp-in-string " " "-" name)) `(,(format "#+%s: " name) p '>) diff --git a/lisp/org.el b/lisp/org.el index dc751656f..bcf8b5986 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -11642,43 +11642,125 @@ keywords relative to each registered export back-end." "TITLE:" "TODO:" "TYP_TODO:" "SELECT_TAGS:" "EXCLUDE_TAGS:")) (defcustom org-structure-template-alist - '((?a . "export ascii") - (?c . "center") - (?C . "comment") - (?e . "example") - (?E . "export") - (?h . "export html") - (?l . "export latex") - (?q . "quote") - (?s . "src") - (?v . "verse")) + '(("a" . "export ascii") + ("c" . "center") + ("C" . "comment") + ("e" . "example") + ("E" . "export") + ("h" . "export html") + ("l" . "export latex") + ("q" . "quote") + ("s" . "src") + ("v" . "verse")) "Structure completion elements. -This is an alist of characters and values. When -`org-insert-structure-template' is called, an additional key is -read. The key is first looked up in this alist, and the -corresponding structure is inserted, with \"#+BEGIN_\" and -\"#+END_\" added automatically." +This is an alist of keys and block types. With +`org-insert-structure-template' a block can be inserted through a +menu. The block type is inserted, with \"#+BEGIN_\" and +\"#+END_\" added automatically. The menukeys are determined +based on the key elements in the `org-structure-template-alist'. +If two entries have the keys \"a\" and \"aa\" respectively, the +former will be inserted by typing \"a TAB/RET/SPC\" and the +latter will be inserted by typing \"aa\". If an entry with the +key \"aab\" is later added it would be inserted by typing \"ab\". + +If loaded, Org Tempo also uses `org-structure-template-alist'. A +block can be inserted by pressing TAB after the string \" (length elms) 3))) + (append + (list + ;; Make a description of the submenu. + (list topkey + (concat + (mapconcat #'cdr + (cl-subseq elms 0 (if long 3 (length elms))) + ", ") + (when long ", ...")))) + ;; List of entries in submenu. + (cl-mapcar #'list + (org--insert-structure-template-unique-keys keys) + (mapcar #'cdr elms) + (make-list (length elms) "")))))) + superlist)) + "Select a key\n============" + "Key: "))) + +(defun org--insert-structure-template-unique-keys (keys) + "Make list of unique, two character long elements from KEYS. + +Elements of length one have a tab appended. Elements of length +two are kept as is. Longer elements are truncated to length two. + +If an element cannot be made unique an error is raised." + (let ((orderd-keys (cl-sort (copy-sequence keys) #'< :key #'length)) + menu-keys) + (dolist (key orderd-keys) + (let ((potential-key + (cl-case (length key) + (1 (concat key "\t")) + (2 key) + (otherwise + (cl-find-if-not (lambda (k) (assoc k menu-keys)) + (mapcar (apply-partially #'concat (substring key 0 1)) + (split-string (substring key 1) "" t))))))) + (if (or (not potential-key) (assoc potential-key menu-keys)) + (user-error "Could not make unique key for %s." key) + (push (cons potential-key key) menu-keys)))) + (mapcar #'car + (cl-sort menu-keys #'< + :key (lambda (elm) (cl-position (cdr elm) keys)))))) + (defun org-insert-structure-template (type) - "Insert a block structure of the type #+begin_foo/#+end_foo. -First read a character, which can be one of the keys in -`org-structure-template-alist'. When it is , prompt the -user for a string to use. With an active region, wrap the region -in the block. Otherwise, insert an empty block." + "Insert a block structure of the type #+begin_foo/#+end_foo. +First choose a block based on `org-structure-template-alist'. +Alternatively, type RET, TAB or SPC to write the block type. +With an active region, wrap the region in the block. Otherwise, +insert an empty block." (interactive - (list - (let* ((key (read-key "Key: ")) - (struct-string - (or (cdr (assq key org-structure-template-alist)) - (and (= key ?\t) - (read-string "Structure type: ")) - (user-error "`%c' has no structure definition" key)))) - struct-string))) + (list (pcase (org--insert-structure-template-mks) + (`("\t" . ,_) (read-string "Structure type: ")) + (`(,_ ,choice . ,_) choice)))) (let* ((region? (use-region-p)) (s (if region? (region-beginning) (point))) (e (copy-marker (if region? (region-end) (point)) t)) diff --git a/testing/lisp/test-org-tempo.el b/testing/lisp/test-org-tempo.el index 20062feeb..6c751d4f8 100644 --- a/testing/lisp/test-org-tempo.el +++ b/testing/lisp/test-org-tempo.el @@ -61,13 +61,14 @@ (ert-deftest test-org-tempo/add-new-templates () "Test that new structures and keywords are added correctly." - ;; Check that deleted keys are not kept + ;; New blocks should be added. (should - (let ((org-structure-template-alist '((?n . "new_block")))) + (let ((org-structure-template-alist '(("n" . "new_block")))) (org-tempo-add-templates) - (assoc "