diff options
Diffstat (limited to 'lisp/progmodes/elisp-mode.el')
-rw-r--r-- | lisp/progmodes/elisp-mode.el | 356 |
1 files changed, 186 insertions, 170 deletions
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index a0968663163..542f8ad0b1b 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -46,160 +46,149 @@ It has `lisp-mode-abbrev-table' as its parent." "Syntax table used in `emacs-lisp-mode'.") (defvar emacs-lisp-mode-map - (let ((map (make-sparse-keymap)) - (menu-map (make-sparse-keymap "Emacs-Lisp")) - (lint-map (make-sparse-keymap)) - (prof-map (make-sparse-keymap)) - (tracing-map (make-sparse-keymap))) + (let ((map (make-sparse-keymap))) (set-keymap-parent map lisp-mode-shared-map) (define-key map "\e\t" 'completion-at-point) (define-key map "\e\C-x" 'eval-defun) (define-key map "\e\C-q" 'indent-pp-sexp) - (bindings--define-key map [menu-bar emacs-lisp] - (cons "Emacs-Lisp" menu-map)) - (bindings--define-key menu-map [eldoc] - '(menu-item "Auto-Display Documentation Strings" eldoc-mode - :button (:toggle . (bound-and-true-p eldoc-mode)) - :help "Display the documentation string for the item under cursor")) - (bindings--define-key menu-map [checkdoc] - '(menu-item "Check Documentation Strings" checkdoc - :help "Check documentation strings for style requirements")) - (bindings--define-key menu-map [re-builder] - '(menu-item "Construct Regexp" re-builder - :help "Construct a regexp interactively")) - (bindings--define-key menu-map [tracing] (cons "Tracing" tracing-map)) - (bindings--define-key tracing-map [tr-a] - '(menu-item "Untrace All" untrace-all - :help "Untrace all currently traced functions")) - (bindings--define-key tracing-map [tr-uf] - '(menu-item "Untrace Function..." untrace-function - :help "Untrace function, and possibly activate all remaining advice")) - (bindings--define-key tracing-map [tr-sep] menu-bar-separator) - (bindings--define-key tracing-map [tr-q] - '(menu-item "Trace Function Quietly..." trace-function-background - :help "Trace the function with trace output going quietly to a buffer")) - (bindings--define-key tracing-map [tr-f] - '(menu-item "Trace Function..." trace-function - :help "Trace the function given as an argument")) - (bindings--define-key menu-map [profiling] (cons "Profiling" prof-map)) - (bindings--define-key prof-map [prof-restall] - '(menu-item "Remove Instrumentation for All Functions" elp-restore-all - :help "Restore the original definitions of all functions being profiled")) - (bindings--define-key prof-map [prof-restfunc] - '(menu-item "Remove Instrumentation for Function..." elp-restore-function - :help "Restore an instrumented function to its original definition")) - - (bindings--define-key prof-map [sep-rem] menu-bar-separator) - (bindings--define-key prof-map [prof-resall] - '(menu-item "Reset Counters for All Functions" elp-reset-all - :help "Reset the profiling information for all functions being profiled")) - (bindings--define-key prof-map [prof-resfunc] - '(menu-item "Reset Counters for Function..." elp-reset-function - :help "Reset the profiling information for a function")) - (bindings--define-key prof-map [prof-res] - '(menu-item "Show Profiling Results" elp-results - :help "Display current profiling results")) - (bindings--define-key prof-map [prof-pack] - '(menu-item "Instrument Package..." elp-instrument-package - :help "Instrument for profiling all function that start with a prefix")) - (bindings--define-key prof-map [prof-func] - '(menu-item "Instrument Function..." elp-instrument-function - :help "Instrument a function for profiling")) - ;; Maybe this should be in a separate submenu from the ELP stuff? - (bindings--define-key prof-map [sep-natprof] menu-bar-separator) - (bindings--define-key prof-map [prof-natprof-stop] - '(menu-item "Stop Native Profiler" profiler-stop - :help "Stop recording profiling information" - :enable (and (featurep 'profiler) - (profiler-running-p)))) - (bindings--define-key prof-map [prof-natprof-report] - '(menu-item "Show Profiler Report" profiler-report - :help "Show the current profiler report" - :enable (and (featurep 'profiler) - (profiler-running-p)))) - (bindings--define-key prof-map [prof-natprof-start] - '(menu-item "Start Native Profiler..." profiler-start - :help "Start recording profiling information")) - - (bindings--define-key menu-map [lint] (cons "Linting" lint-map)) - (bindings--define-key lint-map [lint-di] - '(menu-item "Lint Directory..." elint-directory - :help "Lint a directory")) - (bindings--define-key lint-map [lint-f] - '(menu-item "Lint File..." elint-file - :help "Lint a file")) - (bindings--define-key lint-map [lint-b] - '(menu-item "Lint Buffer" elint-current-buffer - :help "Lint the current buffer")) - (bindings--define-key lint-map [lint-d] - '(menu-item "Lint Defun" elint-defun - :help "Lint the function at point")) - (bindings--define-key menu-map [edebug-defun] - '(menu-item "Instrument Function for Debugging" edebug-defun - :help "Evaluate the top level form point is in, stepping through with Edebug" - :keys "C-u C-M-x")) - (bindings--define-key menu-map [separator-byte] menu-bar-separator) - (bindings--define-key menu-map [disas] - '(menu-item "Disassemble Byte Compiled Object..." disassemble - :help "Print disassembled code for OBJECT in a buffer")) - (bindings--define-key menu-map [byte-recompile] - '(menu-item "Byte-recompile Directory..." byte-recompile-directory - :help "Recompile every `.el' file in DIRECTORY that needs recompilation")) - (bindings--define-key menu-map [emacs-byte-compile-and-load] - '(menu-item "Byte-compile and Load" emacs-lisp-byte-compile-and-load - :help "Byte-compile the current file (if it has changed), then load compiled code")) - (bindings--define-key menu-map [byte-compile] - '(menu-item "Byte-compile This File" emacs-lisp-byte-compile - :help "Byte compile the file containing the current buffer")) - (bindings--define-key menu-map [separator-eval] menu-bar-separator) - (bindings--define-key menu-map [ielm] - '(menu-item "Interactive Expression Evaluation" ielm - :help "Interactively evaluate Emacs Lisp expressions")) - (bindings--define-key menu-map [eval-buffer] - '(menu-item "Evaluate Buffer" eval-buffer - :help "Execute the current buffer as Lisp code")) - (bindings--define-key menu-map [eval-region] - '(menu-item "Evaluate Region" eval-region - :help "Execute the region as Lisp code" - :enable mark-active)) - (bindings--define-key menu-map [eval-sexp] - '(menu-item "Evaluate Last S-expression" eval-last-sexp - :help "Evaluate sexp before point; print value in echo area")) - (bindings--define-key menu-map [separator-format] menu-bar-separator) - (bindings--define-key menu-map [comment-region] - '(menu-item "Comment Out Region" comment-region - :help "Comment or uncomment each line in the region" - :enable mark-active)) - (bindings--define-key menu-map [indent-region] - '(menu-item "Indent Region" indent-region - :help "Indent each nonblank line in the region" - :enable mark-active)) - (bindings--define-key menu-map [indent-line] - '(menu-item "Indent Line" lisp-indent-line)) map) "Keymap for Emacs Lisp mode. All commands in `lisp-mode-shared-map' are inherited by this map.") +(easy-menu-define emacs-lisp-mode-menu emacs-lisp-mode-map + "Menu for Emacs Lisp mode." + '("Emacs-Lisp" + ["Indent Line" lisp-indent-line] + ["Indent Region" indent-region + :help "Indent each nonblank line in the region" + :active mark-active] + ["Comment Out Region" comment-region + :help "Comment or uncomment each line in the region" + :active mark-active] + "---" + ["Evaluate Last S-expression" eval-last-sexp + :help "Evaluate sexp before point; print value in echo area"] + ["Evaluate Region" eval-region + :help "Execute the region as Lisp code" + :active mark-active] + ["Evaluate Buffer" eval-buffer + :help "Execute the current buffer as Lisp code"] + ["Interactive Expression Evaluation" ielm + :help "Interactively evaluate Emacs Lisp expressions"] + "---" + ["Byte-compile This File" emacs-lisp-byte-compile + :help "Byte compile the file containing the current buffer"] + ["Byte-compile and Load" emacs-lisp-byte-compile-and-load + :help "Byte-compile the current file (if it has changed), then load compiled code"] + ["Byte-recompile Directory..." byte-recompile-directory + :help "Recompile every `.el' file in DIRECTORY that needs recompilation"] + ["Disassemble Byte Compiled Object..." disassemble + :help "Print disassembled code for OBJECT in a buffer"] + "---" + ["Instrument Function for Debugging" edebug-defun + :help "Evaluate the top level form point is in, stepping through with Edebug" + :keys "C-u C-M-x"] + ("Navigation" + ["Forward Sexp" forward-sexp + :help "Go to the next s-expression"] + ["Backward Sexp" backward-sexp + :help "Go to the previous s-expression"] + ["Beginning Of Defun" beginning-of-defun + :help "Go to the start of the current function definition"] + ["Up List" up-list + :help "Go one level up and forward"]) + ("Linting" + ["Lint Defun" elint-defun + :help "Lint the function at point"] + ["Lint Buffer" elint-current-buffer + :help "Lint the current buffer"] + ["Lint File..." elint-file + :help "Lint a file"] + ["Lint Directory..." elint-directory + :help "Lint a directory"]) + ("Profiling" + ;; Maybe this should be in a separate submenu from the ELP stuff? + ["Start Native Profiler..." profiler-start + :help "Start recording profiling information"] + ["Show Profiler Report" profiler-report + :help "Show the current profiler report" + :active (and (featurep 'profiler) + (profiler-running-p))] + ["Stop Native Profiler" profiler-stop + :help "Stop recording profiling information" + :active (and (featurep 'profiler) + (profiler-running-p))] + "---" + ["Instrument Function..." elp-instrument-function + :help "Instrument a function for profiling"] + ["Instrument Package..." elp-instrument-package + :help "Instrument for profiling all function that start with a prefix"] + ["Show Profiling Results" elp-results + :help "Display current profiling results"] + ["Reset Counters for Function..." elp-reset-function + :help "Reset the profiling information for a function"] + ["Reset Counters for All Functions" elp-reset-all + :help "Reset the profiling information for all functions being profiled"] + "---" + ["Remove Instrumentation for All Functions" elp-restore-all + :help "Restore the original definitions of all functions being profiled"] + ["Remove Instrumentation for Function..." elp-restore-function + :help "Restore an instrumented function to its original definition"]) + ("Tracing" + ["Trace Function..." trace-function + :help "Trace the function given as an argument"] + ["Trace Function Quietly..." trace-function-background + :help "Trace the function with trace output going quietly to a buffer"] + "---" + ["Untrace All" untrace-all + :help "Untrace all currently traced functions"] + ["Untrace Function..." untrace-function + :help "Untrace function, and possibly activate all remaining advice"]) + ["Construct Regexp" re-builder + :help "Construct a regexp interactively"] + ["Check Documentation Strings" checkdoc + :help "Check documentation strings for style requirements"] + ["Auto-Display Documentation Strings" eldoc-mode + :help "Display the documentation string for the item under cursor" + :style toggle + :selected (bound-and-true-p eldoc-mode)])) + (defun emacs-lisp-byte-compile () "Byte compile the file containing the current buffer." - (interactive) + (interactive nil emacs-lisp-mode) (if buffer-file-name (byte-compile-file buffer-file-name) (error "The buffer must be saved in a file first"))) -(defun emacs-lisp-byte-compile-and-load () - "Byte-compile the current file (if it has changed), then load compiled code." - (interactive) +(defun emacs-lisp--before-compile-buffer () + "Make sure the buffer is saved before compiling." (or buffer-file-name (error "The buffer must be saved in a file first")) - (require 'bytecomp) ;; Recompile if file or buffer has changed since last compilation. (if (and (buffer-modified-p) (y-or-n-p (format "Save buffer %s first? " (buffer-name)))) - (save-buffer)) + (save-buffer))) + +(defun emacs-lisp-byte-compile-and-load () + "Byte-compile the current file (if it has changed), then load compiled code." + (interactive nil emacs-lisp-mode) + (emacs-lisp--before-compile-buffer) + (require 'bytecomp) (byte-recompile-file buffer-file-name nil 0) (load buffer-file-name)) +(declare-function native-compile "comp") +(defun emacs-lisp-native-compile-and-load () + "Native-compile synchronously the current file (if it has changed). +Load the compiled code when finished. + +Use `emacs-lisp-byte-compile-and-load' in combination with +`native-comp-deferred-compilation' set to `t' to achieve asynchronous +native compilation." + (interactive nil emacs-lisp-mode) + (emacs-lisp--before-compile-buffer) + (load (native-compile buffer-file-name))) + (defun emacs-lisp-macroexpand () "Macroexpand the form after point. Comments in the form will be lost." @@ -523,7 +512,7 @@ functions are annotated with \"<f>\" via the (end (unless (or (eq beg (point-max)) (member (char-syntax (char-after beg)) - '(?\s ?\" ?\( ?\)))) + '(?\" ?\())) (condition-case nil (save-excursion (goto-char beg) @@ -557,6 +546,7 @@ functions are annotated with \"<f>\" via the ((elisp--expect-function-p beg) (list nil obarray :predicate #'fboundp + :company-kind #'elisp--company-kind :company-doc-buffer #'elisp--company-doc-buffer :company-docsig #'elisp--company-doc-string :company-location #'elisp--company-location)) @@ -570,6 +560,7 @@ functions are annotated with \"<f>\" via the (symbol-plist sym))) :annotation-function (lambda (str) (if (fboundp (intern-soft str)) " <f>")) + :company-kind #'elisp--company-kind :company-doc-buffer #'elisp--company-doc-buffer :company-docsig #'elisp--company-doc-string :company-location #'elisp--company-location)) @@ -580,6 +571,11 @@ functions are annotated with \"<f>\" via the obarray #'boundp 'strict)) + :company-kind + (lambda (s) + (if (test-completion s elisp--local-variables-completion-table) + 'value + 'variable)) :company-doc-buffer #'elisp--company-doc-buffer :company-docsig #'elisp--company-doc-string :company-location #'elisp--company-location))) @@ -626,11 +622,13 @@ functions are annotated with \"<f>\" via the (looking-at "\\_<let\\*?\\_>")))) (list t obarray :predicate #'boundp + :company-kind (lambda (_) 'variable) :company-doc-buffer #'elisp--company-doc-buffer :company-docsig #'elisp--company-doc-string :company-location #'elisp--company-location)) (_ (list nil obarray :predicate #'fboundp + :company-kind #'elisp--company-kind :company-doc-buffer #'elisp--company-doc-buffer :company-docsig #'elisp--company-doc-string :company-location #'elisp--company-location @@ -646,6 +644,16 @@ functions are annotated with \"<f>\" via the " " (cadr table-etc))) (cddr table-etc))))))))) +(defun elisp--company-kind (str) + (let ((sym (intern-soft str))) + (cond + ((or (macrop sym) (special-form-p sym)) 'keyword) + ((fboundp sym) 'function) + ((boundp sym) 'variable) + ((featurep sym) 'module) + ((facep sym) 'color) + (t 'text)))) + (defun lisp-completion-at-point (&optional _predicate) (declare (obsolete elisp-completion-at-point "25.1")) (elisp-completion-at-point)) @@ -688,7 +696,7 @@ Each function should return a list of xrefs, or nil; the first non-nil result supersedes the xrefs produced by `elisp--xref-find-definitions'.") -(cl-defmethod xref-backend-definitions ((_backend (eql elisp)) identifier) +(cl-defmethod xref-backend-definitions ((_backend (eql 'elisp)) identifier) (require 'find-func) ;; FIXME: use information in source near point to filter results: ;; (dvc-log-edit ...) - exclude 'feature @@ -867,7 +875,7 @@ non-nil result supersedes the xrefs produced by (declare-function xref-apropos-regexp "xref" (pattern)) -(cl-defmethod xref-backend-apropos ((_backend (eql elisp)) pattern) +(cl-defmethod xref-backend-apropos ((_backend (eql 'elisp)) pattern) (apply #'nconc (let ((regexp (xref-apropos-regexp pattern)) lst) @@ -885,7 +893,8 @@ non-nil result supersedes the xrefs produced by (facep sym))) 'strict)) -(cl-defmethod xref-backend-identifier-completion-table ((_backend (eql elisp))) +(cl-defmethod xref-backend-identifier-completion-table ((_backend + (eql 'elisp))) elisp--xref-identifier-completion-table) (cl-defstruct (xref-elisp-location @@ -904,7 +913,13 @@ non-nil result supersedes the xrefs produced by (point-marker))))))) (cl-defmethod xref-location-group ((l xref-elisp-location)) - (xref-elisp-location-file l)) + (let ((file (xref-elisp-location-file l))) + (defvar find-function-C-source-directory) + (if (and find-function-C-source-directory + (string-match-p "\\`src/" file)) + (concat find-function-C-source-directory + (substring file 3)) + file))) (defun elisp-load-path-roots () (if (boundp 'package-user-dir) @@ -914,35 +929,31 @@ non-nil result supersedes the xrefs produced by ;;; Elisp Interaction mode (defvar lisp-interaction-mode-map - (let ((map (make-sparse-keymap)) - (menu-map (make-sparse-keymap "Lisp-Interaction"))) + (let ((map (make-sparse-keymap))) (set-keymap-parent map lisp-mode-shared-map) (define-key map "\e\C-x" 'eval-defun) (define-key map "\e\C-q" 'indent-pp-sexp) (define-key map "\e\t" 'completion-at-point) (define-key map "\n" 'eval-print-last-sexp) - (bindings--define-key map [menu-bar lisp-interaction] - (cons "Lisp-Interaction" menu-map)) - (bindings--define-key menu-map [eval-defun] - '(menu-item "Evaluate Defun" eval-defun - :help "Evaluate the top-level form containing point, or after point")) - (bindings--define-key menu-map [eval-print-last-sexp] - '(menu-item "Evaluate and Print" eval-print-last-sexp - :help "Evaluate sexp before point; print value into current buffer")) - (bindings--define-key menu-map [edebug-defun-lisp-interaction] - '(menu-item "Instrument Function for Debugging" edebug-defun - :help "Evaluate the top level form point is in, stepping through with Edebug" - :keys "C-u C-M-x")) - (bindings--define-key menu-map [indent-pp-sexp] - '(menu-item "Indent or Pretty-Print" indent-pp-sexp - :help "Indent each line of the list starting just after point, or prettyprint it")) - (bindings--define-key menu-map [complete-symbol] - '(menu-item "Complete Lisp Symbol" completion-at-point - :help "Perform completion on Lisp symbol preceding point")) map) "Keymap for Lisp Interaction mode. All commands in `lisp-mode-shared-map' are inherited by this map.") +(easy-menu-define lisp-interaction-mode-menu lisp-interaction-mode-map + "Menu for Lisp Interaction mode." + '("Lisp-Interaction" + ["Complete Lisp Symbol" completion-at-point + :help "Perform completion on Lisp symbol preceding point"] + ["Indent or Pretty-Print" indent-pp-sexp + :help "Indent each line of the list starting just after point, or prettyprint it"] + ["Instrument Function for Debugging" edebug-defun + :help "Evaluate the top level form point is in, stepping through with Edebug" + :keys "C-u C-M-x"] + ["Evaluate and Print" eval-print-last-sexp + :help "Evaluate sexp before point; print value into current buffer"] + ["Evaluate Defun" eval-defun + :help "Evaluate the top-level form containing point, or after point"])) + (define-derived-mode lisp-interaction-mode emacs-lisp-mode "Lisp Interaction" "Major mode for typing and evaluating Lisp forms. Like Lisp mode except that \\[eval-print-last-sexp] evals the Lisp expression @@ -1268,9 +1279,8 @@ If `eval-expression-debug-on-error' is non-nil, which is the default, this command arranges for all errors to enter the debugger." (interactive "P") (if (null eval-expression-debug-on-error) - (let ((value (elisp--eval-last-sexp eval-last-sexp-arg-internal))) - (push value values) - value) + (values--store-value + (elisp--eval-last-sexp eval-last-sexp-arg-internal)) (let ((value (let ((debug-on-error elisp--eval-last-sexp-fake-value)) (cons (elisp--eval-last-sexp eval-last-sexp-arg-internal) @@ -1316,8 +1326,7 @@ Reinitialize the face according to the `defface' specification." ((eq (car form) 'custom-declare-face) ;; Reset the face. (let ((face-symbol (eval (nth 1 form) lexical-binding))) - (setq face-new-frame-defaults - (assq-delete-all face-symbol face-new-frame-defaults)) + (remhash face-symbol face--new-frame-defaults) (put face-symbol 'face-defface-spec nil) (put face-symbol 'face-override-spec nil)) form) @@ -1337,9 +1346,11 @@ if it already has a value.) Return the result of evaluation." ;; FIXME: the print-length/level bindings should only be applied while ;; printing, not while evaluating. + (defvar elisp--eval-defun-result) (let ((debug-on-error eval-expression-debug-on-error) (print-length eval-expression-print-length) - (print-level eval-expression-print-level)) + (print-level eval-expression-print-level) + elisp--eval-defun-result) (save-excursion ;; Arrange for eval-region to "read" the (possibly) altered form. ;; eval-region handles recording which file defines a function or @@ -1351,21 +1362,25 @@ Return the result of evaluation." (end-of-defun) (beginning-of-defun) (setq beg (point)) - (setq form (read (current-buffer))) + (setq form (funcall load-read-function (current-buffer))) (setq end (point))) ;; Alter the form if necessary. (let ((form (eval-sexp-add-defvars - (elisp--eval-defun-1 (macroexpand form))))) + (elisp--eval-defun-1 + (macroexpand form))))) (eval-region beg end standard-output (lambda (_ignore) ;; Skipping to the end of the specified region ;; will make eval-region return. (goto-char end) - form)))))) - (let ((str (eval-expression-print-format (car values)))) - (if str (princ str))) - ;; The result of evaluation has been put onto VALUES. So return it. - (car values)) + ;; This `setq' needs to be added *after* passing + ;; form through `elisp--eval-defun-1' since it + ;; would otherwise "hide" forms like `defvar's and + ;; thus defeat their special treatment. + `(setq elisp--eval-defun-result ,form)))))) + (let ((str (eval-expression-print-format elisp--eval-defun-result))) + (if str (princ str))) + elisp--eval-defun-result)) (defun eval-defun (edebug-it) "Evaluate the top-level form containing point, or after point. @@ -1395,6 +1410,7 @@ which see." (interactive "P") (cond (edebug-it (require 'edebug) + (defvar edebug-all-defs) (eval-defun (not edebug-all-defs))) (t (if (null eval-expression-debug-on-error) |