aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/replace.el
diff options
context:
space:
mode:
authorDavid Kastrup2004-06-24 10:26:24 +0000
committerDavid Kastrup2004-06-24 10:26:24 +0000
commit7c1c02ac6e18673b2dde435b0236cf1d07e2346d (patch)
tree0d70d95fd56b36c9d87162d5010f1b2f6970ae1c /lisp/replace.el
parentbb72b9d0b5248404a55b599d99c0be5454704e4a (diff)
downloademacs-7c1c02ac6e18673b2dde435b0236cf1d07e2346d.tar.gz
emacs-7c1c02ac6e18673b2dde435b0236cf1d07e2346d.zip
(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.
Diffstat (limited to 'lisp/replace.el')
-rw-r--r--lisp/replace.el308
1 files changed, 207 insertions, 101 deletions
diff --git a/lisp/replace.el b/lisp/replace.el
index 89f55c2829e..7763b24f2e7 100644
--- a/lisp/replace.el
+++ b/lisp/replace.el
@@ -95,6 +95,28 @@ strings or patterns."
95 (setq to (read-from-minibuffer (format "%s %s with: " string from) 95 (setq to (read-from-minibuffer (format "%s %s with: " string from)
96 nil nil nil 96 nil nil nil
97 query-replace-to-history-variable from t))) 97 query-replace-to-history-variable from t)))
98 (when (and regexp-flag
99 (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]" to))
100 (let (pos list char)
101 (while
102 (progn
103 (setq pos (match-end 0))
104 (push (substring to 0 (- pos 2)) list)
105 (setq char (aref to (1- pos))
106 to (substring to pos))
107 (cond ((eq char ?\#)
108 (push '(number-to-string replace-count) list))
109 ((eq char ?\,)
110 (setq pos (read-from-string to))
111 (push `(replace-quote ,(car pos)) list)
112 (setq to (substring to (cdr pos)))))
113 (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]" to)))
114 (setq to (nreverse (delete "" (cons to list)))))
115 (replace-match-string-symbols to)
116 (setq to (cons 'replace-eval-replacement
117 (if (> (length to) 1)
118 (cons 'concat to)
119 (car to)))))
98 (list from to current-prefix-arg))) 120 (list from to current-prefix-arg)))
99 121
100(defun query-replace (from-string to-string &optional delimited start end) 122(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.
163In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP, 185In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP,
164and `\\=\\N' (where N is a digit) stands for 186and `\\=\\N' (where N is a digit) stands for
165whatever what matched the Nth `\\(...\\)' in REGEXP. 187whatever what matched the Nth `\\(...\\)' in REGEXP.
166 188`\\?' lets you edit the replacement text in the minibuffer
167When this function is called interactively, the replacement text 189at the given position for each replacement.
168can also contain `\\,' followed by a Lisp expression. The escaped 190
169shorthands for `query-replace-regexp-eval' are also valid 191In interactive calls, the replacement text may contain `\\,'
170here: within the Lisp expression, you can use `\\&' for the whole 192followed by a Lisp expression used as part of the replacement
171match string, `\\N' for partial matches, `\\#&' and `\\#N' for 193text. Inside of that expression, `\\&' is a string denoting the
172the respective numeric values, and `\\#' for `replace-count'. 194whole match, `\\N' a partial matches, `\\#&' and `\\#N' the
173 195respective numeric values from `string-to-number', and `\\#'
174If your Lisp expression is an identifier and the next 196itself for `replace-count', the number of replacements occured so
175letter in the replacement string would be interpreted as part of it, 197far.
176you can wrap it with an expression like `\\,(or \\#)'. Incidentally, 198
177for this particular case you may also enter `\\#' in the replacement 199If your Lisp expression is an identifier and the next letter in
178text directly. 200the replacement string would be interpreted as part of it, you
179 201can wrap it with an expression like `\\,(or \\#)'. Incidentally,
180When you use `\\,' or `\\#' in the replacement, TO-STRING actually 202for this particular case you may also enter `\\#' in the
181becomes a list with expanded shorthands. 203replacement text directly.
182Use \\[repeat-complex-command] after this command to see details." 204
205When using those Lisp features interactively in the replacement
206text, TO-STRING is actually made a list instead of a string.
207Use \\[repeat-complex-command] after this command for details."
183 (interactive 208 (interactive
184 (let ((common 209 (let ((common
185 (query-replace-read-args "Query replace regexp" t))) 210 (query-replace-read-args "Query replace regexp" t)))
186 (list 211 (list (nth 0 common) (nth 1 common) (nth 2 common)
187 (nth 0 common) 212 ;; These are done separately here
188 (if (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]" 213 ;; so that command-history will record these expressions
189 (nth 1 common)) 214 ;; rather than the values they had this time.
190 (let ((to-string (nth 1 common)) pos to-expr char prompt) 215 (if (and transient-mark-mode mark-active)
191 (while (string-match 216 (region-beginning))
192 "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]" 217 (if (and transient-mark-mode mark-active)
193 to-string) 218 (region-end)))))
194 (setq pos (match-end 0)) 219
195 (push (substring to-string 0 (- pos 2)) to-expr)
196 (setq char (aref to-string (1- pos))
197 to-string (substring to-string pos))
198 (cond ((eq char ?\#)
199 (push '(number-to-string replace-count) to-expr))
200 ((eq char ?\,)
201 (setq pos (read-from-string to-string))
202 (push `(replace-quote ,(car pos)) to-expr)
203 (setq to-string (substring to-string (cdr pos))))))
204 (setq to-expr (nreverse (delete "" (cons to-string to-expr))))
205 (replace-match-string-symbols to-expr)
206 (cons 'replace-eval-replacement
207 (if (> (length to-expr) 1)
208 (cons 'concat to-expr)
209 (car to-expr))))
210 (nth 1 common))
211 (nth 2 common)
212 ;; These are done separately here
213 ;; so that command-history will record these expressions
214 ;; rather than the values they had this time.
215 (if (and transient-mark-mode mark-active)
216 (region-beginning))
217 (if (and transient-mark-mode mark-active)
218 (region-end)))))
219 (perform-replace regexp to-string t t delimited nil nil start end)) 220 (perform-replace regexp to-string t t delimited nil nil start end))
220 221
221(define-key esc-map [?\C-%] 'query-replace-regexp) 222(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.
374 375
375In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP, 376In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP,
376and `\\=\\N' (where N is a digit) stands for 377and `\\=\\N' (where N is a digit) stands for
377 whatever what matched the Nth `\\(...\\)' in REGEXP. 378whatever what matched the Nth `\\(...\\)' in REGEXP.
379`\\?' lets you edit the replacement text in the minibuffer
380at the given position for each replacement.
381
382In interactive calls, the replacement text may contain `\\,'
383followed by a Lisp expression used as part of the replacement
384text. Inside of that expression, `\\&' is a string denoting the
385whole match, `\\N' a partial matches, `\\#&' and `\\#N' the
386respective numeric values from `string-to-number', and `\\#'
387itself for `replace-count', the number of replacements occured so
388far.
389
390If your Lisp expression is an identifier and the next letter in
391the replacement string would be interpreted as part of it, you
392can wrap it with an expression like `\\,(or \\#)'. Incidentally,
393for this particular case you may also enter `\\#' in the
394replacement text directly.
395
396When using those Lisp features interactively in the replacement
397text, TO-STRING is actually made a list instead of a string.
398Use \\[repeat-complex-command] after this command for details.
378 399
379If `query-replace-interactive' is non-nil, the last incremental search 400If `query-replace-interactive' is non-nil, the last incremental search
380regexp is used as REGEXP--you don't have to specify it with the minibuffer. 401regexp is used as REGEXP--you don't have to specify it with the minibuffer.
@@ -1115,6 +1136,49 @@ with the `noescape' argument set.
1115 (aset data 2 (if (consp next) next (aref data 3)))))) 1136 (aset data 2 (if (consp next) next (aref data 3))))))
1116 (car (aref data 2))) 1137 (car (aref data 2)))
1117 1138
1139(defun replace-match-data (integers reuse &optional new)
1140 "Like `match-data', but markers in REUSE get invalidated.
1141If NEW is non-NIL, it is set and returned instead of fresh data,
1142but coerced to the correct value of INTEGERS."
1143 (or (and new
1144 (progn
1145 (set-match-data new)
1146 (and (eq new reuse)
1147 (eq (null integers) (markerp (car reuse)))
1148 new)))
1149 (match-data integers
1150 (prog1 reuse
1151 (while reuse
1152 (if (markerp (car reuse))
1153 (set-marker (car reuse) nil))
1154 (setq reuse (cdr reuse)))))))
1155
1156(defun replace-match-maybe-edit (newtext fixedcase literal noedit match-data)
1157 "Make a replacement with `replace-match', editing `\\?'.
1158NEXTEXT, FIXEDCASE, LITERAL are just passed on. If NOEDIT is true, no
1159check for `\\?' is made to save time. MATCH-DATA is used for the
1160replacement. In case editing is done, it is changed to use markers.
1161
1162The return value is non-NIL if there has been no `\\?' or NOEDIT was
1163passed in. If LITERAL is set, no checking is done, anyway."
1164 (unless (or literal noedit)
1165 (setq noedit t)
1166 (while (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\\\?\\)"
1167 newtext)
1168 (setq newtext
1169 (read-input "Edit replacement string: "
1170 (prog1
1171 (cons
1172 (replace-match "" t t newtext 3)
1173 (1+ (match-beginning 3)))
1174 (setq match-data
1175 (replace-match-data
1176 nil match-data match-data))))
1177 noedit nil)))
1178 (set-match-data match-data)
1179 (replace-match newtext fixedcase literal)
1180 noedit)
1181
1118(defun perform-replace (from-string replacements 1182(defun perform-replace (from-string replacements
1119 query-flag regexp-flag delimited-flag 1183 query-flag regexp-flag delimited-flag
1120 &optional repeat-count map start end) 1184 &optional repeat-count map start end)
@@ -1145,6 +1209,7 @@ make, or the user didn't cancel the call."
1145 (search-string from-string) 1209 (search-string from-string)
1146 (real-match-data nil) ; the match data for the current match 1210 (real-match-data nil) ; the match data for the current match
1147 (next-replacement nil) 1211 (next-replacement nil)
1212 (noedit nil)
1148 (keep-going t) 1213 (keep-going t)
1149 (stack nil) 1214 (stack nil)
1150 (replace-count 0) 1215 (replace-count 0)
@@ -1201,7 +1266,9 @@ make, or the user didn't cancel the call."
1201 (setq real-match-data 1266 (setq real-match-data
1202 (if (consp match-again) 1267 (if (consp match-again)
1203 (progn (goto-char (nth 1 match-again)) 1268 (progn (goto-char (nth 1 match-again))
1204 match-again) 1269 (replace-match-data t
1270 real-match-data
1271 match-again))
1205 (and (or match-again 1272 (and (or match-again
1206 ;; MATCH-AGAIN non-nil means we 1273 ;; MATCH-AGAIN non-nil means we
1207 ;; accept an adjacent match. If 1274 ;; accept an adjacent match. If
@@ -1217,7 +1284,7 @@ make, or the user didn't cancel the call."
1217 (funcall search-function search-string limit t) 1284 (funcall search-function search-string limit t)
1218 ;; For speed, use only integers and 1285 ;; For speed, use only integers and
1219 ;; reuse the list used last time. 1286 ;; reuse the list used last time.
1220 (match-data t real-match-data))))) 1287 (replace-match-data t real-match-data)))))
1221 ;; Optionally ignore matches that have a read-only property. 1288 ;; Optionally ignore matches that have a read-only property.
1222 (unless (and query-replace-skip-read-only 1289 (unless (and query-replace-skip-read-only
1223 (text-property-not-all 1290 (text-property-not-all
@@ -1249,16 +1316,23 @@ make, or the user didn't cancel the call."
1249 (set-match-data real-match-data) 1316 (set-match-data real-match-data)
1250 (setq next-replacement 1317 (setq next-replacement
1251 (funcall (car replacements) (cdr replacements) 1318 (funcall (car replacements) (cdr replacements)
1252 replace-count))) 1319 replace-count)
1320 noedit nil))
1253 (if (not query-flag) 1321 (if (not query-flag)
1254 (let ((inhibit-read-only query-replace-skip-read-only)) 1322 (let ((inhibit-read-only query-replace-skip-read-only))
1255 (set-match-data real-match-data) 1323 (setq noedit
1256 (replace-match next-replacement nocasify literal) 1324 (replace-match-maybe-edit
1257 (setq replace-count (1+ replace-count))) 1325 next-replacement nocasify literal
1326 noedit real-match-data)
1327 replace-count (1+ replace-count)))
1258 (undo-boundary) 1328 (undo-boundary)
1259 (let (done replaced key def) 1329 (let (done replaced key def)
1260 ;; Loop reading commands until one of them sets done, 1330 ;; Loop reading commands until one of them sets done,
1261 ;; which means it has finished handling this occurrence. 1331 ;; which means it has finished handling this
1332 ;; occurrence. Any command that sets `done' should
1333 ;; leave behind proper match data for the stack.
1334 ;; Commands not setting `done' need to adjust
1335 ;; `real-match-data'.
1262 (while (not done) 1336 (while (not done)
1263 (set-match-data real-match-data) 1337 (set-match-data real-match-data)
1264 (replace-highlight (match-beginning 0) (match-end 0)) 1338 (replace-highlight (match-beginning 0) (match-end 0))
@@ -1290,37 +1364,49 @@ make, or the user didn't cancel the call."
1290 ((eq def 'backup) 1364 ((eq def 'backup)
1291 (if stack 1365 (if stack
1292 (let ((elt (pop stack))) 1366 (let ((elt (pop stack)))
1293 (goto-char (car elt)) 1367 (goto-char (nth 0 elt))
1294 (setq replaced (eq t (cdr elt))) 1368 (setq replaced (nth 1 elt)
1295 (or replaced 1369 real-match-data
1296 (set-match-data (cdr elt)))) 1370 (replace-match-data
1371 t real-match-data
1372 (nth 2 elt))))
1297 (message "No previous match") 1373 (message "No previous match")
1298 (ding 'no-terminate) 1374 (ding 'no-terminate)
1299 (sit-for 1))) 1375 (sit-for 1)))
1300 ((eq def 'act) 1376 ((eq def 'act)
1301 (or replaced 1377 (or replaced
1302 (progn 1378 (setq noedit
1303 (replace-match next-replacement nocasify literal) 1379 (replace-match-maybe-edit
1304 (setq replace-count (1+ replace-count)))) 1380 next-replacement nocasify literal
1381 noedit real-match-data)
1382 replace-count (1+ replace-count)))
1305 (setq done t replaced t)) 1383 (setq done t replaced t))
1306 ((eq def 'act-and-exit) 1384 ((eq def 'act-and-exit)
1307 (or replaced 1385 (or replaced
1308 (progn 1386 (setq noedit
1309 (replace-match next-replacement nocasify literal) 1387 (replace-match-maybe-edit
1310 (setq replace-count (1+ replace-count)))) 1388 next-replacement nocasify literal
1389 noedit real-match-data)
1390 replace-count (1+ replace-count)))
1311 (setq keep-going nil) 1391 (setq keep-going nil)
1312 (setq done t replaced t)) 1392 (setq done t replaced t))
1313 ((eq def 'act-and-show) 1393 ((eq def 'act-and-show)
1314 (if (not replaced) 1394 (if (not replaced)
1315 (progn 1395 (setq noedit
1316 (replace-match next-replacement nocasify literal) 1396 (replace-match-maybe-edit
1317 (setq replace-count (1+ replace-count)) 1397 next-replacement nocasify literal
1318 (setq replaced t)))) 1398 noedit real-match-data)
1399 replace-count (1+ replace-count)
1400 real-match-data (replace-match-data
1401 t real-match-data)
1402 replaced t)))
1319 ((eq def 'automatic) 1403 ((eq def 'automatic)
1320 (or replaced 1404 (or replaced
1321 (progn 1405 (setq noedit
1322 (replace-match next-replacement nocasify literal) 1406 (replace-match-maybe-edit
1323 (setq replace-count (1+ replace-count)))) 1407 next-replacement nocasify literal
1408 noedit real-match-data)
1409 replace-count (1+ replace-count)))
1324 (setq done t query-flag nil replaced t)) 1410 (setq done t query-flag nil replaced t))
1325 ((eq def 'skip) 1411 ((eq def 'skip)
1326 (setq done t)) 1412 (setq done t))
@@ -1328,36 +1414,45 @@ make, or the user didn't cancel the call."
1328 (recenter nil)) 1414 (recenter nil))
1329 ((eq def 'edit) 1415 ((eq def 'edit)
1330 (let ((opos (point-marker))) 1416 (let ((opos (point-marker)))
1417 (setq real-match-data (replace-match-data
1418 nil real-match-data
1419 real-match-data))
1331 (goto-char (match-beginning 0)) 1420 (goto-char (match-beginning 0))
1332 (save-excursion 1421 (save-excursion
1333 (funcall search-function search-string limit t)
1334 (setq real-match-data (match-data)))
1335 (save-excursion
1336 (save-window-excursion 1422 (save-window-excursion
1337 (recursive-edit))) 1423 (recursive-edit)))
1338 (goto-char opos)) 1424 (goto-char opos)
1339 (set-match-data real-match-data) 1425 (set-marker opos nil))
1340 ;; Before we make the replacement, 1426 ;; Before we make the replacement,
1341 ;; decide whether the search string 1427 ;; decide whether the search string
1342 ;; can match again just after this match. 1428 ;; can match again just after this match.
1343 (if (and regexp-flag nonempty-match) 1429 (if (and regexp-flag nonempty-match)
1344 (setq match-again (and (looking-at search-string) 1430 (setq match-again (and (looking-at search-string)
1345 (match-data))))) 1431 (match-data)))))
1346
1347 ;; Edit replacement. 1432 ;; Edit replacement.
1348 ((eq def 'edit-replacement) 1433 ((eq def 'edit-replacement)
1349 (setq next-replacement 1434 (setq real-match-data (replace-match-data
1435 nil real-match-data
1436 real-match-data)
1437 next-replacement
1350 (read-input "Edit replacement string: " 1438 (read-input "Edit replacement string: "
1351 next-replacement)) 1439 next-replacement)
1352 (or replaced 1440 noedit nil)
1353 (replace-match next-replacement nocasify literal)) 1441 (if replaced
1442 (set-match-data real-match-data)
1443 (setq noedit
1444 (replace-match-maybe-edit
1445 next-replacement nocasify literal noedit
1446 real-match-data)
1447 replaced t))
1354 (setq done t)) 1448 (setq done t))
1355 1449
1356 ((eq def 'delete-and-edit) 1450 ((eq def 'delete-and-edit)
1357 (delete-region (match-beginning 0) (match-end 0)) 1451 (replace-match "" t t)
1358 (set-match-data 1452 (setq real-match-data (replace-match-data
1359 (prog1 (match-data) 1453 nil real-match-data))
1360 (save-excursion (recursive-edit)))) 1454 (replace-dehighlight)
1455 (save-excursion (recursive-edit))
1361 (setq replaced t)) 1456 (setq replaced t))
1362 ;; Note: we do not need to treat `exit-prefix' 1457 ;; Note: we do not need to treat `exit-prefix'
1363 ;; specially here, since we reread 1458 ;; specially here, since we reread
@@ -1372,10 +1467,23 @@ make, or the user didn't cancel the call."
1372 ;; Record previous position for ^ when we move on. 1467 ;; Record previous position for ^ when we move on.
1373 ;; Change markers to numbers in the match data 1468 ;; Change markers to numbers in the match data
1374 ;; since lots of markers slow down editing. 1469 ;; since lots of markers slow down editing.
1375 (setq stack 1470 (push (list (point) replaced
1376 (cons (cons (point) 1471;;; If the replacement has already happened, all we need is the
1377 (or replaced (match-data t))) 1472;;; current match start and end. We could get this with a trivial
1378 stack)))))) 1473;;; match like
1474;;; (save-excursion (goto-char (match-beginning 0))
1475;;; (search-forward (match-string 0))
1476;;; (match-data t))
1477;;; if we really wanted to avoid manually constructing match data.
1478;;; Adding current-buffer is necessary so that match-data calls can
1479;;; return markers which are appropriate for editing.
1480 (if replaced
1481 (list
1482 (match-beginning 0)
1483 (match-end 0)
1484 (current-buffer))
1485 (match-data t)))
1486 stack)))))
1379 1487
1380 ;; The code preventing adjacent regexp matches in the condition 1488 ;; The code preventing adjacent regexp matches in the condition
1381 ;; of the while-loop above will haven taken us one character 1489 ;; of the while-loop above will haven taken us one character
@@ -1405,14 +1513,12 @@ make, or the user didn't cancel the call."
1405 1513
1406(defun replace-highlight (start end) 1514(defun replace-highlight (start end)
1407 (and query-replace-highlight 1515 (and query-replace-highlight
1408 (progn 1516 (if replace-overlay
1409 (or replace-overlay 1517 (move-overlay replace-overlay start end (current-buffer))
1410 (progn 1518 (setq replace-overlay (make-overlay start end))
1411 (setq replace-overlay (make-overlay start end)) 1519 (overlay-put replace-overlay 'face
1412 (overlay-put replace-overlay 'face 1520 (if (facep 'query-replace)
1413 (if (facep 'query-replace) 1521 'query-replace 'region)))))
1414 'query-replace 'region))))
1415 (move-overlay replace-overlay start end (current-buffer)))))
1416 1522
1417;;; arch-tag: 16b4cd61-fd40-497b-b86f-b667c4cf88e4 1523;;; arch-tag: 16b4cd61-fd40-497b-b86f-b667c4cf88e4
1418;;; replace.el ends here 1524;;; replace.el ends here