123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682 |
- ;;; latex-help.el --- Lookup LaTeX symbols -*- lexical-binding: t -*-
- ;;; Commentary:
- ;; This is inspired by an old package (originally from the 90s!!) called
- ;; ltx-help.el. That package used to be called latex-help.el too, but it seems
- ;; to have had its name changed sometime around 2010. This package aims for
- ;; similar functionality, but using more up to date and convention-conforming
- ;; Elisp. For example, the original package still assumes that you may not have
- ;; `add-hook' or `buffer-substring-no-properties'. Only very old versions of
- ;; Emacs are missing these, so almost everyone has them nowadays.
- ;;
- ;; This file is mostly internal functions. People looking to use this are
- ;; probably only interested in the following commands:
- ;; - `latex-help-command'
- ;; - `latex-help-environment'
- ;; - `latex-help-package'
- ;; - `latex-help-class'
- ;; - `latex-help-texdoc'
- ;; - `latex-help-at-point'
- ;; - `latex-help'
- ;; The configuration options controlling these can be found by running
- ;; M-x customize-group RET latex-help RET
- ;;; Code:
- (require 'info)
- (require 'cl-lib)
- (require 'shr)
- (defcustom latex-help-info-manual "latex2e"
- "The name of the info manual to use when looking up latex commands."
- :group 'latex-help
- :type '(choice
- (string :tag "English" "latex2e")
- (string :tag "French" "latex2e-fr")
- (string :tag "Spanish" "latex2e-es")))
- (defcustom latex-help-buffer-name "*latex-help*"
- "The name of the info buffer to use when showing LaTeX documentation."
- :group 'latex-help
- :type 'string)
- (defcustom latex-help-texdoc-buffer-name "*latex-help-texdoc*"
- "The name of the buffer to use when showing texdoc files."
- :group 'latex-help
- :type 'string)
- (defcustom latex-help-texdoc-program "texdoc"
- "The program to use when looking things up with texdoc."
- :group 'latex-help
- :type '(string :tag "Executable name"))
- (defcustom latex-help-max-texdoc-entries 10
- "Maximum number of texdoc entries to show when prompting."
- :group 'latex-help
- :type 'interger)
- (defcustom latex-help-pdf-view-program '(emacs "evince")
- "The program to use to view PDF documentation files."
- :group 'latex-help
- :type '(choice
- (string :tag "External program")
- (const :tag "Texdoc default" texdoc)
- (function :tag "Custom function")
- (list :tag "Emacs Doc-View mode"
- (const :tag "Emacs will be used as the default" emacs)
- (choice :tag "Backup"
- (string :tag "Use external program as a backup")
- (const :tag "Use texdoc default as a backup" texdoc)
- (function :tag "Use a custom function as a backup")))))
- (defcustom latex-help-html-view-program 'emacs
- "The program to use to view HTML documentation files."
- :group 'latex-help
- :type '(choice
- (string :tag "External program")
- (const :tag "Texdoc default" texdoc)
- (const :tag "Emacs internal HTML engine" emacs)
- (function :tag "Custom function")))
- (defcustom latex-help-documentation-roots '("/usr/share/texmf-dist/doc/")
- "The directories to search to discover texdoc entries."
- :group 'latex-help
- :type '(repeat directory))
- (defvar latex-help--class-cache nil
- "Cache of discovered LaTeX document classes.")
- (defvar latex-help--environment-cache nil
- "Cache of discovered LaTeX environments.")
- (defvar latex-help--package-cache nil
- "Cache of discovered LaTeX packages.")
- (defvar latex-help--commands-cache nil
- "Cache of discovered of LaTeX commands.
- These do NOT have a leading '\\'.")
- (defvar latex-help--texdoc-cache nil
- "Cache of texdoc entries.")
- (defvar latex-help--caches-initialized-p nil
- "Non-nil if the latex-help caches have been initialized.")
- (defun latex-help--maybe-init-caches ()
- "Init the latex-help caches if they ware empty."
- (unless latex-help--caches-initialized-p
- (setq latex-help--commands-cache (latex-help--discover-commands)
- latex-help--package-cache (latex-help--discover-packages)
- latex-help--environment-cache (latex-help--discover-environments)
- latex-help--class-cache (latex-help--discover-classes)
- latex-help--texdoc-cache (latex-help--discover-texdoc-entries)
- latex-help--caches-initialized-p t)))
- (defun latex-help--open-file-with (cmd file)
- "Open FILE with shell command CMD."
- (call-process-shell-command (format "%s %s" cmd
- (shell-quote-argument file))
- nil 0))
- (defun latex-help--open-file-with-texdoc (file)
- "Open FILE with texdoc."
- (call-process latex-help-texdoc-program nil 0 nil "--just-view" file))
- (defun latex-help--texdoc-open-pdf-file (file)
- "Open the PDF file FILE."
- (cond
- ((and (listp latex-help-pdf-view-program)
- (eq (car latex-help-pdf-view-program) 'emacs))
- (let ((backup (cadr latex-help-pdf-view-program)))
- (cond
- ((display-graphic-p)
- (find-file-other-window file))
- ((eq backup 'texdoc)
- (latex-help--open-file-with-texdoc file))
- ((functionp backup)
- (funcall backup file))
- ((stringp backup)
- (latex-help--open-file-with backup file)))))
- ((eq latex-help-pdf-view-program 'texdoc)
- (latex-help--open-file-with-texdoc file))
- ((functionp latex-help-pdf-view-program)
- (funcall latex-help-pdf-view-program file))
- ((stringp latex-help-pdf-view-program)
- (latex-help--open-file-with latex-help-pdf-view-program file))))
- (defun latex-help--pop-to-texdoc-buffer ()
- "Pop to (and possibly create) the texdoc buffer.
- The buffer's name is from `latex-help-texdoc-buffer-name'."
- (pop-to-buffer (get-buffer-create latex-help-texdoc-buffer-name))
- (setq buffer-read-only t)
- (special-mode))
- (defun latex-help--texdoc-open-html-file (file)
- "Open the HTML file FILE."
- (cond
- ((eq latex-help-html-view-program 'emacs)
- (latex-help--pop-to-texdoc-buffer)
- (let ((buffer-read-only nil))
- (erase-buffer)
- (insert-file-contents file nil)
- (shr-render-region (point-min) (point-max))
- (goto-char (point-min))))
- ((eq latex-help-html-view-program 'texdoc)
- (latex-help--open-file-with-texdoc file))
- ((functionp latex-help-html-view-program)
- (funcall latex-help-html-view-program file))
- ((stringp latex-help-html-view-program)
- (latex-help--open-file-with latex-help-html-view-program file))))
- (defun latex-help--texdoc-maybe-text-file (file)
- "Try to open FILE as a text file.
- Read FILE into a buffer. If it is a text file, show the user that buffer, and
- return t. Otherwise, kill the buffer and return nil."
- (with-current-buffer (generate-new-buffer "*latex-help-texdoc-temp*")
- (setq buffer-read-only t)
- (special-mode)
- (let ((buffer-read-only nil))
- (erase-buffer)
- (insert-file-contents file nil)
- (if (eq buffer-file-coding-system 'no-conversion)
- ;; the file was a binary file
- (progn
- (let ((kill-buffer-query-functions nil))
- (set-buffer-modified-p nil)
- (kill-buffer (current-buffer))
- (user-error "File \"%s\" is binary" file)))
- ;; we are good to go
- (when-let (old-buffer (get-buffer latex-help-texdoc-buffer-name))
- (kill-buffer old-buffer))
- (rename-buffer latex-help-texdoc-buffer-name)
- (pop-to-buffer (current-buffer))))))
- (defun latex-help--texdoc-open-file (file)
- "Open the texdoc file FILE.
- This will attempt to detect the file's type and open it with the correct
- program."
- (let ((ext (or (file-name-extension file) "")))
- (cond
- ((string-equal-ignore-case ext "pdf")
- (latex-help--texdoc-open-pdf-file file))
- ((string-equal-ignore-case ext "html")
- (latex-help--texdoc-open-html-file file))
- (t (latex-help--texdoc-maybe-text-file file)))))
- (defun latex-help--get-thing-at-point ()
- "Return a cons of the LaTeX thing at point and its type (as a symbol).
- If nothing is found, return nil.
- The following types are known:
- - command
- - package
- - environment
- - class
- The following are some examples:
- - \\textbf{Hello World} -> \\='(\"textbf\" . command)
- - \\begin{math} (on \"math\") -> \\='(\"math\" . environment)
- - \\begin{math} (on \"begin\") -> \\='(\"begin\" . command)
- - \\usepackage{amsmath} (on \"amsmath\") -> \\='(\"amsmath\" . package)
- - \\usepackage{amsmath} (on \"usepackage\") -> \\='(\"usepackage\" . command)"
- (save-excursion
- (let ((orig-point (point)))
- (when (eq (char-after) ?\\)
- (forward-char))
- (when (and (search-backward "\\" nil t)
- (looking-at (rx "\\"
- (group (+ (not (any " " "\n" "("
- "{" "[" "|"
- "}" "]" ")" "%")))))))
- (let ((cmd (match-string-no-properties 1)))
- (if (> (match-end 1) orig-point)
- (cons cmd 'command)
- (goto-char orig-point)
- (condition-case _
- (progn
- (backward-up-list nil t t)
- (when (looking-at (rx "{" (group (+ (not (any "}" "\n"))))))
- (let ((thing (match-string-no-properties 1)))
- (cond
- ((equal cmd "usepackage")
- (cons thing 'package))
- ((or (equal cmd "begin")
- (equal cmd "end"))
- (cons thing 'environment))
- ((equal cmd "documentclass")
- (cons thing 'class))))))
- ;; just return nil
- ((or user-error scan-error)))))))))
- (defun latex-help--is-marker-file (file root)
- "Return non-nil if FILE is a texdoc marker file under ROOT.
- A marker file is a file that signifies that its parent is a texdoc entry."
- (let ((name (file-name-nondirectory file))
- (dirname (file-name-nondirectory
- (directory-file-name (file-name-parent-directory file))))
- (case-fold-search t))
- (and
- (not (length= (file-name-split (file-relative-name file root)) 2))
- (or (string-match (rx bos "readme" (* "." (+ (any (?a . ?z))))) name)
- (string-match (rx bos "doc" eos) name)
- (string-match (rx bos "base" eos) name)
- ;; check if file is just its parent directories name with an .tex or
- ;; .pdf
- (string-match (format "^%s[-0-9]*\\.\\(?:tex\\|pdf\\)$"
- (regexp-quote dirname))
- name)))))
- (defun latex-help--search-texdoc-root (root found)
- "Search the texdoc root directory ROOT and discover package names.
- FOUND is the hash table in which to put the entries."
- (cl-loop with to-search = nil
- for dir = root then (pop to-search)
- while dir
- when (file-directory-p dir) do
- (let ((files (directory-files dir t)))
- (if (cl-member-if (lambda (file)
- (latex-help--is-marker-file file root))
- files)
- ;; dir is an entry
- (puthash (file-name-nondirectory dir) nil found)
- ;; search all subdirs
- (setq to-search
- (nconc to-search
- (seq-filter
- (lambda (file)
- (let ((name (file-name-nondirectory file)))
- (and (not (equal name "."))
- (not (equal name "..")))))
- files)))))))
- (defun latex-help--texdoc-config-files ()
- "Return a list of texdoc config files."
- (with-temp-buffer
- (call-process latex-help-texdoc-program nil t nil "--files")
- ;; goto line 3
- (goto-char (point-min))
- (forward-line 2)
- (cl-loop while (re-search-forward (rx bol (+ " ") "active" "\t"
- (group (+ any)) eol) nil t)
- collect (match-string 1))))
- (defun latex-help--texdoc-config-file-entries (file found)
- "Parse the texdoc config file FILE to find entries.
- This attempts to find entries that might have been missed during the initial
- scan. The entries will be `puthash'ed into FOUND as keys."
- (with-temp-buffer
- (insert-file-contents file)
- (goto-char (point-min))
- (while (re-search-forward (rx bol "adjscore("
- (group (+ (not ")"))) ")")
- nil t)
- (puthash (match-string 1) nil found))
- (goto-char (point-min))
- (while (re-search-forward
- (rx bol "alias" (? "(" (+ (any (?0 . ?9) ".")) ")")
- " " (group (+ (not " ")))
- " = " (group (* (not (any "#" "\n" " ")))))
- nil t)
- (puthash (match-string 1) nil found)
- (let ((m2 (match-string 2)))
- (unless (or (zerop (length m2))
- (seq-contains-p m2 ?/))
- (puthash m2 nil found))))))
- (defun latex-help--discover-texdoc-entries ()
- "Discover texdoc entries in each of `latex-help-documentation-roots'."
- (let ((found (make-hash-table :test 'equal)))
- (dolist (root latex-help-documentation-roots)
- (latex-help--search-texdoc-root root found))
- (dolist (file (latex-help--texdoc-config-files))
- (latex-help--texdoc-config-file-entries file found))
- (cl-loop for entry being the hash-keys of found
- collect entry)))
- (defun latex-help--texdoc-files-for-entry (entry)
- "List the texdoc files for ENTRY.
- This returns a list of conses of the display name of the entry and the file it
- belongs to. The first item the the returned list is the default value when
- prompting with `completing-read'."
- (with-temp-buffer
- (when-let ((exit-code (call-process latex-help-texdoc-program nil t
- nil "-Ml" entry))
- ((not (zerop exit-code))))
- ;; try to get the programs output without the normal Emacs process
- ;; sentinel message
- (goto-char (point-max))
- (forward-line -2)
- (end-of-line)
- (let ((msg (buffer-substring-no-properties (point-min)
- (point))))
- (user-error "Texdoc exited with a non-zero code: %d%s"
- exit-code (if (not (zerop (length msg)))
- (concat "\n\n" msg)
- ""))))
- ;; the process succeeded, try to extract the files it found
- (goto-char (point-min))
- (cl-loop repeat latex-help-max-texdoc-entries
- while (re-search-forward (rx (and bol (= 2 (+ (not "\t")) "\t")
- (group (+ (not "\t")))
- "\t"
- (? (+ (not "\t")))
- "\t"
- (group (* any))))
- nil t)
- for file = (match-string 1)
- for desc = (match-string 2)
- unless (zerop (length desc))
- collect (cons (format "%s (%s)" desc file) file)
- else
- collect (cons (format "%s (%s)" (file-name-nondirectory file) file)
- file))))
- (defun latex-help--texdoc-prompt-for-entry-file (entry)
- "Prompt the user to open a texdoc file from ENTRY.
- This will return nil if the user does not want to open the file."
- (let ((entries (latex-help--texdoc-files-for-entry entry)))
- (if (length= entries 1)
- (and (y-or-n-p (format "Open texdoc \"%s\"?" (caar entries)))
- (cdar entries))
- (let ((ans (completing-read "Texdoc File: " (mapcar 'car entries) nil t
- nil nil (caar entries))))
- (unless (zerop (length ans))
- (cdr (assoc ans entries)))))))
- (defvar latex-help--texdoc-history nil
- "History for `latex-heklp--list-texdoc-files'.")
- (defun latex-help--prompt-texdoc-entry ()
- "Ask the user for a texdoc entry."
- (latex-help--maybe-init-caches)
- (let* ((tap (latex-help--get-thing-at-point))
- (has-default-p (and (member (cdr tap) '(package class))
- (member (car tap) latex-help--texdoc-cache)))
- (ans (completing-read (format "Texdoc Entry%s: "
- (if has-default-p
- (format " (default %s)" (car tap))
- ""))
- latex-help--texdoc-cache
- nil nil nil 'latex-help--texdoc-history
- (and has-default-p (car tap)))))
- (unless (zerop (length ans))
- ans)))
- (defun latex-help--run-index-search (regexp)
- "Search the LaTeX info pages index for REGEXP.
- This returns a list of cache entries suitable for use in
- `latex-help--commands-cache'."
- (with-temp-buffer
- (Info-mode)
- (Info-find-node latex-help-info-manual "Index" nil t)
- (let ((found))
- (while (re-search-forward regexp nil t)
- (let ((match (match-string-no-properties 1))
- (node (match-string-no-properties 2)))
- (if (equal (caar found) match)
- (push (cons node (pos-bol)) (cdar found))
- (push (list match (cons node (pos-bol))) found))))
- found)))
- (defun latex-help--discover-commands ()
- "Discover LaTeX commands.
- This is done by parsing the index for `latex-help-info-manual'."
- (let ((found (latex-help--run-index-search
- (rx (and bol "* \\"
- (group (or
- ","
- (+ (not (any " " "{" ",")))))
- (*? any) ":" (+ " ")
- (group (+? any)) ".")))))
- (push (list "(SPACE)" "\\(SPACE)") found)
- (when-let (entry (assoc "(...\\)" found))
- (setq found (assoc-delete-all "(...\\)" found))
- (push (cons "(" (cdr entry)) found)
- (push (cons ")" (cdr entry)) found))
- (when-let (entry (assoc "[...\\]" found))
- (setq found (assoc-delete-all "[...\\]" found))
- (push (cons "[" (cdr entry)) found)
- (push (cons "]" (cdr entry)) found))
- found))
- (defun latex-help--discover-packages ()
- "Discover LaTeX packages.
- This is done by parsing the index for `latex-help-info-manual'."
- (latex-help--run-index-search (rx (and bol "* package, "
- (group (+? any))
- (any " " ":")
- (+? any) (+ " ")
- (group (+? any))
- "."))))
- (defun latex-help--discover-environments ()
- "Discover LaTeX environments.
- This is done by parsing the index for `latex-help-info-manual'."
- (latex-help--run-index-search (rx (and bol "* environment, "
- (group (+? any))
- (any " " ":" "-")
- (+? any) (+ " ")
- (group (+? any))
- "."))))
- (defun latex-help--discover-classes ()
- "Discover LaTeX document classes.
- This is done by parsing the index for `latex-help-info-manual'."
- (latex-help--run-index-search (rx (and bol "* "
- (group (+ (not (any "," " "))))
- " class:" (+ " ")
- (group (+ (not ".")))))))
- (defun latex-help--info-goto-entry (entry)
- "Open the info page for ENTRY, a cache entry."
- (let ((buffer (get-buffer-create latex-help-buffer-name)))
- (with-current-buffer buffer
- (unless (derived-mode-p 'Info-mode)
- (Info-mode))
- (Info-find-node latex-help-info-manual "Index" nil t)
- (goto-char (cdr entry))
- (Info-follow-nearest-node))
- (pop-to-buffer buffer)))
- (defun latex-help--get-cache-for-type (type)
- "Lookup the cache for TYPE.
- If the caches are not yet initialized, do that first."
- (latex-help--maybe-init-caches)
- (cl-case type
- (command latex-help--commands-cache)
- (package latex-help--package-cache)
- (environment latex-help--environment-cache)
- (class latex-help--class-cache)))
- (defvar latex-help--info-history nil
- "History list for `latex-help--prompt-for'.")
- (defun latex-help--maybe-prompt-entry (name type &optional default)
- "Lookup and prompt the user for the node of NAME.
- The lookup is performed in the correct cache for TYPE. If there is only one
- node associated with NAME, return its entry. Otherwise, ask the user which node
- they want to use.
- If DEFAULT is non-nil, use that instead of prompting. If it does not exist,
- return nil."
- (when-let (entries (cdr (assoc name (latex-help--get-cache-for-type type))))
- (cond
- (default
- (assoc default entries))
- ((length= entries 1)
- (car entries))
- (t
- (let ((resp (completing-read "Select Node: " (mapcar 'car entries)
- nil t nil)))
- (assoc resp entries))))))
- (defun latex-help--prompt-for (type)
- "Prompt for a command, environment, etc. from TYPE.
- This returns the name of the thing that was prompted."
- (let* ((cache (latex-help--get-cache-for-type type))
- (tap (latex-help--get-thing-at-point))
- (default (and (eq (cdr tap) type) (car tap))))
- (unless (assoc default cache)
- (setq default nil))
- (completing-read (format "LaTeX %s%s: "
- (capitalize (symbol-name type))
- (if default
- (format " (default %s)" default)
- ""))
- (latex-help--get-cache-for-type type)
- nil t nil 'latex-help--info-history
- default)))
- ;;;###autoload
- (defun latex-help-command (name &optional node)
- "Lookup the LaTeX command NAME.
- Unless NODE is non-nil, if NAME is in more than one node, prompt the user for
- which to use. If NODE is non-nil, use that instead."
- (interactive (list (latex-help--prompt-for 'command)))
- (when-let (entry (latex-help--maybe-prompt-entry name 'command node))
- (latex-help--info-goto-entry entry)))
- ;;;###autoload
- (defun latex-help-environment (name &optional node)
- "Lookup the LaTeX environment NAME.
- Unless NODE is non-nil, if NAME is in more than one node, prompt the user for
- which to use. If NODE is non-nil, use that instead."
- (interactive (list (latex-help--prompt-for 'environment)))
- (when-let (entry (latex-help--maybe-prompt-entry name 'environment node))
- (latex-help--info-goto-entry entry)))
- ;;;###autoload
- (defun latex-help-package (name &optional node)
- "Lookup the LaTeX package NAME.
- Unless NODE is non-nil, if NAME is in more than one node, prompt the user for
- which to use. If NODE is non-nil, use that instead."
- (interactive (list (latex-help--prompt-for 'package)))
- (when-let (entry (latex-help--maybe-prompt-entry name 'package node))
- (latex-help--info-goto-entry entry)))
- ;;;###autoload
- (defun latex-help-class (name &optional node)
- "Lookup the LaTeX document class NAME.
- Unless NODE is non-nil, if NAME is in more than one node, prompt the user for
- which to use. If NODE is non-nil, use that instead."
- (interactive (list (latex-help--prompt-for 'class)))
- (when-let (entry (latex-help--maybe-prompt-entry name 'class node))
- (latex-help--info-goto-entry entry)))
- ;;;###autoload
- (defun latex-help-texdoc (name)
- "Lookup NAME in the texdoc cache.
- When used interactively, prompt for NAME."
- (interactive (list (latex-help--prompt-texdoc-entry)))
- (latex-help--maybe-init-caches)
- (when-let ((file (latex-help--texdoc-prompt-for-entry-file name)))
- (latex-help--texdoc-open-file file)))
- (defun latex-help--prompt-info-and-texdoc (info-entry texdoc-entry)
- "Prompt the user for both info and texdoc entries.
- INFO-ENTRY is an entry from one of the info caches. TEXDOC-ENTRY is an entry
- from the texdoc cache."
- (let* ((texdoc-files (and texdoc-entry
- (latex-help--texdoc-files-for-entry
- texdoc-entry)))
- (prompts (nconc (mapcar (lambda (file)
- (concat "(Texdoc) " (car file)))
- texdoc-files)
- (mapcar (lambda (node)
- (concat "(Info) " (car node)))
- (cdr info-entry)))))
- (when prompts
- (let ((selected (completing-read "LaTeX Help: " prompts nil t nil
- nil (when texdoc-files
- (car prompts)))))
- (when (string-match (rx bos "(" (group (+ (any (?a . ?z))
- (any (?A . ?Z))))
- ") " (group (* any)))
- selected)
- (if (equal (match-string 1 selected) "Info")
- (cons (assoc (match-string 2 selected) (cdr info-entry)) 'info)
- (cons (cdr (assoc (match-string 2 selected) texdoc-files))
- 'texdoc)))))))
- ;;;###autoload
- (defun latex-help-at-point ()
- "Try to lookup the LaTeX thing at point, whatever it may be.
- This will try to look up the command, package, document class, or environment at
- point. If that thing at point is valid, it will open an info buffer to the
- documentation for that thing."
- (interactive)
- (latex-help--maybe-init-caches)
- (if-let (thing (latex-help--get-thing-at-point))
- (let ((info-entry (assoc (car thing) (latex-help--get-cache-for-type
- (cdr thing))))
- (texdoc-entry (and (member (cdr thing) '(class package environment))
- (cl-find (car thing) latex-help--texdoc-cache
- :test 'equal))))
- (unless (or info-entry texdoc-entry)
- (user-error "Unknown %s: \"%s\""
- (symbol-name (cdr thing))
- (if (eq (cdr thing) 'command)
- (concat "\\" (car thing))
- (car thing))))
- (cl-destructuring-bind (thing . type)
- (latex-help--prompt-info-and-texdoc info-entry texdoc-entry)
- (cl-case type
- (texdoc
- (latex-help--texdoc-open-file thing))
- (info
- (latex-help--info-goto-entry thing)))))
- (user-error "Nothing at point to look up")))
- (defvar latex-help--general-history nil
- "History for `latex-help'.")
- ;;;###autoload
- (defun latex-help ()
- "Get help with LaTeX.
- Prompt the user for an info topic or texdoc file, then open that thing."
- (interactive)
- (let ((prompts)
- (tap (latex-help--get-thing-at-point))
- (def-entry nil)
- (def-name nil))
- (latex-help--maybe-init-caches)
- (cl-flet ((add-cache-for-type (type)
- (dolist (entry (latex-help--get-cache-for-type type))
- (push (format "(Info) %s - %s"
- (capitalize (symbol-name type))
- (car entry))
- prompts)
- (when (and (eq type (cdr tap))
- (equal (car entry) (car tap)))
- (setq def-entry (car prompts)
- def-name (car entry))))))
- (add-cache-for-type 'command)
- (add-cache-for-type 'package)
- (add-cache-for-type 'class)
- (add-cache-for-type 'environment)
- (dolist (entry latex-help--texdoc-cache)
- (push (format "(Texdoc) %s" entry) prompts)
- (when (and (member (cdr tap) '(class package environment))
- (equal entry (car tap)))
- (setq def-entry (car prompts)
- def-name entry)))
- (when-let ((ans (completing-read (format "LaTeX Help%s: "
- (if def-name
- (format " (default %s)"
- def-name)
- ""))
- prompts
- nil t nil 'latex-help--general-history
- def-entry))
- ((not (zerop (length ans)))))
- (if (string-prefix-p "(Texdoc) " ans)
- (latex-help-texdoc (seq-subseq ans (length "(Texdoc) ")))
- (string-match (rx "(Info) " (group (+ (not " ")))
- " - " (group (+ any)))
- ans)
- (when-let ((thing (match-string 2 ans))
- (type (intern (downcase (match-string 1 ans))))
- (entry (latex-help--maybe-prompt-entry thing type)))
- (latex-help--info-goto-entry entry)))))))
- (provide 'latex-help)
- ;;; latex-help.el ends here
|