1153 lines
46 KiB
EmacsLisp
1153 lines
46 KiB
EmacsLisp
;;; elisp-mode-tests.el --- Tests for emacs-lisp-mode -*- lexical-binding: t; -*-
|
||
|
||
;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
|
||
|
||
;; Author: Dmitry Gutov <dgutov@yandex.ru>
|
||
;; Author: Stephen Leake <stephen_leake@member.fsf.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/>.
|
||
|
||
;;; Code:
|
||
|
||
(require 'ert)
|
||
(require 'ert-x)
|
||
(require 'xref)
|
||
(eval-when-compile (require 'cl-lib))
|
||
(require 'ert-x)
|
||
|
||
;;; Completion
|
||
|
||
(defun elisp--test-completions ()
|
||
(let ((data (elisp-completion-at-point)))
|
||
(all-completions (buffer-substring (nth 0 data) (nth 1 data))
|
||
(nth 2 data)
|
||
(plist-get (nthcdr 3 data) :predicate))))
|
||
|
||
(ert-deftest elisp-completes-functions ()
|
||
(with-temp-buffer
|
||
(emacs-lisp-mode)
|
||
(insert "(ba")
|
||
(let ((comps (elisp--test-completions)))
|
||
(should (member "backup-buffer" comps))
|
||
(should-not (member "backup-inhibited" comps)))))
|
||
|
||
(ert-deftest elisp-completes-variables ()
|
||
(with-temp-buffer
|
||
(emacs-lisp-mode)
|
||
(insert "(foo ba")
|
||
(let ((comps (elisp--test-completions)))
|
||
(should (member "backup-inhibited" comps))
|
||
(should-not (member "backup-buffer" comps)))))
|
||
|
||
(ert-deftest elisp-completes-anything-quoted ()
|
||
(dolist (text '("`(foo ba" "(foo 'ba"
|
||
"`(,foo ba" "`,(foo `ba"
|
||
"'(foo (ba"))
|
||
(with-temp-buffer
|
||
(emacs-lisp-mode)
|
||
(insert text)
|
||
(let ((comps (elisp--test-completions)))
|
||
(should (member "backup-inhibited" comps))
|
||
(should (member "backup-buffer" comps))
|
||
(should (member "backup" comps))))))
|
||
|
||
(ert-deftest elisp-completes-variables-unquoted ()
|
||
(dolist (text '("`(foo ,ba" "`(,(foo ba" "`(,ba"))
|
||
(with-temp-buffer
|
||
(emacs-lisp-mode)
|
||
(insert text)
|
||
(let ((comps (elisp--test-completions)))
|
||
(should (member "backup-inhibited" comps))
|
||
(should-not (member "backup-buffer" comps))))))
|
||
|
||
(ert-deftest elisp-completes-functions-in-special-macros ()
|
||
(dolist (text '("(declare-function ba" "(cl-callf2 ba"))
|
||
(with-temp-buffer
|
||
(emacs-lisp-mode)
|
||
(insert text)
|
||
(let ((comps (elisp--test-completions)))
|
||
(should (member "backup-buffer" comps))
|
||
(should-not (member "backup-inhibited" comps))))))
|
||
|
||
(ert-deftest elisp-completes-functions-after-hash-quote ()
|
||
(ert-deftest elisp-completes-functions-after-let-bindings ()
|
||
(with-temp-buffer
|
||
(emacs-lisp-mode)
|
||
(insert "#'ba")
|
||
(let ((comps (elisp--test-completions)))
|
||
(should (member "backup-buffer" comps))
|
||
(should-not (member "backup-inhibited" comps))))))
|
||
|
||
(ert-deftest elisp-completes-local-variables ()
|
||
(with-temp-buffer
|
||
(emacs-lisp-mode)
|
||
(insert "(let ((bar 1) baz) (foo ba")
|
||
(let ((comps (elisp--test-completions)))
|
||
(should (member "backup-inhibited" comps))
|
||
(should (member "bar" comps))
|
||
(should (member "baz" comps)))))
|
||
|
||
(ert-deftest elisp-completest-variables-in-let-bindings ()
|
||
(dolist (text '("(let (ba" "(let* ((ba"))
|
||
(with-temp-buffer
|
||
(emacs-lisp-mode)
|
||
(insert text)
|
||
(let ((comps (elisp--test-completions)))
|
||
(should (member "backup-inhibited" comps))
|
||
(should-not (member "backup-buffer" comps))))))
|
||
|
||
(ert-deftest elisp-completes-functions-after-empty-let-bindings ()
|
||
(with-temp-buffer
|
||
(emacs-lisp-mode)
|
||
(insert "(let () (ba")
|
||
(let ((comps (elisp--test-completions)))
|
||
(should (member "backup-buffer" comps))
|
||
(should-not (member "backup-inhibited" comps)))))
|
||
|
||
(ert-deftest elisp-completes-functions-after-let-bindings-2 ()
|
||
(with-temp-buffer
|
||
(emacs-lisp-mode)
|
||
(insert "(let ((bar 1) (baz 2)) (ba")
|
||
(let ((comps (elisp--test-completions)))
|
||
(should (member "backup-buffer" comps))
|
||
(should-not (member "backup-inhibited" comps)))))
|
||
|
||
;;; eval-last-sexp
|
||
|
||
(ert-deftest eval-last-sexp-print-format-sym ()
|
||
(with-temp-buffer
|
||
(let ((current-prefix-arg '(4)))
|
||
(erase-buffer) (insert "t")
|
||
(call-interactively #'eval-last-sexp)
|
||
(should (equal (buffer-string) "tt")))))
|
||
|
||
(ert-deftest eval-last-sexp-print-format-sym-echo ()
|
||
;; We can only check the echo area when running interactive.
|
||
(skip-when noninteractive)
|
||
(with-temp-buffer
|
||
(let ((current-prefix-arg nil))
|
||
(erase-buffer) (insert "t") (message nil)
|
||
(call-interactively #'eval-last-sexp)
|
||
(should (equal (current-message) "t")))))
|
||
|
||
(ert-deftest eval-last-sexp-print-format-small-int ()
|
||
(with-temp-buffer
|
||
(let ((current-prefix-arg '(4)))
|
||
(erase-buffer) (insert "?A")
|
||
(call-interactively #'eval-last-sexp)
|
||
(should (equal (buffer-string) "?A65")))
|
||
(let ((current-prefix-arg 0))
|
||
(erase-buffer) (insert "?A")
|
||
(call-interactively #'eval-last-sexp)
|
||
(should (equal (buffer-string) "?A65 (#o101, #x41, ?A)")))))
|
||
|
||
(ert-deftest eval-last-sexp-print-format-small-int-echo ()
|
||
(skip-when noninteractive)
|
||
(with-temp-buffer
|
||
(let ((current-prefix-arg nil))
|
||
(erase-buffer) (insert "?A") (message nil)
|
||
(call-interactively #'eval-last-sexp)
|
||
(should (equal (current-message) "65 (#o101, #x41, ?A)")))))
|
||
|
||
(ert-deftest eval-last-sexp-print-format-large-int ()
|
||
(with-temp-buffer
|
||
(let ((eval-expression-print-maximum-character ?A))
|
||
(let ((current-prefix-arg '(4)))
|
||
(erase-buffer) (insert "?B")
|
||
(call-interactively #'eval-last-sexp)
|
||
(should (equal (buffer-string) "?B66")))
|
||
(let ((current-prefix-arg 0))
|
||
(erase-buffer) (insert "?B")
|
||
(call-interactively #'eval-last-sexp)
|
||
(should (equal (buffer-string) "?B66 (#o102, #x42)")))
|
||
(let ((current-prefix-arg -1))
|
||
(erase-buffer) (insert "?B")
|
||
(call-interactively #'eval-last-sexp)
|
||
(should (equal (buffer-string) "?B66 (#o102, #x42, ?B)"))))))
|
||
|
||
(ert-deftest eval-last-sexp-print-format-large-int-echo ()
|
||
(skip-when noninteractive)
|
||
(with-temp-buffer
|
||
(let ((eval-expression-print-maximum-character ?A))
|
||
(let ((current-prefix-arg nil))
|
||
(erase-buffer) (insert "?B") (message nil)
|
||
(call-interactively #'eval-last-sexp)
|
||
(should (equal (current-message) "66 (#o102, #x42)")))
|
||
(let ((current-prefix-arg '-))
|
||
(erase-buffer) (insert "?B") (message nil)
|
||
(call-interactively #'eval-last-sexp)
|
||
(should (equal (current-message) "66 (#o102, #x42, ?B)"))))))
|
||
|
||
;;; eval-defun
|
||
|
||
(ert-deftest eval-defun-prints-edebug-when-instrumented ()
|
||
(skip-when noninteractive)
|
||
(with-temp-buffer
|
||
(let ((current-prefix-arg '(4)))
|
||
(erase-buffer) (insert "(defun foo ())") (message nil)
|
||
(call-interactively #'eval-defun)
|
||
(should (equal (current-message) "Edebug: foo")))))
|
||
|
||
;;; eldoc
|
||
|
||
(defun elisp-mode-tests--face-propertized-string (string)
|
||
"Return substring of STRING with a non-nil `face' property."
|
||
(let* ((start (next-single-property-change 0 'face string))
|
||
(end (and start (next-single-property-change start 'face string))))
|
||
(and end
|
||
(substring string start end))))
|
||
|
||
(ert-deftest elisp--highlight-function-argument-indexed ()
|
||
(dotimes (i 3)
|
||
(should
|
||
(equal (elisp-mode-tests--face-propertized-string
|
||
(elisp--highlight-function-argument 'foo "(A B C)" (1+ i)))
|
||
(propertize (nth i '("A" "B" "C"))
|
||
'face 'eldoc-highlight-function-argument)))))
|
||
|
||
(ert-deftest elisp--highlight-function-argument-keyed-1 ()
|
||
(with-temp-buffer
|
||
(emacs-lisp-mode)
|
||
(insert "(foo prompt bar :b 2)")
|
||
(goto-char (1+ (point-min)))
|
||
(cl-flet ((bold-arg (i)
|
||
(elisp-mode-tests--face-propertized-string
|
||
(elisp--highlight-function-argument
|
||
'foo "(PROMPT LST &key A B C)" i))))
|
||
(should-not (bold-arg 0))
|
||
(progn (forward-sexp) (forward-char))
|
||
(should (equal (bold-arg 1) "PROMPT"))
|
||
(progn (forward-sexp) (forward-char))
|
||
(should (equal (bold-arg 2) "LST"))
|
||
;; Both `:b' and `2' should highlight the `B' arg.
|
||
(progn (forward-sexp) (forward-char))
|
||
(should (equal (bold-arg 3) "B"))
|
||
(progn (forward-sexp) (forward-char))
|
||
(should (equal (bold-arg 4) "B")))))
|
||
|
||
(ert-deftest elisp--highlight-function-argument-keyed-2 ()
|
||
(with-temp-buffer
|
||
(emacs-lisp-mode)
|
||
(insert "(foo :b :a 1)")
|
||
(goto-char (1+ (point-min)))
|
||
(cl-flet ((bold-arg (i)
|
||
(elisp-mode-tests--face-propertized-string
|
||
(elisp--highlight-function-argument
|
||
'foo "(X &key A B C)" i))))
|
||
(should-not (bold-arg 0))
|
||
;; The `:b' specifies positional arg `X'.
|
||
(progn (forward-sexp) (forward-char))
|
||
(should (equal (bold-arg 1) "X"))
|
||
(progn (forward-sexp) (forward-char))
|
||
(should (equal (bold-arg 2) "A"))
|
||
(progn (forward-sexp) (forward-char))
|
||
(should (equal (bold-arg 3) "A")))))
|
||
|
||
;;; xref
|
||
|
||
(defun xref-elisp-test-descr-to-target (xref)
|
||
"Return an appropriate `looking-at' match string for XREF."
|
||
(let* ((loc (xref-item-location xref))
|
||
(type (or (xref-elisp-location-type loc)
|
||
'defun)))
|
||
|
||
(cl-case type
|
||
(defalias
|
||
;; summary: "(defalias xref)"
|
||
;; target : "(defalias 'xref"
|
||
(concat "(defalias '" (substring (xref-item-summary xref) 10 -1)))
|
||
|
||
(defun
|
||
(let ((summary (xref-item-summary xref))
|
||
(file (xref-elisp-location-file loc)))
|
||
(cond
|
||
((string= "c" (file-name-extension file))
|
||
;; summary: "(defun buffer-live-p)"
|
||
;; target : "DEFUN (buffer-live-p"
|
||
(concat
|
||
(upcase (substring summary 1 6))
|
||
" (\""
|
||
(substring summary 7 -1)
|
||
"\""))
|
||
|
||
(t
|
||
(substring summary 0 -1))
|
||
)))
|
||
|
||
(defvar
|
||
(let ((summary (xref-item-summary xref))
|
||
(file (xref-elisp-location-file loc)))
|
||
(cond
|
||
((string= "c" (file-name-extension file))
|
||
;; summary: "(defvar system-name)"
|
||
;; target : "DEFVAR_LISP ("system-name", "
|
||
;; summary: "(defvar abbrev-mode)"
|
||
;; target : DEFVAR_PER_BUFFER ("abbrev-mode"
|
||
(concat
|
||
(upcase (substring summary 1 7))
|
||
(if (bufferp (variable-binding-locus (xref-elisp-location-symbol loc)))
|
||
"_PER_BUFFER (\""
|
||
"_LISP (\"")
|
||
(substring summary 8 -1)
|
||
"\""))
|
||
|
||
(t
|
||
(substring summary 0 -1))
|
||
)))
|
||
|
||
(feature
|
||
;; summary: "(feature xref)"
|
||
;; target : "(provide 'xref)"
|
||
(concat "(provide '" (substring (xref-item-summary xref) 9 -1)))
|
||
|
||
(otherwise
|
||
(substring (xref-item-summary xref) 0 -1))
|
||
)))
|
||
|
||
|
||
;; tmp may be on a different filesystem to the tests, but, ehh.
|
||
(defvar xref--case-insensitive
|
||
(ert-with-temp-directory dir
|
||
(with-temp-file (expand-file-name "hElLo" dir) "hello")
|
||
(file-exists-p (expand-file-name "HELLO" dir)))
|
||
"Non-nil if file system seems to be case-insensitive.")
|
||
|
||
(defun xref-elisp-test-run (xrefs expected-xrefs)
|
||
(should (= (length xrefs) (length expected-xrefs)))
|
||
(while xrefs
|
||
(let* ((xref (pop xrefs))
|
||
(expected (pop expected-xrefs))
|
||
(expected-xref (or (when (consp expected) (car expected)) expected))
|
||
(expected-source (when (consp expected) (cdr expected)))
|
||
(xref-file (xref-elisp-location-file (xref-item-location xref)))
|
||
(expected-file (xref-elisp-location-file
|
||
(xref-item-location expected-xref))))
|
||
|
||
;; Make sure file names compare as strings.
|
||
(when (file-name-absolute-p xref-file)
|
||
(setf (xref-elisp-location-file (xref-item-location xref))
|
||
(file-truename (xref-elisp-location-file (xref-item-location xref)))))
|
||
(when (file-name-absolute-p expected-file)
|
||
(setf (xref-elisp-location-file (xref-item-location expected-xref))
|
||
(file-truename (xref-elisp-location-file
|
||
(xref-item-location expected-xref)))))
|
||
|
||
;; Downcase the filenames for case-insensitive file systems.
|
||
(when xref--case-insensitive
|
||
(setf (xref-elisp-location-file (xref-item-location xref))
|
||
(downcase (xref-elisp-location-file (xref-item-location xref))))
|
||
|
||
(setf (xref-elisp-location-file (xref-item-location expected-xref))
|
||
(downcase (xref-elisp-location-file
|
||
(xref-item-location expected-xref)))))
|
||
|
||
(should (equal xref expected-xref))
|
||
|
||
(xref--goto-location (xref-item-location xref))
|
||
(back-to-indentation)
|
||
(should (looking-at (or expected-source
|
||
(xref-elisp-test-descr-to-target expected)))))
|
||
))
|
||
|
||
(defmacro xref-elisp-deftest (name computed-xrefs expected-xrefs)
|
||
"Define an ert test for an xref-elisp feature.
|
||
COMPUTED-XREFS and EXPECTED-XREFS are lists of xrefs, except if
|
||
an element of EXPECTED-XREFS is a cons (XREF . TARGET), TARGET is
|
||
matched to the found location; otherwise, match
|
||
to (xref-elisp-test-descr-to-target xref)."
|
||
(declare (indent defun)
|
||
(debug (symbolp "name")))
|
||
`(ert-deftest ,(intern (concat "xref-elisp-test-" (symbol-name name))) ()
|
||
(let ((find-file-suppress-same-file-warnings t))
|
||
(xref-elisp-test-run ,computed-xrefs ,expected-xrefs)
|
||
)))
|
||
|
||
;; When tests are run from the Makefile, 'default-directory' is $HOME,
|
||
;; so we must provide this dir to expand-file-name in the expected
|
||
;; results. This also allows running these tests from other
|
||
;; directories.
|
||
;;
|
||
;; We add 'downcase' here to deliberately cause a potential problem on
|
||
;; case-insensitive file systems. On such systems, `load-file-name'
|
||
;; may not have the same case as the real file system, since the user
|
||
;; can set `load-path' to have the wrong case (on my Windows system,
|
||
;; `load-path' has the correct case, so this causes the expected test
|
||
;; values to have the wrong case). This is handled in
|
||
;; `xref-elisp-test-run'.
|
||
(defvar emacs-test-dir
|
||
(funcall (if xref--case-insensitive 'downcase 'identity)
|
||
(file-truename (file-name-directory
|
||
(or load-file-name (buffer-file-name))))))
|
||
|
||
|
||
;; alphabetical by test name
|
||
|
||
;; Autoloads require no special support; they are handled as functions.
|
||
|
||
;; FIXME: defalias-defun-c cmpl-prefix-entry-head
|
||
;; FIXME: defalias-defvar-el allout-mode-map
|
||
|
||
(xref-elisp-deftest find-defs-constructor
|
||
(elisp--xref-find-definitions 'xref-make-elisp-location)
|
||
;; 'xref-make-elisp-location' is just a name for the default
|
||
;; constructor created by the cl-defstruct, so the location is the
|
||
;; cl-defstruct location.
|
||
(list
|
||
(cons
|
||
(xref-make "(cl-defstruct (xref-elisp-location (:constructor xref-make-elisp-location)))"
|
||
(xref-make-elisp-location
|
||
'xref-elisp-location 'define-type
|
||
(expand-file-name "../../../lisp/progmodes/elisp-mode.el" emacs-test-dir)))
|
||
;; It's not worth adding another special case to `xref-elisp-test-descr-to-target' for this
|
||
"(cl-defstruct (xref-elisp-location")
|
||
))
|
||
|
||
(require 'em-xtra)
|
||
(require 'find-dired)
|
||
(xref-elisp-deftest find-defs-defalias-defun-el
|
||
(elisp--xref-find-definitions 'eshell/ff)
|
||
(list
|
||
(xref-make "(defalias eshell/ff)"
|
||
(xref-make-elisp-location
|
||
'eshell/ff 'defalias
|
||
(expand-file-name "../../../lisp/eshell/em-xtra.elc"
|
||
emacs-test-dir)))
|
||
(xref-make "(defun find-name-dired)"
|
||
(xref-make-elisp-location
|
||
'find-name-dired nil
|
||
(expand-file-name "../../../lisp/find-dired.el"
|
||
emacs-test-dir)))))
|
||
|
||
;; FIXME: defconst
|
||
|
||
;; Possible ways of defining the default method implementation for a
|
||
;; generic function. We declare these here, so we know we cover all
|
||
;; cases, and we don't rely on other code not changing.
|
||
;;
|
||
;; When the generic and default method are declared in the same place,
|
||
;; elisp--xref-find-definitions only returns one.
|
||
|
||
(cl-defstruct (xref-elisp-root-type)
|
||
slot-1)
|
||
|
||
(cl-defgeneric xref-elisp-generic-no-methods (arg1 arg2)
|
||
"Doc string generic no-methods."
|
||
;; No default implementation, no methods, but fboundp is true for
|
||
;; this symbol; it calls cl-no-applicable-method
|
||
)
|
||
|
||
;; WORKAROUND: ‘this’ is unused, and the byte compiler complains, so
|
||
;; it should be spelled ‘_this’. But for some unknown reason, that
|
||
;; causes the batch mode test to fail; the symbol shows up as
|
||
;; ‘this’. It passes in interactive tests, so I haven't been able to
|
||
;; track down the problem.
|
||
(cl-defmethod xref-elisp-generic-no-default ((this xref-elisp-root-type) arg2)
|
||
"Doc string generic no-default xref-elisp-root-type."
|
||
"non-default for no-default"
|
||
(list this arg2)) ; silence byte-compiler
|
||
|
||
;; defgeneric after defmethod in file to ensure the fallback search
|
||
;; method of just looking for the function name will fail.
|
||
(cl-defgeneric xref-elisp-generic-no-default (arg1 arg2)
|
||
"Doc string generic no-default generic."
|
||
;; No default implementation; this function calls the cl-generic
|
||
;; dispatching code.
|
||
)
|
||
|
||
(with-no-warnings ; FIXME: Make more specific.
|
||
(cl-defgeneric xref-elisp-generic-co-located-default (arg1 arg2)
|
||
"Doc string generic co-located-default."
|
||
"co-located default"))
|
||
|
||
(with-no-warnings ; FIXME: Make more specific.
|
||
(cl-defmethod xref-elisp-generic-co-located-default ((this xref-elisp-root-type) arg2)
|
||
"Doc string generic co-located-default xref-elisp-root-type."
|
||
"non-default for co-located-default"))
|
||
|
||
(cl-defgeneric xref-elisp-generic-separate-default (arg1 arg2)
|
||
"Doc string generic separate-default."
|
||
;; default implementation provided separately
|
||
)
|
||
|
||
(cl-defmethod xref-elisp-generic-separate-default (arg1 arg2)
|
||
"Doc string generic separate-default default."
|
||
"separate default"
|
||
(list arg1 arg2)) ; silence byte-compiler
|
||
|
||
(cl-defmethod xref-elisp-generic-separate-default ((this xref-elisp-root-type) arg2)
|
||
"Doc string generic separate-default xref-elisp-root-type."
|
||
"non-default for separate-default"
|
||
(list this arg2)) ; silence byte-compiler
|
||
|
||
(cl-defmethod xref-elisp-generic-implicit-generic (arg1 arg2)
|
||
"Doc string generic implicit-generic default."
|
||
"default for implicit generic"
|
||
(list arg1 arg2)) ; silence byte-compiler
|
||
|
||
(cl-defmethod xref-elisp-generic-implicit-generic ((this xref-elisp-root-type) arg2)
|
||
"Doc string generic implicit-generic xref-elisp-root-type."
|
||
"non-default for implicit generic"
|
||
(list this arg2)) ; silence byte-compiler
|
||
|
||
|
||
(xref-elisp-deftest find-defs-defgeneric-no-methods
|
||
(elisp--xref-find-definitions 'xref-elisp-generic-no-methods)
|
||
(list
|
||
(xref-make "(cl-defgeneric xref-elisp-generic-no-methods)"
|
||
(xref-make-elisp-location
|
||
'xref-elisp-generic-no-methods 'cl-defgeneric
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
))
|
||
|
||
(xref-elisp-deftest find-defs-defgeneric-no-default
|
||
(elisp--xref-find-definitions 'xref-elisp-generic-no-default)
|
||
(list
|
||
(xref-make "(cl-defgeneric xref-elisp-generic-no-default)"
|
||
(xref-make-elisp-location
|
||
'xref-elisp-generic-no-default 'cl-defgeneric
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
(xref-make "(cl-defmethod xref-elisp-generic-no-default ((this xref-elisp-root-type) arg2))"
|
||
(xref-make-elisp-location
|
||
(cl--generic-load-hist-format
|
||
'xref-elisp-generic-no-default nil '(xref-elisp-root-type t))
|
||
'cl-defmethod
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
))
|
||
|
||
(xref-elisp-deftest find-defs-defgeneric-co-located-default
|
||
(elisp--xref-find-definitions 'xref-elisp-generic-co-located-default)
|
||
(list
|
||
(xref-make "(cl-defgeneric xref-elisp-generic-co-located-default)"
|
||
(xref-make-elisp-location
|
||
'xref-elisp-generic-co-located-default 'cl-defgeneric
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
(xref-make "(cl-defmethod xref-elisp-generic-co-located-default ((this xref-elisp-root-type) arg2))"
|
||
(xref-make-elisp-location
|
||
(cl--generic-load-hist-format
|
||
'xref-elisp-generic-co-located-default nil
|
||
'(xref-elisp-root-type t))
|
||
'cl-defmethod
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
))
|
||
|
||
(xref-elisp-deftest find-defs-defgeneric-separate-default
|
||
(elisp--xref-find-definitions 'xref-elisp-generic-separate-default)
|
||
(list
|
||
(xref-make "(cl-defgeneric xref-elisp-generic-separate-default)"
|
||
(xref-make-elisp-location
|
||
'xref-elisp-generic-separate-default 'cl-defgeneric
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
(xref-make "(cl-defmethod xref-elisp-generic-separate-default (arg1 arg2))"
|
||
(xref-make-elisp-location
|
||
(cl--generic-load-hist-format
|
||
'xref-elisp-generic-separate-default nil '(t t))
|
||
'cl-defmethod
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
(xref-make "(cl-defmethod xref-elisp-generic-separate-default ((this xref-elisp-root-type) arg2))"
|
||
(xref-make-elisp-location
|
||
(cl--generic-load-hist-format
|
||
'xref-elisp-generic-separate-default nil
|
||
'(xref-elisp-root-type t))
|
||
'cl-defmethod
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
))
|
||
|
||
(xref-elisp-deftest find-defs-defgeneric-implicit-generic
|
||
(elisp--xref-find-definitions 'xref-elisp-generic-implicit-generic)
|
||
(list
|
||
(xref-make "(cl-defmethod xref-elisp-generic-implicit-generic (arg1 arg2))"
|
||
(xref-make-elisp-location
|
||
(cl--generic-load-hist-format
|
||
'xref-elisp-generic-implicit-generic nil '(t t))
|
||
'cl-defmethod
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
(xref-make "(cl-defmethod xref-elisp-generic-implicit-generic ((this xref-elisp-root-type) arg2))"
|
||
(xref-make-elisp-location
|
||
(cl--generic-load-hist-format
|
||
'xref-elisp-generic-implicit-generic nil
|
||
'(xref-elisp-root-type t))
|
||
'cl-defmethod
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
))
|
||
|
||
;; Test that we handle more than one method
|
||
|
||
;; When run from the Makefile, etags is not loaded at compile time,
|
||
;; but it is by the time this test is run. interactively; don't fail
|
||
;; for that.
|
||
(require 'etags)
|
||
(xref-elisp-deftest find-defs-defgeneric-el
|
||
(elisp--xref-find-definitions 'xref-location-marker)
|
||
(list
|
||
(xref-make "(cl-defgeneric xref-location-marker)"
|
||
(xref-make-elisp-location
|
||
'xref-location-marker 'cl-defgeneric
|
||
(expand-file-name "../../../lisp/progmodes/xref.el" emacs-test-dir)))
|
||
(xref-make "(cl-defmethod xref-location-marker ((l xref-elisp-location)))"
|
||
(xref-make-elisp-location
|
||
(cl--generic-load-hist-format
|
||
'xref-location-marker nil '(xref-elisp-location))
|
||
'cl-defmethod
|
||
(expand-file-name "../../../lisp/progmodes/elisp-mode.el" emacs-test-dir)))
|
||
(xref-make "(cl-defmethod xref-location-marker ((l xref-file-location)))"
|
||
(xref-make-elisp-location
|
||
(cl--generic-load-hist-format
|
||
'xref-location-marker nil '(xref-file-location))
|
||
'cl-defmethod
|
||
(expand-file-name "../../../lisp/progmodes/xref.el" emacs-test-dir)))
|
||
(xref-make "(cl-defmethod xref-location-marker ((l xref-buffer-location)))"
|
||
(xref-make-elisp-location
|
||
(cl--generic-load-hist-format
|
||
'xref-location-marker nil '(xref-buffer-location))
|
||
'cl-defmethod
|
||
(expand-file-name "../../../lisp/progmodes/xref.el" emacs-test-dir)))
|
||
(xref-make "(cl-defmethod xref-location-marker ((l xref-bogus-location)))"
|
||
(xref-make-elisp-location
|
||
(cl--generic-load-hist-format
|
||
'xref-location-marker nil '(xref-bogus-location))
|
||
'cl-defmethod
|
||
(expand-file-name "../../../lisp/progmodes/xref.el" emacs-test-dir)))
|
||
(xref-make "(cl-defmethod xref-location-marker ((l xref-etags-location)))"
|
||
(xref-make-elisp-location
|
||
(cl--generic-load-hist-format
|
||
'xref-location-marker nil '(xref-etags-location))
|
||
'cl-defmethod
|
||
(expand-file-name "../../../lisp/progmodes/etags.el" emacs-test-dir)))
|
||
(xref-make "(cl-defmethod xref-location-marker ((l xref-etags-apropos-location)))"
|
||
(xref-make-elisp-location
|
||
(cl--generic-load-hist-format
|
||
'xref-location-marker nil '(xref-etags-apropos-location))
|
||
'cl-defmethod
|
||
(expand-file-name "../../../lisp/progmodes/etags.el" emacs-test-dir)))
|
||
))
|
||
|
||
(xref-elisp-deftest find-defs-defgeneric-eval
|
||
(elisp--xref-find-definitions (eval '(cl-defgeneric stephe-leake-cl-defgeneric ()) t))
|
||
nil)
|
||
|
||
;; Define some mode-local overloadable/overridden functions for xref to find
|
||
(require 'mode-local)
|
||
|
||
(declare-function xref-elisp-overloadable-no-methods-default "elisp-mode-tests")
|
||
(declare-function xref-elisp-overloadable-no-default-default "elisp-mode-tests")
|
||
|
||
(define-overloadable-function xref-elisp-overloadable-no-methods ()
|
||
"Doc string overloadable no-methods.")
|
||
|
||
(define-overloadable-function xref-elisp-overloadable-no-default ()
|
||
"Doc string overloadable no-default.")
|
||
|
||
(define-mode-local-override xref-elisp-overloadable-no-default c-mode
|
||
(_start _end &optional _nonterminal _depth _returnonerror)
|
||
"Doc string overloadable no-default c-mode."
|
||
"result overloadable no-default c-mode.")
|
||
|
||
(define-overloadable-function xref-elisp-overloadable-co-located-default ()
|
||
"Doc string overloadable co-located-default."
|
||
"result overloadable co-located-default.")
|
||
|
||
(define-mode-local-override xref-elisp-overloadable-co-located-default c-mode
|
||
(_start _end &optional _nonterminal _depth _returnonerror)
|
||
"Doc string overloadable co-located-default c-mode."
|
||
"result overloadable co-located-default c-mode.")
|
||
|
||
(define-overloadable-function xref-elisp-overloadable-separate-default ()
|
||
"Doc string overloadable separate-default.")
|
||
|
||
(defun xref-elisp-overloadable-separate-default-default ()
|
||
"Doc string overloadable separate-default default."
|
||
"result overloadable separate-default.")
|
||
|
||
(define-mode-local-override xref-elisp-overloadable-separate-default c-mode
|
||
(_start _end &optional _nonterminal _depth _returnonerror)
|
||
"Doc string overloadable separate-default c-mode."
|
||
"result overloadable separate-default c-mode.")
|
||
|
||
(xref-elisp-deftest find-defs-define-overload-no-methods
|
||
(elisp--xref-find-definitions 'xref-elisp-overloadable-no-methods)
|
||
(list
|
||
(xref-make "(define-overloadable-function xref-elisp-overloadable-no-methods)"
|
||
(xref-make-elisp-location
|
||
'xref-elisp-overloadable-no-methods 'define-overloadable-function
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
))
|
||
|
||
(xref-elisp-deftest find-defs-define-overload-no-default
|
||
(elisp--xref-find-definitions 'xref-elisp-overloadable-no-default)
|
||
(list
|
||
(xref-make "(define-overloadable-function xref-elisp-overloadable-no-default)"
|
||
(xref-make-elisp-location
|
||
'xref-elisp-overloadable-no-default 'define-overloadable-function
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
(xref-make "(define-mode-local-override xref-elisp-overloadable-no-default c-mode)"
|
||
(xref-make-elisp-location
|
||
'(xref-elisp-overloadable-no-default-c-mode . c-mode) 'define-mode-local-override
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
))
|
||
|
||
(xref-elisp-deftest find-defs-define-overload-co-located-default
|
||
(elisp--xref-find-definitions 'xref-elisp-overloadable-co-located-default)
|
||
(list
|
||
(xref-make "(define-overloadable-function xref-elisp-overloadable-co-located-default)"
|
||
(xref-make-elisp-location
|
||
'xref-elisp-overloadable-co-located-default 'define-overloadable-function
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
(xref-make "(define-mode-local-override xref-elisp-overloadable-co-located-default c-mode)"
|
||
(xref-make-elisp-location
|
||
'(xref-elisp-overloadable-co-located-default-c-mode . c-mode) 'define-mode-local-override
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
))
|
||
|
||
(xref-elisp-deftest find-defs-define-overload-separate-default
|
||
(elisp--xref-find-definitions 'xref-elisp-overloadable-separate-default)
|
||
(list
|
||
(xref-make "(define-overloadable-function xref-elisp-overloadable-separate-default)"
|
||
(xref-make-elisp-location
|
||
'xref-elisp-overloadable-separate-default 'define-overloadable-function
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
(xref-make "(defun xref-elisp-overloadable-separate-default-default)"
|
||
(xref-make-elisp-location
|
||
'xref-elisp-overloadable-separate-default-default nil
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
(xref-make "(define-mode-local-override xref-elisp-overloadable-separate-default c-mode)"
|
||
(xref-make-elisp-location
|
||
'(xref-elisp-overloadable-separate-default-c-mode . c-mode) 'define-mode-local-override
|
||
(expand-file-name "elisp-mode-tests.el" emacs-test-dir)))
|
||
))
|
||
|
||
(xref-elisp-deftest find-defs-defun-el
|
||
(elisp--xref-find-definitions 'xref-find-definitions)
|
||
(list
|
||
(xref-make "(defun xref-find-definitions)"
|
||
(xref-make-elisp-location
|
||
'xref-find-definitions nil
|
||
(expand-file-name "../../../lisp/progmodes/xref.el" emacs-test-dir)))))
|
||
|
||
(xref-elisp-deftest find-defs-defun-eval
|
||
(elisp--xref-find-definitions (eval '(defun stephe-leake-defun ()) t))
|
||
nil)
|
||
|
||
(xref-elisp-deftest find-defs-defun-c
|
||
(elisp--xref-find-definitions 'buffer-live-p)
|
||
(list
|
||
(xref-make "(defun buffer-live-p)"
|
||
(xref-make-elisp-location 'buffer-live-p nil "src/buffer.c"))))
|
||
|
||
;; FIXME: deftype
|
||
|
||
(xref-elisp-deftest find-defs-defun-c-defvar-c
|
||
(xref-backend-definitions 'elisp "system-name")
|
||
(list
|
||
(xref-make "(defvar system-name)"
|
||
(xref-make-elisp-location 'system-name 'defvar "src/editfns.c"))
|
||
(xref-make "(defun system-name)"
|
||
(xref-make-elisp-location 'system-name nil "src/editfns.c")))
|
||
)
|
||
|
||
(xref-elisp-deftest find-defs-defun-el-defvar-c
|
||
(xref-backend-definitions 'elisp "abbrev-mode")
|
||
;; It's a minor mode, but the variable is defined in buffer.c
|
||
(list
|
||
(xref-make "(defvar abbrev-mode)"
|
||
(xref-make-elisp-location 'abbrev-mode 'defvar "src/buffer.c"))
|
||
(cons
|
||
(xref-make "(defun abbrev-mode)"
|
||
(xref-make-elisp-location
|
||
'abbrev-mode nil
|
||
(expand-file-name "../../../lisp/abbrev.el" emacs-test-dir)))
|
||
"(define-minor-mode abbrev-mode"))
|
||
)
|
||
|
||
;; Source for both variable and defun is "(define-minor-mode
|
||
;; compilation-minor-mode". There is no way to tell that directly from
|
||
;; the symbol, but we can use (memq sym minor-mode-list) to detect
|
||
;; that the symbol is a minor mode. In non-filtering mode we only
|
||
;; return the function.
|
||
(require 'compile) ;; not loaded by default at test time
|
||
(xref-elisp-deftest find-defs-defun-defvar-el
|
||
(xref-backend-definitions 'elisp "compilation-minor-mode")
|
||
(list
|
||
(cons
|
||
(xref-make "(defun compilation-minor-mode)"
|
||
(xref-make-elisp-location
|
||
'compilation-minor-mode nil
|
||
(expand-file-name "../../../lisp/progmodes/compile.el" emacs-test-dir)))
|
||
"(define-minor-mode compilation-minor-mode")
|
||
))
|
||
|
||
;; Returning only defvar because source near point indicates the user
|
||
;; is searching for a variable, not a function.
|
||
(xref-elisp-deftest find-defs-minor-defvar-c
|
||
(with-temp-buffer
|
||
(emacs-lisp-mode)
|
||
(insert "(foo overwrite-mode")
|
||
(xref-backend-definitions 'elisp
|
||
(xref-backend-identifier-at-point 'elisp)))
|
||
(list
|
||
(cons
|
||
(xref-make "(defvar overwrite-mode)"
|
||
(xref-make-elisp-location 'overwrite-mode 'defvar "src/buffer.c"))
|
||
"DEFVAR_PER_BUFFER (\"overwrite-mode\"")
|
||
))
|
||
|
||
(xref-elisp-deftest find-defs-defvar-el
|
||
(elisp--xref-find-definitions 'xref--history)
|
||
(list
|
||
(xref-make "(defvar xref--history)"
|
||
(xref-make-elisp-location
|
||
'xref--history 'defvar
|
||
(expand-file-name "../../../lisp/progmodes/xref.el" emacs-test-dir)))
|
||
))
|
||
|
||
(xref-elisp-deftest find-defs-defvar-c
|
||
(elisp--xref-find-definitions 'default-directory)
|
||
(list
|
||
(cons
|
||
(xref-make "(defvar default-directory)"
|
||
(xref-make-elisp-location 'default-directory 'defvar "src/buffer.c"))
|
||
;; IMPROVEME: we might be able to compute this target
|
||
"DEFVAR_PER_BUFFER (\"default-directory\"")))
|
||
|
||
(xref-elisp-deftest find-defs-defvar-eval
|
||
(elisp--xref-find-definitions (eval '(defvar stephe-leake-defvar nil) t))
|
||
nil)
|
||
|
||
(xref-elisp-deftest find-defs-face-el
|
||
(elisp--xref-find-definitions 'font-lock-keyword-face)
|
||
;; 'font-lock-keyword-face is both a face and a var
|
||
(list
|
||
(xref-make "(defvar font-lock-keyword-face)"
|
||
(xref-make-elisp-location
|
||
'font-lock-keyword-face 'defvar
|
||
(expand-file-name "../../../lisp/font-lock.el" emacs-test-dir)))
|
||
(xref-make "(defface font-lock-keyword-face)"
|
||
(xref-make-elisp-location
|
||
'font-lock-keyword-face 'defface
|
||
(expand-file-name "../../../lisp/font-lock.el" emacs-test-dir)))
|
||
))
|
||
|
||
(xref-elisp-deftest find-defs-face-eval
|
||
(elisp--xref-find-definitions (eval '(defface stephe-leake-defface nil "") t))
|
||
nil)
|
||
|
||
(xref-elisp-deftest find-defs-feature-el
|
||
(elisp--xref-find-definitions 'xref)
|
||
(list
|
||
(cons
|
||
(xref-make "(feature xref)"
|
||
(xref-make-elisp-location
|
||
'xref 'feature
|
||
(expand-file-name "../../../lisp/progmodes/xref.el" emacs-test-dir)))
|
||
";;; Code:")
|
||
))
|
||
|
||
(xref-elisp-deftest find-defs-feature-eval
|
||
(elisp--xref-find-definitions (eval '(provide 'stephe-leake-feature) t))
|
||
nil)
|
||
|
||
(ert-deftest elisp--preceding-sexp--char-name ()
|
||
(with-temp-buffer
|
||
(emacs-lisp-mode)
|
||
(insert "?\\N{HEAVY CHECK MARK}")
|
||
(should (equal (elisp--preceding-sexp) ?\N{HEAVY CHECK MARK}))))
|
||
|
||
(defun test--font (form search)
|
||
(with-temp-buffer
|
||
(emacs-lisp-mode)
|
||
(if (stringp form)
|
||
(insert form)
|
||
(pp form (current-buffer)))
|
||
(with-suppressed-warnings ((interactive-only font-lock-debug-fontify))
|
||
(font-lock-debug-fontify))
|
||
(goto-char (point-min))
|
||
(and (re-search-forward search nil t)
|
||
(get-text-property (match-beginning 1) 'face))))
|
||
|
||
(ert-deftest test-elisp-font-keywords-1 ()
|
||
;; Special form.
|
||
(should (eq (test--font '(if foo bar) "(\\(if\\)")
|
||
'font-lock-keyword-face))
|
||
;; Macro.
|
||
(should (eq (test--font '(when foo bar) "(\\(when\\)")
|
||
'font-lock-keyword-face))
|
||
(should (eq (test--font '(condition-case nil
|
||
(foo)
|
||
(error (if a b)))
|
||
"(\\(if\\)")
|
||
'font-lock-keyword-face))
|
||
(should (eq (test--font '(condition-case nil
|
||
(foo)
|
||
(when (if a b)))
|
||
"(\\(when\\)")
|
||
'nil)))
|
||
|
||
(ert-deftest test-elisp-font-keywords-2 ()
|
||
(should (eq (test--font '(condition-case nil
|
||
(foo)
|
||
(error (when a b)))
|
||
"(\\(when\\)")
|
||
'font-lock-keyword-face)))
|
||
|
||
(ert-deftest test-elisp-font-keywords-3 ()
|
||
(should (eq (test--font '(setq a '(if when zot))
|
||
"(\\(if\\)")
|
||
nil)))
|
||
|
||
(ert-deftest test-elisp-font-keywords-4 ()
|
||
:expected-result :failed ; FIXME bug#43265
|
||
(should (eq (test--font '(condition-case nil
|
||
(foo)
|
||
((if foo) (when a b)))
|
||
"(\\(if\\)")
|
||
nil)))
|
||
|
||
(ert-deftest test-elisp-font-keywords-5 ()
|
||
(should (eq (test--font '(condition-case (when a)
|
||
(foo)
|
||
(error t))
|
||
"(\\(when\\)")
|
||
nil)))
|
||
|
||
(defmacro elisp-mode-test--with-buffer (text-with-pos &rest body)
|
||
"Eval BODY with buffer and variables from TEXT-WITH-POS.
|
||
All occurrences of {NAME} are removed from TEXT-WITH-POS and
|
||
the remaining text put in a buffer in `elisp-mode'.
|
||
Each NAME is then bound to its position in the text during the
|
||
evaluation of BODY."
|
||
(declare (indent 1))
|
||
(let* ((annot-text (eval text-with-pos t))
|
||
(pieces nil)
|
||
(positions nil)
|
||
(tlen (length annot-text))
|
||
(ofs 0)
|
||
(text-ofs 0))
|
||
(while
|
||
(and (< ofs tlen)
|
||
(let ((m (string-match (rx "{" (group (+ (not "}"))) "}")
|
||
annot-text ofs)))
|
||
(and m
|
||
(let ((var (intern (match-string 1 annot-text))))
|
||
(push (substring annot-text ofs m) pieces)
|
||
(setq text-ofs (+ text-ofs (- m ofs)))
|
||
(push (list var (1+ text-ofs)) positions)
|
||
(setq ofs (match-end 0))
|
||
t)))))
|
||
(push (substring annot-text ofs tlen) pieces)
|
||
(let ((text (apply #'concat (nreverse pieces)))
|
||
(bindings (nreverse positions)))
|
||
`(with-temp-buffer
|
||
(ert-info (,text :prefix "text: ")
|
||
(emacs-lisp-mode)
|
||
(insert ,text)
|
||
(let ,bindings . ,body))))))
|
||
|
||
(ert-deftest elisp-mode-with-buffer ()
|
||
;; Sanity test of macro, also demonstrating how it works.
|
||
(elisp-mode-test--with-buffer
|
||
"{a}123{b}45{c}6"
|
||
(should (equal a 1))
|
||
(should (equal b 4))
|
||
(should (equal c 6))
|
||
(should (equal (buffer-string) "123456"))))
|
||
|
||
(ert-deftest elisp-mode-infer-namespace ()
|
||
(elisp-mode-test--with-buffer
|
||
(concat " ({p1}alphaX {p2}beta {p3}gamma '{p4}delta\n"
|
||
" #'{p5}epsilon `{p6}zeta `(,{p7}eta ,@{p8}theta))\n")
|
||
(should (equal (elisp--xref-infer-namespace p1) 'function))
|
||
(should (equal (elisp--xref-infer-namespace p2) 'maybe-variable))
|
||
(should (equal (elisp--xref-infer-namespace p3) 'maybe-variable))
|
||
(should (equal (elisp--xref-infer-namespace p4) 'any))
|
||
(should (equal (elisp--xref-infer-namespace p5) 'function))
|
||
(should (equal (elisp--xref-infer-namespace p6) 'any))
|
||
(should (equal (elisp--xref-infer-namespace p7) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p8) 'variable)))
|
||
|
||
(elisp-mode-test--with-buffer
|
||
(concat "(let ({p1}alpha {p2}beta ({p3}gamma {p4}delta))\n"
|
||
" ({p5}epsilon {p6}zeta)\n"
|
||
" {p7}eta)\n")
|
||
(should (equal (elisp--xref-infer-namespace p1) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p2) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p3) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p4) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p5) 'function))
|
||
(should (equal (elisp--xref-infer-namespace p6) 'maybe-variable))
|
||
(should (equal (elisp--xref-infer-namespace p7) 'variable)))
|
||
|
||
(elisp-mode-test--with-buffer
|
||
(concat "(let (({p1}alpha {p2}beta)\n"
|
||
" ({p3}gamma ({p4}delta {p5}epsilon)))\n"
|
||
" ({p6}zeta))\n")
|
||
(should (equal (elisp--xref-infer-namespace p1) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p2) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p3) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p4) 'function))
|
||
(should (equal (elisp--xref-infer-namespace p5) 'maybe-variable))
|
||
(should (equal (elisp--xref-infer-namespace p6) 'function)))
|
||
|
||
(elisp-mode-test--with-buffer
|
||
(concat "(defun {p1}alpha () {p2}beta)\n"
|
||
"(defface {p3}gamma ...)\n"
|
||
"(defvar {p4}delta {p5}epsilon)\n"
|
||
"(function {p6}zeta)\n")
|
||
(should (equal (elisp--xref-infer-namespace p1) 'function))
|
||
(should (equal (elisp--xref-infer-namespace p2) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p3) 'face))
|
||
(should (equal (elisp--xref-infer-namespace p4) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p5) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p6) 'function)))
|
||
|
||
(elisp-mode-test--with-buffer
|
||
(concat "(defclass child-class ({p1}parent-1 {p2}parent-2))\n")
|
||
(should (equal (elisp--xref-infer-namespace p1) 'function))
|
||
(should (equal (elisp--xref-infer-namespace p2) 'function)))
|
||
|
||
(elisp-mode-test--with-buffer
|
||
(concat "(require '{p1}alpha)\n"
|
||
"(fboundp '{p2}beta)\n"
|
||
"(boundp '{p3}gamma)\n"
|
||
"(facep '{p4}delta)\n"
|
||
"(define-key map [f1] '{p5}epsilon)\n")
|
||
(should (equal (elisp--xref-infer-namespace p1) 'feature))
|
||
(should (equal (elisp--xref-infer-namespace p2) 'function))
|
||
(should (equal (elisp--xref-infer-namespace p3) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p4) 'face))
|
||
(should (equal (elisp--xref-infer-namespace p5) 'function)))
|
||
|
||
(elisp-mode-test--with-buffer
|
||
(concat "(list {p1}alpha {p2}beta)\n"
|
||
"(progn {p3}gamma {p4}delta)\n"
|
||
"(lambda ({p5}epsilon {p6}zeta) {p7}eta)\n")
|
||
(should (equal (elisp--xref-infer-namespace p1) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p2) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p3) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p4) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p5) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p6) 'variable))
|
||
(should (equal (elisp--xref-infer-namespace p7) 'variable)))
|
||
|
||
(elisp-mode-test--with-buffer
|
||
(concat "'({p1}alpha {p2}beta\n"
|
||
" ({p3}gamma ({p4}delta)))\n")
|
||
(should (equal (elisp--xref-infer-namespace p1) 'any))
|
||
(should (equal (elisp--xref-infer-namespace p2) 'any))
|
||
(should (equal (elisp--xref-infer-namespace p3) 'any))
|
||
(should (equal (elisp--xref-infer-namespace p4) 'any))))
|
||
|
||
|
||
(ert-deftest elisp-shorthand-read-buffer ()
|
||
(let* ((gsym (downcase (symbol-name (cl-gensym "sh-"))))
|
||
(shorthand-sname (format "s-%s" gsym))
|
||
(expected (intern (format "shorthand-longhand-%s" gsym))))
|
||
(cl-assert (not (intern-soft shorthand-sname)))
|
||
(should (equal (let ((read-symbol-shorthands
|
||
'(("s-" . "shorthand-longhand-"))))
|
||
(with-temp-buffer
|
||
(insert shorthand-sname)
|
||
(goto-char (point-min))
|
||
(read (current-buffer))))
|
||
expected))
|
||
(should (not (intern-soft shorthand-sname)))))
|
||
|
||
(ert-deftest elisp-shorthand-read-from-string ()
|
||
(let* ((gsym (downcase (symbol-name (cl-gensym "sh-"))))
|
||
(shorthand-sname (format "s-%s" gsym))
|
||
(expected (intern (format "shorthand-longhand-%s" gsym))))
|
||
(cl-assert (not (intern-soft shorthand-sname)))
|
||
(should (equal (let ((read-symbol-shorthands
|
||
'(("s-" . "shorthand-longhand-"))))
|
||
(car (read-from-string shorthand-sname)))
|
||
expected))
|
||
(should (not (intern-soft shorthand-sname)))))
|
||
|
||
(ert-deftest elisp-shorthand-load-a-file ()
|
||
(let ((test-file (ert-resource-file "simple-shorthand-test.el")))
|
||
(mapatoms (lambda (s)
|
||
(when (string-match "^elisp--foo-" (symbol-name s))
|
||
(unintern s obarray))))
|
||
(load test-file)
|
||
(should (intern-soft "elisp--foo-test"))
|
||
(should-not (intern-soft "f-test"))))
|
||
|
||
(ert-deftest elisp-shorthand-byte-compile-a-file ()
|
||
|
||
(let ((test-file (ert-resource-file "simple-shorthand-test.el"))
|
||
(byte-compiled (ert-resource-file "simple-shorthand-test.elc")))
|
||
(mapatoms (lambda (s)
|
||
(when (string-match "^elisp--foo-" (symbol-name s))
|
||
(unintern s obarray))))
|
||
(byte-compile-file test-file)
|
||
(should-not (intern-soft "f-test"))
|
||
(should (intern-soft "elisp--foo-test"))
|
||
(should-not (fboundp (intern-soft "elisp--foo-test")))
|
||
(load byte-compiled)
|
||
(should (intern-soft "elisp--foo-test"))
|
||
(should-not (intern-soft "f-test"))))
|
||
|
||
(ert-deftest elisp-shorthand-completion-at-point ()
|
||
(let ((test-file (ert-resource-file "simple-shorthand-test.el")))
|
||
(load test-file)
|
||
(with-current-buffer (find-file-noselect test-file)
|
||
(revert-buffer t t)
|
||
(goto-char (point-min))
|
||
(insert "f-test-compl")
|
||
(completion-at-point)
|
||
(goto-char (point-min))
|
||
(should (search-forward "f-test-complete-me" (pos-eol) t))
|
||
(goto-char (point-min))
|
||
(should (string= (symbol-name (read (current-buffer)))
|
||
"elisp--foo-test-complete-me"))
|
||
(revert-buffer t t))))
|
||
|
||
(ert-deftest elisp-shorthand-escape ()
|
||
(let ((test-file (ert-resource-file "simple-shorthand-test.el")))
|
||
(load test-file)
|
||
(should (intern-soft "f-test4---"))
|
||
(should-not (intern-soft "elisp--foo-test4---"))
|
||
(should (= 84 (funcall (intern-soft "f-test4---"))))
|
||
(should (unintern "f-test4---"))))
|
||
|
||
(ert-deftest elisp-dont-shadow-punctuation-only-symbols ()
|
||
(let* ((shorthanded-form '(/= 42 (-foo 42)))
|
||
(expected-longhand-form '(/= 42 (fooey-foo 42)))
|
||
(observed (let ((read-symbol-shorthands
|
||
'(("-" . "fooey-"))))
|
||
(car (read-from-string
|
||
(with-temp-buffer
|
||
(print shorthanded-form (current-buffer))
|
||
(buffer-string)))))))
|
||
(should (equal observed expected-longhand-form))))
|
||
|
||
(ert-deftest test-indentation ()
|
||
(ert-test-erts-file (ert-resource-file "elisp-indents.erts"))
|
||
(ert-test-erts-file (ert-resource-file "flet.erts")
|
||
(lambda ()
|
||
(emacs-lisp-mode)
|
||
(indent-region (point-min) (point-max)))))
|
||
|
||
(ert-deftest elisp-tests-syntax-propertize ()
|
||
(with-temp-buffer
|
||
(emacs-lisp-mode)
|
||
(insert "(a '@)") ;bug#24542
|
||
(should (equal (scan-sexps (+ (point-min) 3) 1) (1- (point-max))))
|
||
(erase-buffer)
|
||
(insert "(a ,@)")
|
||
(should-error (scan-sexps (+ (point-min) 3) 1))))
|
||
|
||
(provide 'elisp-mode-tests)
|
||
;;; elisp-mode-tests.el ends here
|