Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
;;; eev-elinks.el --- `find-efunction-links' and other `find-e*-links'  -*- lexical-binding: nil; -*-

;; Copyright (C) 2012-2024 Free Software Foundation, Inc.
;;
;; This file is part of GNU eev.
;;
;; GNU eev is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; GNU eev is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
;;
;; Author:     Eduardo Ochs <[email protected]>
;; Maintainer: Eduardo Ochs <[email protected]>
;; Version:    20241020
;; Keywords:   e-scripts
;;
;; Latest version: <http://anggtwu.net/eev-current/eev-elinks.el>
;;       htmlized: <http://anggtwu.net/eev-current/eev-elinks.el.html>
;;       See also: <http://anggtwu.net/eev-current/eev-beginner.el.html>
;;                 <http://anggtwu.net/eev-intros/find-eev-intro.html>
;;                 <http://anggtwu.net/eev-intros/find-links-intro.html>
;;                                               (find-eev-intro)
;;                                               (find-links-intro)

;;; Commentary:

;; See this for a (rough) classification of eev's hyperlink functions
;; into several classes:
;;
;;   (find-links-conv-intro "3. Classification")
;;   (find-links-conv-intro "3. Classification" "`find-elinks'")
;;   (find-links-conv-intro "3. Classification" "`find-elinks'+")
;;
;; In this file we define `find-elinks' and several functions based on
;; it that generate relatively simple elisp hyperlinks buffers -
;; buffers which are mostly composed of elisp hyperlinks. They follow
;; these conventions:
;;
;;   (find-links-intro "3. Elisp hyperlinks buffers conventions")
;;
;; The "more complex" `find-elinks'-based functions are the ones which
;; use `ee-template0'. They are defined here:
;;
;;   (find-eevfile "eev-tlinks.el")
;;
;; Here's one example of each function in this file:
;; [to be done]




;; «.around-point»		(to "around-point")
;; «.find-elinks»		(to "find-elinks")
;; «.find-efunction-links»	(to "find-efunction-links")
;; «.find-eloadhistory-links»	(to "find-eloadhistory-links")
;; «.find-evariable-links»	(to "find-evariable-links")
;; «.find-ekey-links»		(to "find-ekey-links")
;; «.find-elongkey-links»	(to "find-elongkey-links")
;; «.find-eapropos-links»	(to "find-eapropos-links")
;; «.find-einfo-links»		(to "find-einfo-links")
;; «.find-eintro»		(to "find-eintro")
;; «.ee-find-wget-links»	(to "ee-find-wget-links")

;; «.ee-code-c-d-filter»	(to "ee-code-c-d-filter")
;; «.ee-find-xxxfile-sexps»	(to "ee-find-xxxfile-sexps")
;; «.find-file-links»		(to "find-file-links")
;; «.find-grep-links»		(to "find-grep-links")
;; «.find-pdflike-page-links»	(to "find-pdflike-page-links")
;; «.ee-hyperlink-prefix»	(to "ee-hyperlink-prefix")

;; «.find-eface-links»		(to "find-eface-links")
;; «.find-ecolor-links»		(to "find-ecolor-links")
;; «.find-epackage-links»	(to "find-epackage-links")
;;   «.ee-package-desc»		(to "ee-package-desc")
;;   «.ee-package-dir»		(to "ee-package-dir")
;; «.find-esetkey-links»	(to "find-esetkey-links")
;; «.find-ekbmacro-links»	(to "find-ekbmacro-links")
;; «.find-emajormode-links»	(to "find-emajormode-links")
;; «.find-eminormodes-links»	(to "find-eminormodes-links")
;; «.find-emodeline-links»	(to "find-emodeline-links")
;; «.find-emenubar-links»	(to "find-emenubar-links")
;; «.find-elocus-links»		(to "find-elocus-links")

;; «.find-code-pdf-links»	(to "find-code-pdf-links")
;; «.find-pdf-links»		(to "find-pdf-links")
;; «.find-code-audiovideo-links»  (to "find-code-audiovideo-links")



;; The functions in these sections were moved to:
;;   (find-eev "eev-hlinks.el")
;;
;; «.find-here-links»		(to "find-here-links")
;; «.find-here-links-beginner»	(to "find-here-links-beginner")
;; «.find-here-links-3»		(to "find-here-links-3")
;; «find-here-links»		(to ".find-here-links")
;; «find-here-links-beginner»	(to ".find-here-links-beginner")
;; «find-here-links-3»		(to ".find-here-links-3")








;;;                                  _                   _       _   
;;;   __ _ _ __ ___  _   _ _ __   __| |      _ __   ___ (_)_ __ | |_ 
;;;  / _` | '__/ _ \| | | | '_ \ / _` |_____| '_ \ / _ \| | '_ \| __|
;;; | (_| | | | (_) | |_| | | | | (_| |_____| |_) | (_) | | | | | |_ 
;;;  \__,_|_|  \___/ \__,_|_| |_|\__,_|     | .__/ \___/|_|_| |_|\__|
;;;                                         |_|                      
;;
;; «around-point» (to ".around-point")
;; (find-eapropos "around-point")

;; (find-elnode "Index" "* thing-at-point:")
;; (find-efunction        'thing-at-point)
;; (find-efile            "thingatpt.el")
(defun ee-url-at-point ()
  (ee-no-properties (thing-at-point 'url)))

(defun ee-stuff-around-point0 (chars)
  (interactive "MChars: \np")		; for tests
  (save-excursion
    (let* ((e (progn (skip-chars-forward  chars) (point)))
	   (s (progn (skip-chars-backward chars) (point))))
      (buffer-substring s e))))

(defun ee-stuff-around-point (chars)
  (interactive "MChars: \np")		; for tests
  (ee-no-properties (ee-stuff-around-point0 chars)))

(defun ee-debpkgname-around-point ()
"Return the name of the Debian package around point.
This function is not very smart."
  (ee-stuff-around-point "a-z0-9-+."))

(defun ee-debpkgname-ask (&optional prompt)
"Ask for the name of a Debian package; the default is the debpkgname at point."
  (read-string (or prompt "Debian package name: ")
	       (ee-debpkgname-around-point)))

(defun ee-manpagename-around-point ()  
"Return the manpagename around point.
This function is not very smart - it doesn't understand section names."
  (interactive)
  (ee-stuff-around-point "A-Za-z0-9-+_:."))

(defun ee-manpagename-ask (&optional prompt)
"Ask for the name of a manpage; the default is the manpage name at point."
  (interactive)
  (read-string (or prompt "Manpage: ")
	       (ee-manpagename-around-point)))

(defun ee-1stclassvideo-around-point-ask ()
"Ask for the name of a first-class video; the default is a string around point."
  (ee-1stclassvideo-ask (ee-stuff-around-point "A-Za-z0-9")))

;; See: (find-eev "eev-videolinks.el" "ee-1stclassvideos-info")
;; Tests: (ee-1stclassvideo-ask "foo")
;;        (ee-1stclassvideo-ask "eev2020")
;;        (ee-1stclassvideo-ask "eev2020video")
;;        (ee-1stclassvideos)
;;
(defun ee-1stclassvideo-ask (default0)
  "An internal function used by `ee-1stclassvideo-around-point-ask'."
  (let* ((default1 (replace-regexp-in-string "video$" "" default0))
	 (videos   (ee-1stclassvideos))
	 (default  (if (member default1 videos) default1))
	 (promptd  (if default (format " (default: %s)" default) ""))
	 (prompt   (format "Stem (\"c\") of the video:%s " promptd)))
    (completing-read prompt videos nil nil nil nil default)))

