X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/05c7103660d89227c489e61bc8328ea26ea11629..56708dce0885db7bbfdf84803c9ad863ea9ed2e0:/lisp/emacs-lisp/lisp-mode.el diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index d08ef0660a..e0030ea766 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -1,6 +1,6 @@ -;;; lisp-mode.el --- Lisp mode, and its idiosyncratic commands. +;;; lisp-mode.el --- Lisp mode, and its idiosyncratic commands -;; Copyright (C) 1985, 1986, 1999 Free Software Foundation, Inc. +;; Copyright (C) 1985, 1986, 1999, 2000, 2001 Free Software Foundation, Inc. ;; Maintainer: FSF ;; Keywords: lisp, languages @@ -25,91 +25,143 @@ ;;; Commentary: ;; The base major mode for editing Lisp code (used also for Emacs Lisp). -;; This mode is documented in the Emacs manual +;; This mode is documented in the Emacs manual. ;;; Code: -(defvar lisp-mode-syntax-table nil "") -(defvar emacs-lisp-mode-syntax-table nil "") -(defvar lisp-mode-abbrev-table nil "") +(defvar lisp-mode-abbrev-table nil) -(if (not emacs-lisp-mode-syntax-table) +(defvar emacs-lisp-mode-syntax-table + (let ((table (make-syntax-table))) (let ((i 0)) - (setq emacs-lisp-mode-syntax-table (make-syntax-table)) (while (< i ?0) - (modify-syntax-entry i "_ " emacs-lisp-mode-syntax-table) + (modify-syntax-entry i "_ " table) (setq i (1+ i))) (setq i (1+ ?9)) (while (< i ?A) - (modify-syntax-entry i "_ " emacs-lisp-mode-syntax-table) + (modify-syntax-entry i "_ " table) (setq i (1+ i))) (setq i (1+ ?Z)) (while (< i ?a) - (modify-syntax-entry i "_ " emacs-lisp-mode-syntax-table) + (modify-syntax-entry i "_ " table) (setq i (1+ i))) (setq i (1+ ?z)) (while (< i 128) - (modify-syntax-entry i "_ " emacs-lisp-mode-syntax-table) + (modify-syntax-entry i "_ " table) (setq i (1+ i))) - (modify-syntax-entry ? " " emacs-lisp-mode-syntax-table) - (modify-syntax-entry ?\t " " emacs-lisp-mode-syntax-table) - (modify-syntax-entry ?\f " " emacs-lisp-mode-syntax-table) - (modify-syntax-entry ?\n "> " emacs-lisp-mode-syntax-table) + (modify-syntax-entry ? " " table) + (modify-syntax-entry ?\t " " table) + (modify-syntax-entry ?\f " " table) + (modify-syntax-entry ?\n "> " table) ;; Give CR the same syntax as newline, for selective-display. - (modify-syntax-entry ?\^m "> " emacs-lisp-mode-syntax-table) - (modify-syntax-entry ?\; "< " emacs-lisp-mode-syntax-table) - (modify-syntax-entry ?` "' " emacs-lisp-mode-syntax-table) - (modify-syntax-entry ?' "' " emacs-lisp-mode-syntax-table) - (modify-syntax-entry ?, "' " emacs-lisp-mode-syntax-table) + (modify-syntax-entry ?\^m "> " table) + (modify-syntax-entry ?\; "< " table) + (modify-syntax-entry ?` "' " table) + (modify-syntax-entry ?' "' " table) + (modify-syntax-entry ?, "' " table) ;; Used to be singlequote; changed for flonums. - (modify-syntax-entry ?. "_ " emacs-lisp-mode-syntax-table) - (modify-syntax-entry ?# "' " emacs-lisp-mode-syntax-table) - (modify-syntax-entry ?\" "\" " emacs-lisp-mode-syntax-table) - (modify-syntax-entry ?\\ "\\ " emacs-lisp-mode-syntax-table) - (modify-syntax-entry ?\( "() " emacs-lisp-mode-syntax-table) - (modify-syntax-entry ?\) ")( " emacs-lisp-mode-syntax-table) - (modify-syntax-entry ?\[ "(] " emacs-lisp-mode-syntax-table) - (modify-syntax-entry ?\] ")[ " emacs-lisp-mode-syntax-table) - ;; All non-word multibyte characters should be `symbol'. - (map-char-table - (function (lambda (key val) - (and (>= key 256) - (/= (char-syntax key) ?w) - (modify-syntax-entry key "_ " - emacs-lisp-mode-syntax-table)))) - (standard-syntax-table)))) - -(if (not lisp-mode-syntax-table) - (progn (setq lisp-mode-syntax-table - (copy-syntax-table emacs-lisp-mode-syntax-table)) - (modify-syntax-entry ?\| "\" " lisp-mode-syntax-table) - (modify-syntax-entry ?\[ "_ " lisp-mode-syntax-table) - (modify-syntax-entry ?\] "_ " lisp-mode-syntax-table))) + (modify-syntax-entry ?. "_ " table) + (modify-syntax-entry ?# "' " table) + (modify-syntax-entry ?\" "\" " table) + (modify-syntax-entry ?\\ "\\ " table) + (modify-syntax-entry ?\( "() " table) + (modify-syntax-entry ?\) ")( " table) + (modify-syntax-entry ?\[ "(] " table) + (modify-syntax-entry ?\] ")[ " table)) + table)) + +(defvar lisp-mode-syntax-table + (let ((table (copy-syntax-table emacs-lisp-mode-syntax-table))) + (modify-syntax-entry ?\[ "_ " table) + (modify-syntax-entry ?\] "_ " table) + (modify-syntax-entry ?# "' 14bn" table) + (modify-syntax-entry ?| "\" 23b" table) + table)) (define-abbrev-table 'lisp-mode-abbrev-table ()) (defvar lisp-imenu-generic-expression - '( - (nil - "^\\s-*(def\\(un\\|subst\\|macro\\|advice\\|ine-skeleton\\)\ -\\s-+\\([-A-Za-z0-9+*|:/]+\\)" 2) - ("Variables" - "^\\s-*(def\\(var\\|const\\|custom\\)\\s-+\\([-A-Za-z0-9+*|:/]+\\)" 2) - ("Types" - "^\\s-*(def\\(group\\|type\\|struct\\|class\\|ine-condition\ -\\|ine-widget\\)\\s-+'?\\([-A-Za-z0-9+*|:/]+\\)" + (list + (list nil + (purecopy (concat "^\\s-*(" + (eval-when-compile + (regexp-opt + '("defun" "defun*" "defsubst" "defmacro" + "defadvice" "define-skeleton" + "define-minor-mode" "define-derived-mode" + "define-compiler-macro" "define-modify-macro" + "defsetf" "define-setf-expander" + "define-method-combination" + "defgeneric" "defmethod") t)) + "\\s-+\\(\\sw\\(\\sw\\|\\s_\\)+\\)")) + 2) + (list (purecopy "Variables") + (purecopy (concat "^\\s-*(" + (eval-when-compile + (regexp-opt + '("defvar" "defconst" "defconstant" "defcustom" + "defparameter" "define-symbol-macro") t)) + "\\s-+\\(\\sw\\(\\sw\\|\\s_\\)+\\)")) + 2) + (list (purecopy "Types") + (purecopy (concat "^\\s-*(" + (eval-when-compile + (regexp-opt + '("defgroup" "deftheme" "deftype" "defstruct" + "defclass" "define-condition" "define-widget" + "defface" "defpackage") t)) + "\\s-+'?\\(\\sw\\(\\sw\\|\\s_\\)+\\)")) 2)) "Imenu generic expression for Lisp mode. See `imenu-generic-expression'.") -(defun lisp-mode-variables (lisp-syntax) - (cond (lisp-syntax - (set-syntax-table lisp-mode-syntax-table))) +;; This was originally in autoload.el and is still used there. +(put 'autoload 'doc-string-elt 3) +(put 'defun 'doc-string-elt 3) +(put 'defun* 'doc-string-elt 3) +(put 'defvar 'doc-string-elt 3) +(put 'defcustom 'doc-string-elt 3) +(put 'deftheme 'doc-string-elt 2) +(put 'defconst 'doc-string-elt 3) +(put 'defmacro 'doc-string-elt 3) +(put 'defmacro* 'doc-string-elt 3) +(put 'defsubst 'doc-string-elt 3) +(put 'define-skeleton 'doc-string-elt 2) +(put 'define-derived-mode 'doc-string-elt 4) +(put 'easy-mmode-define-minor-mode 'doc-string-elt 2) +(put 'define-minor-mode 'doc-string-elt 2) +(put 'define-generic-mode 'doc-string-elt 7) +;; define-global-mode has no explicit docstring. +(put 'easy-mmode-define-global-mode 'doc-string-elt 0) +(put 'define-ibuffer-filter 'doc-string-elt 2) +(put 'define-ibuffer-op 'doc-string-elt 3) +(put 'define-ibuffer-sorter 'doc-string-elt 2) + +(defun lisp-font-lock-syntactic-face-function (state) + (if (nth 3 state) + (if (and (eq (nth 0 state) 1) + ;; This might be a docstring. + (save-excursion + (let ((n 0)) + (goto-char (nth 8 state)) + (condition-case nil + (while (progn (backward-sexp 1) (setq n (1+ n)))) + (scan-error nil)) + (when (> n 0) + (let ((sym (intern-soft + (buffer-substring + (point) (progn (forward-sexp 1) (point)))))) + (eq n (or (get sym 'doc-string-elt) 3))))))) + font-lock-doc-face + font-lock-string-face) + font-lock-comment-face)) + +;; The LISP-SYNTAX argument is used by code in inf-lisp.el and is +;; (uselessly) passed from pp.el, chistory.el, gnus-kill.el and score-mode.el +(defun lisp-mode-variables (&optional lisp-syntax) + (when lisp-syntax + (set-syntax-table lisp-mode-syntax-table)) (setq local-abbrev-table lisp-mode-abbrev-table) - (make-local-variable 'paragraph-start) - (setq paragraph-start (concat page-delimiter "\\|$" )) - (make-local-variable 'paragraph-separate) - (setq paragraph-separate paragraph-start) (make-local-variable 'paragraph-ignore-fill-prefix) (setq paragraph-ignore-fill-prefix t) (make-local-variable 'fill-paragraph-function) @@ -117,8 +169,8 @@ ;; Adaptive fill mode gets in the way of auto-fill, ;; and should make no difference for explicit fill ;; because lisp-fill-paragraph should do the job. - (make-local-variable 'adaptive-fill-mode) - (setq adaptive-fill-mode nil) + ;; I believe that newcomment's auto-fill code properly deals with it -stef + ;;(set (make-local-variable 'adaptive-fill-mode) nil) (make-local-variable 'normal-auto-fill-function) (setq normal-auto-fill-function 'lisp-mode-auto-fill) (make-local-variable 'indent-line-function) @@ -137,12 +189,24 @@ ;; Look within the line for a ; following an even number of backslashes ;; after either a non-backslash or the line beginning. (setq comment-start-skip "\\(\\(^\\|[^\\\\\n]\\)\\(\\\\\\\\\\)*\\);+ *") + (make-local-variable 'comment-add) + (setq comment-add 1) ;default to `;;' in comment-region (make-local-variable 'comment-column) (setq comment-column 40) (make-local-variable 'comment-indent-function) (setq comment-indent-function 'lisp-comment-indent) (make-local-variable 'imenu-generic-expression) - (setq imenu-generic-expression lisp-imenu-generic-expression)) + (setq imenu-generic-expression lisp-imenu-generic-expression) + (make-local-variable 'multibyte-syntax-as-symbol) + (setq multibyte-syntax-as-symbol t) + (set (make-local-variable 'syntax-begin-function) 'beginning-of-defun) + (setq font-lock-defaults + '((lisp-font-lock-keywords + lisp-font-lock-keywords-1 lisp-font-lock-keywords-2) + nil nil (("+-*/.<>=!?$%_&~^:" . "w")) beginning-of-defun + (font-lock-mark-block-function . mark-defun) + (font-lock-syntactic-face-function + . lisp-font-lock-syntactic-face-function)))) (defun lisp-outline-level () "Lisp mode `outline-level' function." @@ -151,25 +215,28 @@ (looking-at outline-regexp) (- (match-end 0) (match-beginning 0)))) - -(defvar shared-lisp-mode-map () - "Keymap for commands shared by all sorts of Lisp modes.") -(if shared-lisp-mode-map - () - (setq shared-lisp-mode-map (make-sparse-keymap)) - (define-key shared-lisp-mode-map "\e\C-q" 'indent-sexp) - (define-key shared-lisp-mode-map "\177" 'backward-delete-char-untabify)) +(defvar lisp-mode-shared-map + (let ((map (make-sparse-keymap))) + (define-key map "\t" 'lisp-indent-line) + (define-key map "\e\C-q" 'indent-sexp) + (define-key map "\177" 'backward-delete-char-untabify) + ;; This gets in the way when viewing a Lisp file in view-mode. As + ;; long as [backspace] is mapped into DEL via the + ;; function-key-map, this should remain disabled!! + ;;;(define-key map [backspace] 'backward-delete-char-untabify) + map) + "Keymap for commands shared by all sorts of Lisp modes.") (defvar emacs-lisp-mode-map () "Keymap for Emacs Lisp mode. -All commands in `shared-lisp-mode-map' are inherited by this map.") +All commands in `lisp-mode-shared-map' are inherited by this map.") (if emacs-lisp-mode-map () (let ((map (make-sparse-keymap "Emacs-Lisp"))) (setq emacs-lisp-mode-map (make-sparse-keymap)) - (set-keymap-parent emacs-lisp-mode-map shared-lisp-mode-map) + (set-keymap-parent emacs-lisp-mode-map lisp-mode-shared-map) (define-key emacs-lisp-mode-map "\e\t" 'lisp-complete-symbol) (define-key emacs-lisp-mode-map "\e\C-x" 'eval-defun) (define-key emacs-lisp-mode-map [menu-bar] (make-sparse-keymap)) @@ -210,7 +277,7 @@ All commands in `shared-lisp-mode-map' are inherited by this map.") (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)))) + (y-or-n-p (format "Save buffer %s first? " (buffer-name)))) (save-buffer)) (let ((compiled-file-name (byte-compile-dest-file buffer-file-name))) (if (file-newer-than-file-p compiled-file-name buffer-file-name) @@ -219,7 +286,7 @@ All commands in `shared-lisp-mode-map' are inherited by this map.") (defcustom emacs-lisp-mode-hook nil "Hook run when entering Emacs Lisp mode." - :options '(turn-on-eldoc-mode imenu-add-menubar-index) + :options '(turn-on-eldoc-mode imenu-add-menubar-index checkdoc-minor-mode) :type 'hook :group 'lisp) @@ -249,20 +316,18 @@ if that value is non-nil." (set-syntax-table emacs-lisp-mode-syntax-table) (setq major-mode 'emacs-lisp-mode) (setq mode-name "Emacs-Lisp") - (lisp-mode-variables nil) + (lisp-mode-variables) (setq imenu-case-fold-search nil) (run-hooks 'emacs-lisp-mode-hook)) -(defvar lisp-mode-map () +(defvar lisp-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map lisp-mode-shared-map) + (define-key map "\e\C-x" 'lisp-eval-defun) + (define-key map "\C-c\C-z" 'run-lisp) + map) "Keymap for ordinary Lisp mode. -All commands in `shared-lisp-mode-map' are inherited by this map.") - -(if lisp-mode-map - () - (setq lisp-mode-map (make-sparse-keymap)) - (set-keymap-parent lisp-mode-map shared-lisp-mode-map) - (define-key lisp-mode-map "\e\C-x" 'lisp-eval-defun) - (define-key lisp-mode-map "\C-c\C-z" 'run-lisp)) +All commands in `lisp-mode-shared-map' are inherited by this map.") (defun lisp-mode () "Major mode for editing Lisp code for Lisps other than GNU Emacs Lisp. @@ -280,33 +345,39 @@ if that value is non-nil." (use-local-map lisp-mode-map) (setq major-mode 'lisp-mode) (setq mode-name "Lisp") - (lisp-mode-variables t) + (lisp-mode-variables) + (make-local-variable 'comment-start-skip) + (setq comment-start-skip + "\\(\\(^\\|[^\\\\\n]\\)\\(\\\\\\\\\\)*\\)\\(;+\\|#|\\) *") + (make-local-variable 'font-lock-keywords-case-fold-search) + (setq font-lock-keywords-case-fold-search t) (setq imenu-case-fold-search t) (set-syntax-table lisp-mode-syntax-table) (run-hooks 'lisp-mode-hook)) -;; This will do unless shell.el is loaded. -(defun lisp-eval-defun nil +;; This will do unless inf-lisp.el is loaded. +(defun lisp-eval-defun (&optional and-go) "Send the current defun to the Lisp process made by \\[run-lisp]." (interactive) (error "Process lisp does not exist")) -(defvar lisp-interaction-mode-map () +(defvar lisp-interaction-mode-map + (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\t" 'lisp-complete-symbol) + (define-key map "\n" 'eval-print-last-sexp) + map) "Keymap for Lisp Interaction mode. -All commands in `shared-lisp-mode-map' are inherited by this map.") - -(if lisp-interaction-mode-map - () - (setq lisp-interaction-mode-map (make-sparse-keymap)) - (set-keymap-parent lisp-interaction-mode-map shared-lisp-mode-map) - (define-key lisp-interaction-mode-map "\e\C-x" 'eval-defun) - (define-key lisp-interaction-mode-map "\e\t" 'lisp-complete-symbol) - (define-key lisp-interaction-mode-map "\n" 'eval-print-last-sexp)) +All commands in `lisp-mode-shared-map' are inherited by this map.") -(defun lisp-interaction-mode () +(defvar lisp-interaction-mode-abbrev-table lisp-mode-abbrev-table) +(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 before point, and prints its value into the buffer, advancing point. +Note that printing is controlled by `eval-expression-print-length' +and `eval-expression-print-level'. Commands: Delete converts tabs to spaces as it moves back. @@ -314,24 +385,59 @@ Paragraphs are separated only by blank lines. Semicolons start comments. \\{lisp-interaction-mode-map} Entry to this mode calls the value of `lisp-interaction-mode-hook' -if that value is non-nil." - (interactive) - (kill-all-local-variables) - (use-local-map lisp-interaction-mode-map) - (setq major-mode 'lisp-interaction-mode) - (setq mode-name "Lisp Interaction") - (set-syntax-table emacs-lisp-mode-syntax-table) - (lisp-mode-variables nil) - (run-hooks 'lisp-interaction-mode-hook)) +if that value is non-nil.") (defun eval-print-last-sexp () - "Evaluate sexp before point; print value into current buffer." + "Evaluate sexp before point; print value into current buffer. + +Note that printing the result is controlled by the variables +`eval-expression-print-length' and `eval-expression-print-level', +which see." (interactive) (let ((standard-output (current-buffer))) (terpri) (eval-last-sexp t) (terpri))) - + + +(defun last-sexp-setup-props (beg end value alt1 alt2) + "Set up text properties for the output of `eval-last-sexp-1'. +BEG and END are the start and end of the output in current-buffer. +VALUE is the Lisp value printed, ALT1 and ALT2 are strings for the +alternative printed representations that can be displayed." + (let ((map (make-sparse-keymap))) + (define-key map "\C-m" 'last-sexp-toggle-display) + (define-key map [down-mouse-2] 'mouse-set-point) + (define-key map [mouse-2] 'last-sexp-toggle-display) + (add-text-properties + beg end + `(printed-value (,value ,alt1 ,alt2) + mouse-face highlight + keymap ,map + help-echo "RET, mouse-2: toggle abbreviated display" + rear-nonsticky (mouse-face keymap help-echo + printed-value))))) + + +(defun last-sexp-toggle-display () + "Toggle between abbreviated and unabbreviated printed representations." + (interactive) + (let ((value (get-text-property (point) 'printed-value))) + (when value + (let ((beg (or (previous-single-property-change (min (point-max) (1+ (point))) + 'printed-value) + (point))) + (end (or (next-single-char-property-change (point) 'printed-value) (point))) + (standard-output (current-buffer)) + (point (point))) + (delete-region beg end) + (insert (nth 1 value)) + (last-sexp-setup-props beg (point) + (nth 0 value) + (nth 2 value) + (nth 1 value)) + (goto-char (min (point-max) point)))))) + (defun eval-last-sexp-1 (eval-last-sexp-arg-internal) "Evaluate sexp before point; print value in minibuffer. With argument, print output into current buffer." @@ -356,10 +462,20 @@ With argument, print output into current buffer." (forward-char -1) (when (eq (preceding-char) ??) (forward-char -1))) + + ;; Skip over `#N='s. + (when (eq (preceding-char) ?=) + (let (labeled-p) + (save-excursion + (skip-chars-backward "0-9#=") + (setq labeled-p (looking-at "\\(#[0-9]+=\\)+"))) + (when labeled-p + (forward-sexp -1)))) + (save-restriction ;; vladimir@cs.ualberta.ca 30-Jul-1997: skip ` in ;; `variable' so that the value is returned, not the - ;; name + ;; name (if (and ignore-quotes (eq (following-char) ?`)) (forward-char)) @@ -379,13 +495,29 @@ With argument, print output into current buffer." 'args))))) expr)) (set-syntax-table stab)))))) - (let ((print-length eval-expression-print-length) - (print-level eval-expression-print-level)) - (prin1 value))))) + (let ((unabbreviated (let ((print-length nil) (print-level nil)) + (prin1-to-string value))) + (print-length eval-expression-print-length) + (print-level eval-expression-print-level) + (beg (point)) + end) + (prog1 + (prin1 value) + (setq end (point)) + (when (and (bufferp standard-output) + (or (not (null print-length)) + (not (null print-level))) + (not (string= unabbreviated + (buffer-substring-no-properties beg end)))) + (last-sexp-setup-props beg end value + unabbreviated + (buffer-substring-no-properties beg end)) + )))))) + (defun eval-last-sexp (eval-last-sexp-arg-internal) "Evaluate sexp before point; print value in minibuffer. -With argument, print output into current buffer." +Interactively, with prefix argument, print output into current buffer." (interactive "P") (if (null eval-expression-debug-on-error) (eval-last-sexp-1 eval-last-sexp-arg-internal) @@ -396,24 +528,30 @@ With argument, print output into current buffer." (unless (eq old-value new-value) (setq debug-on-error new-value)) value))) - -;; Change defvar into defconst within FORM, -;; and likewise for other constructs as necessary. + (defun eval-defun-1 (form) - (cond ((and (eq (car form) 'defvar) + "Change defvar into defconst within FORM. +Likewise for other constructs as necessary." + ;; The code in edebug-defun should be consistent with this, but not + ;; the same, since this gets a macroexpended form. + (cond ((not (listp form)) + form) + ((and (eq (car form) 'defvar) (cdr-safe (cdr-safe form))) ;; Force variable to be bound. (cons 'defconst (cdr form))) - ((and (eq (car form) 'defcustom) - (default-boundp (nth 1 form))) + ;; `defcustom' is now macroexpanded to + ;; `custom-declare-variable' with a quoted value arg. + ((and (eq (car form) 'custom-declare-variable) + (default-boundp (eval (nth 1 form)))) ;; Force variable to be bound. - (set-default (nth 1 form) (eval (nth 2 form))) + (set-default (eval (nth 1 form)) (eval (nth 1 (nth 2 form)))) form) ((eq (car form) 'progn) (cons 'progn (mapcar 'eval-defun-1 (cdr form)))) (t form))) -(defun eval-defun-2 (eval-defun-arg-internal) +(defun eval-defun-2 () "Evaluate defun that point is in or before. The value is displayed in the minibuffer. If the current defun is actually a call to `defvar', @@ -434,8 +572,8 @@ Return the result of evaluation." ;; variable. Re-written using `apply' to avoid capturing ;; variables like `end'. (apply - #'eval-region - (let ((standard-output (if eval-defun-arg-internal (current-buffer) t)) + #'eval-region + (let ((standard-output t) beg end form) ;; Read the form from the buffer, and record where it ends. (save-excursion @@ -455,29 +593,41 @@ Return the result of evaluation." ;; The result of evaluation has been put onto VALUES. So return it. (car values)) -(defun eval-defun (eval-defun-arg-internal) - "Evaluate defun that point is in or before. -The value is displayed in the minibuffer. -If the current defun is actually a call to `defvar', -then reset the variable using the initial value expression -even if the variable already has some other value. -\(Normally `defvar' does not change the variable's value -if it already has a value.\) +(defun eval-defun (edebug-it) + "Evaluate the top-level form containing point, or after point. -With argument, insert value in current buffer after the defun. -Return the result of evaluation." +If the current defun is actually a call to `defvar' or `defcustom', +evaluating it this way resets the variable using its initial value +expression even if the variable already has some other value. +\(Normally `defvar' and `defcustom' do not alter the value if there +already is one.) + +With a prefix argument, instrument the code for Edebug. + +If acting on a `defun' for FUNCTION, and the function was +instrumented, `Edebug: FUNCTION' is printed in the minibuffer. If not +instrumented, just FUNCTION is printed. + +If not acting on a `defun', the result of evaluation is displayed in +the minibuffer. This display is controlled by the variables +`eval-expression-print-length' and `eval-expression-print-level', +which see." (interactive "P") - (if (null eval-expression-debug-on-error) - (eval-defun-2 eval-defun-arg-internal) - (let ((old-value (make-symbol "t")) new-value value) - (let ((debug-on-error old-value)) - (setq value (eval-defun-2 eval-defun-arg-internal)) - (setq new-value debug-on-error)) - (unless (eq old-value new-value) - (setq debug-on-error new-value)) - value))) + (cond (edebug-it + (require 'edebug) + (eval-defun (not edebug-all-defs))) + (t + (if (null eval-expression-debug-on-error) + (eval-defun-2) + (let ((old-value (make-symbol "t")) new-value value) + (let ((debug-on-error old-value)) + (setq value (eval-defun-2)) + (setq new-value debug-on-error)) + (unless (eq old-value new-value) + (setq debug-on-error new-value)) + value))))) + - (defun lisp-comment-indent () (if (looking-at "\\s<\\s<\\s<") (current-column) @@ -488,29 +638,33 @@ Return the result of evaluation." (max (if (bolp) 0 (1+ (current-column))) comment-column)))) +;; This function just forces a more costly detection of comments (using +;; parse-partial-sexp from beginning-of-defun). I.e. It avoids the problem of +;; taking a `;' inside a string started on another line for a comment starter. +;; Note: `newcomment' gets it right in 99% of the cases if you're using +;; font-lock, anyway, so we could get rid of it. -stef (defun lisp-mode-auto-fill () (if (> (current-column) (current-fill-column)) (if (save-excursion - (nth 4 (parse-partial-sexp (save-excursion - (beginning-of-defun) - (point)) - (point)))) + (nth 4 (syntax-ppss (point)))) (do-auto-fill) - (let ((comment-start nil) (comment-start-skip nil)) - (do-auto-fill))))) + (unless (and (boundp 'comment-auto-fill-only-comments) + comment-auto-fill-only-comments) + (let ((comment-start nil) (comment-start-skip nil)) + (do-auto-fill)))))) -(defvar lisp-indent-offset nil "") -(defvar lisp-indent-function 'lisp-indent-function "") +(defvar lisp-indent-offset nil + "If non-nil, indent second line of expressions that many more columns.") +(defvar lisp-indent-function 'lisp-indent-function) (defun lisp-indent-line (&optional whole-exp) "Indent current line as Lisp code. With argument, indent any additional lines of the same expression rigidly along with this one." (interactive "P") - (let ((indent (calculate-lisp-indent)) shift-amt beg end - (pos (- (point-max) (point)))) - (beginning-of-line) - (setq beg (point)) + (let ((indent (calculate-lisp-indent)) shift-amt end + (pos (- (point-max) (point))) + (beg (progn (beginning-of-line) (point)))) (skip-chars-forward " \t") (if (or (null indent) (looking-at "\\s<\\s<\\s<")) ;; Don't alter indentation of a ;;; comment line @@ -616,7 +770,7 @@ is the buffer position of the start of the containing expression." (backward-prefix-chars)) (t ;; Indent beneath first sexp on same line as - ;; calculate-lisp-indent-last-sexp. Again, it's + ;; `calculate-lisp-indent-last-sexp'. Again, it's ;; almost certainly a function call. (goto-char calculate-lisp-indent-last-sexp) (beginning-of-line) @@ -644,12 +798,32 @@ is the buffer position of the start of the containing expression." normal-indent)))))) (defun lisp-indent-function (indent-point state) + "This function is the normal value of the variable `lisp-indent-function'. +It is used when indenting a line within a function call, to see if the +called function says anything special about how to indent the line. + +INDENT-POINT is the position where the user typed TAB, or equivalent. +Point is located at the point to indent under (for default indentation); +STATE is the `parse-partial-sexp' state for that position. + +If the current line is in a call to a Lisp function +which has a non-nil property `lisp-indent-function', +that specifies how to do the indentation. The property value can be +* `defun', meaning indent `defun'-style; +* an integer N, meaning indent the first N arguments specially +like ordinary function arguments and then indent any further +aruments like a body; +* a function to call just as this function was called. +If that function returns nil, that means it doesn't specify +the indentation. + +This function also returns nil meaning don't specify the indentation." (let ((normal-indent (current-column))) (goto-char (1+ (elt state 1))) (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t) (if (and (elt state 2) (not (looking-at "\\sw\\|\\s_"))) - ;; car of form doesn't seem to be a a symbol + ;; car of form doesn't seem to be a symbol (progn (if (not (> (save-excursion (forward-line 1) (point)) calculate-lisp-indent-last-sexp)) @@ -735,7 +909,7 @@ is the buffer position of the start of the containing expression." (goto-char (car (cdr state))) (+ lisp-body-indent (current-column))))) - + ;; (put 'progn 'lisp-indent-function 0), say, causes progn to be indented ;; like defun if the first form is placed on the next line, otherwise ;; it is indented like any other form (i.e. forms line up under first). @@ -762,6 +936,7 @@ is the buffer position of the start of the containing expression." (put 'let* 'lisp-indent-function 1) (put 'while 'lisp-indent-function 1) (put 'if 'lisp-indent-function 2) +(put 'read-if 'lisp-indent-function 2) (put 'catch 'lisp-indent-function 1) (put 'condition-case 'lisp-indent-function 2) (put 'unwind-protect 'lisp-indent-function 1) @@ -826,8 +1001,8 @@ ENDPOS is encountered." (and endpos (<= next-depth 0) (progn - (setq indent-stack (append indent-stack - (make-list (- next-depth) nil)) + (setq indent-stack (nconc indent-stack + (make-list (- next-depth) nil)) last-depth (- last-depth next-depth) next-depth 0))) (or outer-loop-done endpos @@ -869,8 +1044,8 @@ ENDPOS is encountered." (setq outer-loop-done (= (point) last-point)) (setq last-point (point))))))) -;; Indent every line whose first char is between START and END inclusive. (defun lisp-indent-region (start end) + "Indent every line whose first char is between START and END inclusive." (save-excursion (let ((endmark (copy-marker end))) (goto-char start) @@ -878,117 +1053,63 @@ ENDPOS is encountered." (lisp-indent-line)) (indent-sexp endmark) (set-marker endmark nil)))) - + ;;;; Lisp paragraph filling commands. +(defcustom emacs-lisp-docstring-fill-column 65 + "Value of `fill-column' to use when filling a docstring. +Any non-integer value means do not use a different value of +`fill-column' when filling docstrings." + :type '(choice (integer) + (const :tag "Use the current `fill-column'" t)) + :group 'lisp) + (defun lisp-fill-paragraph (&optional justify) - "Like \\[fill-paragraph], but handle Emacs Lisp comments. + "Like \\[fill-paragraph], but handle Emacs Lisp comments and docstrings. If any of the current line is a comment, fill the comment or the paragraph of it that point is in, preserving the comment's indentation and initial semicolons." (interactive "P") - (let ( - ;; Non-nil if the current line contains a comment. - has-comment - - ;; Non-nil if the current line contains code and a comment. - has-code-and-comment - - ;; If has-comment, the appropriate fill-prefix for the comment. - comment-fill-prefix - ) + (or (fill-comment-paragraph justify) + ;; Point is on a program line (a line no comment); we are interested + ;; particularly in docstring lines. + ;; + ;; We bind `paragraph-start' and `paragraph-separate' temporarily. They + ;; are buffer-local, but we avoid changing them so that they can be set + ;; to make `forward-paragraph' and friends do something the user wants. + ;; + ;; `paragraph-start': The `(' in the character alternative and the + ;; left-singlequote plus `(' sequence after the \\| alternative prevent + ;; sexps and backquoted sexps that follow a docstring from being filled + ;; with the docstring. This setting has the consequence of inhibiting + ;; filling many program lines that are not docstrings, which is sensible, + ;; because the user probably asked to fill program lines by accident, or + ;; expecting indentation (perhaps we should try to do indenting in that + ;; case). The `;' and `:' stop the paragraph being filled at following + ;; comment lines and at keywords (e.g., in `defcustom'). Left parens are + ;; escaped to keep font-locking, filling, & paren matching in the source + ;; file happy. + ;; + ;; `paragraph-separate': A clever regexp distinguishes the first line of + ;; a docstring and identifies it as a paragraph separator, so that it + ;; won't be filled. (Since the first line of documentation stands alone + ;; in some contexts, filling should not alter the contents the author has + ;; chosen.) Only the first line of a docstring begins with whitespace + ;; and a quotation mark and ends with a period or (rarely) a comma. + ;; + ;; The `fill-column' is temporarily bound to + ;; `emacs-lisp-docstring-fill-column' if that value is an integer. + (let ((paragraph-start (concat paragraph-start + "\\|\\s-*\\([\(;:\"]\\|`\(\\)")) + (paragraph-separate + (concat paragraph-separate "\\|\\s-*\".*[,\\.]$")) + (fill-column (if (integerp emacs-lisp-docstring-fill-column) + emacs-lisp-docstring-fill-column + fill-column))) + (fill-paragraph justify)) + ;; Never return nil. + t)) - ;; Figure out what kind of comment we are looking at. - (save-excursion - (beginning-of-line) - (cond - - ;; A line with nothing but a comment on it? - ((looking-at "[ \t]*;[; \t]*") - (setq has-comment t - comment-fill-prefix (buffer-substring (match-beginning 0) - (match-end 0)))) - - ;; A line with some code, followed by a comment? Remember that the - ;; semi which starts the comment shouldn't be part of a string or - ;; character. - ((condition-case nil - (save-restriction - (narrow-to-region (point-min) - (save-excursion (end-of-line) (point))) - (while (not (looking-at ";\\|$")) - (skip-chars-forward "^;\n\"\\\\?") - (cond - ((eq (char-after (point)) ?\\) (forward-char 2)) - ((memq (char-after (point)) '(?\" ??)) (forward-sexp 1)))) - (looking-at ";+[\t ]*")) - (error nil)) - (setq has-comment t has-code-and-comment t) - (setq comment-fill-prefix - (concat (make-string (/ (current-column) 8) ?\t) - (make-string (% (current-column) 8) ?\ ) - (buffer-substring (match-beginning 0) (match-end 0))))))) - - (if (not has-comment) - ;; `paragraph-start' is set here (not in the buffer-local - ;; variable so that `forward-paragraph' et al work as - ;; expected) so that filling (doc) strings works sensibly. - ;; Adding the opening paren to avoid the following sexp being - ;; filled means that sexps generally aren't filled as normal - ;; text, which is probably sensible. The `;' and `:' stop the - ;; filled para at following comment lines and keywords - ;; (typically in `defcustom'). - (let ((paragraph-start (concat paragraph-start - "\\|\\s-*[\(;:\"]"))) - (fill-paragraph justify)) - - ;; Narrow to include only the comment, and then fill the region. - (save-excursion - (save-restriction - (beginning-of-line) - (narrow-to-region - ;; Find the first line we should include in the region to fill. - (save-excursion - (while (and (zerop (forward-line -1)) - (looking-at "^[ \t]*;"))) - ;; We may have gone too far. Go forward again. - (or (looking-at ".*;") - (forward-line 1)) - (point)) - ;; Find the beginning of the first line past the region to fill. - (save-excursion - (while (progn (forward-line 1) - (looking-at "^[ \t]*;"))) - (point))) - - ;; Lines with only semicolons on them can be paragraph boundaries. - (let* ((paragraph-start (concat paragraph-start "\\|[ \t;]*$")) - (paragraph-separate (concat paragraph-start "\\|[ \t;]*$")) - (paragraph-ignore-fill-prefix nil) - (fill-prefix comment-fill-prefix) - (after-line (if has-code-and-comment - (save-excursion - (forward-line 1) (point)))) - (end (progn - (forward-paragraph) - (or (bolp) (newline 1)) - (point))) - ;; If this comment starts on a line with code, - ;; include that like in the filling. - (beg (progn (backward-paragraph) - (if (eq (point) after-line) - (forward-line -1)) - (point)))) - (fill-region-as-paragraph beg end - justify nil - (save-excursion - (goto-char beg) - (if (looking-at fill-prefix) - nil - (re-search-forward comment-start-skip) - (point)))))))) - t)) - (defun indent-code-rigidly (start end arg &optional nochange-regexp) "Indent all lines of code, starting in the region, sideways by ARG columns. Does not affect lines starting inside comments or strings, assuming that