mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-11-30 08:09:04 +00:00
971 lines
33 KiB
EmacsLisp
971 lines
33 KiB
EmacsLisp
;;; word-help.el --- keyword help for any language doc'd in TeXinfo.
|
|
|
|
;; Copyright (c) 1996 Free Software Foundation, Inc.
|
|
|
|
;; Author: Jens T. Berger Thielemann <jensthi@ifi.uio.no>
|
|
;; Keywords: help, keyword, languages, completion
|
|
|
|
;; This file is part of GNU Emacs.
|
|
|
|
;; This program 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 2, or (at your option)
|
|
;; any later version.
|
|
|
|
;; This program 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; see the file COPYING. If not, write to the
|
|
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
;; Boston, MA 02111-1307, USA.
|
|
|
|
;;; Commentary:
|
|
|
|
;; This package provides a rather general interface for doing keyword
|
|
;; help in most languages. In short, it'll determine which TeXinfo
|
|
;; file which is relevant for the current mode; cache the index and
|
|
;; use regexps to give you help on the keyword you're looking at.
|
|
|
|
;; Installation
|
|
;; ************
|
|
|
|
;; For the default setup to work for all supported modes, make sure
|
|
;; the Texinfo files from the following packages are installed:
|
|
|
|
;; Texinfo file | Available in archive or URL | Notes
|
|
;; autoconf.info | autoconf-2.10.tar.gz | -
|
|
;; bison.info | bison-1.25.tar.gz | -
|
|
;; libc.info | glibc-1.09.1.tar.gz | -
|
|
;; elisp.info | elisp-manual-19-2.4.tar.gz | -
|
|
;; latex.info | ftp://ftp.dante.de/pub/tex/info/latex2e-help-texinfo/latex2e.texi
|
|
;; groff.info | groff-1.10.tar.gz | -
|
|
;; m4.info | m4-1.4.tar.gz | -
|
|
;; make.info | make-3.75.tar.gz | -
|
|
;; perl.info | http://www.perl.com/CPAN/doc/manual/info/
|
|
;; simula.info | Mail bjort@ifi.uio.no | Written in Norwegian
|
|
;; texinfo.info | texinfo-3.9.tar.gz | -
|
|
|
|
;; BTW: We refer to Texinfo files by just their last component, not
|
|
;; with an absolute file name. You must thus set up
|
|
;; `Info-directory-list' and `Info-default-directory-list' so that
|
|
;; these can automatically be located.
|
|
|
|
;; Usage
|
|
;; *****
|
|
;;
|
|
;; Place the cursor over the function/variable/type/whatever you want
|
|
;; help on. Type "C-h C-i". `word-help' will then make a suggestion
|
|
;; to an index topic; press return to accept this. If not, you may use
|
|
;; tab-completion to find the topic you're interested in.
|
|
|
|
;; `word-help' is also able to do symbol completion via the
|
|
;; `word-help-complete' function. Bind this function to C-TAB by
|
|
;; adding the following line to your .emacs file:
|
|
;;
|
|
;; (global-set-key [?\M-\t] 'word-help-complete)
|
|
;;
|
|
;; Note that some modes automatically override this key; you may
|
|
;; therefore wish to either put the above statement in a hook or
|
|
;; associate the function with an other key.
|
|
|
|
;; Usually, `word-help' is able to determine the relevant Texinfo
|
|
;; file from looking at the buffer's `mode-name'; if not, you can use
|
|
;; the interactive function `set-help-file' to set this.
|
|
|
|
;; Customizing
|
|
;; ***********
|
|
;;
|
|
;; User interface
|
|
;; --------------
|
|
;;
|
|
;; Two variables control the behaviour of the user-interface of
|
|
;; `word-help': `word-help-split-window' and
|
|
;; `word-help-magic-index'. Do C-h v to get more information on
|
|
;; these.
|
|
|
|
;; Adding more Texinfo files
|
|
;; -------------------------
|
|
;;
|
|
;; Associations between mode-names and Texinfo files can be done
|
|
;; through the `word-help-mode-alist' variable, which defines an
|
|
;; `alist' making `set-help-file' able to initialize the necessary
|
|
;; variable.
|
|
|
|
;; NOTE: If you have to customize the regexps, it is *CRUCIAL* that
|
|
;; none of your regexps match the empty string! Not adhering to this
|
|
;; restriction will make `word-help' enter an infinite loop.
|
|
|
|
;; Contacting the author
|
|
;; *********************
|
|
;;
|
|
;; If you wish to contact me for any reason, please feel free to write
|
|
;; to:
|
|
|
|
;; Jens Berger
|
|
;; Spektrumveien 4
|
|
;; N-0666 Oslo
|
|
;; Norway
|
|
;;
|
|
;; E-mail: <jensthi@ifi.uio.no>
|
|
|
|
;; Have fun.
|
|
|
|
;;
|
|
;;; Code:
|
|
;;
|
|
|
|
(require 'info)
|
|
|
|
;;;--------------------
|
|
;;; USER OPTIONS
|
|
;;;--------------------
|
|
|
|
(defvar word-help-split-window t
|
|
"*Non-nil means that the info buffer will pop up in a separate window.
|
|
If nil, we will just switch to it.")
|
|
|
|
(defvar word-help-magic-index t
|
|
"*Non-nil means that the keyword will be searched for in the requested node.
|
|
This is done by determining whether the line the point is positioned
|
|
on after using `Info-goto-node', actually contains the keyword. If
|
|
not, we will search for the first occurence of the keyword. This may
|
|
help when the info file isn't correctly indexed.")
|
|
|
|
;;; ---- end of user configurable variables
|
|
|
|
;;;-------------------------
|
|
;;; ADVANCED USER OPTIONS
|
|
;;;-------------------------
|
|
|
|
(defvar word-help-mode-alist
|
|
'(
|
|
("autoconf"
|
|
(("autoconf" "Macro Index") ("m4" "Macro index"))
|
|
(("AC_\\([A-Za-z0-9_]+\\)" 1)
|
|
("[a-z]+"))
|
|
nil
|
|
nil
|
|
(("AC_\\([A-Za-z0-9_]+\\)" 1 nil (("^[A-Z_]+$")))
|
|
("[a-z_][a-z_]*" 0 nil (("^[a-z_]+$")))))
|
|
|
|
("Bison"
|
|
(("bison" "Index")
|
|
("libc" "Type Index" "Function Index" "Variable Index"))
|
|
(("%[A-Za-z]*")
|
|
("[A-Za-z_][A-Za-z0-9_]*"))
|
|
nil
|
|
nil
|
|
(("%[A-Za-z]*" nil nil (("^%")))
|
|
("[A-Za-z_][A-Za-z0-9_]*" nil nil (("[A-Za-z_][A-Za-z0-9_]*")))))
|
|
|
|
("YACC" . "Bison")
|
|
|
|
("C" (("libc" "Type Index" "Function Index" "Variable Index")))
|
|
("C++" . "C")
|
|
|
|
("Emacs-Lisp"
|
|
(("elisp" "Index"))
|
|
(("[^][ ()\n\t.\"'#]+"))
|
|
nil
|
|
nil
|
|
lisp-complete-symbol)
|
|
|
|
("LaTeX"
|
|
(("latex" "Command Index"))
|
|
(("\\\\\\(begin\\|end\\){\\([^}\n]+\\)}" 2 0)
|
|
("\\\\[A-Za-z]+")
|
|
("\\\\[^A-Za-z]")
|
|
("[A-Za-z]+"))
|
|
nil
|
|
nil
|
|
(("\\\\begin{\\([A-Za-z]*\\)" 1 "}" (("^[A-Za-z]+$")))
|
|
("\\\\end{\\([A-Za-z]*\\)" 1 "}" (("^[A-Za-z]+$")))
|
|
("\\\\renewcommand{\\(\\\\?[A-Za-z]*\\)" 1 "}" (("^\\\\[A-Za-z]+")))
|
|
("\\\\renewcommand\\(\\\\?[A-Za-z]*\\)" 1 "" (("^\\\\[A-Za-z]+")))
|
|
("\\\\renewenvironment{?\\([A-Za-z]*\\)" 1 "}"(("^[A-Za-z]+$")))
|
|
("\\\\[A-Za-z]*" 0 "" (("^\\\\[A-Za-z]+")))))
|
|
|
|
("latex" . "LaTeX")
|
|
|
|
("Nroff"
|
|
(("groff" "Macro Index" "Register Index" "Request Index"))
|
|
(("\\.[^A-Za-z]")
|
|
("\\.[A-Za-z]+")
|
|
("\\.\\([A-Za-z]+\\)" 1))
|
|
nil
|
|
nil
|
|
(("\\.[A-Za-z]*" nil nil (("^\\.[A-Za-z]+$")))
|
|
("\\.\\([A-Za-z]*\\)" 1 nil (("^[A-Za-z]+$")))))
|
|
|
|
("Groff" . "Nroff")
|
|
|
|
("m4"
|
|
(("m4" "Macro index"))
|
|
(("\\([mM]4_\\)?\\([A-Za-z_][A-Za-z_0-9]*\\)" 2))
|
|
nil
|
|
nil
|
|
(("[mM]4_\\([A-Za-z_]?[A-Za-z_0-9]*\\)" 1)
|
|
("[A-Za-z_][A-Za-z_0-9]*")))
|
|
|
|
("Makefile"
|
|
(("make" "Name Index"))
|
|
(("\\.[A-Za-z]+") ;; .SUFFIXES
|
|
("\\$[^()]") ;; $@
|
|
("\\$([^A-Za-z].)") ;; $(<@)
|
|
("\\$[\(\{]\\([a-zA-Z+]\\)" 1) ;; $(wildcard)
|
|
("[A-Za-z]+")) ;; foreach
|
|
nil
|
|
nil
|
|
(("\\.[A-Za-z]*" nil ":" (("^\\.[A-Za-z]+$")))
|
|
("\\$(\\([A-Z]*\\)" 1 ")" (("^[A-Z]")))
|
|
("[a-z]+" nil nil (("^[a-z]+$")))))
|
|
|
|
("Perl"
|
|
(("perl" "Variable Index" "Function Index"))
|
|
(("\\$[^A-Za-z^]") ;; $@
|
|
("\\$\\^[A-Za-z]?") ;; $^D
|
|
("\\$[A-Za-z][A-Za-z_0-9]+") ;; $foobar
|
|
("[A-Za-z_][A-Za-z_0-9]+")) ;; dbmopen
|
|
nil
|
|
nil
|
|
(("\\$[A-Za-z]*" nil nil (("^\\$[A-Za-z]+$"))) ;; $variable
|
|
("[A-Za-z_][A-Za-z_0-9]*" nil nil
|
|
(("^[A-Za-z_][A-Za-z_0-9]*$"))))) ;; function
|
|
|
|
("Simula" (("simula" "Index")) nil t)
|
|
("Ifi Simula" . "Simula")
|
|
("SIMULA" . "Simula")
|
|
|
|
("Texinfo"
|
|
(("texinfo" "Command and Variable Index"))
|
|
(("@\\([A-Za-z]+\\)" 1))
|
|
nil
|
|
nil
|
|
(("@\\([A-Za-z]*\\)" 1)))
|
|
|
|
)
|
|
"Assoc list between `mode-name' and Texinfo files.
|
|
The variable should be initialized with a list of elements with the
|
|
following form:
|
|
|
|
\(mode-name (word-help-info-files) (word-help-keyword-regexps)
|
|
word-help-ignore-case word-help-index-mapper
|
|
word-help-complete-list)
|
|
|
|
where `word-help-info-files', `word-help-keyword-regexps' and so
|
|
forth of course are the values which should be put in these variables
|
|
for this mode. Note that `mode-name' doesn't have to be a legal
|
|
mode-name; the user may use the call `set-help-file', where
|
|
`mode-name' will be used in the `completing-read'.
|
|
|
|
Example entry (for C):
|
|
|
|
\(\"C\" ((\"libc\" \"Type Index\" \"Function Index\" \"Variable Index\"))
|
|
((\"[A-Za-z_][A-Za-z0-9]+\")))
|
|
|
|
The two first variables must be initialized; the two remaining will
|
|
get default values if you omit them or set them to nil. The default
|
|
values are:
|
|
|
|
word-help-keyword-regexps: (\"[A-Za-z_][A-Za-z0-9]+\")
|
|
word-help-ignore-case: nil
|
|
|
|
More settings may be defined in the future.
|
|
|
|
You may also define aliases, if there are several relevant mode-names
|
|
to a single entry. These should be of the form:
|
|
|
|
\(MODE-NAME-ALIAS . MODE-NAME-REAL)
|
|
|
|
For C++, you would use the alias
|
|
|
|
\(\"C++\" . \"C\")
|
|
|
|
to make C++ mode use the same help files as C files do. Please note
|
|
that you can shoot yourself in the foot with this possibility, by
|
|
defining recursive aliases.")
|
|
|
|
;;; --- end of advanced user options
|
|
|
|
(defvar word-help-ignore-case nil
|
|
"Non-nil means that case is ignored when doing lookup.")
|
|
(make-variable-buffer-local 'word-help-ignore-case)
|
|
|
|
(defvar word-help-info-files nil
|
|
"List of info files with respective nodes, for the current mode.
|
|
|
|
This should be a list of the following form:
|
|
|
|
\((INFO-FILE-1 NODE-NAME-1 NODE-NAME-2 ...)
|
|
(INFO-FILE-1 NODE-NAME-1 NODE-NAME-2 ...)
|
|
: : :
|
|
(INFO-FILE-1 NODE-NAME-1 NODE-NAME-2 ...))
|
|
|
|
An example entry for e.g. C would be:
|
|
|
|
\((\"/local/share/gnu/info/libc\" \"Function Index\" \"Type Index\"
|
|
\"Variable Index\"))
|
|
|
|
The files and nodes will be searched/cached in the order specified.
|
|
This variable is usually set by the `word-help-switch-help-file'
|
|
function, which utilizes the `word-help-mode-alist'.")
|
|
(make-variable-buffer-local 'word-help-info-files)
|
|
|
|
(defvar word-help-keyword-regexps nil
|
|
"Regexps for finding keywords in the current mode.
|
|
|
|
This is constructed as a list of the following form:
|
|
|
|
\((REGEXP SUBMATCH-LOOKUP SUBMATCH-CURSOR)
|
|
(REGEXP SUBMATCH-LOOKUP SUBMATCH-CURSOR)
|
|
: : :
|
|
(REGEXP SUBMATCH-LOOKUP SUBMATCH-CURSOR))
|
|
|
|
The regexps will be searched in order for a match which the cursor is
|
|
within.
|
|
|
|
submatch-lookup is the submatch number which will be looked for in the
|
|
index. May be omitted; defaults to 0 (e.g. the entire pattern). This is
|
|
useful in for instance configure lookup; each command is there prefixed
|
|
with 'AC_', which must be ignored when doing a lookup. Example regexp
|
|
entry for this:
|
|
|
|
\(\"AC_\\\\([A-Za-z0-9]+\\\\)\" 1)
|
|
|
|
submatch-cursor is the part of the match which the cursor must be within.
|
|
May be omitted; defaults to 0 (e.g. the entire pattern).")
|
|
(make-variable-buffer-local 'word-help-keyword-regexps)
|
|
(set-default 'word-help-keyword-regexps '(("[A-Za-z_][A-Za-z_0-9]*")))
|
|
|
|
(defvar word-help-index-mapper nil
|
|
"Regexps to use for massaging index-entries into keywords.
|
|
This variable should contain a list of regexps with sub-expressions,
|
|
where we will only look for the sub-expression in the user text.
|
|
|
|
The regexp list should be formatted as:
|
|
|
|
((REGEXP SUBEXP) (REGEXP SUBEXP) ... )
|
|
|
|
If the index entry does not match any of the regexps, it will be ignored.
|
|
|
|
Example:
|
|
|
|
Perl has index entries of the following form:
|
|
|
|
* abs VALUE: perlfunc.
|
|
* accept NEWSOCKET,GENERICSOCKET: perlfunc.
|
|
* alarm SECONDS: perlfunc.
|
|
* atan2 Y,X: perlfunc.
|
|
* bind SOCKET,NAME: perlfunc.
|
|
: : :
|
|
|
|
We will thus try to extract the first word in the index entry -
|
|
\"abs\" from \"abs VALUE\", etc. This is done by the following entry:
|
|
|
|
\((\"^\\\\([^ \\t\\n]+\\\\)\" 1))
|
|
|
|
This value is btw. the default one, and works with most Texinfo files")
|
|
(make-variable-buffer-local 'word-help-index-mapper)
|
|
(set-default 'word-help-index-mapper '(("^\\([^ \t\n]+\\)" 1)))
|
|
|
|
|
|
(defvar word-help-complete-list nil
|
|
"Regexps or function to use for completion of symbols.
|
|
The list should have the following format:
|
|
|
|
((REGEXP SUBMATCH TEXT-APPEND (RE-FILTER-1 REG-FILTER-2 ...)
|
|
: : : : :
|
|
(REGEXP SUBMATCH TEXT-APPEND (RE-FILTER-1 REG-FILTER-2 ...))
|
|
|
|
The two first entries are similar to `word-help-keyword-regexps',
|
|
REGEXP is a regular expression which should match any relevant
|
|
expression, and where SUBMATCH should be used for look up. By
|
|
specifying non-nil REGEXP-FILTERs, we'll only include entries in the
|
|
index which matches the regexp specified.
|
|
|
|
If the contents of this variable is a symbol of a function, this
|
|
function will be called instead. This is useful for modes providing
|
|
a more intelligent function (like `lisp-complete-symbol' in Emacs Lisp mode).
|
|
|
|
If you would like to use another function instead, you may.
|
|
|
|
Non-nil TEXT-APPEND means that this text will be inserted after the
|
|
completion, if we manage to do make a completion.")
|
|
(make-variable-buffer-local 'word-help-complete-list)
|
|
(set-default 'word-help-complete-list '(("[A-Za-z_][A-Za-z_0-9]*")))
|
|
|
|
;;; Work variables
|
|
|
|
|
|
(defvar word-help-main-index nil
|
|
"List of all index entries.
|
|
|
|
See `word-help-process-indexes' for structure formatting.
|
|
|
|
Minor note: This variable is a list if it is initialized, t if
|
|
initializing failed and nil if uninitialized.")
|
|
(make-variable-buffer-local 'word-help-main-index)
|
|
|
|
(defvar word-help-complete-index nil
|
|
"List of regexps for completion, with matching index entries.
|
|
Value is nil if uninitialized, t if initialized but not accessible,
|
|
a list if we're feeling ok.")
|
|
(make-variable-buffer-local 'word-help-complete-index)
|
|
|
|
(defvar word-help-main-obarray nil
|
|
"Global work variable for `word-help' system.
|
|
Do Not mess with this!")
|
|
|
|
(defvar word-help-history nil
|
|
"History for `word-help' minibuffer queries.")
|
|
(make-local-variable 'word-help-history)
|
|
|
|
(defvar word-help-current-help-file nil
|
|
"Current help file active for this mode.")
|
|
|
|
(defvar word-help-index-alist nil
|
|
"An assoc list mapping help files to info indexes.
|
|
This means that `word-help-mode-index' can be init'ed faster.")
|
|
|
|
(defvar word-help-help-mode nil
|
|
"Which mode the help system is bound to for the current mode.")
|
|
(make-variable-buffer-local 'word-help-help-mode)
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;; User Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;;; Debugging
|
|
|
|
;;;###autoload
|
|
(defun reset-word-help ()
|
|
"Clear all cached indexes in the `word-help' system.
|
|
You should only need this when installing new info files, and/or
|
|
adding more Texinfo files to the `word-help' system."
|
|
(interactive)
|
|
(setq word-help-index-alist nil
|
|
word-help-main-index nil
|
|
word-help-info-files nil
|
|
word-help-complete-index nil))
|
|
|
|
|
|
;;; Changing help file
|
|
|
|
;;;###autoload
|
|
(defun set-word-help-file ()
|
|
"Change which set of Texinfo files used for word-help.
|
|
|
|
`word-help' maintains a list over which Texinfo files which are
|
|
relevant for each programming language (`word-help-mode-alist'). It
|
|
usually selects the correct one, based upon the value of `mode-name'.
|
|
If this guess is incorrect, you may also use this function manually to
|
|
instruct future `word-help' calls which Texinfo files to use."
|
|
(interactive)
|
|
(let (helpfile helpguess (completion-ignore-case t))
|
|
;; Try to make a guess
|
|
(setq helpguess (cond
|
|
(word-help-current-help-file)
|
|
((word-help-guess-help-file))))
|
|
;; Ask the user
|
|
(setq helpfile (completing-read
|
|
(if helpguess
|
|
(format "Select help mode (default %s): " helpguess)
|
|
"Select help mode: ")
|
|
word-help-mode-alist
|
|
nil t nil nil))
|
|
(if (equal "" helpfile)
|
|
(setq helpfile helpguess))
|
|
(if helpfile
|
|
(word-help-switch-help-file helpfile))))
|
|
|
|
;;; Main user interface
|
|
|
|
;;;###autoload
|
|
(defun word-help ()
|
|
"Find documentation on the keyword under the cursor.
|
|
The determination of which language the keyword belongs to, is based upon
|
|
The relevant info file is selected by matching `mode-name' (the major
|
|
mode) against the assoc list `word-help-mode-alist'.
|
|
|
|
If this is not possible, `set-help-file' will be invoked for selecting
|
|
the relevant info file. `set-help-file' may also be invoked
|
|
interactively by the user.
|
|
|
|
If the keyword you are looking at is not available in any index, no
|
|
default suggestion will be presented. "
|
|
(interactive)
|
|
(let (myguess guess index-info
|
|
(completion-ignore-case word-help-ignore-case))
|
|
;; Set necessary variables for later lookup
|
|
(word-help-find-help-file)
|
|
;; Have we previously cached datas?
|
|
(word-help-process-indexes)
|
|
(if
|
|
(atom word-help-main-index)
|
|
(message "No help file available for this mode.")
|
|
;; First make a guess at what the user is looking for
|
|
(setq myguess (word-help-guess
|
|
(point)
|
|
(cond
|
|
((not (atom word-help-main-index))
|
|
(car word-help-main-index)))
|
|
word-help-keyword-regexps))
|
|
;; Ask the user himself
|
|
(setq guess (completing-read
|
|
; Format string
|
|
(if myguess
|
|
(format "Look up keyword (default %s): " myguess)
|
|
"Look up keyword: ")
|
|
; Collection
|
|
(car word-help-main-index)
|
|
nil t nil 'word-help-history))
|
|
(if (equal guess "")
|
|
(setq guess myguess))
|
|
;; If we've got anything meaningful to lookup, do so
|
|
(if (not guess)
|
|
(message "Help aborted.")
|
|
(setq index-info (word-help-find-index-node
|
|
guess
|
|
word-help-main-index))
|
|
(if (not index-info)
|
|
(message "Oops, I could not find \"%s\" anyway! Bug?" guess)
|
|
(word-help-goto-index-node (nconc index-info (list guess))))))))
|
|
|
|
;;;###autoload
|
|
(defun word-help-complete ()
|
|
"Perform completion on the symbol preceding the point.
|
|
The determination of which language the keyword belongs to, is based upon
|
|
The relevant info file is selected by matching `mode-name' (the major
|
|
mode) against the assoc list `word-help-mode-alist'.
|
|
|
|
If this is not possible, `set-help-file' will be invoked for selecting
|
|
the relevant info file. `set-help-file' may also be invoked
|
|
interactively by the user.
|
|
|
|
The keywords are extracted from the index of the info file defined for
|
|
this mode, by using the `word-help-complete-list' variable."
|
|
(interactive)
|
|
(word-help-make-complete)
|
|
(cond
|
|
((not word-help-complete-index)
|
|
(message "No completion available for this mode."))
|
|
((symbolp word-help-complete-index)
|
|
(call-interactively word-help-complete-index))
|
|
((listp word-help-complete-index)
|
|
(let ((all-match (word-help-guess-all (point)
|
|
word-help-complete-index t))
|
|
(completion-ignore-case word-help-ignore-case)
|
|
(c-list word-help-complete-index)
|
|
c-entry word-match completion completed)
|
|
;; Loop over and try to find a match
|
|
(while (and all-match (not completed))
|
|
(setq word-match (car all-match)
|
|
c-entry (car c-list)
|
|
c-list (cdr c-list)
|
|
all-match (cdr all-match))
|
|
;; Check whether the current pattern matched
|
|
(if word-match
|
|
(let ((close (nth 3 c-entry))
|
|
(words (nth 4 c-entry)))
|
|
;; Find the maximum completion for this word
|
|
; (print word-match)
|
|
; (print c-entry)
|
|
; (print close)
|
|
(setq completion (try-completion word-match words))
|
|
;; Was the match exact
|
|
(cond ((eq completion t)
|
|
(and close
|
|
(not (looking-at (regexp-quote close)))
|
|
(insert close))
|
|
(setq completed t))
|
|
;; Silently ignore non-matches
|
|
((not completion))
|
|
;; May we complete more unambiguously
|
|
((not (string-equal completion word-match))
|
|
(delete-region (- (point) (length word-match))
|
|
(point))
|
|
(insert completion)
|
|
(if (eq t (try-completion completion words))
|
|
(progn
|
|
(and close
|
|
(not (looking-at (regexp-quote close)))
|
|
(insert close))))
|
|
(setq completed t))
|
|
(t
|
|
(message "Making completion list...")
|
|
(let ((list (all-completions word-match words nil)))
|
|
(setq completed list)
|
|
(with-output-to-temp-buffer "*Completions*"
|
|
(display-completion-list list)))
|
|
(message "Making completion list...done"))))))
|
|
(if (not completed) (message "No match."))))
|
|
(t (message "No completion available for this mode."))))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;; Index mapping ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
(defun word-help-map-index-entries (str re-list)
|
|
"Transform an Info index entry into a programming keyword.
|
|
Uses this by mapping the entries through `word-help-index-mapper'."
|
|
(let ((regexp (car (car re-list)))
|
|
(subexp (car (cdr (car re-list))))
|
|
(next (cdr re-list)))
|
|
(cond
|
|
((string-match regexp str)
|
|
(substring str (match-beginning subexp) (match-end subexp)))
|
|
(next
|
|
(word-help-map-index-entries str next)))))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;; Switch mode files ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;;; Mode lookup
|
|
|
|
(defun word-help-guess-help-file ()
|
|
"Guesses a relevant help file based on mode name.
|
|
Returns nil if no guess could be made. Uses `word-help-mode-alist'."
|
|
(let (guess)
|
|
(cond
|
|
((setq guess (assoc mode-name word-help-mode-alist))
|
|
(car guess)))))
|
|
|
|
|
|
(defun word-help-switch-help-file (helpfile)
|
|
"Changes the help-file to the mode name given.
|
|
Uses `word-help-mode-alist'."
|
|
(if helpfile
|
|
(let (helpdesc)
|
|
(if (not (setq helpdesc (assoc helpfile word-help-mode-alist)))
|
|
(message "No help defined for \"%s\"." helpfile)
|
|
(if (stringp (cdr helpdesc))
|
|
(word-help-switch-help-file (cdr helpdesc))
|
|
(word-help-make-default-map
|
|
helpdesc
|
|
(list 'word-help-help-mode
|
|
'word-help-info-files
|
|
'word-help-keyword-regexps
|
|
'word-help-ignore-case
|
|
'word-help-index-mapper
|
|
'word-help-complete-list))))
|
|
(setq word-help-main-index nil
|
|
word-help-complete-index nil))))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;; Index collection ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
(defun word-help-extract-index (file-name index-list index-map ignore-case)
|
|
"Extract index from filename and the first node name in index list.
|
|
`file-name' is the name of the info file, while `index-list' is a list
|
|
of node-names to search."
|
|
(let (cmd1 cmdlow nodename ob-array next (case-fold-search word-help-ignore-case))
|
|
(setq nodename (car index-list))
|
|
(setq ob-array (make-vector 211 0))
|
|
(message "Processing \"%s\" in %s..." nodename file-name)
|
|
(save-window-excursion
|
|
(Info-goto-node (concat "(" file-name ")" nodename))
|
|
(end-of-buffer)
|
|
(while (re-search-backward "\\* \\([^\n:]+\\):" nil t)
|
|
(setq cmd1 (buffer-substring (match-beginning 1) (match-end 1)))
|
|
(setq cmdlow (if ignore-case (downcase cmd1) cmd1))
|
|
(if index-map
|
|
(setq cmdlow (word-help-map-index-entries cmdlow
|
|
index-map)))
|
|
;; We have to do this workaround to support case-insensitive matching
|
|
(cond
|
|
(cmdlow
|
|
(put (intern cmdlow ob-array) 'word-help-real-name cmd1)
|
|
(intern cmdlow word-help-main-obarray)))))
|
|
(setq next (cond
|
|
((cdr index-list)
|
|
(word-help-extract-index file-name (cdr index-list)
|
|
index-map ignore-case))))
|
|
(nconc (list (list nodename ob-array)) next)))
|
|
|
|
|
|
(defun word-help-collect-indexes (info-file)
|
|
"Process all the indexes in an info file.
|
|
|
|
Uses `word-help-extract-index' on each node, and returns an entry
|
|
suitable for merging into `word-help-process-indexes'. `info-file'
|
|
is an entry of the form
|
|
|
|
\(FILE-NAME INDEX-NAME-1 INDEX-NAME-2 ...)"
|
|
(let ((file (car info-file))
|
|
(nodes (cdr info-file)))
|
|
(nconc (list file) (word-help-extract-index file nodes
|
|
word-help-index-mapper
|
|
word-help-ignore-case))))
|
|
|
|
(defun word-help-process-indexes ()
|
|
"Process all the entries in the global variable `word-help-info-files'.
|
|
Returns a list formatted as follows:
|
|
|
|
\(all-entries-ob
|
|
(file-name-1 (node-name-1 this-node-entries-ob)
|
|
(node-name-2 this-node-entries-ob)
|
|
: : :
|
|
(node-name-n this-node-entries-ob))
|
|
(file-name-2 (node-name-1 this-node-entries-ob)
|
|
(node-name-2 this-node-entries-ob)
|
|
: : :
|
|
(node-name-n this-node-entries-ob))
|
|
: : : : : : : : :
|
|
(file-name-n (node-name-1 this-node-entries-ob)
|
|
(node-name-2 this-node-entries-ob)
|
|
: : :
|
|
(node-name-n this-node-entries-ob)))
|
|
|
|
The symbols in the obarrays may contain the additional property
|
|
`word-help-real-name', which tells the *real* node to go to.
|
|
|
|
Note that we use `word-help-index-alist' to speed up the process. Note
|
|
that `word-help-switch-help-file' must have been called before this function.
|
|
|
|
This structure is then later searched by `word-help-find-index-node'."
|
|
(let (index-words old-index)
|
|
(if (not word-help-main-index)
|
|
(cond
|
|
((setq old-index
|
|
(assoc word-help-help-mode word-help-index-alist))
|
|
(setq word-help-main-index (nth 1 old-index)))
|
|
(word-help-info-files
|
|
(setq word-help-main-obarray (make-vector 307 0)
|
|
index-words (mapcar 'word-help-collect-indexes
|
|
word-help-info-files)
|
|
word-help-main-index
|
|
(append (list word-help-main-obarray) index-words))
|
|
(setq word-help-index-alist (cons (list word-help-help-mode
|
|
word-help-main-index)
|
|
word-help-index-alist)))
|
|
(t (setq word-help-main-index t))))))
|
|
|
|
(defun word-help-find-help-file ()
|
|
"Tries to find and set a relevant help file for the current mode."
|
|
(let (helpguess)
|
|
(if (not word-help-info-files)
|
|
(if (setq helpguess (word-help-guess-help-file))
|
|
(word-help-switch-help-file helpguess)
|
|
(set-help-file)))))
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;; Keyword guess ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(defun word-help-guess-all (cur-point re-list
|
|
&optional copy-to-point)
|
|
"Guesses *all* keywords the user possibly may be looking at.
|
|
Returns a list of all possible keywords. "
|
|
(let ((regexp (car (car re-list)))
|
|
(submatch (cond ((nth 1 (car re-list))) (0)))
|
|
(cursmatch (cond ((nth 2 (car re-list))) (0)))
|
|
(guess nil)
|
|
(next-guess nil)
|
|
(case-fold-search word-help-ignore-case)
|
|
(end-point nil))
|
|
(save-excursion
|
|
(end-of-line)
|
|
(setq end-point (point))
|
|
;; Start at the beginning
|
|
(beginning-of-line)
|
|
(while (and (not guess) (re-search-forward regexp end-point t))
|
|
;; Look whether the cursor is within the match
|
|
(if (and (<= (match-beginning cursmatch) cur-point)
|
|
(>= (match-end cursmatch) cur-point))
|
|
(if (or (not copy-to-point) (<= cur-point (match-end submatch)))
|
|
(setq guess (buffer-substring (match-beginning submatch)
|
|
(if copy-to-point
|
|
cur-point
|
|
(match-end submatch)))))))
|
|
;; If we found anything, return it and call ourselves again
|
|
(if (cdr re-list)
|
|
(setq next-guess (word-help-guess-all cur-point (cdr re-list)
|
|
copy-to-point))))
|
|
(cons guess next-guess)))
|
|
|
|
(defun word-help-guess-match (all-match cmd-array)
|
|
(let ((sym (car all-match)))
|
|
(cond
|
|
((and sym (intern-soft (if word-help-ignore-case
|
|
(downcase sym)
|
|
sym) cmd-array)
|
|
sym))
|
|
((cdr all-match)
|
|
(word-help-guess-match (cdr all-match) cmd-array)))))
|
|
|
|
|
|
(defun word-help-guess (cur-point cmd-array re-list)
|
|
"Guesses what keyword the user is looking at, and returns that.
|
|
CUR-POINT should be the current value of `point', CMD-ARRAY an obarray
|
|
of all the keywords which are defined for the current mode, and
|
|
RE-LIST a list of regexps use for the hunt. See also
|
|
`word-help-keyword-regexps'."
|
|
(let ((all-matches (word-help-guess-all cur-point re-list)))
|
|
; (print all-matches)
|
|
(word-help-guess-match all-matches cmd-array)))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;; Show node for keyword ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;;; Find an index entry
|
|
|
|
(defun word-help-find-index-node (node index-reg)
|
|
"Finds the node named `node' in the index-register `index-reg'.
|
|
`index-reg' has the format as returned (and documented) by the
|
|
`word-help-process-indexes' call. In most cases, this will be equal to
|
|
`word-help-main-index'.
|
|
|
|
Returns a list with format
|
|
(file-name index-node-name index-entry)
|
|
which contains the file and index where the entry can be found.
|
|
Returns nil if the entry can't be found."
|
|
(let (file-info node-name)
|
|
(setq node-name (cond (word-help-ignore-case (downcase node)) (node)))
|
|
(if (intern-soft node-name (car index-reg))
|
|
(setq file-info (word-help-index-search-file node-name
|
|
(cdr index-reg))))
|
|
file-info))
|
|
|
|
(defun word-help-index-search-file (entry file-data)
|
|
"Searches a cached file for the index-entry `entry'."
|
|
(let (this-file next-files file-name node node-infos)
|
|
(setq this-file (car file-data)
|
|
next-files (cdr file-data)
|
|
file-name (car this-file)
|
|
node-infos (cdr this-file)
|
|
node (word-help-index-search-nodes entry node-infos))
|
|
(cond
|
|
(node
|
|
(cons file-name node))
|
|
(next-files (word-help-index-search-file entry next-files)))))
|
|
|
|
(defun word-help-index-search-nodes (entry node-info)
|
|
"Searches a cached list of nodes for the entry `entry'."
|
|
(let (this-node next-nodes node-name node-ob node-sym)
|
|
(setq this-node (car node-info)
|
|
next-nodes (cdr node-info)
|
|
node-name (car this-node)
|
|
node-ob (car (cdr this-node))
|
|
node-sym (intern-soft entry node-ob))
|
|
(cond
|
|
(node-sym
|
|
(list node-name (get node-sym 'word-help-real-name)))
|
|
(next-nodes (word-help-index-search-nodes entry next-nodes)))))
|
|
|
|
;;; Switch to a node in an index
|
|
|
|
(defun word-help-goto-index-node (index-info)
|
|
"Jumps to an index node.
|
|
`index-info' should be a list with the following format:
|
|
|
|
\(FILE-NAME INDEX-NODE-NAME INDEX-ENTRY KEYWORD)"
|
|
|
|
(let* ((file-name (car index-info))
|
|
(node-name (nth 1 index-info))
|
|
(entry-name (nth 2 index-info))
|
|
(kw-name (nth 3 index-info))
|
|
(buffer (current-buffer)))
|
|
(if word-help-split-window
|
|
(pop-to-buffer nil))
|
|
(Info-goto-node (concat "(" file-name ")" node-name))
|
|
(Info-menu entry-name)
|
|
;; Do magic keyword search
|
|
(if word-help-magic-index
|
|
(let (end-point regs this-re found entry-re)
|
|
(setq entry-re (regexp-quote kw-name)
|
|
regs (list (concat
|
|
(if (string-match "^[A-Za-z]" entry-name)
|
|
"\\<" "")
|
|
entry-re
|
|
(if (string-match "[A-Za-z]$" entry-name)
|
|
"\\>" ""))
|
|
(concat "[`\"\(]" entry-re)
|
|
(concat "^" entry-re
|
|
(if (string-match "[A-Za-z]$" entry-name)
|
|
"\\>" ""))))
|
|
(end-of-line)
|
|
(setq end-point (point))
|
|
(beginning-of-line)
|
|
(if (not (re-search-forward (car regs) end-point t))
|
|
(while (and (not found) (car regs))
|
|
(setq this-re (car regs)
|
|
regs (cdr regs)
|
|
found (re-search-forward this-re nil t))))
|
|
(recenter 0)))
|
|
(if word-help-split-window
|
|
(pop-to-buffer buffer))))
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Completion ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
|
|
(defun word-help-extract-matches (from-ob dest-ob re-list)
|
|
"Takes atoms from from-ob, and puts them in dest-ob if they match re-list."
|
|
(let ((regexp (car (car re-list))))
|
|
(mapatoms (lambda (x)
|
|
(if (or (not regexp) (string-match regexp (symbol-name x)))
|
|
(intern (symbol-name x) dest-ob)))
|
|
from-ob)
|
|
(if (cdr re-list)
|
|
(word-help-extract-matches from-ob dest-ob (cdr re-list))))
|
|
dest-ob)
|
|
|
|
(defun word-help-make-complete ()
|
|
"Generates the `word-help-complete-index'."
|
|
(if word-help-complete-index
|
|
nil
|
|
(word-help-find-help-file)
|
|
(cond
|
|
((symbolp word-help-complete-list)
|
|
(setq word-help-complete-index word-help-complete-list))
|
|
(t
|
|
(word-help-process-indexes)
|
|
(if (not (atom word-help-main-index))
|
|
(let ((from-ob (car word-help-main-index)))
|
|
(message "Processing keywords...")
|
|
(setq word-help-complete-index
|
|
(mapcar
|
|
(lambda (cmpl)
|
|
(let
|
|
((regexp (car cmpl))
|
|
(subm (cond ((nth 1 cmpl)) (0)))
|
|
(app (cond ((nth 2 cmpl)) ("")))
|
|
(re-list (cond ((nth 3 cmpl)) ('((".")))))
|
|
(obarr (make-vector 47 0)))
|
|
(list regexp subm subm app
|
|
(word-help-extract-matches from-ob obarr
|
|
re-list))))
|
|
word-help-complete-list))))))))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Misc. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
;;; Default mapping
|
|
|
|
(defun word-help-make-default-map (list vars)
|
|
"Makes a default mapping for `vars', which must be listed in order.
|
|
vars is a list of quoted symbols. If the nth entry in the list is
|
|
non-nil, the nth variable will be given this value. If nil, the var
|
|
will be given the global default value."
|
|
(set (car vars) (cond ((car list)) ((default-value (car vars)))))
|
|
(if (cdr vars)
|
|
(word-help-make-default-map (cdr list) (cdr vars))))
|
|
|
|
(provide 'word-help)
|
|
|
|
;;; word-help.el ends here
|