274 lines
11 KiB
EmacsLisp
274 lines
11 KiB
EmacsLisp
|
;;; 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. This 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.
|
||
|
|
||
|
;;; Code:
|
||
|
(require 'info)
|
||
|
|
||
|
(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)
|
||
|
|
||
|
(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)))
|
||
|
|
||
|
(defvar latex-help--commands-cache nil
|
||
|
"Cahce of discovered of LaTeX commands.
|
||
|
These do NOT have a leading '\\'.")
|
||
|
|
||
|
(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))
|
||
|
|
||
|
(defvar latex-help--package-cache nil
|
||
|
"Cache of discovered LaTeX packages.")
|
||
|
|
||
|
(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))
|
||
|
"."))))
|
||
|
|
||
|
(defvar latex-help--environment-cache nil
|
||
|
"Cache of discovered LaTeX environments.")
|
||
|
|
||
|
(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))
|
||
|
"."))))
|
||
|
|
||
|
(defvar latex-help--class-cache nil
|
||
|
"Cache of discovered LaTeX document classes.")
|
||
|
|
||
|
(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--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)))
|
||
|
|
||
|
(defvar latex-help--caches-initialized-p nil
|
||
|
"Non-nil if the latex-help caches have been initialized.")
|
||
|
|
||
|
(defun latex-help--get-cache-for-type (type)
|
||
|
"Lookup the cache for TYPE.
|
||
|
If the caches are not yet initialized, do that first."
|
||
|
(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--caches-initialized-p t))
|
||
|
(cond
|
||
|
((eq type 'command)
|
||
|
latex-help--commands-cache)
|
||
|
((eq type 'package)
|
||
|
latex-help--package-cache)
|
||
|
((eq type 'environment)
|
||
|
latex-help--environment-cache)
|
||
|
((eq type 'class)
|
||
|
latex-help--class-cache)))
|
||
|
|
||
|
(defun latex-help--history nil
|
||
|
"History list for `latex-help--maybe-prompt-entry'.")
|
||
|
|
||
|
(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 (alist-get name (latex-help--get-cache-for-type type)
|
||
|
nil nil 'equal))
|
||
|
(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--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--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--history
|
||
|
default)))
|
||
|
|
||
|
(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--goto-entry entry)))
|
||
|
|
||
|
(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--goto-entry entry)))
|
||
|
|
||
|
(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--goto-entry entry)))
|
||
|
|
||
|
(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--goto-entry entry)))
|
||
|
|
||
|
(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)
|
||
|
(if-let (thing (latex-help--get-thing-at-point))
|
||
|
(if-let (entry (latex-help--maybe-prompt-entry (car thing)
|
||
|
(cdr thing)))
|
||
|
(latex-help--goto-entry entry)
|
||
|
(user-error "Unknown %s: \"%s\""
|
||
|
(symbol-name (cdr thing))
|
||
|
(if (eq (cdr thing) 'command)
|
||
|
(concat "\\" (car thing))
|
||
|
(car thing))))
|
||
|
(user-error "Nothing at point to look up")))
|
||
|
|
||
|
(provide 'latex-help)
|
||
|
;;; latex-help.el ends here
|