From 34699b85fd84223412b39dc2e455d43756903671 Mon Sep 17 00:00:00 2001 From: Roland Winkler Date: Sun, 5 Jun 2011 00:46:43 -0500 Subject: [PATCH] lisp/textmodes/bibtex.el: new command bibtex-search-entries --- etc/NEWS | 2 + lisp/ChangeLog | 6 +++ lisp/textmodes/bibtex.el | 120 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) diff --git a/etc/NEWS b/etc/NEWS index 9b1bb99eb1b..e657b791505 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -438,6 +438,8 @@ Just set shell-dir-cookie-re to an appropriate regexp. ** BibTeX mode +*** New command `bibtex-search-entries' bound to C-c C-a. + *** New `bibtex-entry-format' option `sort-fields', disabled by default. *** New variable `bibtex-search-entry-globally'. diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 8a7b9f4f822..2e0352a7bdf 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,5 +1,11 @@ 2011-06-05 Roland Winkler + * textmodes/bibtex.el (bibtex-search-buffer): New variable. + (bibtex-search-entries): New command bound to C-c C-a. + (bibtex-display-entries): New function. + +2011-06-05 Roland Winkler + * textmodes/bibtex.el (bibtex-generate-url-list): Fix docstring. (bibtex-insert-kill): After yanking insert newline if necessary. (bibtex-initialize): Call bibtex-string-files-init only once. diff --git a/lisp/textmodes/bibtex.el b/lisp/textmodes/bibtex.el index 6d07a6e3950..12094887f38 100644 --- a/lisp/textmodes/bibtex.el +++ b/lisp/textmodes/bibtex.el @@ -968,6 +968,11 @@ Set this variable before loading BibTeX mode." :group 'bibtex :type 'boolean) +(defcustom bibtex-search-buffer "*BibTeX Search*" + "Buffer for BibTeX search results." + :group 'bibtex + :type 'string) + ;; `bibtex-font-lock-keywords' is a user option, too. But since the ;; patterns used to define this variable are defined in a later ;; section of this file, it is defined later. @@ -1025,6 +1030,7 @@ Set this variable before loading BibTeX mode." (define-key km "\C-c\C-rn" 'bibtex-narrow-to-entry) (define-key km "\C-c\C-rw" 'widen) (define-key km "\C-c\C-l" 'bibtex-url) + (define-key km "\C-c\C-a" 'bibtex-search-entries) (define-key km "\C-c\C-o" 'bibtex-remove-OPT-or-ALT) (define-key km "\C-c\C-e\C-i" 'bibtex-InProceedings) (define-key km "\C-c\C-ei" 'bibtex-InCollection) @@ -1102,6 +1108,8 @@ Set this variable before loading BibTeX mode." ["View Cite Locations (RefTeX)" reftex-view-crossref-from-bibtex (fboundp 'reftex-view-crossref-from-bibtex)]) ("Operating on Buffer or Region" + ["Search Entries" bibtex-search-entries t] + "--" ["Validate Entries" bibtex-validate t] ["Sort Entries" bibtex-sort-buffer t] ["Reformat Entries" bibtex-reformat t] @@ -4789,6 +4797,118 @@ Return the URL or nil if none can be generated." (message "No URL known.")) url))) +;; We could combine multiple seach results with set operations +;; AND, OR, MINUS, and NOT. Would this be useful? +;; How complicated are searches in real life? +;; We could also have other searches such as "publication year newer than...". +(defun bibtex-search-entries (field regexp &optional global display) + "Search BibTeX entries for FIELD matching REGEXP. +REGEXP may be a regexp to search for. +If REGEXP is a function, it is called for each entry with two args, +the buffer positions of beginning and end of entry. Then an entry +is accepted if this function returns non-nil. +If FIELD is an empty string perform search for REGEXP in whole entry. +With GLOBAL non-nil, search in `bibtex-files'. Otherwise the search +is limited to the current buffer. +If DISPLAY is non-nil, display search results in `bibtex-search-buffer'. +When called interactively, DISPLAY is t. +Also, GLOBAL is t if `bibtex-search-entry-globally' is non-nil. +A prefix arg negates the value of `bibtex-search-entry-globally'. +Return alist with elements (KEY FILE ENTRY), +where FILE is the BibTeX file of ENTRY." + (interactive + (list (completing-read + "Field: " + (delete-dups + (apply 'append + bibtex-user-optional-fields + (mapcar (lambda (x) + (append (mapcar 'car (nth 0 (nth 1 x))) + (mapcar 'car (nth 1 (nth 1 x))))) + bibtex-entry-field-alist))) nil t) + (read-string "Regexp: ") + (if bibtex-search-entry-globally + (not current-prefix-arg) + current-prefix-arg) + t)) + (let ((funp (functionp regexp)) + entries text file) + ;; If REGEXP is a function, the value of FIELD is ignored anyway. + ;; Yet to ensure the code below does not fail, we make FIELD + ;; a non-empty string. + (if (and funp (string= "" field)) (setq field "unrestricted")) + (dolist (buffer (if (and global bibtex-files) + (bibtex-initialize t) + (list (current-buffer)))) + (with-current-buffer buffer + (setq file (if buffer-file-name + (file-name-nondirectory buffer-file-name) + (buffer-name buffer))) + (save-excursion + (goto-char (point-min)) + (if (string= "" field) + ;; Unrestricted search. + (while (re-search-forward regexp nil t) + (let ((beg (bibtex-beginning-of-entry)) + (end (bibtex-end-of-entry)) + key) + (if (and (<= beg (match-beginning 0)) + (<= (match-end 0) end) + (save-excursion + (goto-char beg) + (and (looking-at bibtex-entry-head) + (setq key (bibtex-key-in-head))))) + (add-to-list 'entries + (list key file + (buffer-substring-no-properties + beg end)))))) + ;; The following is slow. But it works reliably even in more + ;; complicated cases with BibTeX string constants and crossrefed + ;; entries. If you prefer speed over reliability, perform an + ;; unrestricted search. + (bibtex-map-entries + (lambda (key beg end) + (if (cond (funp (funcall regexp beg end)) + ((and (setq text (bibtex-text-in-field field t)) + (string-match regexp text)))) + (add-to-list 'entries + (list key file + (buffer-substring-no-properties + beg end)))))))))) + (if display + (if entries + (bibtex-display-entries entries) + (message "No BibTeX entries %smatching `%s'" + (if (string= "" field) "" + (format "with field `%s' " field)) + regexp))) + entries)) + +(defun bibtex-display-entries (entries &optional append) + "Display BibTeX ENTRIES in `bibtex-search-buffer'. +ENTRIES is an alist with elements (KEY FILE ENTRY), +where FILE is the BibTeX file of ENTRY. +If APPEND is non-nil, append ENTRIES to those already displayed." + (pop-to-buffer (get-buffer-create bibtex-search-buffer)) + ;; It would be nice if this buffer was editable, though editing + ;; can be meaningful only for individual existing entries + ;; (unlike reordering or creating new entries). + ;; Fancy workaround: Editing commands in the virtual buffer could + ;; jump to the real entry in the real buffer. + (let (buffer-read-only) + (if append (goto-char (point-max)) (erase-buffer)) + (dolist (entry (sort entries (lambda (x y) (string< (car x) (car y))))) + (insert "% " (nth 1 entry) "\n" (nth 2 entry) "\n\n"))) + ;; `bibtex-sort-buffer' fails with the file names associated with + ;; each entry. Prior to sorting we could make the file name + ;; a BibTeX field of each entry (using `bibtex-make-field'). + ;; Or we could make it a text property that we unfold afterwards. + ;; (bibtex-sort-buffer) + (bibtex-mode) + (set-buffer-modified-p nil) + (setq buffer-read-only t) + (goto-char (point-min))) + ;; Make BibTeX a Feature -- 2.11.4.GIT