diff options
Diffstat (limited to 'lisp/textmodes/reftex-index.el')
-rw-r--r-- | lisp/textmodes/reftex-index.el | 1219 |
1 files changed, 1219 insertions, 0 deletions
diff --git a/lisp/textmodes/reftex-index.el b/lisp/textmodes/reftex-index.el new file mode 100644 index 00000000000..05c07bcaba0 --- /dev/null +++ b/lisp/textmodes/reftex-index.el @@ -0,0 +1,1219 @@ +;;; reftex-index.el - Index support with RefTeX +;;; Version: 4.5 +;;; +;;; See main file reftex.el for licensing information + +(provide 'reftex-index) +(require 'reftex) +;;; + +(defvar mark-active) +(defvar zmacs-regions) +(defun reftex-index-selection-or-word (&optional arg) + "Put selection or the word near point into the default index macro. +This uses the information in `reftex-index-default-macro' to make an index +entry. The phrase indexed is the current selection or the word near point. +When called with one `C-u' prefix, let the user have a chance to edit the +index entry. When called with 2 `C-u' as prefix, also ask for the index +macro and other stuff. +When called inside TeX math mode as determined by the `texmathp.el' library +which is part of AUCTeX, the string is first processed with the +`reftex-index-math-format', which see." + (interactive "P") + (let* ((use-default (not (equal arg '(16)))) ; check for double prefix + ;; check if we have an active selection + (active (if (boundp 'zmacs-regions) + (and zmacs-regions (region-exists-p)) ; XEmacs + (and transient-mark-mode mark-active))) ; Emacs + (beg (if active + (region-beginning) + (save-excursion + (skip-syntax-backward "w\\") (point)))) + (end (if active + (region-end) + (save-excursion + (skip-syntax-forward "w\\") (point)))) + (sel (buffer-substring beg end)) + (mathp (condition-case nil (texmathp) (error nil))) + (current-prefix-arg nil) ; we want to call reftex-index without prefix. + key def-char def-tag full-entry repeat-word) + + (if (equal sel "") + ;; Nothing selecte, no word, so use full reftex-index command + (reftex-index) + ;; OK, we have something to index here. + ;; Add the dollars when necessary + (setq key (if mathp + (format reftex-index-math-format sel) + sel)) + ;; Get info from `reftex-index-default-macro' + (setq def-char (if use-default (car reftex-index-default-macro))) + (setq def-tag (if use-default (nth 1 reftex-index-default-macro))) + ;; Does the user want to edit the entry? + (setq full-entry (if arg + (reftex-index-complete-key + def-tag nil (cons key 0)) + key)) + ;; Do we neet to repeat the word outside the macro? + (setq repeat-word (if use-default + (nth 2 reftex-index-default-macro) + (y-or-n-p "Repeat phrase outside macro? "))) + ;; Delete what is in the buffer and make the index entry + (delete-region beg end) + (reftex-index def-char full-entry def-tag (if repeat-word sel nil))))) + +(defun reftex-index (&optional char key tag postfix no-insert) + "Query for an index macro and insert it along with its argments. +The index macros available are those defined in `reftex-index-macro' or +by a call to `reftex-add-index-macros', typically from an AUCTeX style file. +RefteX provides completion for the index tag and the index key, and +will prompt for other arguments." + + (interactive) + + ;; Ensure access to scanning info + (reftex-ensure-index-support t) + (reftex-access-scan-info current-prefix-arg) + + ;; Find out which macro we are going to use + (let* ((char (or char + (reftex-select-with-char reftex-query-index-macro-prompt + reftex-query-index-macro-help))) + (macro (nth 1 (assoc char reftex-key-to-index-macro-alist))) + (entry (or (assoc macro reftex-index-macro-alist) + (error "No index macro associated with %c" char))) + (ntag (nth 1 entry)) + (tag (or tag (nth 1 entry))) + (nargs (nth 4 entry)) + (nindex (nth 5 entry)) + (opt-args (nth 6 entry)) + opt tag1 value) + + ;; Get the supported arguments + (if (stringp tag) + (setq tag1 tag) + (setq tag1 (or (reftex-index-complete-tag tag opt-args) ""))) + (setq key (or key + (reftex-index-complete-key + (if (string= tag1 "") "idx" tag1) + (member nindex opt-args)))) + + ;; Insert the macro and ask for any additional args + (insert macro) + (loop for i from 1 to nargs do + (setq opt (member i opt-args) + value (cond ((= nindex i) key) + ((equal ntag i) tag1) + (t (read-string (concat "Macro arg nr. " + (int-to-string i) + (if opt " (optional)" "") + ": "))))) + (unless (and opt (string= value "")) + (insert (if opt "[" "{") value (if opt "]" "}")))) + (and (stringp postfix) (insert postfix)) + (and key reftex-plug-into-AUCTeX (fboundp 'LaTeX-add-index-entries) + (LaTeX-add-index-entries key)) + (reftex-index-update-taglist tag1) + (reftex-notice-new))) + +(defun reftex-default-index () + (cond ((null reftex-index-default-tag) nil) + ((stringp reftex-index-default-tag) reftex-index-default-tag) + (t (or (get reftex-docstruct-symbol 'default-index-tag) + "idx")))) + +(defun reftex-update-default-index (tag &optional tag-list) + (if (and (not (equal tag "")) + (stringp tag) + (eq reftex-index-default-tag 'last) + (or (null tag-list) + (member tag tag-list))) + (put reftex-docstruct-symbol 'default-index-tag tag))) + +(defun reftex-index-complete-tag (&optional itag opt-args) + ;; Ask the user for a tag, completing on known tags. + ;; ITAG is the argument number which contains the tag. + ;; OPT-ARGS is a list of optional argument indices, as given by + ;; `reftex-parse-args'. + (let* ((opt (and (integerp itag) (member itag opt-args))) + (index-tags (cdr (assq 'index-tags + (symbol-value reftex-docstruct-symbol)))) + (default (reftex-default-index)) + (prompt (concat "Index tag" + (if default (format " (default: %s)" default) "") + (if opt " (optional)" "") ": ")) + (tag (completing-read prompt (mapcar 'list index-tags)))) + (if (and default (equal tag "")) (setq tag default)) + (reftex-update-default-index tag) + tag)) + +(defun reftex-index-select-tag () + ;; Have the user select an index tag. + ;; FIXME: should we cache tag-alist, prompt and help? + (let* ((index-tags (cdr (assoc 'index-tags + (symbol-value reftex-docstruct-symbol)))) + (default (reftex-default-index))) + (cond + ((null index-tags) + (error "No index tags available")) + + ((= (length index-tags) 1) + ;; Just one index, use it + (car index-tags)) + + ((> (length index-tags) 1) + ;; Several indices, ask. + (let* ((tags (copy-sequence index-tags)) + (cnt 0) + tag-alist i val len tag prompt help rpl) + ;; Move idx and glo up in the list to ensure ?i and ?g shortcuts + (if (member "glo" tags) + (setq tags (cons "glo" (delete "glo" tags)))) + (if (member "idx" tags) + (setq tags (cons "idx" (delete "idx" tags)))) + ;; Find unique shortcuts for each index. + (while (setq tag (pop tags)) + (setq len (length tag) + i -1 + val nil) + (catch 'exit + (while (and (< (incf i) len) (null val)) + (unless (assq (aref tag i) tag-alist) + (push (list (aref tag i) + tag + (concat (substring tag 0 i) + "[" (substring tag i (incf i)) "]" + (substring tag i))) + tag-alist) + (throw 'exit t))) + (push (list (+ ?0 (incf cnt)) tag + (concat "[" (int-to-string cnt) "]:" tag)) + tag-alist))) + (setq tag-alist (nreverse tag-alist)) + ;; Compute Prompt and Help strings + (setq prompt + (concat + (format "Select Index%s: " + (if default (format " (Default <%s>)" default) "")) + (mapconcat (lambda(x) (nth 2 x)) tag-alist " "))) + (setq help + (concat "Select an Index\n===============\n" + (if default + (format "[^M] %s (the default)\n" default) + "") + (mapconcat (lambda(x) + (apply 'format "[%c] %s" x)) + tag-alist "\n"))) + ;; Query the user for an index-tag + (setq rpl (reftex-select-with-char prompt help 3 t)) + (message "") + (if (and default (equal rpl ?\C-m)) + default + (if (assq rpl tag-alist) + (progn + (reftex-update-default-index (nth 1 (assq rpl tag-alist))) + (nth 1 (assq rpl tag-alist))) + (error "No index tag associated with %c" rpl))))) + (t (error "This should not happen (reftex-index-select-tag)"))))) + +(defun reftex-index-complete-key (&optional tag optional initial) + ;; Read an index key, with completion. + ;; Restrict completion table on index tag TAG. + ;; OPTIONAL indicates if the arg is optional. + (let* ((table (reftex-sublist-nth + (symbol-value reftex-docstruct-symbol) 6 + (lambda(x) (and (eq (car x) 'index) + (string= (nth 1 x) (or tag "")))) + t)) + (prompt (concat "Index key" (if optional " (optional)" "") ": ")) + (key (completing-read prompt table nil nil initial))) + key)) + +(defun reftex-index-update-taglist (newtag) + ;; add NEWTAG to the list of available index tags. + (let ((cell (assoc 'index-tags (symbol-value reftex-docstruct-symbol)))) + (and newtag (cdr cell) (not (member newtag (cdr cell))) + (push newtag (cdr cell))))) + +(defvar reftex-last-index-file) +(defun reftex-index-globally (&optional data call-file) + "Index a word with a global search and replace. +This works very much like `reftex-query-replace-document', but the +defaults for the search and replace strings are derived from +local context. +When there is an index entry, we try to index similar words. The word +to search for is either a word in direct contact with the index macro +(like `\\index{WORD}WORD' or `WORD\\index{WORD}') or the index key. +The replacement text is the index macro with all its arguments and the +attached word. +When there is no index entry at point, we search for the word near point +and propose to index it like this: `\\index{word}word'. +You get a chance to edit the search and replacement strings. +DATA can be a docstruct entry describing an index entry, and then the +defaults will be derived from it. +CALL-FILE may be the file from where to call the global search command." + (interactive) + (let* ((call-file (cond (call-file call-file) + (reftex-mode (buffer-file-name)) + ((eq major-mode 'reftex-index-mode) + reftex-last-index-file) + (t (error "Need a call file here")))) + (pos (point)) + (data (cond + (data data) + ((and reftex-mode + (save-excursion + (forward-char 20) + (re-search-backward reftex-everything-regexp nil t) + (< (count-lines (min pos (point)) (max pos (point))) + 2))) + (reftex-index-info (buffer-file-name))) + (t nil))) + (ksep (car reftex-index-special-chars)) + (words-include-escapes t) + (case-replace nil) + (case-fold-search t) + word rpl start analyze-list pre key attr actual post) + + ;; Find the word and construct the replacement string + (if (and data (eq (car data) 'index)) + ;; OK, we have an index entry + (progn + (setq analyze-list (reftex-index-analyze-entry data) + pre (car analyze-list) + key (nth 1 analyze-list) + attr (nth 2 analyze-list) + actual (nth 3 analyze-list) + post (nth 4 analyze-list)) + (when (string-match (concat "\\<\\(\\sw+\\)" reftex-index-re) pre) + (setq word (match-string 1 pre) + pre (concat "<<<1>>>" (substring pre (match-end 1))) + rpl (concat pre key attr actual post))) + (when (string-match "}\\(\\sw+\\)\\>[^}]*\\'" post) + (setq word (match-string 1 post) + post (concat (substring post 0 (match-beginning 1)) + "<<<1>>>") + rpl (concat pre key attr actual post))) + (when (and (not word) key) + (if (string-match (concat ".*" (regexp-quote ksep)) key) + (setq word (substring key (match-end 0))) + (setq word key)) + (setq rpl (concat pre key attr actual post)))) + ;; No index entry, just use local word. + (setq word (save-excursion + (buffer-substring-no-properties + (progn (skip-syntax-backward "w") (point)) + (progn (skip-syntax-forward "w") (point)))) + rpl (concat "\\index{" word "}<<<1>>>"))) + ;; Quote what is necessary + (setq word (regexp-quote (downcase word))) + (setq start 0) + (while (setq start (string-match "\\\\" rpl start)) + (setq rpl (replace-match "\\\\" t t rpl) + start (+ 2 start))) + ;; We used <<<1>>> instead of \1 to avoid the quoting. Fix this now. + (if (string-match "<<<1>>>" rpl) + (setq rpl (replace-match "\\1" t t rpl))) + + ;; Give the user a chance to edit the strings + (setq word (read-string "Search: " + (if word (format "\\<\\(%s\\)\\>" word))) + rpl (read-string "Replace with: " rpl)) + + ;; Execute the command + (save-excursion + (switch-to-buffer (get-file-buffer call-file)) + (condition-case nil + (reftex-query-replace-document word rpl) + (error nil))))) + +(defvar reftex-index-map (make-sparse-keymap) + "Keymap used for *Index* buffers.") + +(defvar reftex-index-menu) + +(defvar reftex-last-index-file nil + "Stores the file name from which `reftex-display-index' was called.") +(defvar reftex-index-tag nil + "Stores the tag of the index in an index buffer.") + +(defvar reftex-index-return-marker (make-marker) + "Marker which makes it possible to return from index to old position.") + +(defvar reftex-index-restriction-indicator nil) +(defvar reftex-index-restriction-data nil) + +(defun reftex-index-mode () + "Major mode for managing Index buffers for LaTeX files. +This buffer was created with RefTeX. +Press `?' for a summary of important key bindings, or check the menu. + +Here are all local bindings. + +\\{reftex-index-map}" + (interactive) + (kill-all-local-variables) + (setq major-mode 'reftex-index-mode + mode-name "RefTeX Index") + (use-local-map reftex-index-map) + (set (make-local-variable 'revert-buffer-function) 'reftex-index-revert) + (set (make-local-variable 'reftex-index-restriction-data) nil) + (set (make-local-variable 'reftex-index-restriction-indicator) nil) + (setq mode-line-format + (list "---- " 'mode-line-buffer-identification + " " 'global-mode-string + " R<" 'reftex-index-restriction-indicator ">" + " -%-")) + (setq truncate-lines t) + (make-local-hook 'post-command-hook) + (make-local-hook 'pre-command-hook) + (make-local-variable 'reftex-last-follow-point) + (easy-menu-add reftex-index-menu reftex-index-map) + (add-hook 'post-command-hook 'reftex-index-post-command-hook nil t) + (add-hook 'pre-command-hook 'reftex-index-pre-command-hook nil t) + (run-hooks 'reftex-index-mode-hook)) + +(defconst reftex-index-help +" AVAILABLE KEYS IN INDEX BUFFER + ============================== +! A..Z Goto the section of entries starting with this letter. +n / p next-entry / previous-entry +SPC / TAB Show/Goto the corresponding entry in the LaTeX document. +RET Goto the entry and hide the *Index* window (also on mouse-2). +q / k Hide/Kill *Index* buffer. +C-c = Switch to the TOC buffer. +f / c Toggle follow mode / Toggle display of [c]ontext. +g Refresh *Index* buffer. +r / C-u r Reparse the LaTeX document / Reparse entire LaTeX document. +s Switch to a different index (for documents with multiple indices). +e / C-k Edit/Kill the entry. +* | @ Edit specific part of entry: [*]key [|]attribute [@]visual + With prefix: kill that part. +( ) Toggle entry's beginning/end of page range property. +_ ^ Add/Remove parent key (to make this item a subitem). +& Index the same word everywhere in the document. +} / { Restrict Index to a single document section / Widen. +< / > When restricted, move restriction to previous/next section.") + +(defun reftex-index-show-entry (data &optional no-revisit) + ;; Find an index entry associated with DATA and display it highlighted + ;; in another window. NO-REVISIT means we are not allowed to visit + ;; files for this. + ;; Note: This function just looks for the nearest match of the + ;; context string and may fail if the entry moved and an identical + ;; entry is close to the old position. Frequent rescans make this + ;; safer. + (let* ((file (nth 3 data)) + (literal (nth 2 data)) + (pos (nth 4 data)) + (re (regexp-quote literal)) + (match + (cond + ((or (not no-revisit) + (reftex-get-buffer-visiting file)) + (switch-to-buffer-other-window + (reftex-get-file-buffer-force file nil)) + (goto-char (or pos (point-min))) + (or (looking-at re) + (reftex-nearest-match re (length literal)))) + (t (message reftex-no-follow-message) nil)))) + (when match + (goto-char (match-beginning 0)) + (recenter '(4)) + (reftex-highlight 0 (match-beginning 0) (match-end 0) (current-buffer))) + match)) + +(defun reftex-display-index (&optional tag overriding-restriction + &rest locations) + "Display a buffer with an index compiled from the current document. +When the document has multiple indices, first prompts for the correct one. +When index support is turned off, offer to turn it on. +With one or two `C-u' prefixes, rescan document first. +With prefix 2, restrict index to current document section. +With prefix 3, restrict index to region." + + (interactive) + + ;; Ensure access to scanning info and rescan buffer if prefix are is '(4). + (let ((current-prefix-arg current-prefix-arg)) + (reftex-ensure-index-support t) + (reftex-access-scan-info current-prefix-arg)) + + (set-marker reftex-index-return-marker (point)) + (setq reftex-last-follow-point 1) + + ;; Determine the correct index to process + (let* ((docstruct (symbol-value reftex-docstruct-symbol)) + (docstruct-symbol reftex-docstruct-symbol) + (index-tag (or tag (reftex-index-select-tag))) + (master (reftex-TeX-master-file)) + (calling-file (buffer-file-name)) + (restriction + (or overriding-restriction + (and (interactive-p) + (reftex-get-restriction current-prefix-arg docstruct)))) + (locations + ;; See if we are on an index macro as initial position + (or locations + (let* ((what-macro (reftex-what-macro-safe 1)) + (macro (car what-macro)) + (here-I-am (when (member macro reftex-macros-with-index) + (save-excursion + (goto-char (+ (cdr what-macro) + (length macro))) + (reftex-move-over-touching-args) + (reftex-where-am-I))))) + (if (eq (car (car here-I-am)) 'index) + (list (car here-I-am)))))) + buffer-name) + + (setq buffer-name (reftex-make-index-buffer-name index-tag)) + + ;; Goto the buffer and put it into the correct mode + + (when (or restriction current-prefix-arg) + (reftex-kill-buffer buffer-name)) + + (if (get-buffer-window buffer-name) + (select-window (get-buffer-window buffer-name)) + (let ((default-major-mode 'reftex-index-mode)) + (switch-to-buffer buffer-name))) + + (or (eq major-mode 'reftex-index-mode) (reftex-index-mode)) + + ;; If the buffer is currently restricted, empty it to force update. + (when reftex-index-restriction-data + (reftex-erase-buffer)) + (set (make-local-variable 'reftex-last-index-file) calling-file) + (set (make-local-variable 'reftex-index-tag) index-tag) + (set (make-local-variable 'reftex-docstruct-symbol) docstruct-symbol) + (if restriction + (setq reftex-index-restriction-indicator (car restriction) + reftex-index-restriction-data (cdr restriction)) + (if (interactive-p) + (setq reftex-index-restriction-indicator nil + reftex-index-restriction-data nil))) + (when (= (buffer-size) 0) + ;; buffer is empty - fill it + (message "Building %s buffer..." buffer-name) + + (setq buffer-read-only nil) + (insert (format +"INDEX <%s> on %s +Restriction: <%s> +SPC=view TAB=goto RET=goto+hide [e]dit [q]uit [r]escan [f]ollow [?]Help +------------------------------------------------------------------------------ +" index-tag (abbreviate-file-name master) +(if (eq (car (car reftex-index-restriction-data)) 'toc) + (nth 2 (car reftex-index-restriction-data)) + reftex-index-restriction-indicator))) + + (if (reftex-use-fonts) + (put-text-property 1 (point) 'face reftex-index-header-face)) + (put-text-property 1 (point) 'intangible t) + + (reftex-insert-index docstruct index-tag) + (goto-char (point-min)) + (run-hooks 'reftex-display-copied-context-hook) + (message "Building %s buffer...done." buffer-name) + (setq buffer-read-only t)) + (and locations (apply 'reftex-find-start-point (point) locations)) + (if reftex-index-restriction-indicator + (message "Index restricted: <%s>" reftex-index-restriction-indicator)))) + +(defun reftex-insert-index (docstruct tag &optional update-one remark) + ;; Insert an index into the current buffer. Entries are from the + ;; DOCSTRUCT. + ;; TAG is the subindex to process. + ;; UPDATE-ONE: When non-nil, delete the entry at point and replace + ;; it with whatever the DOCSTRUCT contains. + ;; REMARK can be a note to add to the entry. + (let* ((all docstruct) + (indent " ") + (context reftex-index-include-context) + (context-indent (concat indent " ")) + (section-chars (mapcar 'identity reftex-index-section-letters)) + (this-section-char 0) + (font (reftex-use-fonts)) + (bor (car reftex-index-restriction-data)) + (eor (nth 1 reftex-index-restriction-data)) + (mouse-face + (if (memq reftex-highlight-selection '(mouse both)) + reftex-mouse-selected-face + nil)) + (index-face (reftex-verified-face reftex-label-face + 'font-lock-constant-face + 'font-lock-reference-face)) + sublist cell from to first-char) + + ;; Make the sublist and sort it + (when bor + (setq all (or (memq bor all) all))) + + (while (setq cell (pop all)) + (if (eq cell eor) + (setq all nil) + (and (eq (car cell) 'index) + (equal (nth 1 cell) tag) + (push cell sublist)))) + (setq sublist (sort (nreverse sublist) + (lambda (a b) (string< (nth 8 a) (nth 8 b))))) + + (when update-one + ;; Delete the entry at place + (and (bolp) (forward-char 1)) + (delete-region (previous-single-property-change (1+ (point)) :data) + (or (next-single-property-change (point) :data) + (point-max)))) + + ;; Walk through the list and insert all entries + (while (setq cell (pop sublist)) + (unless update-one + (setq first-char (upcase (string-to-char (nth 6 cell)))) + (when (and (not (equal first-char this-section-char)) + (member first-char section-chars)) + ;; There is a new initial letter, so start a new section + (reftex-index-insert-new-letter first-char font) + (setq section-chars (delete first-char section-chars) + this-section-char first-char)) + (when (= this-section-char 0) + (setq this-section-char ?!) + (reftex-index-insert-new-letter this-section-char font))) + + (setq from (point)) + (insert indent (nth 7 cell)) + (when font + (setq to (point)) + (put-text-property + (- (point) (length (nth 7 cell))) to + 'face index-face) + (goto-char to)) + + (when (or remark (nth 9 cell)) + (and (< (current-column) 40) + ;; FIXME: maybe this is too slow? + (insert (make-string (max (- 40 (current-column)) 0) ?\ ))) + (and (nth 9 cell) (insert " " (substring (nth 5 cell) (nth 9 cell)))) + (and remark (insert " " remark))) + + (insert "\n") + (setq to (point)) + + (when context + (insert context-indent (nth 2 cell) "\n") + (setq to (point))) + (put-text-property from to :data cell) + (when mouse-face + (put-text-property from (1- to) + 'mouse-face mouse-face)) + (goto-char to)))) + + +(defun reftex-index-insert-new-letter (letter &optional font) + ;; Start a new section in the index + (let ((from (point))) + (insert "\n" letter letter letter + "-----------------------------------------------------------------") + (when font + (put-text-property from (point) 'face reftex-index-section-face)) + (insert "\n"))) + +(defun reftex-get-restriction (arg docstruct) + ;; Interprete the prefix ARG and derive index restriction specs. + (let* ((beg (min (point) (or (condition-case nil (mark) (error nil)) + (point-max)))) + (end (max (point) (or (condition-case nil (mark) (error nil)) + (point-min)))) + bor eor label here-I-am) + (cond + ((eq arg 2) + (setq here-I-am (car (reftex-where-am-I)) + bor (if (eq (car here-I-am) 'toc) + here-I-am + (reftex-last-assoc-before-elt + 'toc here-I-am docstruct)) + eor (car (memq (assq 'toc (cdr (memq bor docstruct))) docstruct)) + label (nth 6 bor))) + ((eq arg 3) + (save-excursion + (setq label "region") + (goto-char beg) + (setq bor (car (reftex-where-am-I))) + (setq bor (nth 1 (memq bor docstruct))) + (goto-char end) + (setq eor (nth 1 (memq (car (reftex-where-am-I)) docstruct))))) + (t nil)) + (if (and label (or bor eor)) + (list label bor eor) + nil))) + +(defun reftex-index-pre-command-hook () + ;; Used as pre command hook in *Index* buffer + (reftex-unhighlight 0) + (reftex-unhighlight 1)) + +(defun reftex-index-post-command-hook () + ;; Used in the post-command-hook for the *Index* buffer + (when (get-text-property (point) :data) + (and (> (point) 1) + (not (get-text-property (point) 'intangible)) + (memq reftex-highlight-selection '(cursor both)) + (reftex-highlight 1 + (or (previous-single-property-change (1+ (point)) :data) + (point-min)) + (or (next-single-property-change (point) :data) + (point-max))))) + (if (integerp reftex-index-follow-mode) + ;; Remove delayed action + (setq reftex-index-follow-mode t) + (and reftex-index-follow-mode + (not (equal reftex-last-follow-point (point))) + ;; Show context in other window + (setq reftex-last-follow-point (point)) + (condition-case nil + (reftex-index-visit-location nil (not reftex-revisit-to-follow)) + (error t))))) + +(defun reftex-index-show-help () + "Show a summary of special key bindings." + (interactive) + (with-output-to-temp-buffer "*RefTeX Help*" + (princ reftex-index-help)) + (reftex-enlarge-to-fit "*RefTeX Help*" t) + ;; If follow mode is active, arrange to delay it one command + (if reftex-index-follow-mode + (setq reftex-index-follow-mode 1))) + +(defun reftex-index-next (&optional arg) + "Move to next selectable item." + (interactive "p") + (setq reftex-callback-fwd t) + (or (eobp) (forward-char 1)) + (goto-char (or (next-single-property-change (point) :data) + (point))) + (unless (get-text-property (point) :data) + (goto-char (or (next-single-property-change (point) :data) + (point))))) +(defun reftex-index-previous (&optional arg) + "Move to previous selectable item." + (interactive "p") + (setq reftex-callback-fwd nil) + (goto-char (or (previous-single-property-change (point) :data) + (point))) + (unless (get-text-property (point) :data) + (goto-char (or (previous-single-property-change (point) :data) + (point))))) +(defun reftex-index-toggle-follow () + "Toggle follow (other window follows with context)." + (interactive) + (setq reftex-last-follow-point -1) + (setq reftex-index-follow-mode (not reftex-index-follow-mode))) +(defun reftex-index-toggle-context () + "Toggle inclusion of label context in *Index* buffer. +Label context is only displayed when the labels are there as well." + (interactive) + (setq reftex-index-include-context (not reftex-index-include-context)) + (reftex-index-revert)) +(defun reftex-index-view-entry () + "View document location in other window." + (interactive) + (reftex-index-visit-location)) +(defun reftex-index-goto-entry-and-hide () + "Go to document location in other window. Hide the *Index* window." + (interactive) + (reftex-index-visit-location 'hide)) +(defun reftex-index-goto-entry () + "Go to document location in other window. *Index* window stays." + (interactive) + (reftex-index-visit-location t)) +(defun reftex-index-mouse-goto-line-and-hide (ev) + "Go to document location in other window. Hide the *Index* window." + (interactive "e") + (mouse-set-point ev) + (reftex-index-visit-location 'hide)) +(defun reftex-index-quit () + "Hide the *Index* window and do not move point." + (interactive) + (or (one-window-p) (delete-window)) + (switch-to-buffer (marker-buffer reftex-index-return-marker)) + (goto-char (or (marker-position reftex-index-return-marker) (point)))) +(defun reftex-index-quit-and-kill () + "Kill the *Index* buffer." + (interactive) + (kill-buffer (current-buffer)) + (or (one-window-p) (delete-window)) + (switch-to-buffer (marker-buffer reftex-index-return-marker)) + (goto-char (or (marker-position reftex-index-return-marker) (point)))) +(defun reftex-index-goto-toc (&rest ignore) + "Switch to the table of contents of the current document. +The function will go to the section where the entry at point was defined." + (interactive) + (if (get-text-property (point) :data) + (reftex-index-goto-entry) + (switch-to-buffer (marker-buffer reftex-index-return-marker))) + (delete-other-windows) + (reftex-toc)) +(defun reftex-index-rescan (&rest ignore) + "Regenerate the *Index* buffer after reparsing file of section at point." + (interactive) + (let ((index-tag reftex-index-tag)) + (if (and reftex-enable-partial-scans + (null current-prefix-arg)) + (let* ((data (get-text-property (point) :data)) + (file (nth 3 data)) + (line (+ (count-lines (point-min) (point)) (if (bolp) 1 0)))) + (if (not file) + (error "Don't know which file to rescan. Try `C-u r'") + (switch-to-buffer (reftex-get-file-buffer-force file)) + (setq current-prefix-arg '(4)) + (reftex-display-index index-tag nil line))) + (reftex-index-Rescan)) + (reftex-kill-temporary-buffers))) +(defun reftex-index-Rescan (&rest ignore) + "Regenerate the *Index* buffer after reparsing the entire document." + (interactive) + (let ((index-tag reftex-index-tag) + (line (+ (count-lines (point-min) (point)) (if (bolp) 1 0)))) + (switch-to-buffer + (reftex-get-file-buffer-force reftex-last-index-file)) + (setq current-prefix-arg '(16)) + (reftex-display-index index-tag nil line))) +(defun reftex-index-revert (&rest ignore) + "Regenerate the *Index* from the internal lists. No reparsing os done." + (interactive) + (let ((buf (current-buffer)) + (index-tag reftex-index-tag) + (data (get-text-property (point) :data)) + (line (+ (count-lines (point-min) (point)) (if (bolp) 1 0)))) + (switch-to-buffer + (reftex-get-file-buffer-force reftex-last-index-file)) + (reftex-erase-buffer buf) + (setq current-prefix-arg nil + reftex-last-follow-point 1) + (reftex-display-index index-tag nil data line))) +(defun reftex-index-switch-index-tag (&rest ignore) + "Switch to a different index of the same document." + (interactive) + (switch-to-buffer + (reftex-get-file-buffer-force reftex-last-index-file)) + (setq current-prefix-arg nil) + (reftex-display-index)) + +(defun reftex-index-restrict-to-section (&optional force) + "Restrict index to entries defined in same document sect. as entry at point." + ;; Optional FORCE means, even if point is not on an index entry. + (interactive) + (let* ((data (get-text-property (point) :data)) + (docstruct (symbol-value reftex-docstruct-symbol)) + bor eor) + (if (and (not data) force) + (setq data (assq 'toc docstruct))) + (when data + (setq bor (reftex-last-assoc-before-elt 'toc data docstruct) + eor (car (memq (assq 'toc (cdr (memq bor docstruct))) + docstruct)) + reftex-index-restriction-data (list bor eor) + reftex-index-restriction-indicator (nth 6 bor) ))) + (reftex-index-revert)) + +(defun reftex-index-widen (&rest ignore) + "Show the unrestricted index (all entries)." + (interactive) + (setq reftex-index-restriction-indicator nil + reftex-index-restriction-data nil) + (reftex-index-revert) + (message "Index widened")) +(defun reftex-index-restriction-forward (&rest ignore) + "Restrict to previous section. +When index is currently unrestricted, restrict it to a section. +When index is restricted, select the next section as restriction criterion." + (interactive) + (let* ((docstruct (symbol-value reftex-docstruct-symbol)) + (bor (nth 1 reftex-index-restriction-data))) + (if (or (not bor) + (not (eq (car bor) 'toc))) + (reftex-index-restrict-to-section t) + (setq reftex-index-restriction-indicator (nth 6 bor) + reftex-index-restriction-data + (list bor + (car (memq (assq 'toc (cdr (memq bor docstruct))) + docstruct)))) + (reftex-index-revert)))) +(defun reftex-index-restriction-backward (&rest ignore) + "Restrict to next section. +When index is currently unrestricted, restrict it to a section. +When index is restricted, select the previous section as restriction criterion." + (interactive) + (let* ((docstruct (symbol-value reftex-docstruct-symbol)) + (eor (car reftex-index-restriction-data)) + (bor (reftex-last-assoc-before-elt 'toc eor docstruct t))) + (if (or (not bor) + (not (eq (car bor) 'toc))) + (reftex-index-restrict-to-section t) + (setq reftex-index-restriction-indicator (nth 6 bor) + reftex-index-restriction-data + (list bor eor)) + (reftex-index-revert)))) + +(defun reftex-index-visit-location (&optional final no-revisit) + ;; Visit the tex file corresponding to the index entry on the current line. + ;; If FINAL is t, stay there + ;; If FINAL is 'hide, hide the *Index* window. + ;; Otherwise, move cursor back into *Index* window. + ;; NO-REVISIT means don't visit files, just use live biffers. + + (let* ((data (get-text-property (point) :data)) + (index-window (selected-window)) + show-window show-buffer match) + + (unless data (error "Don't know which index entry to visit")) + + (if (eq (car data) 'index) + (setq match (reftex-index-show-entry data no-revisit))) + + (setq show-window (selected-window) + show-buffer (current-buffer)) + + (unless match + (select-window index-window) + (error "Cannot find location")) + + (select-window index-window) + + ;; Use the `final' parameter to decide what to do next + (cond + ((eq final t) + (reftex-unhighlight 0) + (select-window show-window)) + ((eq final 'hide) + (reftex-unhighlight 0) + (or (one-window-p) (delete-window)) + (switch-to-buffer show-buffer)) + (t nil)))) + +(defun reftex-index-analyze-entry (data) + ;; This splits the index context so that key, attribute and visual + ;; values are accessible individually. + (interactive) + (let* ((arg (nth 5 data)) + (context (nth 2 data)) + (sc reftex-index-special-chars) + (boa (if (string-match (regexp-quote (concat "{" arg "}")) context) + (1+ (match-beginning 0)) + (error "Something is wrong here"))) + (eoa (1- (match-end 0))) + (boactual (if (string-match (concat "[^" (nth 3 sc) "]" (nth 2 sc)) + context boa) + (1+ (match-beginning 0)) + eoa)) + (boattr (if (string-match (concat "[^" (nth 3 sc) "]" (nth 1 sc)) + context boa) + (1+ (match-beginning 0)) + boactual)) + (pre (substring context 0 boa)) + (key (substring context boa boattr)) + (attr (substring context boattr boactual)) + (actual (substring context boactual eoa)) + (post (substring context eoa))) + (list pre key attr actual post))) + +(defun reftex-index-globalize (&optional arg) + "Globalize the current index entry. +This starts a global search and replace to index the same word +at other places in the document. After this function completes, you +need to rescan the document with `r' or `C-u r' in order to get the +entries into the index buffer. +Defaults for the search and replace strings are derived from +the current entry. See the command `reftex-index-globally'." + (interactive) + (let* ((data (get-text-property (point) :data)) + (buf (current-buffer))) + (unless data + (error "No index entry at point")) + (reftex-index-globally data) + (switch-to-buffer buf))) + +(defun reftex-index-edit () + "Edit the index entry at point." + (interactive) + (let* ((data (get-text-property (point) :data)) + old new) + (unless data (error "Don't know which index entry to edit")) + (reftex-index-view-entry) + (setq old (nth 2 data) new (read-string "Edit: " old)) + (reftex-index-change-entry new))) + +(defun reftex-index-toggle-range-beginning () + "Toggle the page range start attribute `|('." + (interactive) + (let* ((data (get-text-property (point) :data)) + (bor (concat (nth 1 reftex-index-special-chars) "(")) + new analyze attr) + (unless data (error "Don't know which index entry to edit")) + (setq analyze (reftex-index-analyze-entry data) + attr (nth 2 analyze)) + (setf (nth 2 analyze) (if (string= attr bor) "" bor)) + (setq new (apply 'concat analyze)) + (reftex-index-change-entry + new (if (string= (nth 2 analyze) bor) + "Entry is now START-OF-PAGE-RANGE" + "START-OF-PAGE-RANGE canceled")))) + +(defun reftex-index-toggle-range-end () + "Toggle the page-range-end attribute `|)'." + (interactive) + (let* ((data (get-text-property (point) :data)) + (eor (concat (nth 1 reftex-index-special-chars) "(")) + new analyze attr) + (unless data (error "Don't know which index entry to edit")) + (setq analyze (reftex-index-analyze-entry data) + attr (nth 2 analyze)) + (setf (nth 2 analyze) (if (string= attr eor) "" eor)) + (setq new (apply 'concat analyze)) + (reftex-index-change-entry + new (if (string= (nth 2 analyze) eor) + "Entry is now END-OF-PAGE-RANGE" + "END-OF-PAGE-RANGE canceled")))) + +(defun reftex-index-edit-key () + "Edit the KEY part of the index entry." + (interactive) + (reftex-index-edit-part nil 1 "" "Key: " t)) + +(defun reftex-index-edit-attribute (&optional arg) + "EDIT the ATTRIBUTE part of the entry. With arg: remove entire ATTRIBUTE." + (interactive "P") + (reftex-index-edit-part arg 2 (nth 1 reftex-index-special-chars) + "Attribute: ")) + +(defun reftex-index-edit-visual (&optional arg) + "EDIT the VISUAL part of the entry. With arg: remove entire VISUAL string." + (interactive "P") + (reftex-index-edit-part arg 3 (nth 2 reftex-index-special-chars) "Visual: ")) + +(defun reftex-index-edit-part (arg n initial prompt &optional dont-allow-empty) + ;; This function does the work for all partial editing commands + (let* ((data (get-text-property (point) :data)) + new analyze opart npart) + (unless data (error "Don't know which index entry to edit")) + ;; Analyze the whole context string + (setq analyze (reftex-index-analyze-entry data) + opart (nth n analyze)) + (and (> (length opart) 0) (setq opart (substring opart 1))) + ;; Have the user editing the part + (setq npart (if arg "" (read-string (concat prompt initial) opart))) + ;; Tests: + (cond ((string= npart opart) + (error "Not changed")) + ((string= npart "") + (if dont-allow-empty + (error "Illegal value") + (setf (nth n analyze) npart))) + (t (setf (nth n analyze) (concat initial npart)))) + (setq new (apply 'concat analyze)) + ;; Change the entry and insert the changed version into the index. + (reftex-index-change-entry + new (if (string= npart "") + (format "Deleted: %s" opart) + (format "New value is: %s" npart))))) + +(defun reftex-index-level-down () + "Make index entry a subitem of another entry." + (interactive) + (let* ((data (get-text-property (point) :data)) + (docstruct (symbol-value reftex-docstruct-symbol)) + old new prefix key) + (unless data (error "Don't know which index entry to change")) + (setq old (nth 2 data) + key (nth 6 data) + prefix (completing-read + "Prefix: " + (reftex-sublist-nth + docstruct 6 + (lambda (x) + (and (eq (car x) 'index) + (string= (nth 1 x) reftex-index-tag))) t))) + (unless (string-match + (concat (regexp-quote (car reftex-index-special-chars)) "\\'") + prefix) + (setq prefix (concat prefix (car reftex-index-special-chars)))) + (if (string-match (regexp-quote key) old) + (setq new (replace-match (concat prefix key) t t old)) + (error "Cannot construct new index key")) + (reftex-index-change-entry new (format "Added prefix: %s" prefix)))) + +(defun reftex-index-level-up () + "Remove the highest level of a hierarchical index entry." + (interactive) + (let* ((data (get-text-property (point) :data)) + old new prefix) + (unless data (error "Don't know which entry to change")) + (setq old (nth 2 data)) + (if (string-match (concat "{\\([^" (nth 0 reftex-index-special-chars) "]*" + "[^" (nth 3 reftex-index-special-chars) "]" + (regexp-quote (nth 0 reftex-index-special-chars)) + "\\)") + old) + (setq prefix (substring old (match-beginning 1) (match-end 1)) + new (concat (substring old 0 (match-beginning 1)) + (substring old (match-end 1)))) + (error "Entry is not a subitem")) + (reftex-index-change-entry new (format "Removed prefix: %s" prefix)))) + +(defun reftex-index-kill () + "FIXME: Not yet implemented" + (interactive) + (error "This function is currently not implemented")) + +(defun reftex-index-undo () + "FIXME: Not yet implemented" + (interactive) + (error "This function is currently not implemented")) + +(defun reftex-index-change-entry (new &optional message) + ;; Change the full context string of the index entry at point to + ;; NEW. This actually edits the buffer where the entry is defined. + + (let* ((data (get-text-property (point) :data)) + old beg end info) + (unless data (error "Cannot change entry")) + (reftex-index-view-entry) + (setq beg (match-beginning 0) end (match-end 0)) + (setq old (nth 2 data)) + (and (equal old new) (error "Entry unchanged")) + (save-excursion + (set-buffer (get-file-buffer (nth 3 data))) + (goto-char beg) + (unless (looking-at (regexp-quote old)) + (error "This should not happen (reftex-index-change-entry)")) + (delete-region beg end) + (insert new) + (goto-char (1- beg)) + (when (and (re-search-forward (reftex-everything-regexp) nil t) + (match-end 10) + (< (abs (- (match-beginning 10) beg)) (length new)) + (setq info (reftex-index-info-safe buffer-file-name))) + (setcdr data (cdr info)))) + (let ((buffer-read-only nil)) + (save-excursion + (reftex-insert-index (list data) reftex-index-tag t + "EDITED"))) + (setq reftex-last-follow-point 1) + (and message (message message)))) + +;; Index map +(define-key reftex-index-map (if (featurep 'xemacs) [(button2)] [(mouse-2)]) + 'reftex-index-mouse-goto-line-and-hide) + +(substitute-key-definition + 'next-line 'reftex-index-next reftex-index-map global-map) +(substitute-key-definition + 'previous-line 'reftex-index-previous reftex-index-map global-map) + +(loop for x in + '(("n" . reftex-index-next) + ("p" . reftex-index-previous) + ("?" . reftex-index-show-help) + (" " . reftex-index-view-entry) + ("\C-m" . reftex-index-goto-entry-and-hide) + ("\C-i" . reftex-index-goto-entry) + ("\C-k" . reftex-index-kill) + ("r" . reftex-index-rescan) + ("R" . reftex-index-Rescan) + ("g" . revert-buffer) + ("q" . reftex-index-quit) + ("k" . reftex-index-quit-and-kill) + ("f" . reftex-index-toggle-follow) + ("s" . reftex-index-switch-index-tag) + ("e" . reftex-index-edit) + ("^" . reftex-index-level-up) + ("_" . reftex-index-level-down) + ("}" . reftex-index-restrict-to-section) + ("{" . reftex-index-widen) + (">" . reftex-index-restriction-forward) + ("<" . reftex-index-restriction-backward) + ("(" . reftex-index-toggle-range-beginning) + (")" . reftex-index-toggle-range-end) + ("|" . reftex-index-edit-attribute) + ("@" . reftex-index-edit-visual) + ("*" . reftex-index-edit-key) + ("&" . reftex-index-globalize) + ("\C-c=". reftex-index-goto-toc) + ("c" . reftex-index-toggle-context)) + do (define-key reftex-index-map (car x) (cdr x))) + +(loop for key across "0123456789" do + (define-key reftex-index-map (vector (list key)) 'digit-argument)) +(define-key reftex-index-map "-" 'negative-argument) + +;; The capital letters and the exclamation mark +(loop for key across (concat "!" reftex-index-section-letters) do + (define-key reftex-index-map (vector (list key)) + (list 'lambda '() '(interactive) + (list 'reftex-index-goto-letter key)))) + +(defun reftex-index-goto-letter (char) + "Go to the CHAR section in the index." + (let ((pos (point)) + (case-fold-search nil)) + (goto-line 3) + (if (re-search-forward (concat "^" (char-to-string char)) nil t) + (progn + (beginning-of-line) + (recenter 0) + (reftex-index-next)) + (goto-char pos) + (error "This <%s> index does not contain entries starting with `%c'" + reftex-index-tag char)))) + +(easy-menu-define + reftex-index-menu reftex-index-map + "Menu for Index buffer" + `("Index" + ["Goto section A-Z" + (message "To go to a section, just press any of: !%s" + reftex-index-section-letters) t] + ["Show Entry" reftex-index-view-entry t] + ["Go To Entry" reftex-index-goto-entry t] + ["Exit & Go To Entry" reftex-index-goto-entry-and-hide t] + ["Table of Contents" reftex-index-goto-toc t] + ["Quit" reftex-index-quit t] + "--" + ("Update" + ["Rebuilt *Index* Buffer" revert-buffer t] + "--" + ["Rescan One File" reftex-index-rescan reftex-enable-partial-scans] + ["Rescan Entire Document" reftex-index-Rescan t]) + ("Restrict" + ["Restrict to section" reftex-index-restrict-to-section t] + ["Widen" reftex-index-widen reftex-index-restriction-indicator] + ["Next Section" reftex-index-restriction-forward + reftex-index-restriction-indicator] + ["Previous Section" reftex-index-restriction-backward + reftex-index-restriction-indicator]) + ("Edit" + ["Edit Entry" reftex-index-edit t] + ["Edit Key" reftex-index-edit-key t] + ["Edit Attribute" reftex-index-edit-attribute t] + ["Edit Visual" reftex-index-edit-visual t] + "--" + ["Add Parentkey" reftex-index-level-down t] + ["Remove Parentkey " reftex-index-level-up t] + "--" + ["Make Start-of-Range" reftex-index-toggle-range-beginning t] + ["Make End-of-Range" reftex-index-toggle-range-end t] + "--" + ["Globalize" reftex-index-globalize t] + ["Kill Entry" reftex-index-kill nil] + "--" + ["Undo" reftex-index-undo nil]) + ("Options" + ["Context" reftex-index-toggle-context :style toggle + :selected reftex-index-include-context] + "--" + ["Follow Mode" reftex-index-toggle-follow :style toggle + :selected reftex-index-follow-mode]) + "--" + ["Help" reftex-index-show-help t])) + +;;; reftex-index.el ends here |