"*Non-nil means `query-replace' and friends ignore read-only matches."
:type 'boolean
:group 'matching
- :version "21.3")
+ :version "21.4")
(defun query-replace-read-args (string regexp-flag &optional noerror)
(unless noerror
string is used as FROM-STRING--you don't have to specify it with the
minibuffer.
-Replacement transfers the case of the old text to the new text,
-if `case-replace' and `case-fold-search'
-are non-nil and FROM-STRING has no uppercase letters.
-\(Preserving case means that if the string matched is all caps, or capitalized,
-then its replacement is upcased or capitalized.)
+Matching is independent of case if `case-fold-search' is non-nil and
+FROM-STRING has no uppercase letters. Replacement transfers the case
+pattern of the old text to the new text, if `case-replace' and
+`case-fold-search' are non-nil and FROM-STRING has no uppercase
+letters. \(Transferring the case pattern means that if the old text
+matched is all caps, or capitalized, then its replacement is upcased
+or capitalized.)
Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
only matches surrounded by word boundaries.
regexp is used as REGEXP--you don't have to specify it with the
minibuffer.
-Preserves case in each replacement if `case-replace' and `case-fold-search'
-are non-nil and REGEXP has no uppercase letters.
+Matching is independent of case if `case-fold-search' is non-nil and
+REGEXP has no uppercase letters. Replacement transfers the case
+pattern of the old text to the new text, if `case-replace' and
+`case-fold-search' are non-nil and REGEXP has no uppercase letters.
+\(Transferring the case pattern means that if the old text matched is
+all caps, or capitalized, then its replacement is upcased or
+capitalized.)
Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
only matches surrounded by word boundaries.
end of the buffer."
(interactive
- (keep-lines-read-args "Keep lines (containing match for regexp): "))
+ (progn
+ (barf-if-buffer-read-only)
+ (keep-lines-read-args "Keep lines (containing match for regexp): ")))
(if rstart
- (goto-char (min rstart rend))
+ (progn
+ (goto-char (min rstart rend))
+ (setq rend (copy-marker (max rstart rend))))
(if (and transient-mark-mode mark-active)
(setq rstart (region-beginning)
rend (copy-marker (region-end)))
;; Now end is first char preserved by the new match.
(if (< start end)
(delete-region start end))))
-
+
(setq start (save-excursion (forward-line 1) (point)))
;; If the match was empty, avoid matching again at same place.
(and (< (point) rend)
end of the buffer."
(interactive
- (keep-lines-read-args "Flush lines (containing match for regexp): "))
+ (progn
+ (barf-if-buffer-read-only)
+ (keep-lines-read-args "Flush lines (containing match for regexp): ")))
(if rstart
- (goto-char (min rstart rend))
+ (progn
+ (goto-char (min rstart rend))
+ (setq rend (copy-marker (max rstart rend))))
(if (and transient-mark-mode mark-active)
(setq rstart (region-beginning)
rend (copy-marker (region-end)))
(define-key map [mouse-2] 'occur-mode-mouse-goto)
(define-key map "\C-c\C-c" 'occur-mode-goto-occurrence)
(define-key map "\C-m" 'occur-mode-goto-occurrence)
- (define-key map "\o" 'occur-mode-goto-occurrence-other-window)
+ (define-key map "o" 'occur-mode-goto-occurrence-other-window)
(define-key map "\C-o" 'occur-mode-display-occurrence)
(define-key map "\M-n" 'occur-next)
(define-key map "\M-p" 'occur-prev)
+ (define-key map "r" 'occur-rename-buffer)
+ (define-key map "c" 'clone-buffer)
(define-key map "g" 'revert-buffer)
- (define-key map "q" 'delete-window)
+ (define-key map "q" 'quit-window)
+ (define-key map "z" 'kill-this-buffer)
map)
"Keymap for `occur-mode'.")
"Arguments to pass to `occur-1' to revert an Occur mode buffer.
See `occur-revert-function'.")
+(defcustom occur-mode-hook '(turn-on-font-lock)
+ "Hook run when entering Occur mode."
+ :type 'hook
+ :group 'matching)
+
+(defcustom occur-hook nil
+ "Hook run when `occur' is called."
+ :type 'hook
+ :group 'matching)
+
(put 'occur-mode 'mode-class 'special)
(defun occur-mode ()
"Major mode for output from \\[occur].
Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it.
\\{occur-mode-map}"
+ (interactive)
(kill-all-local-variables)
(use-local-map occur-mode-map)
(setq major-mode 'occur-mode)
(setq mode-name "Occur")
- (make-local-variable 'revert-buffer-function)
- (set (make-local-variable 'font-lock-defaults)
- '(nil t nil nil nil
- (font-lock-fontify-region-function . occur-fontify-region-function)
- (font-lock-unfontify-region-function . occur-unfontify-region-function)))
- (setq revert-buffer-function 'occur-revert-function)
(set (make-local-variable 'revert-buffer-function) 'occur-revert-function)
(make-local-variable 'occur-revert-arguments)
+ (add-hook 'change-major-mode-hook 'font-lock-defontify nil t)
(run-hooks 'occur-mode-hook))
(defun occur-revert-function (ignore1 ignore2)
"Handle `revert-buffer' for Occur mode buffers."
- (apply 'occur-1 occur-revert-arguments))
+ (apply 'occur-1 (append occur-revert-arguments (list (buffer-name)))))
(defun occur-mode-mouse-goto (event)
"In Occur mode, go to the occurrence whose line you click on."
(select-window window)
(goto-char pos))))
-(defun occur-next (&optional n)
- "Move to the Nth (default 1) next match in an Occur mode buffer."
- (interactive "p")
+(defun occur-find-match (n search message)
(if (not n) (setq n 1))
(let ((r))
(while (> n 0)
- (if (get-text-property (point) 'occur-point)
- (forward-char 1))
- (setq r (next-single-property-change (point) 'occur-point))
+ (setq r (funcall search (point) 'occur-match))
+ (and r
+ (get-text-property r 'occur-match)
+ (setq r (funcall search r 'occur-match)))
(if r
- (goto-char r)
- (error "No more matches"))
+ (goto-char r)
+ (error message))
(setq n (1- n)))))
+(defun occur-next (&optional n)
+ "Move to the Nth (default 1) next match in an Occur mode buffer."
+ (interactive "p")
+ (occur-find-match n #'next-single-property-change "No more matches"))
+
(defun occur-prev (&optional n)
"Move to the Nth (default 1) previous match in an Occur mode buffer."
(interactive "p")
- (if (not n) (setq n 1))
- (let ((r))
- (while (> n 0)
-
- (setq r (get-text-property (point) 'occur-point))
- (if r (forward-char -1))
-
- (setq r (previous-single-property-change (point) 'occur-point))
- (if r
- (goto-char (- r 1))
- (error "No earlier matches"))
-
- (setq n (1- n)))))
+ (occur-find-match n #'previous-single-property-change "No earlier matches"))
\f
(defcustom list-matching-lines-default-context-lines 0
"*Default number of context lines included around `list-matching-lines' matches.
(if forwardp
(eobp)
(bobp))))
- (setq count (+ count (if forwardp 1 -1)))
+ (setq count (+ count (if forwardp -1 1)))
(push
(funcall (if no-props
#'buffer-substring-no-properties
(if (equal input "")
default
input))
- current-prefix-arg))
+ (when current-prefix-arg
+ (prefix-numeric-value current-prefix-arg))))
+
+(defun occur-rename-buffer (&optional unique-p)
+ "Rename the current *Occur* buffer to *Occur: original-buffer-name*.
+Here `original-buffer-name' is the buffer name were occur was originally run.
+When given the prefix argument, the renaming will not clobber the existing
+buffer(s) of that name, but use `generate-new-buffer-name' instead.
+You can add this to `occur-hook' if you always want a separate *Occur*
+buffer for each buffer where you invoke `occur'."
+ (interactive "P")
+ (with-current-buffer
+ (if (eq major-mode 'occur-mode) (current-buffer) (get-buffer "*Occur*"))
+ (rename-buffer (concat "*Occur: "
+ (mapconcat #'buffer-name
+ (car (cddr occur-revert-arguments)) "/")
+ "*")
+ unique-p)))
(defun occur (regexp &optional nlines)
"Show all lines in the current buffer containing a match for REGEXP.
`occur'."
(interactive
(cons
- (let ((bufs (list (read-buffer "First buffer to search: "
- (current-buffer) t)))
- (buf nil))
+ (let* ((bufs (list (read-buffer "First buffer to search: "
+ (current-buffer) t)))
+ (buf nil)
+ (ido-ignore-item-temp-list bufs))
(while (not (string-equal
- (setq buf (read-buffer "Next buffer to search (RET to end): "
- nil t))
+ (setq buf (read-buffer
+ (if (eq read-buffer-function 'ido-read-buffer)
+ "Next buffer to search (C-j to end): "
+ "Next buffer to search (RET to end): ")
+ nil t))
""))
- (push buf bufs))
+ (add-to-list 'bufs buf)
+ (setq ido-ignore-item-temp-list bufs))
(nreverse (mapcar #'get-buffer bufs)))
(occur-read-primary-args)))
(occur-1 regexp nlines bufs))
buf))
(buffer-list))))))
-(defun occur-1 (regexp nlines bufs)
- (let ((occur-buf (get-buffer-create "*Occur*"))
+(defun occur-1 (regexp nlines bufs &optional buf-name)
+ (unless buf-name
+ (setq buf-name "*Occur*"))
+ (let ((occur-buf (get-buffer-create buf-name))
(made-temp-buf nil)
(active-bufs (delq nil (mapcar #'(lambda (buf)
(when (buffer-live-p buf) buf))
(or nlines list-matching-lines-default-context-lines)
(and case-fold-search
(isearch-no-upper-case-p regexp t))
- nil nil nil nil)))
- (let* ((diff (- (length bufs) (length active-bufs)))
- (msg (concat
- (format "Searched %d buffers" (- (length bufs) diff))
- "%s; "
- (format "%s matches for `%s'"
- (if (zerop count)
- "no"
- (format "%d" count))
- regexp))))
- (message msg (if (zerop diff)
- ""
- (format " (%d killed)" diff))))
+ list-matching-lines-buffer-name-face
+ nil list-matching-lines-face nil)))
+ (let* ((bufcount (length active-bufs))
+ (diff (- (length bufs) bufcount)))
+ (message "Searched %d buffer%s%s; %s match%s for `%s'"
+ bufcount (if (= bufcount 1) "" "s")
+ (if (zerop diff) "" (format " (%d killed)" diff))
+ (if (zerop count) "no" (format "%d" count))
+ (if (= count 1) "" "es")
+ regexp))
;; If we had to make a temporary buffer, make it the *Occur*
;; buffer now.
(when made-temp-buf
- (with-current-buffer (get-buffer "*Occur*")
- (kill-this-buffer))
- (rename-buffer "*Occur*"))
+ (with-current-buffer (get-buffer buf-name)
+ (kill-buffer (current-buffer)))
+ (rename-buffer buf-name))
(setq occur-revert-arguments (list regexp nlines bufs)
buffer-read-only t)
(if (> count 0)
(display-buffer occur-buf)
- (kill-buffer occur-buf))))))
+ (kill-buffer occur-buf)))
+ (run-hooks 'occur-hook))))
(defun occur-engine-add-prefix (lines)
(mapcar
#'(lambda (line)
- (concat " :" line "\n"))
+ (concat " :" line "\n"))
lines))
(defun occur-engine (regexp buffers out-buf nlines case-fold-search
title-face prefix-face match-face keep-props)
(with-current-buffer out-buf
(setq buffer-read-only nil)
- (let ((globalcount 0))
+ (let ((globalcount 0)
+ (coding nil))
;; Map over all the buffers
(dolist (buf buffers)
(when (buffer-live-p buf)
(headerpt (with-current-buffer out-buf (point))))
(save-excursion
(set-buffer buf)
+ (or coding
+ ;; Set CODING only if the current buffer locally
+ ;; binds buffer-file-coding-system.
+ (not (local-variable-p 'buffer-file-coding-system))
+ (setq coding buffer-file-coding-system))
(save-excursion
(goto-char (point-min)) ;; begin searching in the buffer
(while (not (eobp))
(setq origpt (point))
(when (setq endpt (re-search-forward regexp nil t))
(setq matches (1+ matches)) ;; increment match count
- (setq globalcount (1+ globalcount))
(setq matchbeg (match-beginning 0)
matchend (match-end 0))
(setq begpt (save-excursion
(add-text-properties (match-beginning 0)
(match-end 0)
(append
- '(occur-match t)
+ `(occur-match t)
(when match-face
- `(face ,match-face)))
+ `(font-lock-face ,match-face)))
curstring)
(setq start (match-end 0))))
;; Generate the string to insert for this match
(let* ((out-line
(concat
- (apply #'propertize (format "%6d:" lines)
+ ;; Using 7 digits aligns tabs properly.
+ (apply #'propertize (format "%7d:" lines)
(append
(when prefix-face
- `(face prefix-face))
+ `(font-lock-face prefix-face))
'(occur-prefix t)))
curstring
"\n"))
;; concatenate them all together.
(apply #'concat
(nconc
- (occur-engine-add-prefix (nreverse (cdr (occur-accumulate-lines (- (1+ nlines)) t))))
+ (occur-engine-add-prefix (nreverse (cdr (occur-accumulate-lines (- (1+ nlines)) keep-props))))
(list out-line)
- (occur-engine-add-prefix (cdr (occur-accumulate-lines (1+ nlines) t))))))))
+ (occur-engine-add-prefix (cdr (occur-accumulate-lines (1+ nlines) keep-props))))))))
;; Actually insert the match display data
(with-current-buffer out-buf
(let ((beg (point))
(unless (= nlines 0)
(insert "-------\n"))
(add-text-properties
- beg (1- end)
- `(occur-target ,marker
- mouse-face highlight help-echo
- "mouse-2: go to this occurrence")))))
+ beg end
+ `(occur-target ,marker help-echo "mouse-2: go to this occurrence"))
+ ;; We don't put `mouse-face' on the newline,
+ ;; because that loses.
+ (add-text-properties beg (1- end) '(mouse-face highlight)))))
(goto-char endpt))
- (setq lines (1+ lines))
- ;; On to the next match...
- (forward-line 1))))
+ (if endpt
+ (progn
+ (setq lines (1+ lines))
+ ;; On to the next match...
+ (forward-line 1))
+ (goto-char (point-max))))))
(when (not (zerop matches)) ;; is the count zero?
+ (setq globalcount (+ globalcount matches))
(with-current-buffer out-buf
(goto-char headerpt)
(let ((beg (point))
(add-text-properties beg end
(append
(when title-face
- `(face ,title-face))
+ `(font-lock-face ,title-face))
`(occur-title ,buf))))
(goto-char (point-min)))))))
+ (if coding
+ ;; CODING is buffer-file-coding-system of the first buffer
+ ;; that locally binds it. Let's use it also for the output
+ ;; buffer.
+ (set-buffer-file-coding-system coding))
;; Return the number of matches
globalcount)))
-(defun occur-fontify-on-property (prop face beg end)
- (let ((prop-beg (or (and (get-text-property (point) prop) (point))
- (next-single-property-change (point) prop nil end))))
- (when (and prop-beg (not (= prop-beg end)))
- (let ((prop-end (next-single-property-change beg prop nil end)))
- (when (and prop-end (not (= prop-end end)))
- (put-text-property prop-beg prop-end 'face face)
- prop-end)))))
-
-(defun occur-fontify-region-function (beg end &optional verbose)
- (when verbose (message "Fontifying..."))
- (let ((inhibit-read-only t))
- (save-excursion
- (dolist (e `((occur-title . ,list-matching-lines-buffer-name-face)
- (occur-match . ,list-matching-lines-face)))
- ; (occur-prefix . ,list-matching-lines-prefix-face)))
- (goto-char beg)
- (let ((change-end nil))
- (while (setq change-end (occur-fontify-on-property (car e)
- (cdr e)
- (point)
- end))
- (goto-char change-end))))))
- (when verbose (message "Fontifying...done")))
-
-(defun occur-unfontify-region-function (beg end)
- (let ((inhibit-read-only t))
- (remove-text-properties beg end '(face nil))))
-
\f
;; It would be nice to use \\[...], but there is no reasonable way
;; to make that display both SPC and Y.
(aset data 2 (if (consp next) next (aref data 3))))))
(car (aref data 2)))
-(defun perform-replace (from-string replacements
+(defun perform-replace (from-string replacements
query-flag regexp-flag delimited-flag
&optional repeat-count map start end)
"Subroutine of `query-replace'. Its complexity handles interactive queries.
which will run faster and probably do exactly what you want. Please
see the documentation of `replace-match' to find out how to simulate
-`case-replace'."
+`case-replace'.
+
+This function returns nil if and only if there were no matches to
+make, or the user didn't cancel the call."
(or map (setq map query-replace-map))
(and query-flag minibuffer-auto-raise
(raise-frame (window-frame (minibuffer-window))))
(setq done t))
((eq def 'backup)
(if stack
- (let ((elt (car stack)))
+ (let ((elt (pop stack)))
(goto-char (car elt))
(setq replaced (eq t (cdr elt)))
(or replaced
- (set-match-data (cdr elt)))
- (setq stack (cdr stack)))
+ (set-match-data (cdr elt))))
(message "No previous match")
(ding 'no-terminate)
(sit-for 1)))
(if (and regexp-flag nonempty-match)
(setq match-again (and (looking-at search-string)
(match-data)))))
-
+
;; Edit replacement.
((eq def 'edit-replacement)
(setq next-replacement
(or replaced
(replace-match next-replacement nocasify literal))
(setq done t))
-
+
((eq def 'delete-and-edit)
(delete-region (match-beginning 0) (match-end 0))
(set-match-data
;; beyond the last replacement. Undo that.
(when (and regexp-flag (not match-again) (> replace-count 0))
(backward-char 1))
-
+
(replace-dehighlight))
(or unread-command-events
(message "Replaced %d occurrence%s"