+2015-01-21 Dmitry Gutov <dgutov@yandex.ru>
+
+ xref: Keep track of temporary buffers (bug#19466).
+ * progmodes/xref.el (xref--temporary-buffers, xref--selected)
+ (xref--inhibit-mark-selected): New variables.
+ (xref--mark-selected): New function.
+ (xref--show-location): Maybe add the buffer to
+ `xref--temporary-buffers', add `xref--mark-selected' to
+ `buffer-list-update-hook' there.
+ (xref--window): Add docstring.
+ (xref-quit): Rename from `xref--quit'. Update both references.
+ Add KILL argument. When it's non-nil, kill the temporary buffers
+ that haven't been selected by the user.
+ (xref--show-xref-buffer): Change the second argument to alist,
+ extract the values for `xref--window' and
+ `xref--temporary-buffers' from it. Add `xref--mark-selected' to
+ `buffer-list-update-hook' to each buffer in the list.
+ (xref--show-xrefs): Move the logic of calling `xref-find-function'
+ here. Save the difference between buffer lists before and after
+ it's called as "temporary buffers", and `pass it to
+ `xref-show-xrefs-function'.
+ (xref--find-definitions, xref-find-references)
+ (xref-find-apropos): Update accordingly.
+
2015-01-20 Artur Malabarba <bruce.connor.am@gmail.com>
* emacs-lisp/package.el (package-dir-info): Fix `while' logic.
(defvar-local xref--display-history nil
"List of pairs (BUFFER . WINDOW), for temporarily displayed buffers.")
+(defvar-local xref--temporary-buffers nil
+ "List of buffers created by xref code.")
+
+(defvar-local xref--selected nil
+ "t if the current buffer has ever been selected.
+Used for temporary buffers.")
+
+(defvar xref--inhibit-mark-selected nil)
+
+(defun xref--mark-selected ()
+ (unless xref--inhibit-mark-selected
+ (setq xref--selected t))
+ (remove-hook 'buffer-list-update-hook #'xref--mark-selected t))
+
(defun xref--save-to-history (buf win)
(let ((restore (window-parameter win 'quit-restore)))
;; Save the new entry if the window displayed another buffer
(defun xref--show-location (location)
(condition-case err
- (let ((xref-buf (current-buffer)))
+ (let ((xref-buf (current-buffer))
+ (bl (buffer-list))
+ (xref--inhibit-mark-selected t))
(xref--goto-location location)
+ (let ((buf (current-buffer)))
+ (unless (memq buf bl)
+ ;; Newly created.
+ (add-hook 'buffer-list-update-hook #'xref--mark-selected nil t)
+ (with-current-buffer xref-buf
+ (push buf xref--temporary-buffers))))
(xref--display-position (point) t 1 xref-buf))
(user-error (message (error-message-string err)))))
(defun xref--location-at-point ()
(get-text-property (point) 'xref-location))
-(defvar-local xref--window nil)
+(defvar-local xref--window nil
+ "ACTION argument to call `display-buffer' with.")
(defun xref-goto-xref ()
"Jump to the xref on the current line and bury the xref buffer."
(let ((loc (or (xref--location-at-point)
(user-error "No reference at point")))
(window xref--window))
- (xref--quit)
+ (xref-quit)
(xref--pop-to-location loc window)))
(define-derived-mode xref--xref-buffer-mode fundamental-mode "XREF"
(setq buffer-read-only t))
(let ((map xref--xref-buffer-mode-map))
- (define-key map (kbd "q") #'xref--quit)
+ (define-key map (kbd "q") #'xref-quit)
(define-key map (kbd "n") #'xref-next-line)
(define-key map (kbd "p") #'xref-prev-line)
(define-key map (kbd "RET") #'xref-goto-xref)
(define-key map (kbd ".") #'xref-next-line)
(define-key map (kbd ",") #'xref-prev-line))
-(defun xref--quit ()
- "Quit all windows in `xref--display-history', then quit current window."
- (interactive)
+(defun xref-quit (&optional kill)
+ "Perform cleanup, then quit the current window.
+The cleanup consists of burying all temporarily displayed
+buffers, and if KILL is non-nil, of killing all buffers that were
+created in the process of showing xrefs.
+
+Exceptions are made for buffers switched to by the user in the
+meantime, and other window configuration changes. These are
+preserved."
+ (interactive "P")a
(let ((window (selected-window))
(history xref--display-history))
(setq xref--display-history nil)
+ (when kill
+ (let ((xref--inhibit-mark-selected t)
+ kill-buffer-query-functions)
+ (dolist (buf xref--temporary-buffers)
+ (unless (buffer-local-value 'xref--selected buf)
+ (kill-buffer buf)))
+ (setq xref--temporary-buffers nil)))
(pcase-dolist (`(,buf . ,win) history)
(when (and (window-live-p win)
(eq buf (window-buffer win)))
(quit-window nil win)))
- (quit-window nil window)))
+ (quit-window kill window)))
(defconst xref-buffer-name "*xref*"
"The name of the buffer to show xrefs.")
(xref-location-group (xref--xref-location x)))
#'equal))
-(defun xref--show-xref-buffer (xrefs window)
+(defun xref--show-xref-buffer (xrefs alist)
(let ((xref-alist (xref--analyze xrefs)))
(with-current-buffer (get-buffer-create xref-buffer-name)
(let ((inhibit-read-only t))
(xref--xref-buffer-mode)
(pop-to-buffer (current-buffer))
(goto-char (point-min))
- (setq xref--window window)
+ (setq xref--window (assoc-default 'window alist))
+ (setq xref--temporary-buffers (assoc-default 'temporary-buffers alist))
+ (dolist (buf xref--temporary-buffers)
+ (with-current-buffer buf
+ (add-hook 'buffer-list-update-hook #'xref--mark-selected nil t)))
(current-buffer)))))
\f
(defvar xref-show-xrefs-function 'xref--show-xref-buffer
"Function to display a list of xrefs.")
-(defun xref--show-xrefs (id kind xrefs window)
- (cond
- ((null xrefs)
- (user-error "No known %s for: %s" kind id))
- ((not (cdr xrefs))
- (xref-push-marker-stack)
- (xref--pop-to-location (xref--xref-location (car xrefs)) window))
- (t
- (xref-push-marker-stack)
- (funcall xref-show-xrefs-function xrefs window))))
+(defun xref--show-xrefs (input kind arg window)
+ (let* ((bl (buffer-list))
+ (xrefs (funcall xref-find-function kind arg))
+ (tb (cl-set-difference (buffer-list) bl)))
+ (cond
+ ((null xrefs)
+ (user-error "No known %s for: %s" (symbol-name kind) input))
+ ((not (cdr xrefs))
+ (xref-push-marker-stack)
+ (xref--pop-to-location (xref--xref-location (car xrefs)) window))
+ (t
+ (xref-push-marker-stack)
+ (funcall xref-show-xrefs-function xrefs
+ `((window . ,window)
+ (temporary-buffers . ,tb)))))))
(defun xref--read-identifier (prompt)
"Return the identifier at point or read it from the minibuffer."
;;; Commands
(defun xref--find-definitions (id window)
- (xref--show-xrefs id "definitions"
- (funcall xref-find-function 'definitions id)
- window))
+ (xref--show-xrefs id 'definitions id window))
;;;###autoload
(defun xref-find-definitions (identifier)
"Find references to the identifier at point.
With prefix argument, prompt for the identifier."
(interactive (list (xref--read-identifier "Find references of: ")))
- (xref--show-xrefs identifier "references"
- (funcall xref-find-function 'references identifier)
- nil))
+ (xref--show-xrefs identifier 'references identifier nil))
;;;###autoload
(defun xref-find-apropos (pattern)
(interactive (list (read-from-minibuffer
"Search for pattern (word list or regexp): ")))
(require 'apropos)
- (xref--show-xrefs pattern "apropos"
- (funcall xref-find-function 'apropos
- (apropos-parse-pattern
- (if (string-equal (regexp-quote pattern) pattern)
- ;; Split into words
- (or (split-string pattern "[ \t]+" t)
- (user-error "No word list given"))
- pattern)))
+ (xref--show-xrefs pattern 'apropos
+ (apropos-parse-pattern
+ (if (string-equal (regexp-quote pattern) pattern)
+ ;; Split into words
+ (or (split-string pattern "[ \t]+" t)
+ (user-error "No word list given"))
+ pattern))
nil))
\f