(defun ee-1stclassvideos ()
  (sort (mapcar 'car ee-1stclassvideos-info) 'string<))

(defun ee-lisp-symbol-around-point ()  
"Return the Lisp symbol around point.
This function is not very smart."
  (interactive)
  (ee-stuff-around-point "A-Za-z0-9-+_:*"))

(defun ee-lisp-symbol-around-point-ask (&optional prompt)
"Ask for a Lisp symbol; the default is the Lisp symbol at point."
  (interactive)
  (read-string (or prompt "Symbol: ")
	       (ee-lisp-symbol-around-point)))

  



;;;   __ _           _            _ _       _        
;;;  / _(_)_ __   __| |       ___| (_)_ __ | | _____ 
;;; | |_| | '_ \ / _` |_____ / _ \ | | '_ \| |/ / __|
;;; |  _| | | | | (_| |_____|  __/ | | | | |   <\__ \
;;; |_| |_|_| |_|\__,_|      \___|_|_|_| |_|_|\_\___/
;;;                                                  
;; «find-elinks»  (to ".find-elinks")
;; `find-elinks' is an internal function used by all functions that
;; generate buffers with hyperlinks, like `find-efunction-links', and
;; by most functions that generate buffers with hyperlinks followed by
;; a templated string, like `find-latex-links'. It is a variant of
;; `find-estring', in the sense that
;;
;;    (find-elinks LIST POS-SPEC)
;;
;; is similar to
;;
;;    (find-estring (ee-links-to-string LIST) pos-spec).
;;
;; Here is how the conversion from LIST to a string works. Try:
;;
;;    (find-elinks '((a sexp) "a string"))
;;
;; In simple examples like the one above each element in LIST becomes
;; a line in the output string: sexps are prefixed by
;; `ee-hyperlink-prefix', and each string becomes a line. Try:
;;
;;    (find-elinks '((a sexp) (another) "" (sexp) "a string" "another"))
;;
;; The "" becomes an empty line. But things can be more complex: here,
;;
;;    (find-elinks '((str "foo\nbar") nil "foo\nbar"))
;;
;; the `(str "foo\nbar")' becomes a single line, with the `\n'
;; displayed as "\n"; the nil is dropped from the list by
;; `ee-remove-nils' and does not become a line, and the string
;; "foo\nbar" becomes two lines.

(defun ee-remove-nils (list)
  "Return a list like LIST, but without the `nil's."
  (let (newlist)
    (mapc (lambda (e) (if e (setq newlist (cons e newlist))))
	  list)
    (nreverse newlist)))

(defun ee-links-to-string0 (list)
  "Convert a LIST of strings and sexps to a big string."
  (mapconcat (lambda (o) (if (stringp o) o (ee-HS o)))
	     (ee-remove-nils list)
	     "\n"))

(defun ee-links-to-string (list)
  "Convert a LIST of strings and sexps to a big string (newline-terminated)."
  (concat (ee-links-to-string0 list)
	  "\n"))

(defun find-elinks (links &rest pos-spec-list)
  "Visit a temporary buffer containing LINKS converted to hyperlink lines."
  (let ((ee-buffer-name (or ee-buffer-name "*Elisp hyperlinks*")))
    (apply 'find-estring (ee-links-to-string links) pos-spec-list)))

(defun find-elinks-elisp (links &rest pos-spec-list)
  "Visit a temporary buffer containing LINKS converted to hyperlink lines.
The buffer is put in Emacs Lisp mode."
  (let ((ee-buffer-name (or ee-buffer-name "*Elisp hyperlinks*"))
	(ee-hyperlink-prefix ";; "))
    (apply 'find-estring-elisp (ee-links-to-string links) pos-spec-list)))





;;;   __ _           _             __                  _   _             
;;;  / _(_)_ __   __| |       ___ / _|_   _ _ __   ___| |_(_) ___  _ __  
;;; | |_| | '_ \ / _` |_____ / _ \ |_| | | | '_ \ / __| __| |/ _ \| '_ \ 
;;; |  _| | | | | (_| |_____|  __/  _| |_| | | | | (__| |_| | (_) | | | |
;;; |_| |_|_| |_|\__,_|      \___|_|  \__,_|_| |_|\___|\__|_|\___/|_| |_|
;;;                                                                      
;; «find-efunction-links» (to ".find-efunction-links")
;; `find-efunction-links' is one of the simplest functions that
;; generate buffers with hyperlinks. It is mentioned in
;; `find-eev-quick-intro':
;; 
;;   (find-eev-quick-intro "4. Creating Elisp Hyperlinks")
;;   (find-eev-quick-intro  "(find-efunction-links 'find-file)")
;;   (find-eev-quick-intro "4.2. `find-ekey-links' and friends")
;;
;; Key binding: (find-eev "eev-mode.el" "eev-mode-map-set" "M-h" "M-f")
;;       Tests: (find-efunction-links 'find-file)
;;                       (eek "M-h M-f find-file")
;;
(defun find-efunction-links (&optional f &rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks related to the function F."
  (interactive (find-function-read))
  (let ((ee-buffer-name (or ee-buffer-name "*find-efunction-links*")))
    (apply 'find-elinks
      `((find-efunction-links ',f ,@pos-spec-list)
        (eek ,(format "M-h M-f  %s" f))
        (find-eev-quick-intro "4.2. `find-ekey-links' and friends")
	""
	,@(ee-find-efunction-links f)
	)
      pos-spec-list)))

(defun ee-find-efunction-links (f)
  "Return a list of hyperlinks for F (a function symbol).
This is an internal function used by `find-efunction-links' and
`find-ekey-links'."
  `((find-efunctiondescr ',f)
    (find-efunction ',f)
    (find-efunctionpp ',f)
    (find-efunctiond ',f)
    (find-ealias ',f)
    (find-eppp (symbol-plist ',f))
    (find-hfunction ',f)
    ""
    ,@(if (commandp f)
	  `((Info-goto-emacs-command-node ',f)
	    (find-enode "Command Index" ,(format "* %S:" f))
	    ))
    (find-elnode "Index" ,(format "* %S:" f))
    ""
    (where-is ',f)
    ""
    (find-efunctionlgrep ',f)
    (find-efunctionlgrep ',f ',f)
    (find-eloadhistory-for ',f)
    (find-eloadhistory-for ',f 2 ,(format " %s)" f))
    ;; (find-estring-elisp (ee-eloadhistory-find-flines))
    ""
    (symbol-file ',f 'defun)
    (find-fline (symbol-file ',f 'defun))
    (find-eppp (assoc (symbol-file ',f 'defun) load-history))
    (find-eppp (assoc ,(symbol-file f 'defun) load-history))
    (find-eppp (mapcar 'car load-history))
    (find-estring (mapconcat 'identity (mapcar 'car load-history) "\n"))
    ""
    (find-estring (documentation ',f))
    (find-estring (documentation ',f t))
    (describe-function ',f)
    ;; (find-eCfunction ',f)		; obsolete
    ))



;; «find-eloadhistory-links»  (to ".find-eloadhistory-links")
;; Skel: (find-find-links-links-new "eloadhistory" "" "")
;; Test: (find-eloadhistory-links)
;;
(defun find-eloadhistory-links (&rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks for eloadhistory."
  (interactive)
  (apply
   'find-elinks
   `((find-eloadhistory-links ,@pos-spec-list)
     ;; Convention: the first sexp always regenerates the buffer.
     (find-efunction 'find-eloadhistory-links)
     ""
     (find-elnode "Where Defined" "load-history")
     (find-estring-elisp (ee-eloadhistory-find-flines))
     (find-estring-elisp (ee-eloadhistory-fors))
     )
   pos-spec-list))

;; Tests: (ee-shorten-elc (symbol-file 'next-line 'defun))
;;        (find-estring-elisp (ee-eloadhistory-fors))
;;
(defun ee-eloadhistory-find-flines ()
  "An internal function used by `find-eloadhistory-links'."
  (mapconcat
   (lambda (fname) (format "%S\n" `(find-fline ,(ee-shorten-elc fname))))
   (mapcar 'car load-history)
   ""))

(defun ee-eloadhistory-fors ()
  "An internal function used by `find-eloadhistory-links'."
  (mapconcat
   (lambda (fname) (format "%S\n" `(find-eloadhistory-for ,fname)))
   (mapcar 'car load-history)
   ""))

(defun ee-shorten-elc (fname)
  "An internal function used by `ee-eloadhistory-find-flines'."
  (ee-shorten-file-name (replace-regexp-in-string ".elc$" ".el" fname)))




;;;   __ _           _                            _       _     _      
;;;  / _(_)_ __   __| |       _____   ____ _ _ __(_) __ _| |__ | | ___ 
;;; | |_| | '_ \ / _` |_____ / _ \ \ / / _` | '__| |/ _` | '_ \| |/ _ \
;;; |  _| | | | | (_| |_____|  __/\ V / (_| | |  | | (_| | |_) | |  __/
;;; |_| |_|_| |_|\__,_|      \___| \_/ \__,_|_|  |_|\__,_|_.__/|_|\___|
;;;                                                                    
;; «find-evariable-links» (to ".find-evariable-links")
;; Skel: (find-find-links-links-new "evariable" "var" "")
;; Tests: (find-evariable-links 'isearch-mode-map)
;;        (find-evariable-links 'line-move-visual)
;;                (eek "M-h M-v  line-move-visual")
;; Key binding: (find-eev "eev-mode.el" "eev-mode-map-set" "M-h" "M-v")
;;        
(defun find-evariable-links (var &rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks about a variable."
  (interactive (find-function-read 'defvar))
  (apply
   'find-elinks
   `((find-evariable-links ',var ,@pos-spec-list)
     (eek ,(format "M-h M-v  %s" var))
     ;; Convention: the first sexp always regenerates the buffer.
     (find-efunction 'find-evariable-links)
     ""
     ,var
     (describe-variable ',var)
     (find-evardescr ',var)
     (find-evariable ',var)
     (find-epp ,var)
     (find-eppp (symbol-plist ',var))
     (find-hvariable ',var)
     ""
     (find-enode "Variable Index" ,(format "* %S:" var))
     (find-elnode "Index" ,(format "* %S:" var))
     ""
     (keymapp ,var)
     (find-ekeymapdescr ,var)
     (describe-keymap ,var)
     )
   pos-spec-list))


;;;   __ _           _            _              
;;;  / _(_)_ __   __| |       ___| | _____ _   _ 
;;; | |_| | '_ \ / _` |_____ / _ \ |/ / _ \ | | |
;;; |  _| | | | | (_| |_____|  __/   <  __/ |_| |
;;; |_| |_|_| |_|\__,_|      \___|_|\_\___|\__, |
;;;                                        |___/ 
;;
;; «find-ekey-links» (to ".find-ekey-links")
;; Skel: (find-find-links-links-old "\\M-k" "ekey" "key")
;;
;; The functions in this section generate buffers with hyperlinks
;; about a key sequence. Like this,
;;
;;   (find-eev-quick-intro "4. Creating Elisp Hyperlinks")
;;   (find-eev-quick-intro "(find-efunction-links 'find-file)")
;;
;; but for a key sequence instead of for a function. Try:
;;
;;    (eek "M-h M-k C-x 4 0")
;;    (find-elongkey-links "C-x 4 0")
;;    (find-ekey-links      "\C-x40")
;;    (find-ekey-links [?\C-x ?4 ?0])
;;    (read-key-sequence        "Key seq:")
;;    (read-key-sequence-vector "Key seq:")
;;
;; The high-level functions here are `find-ekey-links' and
;; `find-elongkey-links'. In the terminology of this section, a "key"
;; means a key sequence as a vector or a string, as described here,
;;
;;   (find-elnode "Key Sequence Input")
;;   (find-elnode "Strings of Events")
;;
;; and by "long key" we mean a key sequence in "keyboard macro" form,
;; with or without the ";; <comment>"s. This format is described here:
;;
;;   (find-efunctiondescr 'edmacro-mode "Format of keyboard macros")
;;
;; Here are some conversion functions:
;;
;;    (format-kbd-macro     "\C-x40")
;;    (format-kbd-macro     "\C-x40" t)
;;    (ee-format-kbd-macro  "\C-x40")
;;    (read-kbd-macro      "C-x 4 0")
;;
;; Note that the last hyperlinks in these buffers are conversions:
;;
;;    (find-ekey-links      "\C-x40" 2 "format-kbd-macro")
;;    (find-elongkey-links "C-x 4 0" 2 "format-kbd-macro")
;;
;; Only `find-ekey-links' is bound to a key sequence (`M-h M-k'),
;; because `interactive' can return a key but not a long key:
;;
;;   (find-efunctiondescr 'interactive "Key sequence")
;;
;; A good way to save to our notes hyperlinks about a key sequence is
;; by using the lines 4 and 5 in a `M-h M-k' buffer. For example, in
;; the case of `M-c', they are:
;;
;;   (eek "M-h M-k M-c  ;; capitalize-word")
;;   (find-efunctiondescr 'capitalize-word)
;;

;; Moved to eev-mode.el:
;; (define-key eev-mode-map "\M-h\M-k" 'find-ekey-links)

;; Test: (ee-format-kbd-macro [down])
(defun ee-format-kbd-macro (key)
  "Example: (ee-format-kbd-macro [down])  --> \"<down>  ;; next-line\""
  (replace-regexp-in-string "[ \t][ \t]+" "  " (format-kbd-macro key t)))

;; Test: (find-ekey-links "\C-x40")
(defun find-ekey-links (key &rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks related to the key sequence KEY.
See: (find-eev \"eev-elinks.el\" \"find-ekey-links\")"
  (interactive "kElisp hyperlinks for key: ")
  (let ((ee-buffer-name (or ee-buffer-name "*find-ekey-links*"))
        (longkey     (format-kbd-macro key))
	(longkey+ (ee-format-kbd-macro key))
	(f                (key-binding key)))
    (apply 'find-elinks
	   `((find-ekey-links ,key ,@pos-spec-list)
	     ;; Convention: the first sexp always regenerates the buffer.
	     (find-elongkey-links      ,longkey)
	     (find-elongkey-links      ,longkey+)
	     (find-efunction-links    ',f)
	     ;; (find-efunctiondescr ',f)
	     ""
	     ;; (find-eev-quick-intro "4.2. `find-ekey-links' and friends")
	     ;; ""
	     ,@(ee-find-eboundkey-links key f)
	     )
	   pos-spec-list)))

;; «find-elongkey-links» (to ".find-elongkey-links")
;; Test: (find-elongkey-links "C-x 4 0")
(defun find-elongkey-links (longkey &rest pos-spec-list)
  "Like `find-ekey-links', but LONGKEY is a key sequence \"spelled out\".
Example: (find-elongkey-links \"M-h M-k\")
See `read-kbd-macro' and `edmacro-mode' for the format."
  (interactive "sElisp hyperlinks for key (long format): ")
  (let* ((ee-buffer-name (or ee-buffer-name "*find-elongkey-links*"))
         (key (read-kbd-macro longkey))
	 (f   (key-binding key)))
    (apply 'find-elinks
	   `((find-elongkey-links   ,longkey)
	     (find-ekey-links       ,key)
	     (find-efunction-links ',f)
	     ""
	     ,@(ee-find-eboundkey-links key f)
	     )
	   pos-spec-list)))

;; Test: (find-elinks (ee-find-eboundkey-links "\M-c" 'capitalize-word))
(defun ee-find-eboundkey-links (key f)
  "From KEY and its binding, F, produce a list of hyperlinks.
This is an internal function used by `find-ekey-links' and
`find-elongkey-links'."
  (let ((longkey     (format-kbd-macro key))
	(longkey+ (ee-format-kbd-macro key)))
    `((find-eev-quick-intro "4.2. `find-ekey-links' and friends")
      (eek ,(format "M-h M-k  %s" longkey))
      (eek ,(format "M-h M-k  %s" longkey+))
      (eek ,(format "C-h   k  %s" longkey+))
      (eek ,(format "M-h M-f  %s" f))
      ""
      (find-efunctiondescr ',f)
      (find-ekeydescr ,key)
      (find-efunction ',f)
      (find-efunctionpp ',f)
      (find-efunctiond ',f)
      ""
      (Info-goto-emacs-key-command-node ,key)
      (Info-goto-emacs-command-node ',f)
      (find-enode "Command Index" ,(format "* %S:" f))
      (find-elnode "Index" ,(format "* %S:" f))
      ""
      (describe-key-briefly ,key)
      (find-estring (documentation ',f))
      (find-estring (documentation ',f t))
      (describe-key ,key)
      (describe-function ',f)
      ""
      (where-is ',f)
      (key-description ,key)
      (format-kbd-macro ,key)
      (format-kbd-macro ,key t)
      (ee-format-kbd-macro ,key)
      (key-binding ,key)
      )))




;;;                                             
;;;   ___  __ _ _ __  _ __ ___  _ __   ___  ___ 
;;;  / _ \/ _` | '_ \| '__/ _ \| '_ \ / _ \/ __|
;;; |  __/ (_| | |_) | | | (_) | |_) | (_) \__ \
;;;  \___|\__,_| .__/|_|  \___/| .__/ \___/|___/
;;;            |_|             |_|              
;;
;; «find-eapropos-links»  (to ".find-eapropos-links")
;; Skel: (find-find-links-links-new "eapropos" "regexp" "ee-buffer-name")
;; Test: (find-eapropos-links "process")
;;
(defun find-eapropos-links (&optional regexp &rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks for eapropos."
  (interactive "sApropos symbol (regexp): ")
  (setq regexp (or regexp "{regexp}"))
  (let ((ee-buffer-name (or ee-buffer-name "*find-eapropos-links*")))
    (apply
     'find-elinks
     `((find-eapropos-links ,regexp ,@pos-spec-list)
       ;; Convention: the first sexp always regenerates the buffer.
       (find-efunction 'find-eapropos-links)
       ""
       (find-eaproposf ,regexp)
       (find-eaproposv ,regexp)
       (find-eapropos ,regexp)
       ""
       (apropos ,regexp)
       (apropos-command ,regexp)
       (apropos-function ,regexp)
       (apropos-variable ,regexp)
       (apropos-local-variable ,regexp)
       ""
       (eek ,(format "M-h a %s" regexp))
       (eek ,(format "C-h a %s" regexp))
       )
     pos-spec-list)))



;;;   __ _           _            _        __       
;;;  / _(_)_ __   __| |       ___(_)_ __  / _| ___  
;;; | |_| | '_ \ / _` |_____ / _ \ | '_ \| |_ / _ \ 
;;; |  _| | | | | (_| |_____|  __/ | | | |  _| (_) |
;;; |_| |_|_| |_|\__,_|      \___|_|_| |_|_|  \___/ 
;;;                                                 
;; «find-einfo-links» (to ".find-einfo-links")
;; Tests: (progn (find-enode "Lisp Eval") (find-einfo-links))
;;        (progn (find-enode "Lisp Eval") (eek "M-h M-i"))
;;
;; Key binding:
;; (find-eev "eev-mode.el" "eev-mode-map-set")
;; (find-eev "eev-mode.el" "eev-mode-map-set" "\\M-h\\M-i")
;;
;; See:
;; (find-eev "eev-hlinks.el" "hprog")
;; (find-eev "eev-hlinks.el" "hprog" "(ee-find-info-links)")
;; (find-eev-quick-intro "10.2. Generating short hyperlinks to info nodes")

(defvar ee-info-file "")

(defun ee-infop          () (get-buffer "*info*"))
(defun ee-info-node      () (with-current-buffer "*info*" Info-current-node))
(defun ee-info-book+     () (with-current-buffer "*info*" Info-current-file))
(defun ee-info-book-     () (file-name-nondirectory (ee-info-book+)))
(defun ee-info-fullnode  () (format "(%s)%s" (ee-info-book-) (ee-info-node)))
(defun ee-info-fullnode+ () (format "(%s)%s" (ee-info-book+) (ee-info-node)))

(defun ee-info-file-     () (file-name-nondirectory ee-info-file))
(defun ee-info-shortp    () (string= (ee-info-book-) (ee-info-file-)))
(defun ee-info-shortf    () (ee-intern "find-%snode" ee-info-code))
(defun ee-info-shortlink () (list (ee-info-shortf) (ee-info-node)))

(defun ee-find-info-links ()
  `((info      ,(ee-info-fullnode))
    (find-node ,(ee-info-fullnode))
    ,(if (ee-info-shortp) (ee-info-shortlink))
    ))

(defun find-einfo-links (&optional fullnode &rest rest)
  "Visit a temporary buffer containing hyperlinks to the current info page.
When possible try to produce also a short hyperlink, like the last one in:

  (info \"(bashref)Pipelines\")
  (find-node \"(bashref)Pipelines\")
  (find-bashnode \"Pipelines\")

The short link is generated when the non-directory part of the
current value of the global variable `ee-info-file' - set by the
last call to a function of the form `find-XXXnode' - matches the
value of (ee-info-book-). This only works reliably for info
\"books\" that are in `Info-directory-list'."
  (interactive)
  (setq fullnode (or fullnode (ee-info-fullnode)))
  (apply 'find-elinks `(
    ;; Convention: the first sexp always regenerates the buffer.
    (find-einfo-links ,fullnode ,@rest)
    (find-einfo-links ,(ee-info-fullnode+) ,@rest)
    ""
    ,@(ee-find-info-links)
    ) rest))


;; A test: (ee-intro-stem "*(find-foo-intro)*")

;; Old version:

;; (defun find-einfo-links (&optional intro &rest rest)
;;   "Visit a temporary buffer containing hyperlinks to the current info page.
;; When possible, try to produce also a shorter hyperlink, like the last one in:
;;   (info \"(bashref)Pipelines\")
;;   (find-node \"(bashref)Pipelines\")
;;   (find-bashnode \"Pipelines\")
;; The hack for generating the shorter hyperlink uses the global
;; variables `ee-info-code' and `ee-info-file' - see:
;;   (progn
;;    (find-code-c-d \"bash\" \"/usr/share/doc/bash/examples/\" \"bashref\")
;;    (ee-goto-position \"ee-info-code\"))
;; 
;; As an extra hack, if this function is called from a \"*(find-???-intro)*\"
;; buffer, also generate a link to that buffer."
;;   (interactive)
;;   (setq intro (or intro (ee-intro-stem (buffer-name (current-buffer)))))
;;   (apply 'find-elinks `(
;;     ;; Convention: the first sexp always regenerates the buffer.
;;     (find-einfo-links ,intro ,@rest)
;;     ;; Body:
;;     ""
;;     ,@(if (ee-infop)
;; 	  (ee-find-info-links)
;;        '("[No \"*info*\" buffer]"))
;;     ""
;;     ,@(if intro
;; 	 ;; (list (ee-intern "find-%s-intro" intro))
;; 	 (ee-find-intro-links)
;;        ;; else: "[Not invoked from a \"*find-xxx-intro*\" buffer]"
;;        )
;;     ) rest))



;;;   __ _           _            _       _             
;;;  / _(_)_ __   __| |       ___(_)_ __ | |_ _ __ ___  
;;; | |_| | '_ \ / _` |_____ / _ \ | '_ \| __| '__/ _ \ 
;;; |  _| | | | | (_| |_____|  __/ | | | | |_| | | (_) |
;;; |_| |_|_| |_|\__,_|      \___|_|_| |_|\__|_|  \___/ 
;;;                                                     
;; «find-eintro» (to ".find-eintro")

;; Test: (ee-intro-stem "*(find-eev-quick-intro)*")
(defun ee-intro-stem (&optional bufname)
  "Convert a string of the form \"*(find-STEM-intro)*\" to \"STEM\".
When BUFNAME is nil use the name of the current buffer instead.
When BUFNAME is a string that is not of the right form return nil.
This can be used to test if the current buffer is an intro buffer."
  (setq bufname (or bufname (buffer-name)))
  (if (string-match "^\\*(find-\\(.*\\)-intro)\\*$" bufname)
      (match-string 1 bufname)))

;; Test: (find-elinks (ee-find-intro-links "FOO"))
(defun ee-find-intro-links (&optional stem)
  (setq stem (or stem (ee-intro-stem)))
  (let ((find-xxx-intro (ee-intern "find-%s-intro" stem))
	(url (format "http://anggtwu.net/eev-intros/find-%s-intro.html" stem)))
    `(,(ee-H url)
      (,find-xxx-intro)
      )))

;; Test: (find-eintro-links "eev-quick")
(defun find-eintro-links (&optional stem &rest rest)
  "Visit a temporary buffer containing hyperlinks to the current intro.
This only works reliably if either 1) the current buffer has a
name like \"*(find-STEM-intro)*\", or 2) STEM is given explicitly."
  (interactive)
  (setq stem (or stem (ee-intro-stem)))
  (apply 'find-elinks `(
    ;; Convention: the first sexp always regenerates the buffer.
    (find-eintro-links ,stem ,@rest)
    ""
    ,@(ee-find-intro-links stem)
    ) rest))


;; Note that eev-mode.el has this:
;; (define-key eev-mode-map "\M-h\M-i" 'find-eintro-or-einfo-links)
(defun find-eintro-or-einfo-links ()
  "Visit a temporary buffer containing hyperlinks to the current intro buffer.
If we're not in an intro buffer, visit a temporary buffer
containing hyperlinks to the current _info node_ instead. This is
a hack to let use use `M-h M-i' for both \"intro\" and \"info\"."
  (interactive)
  (if (ee-intro-stem)
      (find-eintro-links)
    (find-einfo-links)))


;; «ee-find-wget-links»  (to ".ee-find-wget-links")
;; Test: (find-elinks (ee-find-wgetes-links "maxima" "eev-demo"))
;;       (find-elinks (ee-find-wgetangg-links "LUA/Co1.lua" "Co"))
;;       (find-elinks (ee-find-wget-links "http://angg.twu.net/davidwest.txt"))
;;  See: (find-eev "eev-hlinks.el" "hprog")
;;       (find-eev "eev-hlinks.el" "hprog" "ee-find-wgetes-links")
;;
(defun ee-find-wgetes-links (&optional stem tag)
  (setq stem (or stem (ee-wgetes-bufferp)))
  (setq tag  (or tag  (ee-preceding-tag-flash-no-error)))
  `((find-es      ,stem ,@(if tag (list tag)))
    (find-es-wget ,stem ,@(if tag (list tag)))))

(defun ee-find-wgetangg-links (&optional stem tag)
  (setq stem (or stem (ee-wgetangg-bufferp)))
  (setq tag  (or tag  (ee-preceding-tag-flash-no-error)))
  `((find-angg      ,stem ,@(if tag (list tag)))
    (find-angg-wget ,stem ,@(if tag (list tag)))))

(defun ee-find-wget-links (&optional url)
  (setq url (or url (ee-wget-bufferp)))
  `((find-wget ,url)))





;;;                _                          _        __ _ _ _            
;;;   ___ ___   __| | ___        ___       __| |      / _(_) | |_ ___ _ __ 
;;;  / __/ _ \ / _` |/ _ \_____ / __|____ / _` |_____| |_| | | __/ _ \ '__|
;;; | (_| (_) | (_| |  __/_____| (_|_____| (_| |_____|  _| | | ||  __/ |   
;;;  \___\___/ \__,_|\___|      \___|     \__,_|     |_| |_|_|\__\___|_|   
;;;                                                                        
;; «ee-code-c-d-filter» (to ".ee-code-c-d-filter")
;; This is a trick to make `find-file-links' generate short hyperlinks.
;; See: (find-eev-quick-intro "10.1. Generating short hyperlinks to files")
;;
;; Note that this code is very old and uses only very basic Lisp
;; functions. The functions in eev-kla.el do something similar to
;; this, but using cl-loop. See:
;;   (find-kla-intro)
;;
;; Each call to `(code-c-d C D)' generates an entry `(C D)' in the
;; alist `ee-code-c-d-pairs'. Try:
;;
;;   (code-c-d "foo"  "/FOO")
;;   (code-c-d "bar"  "/FOO/BAR/")
;;   (code-c-d "plic" "/FOO/BAR/PLIC/")
;;   (find-epp ee-code-c-d-pairs)
;;
;; You will see that the `ee-code-c-d-pairs' now begins with:
;;
;;   (("plic" "/FOO/BAR/PLIC/")
;;    ("bar" "/FOO/BAR/")
;;    ("foo" "/FOO")
;;    ...
;;    )
;;
;; And if you run
;;
;;   (find-file-links "/FOO/BAR/PLIC/ploc")
;;
;; Then the last links in the `find-file-links' buffer will be:
;;
;;   (find-plicfile          "ploc")
;;   (find-barfile      "PLIC/ploc")
;;   (find-foofile "/BAR/PLIC/ploc")
;;
;; They are generated by `ee-find-xxxfile-sexps'. Try:
;;
;;   (find-epp (ee-find-xxxfile-sexps "/FOO/BAR/PLIC/ploc"))
;;
;; To understand the implementation, try the sexps below. Note that
;; `find-code-c-d-filter-1' and `find-code-c-d-filter-2' are debugging
;; functions.
;;
;;   (find-eppp ee-code-c-d-pairs)
;;   (find-code-c-d-filter-1 'c-d)
;;   (find-code-c-d-filter-1 'c)
;;   (find-code-c-d-filter-1 'd)
;;   (find-code-c-d-filter-1 'ed)
;;   (find-code-c-d-filter-2 "/FOO/BAR/PLIC/ploc" '(list c fname-))
;;   (find-code-c-d-filter-2 "/FOO/BAR/PLIC/ploc" '(ee-intern "find-%sfile" c))
;;
;; See: (find-evariable 'ee-code-c-d-pairs)
;;      (find-elnode "Association Lists")

(defun ee-filter (f list)
  "Return the elements of LIST for which (F elt) is true.
Actually return a list of `(F elt)'s."
  (ee-remove-nils (mapcar f list)))

(defun ee-code-c-d-filter-1 (code)
  "Run CODE on each `c-d' of `ee-code-c-d-pairs' and return a list of results.
This is a simpler version of `ee-code-c-d-filter-2', used only for debugging."
  (ee-filter
   (lambda (c-d)
     (let* ((c (car c-d))
	    (d (cadr c-d))
	    (ed (ee-expand d)))
       (eval code)))
   ee-code-c-d-pairs))

(defun ee-code-c-d-filter-2 (fname code)
  "Run CODE on each `c-d' of `ee-code-c-d-pairs' and return a list of results.
Only eval CODE when (ee-expand D) is a prefix of (ee-expand FNAME).
CODE is evaluated inside a `let' that sets the variables `c',
`d', `ed', `efname', and `fname-'. See the source for their meanings."
  (let ((efname (ee-expand fname)))
    (ee-filter
     (lambda (c-d)
       (let* ((c (car c-d))
	      (d (cadr c-d))
	      (ed (ee-expand d)))
	 (if (ee-prefixp ed efname)
	     (let ((fname- (ee-remove-prefix ed efname)))
	       (eval code)))))
     ee-code-c-d-pairs)))

(defun find-code-c-d-filter-1 (code)
  "For debugging. See the comments in the source."
  (find-epp (ee-code-c-d-filter-1 code)))

(defun find-code-c-d-filter-2 (fname code)
  "For debugging. See the comments in the source."
  (find-epp (ee-code-c-d-filter-2 fname code)))

;; ---

(defun ee-prefixp (prefix str)
  "Return t if STR begins with PREFIX."
  (and (<= (length prefix) (length str))
       (equal prefix (substring str 0 (length prefix)))))

(defun ee-remove-prefix (prefix str)
  "Example: (ee-remove-prefix \"ab\" \"abcde\") --> \"cde\""
  (and (<= (length prefix) (length str))
       (equal prefix (substring str 0 (length prefix)))
       (substring str (length prefix))))

(defun ee-replace-prefix0 (prefix newprefix fname)
  (if (ee-prefixp prefix fname)
      (concat newprefix (ee-remove-prefix prefix fname))))

(defun ee-replace-prefix (prefix newprefix fname)
  (ee-replace-prefix0 (ee-expand prefix) newprefix (ee-expand fname)))

(defun ee-intern (fmt &rest args)
  "The result of (format FMT ARGS), converted to a symbol"
  (intern (apply 'format fmt args)))

;; «ee-find-xxxfile-sexps»  (to ".ee-find-xxxfile-sexps")
(defun ee-find-xxxfile-sexps (fname)
  "For each (C D) in ee-code-c-d-pairs test if D is a prefix of FNAME;
when this is true remove the prefix D from FNAME, and put the sexp
(find-Cfile \"FNAME-\") in the list of results. Return that list."
  (ee-code-c-d-filter-2
   fname
   '(list (ee-intern "find-%sfile" c) fname-)))





;;;   __ _           _        __ _ _            _ _       _        
;;;  / _(_)_ __   __| |      / _(_) | ___      | (_)_ __ | | _____ 
;;; | |_| | '_ \ / _` |_____| |_| | |/ _ \_____| | | '_ \| |/ / __|
;;; |  _| | | | | (_| |_____|  _| | |  __/_____| | | | | |   <\__ \
;;; |_| |_|_| |_|\__,_|     |_| |_|_|\___|     |_|_|_| |_|_|\_\___/
;;;                                                                
;; «find-file-links» (to ".find-file-links")
;; Skel: (find-find-links-links-old "f" "file" "fname")
;; A test: (find-file-links "~/tmp/foo")

;; Moved to eev-mode.el:
;; (define-key eev-mode-map "\M-hf" 'find-file-links)

(defun ee-if-prefixp (d newd fname code)
  "An internal function used by `find-file-links'."
  (let* ((ed     (ee-expand d))
	 (efname (ee-expand fname)))
    (if (ee-prefixp ed efname)
	(let* ((fname- (ee-remove-prefix ed efname))
	       (fname+ (concat newd fname-)))
	  (eval code)))))

(defun ee-find-file-links (&optional fname)
  (setq fname (or fname (or (buffer-file-name) default-directory)))
  `(,(ee-if-prefixp "~/" "~/" fname '`(find-fline ,fname+))
    ,(ee-if-prefixp "$S/http/"   "http://" fname '(ee-H fname+))
    ,(ee-if-prefixp "$S/https/" "https://" fname '(ee-H fname+))
    ,(ee-if-prefixp "$S/shttp/" "shttp://" fname '(ee-H fname+))
    ""
    (find-file ,fname)		; non-refinable
    (find-fline ,fname)		; refinable
    ,@(ee-find-xxxfile-sexps (ee-expand fname))
    ;;
    ,@(ee-find-file-extra-links fname) ; customizable by the user
    ))

(defun ee-find-file-extra-links (fname) ()) ; customize this

(defun find-file-links (fname &rest pos-spec-list)
  (interactive (list (or (buffer-file-name) default-directory)))
  (apply 'find-elinks
	 `((find-file-links ,fname ,@pos-spec-list)
	   ,@(ee-find-file-links fname)
	   )
	 pos-spec-list))



;;;   __ _           _                                  _ _       _        
;;;  / _(_)_ __   __| |       __ _ _ __ ___ _ __       | (_)_ __ | | _____ 
;;; | |_| | '_ \ / _` |_____ / _` | '__/ _ \ '_ \ _____| | | '_ \| |/ / __|
;;; |  _| | | | | (_| |_____| (_| | | |  __/ |_) |_____| | | | | |   <\__ \
;;; |_| |_|_| |_|\__,_|      \__, |_|  \___| .__/      |_|_|_| |_|_|\_\___/
;;;                          |___/         |_|                             
;;
;; «find-grep-links» (to ".find-grep-links")
;; Skel: (find-find-links-links-new "grep" "" "")
;;
;; The functions `find-grep-links' and `ee-find-grep-links' are
;; similar to `find-file-links', described here,
;;
;;   (find-eev-quick-intro "10.1. Generating short hyperlinks to files")
;;
;; in the sense that they generate short hyperlinks to the default
;; directory and to its parent directories, but 1) they generate
;; `find-xxxgrep' links instead of `find-xxxfile' links, and 2) they
;; combine them with most recent elements in `grep-history'.
;;
;; Here's a micro-tutorial. Run `M-x grep', and complete the grep
;; command with a string to search for and a list of files, like this,
;;
;;   grep --color -nH --null -e
;;   -->
;;   grep --color -nH --null -e Punch *.el
;;
;; and hit RET. You should get a buffer named "*grep*" with the
;; results. If you type `M-h M-h' there the function `find-here-links'
;; will run `ee-find-grep-links' to generate hyperlinks to the result
;; of running that grep command, and one of those hyperlinks will be:
;;
;;   (find-eevgrep "grep --color -nH --null -e Punch *.el")
;;
(defun find-grep-links (&rest pos-spec-list)
"Visit a temporary buffer containing `find-xxxgrep' sexps."
  (interactive)
  (apply
   'find-elinks
   `((find-grep-links ,@pos-spec-list)
     ;; Convention: the first sexp always regenerates the buffer.
     (find-efunction 'find-grep-links)
     ""
     ,@(ee-find-grep-links)
     )
   pos-spec-list))

(defun ee-find-grep-links ()
  "An internal function used by `find-grep-links'."
  (append
   (ee-find-grep-links0
    (ee-find-grep-functions default-directory)
    (ee-find-grep-commands))
   (ee-find-grep-links1)))

;; Low-level functions used by `ee-find-grep-links'.
;; Tests:
;;   (find-elinks (ee-find-grep-links))
;;   (ee-find-grep-links)
;;   (ee-find-grep-links0 '(find-Agrep find-Bgrep) '("grep AA *" "grep BB *"))
;;     (ee-find-grep-functions ee-emacs-lisp-directory)
;;     (ee-find-grep-functions ee-eev-source-directory)
;;     (ee-find-grep-commands)
;;   (find-elinks (ee-find-grep-links1))
;;
(defun ee-find-grep-links0 (find-xxxgreps grep-commands)
  "An internal function used by `find-grep-links'."
  (let (result)
    (dolist (head find-xxxgreps)
      (dolist (command grep-commands)
	(setq result (cons `(,head ,command) result))))
    (nreverse result)))

(defun ee-find-grep-commands ()
  "An internal function used by `find-grep-links'."
  (cons "grep -nH -e _ *" (ee-first-n-elements 4 grep-history)))

(defun ee-first-n-elements (n list)
  "Example: (ee-first-n-elements 2 '(a b c d e f))   ==> (a b)"
  (if (and (> n 0) list)
      (cons (car list)
	    (ee-first-n-elements (1- n) (cdr list)))))

(defun ee-find-grep-functions (dir)
  "An internal function used by `find-grep-links'."
  (ee-code-c-d-filter-2 dir '(ee-intern "find-%sgrep" c)))

;; See:
;; https://lists.gnu.org/archive/html/help-gnu-emacs/2021-02/msg00778.html
;;
(defun ee-find-grep-links1 ()
  "An internal function used by `find-grep-links'."
  (let ((dir (ee-shorten-file-name default-directory)))
    (list (ee-template0 "
# (ee-find-grep {(ee-S dir)} {(ee-S (car grep-history))})

(let ((default-directory {(ee-S dir)}))
  (grep {(ee-S (car grep-history))})
  )
"))))







;;;            _  __ _ _ _                                     
;;;  _ __   __| |/ _| (_) | _____       _ __   __ _  __ _  ___ 
;;; | '_ \ / _` | |_| | | |/ / _ \_____| '_ \ / _` |/ _` |/ _ \
;;; | |_) | (_| |  _| | |   <  __/_____| |_) | (_| | (_| |  __/
;;; | .__/ \__,_|_| |_|_|_|\_\___|     | .__/ \__,_|\__, |\___|
;;; |_|                                |_|          |___/      
;;
;; «find-pdflike-page-links» (to ".find-pdflike-page-links")
;; The function `find-pdflike-page-links' is called from
;; `find-pdf-links' (`M-h M-p') when you call it in a buffer that is
;; not in dired mode. See:
;;   (to "find-pdf-links")
;;   (find-pdf-like-intro "10. Generating a pair with the page number")
;;   (find-pdf-like-intro "11. How `M-h M-p' guesses everything")
;;
;; Skel: (find-find-links-links-new "pdflike-page" "page bufname offset" "")
;;
(defun find-pdflike-page-links (&optional page bufname offset &rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks to a pdf-like document.
See: (find-pdf-like-intro)
     (find-pdf-like-intro \"refining hyperlinks to pages\")"
  (interactive)
  (setq page    (or page (ee-current-page)))
  (setq bufname (or bufname (buffer-name)))
  (setq offset  (or offset ee-page-offset))
  (apply
   'find-elinks
   `((find-pdflike-page-links ,page ,bufname ,offset ,@pos-spec-list)
     ;; Convention: the first sexp always regenerates the buffer.
     (find-pdf-like-intro "10. Generating a pair with the page number")
     (find-pdf-like-intro "11. How `M-h M-p' guesses everything")
     ;; (find-efunction 'find-pdflike-page-links)
     ""
     ,@(ee-pdflike-page-links page bufname offset)
     )
   pos-spec-list))

(defun ee-pdflike-page-links (&optional page bufname offset)
  (setq page    (or page (ee-current-page)))
  (setq bufname (or bufname (buffer-name)))
  (setq offset  (or offset ee-page-offset))
  (let* ((c          ee-page-c)
	 (fname      ee-page-fname)
	 (find-cpage (ee-intern "find-%spage" c))
	 (find-ctext (ee-intern "find-%stext" c))
	 (kill       (or (ee-region-or-last-kill) ""))
	 (page-      (- page offset))
	 )
    `((,find-cpage ,page)
      (,find-ctext ,page)
      (,find-cpage (+ ,offset ,page-))
      (,find-ctext (+ ,offset ,page-))
      ""
      (,find-cpage ,page ,kill)
      (,find-ctext ,page ,kill)
      (,find-cpage (+ ,offset ,page-) ,kill)
      (,find-ctext (+ ,offset ,page-) ,kill)
      ""
      (code-pdf-page ,c ,fname)
      (code-pdf-text ,c ,fname ,offset)
      ,(ee-HS bufname)
      )))

;; These are internal functions used by `find-pdflike-page-links' and
;; `ee-pdflike-page-links'.

;; Based on: (find-efunction 'count-lines)
;;
(defun ee-count-formfeeds (start end)
  (save-excursion
    (save-restriction
      (narrow-to-region start end)
      (goto-char (point-min))
      (save-match-data
	(let ((done 0))
	  (while (re-search-forward "[\f]" nil t 1)
	    (setq done (+ 1 done)))
	  done)))))

(defun ee-current-page ()
  (+ 1 (ee-count-formfeeds (point-min) (point))))

(defun ee-last-kill ()
  (if (stringp (car kill-ring))
    (ee-no-properties (car kill-ring))))

(defun ee-region ()
  (if (region-active-p)
      (buffer-substring-no-properties (point) (mark))))

(defun ee-region-or-last-kill ()
  (or (ee-region) (ee-last-kill)))








;;;  _                           _ _       _                         __ _      
;;; | |__  _   _ _ __   ___ _ __| (_)_ __ | | __     _ __  _ __ ___ / _(_)_  __
;;; | '_ \| | | | '_ \ / _ \ '__| | | '_ \| |/ /____| '_ \| '__/ _ \ |_| \ \/ /
;;; | | | | |_| | |_) |  __/ |  | | | | | |   <_____| |_) | | |  __/  _| |>  < 
;;; |_| |_|\__, | .__/ \___|_|  |_|_|_| |_|_|\_\    | .__/|_|  \___|_| |_/_/\_\
;;;        |___/|_|                                 |_|                        
;;
;; «ee-hyperlink-prefix» (to ".ee-hyperlink-prefix")
;; Tests: (ee-hyperlink-prefix)
;;         ee-hyperlink-prefix

(defun ee-hyperlink-prefix ()
  "A lispish interface for customizing the variable `ee-hyperlink-prefix'.
See the comments in the source code."
  (interactive)
  (find-elinks
   `((ee-hyperlink-prefix)
     ;; Convention: the first sexp always regenerates the buffer.
     (setq ee-hyperlink-prefix ,ee-hyperlink-prefix) ; current value
     ""
     (setq ee-hyperlink-prefix "# ")	; other common values
     (setq ee-hyperlink-prefix ";; ")
     (setq ee-hyperlink-prefix "-- ")
     (setq ee-hyperlink-prefix "// ")
     (setq ee-hyperlink-prefix "% ")
     )))




;;;   __ _           _             __                      _ _       _        
;;;  / _(_)_ __   __| |       ___ / _| __ _  ___ ___      | (_)_ __ | | _____ 
;;; | |_| | '_ \ / _` |_____ / _ \ |_ / _` |/ __/ _ \_____| | | '_ \| |/ / __|
;;; |  _| | | | | (_| |_____|  __/  _| (_| | (_|  __/_____| | | | | |   <\__ \
;;; |_| |_|_| |_|\__,_|      \___|_|  \__,_|\___\___|     |_|_|_| |_|_|\_\___/
;;;                                                                           
;; «find-eface-links» (to ".find-eface-links")
;; Skel: (find-find-links-links-new "eface" "face-symbol" "fg bg")
;; Test: (find-eface-links 'eepitch-star-face)
;; Key binding:
;;   (find-eev "eev-mode.el" "eev-mode-map-set")
;;   (find-eev "eev-mode.el" "eev-mode-map-set" "M-h" "M-s" "find-eface-links")
;;
(defun find-eface-links (&optional face-symbol &rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks about FACE-SYMBOL.
When called interactively generate hyperlinks about the face at point.
See the documentation for `ee-face-at-point' to understand what that
means."
  (interactive (list (ee-face-at-point current-prefix-arg)))
  (setq face-symbol (or face-symbol "{face-symbol}"))
  (let* ((fg (face-foreground face-symbol))
         (bg (face-background face-symbol)))
    (apply
     'find-elinks
     `((find-eface-links ',face-symbol ,@pos-spec-list)
       ;; Convention: the first sexp always regenerates the buffer.
       (find-efunction 'find-eface-links)
       ""
       (find-enode "Faces")
       (find-elnode "Faces")
       ""
       (find-efacedescr     ',face-symbol)
       (find-efaces         ',face-symbol)
       (find-eface          ',face-symbol)
       (describe-face       ',face-symbol)
       (customize-face      ',face-symbol)
       (set-face-foreground ',face-symbol ,fg)
       (set-face-background ',face-symbol ,bg)
       ""
       (find-ecolor-links ,fg)
       (find-ecolor-links ,bg)
       (find-ecolors)
       ""
       (face-all-attributes ',face-symbol (selected-frame))
       (find-eppp (face-all-attributes ',face-symbol (selected-frame)))
       ""
       (face-id ',face-symbol)
       (find-eppp (mapcar (lambda (face) (cons (face-id face) face)) (face-list)))
       )
     pos-spec-list)))




(defun ee-face-at-point (&optional arg)
  "Return the face at point as a symbol; ARG determines what that means.
When ARG is nil this returns (face-at-point); in a font-locked
buffer this is typically something like `font-lock-comment-face'.
When ARG is 1 and the character at point is a glyph this returns
the face of that glyph - something like `eepitch-star-face'.
When ARG is anything else this returns (symbol-at-point); so if
the point is on the name of face this returns the face with that
name.

See the source code for `find-eface-links' and `find-efacedescr'
to understand how this is used. ARG is usually
`current-prefix-arg', so the default for it is to be nil."
  (if (eq arg nil)
      (or (face-at-point) 'default)
    (if (eq arg 1)
	(ee-face-of-glyph (char-after (point)))
      (symbol-at-point))))
  
;; Tests: (ee-face-of-glyph ?*)
;;        (ee-face-of-glyph ?@)
(defun ee-face-of-glyph (char)
  "An internal function used by `ee-face-at-point'."
  (let* ((display-table standard-display-table)
         (disp-vector  (and display-table (aref display-table char)))
         (disp-vector0 (and disp-vector   (aref disp-vector 0)))
	 (face         (and disp-vector0  (glyph-face disp-vector0))))
    face))




;;;   __ _           _                     _                _ _       _        
;;;  / _(_)_ __   __| |      ___  ___ ___ | | ___  _ __    | (_)_ __ | | _____ 
;;; | |_| | '_ \ / _` |____ / _ \/ __/ _ \| |/ _ \| '__|___| | | '_ \| |/ / __|
;;; |  _| | | | | (_| |____|  __/ (_| (_) | | (_) | | |____| | | | | |   <\__ \
;;; |_| |_|_| |_|\__,_|     \___|\___\___/|_|\___/|_|      |_|_|_| |_|_|\_\___/
;;;                                                                              
;; «find-ecolor-links» (to ".find-ecolor-links")
;; Skel: (find-find-links-links-new "color" "initialcolor" "")
;; Tests: (find-ecolor-links)
;;        (find-ecolor-links "sienna")
;;        (find-ecolor-links "#123456")
;;        (ee-color-values   "sienna")
;;        (ee-color-values   "#123456")
;;        (ee-color-choose-tk)
;;        (ee-color-choose-tk "sienna")
;;        (ee-color-choose-tk "#123456")
;;
(defun find-ecolor-links (&optional initialcolor &rest pos-spec-list)
  "Visit a temporary buffer containing hyperlinks for the color INITIALCOLOR."
  (interactive)
  (setq initialcolor (or initialcolor "#123456"))
  (apply
   'find-elinks
   `((find-ecolor-links ,initialcolor ,@pos-spec-list)
     (find-ecolor-links (read-color))
     ;; Convention: the first sexp always regenerates the buffer.
     (find-efunction 'find-ecolor-links)
     ""
     (find-enode "Colors")
     (find-elnode "Color Names")
     ""
     (find-ecolor-links (ee-color-choose-tk ,(or initialcolor "gray")))
     (find-ecolor-links ,(or initialcolor "gray"))
     (find-ecolors)
     (find-ecolors ,initialcolor)
     ,`(insert (propertize " Sample " 'face '(:foreground ,initialcolor)))
     ,`(insert (propertize " Sample " 'face '(:background ,initialcolor)))
     ,`(ee-color-values ,initialcolor)
     (kill-new ,initialcolor)
     (kill-new ,(ee-color-values initialcolor))
     )
   pos-spec-list))

(defun ee-color-values (color)
  "Return the #RRGGBB representation for COLOR."
  (apply 'format "#%02x%02x%02x"
	 (mapcar (lambda (c) (lsh c -8)) (color-values color))))

(defun ee-color-choose-tk (&optional initialcolor)
  "Call Tcl/Tk to choose a color similar to INITIALCOLOR.
The original version of this function used the directory $EEVTMPDIR.
This version uses the file /tmp/ee.tcl instead of $EEVTMPDIR/ee.tcl.
Don't use this in multi-user machines."
  (let ((ee-file-tcl "/tmp/ee.tcl")
	(tclcode (format "puts [tk_chooseColor -initialcolor %s]; exit\n"
			 (or initialcolor "gray"))))
    ;; (ee-write-string tclcode ee-file-tcl)
    (write-region tclcode nil ee-file-tcl)
    (find-sh0 (format "wish %s" ee-file-tcl))))



;;;   __ _           _                             _                          _ 
;;;  / _(_)_ __   __| |       ___ _ __   __ _  ___| | ____ _  __ _  ___      | |
;;; | |_| | '_ \ / _` |_____ / _ \ '_ \ / _` |/ __| |/ / _` |/ _` |/ _ \_____| |
;;; |  _| | | | | (_| |_____|  __/ |_) | (_| | (__|   < (_| | (_| |  __/_____| |
;;; |_| |_|_| |_|\__,_|      \___| .__/ \__,_|\___|_|\_\__,_|\__, |\___|     |_|
;;;                              |_|                         |___/              
;;
;; «find-epackage-links»  (to ".find-epackage-links")
;; Skel: (find-find-links-links-new "epackage" "pkg c d" "")
;; Test: (find-epackage-links 'lua-mode)
;;       (find-epackage-links 'tetris)
;;       (find-epackage-links 'foo)
;;
(defun find-epackage-links (&optional pkg c d &rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks for an Emacs package.
PKG must be a symbol; C and D are arguments for `code-c-d'.
If D is t then try to use `ee-package-dir' to get the directory."
  (interactive (list (symbol-at-point)))
  (setq pkg (or pkg '{pkg}))
  (setq c (or c (ee-find-epackage-pkg-to-c pkg)))
  (setq d (cond ((eq d t) (ee-package-dir pkg))
		((eq d nil) "{d}")
		(t d)))
  (apply
   'find-elinks
   `((find-epackage-links ,(ee-add-quote pkg) ,c ,d ,@pos-spec-list)
     (find-epackage-links ,(ee-add-quote pkg) ,c t ,@pos-spec-list)
     ;; Convention: the first sexp always regenerates the buffer.
     (find-efunction 'find-epackage-links)
     (find-elpafile "")
     ""
     ,(ee-find-epackage-links0 pkg c d)
     )
   pos-spec-list))

(defun ee-find-epackage-links0 (pkg c d)
  "This is an internal function used by `find-epackage-links'."
  (setq c (or c (ee-find-epackage-pkg-to-c pkg)))
  (let* ((spkg (format "\n  %s " pkg))
	 (findelpafiles0 (ee-package-findelpafiles pkg))
	 (findelpafiles1 (reverse findelpafiles0))
	 (findelpafiles (mapconcat 'ee-HS findelpafiles1 "\n"))
	 )
    (ee-template0 "\
# (find-epackages '{pkg})
# (find-epackage-links '{pkg})
# (find-epackages '{pkg})
# (find-epackage '{pkg})
{findelpafiles}

# (ee-package-dir '{pkg})
# (find-epp     (ee-package-desc '{pkg}))
# (find-estruct (ee-package-desc '{pkg}))

# (package-initialize)
# (package-refresh-contents)
# (package-install '{pkg})
# (find-epackage   '{pkg})
# (ee-package-dir  '{pkg})
# (find-fline    (ee-package-dir '{pkg}))
# (find-epp     (ee-package-desc '{pkg}))
# (find-estruct (ee-package-desc '{pkg}))
# (kill-new      (ee-package-url '{pkg}))
# (insert \"\\n# \" (ee-package-url '{pkg}))
# (package-delete (ee-package-desc '{pkg}))
# (package-delete (package-get-descriptor '{pkg}))

# (find-epackage-links '{pkg} \"{c}\" t)
# (find-epackages      '{pkg})
# (find-epackage       '{pkg})
# (code-c-d \"{c}\" \"{d}\")
# (find-{c}file \"\")

# (find-melpa-links)

# http://elpa.gnu.org/packages/{pkg}.html
# http://elpa.nongnu.org/nongnu/{pkg}.html
# http://melpa.org/#/{pkg}
")))

(defun ee-find-epackage-pkg-to-c (pkg)
  "Convert PKG (a symbol) to a \"c\" for a `code-c-d'."
  (replace-regexp-in-string "[-]" "" (symbol-name pkg)))



;; Tests: (find-fline ee-elpadir)
;;        (find-fline "~/.emacs.d/elpa/" "lua-mode-")
;;        (ee-file-expand-wildcards-slash "~/.emacs.d/elpa/lua-mode-*")
;;        (ee-package-findelpafiles "lua-mode")
;;
(defun ee-package-findelpafiles (pkgname)
  "Convert a PKGNAME to a list of `(find-elpafile ...)' sexps."
  (let* ((pattern (format "%s%s-*" ee-elpadir pkgname))
	 (fnames (ee-file-expand-wildcards-slash pattern)))
    (mapcar (lambda (s) (list 'find-elpafile s)) fnames)))

(defun ee-file-expand-wildcards-slash (pattern)
"Like `file-expand-wildcards' but with `ee-file-name-nondirectory-slash' & sort."
  (let* ((fnames0 (file-expand-wildcards pattern))
	 (fnames1 (mapcar 'ee-file-name-nondirectory-slash fnames0))
	 (fnames2 (sort fnames1 'string<)))
    fnames2))

(defun ee-file-name-nondirectory-slash (fname)
"Like `file-name-nondirectory', but appends a / to FNAME if it is a directory."
  (concat (file-name-nondirectory fname)
	  (if (file-directory-p fname) "/" "")))




;; «ee-package-desc»  (to ".ee-package-desc")
;; «ee-package-dir»  (to ".ee-package-dir")
;; This function converts a package name (a symbol) into the directory
;; in which that package was installed (or nil), using functions from
;; "package.el".
;;
;; Tests: (require 'package)
;;        (package-initialize)
;;        (ee-package-dir 'lua-mode)
;;        (ee-package-dir 'tetris)
;;        (ee-package-dir 'foo)
;;        (ee-package-desc 'lua-mode)
;;        (ee-package-desc 'tetris)
;;        (ee-package-desc 'foo)
;;
;; WARNING: the function `ee-package-dir' and its dependency
;; `ee-package-desc' use several functions from "package.el", and I
;; don't understand package.el well enough!
;;
;; UPDATE (2024oct20): apparently we can use `package-get-descriptor'
;; instead of `ee-package-desc'...
;;
;; See: (find-efile "emacs-lisp/package.el" "(cl-defstruct (package-desc")
;;      (find-efunction 'describe-package-1)
;;      (find-efunction 'describe-package-1 "(let* ((desc ")
;;
(defun ee-package-desc (pkg)
"An internal function used by `ee-package-dir'.
Convert PKG - a symbol - to a package-desc structure (or to nil)."
  (or (if (package-desc-p pkg) pkg)
      (cadr (assq pkg package-alist))
      (let ((built-in (assq pkg package--builtins)))
	(if built-in
	    (package--from-builtin built-in)
	  (cadr (assq pkg package-archive-contents))))))

(defun ee-package-dir (pkg)
"Convert the name of the package PKG to the directory where it was installed."
  (let* ((desc (ee-package-desc pkg))
	 (dir (and desc (package-desc-dir desc))))
    (if (stringp dir)
	(replace-regexp-in-string
	 "\\([^/]\\)$" "\\1/"
	 (ee-shorten-file-name dir)))))

(defun ee-package-url (pkg)
  "Convert the name of the package PKG to its website."
  (let ((desc (ee-package-desc pkg)))
    (and desc (alist-get :url (package-desc-extras desc)))))







;;;   __ _           _                      _   _              
;;;  / _(_)_ __   __| |       ___  ___  ___| |_| | _____ _   _ 
;;; | |_| | '_ \ / _` |_____ / _ \/ __|/ _ \ __| |/ / _ \ | | |
;;; |  _| | | | | (_| |_____|  __/\__ \  __/ |_|   <  __/ |_| |
;;; |_| |_|_| |_|\__,_|      \___||___/\___|\__|_|\_\___|\__, |
;;;                                                      |___/ 
;;
;; «find-esetkey-links»  (to ".find-esetkey-links")
;; Skel: (find-find-links-links-new "esetkey" "key command" "longkey")
;; Test: (find-esetkey-links (kbd "M-o") 'other-window)
;;  See: (find-eevtemplhsubs "14:23" "4. `find-esetkey-links'")
;;       (find-eevtemplvideo "14:23" "4. `find-esetkey-links'")
;;       (find-eevtemplhsubs "14:46" "If we just run (find-esetkey-links)")
;;       (find-eevtemplvideo "14:46" "If we just run (find-esetkey-links)")
;;
(defun find-esetkey-links (&optional key command &rest pos-spec-list)
  "Visit a temporary buffer containing sexps for setting a key."
  (interactive
   (let* ((menu-prompting nil)
          (key (read-key-sequence "Set key: " nil t))
	  (longkey (format-kbd-macro key))
	  (command (ee-read-command)))
     (list key command)))
  (setq key (or key (kbd "M-o")))
  (setq command (or command 'other-window))
  (let* ((longkey (format-kbd-macro key)))
    (apply
     'find-elinks-elisp
     `((find-esetkey-links (kbd ,longkey) ',command ,@pos-spec-list)
       (find-esetkey-links (kbd "M-o") 'other-window ,@pos-spec-list)
       ;; Convention: the first sexp always regenerates the buffer.
       (find-ekeydescr (kbd ,longkey))
       (find-efunctiondescr ',command)
       (find-efunction 'find-esetkey-links)
       ""
       (find-enode "Rebinding" "M-x global-set-key")
       (find-elnode "Changing Key Bindings" "Function: define-key")
       (find-efunctiondescr 'global-set-key)
       (find-efunctiondescr 'global-unset-key)
       (find-efunctiondescr 'local-set-key)
       (find-efunctiondescr 'local-unset-key)
       (find-efunctiondescr 'define-key)
       (find-efunctiondescr 'kbd)
       (find-enode "Misc Help" "describe-bindings")
       (eek "C-h b  ;; describe-bindings")
       ""
       ,(ee-template0 "\
(global-set-key   (kbd \"{longkey}\") '{command})
(global-unset-key (kbd \"{longkey}\"))

(local-set-key    (kbd \"{longkey}\") '{command})
(local-unset-key  (kbd \"{longkey}\"))

;; (find-ekeymapdescr eev-mode-map)
(define-key eev-mode-map (kbd \"{longkey}\") '{command})
(define-key eev-mode-map (kbd \"{longkey}\") nil)
")
       )
     pos-spec-list)))

(defun ee-read-command ()
  "An internal function used by `find-esetkey-links'."
  (let* ((cmd-at-pt (ee-command-at-point))
	 (prompt (if cmd-at-pt
		     (format "Command (default %s): " cmd-at-pt)
		   "Command: ")))
    (read-command prompt cmd-at-pt)))

(defun ee-command-at-point ()
  "An internal function used by `find-esetkey-links'."
  (let ((symbol (symbol-at-point)))
    (if (commandp symbol) symbol)))




;;;   __ _           _                                      
;;;  / _(_)_ __   __| |      _ __ ___   __ _  ___ _ __ ___  
;;; | |_| | '_ \ / _` |_____| '_ ` _ \ / _` |/ __| '__/ _ \ 
;;; |  _| | | | | (_| |_____| | | | | | (_| | (__| | | (_) |
;;; |_| |_|_| |_|\__,_|     |_| |_| |_|\__,_|\___|_|  \___/ 
;;;                                                         
;; «find-ekbmacro-links» (to ".find-ekbmacro-links")
;; Skel: (find-find-links-links-new "ekbmacro" "" "")
;; Test: (find-ekbmacro-links)
;;
;; The standard way to save and edit keyboard macros is with
;; the key sequences explained here,
;;   (find-enode "Keyboard Macros")
;;   (find-enode "Save Keyboard Macro")
;;   (find-enode "Edit Keyboard Macro")
;; but I prefer to use this function. It is bound to `M-h M':
;;   (find-eev "eev-mode.el" "eev-mode-map-set")
;;   (find-eev "eev-mode.el" "eev-mode-map-set" "\\M-hM")
;;
(defun find-ekbmacro-links (&rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks on keyboard macros."
  (interactive)
  (apply
   'find-elinks
   `((find-ekbmacro-links ,@pos-spec-list)
     ;; Convention: the first sexp always regenerates the buffer.
     (find-efunction 'find-ekbmacro-links)
     ""
     (format-kbd-macro last-kbd-macro)
     (setq last-kbd-macro (kbd ,(format-kbd-macro last-kbd-macro)))
     (eek ,(format-kbd-macro last-kbd-macro))
     ""
     (find-enode "Keyboard Macros")
     (find-enode "Save Keyboard Macro")
     (find-enode "Edit Keyboard Macro")
     (find-efunctiondescr 'edmacro-mode)
     (eek "M-h M-k C-x C-k C-e  ;; kmacro-edit-macro-repeat")
     (eek "        C-x C-k C-e  ;; kmacro-edit-macro-repeat")
     (eek "M-h M-k C-x C-k l    ;; kmacro-edit-lossage")
     (eek "        C-x C-k l    ;; kmacro-edit-lossage")
     )
   pos-spec-list))



;; «find-emajormode-links»  (to ".find-emajormode-links")
;; Skel: (find-find-links-links-new "emajormode" "mode" "")
;; Test: (find-emajormode-links major-mode)
;;
(defun find-emajormode-links (&optional mode &rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks about the major mode."
  (interactive (list major-mode))
  (setq mode (or mode "{mode}"))
  (if (symbolp mode) (setq mode (symbol-name mode)))
  (let ((ee-buffer-name (or ee-buffer-name "*find-emajormode-links*")))
    (apply
     'find-elinks
     `((find-emajormode-links ,mode ,@pos-spec-list)
       ;; Convention: the first sexp always regenerates the buffer.
       (find-efunction 'find-emajormode-links)
       ""
       ,(ee-template0 "\
# (find-efunction-links '{mode})
# (find-efunctiondescr  '{mode})
# (find-efunction       '{mode})
# (find-hvariable '{mode}-map)
#
# (find-estring (documentation '{mode}))
# (find-estring (documentation '{mode} t))
")
       )
     pos-spec-list)))




;; «find-eminormodes-links»  (to ".find-eminormodes-links")
;; Skel: (find-find-links-links-new "eminormodes" "" "")
;; Test: (find-eminormodes-links)
;;
(defun find-eminormodes-links (&rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks about minor modes."
  (interactive)
  (apply
   'find-elinks
   `((find-eminormodes-links ,@pos-spec-list)
     ;; Convention: the first sexp always regenerates the buffer.
     (find-efunction 'find-eminormodes-links)
     ""
     (find-elnode "Minor Modes")
     (find-eppp minor-mode-list)
     (find-eppp local-minor-modes)
     (find-eppp global-minor-modes)
     ""
     (require 'dash)
     (find-eppp (--filter (not (boundp it)) minor-mode-list))
     (find-eppp (--filter (and (boundp it) (symbol-value it)) minor-mode-list))
     (find-elinks (--map `(find-efunctiondescr ',it) minor-mode-list))
     (find-elinks (--map `(find-efunction ',it) minor-mode-list))
     (find-node "(dash)")
     (find-node "(dash)Index")
     ""
     (find-elnode "Active Keymaps")
     (find-elnode "Standard Keymaps")
     (find-elnode "Searching Keymaps")
     (find-elnode "Controlling Active Maps")
     (find-elnode "Controlling Active Maps" "current-minor-mode-maps")
     (find-elnode "Controlling Active Maps" "minor-mode-key-binding")
     (find-eppp (current-minor-mode-maps))
     (find-eppp minor-mode-map-alist)
     (find-eppp (mapcar 'car minor-mode-map-alist))
     (find-eppp (-map 'car minor-mode-map-alist))
     ""
     (find-efunctiondescr 'define-minor-mode)
     (find-efunction 'define-minor-mode)
     (find-eppm '(define-minor-mode ee-FOO-mode "no doc" :keymap ee-FOO-map))
     )
   pos-spec-list))




;; «find-emodeline-links»  (to ".find-emodeline-links")
;; Skel: (find-find-links-links-new "emodeline" "" "")
;; Test: (find-emodeline-links)
;;
(defun find-emodeline-links (&rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks about the mode line.
You can use this to understand how the mode line works."
  (interactive)
  (apply
   'find-elinks
   `((find-emodeline-links ,@pos-spec-list)
     ;; Convention: the first sexp always regenerates the buffer.
     (find-efunction 'find-emodeline-links)
     ""
     (find-elnode "Mode Line Format")
     (find-elnode "Emulating Mode Line")
     (find-evardescr 'mode-line-format)
     (find-epp mode-line-format)
     (find-epp (format-mode-line mode-line-format))
     (find-epp (format-mode-line mode-line-format 'header-line))
     (ee-no-properties (format-mode-line mode-line-format))
     (insert "\n# " (format-mode-line mode-line-format))
     ""
     (find-estring (format-mode-line mode-line-format))
     (find-estring (format-mode-line header-line-format))
     ""
     (find-evardescr 'mode-line-front-space)
     (find-evardescr 'mode-line-mule-info)
     (find-evardescr 'mode-line-client)
     (find-evardescr 'mode-line-modified)
     (find-evardescr 'mode-line-remote)
     (find-evardescr 'mode-line-frame-identification)
     (find-evardescr 'mode-line-buffer-identification)
     (find-evardescr 'mode-line-position)
     (find-evardescr 'mode-line-modes)
     (find-evardescr 'mode-line-misc-info)
     (find-evardescr 'mode-line-end-spaces)
     )
   pos-spec-list))


;; «find-emenubar-links»  (to ".find-emenubar-links")
;; Skel: (find-find-links-links-new "emenubar" "" "")
;; Test: (find-emenubar-links)
;;
(defun find-emenubar-links (&rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks about the menu bar."
  (interactive)
  (apply
   'find-elinks
   `((find-emenubar-links ,@pos-spec-list)
     ;; Convention: the first sexp always regenerates the buffer.
     (find-efunction 'find-emenubar-links)
     ""
     ,(ee-template0 "\
# (find-enode  \"Menu Bar\")
# (find-elnode \"Menu Bar\")
# (find-efile  \"menu-bar.el\")
# (find-elnode \"Easy Menu\")
# (find-efile  \"easymenu.el\")
# (find-egrep \"grep --color=auto -nH --null -e easy-menu-define *.el */*.el\")

# (describe-bindings [(menu-bar)])
# (describe-bindings [(menu-bar) file])
# (describe-bindings [(menu-bar) edit])
# (describe-bindings [(menu-bar) options])
# (describe-bindings [(menu-bar) buffer])
# (describe-bindings [(menu-bar) tools])
# (describe-bindings [(menu-bar) help-menu])

# (find-ekeymapdescr menu-bar-file-menu)
# (find-ekeymapdescr menu-bar-edit-menu)
# (find-ekeymapdescr menu-bar-options-menu)
# (find-eapropos    \"menu-bar.*buffer\")
# (find-ekeymapdescr menu-bar-tools-menu)
# (find-ekeymapdescr menu-bar-help-menu)
# (find-evardescr   'menu-bar-final-items)

# (find-efunctiondescr 'menu-bar-mode)
# (call-interactively  'menu-bar-mode)
# (find-esetkey-links (kbd \"s-m\") 'menu-bar-mode)

# (load             \"menu-bar.el\")
# (find-efunctionpp 'menu-bar-mode)
# (menu-bar-mode 'toggle)
# (load             \"menu-bar.elc\")
")
     )
   pos-spec-list))



;;;  _                         
;;; | |    ___   ___ _   _ ___ 
;;; | |   / _ \ / __| | | / __|
;;; | |__| (_) | (__| |_| \__ \
;;; |_____\___/ \___|\__,_|___/
;;;                            
;; «find-elocus-links»  (to ".find-elocus-links")
;; Skel: (find-find-links-links-new "elocus" "key keymap" "f")
;; Test: (find-elocus-links)
;;       (find-elocus-links "M-h M-h" eev-mode-map)
;;
(defun find-elocus-links (&optional key keymap &rest pos-spec-list)
  "Visit a temporary buffer with info about KEY and its locus (a KEYMAP).
When `find-elocus-links' is called interactively it asks the user for a
key sequence, calls `help--binding-locus' to find the keymap in which
that key sequence is defined, and calls `keymap-lookup' to find the
command bound to that key sequence - and then it shows lots of
hyperlinks about all that."
  (interactive (list (ee-read-key-sequence "Key sequence: ")))
  (setq key    (or key "{key}"))
  (setq keymap (or keymap (ee-binding-locus key) "{keymap}"))
  (let* ((f (ee-keymap-lookup (symbol-value keymap) key)))
    (apply
     'find-elinks-elisp
     `((find-elocus-links ,key ',keymap ,@pos-spec-list)
       ;; Convention: the first sexp always regenerates the buffer.
       (find-efunction 'find-elocus-links)
       ""
       ,(ee-template0 "\
;; (require 'helpful)
;; (find-evariable       '{keymap})
;; (find-evardescr       '{keymap})
;; (find-ekeymapdescr     {keymap})
;; (find-ekeymapdescr     {keymap} {(ee-S key)} \"{f}\")
;; (find-hkeymap-links   '{keymap})
;; (find-hkeymap-links   '{keymap} 2 {(ee-S key)} \"{f}\")
;; (find-efunction-links '{f})
;; (find-efunctiondescr  '{f})
;; (find-efunction       '{f})
")
       )
     pos-spec-list)))


;; Internal functions used by `find-elocus-links'.
;; Tests: (ee-read-key-sequence "Key sequence: ")
;;        (ee-binding-locus "M-h M-h")
;;        (ee-keymap-lookup eev-mode-map "M-h")
;;        (ee-keymap-lookup eev-mode-map "M-h M-h")
;;        (ee-keymap-lookup eev-mode-map "M-h M-h M-h")
;;        (ee-keymap-lookup "foo"        "M-h M-h M-h")
;;        (ee-active-minor-modes)
;;   See: (find-efunction 'help--binding-locus)
;;
(defun ee-read-key-sequence (prompt)
  (format-kbd-macro (read-key-sequence-vector prompt)))

(defun ee-binding-locus (key)
  (help--binding-locus (kbd key) nil))

(defun ee-keymap-lookup (keymap key)
  (if (keymapp keymap)
      (let ((result (keymap-lookup keymap key)))
	(and (commandp result)
	     result))))






;;;   __ _           _                     _                 _ _       _        
;;;  / _(_)_ __   __| |       ___ ___   __| | _____/\__     | (_)_ __ | | _____ 
;;; | |_| | '_ \ / _` |_____ / __/ _ \ / _` |/ _ \    /_____| | | '_ \| |/ / __|
;;; |  _| | | | | (_| |_____| (_| (_) | (_| |  __/_  _\_____| | | | | |   <\__ \
;;; |_| |_|_| |_|\__,_|      \___\___/ \__,_|\___| \/       |_|_|_| |_|_|\_\___/
;;;                                                                             
;; «find-code-pdf-links»  (to ".find-code-pdf-links")
;; See: (to "find-pdf-links")
;;      (find-pdf-like-intro "9. Generating three pairs" "find-code-pdf-links")

;; See: (find-efunction 'ee-if-prefixp)
;; Tests:                     (ee-expand "~/foo")
;;                            (ee-expand "$S/https/")
;;      (ee-shorten-file-name (ee-expand "~/foo"))
;;      (ee-shorten-file-name (ee-expand "$S/https/"))
(defun ee-shorten-file-name (fname)
  "Shorten FNAME if possible to make it start with \"$S/\" or \"~/\"."
  (or (ee-if-prefixp "$S/" "$S/" fname 'fname+)
      (ee-if-prefixp "~/"  "~/"  fname 'fname+)
      fname))

(defun find-code-pdf-links (&optional fname c &rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks and `code-pdf-*'s to a PDF file."
  (interactive (list (and (eq major-mode 'dired-mode) (ee-dired-to-fname))))
  (if fname (setq fname (ee-shorten-file-name fname)))
  (setq fname (or fname "{fname}"))
  (setq c (or c "{c}"))
  (let ((dir (file-name-directory fname)))
    (apply 'find-elinks-elisp
     `((find-code-pdf-links ,fname ,c ,@pos-spec-list)
       ;; Convention: the first sexp always regenerates the buffer.
       ;;
       ;; (find-efunction 'find-code-pdf-links)
       ,(ee-template0 "\
;; See: (find-eev-quick-intro \"9.1. `code-c-d'\")
;;      (find-pdf-like-intro \"3. Hyperlinks to PDF files\")
;;      (find-pdf-like-intro \"7. Shorter hyperlinks to PDF files\")
;;      (find-pdf-like-intro \"9. Generating three pairs\")
;;      (find-pdf-like-intro \"9. Generating three pairs\" \"`M-h M-p'\")

;; (find-fline {(ee-S (file-name-directory fname))})
\(code-c-d \"{c}\" \"{(file-name-directory fname)}\")
;; (find-{c}file \"\")

;; (find-pdf-page \"{fname}\")
;; (find-pdf-text \"{fname}\")
\(code-pdf-page \"{c}\" \"{fname}\")
\(code-pdf-text \"{c}\" \"{fname}\")
;; (find-{c}page)
;; (find-{c}text)
")
       )
     pos-spec-list)))



;; «find-pdf-links»  (to ".find-pdf-links")
;; This function - usually bound to `M-h M-p' - behaves in one way
;; when invoked from dired buffers and in a totally different way when
;; invoked from other buffers. In a dired buffer it supposes that the
;; current line contains the name of a PDF, and it generates a buffer
;; whose main part is a pair `code-pdf-page'/`code-pdf-text' that lets
;; you define short hyperlinks to that PDF. See:
;;
;;   (find-pdf-like-intro "7. Shorter hyperlinks to PDF files")
;;   (find-pdf-like-intro "9. Generating three pairs" "`M-h M-p' in Dired mode")
;;
;; When the current buffer is not in dired mode this function supposes
;; that the buffer contains the "text" of a PDF, as explained here:
;;
;;   (find-pdf-like-intro "3. Hyperlinks to PDF files" "find-pdf-text")
;;
;; and it tries to generate short hyperlinks to the current page of
;; it, making lots of guesses, and often guessing everything wrong.
;; See:
;;
;;   (find-pdf-like-intro "10. Generating a pair with the page number")
;;   (find-pdf-like-intro "11. How `M-h M-p' guesses everything")
;;
(defun find-pdf-links ()
"Run either `find-code-pdf-links' or `find-pdflike-page-links'."
  (interactive)
  (if (eq major-mode 'dired-mode)
      (find-code-pdf-links (ee-dired-to-fname))
    (find-pdflike-page-links)))



;; «find-code-audiovideo-links»  (to ".find-code-audiovideo-links")
;; Skel: (find-find-links-links-new "code-audiovideo" "fname c" "dir")
;;
(defun find-code-audiovideo-links (&optional fname c &rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks for code-audiovideo."
  (interactive (list (and (eq major-mode 'dired-mode) (ee-dired-to-fname))))
  (if fname (setq fname (ee-shorten-file-name fname)))
  (setq fname (or fname "{fname}"))
  (setq c (or c "{c}"))
  (let* ((dir (file-name-directory fname)))
    (apply
     'find-elinks-elisp
     `((find-code-audiovideo-links ,fname ,c ,@pos-spec-list)
       ;; Convention: the first sexp always regenerates the buffer.
       ;; (find-efunction 'find-code-audiovideo-links)
       ;; ""
       ,(ee-template0 "\
;; See: (find-eev-quick-intro \"9.1. `code-c-d'\")
;;      (find-pdf-like-intro \"9. Generating three pairs\" \"`M-h M-p'\")
;;      (find-audiovideo-intro \"2.1. `find-code-audiovideo-links'\")

;; Links to this directory:
;; (find-fline {(ee-S (file-name-directory fname))})
\(code-c-d \"{c}\" \"{(file-name-directory fname)}\")
;; (find-{c}file \"\")

;; Links to a PDF file:
;; (find-pdf-page \"{fname}\")
;; (find-pdf-text \"{fname}\")
\(code-pdf-page \"{c}\" \"{fname}\")
\(code-pdf-text \"{c}\" \"{fname}\")
;; (find-{c}page)
;; (find-{c}text)

;; Links to an audio file:
;; (find-audio \"{fname}\")
\(code-audio \"{c}audio\" \"{fname}\")
;; (find-{c}audio)
;; (find-{c}audio \"0:00\")

;; Links to a video file:
;; (find-video \"{fname}\")
\(code-video \"{c}video\" \"{fname}\")
;; (find-{c}video)
;; (find-{c}video \"0:00\")
;;
;; (eev-avadj-mode 0)
;; (eev-avadj-mode)

;; Links to an shell-like program (for eepitch):
;; (eepitch-comint \"{c}\" \"{fname}\")

(defun eepitch-{c} () (interactive)
  (eepitch-comint \"{c}\"
     \"{fname}\"))

;; Test:

* (eepitch-{c})
* (eepitch-kill)
* (eepitch-{c})
")
       )
     pos-spec-list)))

;; Tests:
;; (find-code-audiovideo-links "~/eev-videos/three-keys-2.mp4")




;; ------------------------------------------------------------
;; Old stuff:

;; The rest of this block of comments was cut & pasted straight from
;; eev-insert.el, but most of what they say still hold...
;;
;; This is the ugliest part of eev's code. It's being rewritten. Even
;; if work on it may seem stalled, it _is_ being rewritten. In some
;; sense.
;;
;; I got tired of writing all my hyperlinks by hand, so I created
;; these functions. The "new way of creating hyperlinks" (the first
;; block of this file) adds the following key bindings to
;; eev-mode-map:
;;
;;   M-h M-k  find-ekey-links
;;   M-h M-f  find-efunction-links
;;   M-h M-v  find-evariable-links
;;   M-h M-i  find-einfo-links
;;   M-h M-d  find-debpkg-links
;;   M-h f    find-file-links
;;   M-h m    find-last-manpage-links
;;   M-h M-m  find-manpage-links
;;
;; All of them work similarly. For example: type M-h M-k RET, and
;; `find-ekey-links' will create and display a buffer called "*Elisp
;; hyperlinks*", like this:
;;
;;    _____________________________________________________________ 
;;   |(find-ekey-links "\r")                                       |
;;   |(find-elongkey-links "RET")                                  |
;;   |(find-elongkey-links "RET  ;; newline")                      |
;;   |"RET  ;; newline"                                            |
;;   |                                                             |
;;   |(where-is 'newline)                                          |
;;   |(describe-function 'newline)                                 |
;;   |(find-efunctiondescr 'newline)                               |
;;   |(find-efunction 'newline)                                    |
;;   |(find-efunctionpp 'newline)                                  |
;;   |(find-efunctiond 'newline)                                   |
;;   |(find-eCfunction 'newline)                                   |
;;   |(find-estring (documentation 'newline))                      |
;;   |(find-estring (documentation 'newline t))                    |
;;   |                                                             |
;;   |(describe-key "\r")                                          |
;;   |(describe-key-briefly "\r")                                  |
;;   |(find-ekeydescr "\r")                                        |
;;   |(Info-goto-emacs-key-command-node "\r")                      |
;;   |(Info-goto-emacs-command-node 'newline)                      |
;;   |(find-enode "Command Index" "* newline:")                    |
;;   |(find-elnode "Index" "* newline:")                           |
;;   |                                                             |
;;   |(key-description "\r")                                       |
;;   |(format-kbd-macro "\r")                                      |
;;   |(format-kbd-macro "\r" t)                                    |
;;   |(key-binding "\r")                                           |
;;   |                                                             |
;;   |                                                             |
;;   |                                                             |
;;   |--:**  *Elisp hyperlinks*   All L28     (Fundamental)--------|
;;   |_____________________________________________________________|
;;
;;
;; That is, a lot of hyperlinks pointing to interesting pieces of
;; information about the key RET and the command (`newline') that is
;; bound to it. Then you may follow these hyperlinks by evaluating the
;; sexps or you may copy them to other files by copying their text.
;;
;; [To do: explain M-h M-y. There's an example in `eesteps' format in
;; the NEWS file.]

;; See: <http://anggtwu.net/eev-current/README.html>
;; and: <http://anggtwu.net/eev-current/NEWS.html>

;; The second part of this file contains some older functions that
;; insert Elisp hyperlinks at the current buffer -- like `inn', that
;; inserts a hyperlink to the info node currently being visited -- or
;; transform text -- for example, a series of lines, each one
;; containing the name of a Debian package -- into hyperliks.

'(

(defun ee-buffer-manpage-name (&optional bufname)
  "Return the name of the manpage in the buffer BUFNAME, or nil if none.
The default for BUFNAME is the name of the current buffer.
This function does a simple string matching and converts \"*Man
foo*\" to \"foo\"."
  (if (null bufname)
      (setq bufname (buffer-name)))
  (and bufname
       (string-match "^\\*\\(Wo\\)?Man \\(.*\\)\\*$" bufname)
       (match-string 2 bufname)))
  
(defun find-last-manpage-links (manpagename &rest rest)
  "Visit a temporary buffer containing hyperlinks related to a manpage.
Use this when you are in a manpage buffer and you want links to it."
  (interactive (list (ee-buffer-manpage-name)))
  (apply 'find-elinks
	 (list (ee-pph `(find-man-links ,manpagename))
	       ""
	       (ee-pph `(find-man ,manpagename)))
	 rest))

(defun find-manpage-links (manpagename &rest rest)
  "Visit a temporary buffer containing hyperlinks related to a manpage.
Use this when point is over a manpage name and you want links to that page."
  (interactive (list (ee-manpagename-ask)))
  (apply 'find-elinks
	 (list (ee-pph `(find-man-links ,manpagename))
	       ""
	       (ee-pph `(find-man ,manpagename)))
	 rest))

)

;; Creating temporary buffers with lots of elisp hyperlinks is an idea
;; that I only had relatively recently - in 2004, I think... before
;; that I used some functions that either inserted hyperlinks into the
;; current buffer or modified the text in the current buffer to
;; produce hyperlinks. For example, `M-x inn' inserted a link to an
;; info node, and `M-x dff' converted a line with the name of a debian
;; package into three lines, each one with a hyperlink to something
;; related to that debian package...






;; TODO: move these functions to another file (eev-video.el?)
;; (find-angg ".emacs" "mm:ss")
;; (find-angg ".emacs" "find-mplayer")
;; (find-angg ".emacs" "code-mplayer")

;; (find-man "1 mplayer" "  -ss ")
;; (find-man "1 mplayer" "  -fs ")
;; (find-man "1 mplayer" "  -osdlevel ")



;; Tests:
;; (find-upload-links "eev-current/eev-template.el")
;; (find-download-links "" "" "eev-current/eev-template.el")
;; (eevt-down "eev-current/" "emacs/eev/" "eev-template.el")
;






(provide 'eev-elinks)





;; Local Variables:
;; coding:            utf-8-unix
;; ee-anchor-format:  "«%s»"
;; no-byte-compile:   t
;; End: