Browse Source

Replace easy templates with org-insert-structure-template

* lisp/org.el (org-insert-structure-template): New function for
  wrapping region (or element at point) in a begin/end block.
  (org-structure-predefined-blocks): New option holding predefined
  blocks, for completion.
  (org-try-structure-completion,
  org-complete-expand-structure-template): Remove functions.
* doc/org.texi (Inserting structure templates): Document.
* testing/lisp/test-org.el (test-org/insert-template): New test.
Eric Abrahamsen 3 years ago
parent
commit
c04e357f3d
4 changed files with 158 additions and 124 deletions
  1. 46 55
      doc/org.texi
  2. 4 0
      etc/ORG-NEWS
  3. 63 69
      lisp/org.el
  4. 45 0
      testing/lisp/test-org.el

+ 46 - 55
doc/org.texi

@@ -749,7 +749,7 @@ Specific header arguments
 Miscellaneous
 
 * Completion::                  M-TAB guesses completions
-* Easy templates::              Quick insertion of structural elements
+* Structure templates::         Quick insertion of structural elements
 * Speed keys::                  Electric commands at the beginning of a headline
 * Code evaluation security::    Org mode files evaluate inline code
 * Customization::               Adapting Org to changing tastes
@@ -10108,18 +10108,16 @@ look like the fontified Emacs buffer@footnote{This works automatically for
 the HTML back-end (it requires version 1.34 of the @file{htmlize.el} package,
 which you need to install).  Fontified code chunks in @LaTeX{} can be
 achieved using either the
-@url{https://www.ctan.org/tex-archive/macros/latex/contrib/listings/?lang=en, listings,}
-or the
-@url{https://github.com/gpoore/minted, minted,} package.
+@url{https://www.ctan.org/tex-archive/macros/latex/contrib/listings/?lang=en,
+listings,} or the @url{https://github.com/gpoore/minted, minted,} package.
 If you use minted or listing, you must load the packages manually, for
-example by adding the desired package to
-@code{org-latex-packages-alist}.  Refer to @code{org-latex-listings}
-for details.}.  This is done with the @samp{src} block, where you also need
-to specify the name of the major mode that should be used to fontify the
-example@footnote{Code in @samp{src} blocks may also be evaluated either
-interactively or on export.  @xref{Working with source code}, for more
-information on evaluating code blocks.}, see @ref{Easy templates} for
-shortcuts to easily insert code blocks.
+example by adding the desired package to @code{org-latex-packages-alist}.
+Refer to @code{org-latex-listings} for details.}.  This is done with the
+@samp{src} block, where you also need to specify the name of the major mode
+that should be used to fontify the example@footnote{Code in @samp{src} blocks
+may also be evaluated either interactively or on export.  @xref{Working with
+source code}, for more information on evaluating code blocks.}, see
+@ref{Structure templates} for shortcuts to easily insert code blocks.
 @cindex #+BEGIN_SRC
 
 @example
@@ -10185,9 +10183,8 @@ If the syntax for the label format conflicts with the language syntax, use a
 HTML export also allows examples to be published as text areas (@pxref{Text
 areas in HTML export}).
 
-Because the @code{#+BEGIN_...} and @code{#+END_...} patterns need to be added
-so often, shortcuts are provided using the Easy templates facility
-(@pxref{Easy templates}).
+Because the @code{#+BEGIN_...} @dots{} @code{#+END_...} patterns need to be
+added so often, a shortcut is provided (@pxref{Structure templates}).
 
 @table @kbd
 @kindex C-c '
@@ -15243,12 +15240,12 @@ A @samp{src} block conforms to this structure:
 #+END_SRC
 @end example
 
-Org mode's templates system (@pxref{Easy templates}) speeds up creating
-@samp{src} code blocks with just three keystrokes.  Do not be put-off by
-having to remember the source block syntax.  Org also works with other
-completion systems in Emacs, some of which predate Org and have custom
-domain-specific languages for defining templates.  Regular use of templates
-reduces errors, increases accuracy, and maintains consistency.
+Do not be put-off by having to remember the source block syntax.  Org mode
+offers a command for wrapping existing text in a block (@pxref{Structure
+templates}).  Org also works with other completion systems in Emacs, some of
+which predate Org and have custom domain-specific languages for defining
+templates.  Regular use of templates reduces errors, increases accuracy, and
+maintains consistency.
 
 @cindex source code, inline
 An inline code block conforms to this structure:
@@ -17299,7 +17296,7 @@ emacs -Q --batch --eval "
 
 @menu
 * Completion::                  M-TAB guesses completions
-* Easy templates::              Quick insertion of structural elements
+* Structure templates::         Quick insertion of structural elements
 * Speed keys::                  Electric commands at the beginning of a headline
 * Code evaluation security::    Org mode files evaluate inline code
 * Customization::               Adapting Org to changing tastes
@@ -17374,46 +17371,40 @@ If your desktop intercepts the combo @kbd{M-@key{TAB}} to switch windows, use
 environment.
 @end table
 
-@node Easy templates
-@section Easy templates
+@node Structure templates
+@section Structure templates
 @cindex template insertion
 @cindex insertion, of templates
 
-With just a few keystrokes, Org's easy templates inserts empty pairs of
-structural elements, such as @code{#+BEGIN_SRC} and @code{#+END_SRC}.  Easy
-templates use an expansion mechanism, which is native to Org, in a process
-similar to @file{yasnippet} and other Emacs template expansion packages.
+With just a few keystrokes, it is possible to insert empty structural blocks,
+such as @samp{#+BEGIN_SRC} @dots{} @samp{#+END_SRC}, or to wrap existing text
+in such a block.
 
-@kbd{<} @kbd{s} @kbd{@key{TAB}} expands to a @samp{src} code block.
-
-@kbd{<} @kbd{l} @kbd{@key{TAB}} expands to:
-
-#+BEGIN_EXPORT latex
-
-#+END_EXPORT
+@table @kbd
+@orgcmd{C-c C-x w,org-insert-structure-template}
+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 @key{TAB}, the user is prompted to enter a type.
+@end table
 
-Org comes with these pre-defined easy templates:
-
-@multitable @columnfractions 0.1 0.9
-@item @kbd{s} @tab @code{#+BEGIN_SRC ... #+END_SRC}
-@item @kbd{e} @tab @code{#+BEGIN_EXAMPLE ... #+END_EXAMPLE}
-@item @kbd{q} @tab @code{#+BEGIN_QUOTE ... #+END_QUOTE}
-@item @kbd{v} @tab @code{#+BEGIN_VERSE ... #+END_VERSE}
-@item @kbd{c} @tab @code{#+BEGIN_CENTER ... #+END_CENTER}
-@item @kbd{C} @tab @code{#+BEGIN_COMMENT ... #+END_COMMENT}
-@item @kbd{l} @tab @code{#+BEGIN_EXPORT latex ... #+END_EXPORT}
-@item @kbd{L} @tab @code{#+LATEX:}
-@item @kbd{h} @tab @code{#+BEGIN_EXPORT html ... #+END_EXPORT}
-@item @kbd{H} @tab @code{#+HTML:}
-@item @kbd{a} @tab @code{#+BEGIN_EXPORT ascii ... #+END_EXPORT}
-@item @kbd{A} @tab @code{#+ASCII:}
-@item @kbd{i} @tab @code{#+INDEX:} line
-@item @kbd{I} @tab @code{#+INCLUDE:} line
+@vindex org-structure-template-alist
+Available structure types are defined in @code{org-structure-template-alist},
+see the docstring for adding or changing values.
+
+@multitable @columnfractions 0.2 0.8
+@item @kbd{c} @tab @samp{#+BEGIN_CENTER}
+@item @kbd{C} @tab @samp{#+BEGIN_COMMENT}
+@item @kbd{e} @tab @samp{#+BEGIN_EXAMPLE}
+@item @kbd{E} @tab @samp{#+BEGIN_EXPORT}
+@item @kbd{a} @tab @samp{#+BEGIN_EXPORT ascii}
+@item @kbd{h} @tab @samp{#+BEGIN_EXPORT html}
+@item @kbd{l} @tab @samp{#+BEGIN_EXPORT latex}
+@item @kbd{s} @tab @samp{#+BEGIN_SRC}
+@item @kbd{q} @tab @samp{#+BEGIN_QUOTE}
+@item @kbd{v} @tab @samp{#+BEGIN_VERSE}
 @end multitable
 
-More templates can added by customizing the variable
-@code{org-structure-template-alist}, whose docstring has additional details.
-
 @node Speed keys
 @section Speed keys
 @cindex speed keys

+ 4 - 0
etc/ORG-NEWS

@@ -149,6 +149,10 @@ you should expect to see something like:
 #+END_EXAMPLE
 
 ** New functions
+*** ~org-insert-structure-template~
+
+This function can be used to wrap existing text of Org elements in
+a #+BEGIN_FOO/#+END_FOO block.  Bound to C-c C-x w by default.
 
 *** ~org-export-excluded-from-toc-p~
 

+ 63 - 69
lisp/org.el

@@ -6740,8 +6740,6 @@ Use `\\[org-edit-special]' to edit table.el tables"))
        ((run-hook-with-args-until-success
 	 'org-tab-after-check-for-cycling-hook))
 
-       ((org-try-structure-completion))
-
        ((run-hook-with-args-until-success
 	 'org-tab-before-tab-emulation-hook))
 
@@ -11859,76 +11857,71 @@ keywords relative to each registered export back-end."
     "TITLE:" "TODO:" "TYP_TODO:" "SELECT_TAGS:" "EXCLUDE_TAGS:"))
 
 (defcustom org-structure-template-alist
-  '(("s" "#+BEGIN_SRC ?\n\n#+END_SRC")
-    ("e" "#+BEGIN_EXAMPLE\n?\n#+END_EXAMPLE")
-    ("q" "#+BEGIN_QUOTE\n?\n#+END_QUOTE")
-    ("v" "#+BEGIN_VERSE\n?\n#+END_VERSE")
-    ("V" "#+BEGIN_VERBATIM\n?\n#+END_VERBATIM")
-    ("c" "#+BEGIN_CENTER\n?\n#+END_CENTER")
-    ("C" "#+BEGIN_COMMENT\n?\n#+END_COMMENT")
-    ("l" "#+BEGIN_EXPORT latex\n?\n#+END_EXPORT")
-    ("L" "#+LaTeX: ")
-    ("h" "#+BEGIN_EXPORT html\n?\n#+END_EXPORT")
-    ("H" "#+HTML: ")
-    ("a" "#+BEGIN_EXPORT ascii\n?\n#+END_EXPORT")
-    ("A" "#+ASCII: ")
-    ("i" "#+INDEX: ?")
-    ("I" "#+INCLUDE: %file ?"))
+  '((?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 a list of abbreviation keys and values.  The value gets inserted
-if you type `<' followed by the key and then press the completion key,
-usually `TAB'.  %file will be replaced by a file name after prompting
-for the file using completion.  The cursor will be placed at the position
-of the `?' in the template.
-There are two templates for each key, the first uses the original Org syntax,
-the second uses Emacs Muse-like syntax tags.  These Muse-like tags become
-the default when the /org-mtags.el/ module has been loaded.  See also the
-variable `org-mtags-prefer-muse-templates'."
+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."
   :group 'org-completion
   :type '(repeat
-	  (list
-	   (string :tag "Key")
-	   (string :tag "Template")))
-  :version "26.1"
-  :package-version '(Org . "8.3"))
-
-(defun org-try-structure-completion ()
-  "Try to complete a structure template before point.
-This looks for strings like \"<e\" on an otherwise empty line and
-expands them."
-  (let ((l (buffer-substring (point-at-bol) (point)))
-	a)
-    (when (and (looking-at "[ \t]*$")
-	       (string-match "^[ \t]*<\\([a-zA-Z]+\\)$" l)
-	       (setq a (assoc (match-string 1 l) org-structure-template-alist)))
-      (org-complete-expand-structure-template (+ -1 (point-at-bol)
-						 (match-beginning 1)) a)
-      t)))
-
-(defun org-complete-expand-structure-template (start cell)
-  "Expand a structure template."
-  (let ((rpl (nth 1 cell))
-	(ind ""))
-    (delete-region start (point))
-    (when (string-match "\\`[ \t]*#\\+" rpl)
-      (cond
-       ((bolp))
-       ((not (string-match "\\S-" (buffer-substring (point-at-bol) (point))))
-	(setq ind (buffer-substring (point-at-bol) (point))))
-       (t (newline))))
-    (setq start (point))
-    (when (string-match "%file" rpl)
-      (setq rpl (replace-match
-		 (concat
-		  "\""
-		  (save-match-data
-		    (abbreviate-file-name (read-file-name "Include file: ")))
-		  "\"")
-		 t t rpl)))
-    (setq rpl (mapconcat 'identity (split-string rpl "\n")
-			 (concat "\n" ind)))
-    (insert rpl)
-    (when (re-search-backward "\\?" start t) (delete-char 1))))
+	  (cons (character :tag "Key")
+		(string :tag "Template")))
+  :package-version '(Org . "9.2"))
+
+(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 <TAB>, prompt the
+user for a string to use.  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)))
+  (let* ((region? (use-region-p))
+	 (s (if region? (region-beginning) (point)))
+	 (e (copy-marker (if region? (region-end) (point)) t))
+	 column)
+    (when (string-match-p
+	   (concat "\\`" (regexp-opt '("example" "export" "src")))
+	   type)
+      (org-escape-code-in-region s e))
+    (goto-char s)
+    (setq column (current-indentation))
+    (beginning-of-line)
+    (indent-to column)
+    (insert (format "#+begin_%s\n" type))
+    (goto-char e)
+    (if (bolp)
+	(progn
+	  (skip-chars-backward " \n\t")
+	  (forward-line))
+      (end-of-line)
+      (insert "\n"))
+    (indent-to column)
+    (insert (format "#+end_%s\n"
+		    (car (split-string type))))
+    (when (or (not region?)
+	      (string-match-p "src\\|\\`export\\'" type))
+      (goto-char s)
+      (end-of-line))
+    (set-marker e nil)))
 
 ;;;; TODO, DEADLINE, Comments
 
@@ -19393,6 +19386,7 @@ COMMANDS is a list of alternating OLDDEF NEWDEF command names."
 (org-defkey org-mode-map "\C-c\C-xE"    'org-inc-effort)
 (org-defkey org-mode-map "\C-c\C-xo"    'org-toggle-ordered-property)
 (org-defkey org-mode-map "\C-c\C-xi"    'org-columns-insert-dblock)
+(org-defkey org-mode-map "\C-c\C-xw"    'org-insert-structure-template)
 (org-defkey org-mode-map [(control ?c) (control ?x) ?\;] 'org-timer-set-timer)
 
 (org-defkey org-mode-map "\C-c\C-x."    'org-timer)

+ 45 - 0
testing/lisp/test-org.el

@@ -4000,6 +4000,51 @@ Text.
        (org-next-block 1 nil "^[ \t]*#\\+BEGIN_QUOTE")
        (looking-at "#\\+begin_quote")))))
 
+(ert-deftest test-org/insert-template ()
+  "Test `org-insert-structure-template'."
+  ;; Test in empty buffer.
+  (should
+   (string= "#+begin_foo\n#+end_foo\n"
+	    (org-test-with-temp-text ""
+	      (org-insert-structure-template "foo")
+	      (buffer-string))))
+  ;; Test with multiple lines in buffer.
+  (should
+   (string= "#+begin_foo\nI'm a paragraph\n#+end_foo\n\nI'm a second paragraph"
+	    (org-test-with-temp-text "I'm a paragraph\n\nI'm a second paragraph"
+	      (org-mark-element)
+	      (org-insert-structure-template "foo")
+	      (buffer-string))))
+  ;; Test with text in buffer, no region, no final newline.
+  (should
+   (string= "#+begin_foo\nI'm a paragraph.\n#+end_foo\n"
+	    (org-test-with-temp-text "I'm a paragraph."
+	      (org-mark-element)
+	      (org-insert-structure-template "foo")
+	      (buffer-string))))
+  ;; Test with text in buffer and region set.
+  (should
+   (string= "#+begin_foo\nI'm a paragraph\n\nI'm a second paragrah\n#+end_foo\n"
+	    (org-test-with-temp-text "I'm a paragraph\n\nI'm a second paragrah"
+	      (set-mark (point))
+	      (goto-char (point-max))
+	      (org-insert-structure-template "foo")
+	      (buffer-string))))
+  ;; Test with example escaping.
+  (should
+   (string= "#+begin_example\n,* Heading\n#+end_example\n"
+	    (org-test-with-temp-text "* Heading"
+	      (org-mark-element)
+	      (org-insert-structure-template "example")
+	      (buffer-string))))
+  ;; Test with indentation.
+  (should
+   (string= "  #+begin_foo\n  This is a paragraph\n  #+end_foo\n"
+	    (org-test-with-temp-text "  This is a paragraph"
+	      (org-mark-element)
+	      (org-insert-structure-template "foo")
+	      (buffer-string)))))
+
 (ert-deftest test-org/previous-block ()
   "Test `org-previous-block' specifications."
   ;; Regular test.