emacs/test/lisp/emacs-lisp/lisp-mode-tests.el

383 lines
12 KiB
EmacsLisp
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; lisp-mode-tests.el --- Test Lisp editing commands -*- lexical-binding: t; -*-
;; Copyright (C) 2017-2024 Free Software Foundation, Inc.
;; 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 'ert)
(require 'cl-lib)
(require 'lisp-mode)
(require 'faceup)
;;; Indentation
(defconst lisp-mode-tests--correctly-indented-sexp "\
\(a
(prog1
(prog1
1
2)
2)
(fun arg1
arg2)
(1
\"string
noindent\" (\"string2
noindent\" 3
4)
2) ; comment
;; comment
b)")
(ert-deftest indent-sexp ()
"Test basics of \\[indent-sexp]."
(with-temp-buffer
(insert lisp-mode-tests--correctly-indented-sexp)
(goto-char (point-min))
(let ((indent-tabs-mode nil)
(correct lisp-mode-tests--correctly-indented-sexp))
(dolist (mode '(fundamental-mode emacs-lisp-mode))
(funcall mode)
(indent-sexp)
;; Don't mess up correctly indented code.
(should (string= (buffer-string) correct))
;; Correctly add indentation.
(save-excursion
(while (not (eobp))
(delete-horizontal-space)
(forward-line)))
(indent-sexp)
(should (equal (buffer-string) correct))
;; Correctly remove indentation.
(save-excursion
(let ((n 0))
(while (not (eobp))
(unless (looking-at "noindent\\|^[[:blank:]]*$")
(insert (make-string n ?\s)))
(cl-incf n)
(forward-line))))
(indent-sexp)
(should (equal (buffer-string) correct))))))
(ert-deftest indent-subsexp ()
"Make sure calling `indent-sexp' inside a sexp works."
(with-temp-buffer
(insert "\
\(d1 xx
(d2 yy
zz)
11)")
(let ((correct (buffer-string)))
(search-backward "d2")
(up-list -1)
(indent-sexp)
(should (equal (buffer-string) correct))
(backward-sexp)
(end-of-line)
(indent-sexp)
(should (equal (buffer-string) correct)))))
(ert-deftest indent-sexp-in-string ()
"Make sure calling `indent-sexp' inside a string works."
;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=21343.
(with-temp-buffer
(emacs-lisp-mode)
(insert "\";\"")
(let ((correct (buffer-string)))
(search-backward ";")
(indent-sexp)
(should (equal (buffer-string) correct)))))
(ert-deftest indent-sexp-stop ()
"Make sure `indent-sexp' stops at the end of the sexp."
;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=26878.
(with-temp-buffer
(emacs-lisp-mode)
(insert "(a ()\n)")
(let ((original (buffer-string)))
(search-backward "a ")
(goto-char (match-end 0))
(indent-sexp)
;; The final paren should not be indented, because the sexp
;; we're indenting ends on the previous line.
(should (equal (buffer-string) original)))))
(ert-deftest indent-sexp-go ()
"Make sure `indent-sexp' doesn't stop after #s."
;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=31984.
(with-temp-buffer
(emacs-lisp-mode)
(insert "#s(foo\nbar)\n")
(goto-char (point-min))
(indent-sexp)
(should (equal (buffer-string) "\
#s(foo
bar)\n"))))
(ert-deftest indent-sexp-cant-go ()
"`indent-sexp' shouldn't error before a sexp."
;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=31984#32.
(with-temp-buffer
(emacs-lisp-mode)
(insert "(())")
(goto-char (1+ (point-min)))
;; Paredit calls `indent-sexp' from this position.
(indent-sexp)
(should (equal (buffer-string) "(())"))))
(ert-deftest indent-sexp-stop-before-eol-comment ()
"`indent-sexp' shouldn't look for more sexps after an eol comment."
;; See https://debbugs.gnu.org/35286.
(with-temp-buffer
(emacs-lisp-mode)
(let ((str "() ;;\n x"))
(insert str)
(goto-char (point-min))
(indent-sexp)
;; The "x" is in the next sexp, so it shouldn't get indented.
(should (equal (buffer-string) str)))))
(ert-deftest indent-sexp-stop-before-eol-non-lisp ()
"`indent-sexp' shouldn't be too aggressive in non-Lisp modes."
;; See https://debbugs.gnu.org/35286#13.
(with-temp-buffer
(prolog-mode)
(let ((str "\
x(H) -->
{y(H)}.
a(A) -->
b(A)."))
(insert str)
(search-backward "{")
(indent-sexp)
;; There's no line-spanning sexp, so nothing should be indented.
(should (equal (buffer-string) str)))))
(ert-deftest lisp-indent-region ()
"Test basics of `lisp-indent-region'."
(with-temp-buffer
(insert lisp-mode-tests--correctly-indented-sexp)
(goto-char (point-min))
(let ((indent-tabs-mode nil)
(correct lisp-mode-tests--correctly-indented-sexp))
(emacs-lisp-mode)
(indent-region (point-min) (point-max))
;; Don't mess up correctly indented code.
(should (string= (buffer-string) correct))
;; Correctly add indentation.
(save-excursion
(while (not (eobp))
(delete-horizontal-space)
(forward-line)))
(indent-region (point-min) (point-max))
(should (equal (buffer-string) correct))
;; Correctly remove indentation.
(save-excursion
(let ((n 0))
(while (not (eobp))
(unless (looking-at "noindent\\|^[[:blank:]]*$")
(insert (make-string n ?\s)))
(cl-incf n)
(forward-line))))
(indent-region (point-min) (point-max))
(should (equal (buffer-string) correct)))))
(ert-deftest lisp-indent-region-defun-with-docstring ()
"Test Bug#26619."
(with-temp-buffer
(insert "\
\(defun test ()
\"This is a test.
Test indentation in emacs-lisp-mode\"
(message \"Hi!\"))")
(let ((indent-tabs-mode nil)
(correct (buffer-string)))
(emacs-lisp-mode)
(indent-region (point-min) (point-max))
(should (equal (buffer-string) correct)))))
(ert-deftest lisp-indent-region-open-paren ()
(with-temp-buffer
(insert "\
\(with-eval-after-load 'foo
(setq bar `(
baz)))")
(let ((indent-tabs-mode nil)
(correct (buffer-string)))
(emacs-lisp-mode)
(indent-region (point-min) (point-max))
(should (equal (buffer-string) correct)))))
(ert-deftest lisp-indent-region-in-sexp ()
(with-temp-buffer
(insert "\
\(when t
(when t
(list 1 2 3)
'etc)
(quote etc)
(quote etc))")
(let ((indent-tabs-mode nil)
(correct (buffer-string)))
(emacs-lisp-mode)
(search-backward "1")
(indent-region (point) (point-max))
(should (equal (buffer-string) correct)))))
(ert-deftest lisp-indent-region-after-string-literal ()
(with-temp-buffer
(insert "\
\(user-error \"Unexpected initialization file: `%s'
Expected initialization file: `%s'\"
(abbreviate-file-name user-init-file)
(abbreviate-file-name this-init-file))")
(let ((indent-tabs-mode nil)
(correct (buffer-string)))
(emacs-lisp-mode)
(indent-region (point-min) (point-max))
(should (equal (buffer-string) correct)))))
(ert-deftest lisp-comment-indent-1 ()
(with-temp-buffer
(insert "\
\(let ( ;sf
(x 3))
4)")
(let ((indent-tabs-mode nil)
(correct (buffer-string)))
(emacs-lisp-mode)
(goto-char (point-min))
(comment-indent)
(should (equal (buffer-string) correct)))))
(ert-deftest lisp-comment-indent-2 ()
(with-temp-buffer
(insert "\
\(let (;;sf
(x 3))
4)")
(let ((indent-tabs-mode nil)
(correct (buffer-string)))
(emacs-lisp-mode)
(goto-char (point-min))
(comment-indent)
(should (equal (buffer-string) correct)))))
(ert-deftest lisp-indent-with-read-only-field ()
"Test indentation on line with read-only field (Bug#32014)."
(with-temp-buffer
(insert (propertize "prompt> " 'field 'output 'read-only t
'rear-nonsticky t 'front-sticky '(read-only)))
(insert " foo")
(lisp-indent-line)
(should (equal (buffer-string) "prompt> foo"))))
(ert-deftest lisp-indent-unfinished-string ()
"Don't infloop on unfinished string (Bug#37045)."
(with-temp-buffer
(insert "\"\n")
(lisp-indent-region (point-min) (point-max))))
(ert-deftest lisp-indent-defun ()
(with-temp-buffer
(lisp-mode)
(let ((orig "(defun x ()
(print (quote ( thingy great
stuff)))
(print (quote (thingy great
stuff))))"))
(insert orig)
(indent-region (point-min) (point-max))
(should (equal (buffer-string) orig)))))
;;; Fontification
(ert-deftest lisp-fontify-confusables ()
"Unescaped 'smart quotes' should be fontified in `font-lock-warning-face'."
(with-temp-buffer
(dolist (ch
'(#x2018 ;; LEFT SINGLE QUOTATION MARK
#x2019 ;; RIGHT SINGLE QUOTATION MARK
#x201B ;; SINGLE HIGH-REVERSED-9 QUOTATION MARK
#x201C ;; LEFT DOUBLE QUOTATION MARK
#x201D ;; RIGHT DOUBLE QUOTATION MARK
#x201F ;; DOUBLE HIGH-REVERSED-9 QUOTATION MARK
#x301E ;; DOUBLE PRIME QUOTATION MARK
#xFF02 ;; FULLWIDTH QUOTATION MARK
#xFF07 ;; FULLWIDTH APOSTROPHE
))
(insert (format "«w:%c»foo \\%cfoo\n" ch ch)))
(let ((faceup (buffer-string)))
(faceup-clean-buffer)
(should (faceup-test-font-lock-buffer 'emacs-lisp-mode faceup)))))
(ert-deftest test-lisp-current-defun-name ()
(require 'edebug)
(with-temp-buffer
(emacs-lisp-mode)
(insert "(defun foo ()\n'bar)\n")
(goto-char 5)
(should (equal (lisp-current-defun-name) "foo")))
(with-temp-buffer
(emacs-lisp-mode)
(insert "(define-flabbergast-test zot ()\n'bar)\n")
(goto-char 5)
(should (equal (lisp-current-defun-name) "zot")))
;; These tests should probably work after bug#49592 has been fixed.
;; (with-temp-buffer
;; (emacs-lisp-mode)
;; (insert "(progn\n ;; comment\n ;; about that\n (define-key ...)\n )")
;; (goto-char 5)
;; (should (equal (lisp-current-defun-name) "progn")))
;; (with-temp-buffer
;; (emacs-lisp-mode)
;; (insert "(defblarg \"a\" 'b)")
;; (goto-char 5)
;; (should (equal (lisp-current-defun-name) "defblarg")))
)
(ert-deftest test-font-lock-keywords ()
"Keywords should be fontified in `font-lock-keyword-face`."
(with-temp-buffer
(emacs-lisp-mode)
(mapc (lambda (el-keyword)
(erase-buffer)
(insert (format "(%s some-symbol () \"hello\"" el-keyword))
(font-lock-ensure)
;; Verify face property throughout the keyword
(let* ((begin (1+ (point-min)))
(end (1- (+ begin (length el-keyword)))))
(mapc (lambda (pos)
(should (equal (get-text-property pos 'face)
'font-lock-keyword-face)))
(number-sequence begin end))))
'("defsubst" "cl-defsubst" "define-inline"
"define-advice" "defadvice" "defalias"
"define-derived-mode" "define-minor-mode"
"define-generic-mode" "define-global-minor-mode"
"define-globalized-minor-mode" "define-skeleton"
"define-widget" "ert-deftest" "defconst" "defcustom"
"defvaralias" "defvar-local" "defface" "define-error"))))
(provide 'lisp-mode-tests)
;;; lisp-mode-tests.el ends here