From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: "H. Dieter Wilhelm" Newsgroups: gmane.emacs.bugs Subject: bug#60587: Patch for adding links to symbols' help documentation Date: Sun, 15 Jan 2023 13:48:57 +0100 Message-ID: <86a62k55fq.fsf@duenenhof-wilhelm.de> References: <86y1qgr1bf.fsf@duenenhof-wilhelm.de> <86tu13qydg.fsf@duenenhof-wilhelm.de> <83h6x2u74b.fsf@gnu.org> <867cxqui0w.fsf@duenenhof-wilhelm.de> <83sfgd8u8l.fsf@gnu.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="35279"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Cc: 60587@debbugs.gnu.org, monnier@iro.umontreal.ca To: Eli Zaretskii Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Sun Jan 15 13:50:18 2023 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pH2Sn-0008wB-DC for geb-bug-gnu-emacs@m.gmane-mx.org; Sun, 15 Jan 2023 13:50:17 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pH2Sd-0007k5-Cc; Sun, 15 Jan 2023 07:50:07 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pH2SY-0007jn-Um for bug-gnu-emacs@gnu.org; Sun, 15 Jan 2023 07:50:03 -0500 Original-Received: from debbugs.gnu.org ([209.51.188.43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1pH2SY-0000n3-LC for bug-gnu-emacs@gnu.org; Sun, 15 Jan 2023 07:50:02 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1pH2SY-0007e1-Fe for bug-gnu-emacs@gnu.org; Sun, 15 Jan 2023 07:50:02 -0500 X-Loop: help-debbugs@gnu.org Resent-From: "H. Dieter Wilhelm" Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sun, 15 Jan 2023 12:50:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 60587 X-GNU-PR-Package: emacs Original-Received: via spool by 60587-submit@debbugs.gnu.org id=B60587.167378695629329 (code B ref 60587); Sun, 15 Jan 2023 12:50:02 +0000 Original-Received: (at 60587) by debbugs.gnu.org; 15 Jan 2023 12:49:16 +0000 Original-Received: from localhost ([127.0.0.1]:56370 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1pH2Rn-0007cy-D3 for submit@debbugs.gnu.org; Sun, 15 Jan 2023 07:49:16 -0500 Original-Received: from mout.kundenserver.de ([212.227.17.13]:48499) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1pH2Ri-0007cg-4V for 60587@debbugs.gnu.org; Sun, 15 Jan 2023 07:49:13 -0500 Original-Received: from ping ([109.250.147.186]) by mrelayeu.kundenserver.de (mreue107 [212.227.15.183]) with ESMTPSA (Nemesis) id 1MrxfX-1ovfmI0X4v-00nzdx; Sun, 15 Jan 2023 13:48:59 +0100 In-Reply-To: <83sfgd8u8l.fsf@gnu.org> (Eli Zaretskii's message of "Sat, 14 Jan 2023 09:12:42 +0200") X-Provags-ID: V03:K1:45Zz/YuWugNOj4LZCo31tffOlfDQk3+P1JvmUozIdPT+dIdwimq 0/W4lPEbtFC5Wjd5TKEi7OMoeAuqZp2LyD8yLLZLCSXO24lWgdA646AmTUFkr8DypnnqsFP h+ZWt39ecvAhiNAqYE6mWcKRuXVbmEXbEVYMyhU/ZrzW8qZ+fm9HUCHIz80kRrPPQ86DKWm X5ACmZeRZJ/aP2mjjIl2A== UI-OutboundReport: notjunk:1;M01:P0:BqWUiPlOdvY=;h37+UyTd1uZJhU9SD5l7mku0928 56Kyc/XsBBVq7vbnDXaHCcRniJ6JSnrI3k4iT9QcmwVZFehGqu55zdvGuXTUK/akckxFgZOYp HtJ+MfRETdOmnqbShtnF8YD1DJPjQlbo6UDOJanLfd/oUf4EzZh+zyxslfLvMz375qKrN25uC jKYQfHYudGMqi7abfxSY/3R9Lfcgjwl9CzzMPVn+oExWhmUfX71Fb72O5UTs5J9g8pd7TI7Bj +rp0Ihlf1CKu+rpRnB+Yddj3k2pxGPBViUDbGeqP8UumpQhCtRDUfIgfmXbaPs2TOKxVmpKCw ZWZ6VhfF4Y+nWQQCGSP59J5ILFU+63kOXkdjrgGZcW5ngTg6tca6Pte3rCqcDmcwlFbW1i4rx WUsmOC2LqqklGqE7LyM2uHbMpwF09a5Ybht9dFhKWIWT24cXO+Y64EBoUe5u76u5NijVtOKx4 uhI9b6CViaTjkJ96nYwCtYzMqKpAq6Z015iR2jWcbXc2UhlPiNZNCaSsEjentSW9LTPmMosXe IDRDBTd1NMfR43/OLaMuY2kJXTSKmstOV2UUWql2gfeso1ajPyCrzm/4VFawtcFC8FJXwzC6G qbaEgn2GSKtRQe3oPqXHCA+sX8k7ELa6w5SwEP2MMEP3p1J7FU6lLMYXTTI/VcRzZ/apjvCVR ebyJG2hUpkuj+R3dEcaOJkUlyTJZORR11MIng568vbbhEh2wqK1SerYQ716IMzY= X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:253427 Archived-At: --=-=-= Content-Type: text/plain Could you please check the attached patch of info.el? I'm a bit concerned about the "symbol link" face: I think it's helpful that they're distinct from the info link face. On the other hand the colour is then inconsistent to links as they appear in the help buffers. The other concern is that the info documentation from freshly installed packages (in the current Emacs session) are still excluded from the linking process.. I'm looking forward to your assessments --=-=-= Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename=0001-Provide-links-in-Info-from-symbols-to-their-help-doc.patch Content-Transfer-Encoding: quoted-printable >From 57725936981c50cd1234d8c01c86733cc29d791f Mon Sep 17 00:00:00 2001 From: Dieter Wilhelm Date: Sun, 15 Jan 2023 13:30:45 +0100 Subject: [PATCH] Provide links in Info from symbols to their help documentation --- lisp/info.el | 383 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 383 insertions(+) diff --git a/lisp/info.el b/lisp/info.el index 035dff66e7..876addd7e1 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -5510,6 +5510,389 @@ info--manual-names Info-directory-list (mapcar #'car Info-suffix-list)))))))) =20 + +;;; Commentary: + +;; The code below provides links of symbols (functions, variables, and +;; faces) within Emacs' Info viewer to their builtin help +;; documentation. This linking is done when symbol names in texinfo +;; documentation (like the Emacs- and Elisp manual) are: + +;; 1. Quoted symbol names like `quoted-symbol' or: + +;; 2. Function names which are prefixed by M-x, for example M-x +;; function-name or are quoted and prefixed, like `M-x function-name'. + +;; 3. Function names appearing behind the following forms, which +;; occur, for example, in the Elisp manual: + +;; -- Special Form: function-name +;; -- Command: ... +;; -- Function: ... +;; -- Macro: ... + +;; 4. And variables names behind the following text: + +;; -- User Option: variable-name +;; -- Variable: ... + +;; In any case all symbol names must be known to Emacs, which means it +;; is either a built-in, or its Lisp package is loaded for the current +;; Emacs session, or the symbol is auto-loaded. + +;; You can follow the additional links with the usual Info +;; keybindings. The customisation variable +;; `mouse-1-click-follows-link' is influencing the clicking behavior +;; (and tooltips) of the links, the variable's default is 450 (milli +;; seconds) setting it to nil means only clicking with mouse-2 is +;; following the link (hint: Drew Adams). + +;; The link color of symbols - referencing their builtin documentation +;; - is distinct from links which are referencing further Info +;; documentation. + +;; Below code is checking if Info documents are relevant Elisp and +;; Emacs related files to avoid false positives. Please see the +;; customization variable `info-none-emacs-or-elisp-documents'. + +;; The code uses mostly mechanisms from Emacs' lisp/help-mode.el file. + + +;;; Code: + +(require 'button) +(require 'cl-lib) +(require 'help-mode) +(require 'cl-seq) +(require 'subr-x) + +(defcustom info-make-xref-flag t + "Non-nil means Emacs creates symbol links in info buffers. +Please see the function `info-make-xrefs' for further +information." + :type '(choice (const :tag "Create links" t) + (const :tag "Do not link" nil)) + :version "30.1" + :group 'info) + +;; Toggle on or off the linking to help documents +(when info-make-xref-flag + (add-hook 'Info-selection-hook 'info-make-xrefs)) + +(defvar info-emacs-info-dir-content nil + "List of file names in Emacs' info directories. +It is used to check if the current info file `Info-current-file' +belongs to the Emacs and Elisp context. This variable will be +initialised when opening the first info file.") + +;; Turn better into a regexp list? Hint: Stefan Monier or switch to +;; alists with an explanation of file name? +(defcustom info-none-emacs-or-elisp-documents + '("aarm2012" ; Stefan: Ada manual, Elpa archive + "arm2012" ; Stefan: Ada manual + "sicp" ; T.V: Structure and Interpretation of Computer Programs, + ; Melpa archive + ) + "List of (known) documentation which is not related to GNU Emacs or Elis= p. +As well as documents which should not be searched for linking to +help documentation, for more details please see +`info-check-docu-p'. The list must contain only the base name of +files, without the file extension \".info\"." + :type '(repeat string) + :version "30.1" + :group 'info) + +(defun info-check-docu-p () + "Check if the current info file is relevant to Emacs or Elisp. +That means `Info-current-file' is either found in Emacs' info/ +directory or in `package-user-dir' and is not included in the +`info-none-emacs-or-elisp-documents' list." + (unless info-emacs-info-dir-content + (info-compile-emacs-info-dir-content)) + (let* ((ifile Info-current-file) + (ifi (when ifile + (file-name-sans-extension + (file-name-nondirectory ifile)))) + (pdir (when (boundp 'package-user-dir) + (expand-file-name + package-user-dir))) + ;; Check if checking pdir is redundant because Package adds + ;; info package folders to Info-directory-list anyway? + (ifiles info-emacs-info-dir-content) + (ndocu info-none-emacs-or-elisp-documents) + (is-info (and ifile + (or (assoc-string (concat ifi ".info") ifiles) + ;; the top info "dir" file + (assoc-string (concat ifi ".info.gz") ifiles) + ;; info files might be archived! + (when pdir (string-match pdir ifile))) + (not (assoc-string ifi ndocu))))) + (unless is-info + (message "No Emacs related info file: %s." ifile)) + is-info)) + +(defvar describe-symbol-backends) ;from help-mode.el +(defvar help-xref-following) ;dito + +(defface info-color + '((t (:inherit font-lock-doc-face + ;; font-lock-preprocessor-face ; similar to link face (de= fault) + ;; font-lock-builtin-face ; similar (default Emacs) + ;; font-lock-function-name-face ; similar (default) + ;; info-face + ))) + "Face for the `symbol' reference items in `info' nodes." + :group 'info-colors) + +;; Button types +(define-button-type 'info + 'link t ;for Info-next-reference-or-link + 'follow-link t + 'face 'info-color + 'action #'info-button-action) + +(define-button-type 'info-function + :supertype 'info + 'info-function 'describe-function + 'info-echo (purecopy "mouse-2, RET: describe this function")) + +(define-button-type 'info-variable + :supertype 'info + 'info-function 'describe-variable + 'info-echo (purecopy "mouse-2, RET: describe this variable")) + +(define-button-type 'info-face + :supertype 'info + 'info-function 'describe-face + 'info-echo (purecopy "mouse-2, RET: describe this face")) + +(define-button-type 'info-symbol + :supertype 'info + 'info-function #'describe-symbol + 'info-echo (purecopy "mouse-2, RET: describe this symbol")) + +(define-button-type 'info-function-def + :supertype 'info + 'info-function (lambda (fun &optional file type) + (or file + (setq file (find-lisp-object-file-name fun type))) + (if (not file) + (message "Unable to find defining file") + (require 'find-func) + (when (eq file 'C-source) + (setq file + (help-C-file-name (indirect-function fun) 'fu= n))) + ;; Don't use find-function-noselect because it follows + ;; aliases (which fails for built-in functions). + (let ((location + (find-function-search-for-symbol fun type file= ))) + (pop-to-buffer (car location)) + (run-hooks 'find-function-after-hook) + (if (cdr location) + (goto-char (cdr location)) + (message "Unable to find location in file"))))) + 'info-echo (purecopy "mouse-2, RET: find function's definition")) + +;; Functions + +(defun info-compile-emacs-info-dir-content () + "Build a list of file names from Emacs' info directories. +This function fills `info-emacs-info-dir-content' with files from +`Info-directory-list'." + (setq info-emacs-info-dir-content + (mapcar 'file-name-nondirectory ;'file-name-sans-extension + (directory-files + (car + ;; search for the main Emacs' info/ directory, when this + ;; function is called Info-directory-list is already + ;; initialised + (cl-member "[^.]emacs" Info-directory-list :test + 'string-match-p)) + ;; don't list "." and ".." + t "[^.]$")))) + +(defun info-button-action (button) + "Call BUTTON's help function." + (info-do-xref nil + (button-get button 'info-function) + (button-get button 'info-args))) + +(defun info-do-xref (_pos function args) + "Call the help cross-reference function FUNCTION with args ARGS. +Things are set up properly so that the resulting `help-buffer' has +a proper [back] button." + ;; There is a reference at point. Follow it. + (let ((help-xref-following nil)) + (apply + function (if (eq function 'info) + (append args (list (generate-new-buffer-name "*info*")))= args)))) + +(defun info-button (match-number type &rest args) + "Make a hyperlink for cross-reference text previously matched. +MATCH-NUMBER is the subexpression of interest in the last matched +regexp. TYPE is the type of button to use. Any remaining arguments are +passed to the button's info-function when it is invoked. +See `info-make-xrefs' Don't forget ARGS." + ;; Don't munge properties we've added, especially in some instances. + (unless (button-at (match-beginning match-number)) + ;; (message "Creating button: %s." args) + (make-text-button (match-beginning match-number) + (match-end match-number) + 'type type 'info-args args))) + +(defvar info-symbol-context + '(( variable . "variable\\|option") + ( function . "function\\|command\\|call") + ( face . "face") + ( ignore . "symbol\\|program\\|property") + ;; ignore symbols following this context type + ( definition . "source \\(?:code \\)?\\(?:of\\|for\\)")) + ;; function definitions in files + "This list helps to distinguish symbol types. +Words in info documentation preceding a (quoted) symbol are used +to distinguish variables, functions, faces and symbols. The +context information can also be used to ignore symbols because +there is no help documentation for them. The strings of the the +list are becoming part of `info-symbol-regexp'.") + +(defvar info-symbol-regexp + ;; use purecopy? + (concat + "\\(" ; Context start + "\\<\\(" ; Contex type definition + (string-remove-suffix + "\\|" + (mapconcat + (lambda (x) (concat "\\(" (cdr x) "\\)\\|")) + info-symbol-context "")) + "\\)" ; Context type definition end + "[ \t\n]+" ; Separators to quoted symbols + "\\)?" ; End of context + ;; quoted symbol + "['`=E2=80=98]" ; opening quotes + ;; Note: Symbol starting with word-syntax character: + "\\(\\sw\\(\\sw\\|\\s_\\)+\\|`\\)" ; The symbol itself + "['=E2=80=99]" ; End quotes + ) + "The regular expression for matching symbols to their help documentation. +It is comprised of the symbol's context and the (quoted) symbol +name. The various groups of context regular expressions are +matched in `info-make-xrefs' to distinct info buttons.") + +(defun info-check-type( type) + "Check if TYPE corresponds to the current search result. +The function is used in `info-make-xrefs'." + (let* ((isc info-symbol-context) + (n 3) ;embedded within 2 groups + (l (+ 3 (length isc)))) + (while (and (not (eq type (caar isc) ) ) (< n l) ) + (setq n (1+ n)) + (setq isc (cdr isc))) + (match-string n))) + + +;;;###autoload +(defun info-make-xrefs (&optional buffer) + "Parse and hyperlink documentation cross-references in the given BUFFER. +Find cross-reference information in a buffer and activate such +cross references for selection with `help-follow'. The current +buffer is processed if the BUFFER argument is omitted. + + Cross-references have the canonical (quoted) form `symbol-name' +and the type of reference may be disambiguated by the preceding +word(s) as compiled in `info-symbol-regexp'. For example: Symbol +names are receiving distinct variable buttons when preceeded by +the words \"variable\" or \"option\". + +Variables are also detected when their names follow below form: + + -- User Option: variable-name + -- Variable: ... + +Function names are also detected when prefixed by `M-x`, for +example `M-x function-name` or are quoted and prefixed like `M-x +function-name`. + +Function names are detected, as well, when appearing behind the +following forms, which occur - for example - in the Elisp manual: + + -- Special Form: function-name + -- Command: ... + -- Function: ... + -- Macro: ... + +The linking is similar to mechanisms from lisp/help.el." + (interactive "b") + (when (info-check-docu-p) + (with-current-buffer (or buffer (current-buffer)) + (save-excursion + (goto-char (point-min)) + (with-silent-modifications ;hint from Stefan + (let ((case-fold-search t) + (inhibit-read-only t)) + (with-syntax-table emacs-lisp-mode-syntax-table + ;; Quoted symbols + (save-excursion + (while (re-search-forward info-symbol-regexp nil t) + (let* ((data (match-string (+ 3 (length info-symbol-cont= ext)))) + (sym (intern-soft data))) + (if sym + (cond + ((info-check-type 'variable) + (and (or (boundp sym) + (get sym 'variable-documentation)) + (info-button 8 'info-variable sym))) + ((info-check-type 'function) + (and (fboundp sym) + (info-button 8 'info-function sym))) + ((info-check-type 'face) + (and (facep sym) + (info-button 8 'info-face sym))) + ((info-check-type 'ignore)) + ((info-check-type 'definition) + (info-button 8 'info-function-def sym)) + ;; symbols + ((cl-some (lambda (x) (funcall (nth 1 x) sym)) + describe-symbol-backends) + (info-button 8 'info-symbol sym))))))) + ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; (info "(elisp) Eval") + ;; Elisp manual -- Special Form: + ;; -- Command: + ;; -- Function: function-name function + ;; -- Macro: + (save-excursion + (while (re-search-forward + "-- \\(Special Form:\\|Command:\\|Function:\\|Macr= o:\\) " + nil t) + (looking-at "\\(\\sw\\|\\s_\\)+") + (let ((sym (intern-soft (match-string 0)))) + (if (fboundp sym) + (info-button 0 'info-function sym))))) + ;; -- User Option: + ;; -- Variable: variable-name + (save-excursion + (while (re-search-forward + "-- \\(User Option:\\|Variable:\\) " + nil t) + (looking-at "\\(\\sw\\|\\s_\\)+") + (let ((sym (intern-soft (match-string 0)))) + (if (boundp sym) + (info-button 0 'info-variable sym))))) + ;; M-x prefixed functions + (save-excursion + (while (re-search-forward + ;; Assume command name is only word and symbol + ;; characters to get things like `use M-x foo->bar= '. + ;; Command required to end with word constituent + ;; to avoid `.' at end of a sentence. + ;; "\\