diff options
| author | Stefan Monnier | 2019-04-12 12:37:00 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2019-04-12 12:37:00 -0400 |
| commit | 896e5802160c2797e689a7565599ebb1bd171295 (patch) | |
| tree | 537a66725af4b6f8cc64a17003bdeb631f5245f9 | |
| parent | 2bc2a3ecaf3d1992cbc8b14609c16ebd4498b155 (diff) | |
| download | emacs-896e5802160c2797e689a7565599ebb1bd171295.tar.gz emacs-896e5802160c2797e689a7565599ebb1bd171295.zip | |
* lisp/help-fns.el (help-fns-describe-variable-functions): New hook
(help-fns--compiler-macro, help-fns--parent-mode, help-fns--obsolete)
(help-fns--interactive-only): Indent output by 2 spaces.
(help-fns--side-effects): New function extracted from
describe-function-1.
(help-fns-describe-function-functions): Use it.
(help-fns--first-release, help-fns--mention-first-release): New functions.
(help-fns-function-description-header): Keymaps and macros can't
be interactive.
(help-fns--ensure-empty-line): New function.
(describe-function-1): Use it.
(help-fns--var-safe-local, help-fns--var-risky)
(help-fns--var-ignored-local, help-fns--var-file-local)
(help-fns--var-watchpoints, help-fns--var-obsolete)
(help-fns--var-alias, help-fns--var-bufferlocal): New functions,
extacted from describe-variable.
(describe-variable): Run help-fns-describe-variable-functions instead.
| -rw-r--r-- | etc/NEWS | 6 | ||||
| -rw-r--r-- | etc/NEWS.1-17 | 4 | ||||
| -rw-r--r-- | lisp/help-fns.el | 414 |
3 files changed, 254 insertions, 170 deletions
| @@ -847,6 +847,9 @@ directories in the destination. | |||
| 847 | ** Help | 847 | ** Help |
| 848 | 848 | ||
| 849 | --- | 849 | --- |
| 850 | *** Description of variables and functions give an estimated first release | ||
| 851 | |||
| 852 | --- | ||
| 850 | *** Output format of 'C-h l' ('view-lossage') has changed. | 853 | *** Output format of 'C-h l' ('view-lossage') has changed. |
| 851 | For convenience, 'view-lossage' now displays the last keystrokes | 854 | For convenience, 'view-lossage' now displays the last keystrokes |
| 852 | and commands in the same format as the edit buffer of | 855 | and commands in the same format as the edit buffer of |
| @@ -1497,6 +1500,9 @@ performs (setq-local indent-line-function #'indent-relative). | |||
| 1497 | 1500 | ||
| 1498 | * Lisp Changes in Emacs 27.1 | 1501 | * Lisp Changes in Emacs 27.1 |
| 1499 | 1502 | ||
| 1503 | ** New 'help-fns-describe-variable-functions' hook. | ||
| 1504 | Makes it possible to add metadata information to describe-variable. | ||
| 1505 | |||
| 1500 | ** i18n (internationalization) | 1506 | ** i18n (internationalization) |
| 1501 | 1507 | ||
| 1502 | *** ngettext can be used now to return the right plural form | 1508 | *** ngettext can be used now to return the right plural form |
diff --git a/etc/NEWS.1-17 b/etc/NEWS.1-17 index 758ef65ed95..1ce36fe99da 100644 --- a/etc/NEWS.1-17 +++ b/etc/NEWS.1-17 | |||
| @@ -2339,9 +2339,9 @@ It's Beat CCA Week. | |||
| 2339 | 2339 | ||
| 2340 | ** Lisp macros now exist. | 2340 | ** Lisp macros now exist. |
| 2341 | For example, you can write | 2341 | For example, you can write |
| 2342 | (defmacro cadr (arg) (list 'car (list 'cdr arg))) | 2342 | (defmacro mycadr (arg) (list 'car (list 'cdr arg))) |
| 2343 | and then the expression | 2343 | and then the expression |
| 2344 | (cadr foo) | 2344 | (mycadr foo) |
| 2345 | will expand into | 2345 | will expand into |
| 2346 | (car (cdr foo)) | 2346 | (car (cdr foo)) |
| 2347 | 2347 | ||
diff --git a/lisp/help-fns.el b/lisp/help-fns.el index 06b4ec8c209..50d69e70de4 100644 --- a/lisp/help-fns.el +++ b/lisp/help-fns.el | |||
| @@ -40,7 +40,21 @@ | |||
| 40 | "List of functions to run in help buffer in `describe-function'. | 40 | "List of functions to run in help buffer in `describe-function'. |
| 41 | Those functions will be run after the header line and argument | 41 | Those functions will be run after the header line and argument |
| 42 | list was inserted, and before the documentation will be inserted. | 42 | list was inserted, and before the documentation will be inserted. |
| 43 | The functions will receive the function name as argument.") | 43 | The functions will receive the function name as argument. |
| 44 | They can assume that a newline was output just before they were called, | ||
| 45 | and they should terminate any of their own output with a newline. | ||
| 46 | By convention they should indent their output by 2 spaces.") | ||
| 47 | |||
| 48 | (defvar help-fns-describe-variable-functions nil | ||
| 49 | "List of functions to run in help buffer in `describe-variable'. | ||
| 50 | Those functions will be run after the header line and value was inserted, | ||
| 51 | and before the documentation will be inserted. | ||
| 52 | The functions will receive the variable name as argument. | ||
| 53 | They can assume that a newline was output just before they were called, | ||
| 54 | and they should terminate any of their own output with a newline. | ||
| 55 | By convention they should indent their output by 2 spaces. | ||
| 56 | Current buffer is the buffer in which we queried the variable, | ||
| 57 | and the output should go to `standard-output'.") | ||
| 44 | 58 | ||
| 45 | ;; Functions | 59 | ;; Functions |
| 46 | 60 | ||
| @@ -412,7 +426,7 @@ suitable file is found, return nil." | |||
| 412 | (defun help-fns--compiler-macro (function) | 426 | (defun help-fns--compiler-macro (function) |
| 413 | (let ((handler (function-get function 'compiler-macro))) | 427 | (let ((handler (function-get function 'compiler-macro))) |
| 414 | (when handler | 428 | (when handler |
| 415 | (insert "\nThis function has a compiler macro") | 429 | (insert " This function has a compiler macro") |
| 416 | (if (symbolp handler) | 430 | (if (symbolp handler) |
| 417 | (progn | 431 | (progn |
| 418 | (insert (format-message " `%s'" handler)) | 432 | (insert (format-message " `%s'" handler)) |
| @@ -486,7 +500,7 @@ suitable file is found, return nil." | |||
| 486 | (get function | 500 | (get function |
| 487 | 'derived-mode-parent)))) | 501 | 'derived-mode-parent)))) |
| 488 | (when parent-mode | 502 | (when parent-mode |
| 489 | (insert (substitute-command-keys "\nParent mode: `")) | 503 | (insert (substitute-command-keys " Parent mode: `")) |
| 490 | (let ((beg (point))) | 504 | (let ((beg (point))) |
| 491 | (insert (format "%s" parent-mode)) | 505 | (insert (format "%s" parent-mode)) |
| 492 | (make-text-button beg (point) | 506 | (make-text-button beg (point) |
| @@ -500,15 +514,15 @@ suitable file is found, return nil." | |||
| 500 | (get function 'byte-obsolete-info))) | 514 | (get function 'byte-obsolete-info))) |
| 501 | (use (car obsolete))) | 515 | (use (car obsolete))) |
| 502 | (when obsolete | 516 | (when obsolete |
| 503 | (insert "\nThis " | 517 | (insert " This " |
| 504 | (if (eq (car-safe (symbol-function function)) 'macro) | 518 | (if (eq (car-safe (symbol-function function)) 'macro) |
| 505 | "macro" | 519 | "macro" |
| 506 | "function") | 520 | "function") |
| 507 | " is obsolete") | 521 | " is obsolete") |
| 508 | (when (nth 2 obsolete) | 522 | (when (nth 2 obsolete) |
| 509 | (insert (format " since %s" (nth 2 obsolete)))) | 523 | (insert (format " since %s" (nth 2 obsolete)))) |
| 510 | (insert (cond ((stringp use) (concat ";\n" use)) | 524 | (insert (cond ((stringp use) (concat ";\n " use)) |
| 511 | (use (format-message ";\nuse `%s' instead." use)) | 525 | (use (format-message ";\n use `%s' instead." use)) |
| 512 | (t ".")) | 526 | (t ".")) |
| 513 | "\n")))) | 527 | "\n")))) |
| 514 | 528 | ||
| @@ -538,17 +552,65 @@ FILE is the file where FUNCTION was probably defined." | |||
| 538 | (memq function | 552 | (memq function |
| 539 | byte-compile-interactive-only-functions))))) | 553 | byte-compile-interactive-only-functions))))) |
| 540 | (when interactive-only | 554 | (when interactive-only |
| 541 | (insert "\nThis function is for interactive use only" | 555 | (insert " This function is for interactive use only" |
| 542 | ;; Cf byte-compile-form. | 556 | ;; Cf byte-compile-form. |
| 543 | (cond ((stringp interactive-only) | 557 | (cond ((stringp interactive-only) |
| 544 | (format ";\nin Lisp code %s" interactive-only)) | 558 | (format ";\n in Lisp code %s" interactive-only)) |
| 545 | ((and (symbolp 'interactive-only) | 559 | ((and (symbolp 'interactive-only) |
| 546 | (not (eq interactive-only t))) | 560 | (not (eq interactive-only t))) |
| 547 | (format-message ";\nin Lisp code use `%s' instead." | 561 | (format-message ";\n in Lisp code use `%s' instead." |
| 548 | interactive-only)) | 562 | interactive-only)) |
| 549 | (t ".")) | 563 | (t ".")) |
| 550 | "\n"))))) | 564 | "\n"))))) |
| 551 | 565 | ||
| 566 | (add-hook 'help-fns-describe-function-functions #'help-fns--side-effects) | ||
| 567 | (defun help-fns--side-effects (function) | ||
| 568 | (when (and (symbolp function) | ||
| 569 | (or (function-get function 'pure) | ||
| 570 | (function-get function 'side-effect-free))) | ||
| 571 | (insert " This function does not change global state, " | ||
| 572 | "including the match data.\n"))) | ||
| 573 | |||
| 574 | (defun help-fns--first-release (symbol) | ||
| 575 | "Return the likely first release that defined SYMBOL." | ||
| 576 | ;; Code below relies on the etc/NEWS* files. | ||
| 577 | ;; FIXME: Maybe we should also use the */ChangeLog* files when available. | ||
| 578 | ;; FIXME: Maybe we should also look for announcements of the addition | ||
| 579 | ;; of the *packages* in which the function is defined. | ||
| 580 | (let* ((name (symbol-name symbol)) | ||
| 581 | (re (concat "\\_<" (regexp-quote name) "\\_>")) | ||
| 582 | (news (directory-files data-directory t "\\`NEWS.[1-9]")) | ||
| 583 | (first nil)) | ||
| 584 | (with-temp-buffer | ||
| 585 | (dolist (f news) | ||
| 586 | (erase-buffer) | ||
| 587 | (insert-file-contents f) | ||
| 588 | (goto-char (point-min)) | ||
| 589 | (search-forward "\n*") | ||
| 590 | (while (re-search-forward re nil t) | ||
| 591 | (save-excursion | ||
| 592 | ;; Almost all entries are of the form "* ... in Emacs NN.MM." | ||
| 593 | ;; but there are also a few in the form "* Emacs NN.MM is a bug | ||
| 594 | ;; fix release ...". | ||
| 595 | (if (not (re-search-backward "^\\*.* Emacs \\([0-9.]+[0-9]\\)" | ||
| 596 | nil t)) | ||
| 597 | (message "Ref found in non-versioned section in %S" | ||
| 598 | (file-name-nondirectory f)) | ||
| 599 | (let ((version (match-string 1))) | ||
| 600 | (when (or (null first) (version< version first)) | ||
| 601 | (setq first version)))))))) | ||
| 602 | first)) | ||
| 603 | |||
| 604 | (add-hook 'help-fns-describe-function-functions | ||
| 605 | #'help-fns--mention-first-release) | ||
| 606 | (add-hook 'help-fns-describe-variable-functions | ||
| 607 | #'help-fns--mention-first-release) | ||
| 608 | (defun help-fns--mention-first-release (object) | ||
| 609 | (let ((first (if (symbolp object) (help-fns--first-release object)))) | ||
| 610 | (when first | ||
| 611 | (princ (format " Probably introduced at or before Emacs version %s.\n" | ||
| 612 | first))))) | ||
| 613 | |||
| 552 | (defun help-fns-short-filename (filename) | 614 | (defun help-fns-short-filename (filename) |
| 553 | (let* ((abbrev (abbreviate-file-name filename)) | 615 | (let* ((abbrev (abbreviate-file-name filename)) |
| 554 | (short abbrev)) | 616 | (short abbrev)) |
| @@ -611,9 +673,9 @@ Returns a list of the form (REAL-FUNCTION DEF ALIASED REAL-DEF)." | |||
| 611 | (memq (car-safe def) '(macro lambda closure))) | 673 | (memq (car-safe def) '(macro lambda closure))) |
| 612 | (stringp file-name) | 674 | (stringp file-name) |
| 613 | (help-fns--autoloaded-p function file-name)) | 675 | (help-fns--autoloaded-p function file-name)) |
| 614 | (if (commandp def) | 676 | (concat |
| 615 | "an interactive autoloaded " | 677 | "an autoloaded " (if (commandp def) |
| 616 | "an autoloaded ") | 678 | "interactive ")) |
| 617 | (if (commandp def) "an interactive " "a ")))) | 679 | (if (commandp def) "an interactive " "a ")))) |
| 618 | 680 | ||
| 619 | ;; Print what kind of function-like object FUNCTION is. | 681 | ;; Print what kind of function-like object FUNCTION is. |
| @@ -627,14 +689,16 @@ Returns a list of the form (REAL-FUNCTION DEF ALIASED REAL-DEF)." | |||
| 627 | (aliased | 689 | (aliased |
| 628 | (format-message "an alias for `%s'" real-def)) | 690 | (format-message "an alias for `%s'" real-def)) |
| 629 | ((subrp def) | 691 | ((subrp def) |
| 630 | (if (eq 'unevalled (cdr (subr-arity def))) | 692 | (concat beg (if (eq 'unevalled (cdr (subr-arity def))) |
| 631 | (concat beg "special form") | 693 | "special form" |
| 632 | (concat beg "built-in function"))) | 694 | "built-in function"))) |
| 633 | ((autoloadp def) | 695 | ((autoloadp def) |
| 634 | (format "%s autoloaded %s" | 696 | (format "an autoloaded %s" |
| 635 | (if (commandp def) "an interactive" "an") | 697 | (cond |
| 636 | (if (eq (nth 4 def) 'keymap) "keymap" | 698 | ((commandp def) "interactive Lisp function") |
| 637 | (if (nth 4 def) "Lisp macro" "Lisp function")))) | 699 | ((eq (nth 4 def) 'keymap) "keymap") |
| 700 | ((nth 4 def) "Lisp macro") | ||
| 701 | (t "Lisp function")))) | ||
| 638 | ((or (eq (car-safe def) 'macro) | 702 | ((or (eq (car-safe def) 'macro) |
| 639 | ;; For advised macros, def is a lambda | 703 | ;; For advised macros, def is a lambda |
| 640 | ;; expression or a byte-code-function-p, so we | 704 | ;; expression or a byte-code-function-p, so we |
| @@ -685,6 +749,10 @@ Returns a list of the form (REAL-FUNCTION DEF ALIASED REAL-DEF)." | |||
| 685 | (help-xref-button 1 'help-function-def function file-name)))) | 749 | (help-xref-button 1 'help-function-def function file-name)))) |
| 686 | (princ ".")))) | 750 | (princ ".")))) |
| 687 | 751 | ||
| 752 | (defun help-fns--ensure-empty-line () | ||
| 753 | (unless (eolp) (insert "\n")) | ||
| 754 | (unless (eq ?\n (char-before (1- (point)))) (insert "\n"))) | ||
| 755 | |||
| 688 | ;;;###autoload | 756 | ;;;###autoload |
| 689 | (defun describe-function-1 (function) | 757 | (defun describe-function-1 (function) |
| 690 | (let ((pt1 (with-current-buffer (help-buffer) (point)))) | 758 | (let ((pt1 (with-current-buffer (help-buffer) (point)))) |
| @@ -722,12 +790,10 @@ Returns a list of the form (REAL-FUNCTION DEF ALIASED REAL-DEF)." | |||
| 722 | real-function key-bindings-buffer) | 790 | real-function key-bindings-buffer) |
| 723 | ;; E.g. an alias for a not yet defined function. | 791 | ;; E.g. an alias for a not yet defined function. |
| 724 | ((invalid-function void-function) doc-raw)))) | 792 | ((invalid-function void-function) doc-raw)))) |
| 793 | (help-fns--ensure-empty-line) | ||
| 725 | (run-hook-with-args 'help-fns-describe-function-functions function) | 794 | (run-hook-with-args 'help-fns-describe-function-functions function) |
| 726 | (insert "\n" (or doc "Not documented."))) | 795 | (help-fns--ensure-empty-line) |
| 727 | (when (or (function-get function 'pure) | 796 | (insert (or doc "Not documented."))) |
| 728 | (function-get function 'side-effect-free)) | ||
| 729 | (insert "\nThis function does not change global state, " | ||
| 730 | "including the match data.")) | ||
| 731 | ;; Avoid asking the user annoying questions if she decides | 797 | ;; Avoid asking the user annoying questions if she decides |
| 732 | ;; to save the help buffer, when her locale's codeset | 798 | ;; to save the help buffer, when her locale's codeset |
| 733 | ;; isn't UTF-8. | 799 | ;; isn't UTF-8. |
| @@ -830,7 +896,6 @@ it is displayed along with the global value." | |||
| 830 | (message "You did not specify a variable") | 896 | (message "You did not specify a variable") |
| 831 | (save-excursion | 897 | (save-excursion |
| 832 | (let ((valvoid (not (with-current-buffer buffer (boundp variable)))) | 898 | (let ((valvoid (not (with-current-buffer buffer (boundp variable)))) |
| 833 | (permanent-local (get variable 'permanent-local)) | ||
| 834 | val val-start-pos locus) | 899 | val val-start-pos locus) |
| 835 | ;; Extract the value before setting up the output buffer, | 900 | ;; Extract the value before setting up the output buffer, |
| 836 | ;; in case `buffer' *is* the output buffer. | 901 | ;; in case `buffer' *is* the output buffer. |
| @@ -846,26 +911,26 @@ it is displayed along with the global value." | |||
| 846 | (prin1 variable) | 911 | (prin1 variable) |
| 847 | (setq file-name (find-lisp-object-file-name variable 'defvar)) | 912 | (setq file-name (find-lisp-object-file-name variable 'defvar)) |
| 848 | 913 | ||
| 849 | (if file-name | 914 | (princ (if file-name |
| 850 | (progn | 915 | (progn |
| 851 | (princ (format-message | 916 | (princ (format-message |
| 852 | " is a variable defined in `%s'.\n" | 917 | " is a variable defined in `%s'.\n" |
| 853 | (if (eq file-name 'C-source) | 918 | (if (eq file-name 'C-source) |
| 854 | "C source code" | 919 | "C source code" |
| 855 | (file-name-nondirectory file-name)))) | 920 | (file-name-nondirectory file-name)))) |
| 856 | (with-current-buffer standard-output | 921 | (with-current-buffer standard-output |
| 857 | (save-excursion | 922 | (save-excursion |
| 858 | (re-search-backward (substitute-command-keys | 923 | (re-search-backward (substitute-command-keys |
| 859 | "`\\([^`']+\\)'") | 924 | "`\\([^`']+\\)'") |
| 860 | nil t) | 925 | nil t) |
| 861 | (help-xref-button 1 'help-variable-def | 926 | (help-xref-button 1 'help-variable-def |
| 862 | variable file-name))) | 927 | variable file-name))) |
| 863 | (if valvoid | 928 | (if valvoid |
| 864 | (princ "It is void as a variable.") | 929 | "It is void as a variable." |
| 865 | (princ "Its "))) | 930 | "Its ")) |
| 866 | (if valvoid | 931 | (if valvoid |
| 867 | (princ " is void as a variable.") | 932 | " is void as a variable." |
| 868 | (princ (substitute-command-keys "'s "))))) | 933 | (substitute-command-keys "'s "))))) |
| 869 | (unless valvoid | 934 | (unless valvoid |
| 870 | (with-current-buffer standard-output | 935 | (with-current-buffer standard-output |
| 871 | (setq val-start-pos (point)) | 936 | (setq val-start-pos (point)) |
| @@ -894,7 +959,7 @@ it is displayed along with the global value." | |||
| 894 | (let* ((sv (get variable 'standard-value)) | 959 | (let* ((sv (get variable 'standard-value)) |
| 895 | (origval (and (consp sv) | 960 | (origval (and (consp sv) |
| 896 | (condition-case nil | 961 | (condition-case nil |
| 897 | (eval (car sv)) | 962 | (eval (car sv) t) |
| 898 | (error :help-eval-error)))) | 963 | (error :help-eval-error)))) |
| 899 | from) | 964 | from) |
| 900 | (when (and (consp sv) | 965 | (when (and (consp sv) |
| @@ -969,132 +1034,17 @@ it is displayed along with the global value." | |||
| 969 | (let* ((alias (condition-case nil | 1034 | (let* ((alias (condition-case nil |
| 970 | (indirect-variable variable) | 1035 | (indirect-variable variable) |
| 971 | (error variable))) | 1036 | (error variable))) |
| 972 | (obsolete (get variable 'byte-obsolete-variable)) | ||
| 973 | (watchpoints (get-variable-watchers variable)) | ||
| 974 | (use (car obsolete)) | ||
| 975 | (safe-var (get variable 'safe-local-variable)) | ||
| 976 | (doc (or (documentation-property | 1037 | (doc (or (documentation-property |
| 977 | variable 'variable-documentation) | 1038 | variable 'variable-documentation) |
| 978 | (documentation-property | 1039 | (documentation-property |
| 979 | alias 'variable-documentation))) | 1040 | alias 'variable-documentation)))) |
| 980 | (extra-line nil)) | ||
| 981 | 1041 | ||
| 982 | ;; Mention if it's a local variable. | 1042 | (with-current-buffer buffer |
| 983 | (cond | 1043 | (run-hook-with-args 'help-fns-describe-variable-functions |
| 984 | ((and (local-variable-if-set-p variable) | 1044 | variable)) |
| 985 | (or (not (local-variable-p variable)) | 1045 | |
| 986 | (with-temp-buffer | 1046 | (with-current-buffer standard-output |
| 987 | (local-variable-if-set-p variable)))) | 1047 | (help-fns--ensure-empty-line)) |
| 988 | (setq extra-line t) | ||
| 989 | (princ " Automatically becomes ") | ||
| 990 | (if permanent-local | ||
| 991 | (princ "permanently ")) | ||
| 992 | (princ "buffer-local when set.\n")) | ||
| 993 | ((not permanent-local)) | ||
| 994 | ((bufferp locus) | ||
| 995 | (setq extra-line t) | ||
| 996 | (princ | ||
| 997 | (substitute-command-keys | ||
| 998 | " This variable's buffer-local value is permanent.\n"))) | ||
| 999 | (t | ||
| 1000 | (setq extra-line t) | ||
| 1001 | (princ (substitute-command-keys | ||
| 1002 | " This variable's value is permanent \ | ||
| 1003 | if it is given a local binding.\n")))) | ||
| 1004 | |||
| 1005 | ;; Mention if it's an alias. | ||
| 1006 | (unless (eq alias variable) | ||
| 1007 | (setq extra-line t) | ||
| 1008 | (princ (format-message | ||
| 1009 | " This variable is an alias for `%s'.\n" | ||
| 1010 | alias))) | ||
| 1011 | |||
| 1012 | (when obsolete | ||
| 1013 | (setq extra-line t) | ||
| 1014 | (princ " This variable is obsolete") | ||
| 1015 | (if (nth 2 obsolete) | ||
| 1016 | (princ (format " since %s" (nth 2 obsolete)))) | ||
| 1017 | (princ (cond ((stringp use) (concat ";\n " use)) | ||
| 1018 | (use (format-message ";\n use `%s' instead." | ||
| 1019 | (car obsolete))) | ||
| 1020 | (t "."))) | ||
| 1021 | (terpri)) | ||
| 1022 | |||
| 1023 | (when watchpoints | ||
| 1024 | (setq extra-line t) | ||
| 1025 | (princ " Calls these functions when changed: ") | ||
| 1026 | (princ watchpoints) | ||
| 1027 | (terpri)) | ||
| 1028 | |||
| 1029 | (when (member (cons variable val) | ||
| 1030 | (with-current-buffer buffer | ||
| 1031 | file-local-variables-alist)) | ||
| 1032 | (setq extra-line t) | ||
| 1033 | (if (member (cons variable val) | ||
| 1034 | (with-current-buffer buffer | ||
| 1035 | dir-local-variables-alist)) | ||
| 1036 | (let ((file (and (buffer-file-name buffer) | ||
| 1037 | (not (file-remote-p | ||
| 1038 | (buffer-file-name buffer))) | ||
| 1039 | (dir-locals-find-file | ||
| 1040 | (buffer-file-name buffer)))) | ||
| 1041 | (is-directory nil)) | ||
| 1042 | (princ (substitute-command-keys | ||
| 1043 | " This variable's value is directory-local")) | ||
| 1044 | (when (consp file) ; result from cache | ||
| 1045 | ;; If the cache element has an mtime, we | ||
| 1046 | ;; assume it came from a file. | ||
| 1047 | (if (nth 2 file) | ||
| 1048 | ;; (car file) is a directory. | ||
| 1049 | (setq file (dir-locals--all-files (car file))) | ||
| 1050 | ;; Otherwise, assume it was set directly. | ||
| 1051 | (setq file (car file) | ||
| 1052 | is-directory t))) | ||
| 1053 | (if (null file) | ||
| 1054 | (princ ".\n") | ||
| 1055 | (princ ", set ") | ||
| 1056 | (princ (substitute-command-keys | ||
| 1057 | (cond | ||
| 1058 | (is-directory "for the directory\n `") | ||
| 1059 | ;; Many files matched. | ||
| 1060 | ((and (consp file) (cdr file)) | ||
| 1061 | (setq file (file-name-directory (car file))) | ||
| 1062 | (format "by one of the\n %s files in the directory\n `" | ||
| 1063 | dir-locals-file)) | ||
| 1064 | (t (setq file (car file)) | ||
| 1065 | "by the file\n `")))) | ||
| 1066 | (with-current-buffer standard-output | ||
| 1067 | (insert-text-button | ||
| 1068 | file 'type 'help-dir-local-var-def | ||
| 1069 | 'help-args (list variable file))) | ||
| 1070 | (princ (substitute-command-keys "'.\n")))) | ||
| 1071 | (princ (substitute-command-keys | ||
| 1072 | " This variable's value is file-local.\n")))) | ||
| 1073 | |||
| 1074 | (when (memq variable ignored-local-variables) | ||
| 1075 | (setq extra-line t) | ||
| 1076 | (princ " This variable is ignored as a file-local \ | ||
| 1077 | variable.\n")) | ||
| 1078 | |||
| 1079 | ;; Can be both risky and safe, eg auto-fill-function. | ||
| 1080 | (when (risky-local-variable-p variable) | ||
| 1081 | (setq extra-line t) | ||
| 1082 | (princ " This variable may be risky if used as a \ | ||
| 1083 | file-local variable.\n") | ||
| 1084 | (when (assq variable safe-local-variable-values) | ||
| 1085 | (princ (substitute-command-keys | ||
| 1086 | " However, you have added it to \ | ||
| 1087 | `safe-local-variable-values'.\n")))) | ||
| 1088 | |||
| 1089 | (when safe-var | ||
| 1090 | (setq extra-line t) | ||
| 1091 | (princ " This variable is safe as a file local variable ") | ||
| 1092 | (princ "if its value\n satisfies the predicate ") | ||
| 1093 | (princ (if (byte-code-function-p safe-var) | ||
| 1094 | "which is a byte-compiled expression.\n" | ||
| 1095 | (format-message "`%s'.\n" safe-var)))) | ||
| 1096 | |||
| 1097 | (if extra-line (terpri)) | ||
| 1098 | (princ "Documentation:\n") | 1048 | (princ "Documentation:\n") |
| 1099 | (with-current-buffer standard-output | 1049 | (with-current-buffer standard-output |
| 1100 | (insert (or doc "Not documented as a variable.")))) | 1050 | (insert (or doc "Not documented as a variable.")))) |
| @@ -1121,6 +1071,134 @@ file-local variable.\n") | |||
| 1121 | ;; Return the text we displayed. | 1071 | ;; Return the text we displayed. |
| 1122 | (buffer-string)))))))) | 1072 | (buffer-string)))))))) |
| 1123 | 1073 | ||
| 1074 | (add-hook 'help-fns-describe-variable-functions #'help-fns--var-safe-local) | ||
| 1075 | (defun help-fns--var-safe-local (variable) | ||
| 1076 | (let ((safe-var (get variable 'safe-local-variable))) | ||
| 1077 | (when safe-var | ||
| 1078 | (princ " This variable is safe as a file local variable ") | ||
| 1079 | (princ "if its value\n satisfies the predicate ") | ||
| 1080 | (princ (if (byte-code-function-p safe-var) | ||
| 1081 | "which is a byte-compiled expression.\n" | ||
| 1082 | (format-message "`%s'.\n" safe-var)))))) | ||
| 1083 | |||
| 1084 | (add-hook 'help-fns-describe-variable-functions #'help-fns--var-risky) | ||
| 1085 | (defun help-fns--var-risky (variable) | ||
| 1086 | ;; Can be both risky and safe, eg auto-fill-function. | ||
| 1087 | (when (risky-local-variable-p variable) | ||
| 1088 | (princ " This variable may be risky if used as a \ | ||
| 1089 | file-local variable.\n") | ||
| 1090 | (when (assq variable safe-local-variable-values) | ||
| 1091 | (princ (substitute-command-keys | ||
| 1092 | " However, you have added it to \ | ||
| 1093 | `safe-local-variable-values'.\n"))))) | ||
| 1094 | |||
| 1095 | (add-hook 'help-fns-describe-variable-functions #'help-fns--var-ignored-local) | ||
| 1096 | (defun help-fns--var-ignored-local (variable) | ||
| 1097 | (when (memq variable ignored-local-variables) | ||
| 1098 | (princ " This variable is ignored as a file-local \ | ||
| 1099 | variable.\n"))) | ||
| 1100 | |||
| 1101 | (add-hook 'help-fns-describe-variable-functions #'help-fns--var-file-local) | ||
| 1102 | (defun help-fns--var-file-local (variable) | ||
| 1103 | (when (boundp variable) | ||
| 1104 | (let ((val (symbol-value variable))) | ||
| 1105 | (when (member (cons variable val) | ||
| 1106 | file-local-variables-alist) | ||
| 1107 | (if (member (cons variable val) | ||
| 1108 | dir-local-variables-alist) | ||
| 1109 | (let ((file (and buffer-file-name | ||
| 1110 | (not (file-remote-p buffer-file-name)) | ||
| 1111 | (dir-locals-find-file buffer-file-name))) | ||
| 1112 | (is-directory nil)) | ||
| 1113 | (princ (substitute-command-keys | ||
| 1114 | " This variable's value is directory-local")) | ||
| 1115 | (when (consp file) ; result from cache | ||
| 1116 | ;; If the cache element has an mtime, we | ||
| 1117 | ;; assume it came from a file. | ||
| 1118 | (if (nth 2 file) | ||
| 1119 | ;; (car file) is a directory. | ||
| 1120 | (setq file (dir-locals--all-files (car file))) | ||
| 1121 | ;; Otherwise, assume it was set directly. | ||
| 1122 | (setq file (car file) | ||
| 1123 | is-directory t))) | ||
| 1124 | (if (null file) | ||
| 1125 | (princ ".\n") | ||
| 1126 | (princ ", set ") | ||
| 1127 | (princ (substitute-command-keys | ||
| 1128 | (cond | ||
| 1129 | (is-directory "for the directory\n `") | ||
| 1130 | ;; Many files matched. | ||
| 1131 | ((and (consp file) (cdr file)) | ||
| 1132 | (setq file (file-name-directory (car file))) | ||
| 1133 | (format "by one of the\n %s files in the directory\n `" | ||
| 1134 | dir-locals-file)) | ||
| 1135 | (t (setq file (car file)) | ||
| 1136 | "by the file\n `")))) | ||
| 1137 | (with-current-buffer standard-output | ||
| 1138 | (insert-text-button | ||
| 1139 | file 'type 'help-dir-local-var-def | ||
| 1140 | 'help-args (list variable file))) | ||
| 1141 | (princ (substitute-command-keys "'.\n")))) | ||
| 1142 | (princ (substitute-command-keys | ||
| 1143 | " This variable's value is file-local.\n"))))))) | ||
| 1144 | |||
| 1145 | (add-hook 'help-fns-describe-variable-functions #'help-fns--var-watchpoints) | ||
| 1146 | (defun help-fns--var-watchpoints (variable) | ||
| 1147 | (let ((watchpoints (get-variable-watchers variable))) | ||
| 1148 | (when watchpoints | ||
| 1149 | (princ " Calls these functions when changed: ") | ||
| 1150 | ;; FIXME: Turn function names into hyperlinks. | ||
| 1151 | (princ watchpoints) | ||
| 1152 | (terpri)))) | ||
| 1153 | |||
| 1154 | (add-hook 'help-fns-describe-variable-functions #'help-fns--var-obsolete) | ||
| 1155 | (defun help-fns--var-obsolete (variable) | ||
| 1156 | (let* ((obsolete (get variable 'byte-obsolete-variable)) | ||
| 1157 | (use (car obsolete))) | ||
| 1158 | (when obsolete | ||
| 1159 | (princ " This variable is obsolete") | ||
| 1160 | (if (nth 2 obsolete) | ||
| 1161 | (princ (format " since %s" (nth 2 obsolete)))) | ||
| 1162 | (princ (cond ((stringp use) (concat ";\n " use)) | ||
| 1163 | (use (format-message ";\n use `%s' instead." | ||
| 1164 | (car obsolete))) | ||
| 1165 | (t "."))) | ||
| 1166 | (terpri)))) | ||
| 1167 | |||
| 1168 | (add-hook 'help-fns-describe-variable-functions #'help-fns--var-alias) | ||
| 1169 | (defun help-fns--var-alias (variable) | ||
| 1170 | ;; Mention if it's an alias. | ||
| 1171 | (let ((alias (condition-case nil | ||
| 1172 | (indirect-variable variable) | ||
| 1173 | (error variable)))) | ||
| 1174 | (unless (eq alias variable) | ||
| 1175 | (princ (format-message | ||
| 1176 | " This variable is an alias for `%s'.\n" | ||
| 1177 | alias))))) | ||
| 1178 | |||
| 1179 | (add-hook 'help-fns-describe-variable-functions #'help-fns--var-bufferlocal) | ||
| 1180 | (defun help-fns--var-bufferlocal (variable) | ||
| 1181 | (let ((permanent-local (get variable 'permanent-local)) | ||
| 1182 | (locus (variable-binding-locus variable))) | ||
| 1183 | ;; Mention if it's a local variable. | ||
| 1184 | (cond | ||
| 1185 | ((and (local-variable-if-set-p variable) | ||
| 1186 | (or (not (local-variable-p variable)) | ||
| 1187 | (with-temp-buffer | ||
| 1188 | (local-variable-if-set-p variable)))) | ||
| 1189 | (princ " Automatically becomes ") | ||
| 1190 | (if permanent-local | ||
| 1191 | (princ "permanently ")) | ||
| 1192 | (princ "buffer-local when set.\n")) | ||
| 1193 | ((not permanent-local)) | ||
| 1194 | ((bufferp locus) | ||
| 1195 | (princ | ||
| 1196 | (substitute-command-keys | ||
| 1197 | " This variable's buffer-local value is permanent.\n"))) | ||
| 1198 | (t | ||
| 1199 | (princ (substitute-command-keys | ||
| 1200 | " This variable's value is permanent \ | ||
| 1201 | if it is given a local binding.\n")))))) | ||
| 1124 | 1202 | ||
| 1125 | (defvar help-xref-stack-item) | 1203 | (defvar help-xref-stack-item) |
| 1126 | 1204 | ||