Use lexical-binding in tempo.el and add tests

For discussion, see the following thread:
https://lists.gnu.org/archive/html/emacs-devel/2019-05/msg00395.html
* lisp/tempo.el: Use lexical-binding.
(tempo-define-template): Expand documentation to mention
`tempo-user-elements'.
(tempo-local-tags, tempo-collection, tempo-dirty-collection)
(tempo-marks, tempo-match-finder): Define with defvar-local.
(tempo-named-insertions, tempo-region-start, tempo-region-stop): Make
them automatically buffer-local.
* test/lisp/tempo-tests.el: Add tests for tempo.el.
This commit is contained in:
Federico Tedin 2019-05-14 09:16:00 -03:00 committed by Basil L. Contovounesios
parent 5856512911
commit eb2e9a2ca2
2 changed files with 246 additions and 23 deletions

View file

@ -1,11 +1,11 @@
;;; tempo.el --- Flexible template insertion
;;; tempo.el --- Flexible template insertion -*- lexical-binding: t; -*-
;; Copyright (C) 1994-1995, 2001-2019 Free Software Foundation, Inc.
;; Author: David Kågedal <davidk@lysator.liu.se>
;; Created: 16 Feb 1994
;; Kågedal's last version number: 1.2.4
;; Keywords: extensions, languages, tools
;; Keywords: abbrev, extensions, languages, tools
;; This file is part of GNU Emacs.
@ -152,7 +152,7 @@ setting it to (upcase), for example.")
(defvar tempo-tags nil
"An association list with tags and corresponding templates.")
(defvar tempo-local-tags '((tempo-tags . nil))
(defvar-local tempo-local-tags '((tempo-tags . nil))
"A list of locally installed tag completion lists.
It is an association list where the car of every element is a symbol
whose variable value is a template list. The cdr part, if non-nil,
@ -161,16 +161,16 @@ documentation for the function `tempo-complete-tag' for more info.
`tempo-tags' is always in the last position in this list.")
(defvar tempo-collection nil
(defvar-local tempo-collection nil
"A collection of all the tags defined for the current buffer.")
(defvar tempo-dirty-collection t
(defvar-local tempo-dirty-collection t
"Indicates if the tag collection needs to be rebuilt.")
(defvar tempo-marks nil
(defvar-local tempo-marks nil
"A list of marks to jump to with `\\[tempo-forward-mark]' and `\\[tempo-backward-mark]'.")
(defvar tempo-match-finder "\\b\\([[:word:]]+\\)\\="
(defvar-local tempo-match-finder "\\b\\([[:word:]]+\\)\\="
"The regexp or function used to find the string to match against tags.
If `tempo-match-finder' is a string, it should contain a regular
@ -195,23 +195,15 @@ A list of symbols which are bound to functions that take one argument.
This function should return something to be sent to `tempo-insert' if
it recognizes the argument, and nil otherwise.")
(defvar tempo-named-insertions nil
(defvar-local tempo-named-insertions nil
"Temporary storage for named insertions.")
(defvar tempo-region-start (make-marker)
(defvar-local tempo-region-start (make-marker)
"Region start when inserting around the region.")
(defvar tempo-region-stop (make-marker)
(defvar-local tempo-region-stop (make-marker)
"Region stop when inserting around the region.")
;; Make some variables local to every buffer
(make-variable-buffer-local 'tempo-marks)
(make-variable-buffer-local 'tempo-local-tags)
(make-variable-buffer-local 'tempo-match-finder)
(make-variable-buffer-local 'tempo-collection)
(make-variable-buffer-local 'tempo-dirty-collection)
;;; Functions
;;
@ -268,11 +260,14 @@ The elements in ELEMENTS can be of several types:
- `n>': Inserts a newline and indents line.
- `o': Like `%' but leaves the point before the newline.
- nil: It is ignored.
- Anything else: It is evaluated and the result is treated as an
element to be inserted. One additional tag is useful for these
cases. If an expression returns a list (l foo bar), the elements
after `l' will be inserted according to the usual rules. This makes
it possible to return several elements from one expression."
- Anything else: Each function in `tempo-user-elements' is called
with it as argument until one of them returns non-nil, and the
result is inserted. If all of them return nil, it is evaluated and
the result is treated as an element to be inserted. One additional
tag is useful for these cases. If an expression returns a list (l
foo bar), the elements after `l' will be inserted according to the
usual rules. This makes it possible to return several elements
from one expression."
(let* ((template-name (intern (concat "tempo-template-"
name)))
(command-name template-name))

228
test/lisp/tempo-tests.el Normal file
View file

@ -0,0 +1,228 @@
;;; tempo-tests.el --- Test suite for tempo.el -*- lexical-binding: t; -*-
;; Copyright (C) 2019 Free Software Foundation, Inc.
;; Author: Federico Tedin <federicotedin@gmail.com>
;; Keywords: abbrev
;; 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/>.
;;; Code:
(require 'tempo)
(eval-when-compile (require 'cl-lib))
(ert-deftest tempo-string-element-test ()
"Test a template containing a string element."
(with-temp-buffer
(tempo-define-template "test" '("GNU Emacs Tempo test"))
(tempo-insert-template 'tempo-template-test nil)
(should (equal (buffer-string) "GNU Emacs Tempo test"))))
(ert-deftest tempo-p-bare-element-test ()
"Test a template containing a bare `p' element."
(with-temp-buffer
(tempo-define-template "test" '("abcde" p))
(tempo-insert-template 'tempo-template-test nil)
(tempo-forward-mark)
(should (equal (point) 6))))
(ert-deftest tempo-r-bare-element-test ()
"Test a template containing a bare `r' element."
(with-temp-buffer
(tempo-define-template "test" '("abcde" r "ghijk"))
(insert "F")
(set-mark (point))
(goto-char (point-min))
(tempo-insert-template 'tempo-template-test t)
(should (equal (buffer-string) "abcdeFghijk"))))
(ert-deftest tempo-p-element-test ()
"Testing template containing a `p' (prompt) element."
(with-temp-buffer
(tempo-define-template "test" '("hello " (p ">")))
(let ((tempo-interactive t))
(cl-letf (((symbol-function 'read-string) (lambda (&rest _) "world")))
(tempo-insert-template 'tempo-template-test nil))
(should (equal (buffer-string) "hello world")))))
(ert-deftest tempo-P-element-test ()
"Testing template containing a `P' (prompt) element."
(with-temp-buffer
(tempo-define-template "test" '("hello " (P ">")))
;; By default, `tempo-interactive' is nil, `P' should ignore this.
(cl-letf (((symbol-function 'read-string) (lambda (&rest _) "world")))
(tempo-insert-template 'tempo-template-test nil))
(should (equal (buffer-string) "hello world"))))
(ert-deftest tempo-r-element-test ()
"Testing template containing an `r' (with prompt) element."
(with-temp-buffer
(tempo-define-template "test" '("abcde" (r ">") "ghijk"))
(let ((tempo-interactive t))
(cl-letf (((symbol-function 'read-string) (lambda (&rest _) "F")))
(tempo-insert-template 'tempo-template-test nil))
(should (equal (buffer-string) "abcdeFghijk")))))
(ert-deftest tempo-s-element-test ()
"Testing template containing an `s' element."
(with-temp-buffer
(tempo-define-template "test" '("hello " (p ">" P1) " " (s P1)))
(let ((tempo-interactive t))
(cl-letf (((symbol-function 'read-string) (lambda (&rest _) "world!")))
(tempo-insert-template 'tempo-template-test nil))
(should (equal (buffer-string) "hello world! world!")))))
(ert-deftest tempo-&-element-test ()
"Testing template containing an `&' element."
(tempo-define-template "test" '(& "test"))
(with-temp-buffer
(insert " ")
(tempo-insert-template 'tempo-template-test nil)
(should (equal (buffer-string) " test")))
(with-temp-buffer
(insert "hello")
(tempo-insert-template 'tempo-template-test nil)
(should (equal (buffer-string) "hello\ntest"))))
(ert-deftest tempo-%-element-test ()
"Testing template containing an `%' element."
(tempo-define-template "test" '("test" %))
(with-temp-buffer
(tempo-insert-template 'tempo-template-test nil)
(should (equal (buffer-string) "test")))
(with-temp-buffer
(insert "hello")
(goto-char (point-min))
(tempo-insert-template 'tempo-template-test nil)
(should (equal (buffer-string) "test\nhello"))))
(ert-deftest tempo-n-element-test ()
"Testing template containing an `n' element."
(tempo-define-template "test" '("test" n "test"))
(with-temp-buffer
(tempo-insert-template 'tempo-template-test nil)
(should (equal (buffer-string) "test\ntest"))))
(ert-deftest tempo-n>-element-test ()
"Testing template containing an `n>' element."
(tempo-define-template "test" '("(progn" n> "(list 1 2 3))"))
(with-temp-buffer
(emacs-lisp-mode)
(tempo-insert-template 'tempo-template-test nil)
;; Tempo should have inserted two spaces before (list 1 2 3)
(should (equal (buffer-string) "(progn\n (list 1 2 3))"))))
(ert-deftest tempo->-element-test ()
"Testing template containing a `>' element."
(with-temp-buffer
(emacs-lisp-mode)
(insert "(progn\n)")
(backward-char)
(tempo-define-template "test" '("(list 1 2 3)" >))
(tempo-insert-template 'tempo-template-test nil)
;; Tempo should have inserted two spaces before (list 1 2 3)
(should (equal (buffer-string) "(progn\n (list 1 2 3))"))))
(ert-deftest tempo-r>-bare-element-test ()
"Testing template containing a bare `r>' element."
(with-temp-buffer
(tempo-define-template "test" '("(progn" n r> ")"))
(emacs-lisp-mode)
(insert "(list 1 2 3)")
(set-mark (point))
(goto-char (point-min))
(tempo-insert-template 'tempo-template-test t)
;; Tempo should have inserted two spaces before (list 1 2 3)
(should (equal (buffer-string) "(progn\n (list 1 2 3))"))))
(ert-deftest tempo-r>-element-test ()
"Testing template containing an `r>' (with prompt) element."
(tempo-define-template "test" '("(progn" n (r> ":") ")"))
(with-temp-buffer
;; Test on-region use
(emacs-lisp-mode)
(insert "(list 1 2 3)")
(set-mark (point))
(goto-char (point-min))
(tempo-insert-template 'tempo-template-test t)
(should (equal (buffer-string) "(progn\n (list 1 2 3))")))
(with-temp-buffer
;; Test interactive use
(emacs-lisp-mode)
(let ((tempo-interactive t))
(cl-letf (((symbol-function 'read-string) (lambda (&rest _) " (list 1 2 3)")))
(tempo-insert-template 'tempo-template-test nil))
(should (equal (buffer-string) "(progn\n (list 1 2 3))")))))
(ert-deftest tempo-o-element-test ()
"Testing template containing an `o' element."
(with-temp-buffer
(tempo-define-template "test" '("test" o))
(insert "hello")
(goto-char (point-min))
(tempo-insert-template 'tempo-template-test nil)
(should (equal (buffer-string) "test\nhello"))
(should (equal (point) 5))))
(ert-deftest tempo-nil-element-test ()
"Testing template with nil elements."
(with-temp-buffer
(tempo-define-template "test" '("Hello," nil " World!"))
(tempo-insert-template 'tempo-template-test nil)
(should (equal (buffer-string) "Hello, World!"))))
(ert-deftest tempo-eval-element-test ()
"Testing template with Emacs Lisp expressions."
(with-temp-buffer
(tempo-define-template "test" '((int-to-string (+ 1 1)) "=" (concat "1" "+1")))
(tempo-insert-template 'tempo-template-test nil)
(should (equal (buffer-string) "2=1+1"))))
(ert-deftest tempo-l-element-test ()
"Testing template containing an `l' element."
(with-temp-buffer
(tempo-define-template "test" '("list: " (l "1, " "2, " (int-to-string (+ 1 2)))))
(tempo-insert-template 'tempo-template-test nil)
(should (equal (buffer-string) "list: 1, 2, 3"))))
(ert-deftest tempo-tempo-user-elements-test ()
"Testing a template with elements for `tempo-user-elements'."
(with-temp-buffer
(make-local-variable 'tempo-user-elements)
(add-to-list 'tempo-user-elements (lambda (x) (int-to-string (* x x))))
(tempo-define-template "test" '(1 " " 2 " " 3 " " 4))
(tempo-insert-template 'tempo-template-test nil)
(should (equal (buffer-string) "1 4 9 16"))))
(ert-deftest tempo-expand-tag-test ()
"Testing expansion of a template with a tag."
(with-temp-buffer
(tempo-define-template "test" '("Hello, World!") "hello")
(insert "hello")
(tempo-complete-tag)
(should (equal (buffer-string) "Hello, World!"))))
(ert-deftest tempo-expand-partial-tag-test ()
"Testing expansion of a template with a tag, with a partial match."
(with-temp-buffer
(tempo-define-template "test" '("Hello, World!") "hello")
(insert "hel")
(tempo-complete-tag)
(should (equal (buffer-string) "Hello, World!"))))
(provide 'tempo-tests)
;;; tempo-tests.el ends here