summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRasmus <rasmus@gmx.us>2017-12-05 11:33:59 +0100
committerRasmus <rasmus@gmx.us>2017-12-08 00:29:08 +0100
commit94f1a5843826ab332b601397aedb1f2b052fb0c0 (patch)
tree64d2bcb096f37e2394ae713ab091a596fe9f9f27
parentd435c75f543cce08b578a399c531a28c37a48b39 (diff)
downloadorg-mode-94f1a5843826ab332b601397aedb1f2b052fb0c0.tar.gz
org-tempo.el: New file for expansion of templates
* lisp/org-tempo.el: New file. * doc/org.texi (Structure templates): * lisp/org.el (org-modules): Add org-tempo. * testing/lisp/test-org-tempo.el: New file.
-rw-r--r--doc/org.texi19
-rw-r--r--etc/ORG-NEWS4
-rw-r--r--lisp/org-tempo.el164
-rw-r--r--lisp/org.el1
-rw-r--r--testing/lisp/test-org-tempo.el75
5 files changed, 261 insertions, 2 deletions
diff --git a/doc/org.texi b/doc/org.texi
index 5becb01..8a54ca2 100644
--- a/doc/org.texi
+++ b/doc/org.texi
@@ -11948,9 +11948,13 @@ options can be configured via @code{org-html-mathjax-options}, or in the
buffer. For example, with the following settings,
@smallexample
#+HTML_MATHJAX: align: left indent: 5em tagside: left font: Neo-Euler
+#+HTML_MATHJAX: cancel.js noErrors.js
@end smallexample
equation labels will be displayed on the left margin and equations will be
-five ems from the left margin.
+five ems from the left margin. In addition, it loads the two MathJax
+extensions @samp{cancel.js} and @samp{noErrors.js}@footnote{See
+@uref{http://docs.mathjax.org/en/latest/tex.html#tex-extensions, TeX and
+LaTeX extensions} in the @uref{http://docs.mathjax.org, MathJax manual} to learn about extensions.}.
@noindent See the docstring of
@code{org-html-mathjax-options} for all supported variables. The MathJax
@@ -17396,6 +17400,19 @@ the key is @key{TAB}, the user is prompted to enter a type.
Available structure types are defined in @code{org-structure-template-alist},
see the docstring for adding or changing values.
+@cindex Tempo
+@cindex Template expansion
+@cindex template insertion
+@cindex insertion, of templates
+@vindex org-tempo-keywords-alist
+@vindex org-structure-template-alist
+Org Tempo expands snippets to structures defined in
+@code{org-structure-template-alist} and @code{org-tempo-keywords-alist}. For
+example, @code{org-tempo} expands @kbd{< s @key{TAB}} to a code block.
+Enable it by customizing @code{org-modules} or add @code{(require
+'org-tempo)} to your Emacs init file@footnote{For more information, please
+refer to the commentary section in @file{org-tempo.el}.}.
+
@multitable @columnfractions 0.2 0.8
@item @kbd{c} @tab @samp{#+BEGIN_CENTER}
@item @kbd{C} @tab @samp{#+BEGIN_COMMENT}
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 66865ff..9d6c91e 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -72,6 +72,8 @@ document, use =shrink= value instead, or in addition to align:
#+END_EXAMPLE
** New features
+*** ~org-tempo~ may used for snippet expansion of structure template.
+See manual and commentary in ~org-tempo.el~ for details.
*** Exclude unnumbered headlines from table of contents
Set their =UNNUMBERED= property to the special =notoc= value. See
manual for details.
@@ -167,7 +169,7 @@ See docstring for details.
** Removed functions
*** ~org-try-structure-completion~
-
+=org-tempo= may be used as a replacement.
** Removed variables
*** org-babel-use-quick-and-dirty-noweb-expansion
diff --git a/lisp/org-tempo.el b/lisp/org-tempo.el
new file mode 100644
index 0000000..7c37c9f
--- /dev/null
+++ b/lisp/org-tempo.el
@@ -0,0 +1,164 @@
+;;; org-tempo.el --- Template expansion for Org structures -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2017 Free Software Foundation, Inc.
+;;
+;; Author: Rasmus Pank Roulund <emacs at pank dot eu>
+;; Keywords: outlines, hypermedia, calendar, wp
+;; Homepage: http://orgmode.org
+;;
+;; This file is part of GNU Emacs.
+;;
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+;;
+;;; Commentary:
+;;
+;; Org Tempo reimplements completions of structure template before
+;; point like `org-try-structure-completion' in Org v9.1 and earlier.
+;; For example, strings like "<e" at the beginning of the line will be
+;; expanded to an example block.
+;;
+;; All blocks defined in `org-structure-template-alist' are added as
+;; Org Tempo shortcuts, in addition to keywords defined in
+;; `org-tempo-keywords-alist'.
+;;
+;; `tempo' can also be used to define more sophisticated keywords
+;; completions. See the section "Additional keywords" below for
+;; additional details.
+;;
+;;; Code:
+
+(require 'tempo)
+(require 'cl-lib)
+
+
+(defgroup org-tempo nil
+ "Options for template expansion of Org structures"
+ :tag "Org structure"
+ :group 'org)
+
+(defvar org-tempo-tags nil
+ "Tempo tags for org-mode")
+
+(defcustom org-tempo-keywords-alist
+ '((?L . "latex")
+ (?H . "html")
+ (?A . "ascii")
+ (?i . "index"))
+ "Keyword completion elements.
+
+Like `org-structure-template-alist' this alist of KEY characters
+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:"
+ :group 'org-tempo
+ :type '(repeat (cons (character :tag "Key")
+ (string :tag "Keyword")))
+ :package-version '(Org . "9.2"))
+
+
+
+;;; Org Tempo functions and setup.
+
+(defun org-tempo-setup ()
+ (org-tempo-add-templates)
+ (tempo-use-tag-list 'org-tempo-tags)
+ (setq-local tempo-match-finder "^ *\\(<[[:word:]]\\)\\="))
+
+(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))))
+ ;; 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.
+ (mapc (lambda (key)
+ (if (assoc-string key org-tempo-tags)
+ (setq org-tempo-tags
+ (delete (assoc-string key org-tempo-tags)
+ org-tempo-tags))))
+ keys)
+ (mapc #'org-tempo-add-block org-structure-template-alist)
+ (mapc #'org-tempo-add-keyword org-tempo-keywords-alist)))
+
+(defun org-tempo-add-block (entry)
+ "Add block entry from `org-structure-template-alist'."
+ (let* ((key (format "<%c" (car entry)))
+ (name (cdr entry)))
+ (tempo-define-template (format "org-%s" (replace-regexp-in-string " " "-" name))
+ `(,(format "#+begin_%s " name) p '> n n
+ ,(format "#+end_%s" (car (split-string name " ")))
+ >)
+ key
+ (format "Insert a %s block" name)
+ 'org-tempo-tags)))
+
+(defun org-tempo-add-keyword (entry)
+ "Add keyword entry from `org-tempo-keywords-alist'."
+ (let* ((key (format "<%c" (car entry)))
+ (name (cdr entry)))
+ (tempo-define-template (format "org-%s" (replace-regexp-in-string " " "-" name))
+ `(,(format "#+%s: " name) p '>)
+ key
+ (format "Insert a %s keyword" name)
+ 'org-tempo-tags)))
+
+;;; Additional keywords
+
+(defun org-tempo--include-file ()
+ "Ask for file name and take care of quit"
+ (let* ((inhibit-quit t))
+ (unless (with-local-quit
+ (prog1 t
+ (insert
+ (format "#+include: \"%s\" " (file-relative-name
+ (read-file-name "Include file: "))))))
+ (insert "<I")
+ (setq quit-flag nil))))
+
+(tempo-define-template "org-include"
+ '((org-tempo--include-file)
+ p >)
+ "<I"
+ "Include keyword"
+ 'org-tempo-tags)
+
+
+;;; Setup of Org Tempo
+;;
+;; Org Tempo is set up with each new Org buffer and potentially in the
+;; current Org buffer.
+;;
+;; Tempo templates can only be added after Org is loaded as
+;; `org-structure-template-alist' must be loaded.
+
+(add-hook 'org-mode-hook 'org-tempo-setup)
+(add-hook 'org-tab-before-tab-emulation-hook
+ 'tempo-complete-tag)
+(when (eq major-mode 'org-mode) (org-tempo-setup))
+
+(eval-after-load 'org
+ '(org-tempo-add-templates))
+
+(provide 'org-tempo)
+
+;;; org-tempo.el ends here
diff --git a/lisp/org.el b/lisp/org.el
index f77cd62..a5c1182 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -706,6 +706,7 @@ For export specific modules, see also `org-export-backends'."
(const :tag " mouse: Additional mouse support" org-mouse)
(const :tag " protocol: Intercept calls from emacsclient" org-protocol)
(const :tag " rmail: Links to RMAIL folders/messages" org-rmail)
+ (const :tag " tempo: Fast completion for structures" org-tempo)
(const :tag " w3m: Special cut/paste from w3m to Org mode." org-w3m)
(const :tag "C annotate-file: Annotate a file with org syntax" org-annotate-file)
diff --git a/testing/lisp/test-org-tempo.el b/testing/lisp/test-org-tempo.el
new file mode 100644
index 0000000..060a7da
--- /dev/null
+++ b/testing/lisp/test-org-tempo.el
@@ -0,0 +1,75 @@
+;;; test-org-tempo.el --- Tests for test-org-tempo.el -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2017 Rasmus Pank Roulund
+
+;; Author: Rasmus Pank Roulund <emacs at pank dot eu>
+
+;; This file is not part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'org-tempo)
+
+(unless (featurep 'org-temp)
+ (signal 'missing-test-dependency "org-tempo"))
+
+(ert-deftest test-org-tempo/completion ()
+ "Test that blocks and keywords are expanded correctly by org-tempo."
+ ;; Tempo completion should recognize snippet keywords and expand with tab
+ (should
+ (equal (org-test-with-temp-text "<L<point>"
+ (org-tempo-setup)
+ (tempo-complete-tag)
+ (buffer-string))
+ "#+latex: "))
+ ;; Tempo completion should recognize snippet Blocks
+ (should
+ (equal (org-test-with-temp-text "<l<point>"
+ (org-tempo-setup)
+ (call-interactively 'org-cycle)
+ (buffer-string))
+ "#+begin_export latex \n\n#+end_export"))
+ ;; Tab should work for expansion.
+ (should
+ (equal (org-test-with-temp-text "<L<point>"
+ (org-tempo-setup)
+ (tempo-complete-tag)
+ (buffer-string))
+ (org-test-with-temp-text "<L<point>"
+ (org-tempo-setup)
+ (org-cycle)
+ (buffer-string))))
+ ;; Tempo should not expand unknown snippets
+ (equal (org-test-with-temp-text "<k"
+ (org-tempo-setup)
+ (call-interactively 'org-cycle)
+ (buffer-string))
+ "<k"))
+
+(ert-deftest test-org-tempo/add-new-templates ()
+ "Test that new structures and keywords are added correctly."
+ ;; Check that deleted keys are not kept
+ (should
+ (let ((org-structure-template-alist '((?n . "new_block"))))
+ (org-tempo-add-templates)
+ (assoc "<n" org-tempo-tags)))
+ (should
+ (let ((org-tempo-keywords-alist '((?N . "new_keyword"))))
+ (org-tempo-add-templates)
+ (assoc "<N" org-tempo-tags))))
+
+(provide 'test-org-tempo)
+;;; test-org-tempo.el end here