(query-replace-read-args): Implement \,' and
\#'
replacements here. (query-replace-regexp): Doc string explaining this and the new `\?' replacement. Remove `\,' and `\#' implementation here, as it is better placed in `query-replace-read-args'. (replace-regexp): Explain `\,', `\#' and `\?'. (replace-match-data): New function for thorough reuse/destruction of old match-data. (replace-match-maybe-edit): Function for implementing `\?' editing. (perform-replace): Fix maintaining of the match stack including already matched regions, implement `\?', fix various problems with regions while editing and other stuff. (replace-highlight): Simplified.
This commit is contained in:
parent
bb72b9d0b5
commit
7c1c02ac6e
2 changed files with 221 additions and 98 deletions
|
@ -1,3 +1,20 @@
|
|||
2004-06-24 David Kastrup <dak@gnu.org>
|
||||
|
||||
* replace.el (query-replace-read-args): Implement `\,' and `\#'
|
||||
replacements here.
|
||||
(query-replace-regexp): Doc string explaining this and the new
|
||||
`\?' replacement. Remove `\,' and `\#' implementation here, as it
|
||||
is better placed in `query-replace-read-args'.
|
||||
(replace-regexp): Explain `\,', `\#' and `\?'.
|
||||
(replace-match-data): New function for thorough reuse/destruction
|
||||
of old match-data.
|
||||
(replace-match-maybe-edit): Function for implementing `\?'
|
||||
editing.
|
||||
(perform-replace): Fix maintaining of the match stack including
|
||||
already matched regions, implement `\?', fix various problems
|
||||
with regions while editing and other stuff.
|
||||
(replace-highlight): Simplified.
|
||||
|
||||
2004-06-24 Daniel Pfeiffer <occitan@esperanto.org>
|
||||
|
||||
* progmodes/grep.el (grep-error-screen-columns): New variable.
|
||||
|
|
302
lisp/replace.el
302
lisp/replace.el
|
@ -95,6 +95,28 @@ strings or patterns."
|
|||
(setq to (read-from-minibuffer (format "%s %s with: " string from)
|
||||
nil nil nil
|
||||
query-replace-to-history-variable from t)))
|
||||
(when (and regexp-flag
|
||||
(string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]" to))
|
||||
(let (pos list char)
|
||||
(while
|
||||
(progn
|
||||
(setq pos (match-end 0))
|
||||
(push (substring to 0 (- pos 2)) list)
|
||||
(setq char (aref to (1- pos))
|
||||
to (substring to pos))
|
||||
(cond ((eq char ?\#)
|
||||
(push '(number-to-string replace-count) list))
|
||||
((eq char ?\,)
|
||||
(setq pos (read-from-string to))
|
||||
(push `(replace-quote ,(car pos)) list)
|
||||
(setq to (substring to (cdr pos)))))
|
||||
(string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]" to)))
|
||||
(setq to (nreverse (delete "" (cons to list)))))
|
||||
(replace-match-string-symbols to)
|
||||
(setq to (cons 'replace-eval-replacement
|
||||
(if (> (length to) 1)
|
||||
(cons 'concat to)
|
||||
(car to)))))
|
||||
(list from to current-prefix-arg)))
|
||||
|
||||
(defun query-replace (from-string to-string &optional delimited start end)
|
||||
|
@ -163,59 +185,38 @@ Fourth and fifth arg START and END specify the region to operate on.
|
|||
In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP,
|
||||
and `\\=\\N' (where N is a digit) stands for
|
||||
whatever what matched the Nth `\\(...\\)' in REGEXP.
|
||||
`\\?' lets you edit the replacement text in the minibuffer
|
||||
at the given position for each replacement.
|
||||
|
||||
When this function is called interactively, the replacement text
|
||||
can also contain `\\,' followed by a Lisp expression. The escaped
|
||||
shorthands for `query-replace-regexp-eval' are also valid
|
||||
here: within the Lisp expression, you can use `\\&' for the whole
|
||||
match string, `\\N' for partial matches, `\\#&' and `\\#N' for
|
||||
the respective numeric values, and `\\#' for `replace-count'.
|
||||
In interactive calls, the replacement text may contain `\\,'
|
||||
followed by a Lisp expression used as part of the replacement
|
||||
text. Inside of that expression, `\\&' is a string denoting the
|
||||
whole match, `\\N' a partial matches, `\\#&' and `\\#N' the
|
||||
respective numeric values from `string-to-number', and `\\#'
|
||||
itself for `replace-count', the number of replacements occured so
|
||||
far.
|
||||
|
||||
If your Lisp expression is an identifier and the next
|
||||
letter in the replacement string would be interpreted as part of it,
|
||||
you can wrap it with an expression like `\\,(or \\#)'. Incidentally,
|
||||
for this particular case you may also enter `\\#' in the replacement
|
||||
text directly.
|
||||
If your Lisp expression is an identifier and the next letter in
|
||||
the replacement string would be interpreted as part of it, you
|
||||
can wrap it with an expression like `\\,(or \\#)'. Incidentally,
|
||||
for this particular case you may also enter `\\#' in the
|
||||
replacement text directly.
|
||||
|
||||
When you use `\\,' or `\\#' in the replacement, TO-STRING actually
|
||||
becomes a list with expanded shorthands.
|
||||
Use \\[repeat-complex-command] after this command to see details."
|
||||
When using those Lisp features interactively in the replacement
|
||||
text, TO-STRING is actually made a list instead of a string.
|
||||
Use \\[repeat-complex-command] after this command for details."
|
||||
(interactive
|
||||
(let ((common
|
||||
(query-replace-read-args "Query replace regexp" t)))
|
||||
(list
|
||||
(nth 0 common)
|
||||
(if (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]"
|
||||
(nth 1 common))
|
||||
(let ((to-string (nth 1 common)) pos to-expr char prompt)
|
||||
(while (string-match
|
||||
"\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]"
|
||||
to-string)
|
||||
(setq pos (match-end 0))
|
||||
(push (substring to-string 0 (- pos 2)) to-expr)
|
||||
(setq char (aref to-string (1- pos))
|
||||
to-string (substring to-string pos))
|
||||
(cond ((eq char ?\#)
|
||||
(push '(number-to-string replace-count) to-expr))
|
||||
((eq char ?\,)
|
||||
(setq pos (read-from-string to-string))
|
||||
(push `(replace-quote ,(car pos)) to-expr)
|
||||
(setq to-string (substring to-string (cdr pos))))))
|
||||
(setq to-expr (nreverse (delete "" (cons to-string to-expr))))
|
||||
(replace-match-string-symbols to-expr)
|
||||
(cons 'replace-eval-replacement
|
||||
(if (> (length to-expr) 1)
|
||||
(cons 'concat to-expr)
|
||||
(car to-expr))))
|
||||
(nth 1 common))
|
||||
(nth 2 common)
|
||||
;; These are done separately here
|
||||
;; so that command-history will record these expressions
|
||||
;; rather than the values they had this time.
|
||||
(if (and transient-mark-mode mark-active)
|
||||
(region-beginning))
|
||||
(if (and transient-mark-mode mark-active)
|
||||
(region-end)))))
|
||||
(list (nth 0 common) (nth 1 common) (nth 2 common)
|
||||
;; These are done separately here
|
||||
;; so that command-history will record these expressions
|
||||
;; rather than the values they had this time.
|
||||
(if (and transient-mark-mode mark-active)
|
||||
(region-beginning))
|
||||
(if (and transient-mark-mode mark-active)
|
||||
(region-end)))))
|
||||
|
||||
(perform-replace regexp to-string t t delimited nil nil start end))
|
||||
|
||||
(define-key esc-map [?\C-%] 'query-replace-regexp)
|
||||
|
@ -374,7 +375,27 @@ Fourth and fifth arg START and END specify the region to operate on.
|
|||
|
||||
In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP,
|
||||
and `\\=\\N' (where N is a digit) stands for
|
||||
whatever what matched the Nth `\\(...\\)' in REGEXP.
|
||||
whatever what matched the Nth `\\(...\\)' in REGEXP.
|
||||
`\\?' lets you edit the replacement text in the minibuffer
|
||||
at the given position for each replacement.
|
||||
|
||||
In interactive calls, the replacement text may contain `\\,'
|
||||
followed by a Lisp expression used as part of the replacement
|
||||
text. Inside of that expression, `\\&' is a string denoting the
|
||||
whole match, `\\N' a partial matches, `\\#&' and `\\#N' the
|
||||
respective numeric values from `string-to-number', and `\\#'
|
||||
itself for `replace-count', the number of replacements occured so
|
||||
far.
|
||||
|
||||
If your Lisp expression is an identifier and the next letter in
|
||||
the replacement string would be interpreted as part of it, you
|
||||
can wrap it with an expression like `\\,(or \\#)'. Incidentally,
|
||||
for this particular case you may also enter `\\#' in the
|
||||
replacement text directly.
|
||||
|
||||
When using those Lisp features interactively in the replacement
|
||||
text, TO-STRING is actually made a list instead of a string.
|
||||
Use \\[repeat-complex-command] after this command for details.
|
||||
|
||||
If `query-replace-interactive' is non-nil, the last incremental search
|
||||
regexp is used as REGEXP--you don't have to specify it with the minibuffer.
|
||||
|
@ -1115,6 +1136,49 @@ with the `noescape' argument set.
|
|||
(aset data 2 (if (consp next) next (aref data 3))))))
|
||||
(car (aref data 2)))
|
||||
|
||||
(defun replace-match-data (integers reuse &optional new)
|
||||
"Like `match-data', but markers in REUSE get invalidated.
|
||||
If NEW is non-NIL, it is set and returned instead of fresh data,
|
||||
but coerced to the correct value of INTEGERS."
|
||||
(or (and new
|
||||
(progn
|
||||
(set-match-data new)
|
||||
(and (eq new reuse)
|
||||
(eq (null integers) (markerp (car reuse)))
|
||||
new)))
|
||||
(match-data integers
|
||||
(prog1 reuse
|
||||
(while reuse
|
||||
(if (markerp (car reuse))
|
||||
(set-marker (car reuse) nil))
|
||||
(setq reuse (cdr reuse)))))))
|
||||
|
||||
(defun replace-match-maybe-edit (newtext fixedcase literal noedit match-data)
|
||||
"Make a replacement with `replace-match', editing `\\?'.
|
||||
NEXTEXT, FIXEDCASE, LITERAL are just passed on. If NOEDIT is true, no
|
||||
check for `\\?' is made to save time. MATCH-DATA is used for the
|
||||
replacement. In case editing is done, it is changed to use markers.
|
||||
|
||||
The return value is non-NIL if there has been no `\\?' or NOEDIT was
|
||||
passed in. If LITERAL is set, no checking is done, anyway."
|
||||
(unless (or literal noedit)
|
||||
(setq noedit t)
|
||||
(while (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\\\?\\)"
|
||||
newtext)
|
||||
(setq newtext
|
||||
(read-input "Edit replacement string: "
|
||||
(prog1
|
||||
(cons
|
||||
(replace-match "" t t newtext 3)
|
||||
(1+ (match-beginning 3)))
|
||||
(setq match-data
|
||||
(replace-match-data
|
||||
nil match-data match-data))))
|
||||
noedit nil)))
|
||||
(set-match-data match-data)
|
||||
(replace-match newtext fixedcase literal)
|
||||
noedit)
|
||||
|
||||
(defun perform-replace (from-string replacements
|
||||
query-flag regexp-flag delimited-flag
|
||||
&optional repeat-count map start end)
|
||||
|
@ -1145,6 +1209,7 @@ make, or the user didn't cancel the call."
|
|||
(search-string from-string)
|
||||
(real-match-data nil) ; the match data for the current match
|
||||
(next-replacement nil)
|
||||
(noedit nil)
|
||||
(keep-going t)
|
||||
(stack nil)
|
||||
(replace-count 0)
|
||||
|
@ -1201,7 +1266,9 @@ make, or the user didn't cancel the call."
|
|||
(setq real-match-data
|
||||
(if (consp match-again)
|
||||
(progn (goto-char (nth 1 match-again))
|
||||
match-again)
|
||||
(replace-match-data t
|
||||
real-match-data
|
||||
match-again))
|
||||
(and (or match-again
|
||||
;; MATCH-AGAIN non-nil means we
|
||||
;; accept an adjacent match. If
|
||||
|
@ -1217,7 +1284,7 @@ make, or the user didn't cancel the call."
|
|||
(funcall search-function search-string limit t)
|
||||
;; For speed, use only integers and
|
||||
;; reuse the list used last time.
|
||||
(match-data t real-match-data)))))
|
||||
(replace-match-data t real-match-data)))))
|
||||
;; Optionally ignore matches that have a read-only property.
|
||||
(unless (and query-replace-skip-read-only
|
||||
(text-property-not-all
|
||||
|
@ -1249,16 +1316,23 @@ make, or the user didn't cancel the call."
|
|||
(set-match-data real-match-data)
|
||||
(setq next-replacement
|
||||
(funcall (car replacements) (cdr replacements)
|
||||
replace-count)))
|
||||
replace-count)
|
||||
noedit nil))
|
||||
(if (not query-flag)
|
||||
(let ((inhibit-read-only query-replace-skip-read-only))
|
||||
(set-match-data real-match-data)
|
||||
(replace-match next-replacement nocasify literal)
|
||||
(setq replace-count (1+ replace-count)))
|
||||
(setq noedit
|
||||
(replace-match-maybe-edit
|
||||
next-replacement nocasify literal
|
||||
noedit real-match-data)
|
||||
replace-count (1+ replace-count)))
|
||||
(undo-boundary)
|
||||
(let (done replaced key def)
|
||||
;; Loop reading commands until one of them sets done,
|
||||
;; which means it has finished handling this occurrence.
|
||||
;; which means it has finished handling this
|
||||
;; occurrence. Any command that sets `done' should
|
||||
;; leave behind proper match data for the stack.
|
||||
;; Commands not setting `done' need to adjust
|
||||
;; `real-match-data'.
|
||||
(while (not done)
|
||||
(set-match-data real-match-data)
|
||||
(replace-highlight (match-beginning 0) (match-end 0))
|
||||
|
@ -1290,37 +1364,49 @@ make, or the user didn't cancel the call."
|
|||
((eq def 'backup)
|
||||
(if stack
|
||||
(let ((elt (pop stack)))
|
||||
(goto-char (car elt))
|
||||
(setq replaced (eq t (cdr elt)))
|
||||
(or replaced
|
||||
(set-match-data (cdr elt))))
|
||||
(goto-char (nth 0 elt))
|
||||
(setq replaced (nth 1 elt)
|
||||
real-match-data
|
||||
(replace-match-data
|
||||
t real-match-data
|
||||
(nth 2 elt))))
|
||||
(message "No previous match")
|
||||
(ding 'no-terminate)
|
||||
(sit-for 1)))
|
||||
((eq def 'act)
|
||||
(or replaced
|
||||
(progn
|
||||
(replace-match next-replacement nocasify literal)
|
||||
(setq replace-count (1+ replace-count))))
|
||||
(setq noedit
|
||||
(replace-match-maybe-edit
|
||||
next-replacement nocasify literal
|
||||
noedit real-match-data)
|
||||
replace-count (1+ replace-count)))
|
||||
(setq done t replaced t))
|
||||
((eq def 'act-and-exit)
|
||||
(or replaced
|
||||
(progn
|
||||
(replace-match next-replacement nocasify literal)
|
||||
(setq replace-count (1+ replace-count))))
|
||||
(setq noedit
|
||||
(replace-match-maybe-edit
|
||||
next-replacement nocasify literal
|
||||
noedit real-match-data)
|
||||
replace-count (1+ replace-count)))
|
||||
(setq keep-going nil)
|
||||
(setq done t replaced t))
|
||||
((eq def 'act-and-show)
|
||||
(if (not replaced)
|
||||
(progn
|
||||
(replace-match next-replacement nocasify literal)
|
||||
(setq replace-count (1+ replace-count))
|
||||
(setq replaced t))))
|
||||
(setq noedit
|
||||
(replace-match-maybe-edit
|
||||
next-replacement nocasify literal
|
||||
noedit real-match-data)
|
||||
replace-count (1+ replace-count)
|
||||
real-match-data (replace-match-data
|
||||
t real-match-data)
|
||||
replaced t)))
|
||||
((eq def 'automatic)
|
||||
(or replaced
|
||||
(progn
|
||||
(replace-match next-replacement nocasify literal)
|
||||
(setq replace-count (1+ replace-count))))
|
||||
(setq noedit
|
||||
(replace-match-maybe-edit
|
||||
next-replacement nocasify literal
|
||||
noedit real-match-data)
|
||||
replace-count (1+ replace-count)))
|
||||
(setq done t query-flag nil replaced t))
|
||||
((eq def 'skip)
|
||||
(setq done t))
|
||||
|
@ -1328,36 +1414,45 @@ make, or the user didn't cancel the call."
|
|||
(recenter nil))
|
||||
((eq def 'edit)
|
||||
(let ((opos (point-marker)))
|
||||
(setq real-match-data (replace-match-data
|
||||
nil real-match-data
|
||||
real-match-data))
|
||||
(goto-char (match-beginning 0))
|
||||
(save-excursion
|
||||
(funcall search-function search-string limit t)
|
||||
(setq real-match-data (match-data)))
|
||||
(save-excursion
|
||||
(save-window-excursion
|
||||
(recursive-edit)))
|
||||
(goto-char opos))
|
||||
(set-match-data real-match-data)
|
||||
(goto-char opos)
|
||||
(set-marker opos nil))
|
||||
;; Before we make the replacement,
|
||||
;; decide whether the search string
|
||||
;; can match again just after this match.
|
||||
(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
|
||||
(setq real-match-data (replace-match-data
|
||||
nil real-match-data
|
||||
real-match-data)
|
||||
next-replacement
|
||||
(read-input "Edit replacement string: "
|
||||
next-replacement))
|
||||
(or replaced
|
||||
(replace-match next-replacement nocasify literal))
|
||||
next-replacement)
|
||||
noedit nil)
|
||||
(if replaced
|
||||
(set-match-data real-match-data)
|
||||
(setq noedit
|
||||
(replace-match-maybe-edit
|
||||
next-replacement nocasify literal noedit
|
||||
real-match-data)
|
||||
replaced t))
|
||||
(setq done t))
|
||||
|
||||
((eq def 'delete-and-edit)
|
||||
(delete-region (match-beginning 0) (match-end 0))
|
||||
(set-match-data
|
||||
(prog1 (match-data)
|
||||
(save-excursion (recursive-edit))))
|
||||
(replace-match "" t t)
|
||||
(setq real-match-data (replace-match-data
|
||||
nil real-match-data))
|
||||
(replace-dehighlight)
|
||||
(save-excursion (recursive-edit))
|
||||
(setq replaced t))
|
||||
;; Note: we do not need to treat `exit-prefix'
|
||||
;; specially here, since we reread
|
||||
|
@ -1372,10 +1467,23 @@ make, or the user didn't cancel the call."
|
|||
;; Record previous position for ^ when we move on.
|
||||
;; Change markers to numbers in the match data
|
||||
;; since lots of markers slow down editing.
|
||||
(setq stack
|
||||
(cons (cons (point)
|
||||
(or replaced (match-data t)))
|
||||
stack))))))
|
||||
(push (list (point) replaced
|
||||
;;; If the replacement has already happened, all we need is the
|
||||
;;; current match start and end. We could get this with a trivial
|
||||
;;; match like
|
||||
;;; (save-excursion (goto-char (match-beginning 0))
|
||||
;;; (search-forward (match-string 0))
|
||||
;;; (match-data t))
|
||||
;;; if we really wanted to avoid manually constructing match data.
|
||||
;;; Adding current-buffer is necessary so that match-data calls can
|
||||
;;; return markers which are appropriate for editing.
|
||||
(if replaced
|
||||
(list
|
||||
(match-beginning 0)
|
||||
(match-end 0)
|
||||
(current-buffer))
|
||||
(match-data t)))
|
||||
stack)))))
|
||||
|
||||
;; The code preventing adjacent regexp matches in the condition
|
||||
;; of the while-loop above will haven taken us one character
|
||||
|
@ -1405,14 +1513,12 @@ make, or the user didn't cancel the call."
|
|||
|
||||
(defun replace-highlight (start end)
|
||||
(and query-replace-highlight
|
||||
(progn
|
||||
(or replace-overlay
|
||||
(progn
|
||||
(setq replace-overlay (make-overlay start end))
|
||||
(overlay-put replace-overlay 'face
|
||||
(if (facep 'query-replace)
|
||||
'query-replace 'region))))
|
||||
(move-overlay replace-overlay start end (current-buffer)))))
|
||||
(if replace-overlay
|
||||
(move-overlay replace-overlay start end (current-buffer))
|
||||
(setq replace-overlay (make-overlay start end))
|
||||
(overlay-put replace-overlay 'face
|
||||
(if (facep 'query-replace)
|
||||
'query-replace 'region)))))
|
||||
|
||||
;;; arch-tag: 16b4cd61-fd40-497b-b86f-b667c4cf88e4
|
||||
;;; replace.el ends here
|
||||
|
|
Loading…
Add table
Reference in a new issue