aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Gutov2015-11-14 13:02:35 +0200
committerDmitry Gutov2015-11-14 13:02:35 +0200
commitf234fc2cb319de1e5e2eca1a84450ec220ce7955 (patch)
tree37134b43d4270bf955757a7c7f571756b493c7a8
parent4d71d2471aaf341791fd728287bf8db62aebb3ba (diff)
parent138ad3d93b7abe08ac399f582aa6c8aac869e17e (diff)
downloademacs-f234fc2cb319de1e5e2eca1a84450ec220ce7955.tar.gz
emacs-f234fc2cb319de1e5e2eca1a84450ec220ce7955.zip
Merge branch 'master' into emacs-25
-rw-r--r--lisp/emulation/cua-rect.el26
-rw-r--r--lisp/progmodes/elisp-mode.el41
-rw-r--r--lisp/progmodes/etags.el21
-rw-r--r--lisp/progmodes/xref.el249
-rw-r--r--lisp/rect.el32
-rw-r--r--lisp/replace.el87
-rw-r--r--lisp/simple.el229
-rw-r--r--lisp/vc/diff-mode.el2
-rw-r--r--src/casefiddle.c22
9 files changed, 415 insertions, 294 deletions
diff --git a/lisp/emulation/cua-rect.el b/lisp/emulation/cua-rect.el
index ea8b52476f7..d389f6ec0a2 100644
--- a/lisp/emulation/cua-rect.el
+++ b/lisp/emulation/cua-rect.el
@@ -666,6 +666,22 @@ If command is repeated at same position, delete the rectangle."
666 (setq rect (cons row rect)))))) 666 (setq rect (cons row rect))))))
667 (nreverse rect))) 667 (nreverse rect)))
668 668
669(defun cua--extract-rectangle-bounds ()
670 (let (rect)
671 (if (not (cua--rectangle-virtual-edges))
672 (cua--rectangle-operation nil nil nil nil nil ; do not tabify
673 (lambda (s e _l _r)
674 (setq rect (cons (cons s e) rect))))
675 (cua--rectangle-operation nil 1 nil nil nil ; do not tabify
676 (lambda (s e l r _v)
677 (goto-char s)
678 (move-to-column l)
679 (setq s (point))
680 (move-to-column r)
681 (setq e (point))
682 (setq rect (cons (cons s e) rect)))))
683 (nreverse rect)))
684
669(defun cua--insert-rectangle (rect &optional below paste-column line-count) 685(defun cua--insert-rectangle (rect &optional below paste-column line-count)
670 ;; Insert rectangle as insert-rectangle, but don't set mark and exit with 686 ;; Insert rectangle as insert-rectangle, but don't set mark and exit with
671 ;; point at either next to top right or below bottom left corner 687 ;; point at either next to top right or below bottom left corner
@@ -1394,6 +1410,8 @@ With prefix arg, indent to that column."
1394 1410
1395(add-function :around region-extract-function 1411(add-function :around region-extract-function
1396 #'cua--rectangle-region-extract) 1412 #'cua--rectangle-region-extract)
1413(add-function :around region-insert-function
1414 #'cua--insert-rectangle)
1397(add-function :around redisplay-highlight-region-function 1415(add-function :around redisplay-highlight-region-function
1398 #'cua--rectangle-highlight-for-redisplay) 1416 #'cua--rectangle-highlight-for-redisplay)
1399 1417
@@ -1405,8 +1423,12 @@ With prefix arg, indent to that column."
1405 1423
1406(defun cua--rectangle-region-extract (orig &optional delete) 1424(defun cua--rectangle-region-extract (orig &optional delete)
1407 (cond 1425 (cond
1408 ((not cua--rectangle) (funcall orig delete)) 1426 ((not cua--rectangle)
1409 ((eq delete 'delete-only) (cua--delete-rectangle)) 1427 (funcall orig delete))
1428 ((eq delete 'bounds)
1429 (cua--extract-rectangle-bounds))
1430 ((eq delete 'delete-only)
1431 (cua--delete-rectangle))
1410 (t 1432 (t
1411 (let* ((strs (cua--extract-rectangle)) 1433 (let* ((strs (cua--extract-rectangle))
1412 (str (mapconcat #'identity strs "\n"))) 1434 (str (mapconcat #'identity strs "\n")))
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
index af2ea56dcee..2c22483e86f 100644
--- a/lisp/progmodes/elisp-mode.el
+++ b/lisp/progmodes/elisp-mode.el
@@ -228,8 +228,7 @@ Blank lines separate paragraphs. Semicolons start comments.
228 228
229\\{emacs-lisp-mode-map}" 229\\{emacs-lisp-mode-map}"
230 :group 'lisp 230 :group 'lisp
231 (defvar xref-find-function) 231 (defvar xref-backend-functions)
232 (defvar xref-identifier-completion-table-function)
233 (defvar project-library-roots-function) 232 (defvar project-library-roots-function)
234 (lisp-mode-variables nil nil 'elisp) 233 (lisp-mode-variables nil nil 'elisp)
235 (add-hook 'after-load-functions #'elisp--font-lock-flush-elisp-buffers) 234 (add-hook 'after-load-functions #'elisp--font-lock-flush-elisp-buffers)
@@ -239,9 +238,7 @@ Blank lines separate paragraphs. Semicolons start comments.
239 (setq imenu-case-fold-search nil) 238 (setq imenu-case-fold-search nil)
240 (add-function :before-until (local 'eldoc-documentation-function) 239 (add-function :before-until (local 'eldoc-documentation-function)
241 #'elisp-eldoc-documentation-function) 240 #'elisp-eldoc-documentation-function)
242 (setq-local xref-find-function #'elisp-xref-find) 241 (add-hook 'xref-backend-functions #'elisp--xref-backend nil t)
243 (setq-local xref-identifier-completion-table-function
244 #'elisp--xref-identifier-completion-table)
245 (setq-local project-library-roots-function #'elisp-library-roots) 242 (setq-local project-library-roots-function #'elisp-library-roots)
246 (add-hook 'completion-at-point-functions 243 (add-hook 'completion-at-point-functions
247 #'elisp-completion-at-point nil 'local)) 244 #'elisp-completion-at-point nil 'local))
@@ -588,21 +585,7 @@ It can be quoted, or be inside a quoted form."
588(declare-function xref-make "xref" (summary location)) 585(declare-function xref-make "xref" (summary location))
589(declare-function xref-collect-references "xref" (symbol dir)) 586(declare-function xref-collect-references "xref" (symbol dir))
590 587
591(defun elisp-xref-find (action id) 588(defun elisp--xref-backend () 'elisp)
592 (require 'find-func)
593 ;; FIXME: use information in source near point to filter results:
594 ;; (dvc-log-edit ...) - exclude 'feature
595 ;; (require 'dvc-log-edit) - only 'feature
596 ;; Semantic may provide additional information
597 (pcase action
598 (`definitions
599 (let ((sym (intern-soft id)))
600 (when sym
601 (elisp--xref-find-definitions sym))))
602 (`references
603 (elisp--xref-find-references id))
604 (`apropos
605 (elisp--xref-find-apropos id))))
606 589
607;; WORKAROUND: This is nominally a constant, but the text properties 590;; WORKAROUND: This is nominally a constant, but the text properties
608;; are not preserved thru dump if use defconst. See bug#21237. 591;; are not preserved thru dump if use defconst. See bug#21237.
@@ -638,7 +621,17 @@ Each function should return a list of xrefs, or nil; the first
638non-nil result supercedes the xrefs produced by 621non-nil result supercedes the xrefs produced by
639`elisp--xref-find-definitions'.") 622`elisp--xref-find-definitions'.")
640 623
641;; FIXME: name should be singular; match xref-find-definition 624(cl-defmethod xref-backend-definitions ((_backend (eql elisp)) identifier)
625 (require 'find-func)
626 ;; FIXME: use information in source near point to filter results:
627 ;; (dvc-log-edit ...) - exclude 'feature
628 ;; (require 'dvc-log-edit) - only 'feature
629 ;; Semantic may provide additional information
630 ;;
631 (let ((sym (intern-soft identifier)))
632 (when sym
633 (elisp--xref-find-definitions sym))))
634
642(defun elisp--xref-find-definitions (symbol) 635(defun elisp--xref-find-definitions (symbol)
643 ;; The file name is not known when `symbol' is defined via interactive eval. 636 ;; The file name is not known when `symbol' is defined via interactive eval.
644 (let (xrefs) 637 (let (xrefs)
@@ -805,7 +798,7 @@ non-nil result supercedes the xrefs produced by
805(declare-function project-roots "project") 798(declare-function project-roots "project")
806(declare-function project-current "project") 799(declare-function project-current "project")
807 800
808(defun elisp--xref-find-references (symbol) 801(cl-defmethod xref-backend-references ((_backend (eql elisp)) symbol)
809 "Find all references to SYMBOL (a string) in the current project." 802 "Find all references to SYMBOL (a string) in the current project."
810 (cl-mapcan 803 (cl-mapcan
811 (lambda (dir) 804 (lambda (dir)
@@ -815,7 +808,7 @@ non-nil result supercedes the xrefs produced by
815 (project-roots pr) 808 (project-roots pr)
816 (project-library-roots pr))))) 809 (project-library-roots pr)))))
817 810
818(defun elisp--xref-find-apropos (regexp) 811(cl-defmethod xref-backend-apropos ((_backend (eql elisp)) regexp)
819 (apply #'nconc 812 (apply #'nconc
820 (let (lst) 813 (let (lst)
821 (dolist (sym (apropos-internal regexp)) 814 (dolist (sym (apropos-internal regexp))
@@ -832,7 +825,7 @@ non-nil result supercedes the xrefs produced by
832 (facep sym))) 825 (facep sym)))
833 'strict)) 826 'strict))
834 827
835(defun elisp--xref-identifier-completion-table () 828(cl-defmethod xref-backend-identifier-completion-table ((_backend (eql elisp)))
836 elisp--xref-identifier-completion-table) 829 elisp--xref-identifier-completion-table)
837 830
838(cl-defstruct (xref-elisp-location 831(cl-defstruct (xref-elisp-location
diff --git a/lisp/progmodes/etags.el b/lisp/progmodes/etags.el
index 38c5cc2bdb6..ae1aa11fbc2 100644
--- a/lisp/progmodes/etags.el
+++ b/lisp/progmodes/etags.el
@@ -2084,17 +2084,12 @@ for \\[find-tag] (which see)."
2084 2084
2085(defvar etags-xref-find-definitions-tag-order '(tag-exact-match-p 2085(defvar etags-xref-find-definitions-tag-order '(tag-exact-match-p
2086 tag-implicit-name-match-p) 2086 tag-implicit-name-match-p)
2087 "Tag order used in `etags-xref-find' to look for definitions.") 2087 "Tag order used in `xref-backend-definitions' to look for definitions.")
2088 2088
2089;;;###autoload 2089(cl-defmethod xref-backend-identifier-completion-table ((_backend (eql etags)))
2090(defun etags-xref-find (action id) 2090 (tags-lazy-completion-table))
2091 (pcase action 2091
2092 (`definitions (etags--xref-find-definitions id)) 2092(cl-defmethod xref-backend-references ((_backend (eql etags)) symbol)
2093 (`references (etags--xref-find-references id))
2094 (`apropos (etags--xref-find-definitions id t))))
2095
2096(defun etags--xref-find-references (symbol)
2097 ;; TODO: Merge together with the Elisp impl.
2098 (cl-mapcan 2093 (cl-mapcan
2099 (lambda (dir) 2094 (lambda (dir)
2100 (xref-collect-references symbol dir)) 2095 (xref-collect-references symbol dir))
@@ -2103,6 +2098,12 @@ for \\[find-tag] (which see)."
2103 (project-roots pr) 2098 (project-roots pr)
2104 (project-library-roots pr))))) 2099 (project-library-roots pr)))))
2105 2100
2101(cl-defmethod xref-backend-definitions ((_backend (eql etags)) symbol)
2102 (etags--xref-find-definitions symbol))
2103
2104(cl-defmethod xref-backend-apropos ((_backend (eql etags)) symbol)
2105 (etags--xref-find-definitions symbol t))
2106
2106(defun etags--xref-find-definitions (pattern &optional regexp?) 2107(defun etags--xref-find-definitions (pattern &optional regexp?)
2107 ;; This emulates the behaviour of `find-tag-in-order' but instead of 2108 ;; This emulates the behaviour of `find-tag-in-order' but instead of
2108 ;; returning one match at a time all matches are returned as list. 2109 ;; returning one match at a time all matches are returned as list.
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el
index 89a06046ca2..6a3b42ff646 100644
--- a/lisp/progmodes/xref.el
+++ b/lisp/progmodes/xref.el
@@ -23,14 +23,21 @@
23;; referencing commands, in particular "find-definition". 23;; referencing commands, in particular "find-definition".
24;; 24;;
25;; Some part of the functionality must be implemented in a language 25;; Some part of the functionality must be implemented in a language
26;; dependent way and that's done by defining `xref-find-function', 26;; dependent way and that's done by defining an xref backend.
27;; `xref-identifier-at-point-function' and
28;; `xref-identifier-completion-table-function', which see.
29;; 27;;
30;; A major mode should make these variables buffer-local first. 28;; That consists of a constructor function, which should return a
29;; backend value, and a set of implementations for the generic
30;; functions:
31;; 31;;
32;; `xref-find-function' can be called in several ways, see its 32;; `xref-backend-identifier-at-point',
33;; description. It has to operate with "xref" and "location" values. 33;; `xref-backend-identifier-completion-table',
34;; `xref-backend-definitions', `xref-backend-references',
35;; `xref-backend-apropos', which see.
36;;
37;; A major mode would normally use `add-hook' to add the backend
38;; constructor to `xref-backend-functions'.
39;;
40;; The last three methods operate with "xref" and "location" values.
34;; 41;;
35;; One would usually call `make-xref' and `xref-make-file-location', 42;; One would usually call `make-xref' and `xref-make-file-location',
36;; `xref-make-buffer-location' or `xref-make-bogus-location' to create 43;; `xref-make-buffer-location' or `xref-make-bogus-location' to create
@@ -38,15 +45,19 @@
38;; class inheriting from `xref-location' and implementing 45;; class inheriting from `xref-location' and implementing
39;; `xref-location-group' and `xref-location-marker'. 46;; `xref-location-group' and `xref-location-marker'.
40;; 47;;
48;; There's a special kind of xrefs we call "match xrefs", which
49;; correspond to search results. For these values,
50;; `xref-match-length' must be defined, and `xref-location-marker'
51;; must return the beginning of the match.
52;;
41;; Each identifier must be represented as a string. Implementers can 53;; Each identifier must be represented as a string. Implementers can
42;; use string properties to store additional information about the 54;; use string properties to store additional information about the
43;; identifier, but they should keep in mind that values returned from 55;; identifier, but they should keep in mind that values returned from
44;; `xref-identifier-completion-table-function' should still be 56;; `xref-backend-identifier-completion-table' should still be
45;; distinct, because the user can't see the properties when making the 57;; distinct, because the user can't see the properties when making the
46;; choice. 58;; choice.
47;; 59;;
48;; See the functions `etags-xref-find' and `elisp-xref-find' for full 60;; See the etags and elisp-mode implementations for full examples.
49;; examples.
50 61
51;;; Code: 62;;; Code:
52 63
@@ -79,8 +90,8 @@ This is typically the filename.")
79 "Return the line number corresponding to the location." 90 "Return the line number corresponding to the location."
80 nil) 91 nil)
81 92
82(cl-defgeneric xref-match-bounds (_item) 93(cl-defgeneric xref-match-length (_item)
83 "Return a cons with columns of the beginning and end of the match." 94 "Return the length of the match."
84 nil) 95 nil)
85 96
86;;;; Commonly needed location classes are defined here: 97;;;; Commonly needed location classes are defined here:
@@ -109,7 +120,7 @@ Line numbers start from 1 and columns from 0.")
109 (save-excursion 120 (save-excursion
110 (goto-char (point-min)) 121 (goto-char (point-min))
111 (beginning-of-line line) 122 (beginning-of-line line)
112 (move-to-column column) 123 (forward-char column)
113 (point-marker)))))) 124 (point-marker))))))
114 125
115(cl-defmethod xref-location-group ((l xref-file-location)) 126(cl-defmethod xref-location-group ((l xref-file-location))
@@ -176,55 +187,60 @@ LOCATION is an `xref-location'."
176 (location :initarg :location 187 (location :initarg :location
177 :type xref-file-location 188 :type xref-file-location
178 :reader xref-item-location) 189 :reader xref-item-location)
179 (end-column :initarg :end-column)) 190 (length :initarg :length :reader xref-match-length))
180 :comment "An xref item describes a reference to a location 191 :comment "A match xref item describes a search result.")
181somewhere.")
182
183(cl-defmethod xref-match-bounds ((i xref-match-item))
184 (with-slots (end-column location) i
185 (cons (xref-file-location-column location)
186 end-column)))
187 192
188(defun xref-make-match (summary end-column location) 193(defun xref-make-match (summary location length)
189 "Create and return a new `xref-match-item'. 194 "Create and return a new `xref-match-item'.
190SUMMARY is a short string to describe the xref. 195SUMMARY is a short string to describe the xref.
191END-COLUMN is the match end column number inside SUMMARY. 196LOCATION is an `xref-location'.
192LOCATION is an `xref-location'." 197LENGTH is the match length, in characters."
193 (make-instance 'xref-match-item :summary summary :location location 198 (make-instance 'xref-match-item :summary summary
194 :end-column end-column)) 199 :location location :length length))
195 200
196 201
197;;; API 202;;; API
198 203
199(declare-function etags-xref-find "etags" (action id)) 204;; We make the etags backend the default for now, until something
200(declare-function tags-lazy-completion-table "etags" ()) 205;; better comes along.
206(defvar xref-backend-functions (list #'xref--etags-backend)
207 "Special hook to find the xref backend for the current context.
208Each functions on this hook is called in turn with no arguments
209and should return either nil to mean that it is not applicable,
210or an xref backend, which is a value to be used to dispatch the
211generic functions.")
201 212
202;; For now, make the etags backend the default. 213(defun xref-find-backend ()
203(defvar xref-find-function #'etags-xref-find 214 (run-hook-with-args-until-success 'xref-backend-functions))
204 "Function to look for cross-references.
205It can be called in several ways:
206 215
207 (definitions IDENTIFIER): Find definitions of IDENTIFIER. The 216(defun xref--etags-backend () 'etags)
208result must be a list of xref objects. If IDENTIFIER contains
209sufficient information to determine a unique definition, returns
210only that definition. If there are multiple possible definitions,
211return all of them. If no definitions can be found, return nil.
212 217
213 (references IDENTIFIER): Find references of IDENTIFIER. The 218(cl-defgeneric xref-backend-definitions (backend identifier)
214result must be a list of xref objects. If no references can be 219 "Find definitions of IDENTIFIER.
215found, return nil.
216 220
217 (apropos PATTERN): Find all symbols that match PATTERN. PATTERN 221The result must be a list of xref objects. If IDENTIFIER
218is a regexp. 222contains sufficient information to determine a unique definition,
223return only that definition. If there are multiple possible
224definitions, return all of them. If no definitions can be found,
225return nil.
219 226
220IDENTIFIER can be any string returned by 227IDENTIFIER can be any string returned by
221`xref-identifier-at-point-function', or from the table returned 228`xref-backend-identifier-at-point', or from the table returned by
222by `xref-identifier-completion-table-function'. 229`xref-backend-identifier-completion-table'.
223 230
224To create an xref object, call `xref-make'.") 231To create an xref object, call `xref-make'.")
225 232
226(defvar xref-identifier-at-point-function #'xref-default-identifier-at-point 233(cl-defgeneric xref-backend-references (backend identifier)
227 "Function to get the relevant identifier at point. 234 "Find references of IDENTIFIER.
235The result must be a list of xref objects. If no references can
236be found, return nil.")
237
238(cl-defgeneric xref-backend-apropos (backend pattern)
239 "Find all symbols that match PATTERN.
240PATTERN is a regexp")
241
242(cl-defgeneric xref-backend-identifier-at-point (_backend)
243 "Return the relevant identifier at point.
228 244
229The return value must be a string or nil. nil means no 245The return value must be a string or nil. nil means no
230identifier at point found. 246identifier at point found.
@@ -232,16 +248,14 @@ identifier at point found.
232If it's hard to determine the identifier precisely (e.g., because 248If it's hard to determine the identifier precisely (e.g., because
233it's a method call on unknown type), the implementation can 249it's a method call on unknown type), the implementation can
234return a simple string (such as symbol at point) marked with a 250return a simple string (such as symbol at point) marked with a
235special text property which `xref-find-function' would recognize 251special text property which e.g. `xref-backend-definitions' would
236and then delegate the work to an external process.") 252recognize and then delegate the work to an external process."
237
238(defvar xref-identifier-completion-table-function #'tags-lazy-completion-table
239 "Function that returns the completion table for identifiers.")
240
241(defun xref-default-identifier-at-point ()
242 (let ((thing (thing-at-point 'symbol))) 253 (let ((thing (thing-at-point 'symbol)))
243 (and thing (substring-no-properties thing)))) 254 (and thing (substring-no-properties thing))))
244 255
256(cl-defgeneric xref-backend-identifier-completion-table (backend)
257 "Returns the completion table for identifiers.")
258
245 259
246;;; misc utilities 260;;; misc utilities
247(defun xref--alistify (list key test) 261(defun xref--alistify (list key test)
@@ -345,22 +359,14 @@ elements is negated."
345 (pcase-let ((`(,beg . ,end) 359 (pcase-let ((`(,beg . ,end)
346 (save-excursion 360 (save-excursion
347 (or 361 (or
348 (xref--match-buffer-bounds xref--current-item) 362 (let ((length (xref-match-length xref--current-item)))
363 (and length (cons (point) (+ (point) length))))
349 (back-to-indentation) 364 (back-to-indentation)
350 (if (eolp) 365 (if (eolp)
351 (cons (line-beginning-position) (1+ (point))) 366 (cons (line-beginning-position) (1+ (point)))
352 (cons (point) (line-end-position))))))) 367 (cons (point) (line-end-position)))))))
353 (pulse-momentary-highlight-region beg end 'next-error))) 368 (pulse-momentary-highlight-region beg end 'next-error)))
354 369
355(defun xref--match-buffer-bounds (item)
356 (save-excursion
357 (let ((bounds (xref-match-bounds item)))
358 (when bounds
359 (cons (progn (move-to-column (car bounds))
360 (point))
361 (progn (move-to-column (cdr bounds))
362 (point)))))))
363
364;; etags.el needs this 370;; etags.el needs this
365(defun xref-clear-marker-stack () 371(defun xref-clear-marker-stack ()
366 "Discard all markers from the marker stack." 372 "Discard all markers from the marker stack."
@@ -487,50 +493,54 @@ WINDOW controls how the buffer is displayed:
487 (progn 493 (progn
488 (save-excursion 494 (save-excursion
489 (goto-char (point-min)) 495 (goto-char (point-min))
490 ;; TODO: Check that none of the matches are out of date;
491 ;; offer to re-scan otherwise. Note that saving the last
492 ;; modification tick won't work, as long as not all of the
493 ;; buffers are kept open.
494 (while (setq item (xref--search-property 'xref-item)) 496 (while (setq item (xref--search-property 'xref-item))
495 (when (xref-match-bounds item) 497 (when (xref-match-length item)
496 (save-excursion 498 (save-excursion
497 ;; FIXME: Get rid of xref--goto-location, by making 499 (let* ((loc (xref-item-location item))
498 ;; xref-match-bounds return markers already. 500 (beg (xref-location-marker loc))
499 (xref--goto-location (xref-item-location item)) 501 (len (xref-match-length item)))
500 (let ((bounds (xref--match-buffer-bounds item)) 502 ;; Perform sanity check first.
501 (beg (make-marker)) 503 (xref--goto-location loc)
502 (end (make-marker))) 504 ;; FIXME: The check should probably be a generic
503 (move-marker beg (car bounds)) 505 ;; function, instead of the assumption that all
504 (move-marker end (cdr bounds)) 506 ;; matches contain the full line as summary.
505 (push (cons beg end) pairs))))) 507 ;; TODO: Offer to re-scan otherwise.
508 (unless (equal (buffer-substring-no-properties
509 (line-beginning-position)
510 (line-end-position))
511 (xref-item-summary item))
512 (user-error "Search results out of date"))
513 (push (cons beg len) pairs)))))
506 (setq pairs (nreverse pairs))) 514 (setq pairs (nreverse pairs)))
507 (unless pairs (user-error "No suitable matches here")) 515 (unless pairs (user-error "No suitable matches here"))
508 (xref--query-replace-1 from to pairs)) 516 (xref--query-replace-1 from to pairs))
509 (dolist (pair pairs) 517 (dolist (pair pairs)
510 (move-marker (car pair) nil) 518 (move-marker (car pair) nil)))))
511 (move-marker (cdr pair) nil)))))
512 519
520;; FIXME: Write a nicer UI.
513(defun xref--query-replace-1 (from to pairs) 521(defun xref--query-replace-1 (from to pairs)
514 (let* ((query-replace-lazy-highlight nil) 522 (let* ((query-replace-lazy-highlight nil)
515 current-pair current-buf 523 current-beg current-len current-buf
516 ;; Counteract the "do the next match now" hack in 524 ;; Counteract the "do the next match now" hack in
517 ;; `perform-replace'. And still, it'll report that those 525 ;; `perform-replace'. And still, it'll report that those
518 ;; matches were "filtered out" at the end. 526 ;; matches were "filtered out" at the end.
519 (isearch-filter-predicate 527 (isearch-filter-predicate
520 (lambda (beg end) 528 (lambda (beg end)
521 (and current-pair 529 (and current-beg
522 (eq (current-buffer) current-buf) 530 (eq (current-buffer) current-buf)
523 (>= beg (car current-pair)) 531 (>= beg current-beg)
524 (<= end (cdr current-pair))))) 532 (<= end (+ current-beg current-len)))))
525 (replace-re-search-function 533 (replace-re-search-function
526 (lambda (from &optional _bound noerror) 534 (lambda (from &optional _bound noerror)
527 (let (found) 535 (let (found pair)
528 (while (and (not found) pairs) 536 (while (and (not found) pairs)
529 (setq current-pair (pop pairs) 537 (setq pair (pop pairs)
530 current-buf (marker-buffer (car current-pair))) 538 current-beg (car pair)
539 current-len (cdr pair)
540 current-buf (marker-buffer current-beg))
531 (pop-to-buffer current-buf) 541 (pop-to-buffer current-buf)
532 (goto-char (car current-pair)) 542 (goto-char current-beg)
533 (when (re-search-forward from (cdr current-pair) noerror) 543 (when (re-search-forward from (+ current-beg current-len) noerror)
534 (setq found t))) 544 (setq found t)))
535 found)))) 545 found))))
536 ;; FIXME: Despite this being a multi-buffer replacement, `N' 546 ;; FIXME: Despite this being a multi-buffer replacement, `N'
@@ -695,7 +705,8 @@ Return an alist of the form ((FILENAME . (XREF ...)) ...)."
695 705
696(defun xref--read-identifier (prompt) 706(defun xref--read-identifier (prompt)
697 "Return the identifier at point or read it from the minibuffer." 707 "Return the identifier at point or read it from the minibuffer."
698 (let ((id (funcall xref-identifier-at-point-function))) 708 (let* ((backend (xref-find-backend))
709 (id (xref-backend-identifier-at-point backend)))
699 (cond ((or current-prefix-arg 710 (cond ((or current-prefix-arg
700 (not id) 711 (not id)
701 (xref--prompt-p this-command)) 712 (xref--prompt-p this-command))
@@ -705,7 +716,7 @@ Return an alist of the form ((FILENAME . (XREF ...)) ...)."
705 "[ :]+\\'" prompt)) 716 "[ :]+\\'" prompt))
706 id) 717 id)
707 prompt) 718 prompt)
708 (funcall xref-identifier-completion-table-function) 719 (xref-backend-identifier-completion-table backend)
709 nil nil nil 720 nil nil nil
710 'xref--read-identifier-history id)) 721 'xref--read-identifier-history id))
711 (t id)))) 722 (t id))))
@@ -714,7 +725,9 @@ Return an alist of the form ((FILENAME . (XREF ...)) ...)."
714;;; Commands 725;;; Commands
715 726
716(defun xref--find-xrefs (input kind arg window) 727(defun xref--find-xrefs (input kind arg window)
717 (let ((xrefs (funcall xref-find-function kind arg))) 728 (let ((xrefs (funcall (intern (format "xref-backend-%s" kind))
729 (xref-find-backend)
730 arg)))
718 (unless xrefs 731 (unless xrefs
719 (user-error "No %s found for: %s" (symbol-name kind) input)) 732 (user-error "No %s found for: %s" (symbol-name kind) input))
720 (xref--show-xrefs xrefs window))) 733 (xref--show-xrefs xrefs window)))
@@ -799,14 +812,9 @@ and just use etags."
799 :lighter "" 812 :lighter ""
800 (if xref-etags-mode 813 (if xref-etags-mode
801 (progn 814 (progn
802 (setq xref-etags-mode--saved 815 (setq xref-etags-mode--saved xref-backend-functions)
803 (cons xref-find-function 816 (kill-local-variable 'xref-backend-functions))
804 xref-identifier-completion-table-function)) 817 (setq-local xref-backend-functions xref-etags-mode--saved)))
805 (kill-local-variable 'xref-find-function)
806 (kill-local-variable 'xref-identifier-completion-table-function))
807 (setq-local xref-find-function (car xref-etags-mode--saved))
808 (setq-local xref-identifier-completion-table-function
809 (cdr xref-etags-mode--saved))))
810 818
811(declare-function semantic-symref-find-references-by-name "semantic/symref") 819(declare-function semantic-symref-find-references-by-name "semantic/symref")
812(declare-function semantic-find-file-noselect "semantic/fw") 820(declare-function semantic-find-file-noselect "semantic/fw")
@@ -826,10 +834,11 @@ tools are used, and when."
826 (hits (and res (oref res hit-lines))) 834 (hits (and res (oref res hit-lines)))
827 (orig-buffers (buffer-list))) 835 (orig-buffers (buffer-list)))
828 (unwind-protect 836 (unwind-protect
829 (delq nil 837 (cl-mapcan (lambda (hit) (xref--collect-matches
830 (mapcar (lambda (hit) (xref--collect-match 838 hit (format "\\_<%s\\_>" (regexp-quote symbol))))
831 hit (format "\\_<%s\\_>" (regexp-quote symbol)))) 839 hits)
832 hits)) 840 ;; TODO: Implement "lightweight" buffer visiting, so that we
841 ;; don't have to kill them.
833 (mapc #'kill-buffer 842 (mapc #'kill-buffer
834 (cl-set-difference (buffer-list) orig-buffers))))) 843 (cl-set-difference (buffer-list) orig-buffers)))))
835 844
@@ -860,9 +869,9 @@ IGNORES is a list of glob patterns."
860 (match-string 1)) 869 (match-string 1))
861 hits))) 870 hits)))
862 (unwind-protect 871 (unwind-protect
863 (delq nil 872 (cl-mapcan (lambda (hit) (xref--collect-matches hit regexp))
864 (mapcar (lambda (hit) (xref--collect-match hit regexp)) 873 (nreverse hits))
865 (nreverse hits))) 874 ;; TODO: Same as above.
866 (mapc #'kill-buffer 875 (mapc #'kill-buffer
867 (cl-set-difference (buffer-list) orig-buffers))))) 876 (cl-set-difference (buffer-list) orig-buffers)))))
868 877
@@ -918,7 +927,7 @@ IGNORES is a list of glob patterns."
918 (match-string 1 str))))) 927 (match-string 1 str)))))
919 str t t)) 928 str t t))
920 929
921(defun xref--collect-match (hit regexp) 930(defun xref--collect-matches (hit regexp)
922 (pcase-let* ((`(,line . ,file) hit) 931 (pcase-let* ((`(,line . ,file) hit)
923 (buf (or (find-buffer-visiting file) 932 (buf (or (find-buffer-visiting file)
924 (semantic-find-file-noselect file)))) 933 (semantic-find-file-noselect file))))
@@ -926,18 +935,22 @@ IGNORES is a list of glob patterns."
926 (save-excursion 935 (save-excursion
927 (goto-char (point-min)) 936 (goto-char (point-min))
928 (forward-line (1- line)) 937 (forward-line (1- line))
929 (syntax-propertize (line-end-position)) 938 (let ((line-end (line-end-position))
930 ;; TODO: Handle multiple matches per line. 939 (line-beg (line-beginning-position))
931 (when (re-search-forward regexp (line-end-position) t) 940 matches)
932 (goto-char (match-beginning 0)) 941 (syntax-propertize line-end)
933 (let ((loc (xref-make-file-location file line 942 ;; FIXME: This results in several lines with the same
934 (current-column)))) 943 ;; summary. Solve with composite pattern?
935 (goto-char (match-end 0)) 944 (while (re-search-forward regexp line-end t)
936 (xref-make-match (buffer-substring 945 (let* ((beg-column (- (match-beginning 0) line-beg))
937 (line-beginning-position) 946 (end-column (- (match-end 0) line-beg))
938 (line-end-position)) 947 (loc (xref-make-file-location file line beg-column))
939 (current-column) 948 (summary (buffer-substring line-beg line-end)))
940 loc))))))) 949 (add-face-text-property beg-column end-column 'highlight
950 t summary)
951 (push (xref-make-match summary loc (- end-column beg-column))
952 matches)))
953 (nreverse matches))))))
941 954
942(provide 'xref) 955(provide 'xref)
943 956
diff --git a/lisp/rect.el b/lisp/rect.el
index acd3a48f2da..46ebbf259cf 100644
--- a/lisp/rect.el
+++ b/lisp/rect.el
@@ -257,6 +257,19 @@ Return it as a list of strings, one for each line of the rectangle."
257 (apply-on-rectangle 'extract-rectangle-line start end lines) 257 (apply-on-rectangle 'extract-rectangle-line start end lines)
258 (nreverse (cdr lines)))) 258 (nreverse (cdr lines))))
259 259
260(defun extract-rectangle-bounds (start end)
261 "Return the bounds of the rectangle with corners at START and END.
262Return it as a list of (START . END) positions, one for each line of
263the rectangle."
264 (let (bounds)
265 (apply-on-rectangle
266 (lambda (startcol endcol)
267 (move-to-column startcol)
268 (push (cons (prog1 (point) (move-to-column endcol)) (point))
269 bounds))
270 start end)
271 (nreverse bounds)))
272
260(defvar killed-rectangle nil 273(defvar killed-rectangle nil
261 "Rectangle for `yank-rectangle' to insert.") 274 "Rectangle for `yank-rectangle' to insert.")
262 275
@@ -563,6 +576,8 @@ with a prefix argument, prompt for START-AT and FORMAT."
563 #'rectangle--unhighlight-for-redisplay) 576 #'rectangle--unhighlight-for-redisplay)
564(add-function :around region-extract-function 577(add-function :around region-extract-function
565 #'rectangle--extract-region) 578 #'rectangle--extract-region)
579(add-function :around region-insert-function
580 #'rectangle--insert-region)
566 581
567(defvar rectangle-mark-mode-map 582(defvar rectangle-mark-mode-map
568 (let ((map (make-sparse-keymap))) 583 (let ((map (make-sparse-keymap)))
@@ -681,8 +696,12 @@ Ignores `line-move-visual'."
681 696
682 697
683(defun rectangle--extract-region (orig &optional delete) 698(defun rectangle--extract-region (orig &optional delete)
684 (if (not rectangle-mark-mode) 699 (cond
685 (funcall orig delete) 700 ((not rectangle-mark-mode)
701 (funcall orig delete))
702 ((eq delete 'bounds)
703 (extract-rectangle-bounds (region-beginning) (region-end)))
704 (t
686 (let* ((strs (funcall (if delete 705 (let* ((strs (funcall (if delete
687 #'delete-extract-rectangle 706 #'delete-extract-rectangle
688 #'extract-rectangle) 707 #'extract-rectangle)
@@ -696,7 +715,14 @@ Ignores `line-move-visual'."
696 (put-text-property 0 (length str) 'yank-handler 715 (put-text-property 0 (length str) 'yank-handler
697 `(rectangle--insert-for-yank ,strs t) 716 `(rectangle--insert-for-yank ,strs t)
698 str) 717 str)
699 str)))) 718 str)))))
719
720(defun rectangle--insert-region (orig strings)
721 (cond
722 ((not rectangle-mark-mode)
723 (funcall orig strings))
724 (t
725 (funcall #'insert-rectangle strings))))
700 726
701(defun rectangle--insert-for-yank (strs) 727(defun rectangle--insert-for-yank (strs)
702 (push (point) buffer-undo-list) 728 (push (point) buffer-undo-list)
diff --git a/lisp/replace.el b/lisp/replace.el
index d6590c5516a..b6802aeaf57 100644
--- a/lisp/replace.el
+++ b/lisp/replace.el
@@ -284,7 +284,7 @@ the original string if not."
284 (and current-prefix-arg (not (eq current-prefix-arg '-))) 284 (and current-prefix-arg (not (eq current-prefix-arg '-)))
285 (and current-prefix-arg (eq current-prefix-arg '-))))) 285 (and current-prefix-arg (eq current-prefix-arg '-)))))
286 286
287(defun query-replace (from-string to-string &optional delimited start end backward) 287(defun query-replace (from-string to-string &optional delimited start end backward region-noncontiguous-p)
288 "Replace some occurrences of FROM-STRING with TO-STRING. 288 "Replace some occurrences of FROM-STRING with TO-STRING.
289As each match is found, the user must type a character saying 289As each match is found, the user must type a character saying
290what to do with it. For directions, type \\[help-command] at that time. 290what to do with it. For directions, type \\[help-command] at that time.
@@ -328,22 +328,21 @@ To customize possible responses, change the bindings in `query-replace-map'."
328 (if current-prefix-arg 328 (if current-prefix-arg
329 (if (eq current-prefix-arg '-) " backward" " word") 329 (if (eq current-prefix-arg '-) " backward" " word")
330 "") 330 "")
331 (if (and transient-mark-mode mark-active) " in region" "")) 331 (if (use-region-p) " in region" ""))
332 nil))) 332 nil)))
333 (list (nth 0 common) (nth 1 common) (nth 2 common) 333 (list (nth 0 common) (nth 1 common) (nth 2 common)
334 ;; These are done separately here 334 ;; These are done separately here
335 ;; so that command-history will record these expressions 335 ;; so that command-history will record these expressions
336 ;; rather than the values they had this time. 336 ;; rather than the values they had this time.
337 (if (and transient-mark-mode mark-active) 337 (if (use-region-p) (region-beginning))
338 (region-beginning)) 338 (if (use-region-p) (region-end))
339 (if (and transient-mark-mode mark-active) 339 (nth 3 common)
340 (region-end)) 340 (if (use-region-p) (region-noncontiguous-p)))))
341 (nth 3 common)))) 341 (perform-replace from-string to-string t nil delimited nil nil start end backward region-noncontiguous-p))
342 (perform-replace from-string to-string t nil delimited nil nil start end backward))
343 342
344(define-key esc-map "%" 'query-replace) 343(define-key esc-map "%" 'query-replace)
345 344
346(defun query-replace-regexp (regexp to-string &optional delimited start end backward) 345(defun query-replace-regexp (regexp to-string &optional delimited start end backward region-noncontiguous-p)
347 "Replace some things after point matching REGEXP with TO-STRING. 346 "Replace some things after point matching REGEXP with TO-STRING.
348As each match is found, the user must type a character saying 347As each match is found, the user must type a character saying
349what to do with it. For directions, type \\[help-command] at that time. 348what to do with it. For directions, type \\[help-command] at that time.
@@ -408,18 +407,17 @@ Use \\[repeat-complex-command] after this command for details."
408 (if (eq current-prefix-arg '-) " backward" " word") 407 (if (eq current-prefix-arg '-) " backward" " word")
409 "") 408 "")
410 " regexp" 409 " regexp"
411 (if (and transient-mark-mode mark-active) " in region" "")) 410 (if (use-region-p) " in region" ""))
412 t))) 411 t)))
413 (list (nth 0 common) (nth 1 common) (nth 2 common) 412 (list (nth 0 common) (nth 1 common) (nth 2 common)
414 ;; These are done separately here 413 ;; These are done separately here
415 ;; so that command-history will record these expressions 414 ;; so that command-history will record these expressions
416 ;; rather than the values they had this time. 415 ;; rather than the values they had this time.
417 (if (and transient-mark-mode mark-active) 416 (if (use-region-p) (region-beginning))
418 (region-beginning)) 417 (if (use-region-p) (region-end))
419 (if (and transient-mark-mode mark-active) 418 (nth 3 common)
420 (region-end)) 419 (if (use-region-p) (region-noncontiguous-p)))))
421 (nth 3 common)))) 420 (perform-replace regexp to-string t t delimited nil nil start end backward region-noncontiguous-p))
422 (perform-replace regexp to-string t t delimited nil nil start end backward))
423 421
424(define-key esc-map [?\C-%] 'query-replace-regexp) 422(define-key esc-map [?\C-%] 'query-replace-regexp)
425 423
@@ -485,10 +483,8 @@ for Lisp calls." "22.1"))
485 ;; and the user might enter a single token. 483 ;; and the user might enter a single token.
486 (replace-match-string-symbols to) 484 (replace-match-string-symbols to)
487 (list from (car to) current-prefix-arg 485 (list from (car to) current-prefix-arg
488 (if (and transient-mark-mode mark-active) 486 (if (use-region-p) (region-beginning))
489 (region-beginning)) 487 (if (use-region-p) (region-end))))))
490 (if (and transient-mark-mode mark-active)
491 (region-end))))))
492 (perform-replace regexp (cons 'replace-eval-replacement to-expr) 488 (perform-replace regexp (cons 'replace-eval-replacement to-expr)
493 t 'literal delimited nil nil start end)) 489 t 'literal delimited nil nil start end))
494 490
@@ -523,10 +519,8 @@ Fourth and fifth arg START and END specify the region to operate on."
523 (list from to 519 (list from to
524 (and current-prefix-arg 520 (and current-prefix-arg
525 (prefix-numeric-value current-prefix-arg)) 521 (prefix-numeric-value current-prefix-arg))
526 (if (and transient-mark-mode mark-active) 522 (if (use-region-p) (region-beginning))
527 (region-beginning)) 523 (if (use-region-p) (region-end)))))
528 (if (and transient-mark-mode mark-active)
529 (region-end)))))
530 (let (replacements) 524 (let (replacements)
531 (if (listp to-strings) 525 (if (listp to-strings)
532 (setq replacements to-strings) 526 (setq replacements to-strings)
@@ -587,13 +581,11 @@ and TO-STRING is also null.)"
587 (if (eq current-prefix-arg '-) " backward" " word") 581 (if (eq current-prefix-arg '-) " backward" " word")
588 "") 582 "")
589 " string" 583 " string"
590 (if (and transient-mark-mode mark-active) " in region" "")) 584 (if (use-region-p) " in region" ""))
591 nil))) 585 nil)))
592 (list (nth 0 common) (nth 1 common) (nth 2 common) 586 (list (nth 0 common) (nth 1 common) (nth 2 common)
593 (if (and transient-mark-mode mark-active) 587 (if (use-region-p) (region-beginning))
594 (region-beginning)) 588 (if (use-region-p) (region-end))
595 (if (and transient-mark-mode mark-active)
596 (region-end))
597 (nth 3 common)))) 589 (nth 3 common))))
598 (perform-replace from-string to-string nil nil delimited nil nil start end backward)) 590 (perform-replace from-string to-string nil nil delimited nil nil start end backward))
599 591
@@ -661,13 +653,11 @@ which will run faster and will not set the mark or print anything."
661 (if (eq current-prefix-arg '-) " backward" " word") 653 (if (eq current-prefix-arg '-) " backward" " word")
662 "") 654 "")
663 " regexp" 655 " regexp"
664 (if (and transient-mark-mode mark-active) " in region" "")) 656 (if (use-region-p) " in region" ""))
665 t))) 657 t)))
666 (list (nth 0 common) (nth 1 common) (nth 2 common) 658 (list (nth 0 common) (nth 1 common) (nth 2 common)
667 (if (and transient-mark-mode mark-active) 659 (if (use-region-p) (region-beginning))
668 (region-beginning)) 660 (if (use-region-p) (region-end))
669 (if (and transient-mark-mode mark-active)
670 (region-end))
671 (nth 3 common)))) 661 (nth 3 common))))
672 (perform-replace regexp to-string nil t delimited nil nil start end backward)) 662 (perform-replace regexp to-string nil t delimited nil nil start end backward))
673 663
@@ -832,7 +822,7 @@ a previously found match."
832 (unless (or (bolp) (eobp)) 822 (unless (or (bolp) (eobp))
833 (forward-line 0)) 823 (forward-line 0))
834 (point-marker))))) 824 (point-marker)))))
835 (if (and interactive transient-mark-mode mark-active) 825 (if (and interactive (use-region-p))
836 (setq rstart (region-beginning) 826 (setq rstart (region-beginning)
837 rend (progn 827 rend (progn
838 (goto-char (region-end)) 828 (goto-char (region-end))
@@ -901,7 +891,7 @@ starting on the same line at which another match ended is ignored."
901 (progn 891 (progn
902 (goto-char (min rstart rend)) 892 (goto-char (min rstart rend))
903 (setq rend (copy-marker (max rstart rend)))) 893 (setq rend (copy-marker (max rstart rend))))
904 (if (and interactive transient-mark-mode mark-active) 894 (if (and interactive (use-region-p))
905 (setq rstart (region-beginning) 895 (setq rstart (region-beginning)
906 rend (copy-marker (region-end))) 896 rend (copy-marker (region-end)))
907 (setq rstart (point) 897 (setq rstart (point)
@@ -951,7 +941,7 @@ a previously found match."
951 (setq rend (max rstart rend))) 941 (setq rend (max rstart rend)))
952 (goto-char rstart) 942 (goto-char rstart)
953 (setq rend (point-max))) 943 (setq rend (point-max)))
954 (if (and interactive transient-mark-mode mark-active) 944 (if (and interactive (use-region-p))
955 (setq rstart (region-beginning) 945 (setq rstart (region-beginning)
956 rend (region-end)) 946 rend (region-end))
957 (setq rstart (point) 947 (setq rstart (point)
@@ -2068,7 +2058,7 @@ It is called with three arguments, as if it were
2068 2058
2069(defun perform-replace (from-string replacements 2059(defun perform-replace (from-string replacements
2070 query-flag regexp-flag delimited-flag 2060 query-flag regexp-flag delimited-flag
2071 &optional repeat-count map start end backward) 2061 &optional repeat-count map start end backward region-noncontiguous-p)
2072 "Subroutine of `query-replace'. Its complexity handles interactive queries. 2062 "Subroutine of `query-replace'. Its complexity handles interactive queries.
2073Don't use this in your own program unless you want to query and set the mark 2063Don't use this in your own program unless you want to query and set the mark
2074just as `query-replace' does. Instead, write a simple loop like this: 2064just as `query-replace' does. Instead, write a simple loop like this:
@@ -2115,6 +2105,9 @@ It must return a string."
2115 2105
2116 ;; If non-nil, it is marker saying where in the buffer to stop. 2106 ;; If non-nil, it is marker saying where in the buffer to stop.
2117 (limit nil) 2107 (limit nil)
2108 ;; Use local binding in add-function below.
2109 (isearch-filter-predicate isearch-filter-predicate)
2110 (region-bounds nil)
2118 2111
2119 ;; Data for the next match. If a cons, it has the same format as 2112 ;; Data for the next match. If a cons, it has the same format as
2120 ;; (match-data); otherwise it is t if a match is possible at point. 2113 ;; (match-data); otherwise it is t if a match is possible at point.
@@ -2127,6 +2120,24 @@ It must return a string."
2127 "Query replacing %s with %s: (\\<query-replace-map>\\[help] for help) ") 2120 "Query replacing %s with %s: (\\<query-replace-map>\\[help] for help) ")
2128 minibuffer-prompt-properties)))) 2121 minibuffer-prompt-properties))))
2129 2122
2123 ;; Unless a single contiguous chunk is selected, operate on multiple chunks.
2124 (when region-noncontiguous-p
2125 (setq region-bounds
2126 (mapcar (lambda (position)
2127 (cons (copy-marker (car position))
2128 (copy-marker (cdr position))))
2129 (funcall region-extract-function 'bounds)))
2130 (add-function :after-while isearch-filter-predicate
2131 (lambda (start end)
2132 (delq nil (mapcar
2133 (lambda (bounds)
2134 (and
2135 (>= start (car bounds))
2136 (<= start (cdr bounds))
2137 (>= end (car bounds))
2138 (<= end (cdr bounds))))
2139 region-bounds)))))
2140
2130 ;; If region is active, in Transient Mark mode, operate on region. 2141 ;; If region is active, in Transient Mark mode, operate on region.
2131 (if backward 2142 (if backward
2132 (when end 2143 (when end
diff --git a/lisp/simple.el b/lisp/simple.el
index b115a2a0cbb..deb5c888c92 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -970,15 +970,34 @@ instead of deleted."
970(defvar region-extract-function 970(defvar region-extract-function
971 (lambda (delete) 971 (lambda (delete)
972 (when (region-beginning) 972 (when (region-beginning)
973 (if (eq delete 'delete-only) 973 (cond
974 (delete-region (region-beginning) (region-end)) 974 ((eq delete 'bounds)
975 (filter-buffer-substring (region-beginning) (region-end) delete)))) 975 (list (cons (region-beginning) (region-end))))
976 ((eq delete 'delete-only)
977 (delete-region (region-beginning) (region-end)))
978 (t
979 (filter-buffer-substring (region-beginning) (region-end) delete)))))
976 "Function to get the region's content. 980 "Function to get the region's content.
977Called with one argument DELETE. 981Called with one argument DELETE.
978If DELETE is `delete-only', then only delete the region and the return value 982If DELETE is `delete-only', then only delete the region and the return value
979is undefined. If DELETE is nil, just return the content as a string. 983is undefined. If DELETE is nil, just return the content as a string.
984If DELETE is `bounds', then don't delete, but just return the
985boundaries of the region as a list of (START . END) positions.
980If anything else, delete the region and return its content as a string.") 986If anything else, delete the region and return its content as a string.")
981 987
988(defvar region-insert-function
989 (lambda (lines)
990 (let ((first t))
991 (while lines
992 (or first
993 (insert ?\n))
994 (insert-for-yank (car lines))
995 (setq lines (cdr lines)
996 first nil))))
997 "Function to insert the region's content.
998Called with one argument LINES.
999Insert the region as a list of lines.")
1000
982(defun delete-backward-char (n &optional killflag) 1001(defun delete-backward-char (n &optional killflag)
983 "Delete the previous N characters (following if N is negative). 1002 "Delete the previous N characters (following if N is negative).
984If Transient Mark mode is enabled, the mark is active, and N is 1, 1003If Transient Mark mode is enabled, the mark is active, and N is 1,
@@ -3419,7 +3438,8 @@ and only used if a buffer is displayed."
3419 3438
3420(defun shell-command-on-region (start end command 3439(defun shell-command-on-region (start end command
3421 &optional output-buffer replace 3440 &optional output-buffer replace
3422 error-buffer display-error-buffer) 3441 error-buffer display-error-buffer
3442 region-noncontiguous-p)
3423 "Execute string COMMAND in inferior shell with region as input. 3443 "Execute string COMMAND in inferior shell with region as input.
3424Normally display output (if any) in temp buffer `*Shell Command Output*'; 3444Normally display output (if any) in temp buffer `*Shell Command Output*';
3425Prefix arg means replace the region with it. Return the exit code of 3445Prefix arg means replace the region with it. Return the exit code of
@@ -3482,7 +3502,8 @@ interactively, this is t."
3482 current-prefix-arg 3502 current-prefix-arg
3483 current-prefix-arg 3503 current-prefix-arg
3484 shell-command-default-error-buffer 3504 shell-command-default-error-buffer
3485 t))) 3505 t
3506 (region-noncontiguous-p))))
3486 (let ((error-file 3507 (let ((error-file
3487 (if error-buffer 3508 (if error-buffer
3488 (make-temp-file 3509 (make-temp-file
@@ -3491,96 +3512,109 @@ interactively, this is t."
3491 temporary-file-directory))) 3512 temporary-file-directory)))
3492 nil)) 3513 nil))
3493 exit-status) 3514 exit-status)
3494 (if (or replace 3515 ;; Unless a single contiguous chunk is selected, operate on multiple chunks.
3495 (and output-buffer 3516 (if region-noncontiguous-p
3496 (not (or (bufferp output-buffer) (stringp output-buffer))))) 3517 (let ((input (concat (funcall region-extract-function 'delete) "\n"))
3497 ;; Replace specified region with output from command. 3518 output)
3498 (let ((swap (and replace (< start end)))) 3519 (with-temp-buffer
3499 ;; Don't muck with mark unless REPLACE says we should. 3520 (insert input)
3500 (goto-char start) 3521 (call-process-region (point-min) (point-max)
3501 (and replace (push-mark (point) 'nomsg)) 3522 shell-file-name t t
3502 (setq exit-status 3523 nil shell-command-switch
3503 (call-process-region start end shell-file-name replace 3524 command)
3504 (if error-file 3525 (setq output (split-string (buffer-string) "\n")))
3505 (list t error-file) 3526 (goto-char start)
3506 t) 3527 (funcall region-insert-function output))
3507 nil shell-command-switch command)) 3528 (if (or replace
3508 ;; It is rude to delete a buffer which the command is not using. 3529 (and output-buffer
3509 ;; (let ((shell-buffer (get-buffer "*Shell Command Output*"))) 3530 (not (or (bufferp output-buffer) (stringp output-buffer)))))
3510 ;; (and shell-buffer (not (eq shell-buffer (current-buffer))) 3531 ;; Replace specified region with output from command.
3511 ;; (kill-buffer shell-buffer))) 3532 (let ((swap (and replace (< start end))))
3512 ;; Don't muck with mark unless REPLACE says we should. 3533 ;; Don't muck with mark unless REPLACE says we should.
3513 (and replace swap (exchange-point-and-mark))) 3534 (goto-char start)
3514 ;; No prefix argument: put the output in a temp buffer, 3535 (and replace (push-mark (point) 'nomsg))
3515 ;; replacing its entire contents. 3536 (setq exit-status
3516 (let ((buffer (get-buffer-create 3537 (call-process-region start end shell-file-name replace
3517 (or output-buffer "*Shell Command Output*")))) 3538 (if error-file
3518 (unwind-protect 3539 (list t error-file)
3519 (if (eq buffer (current-buffer)) 3540 t)
3520 ;; If the input is the same buffer as the output, 3541 nil shell-command-switch command))
3521 ;; delete everything but the specified region, 3542 ;; It is rude to delete a buffer which the command is not using.
3522 ;; then replace that region with the output. 3543 ;; (let ((shell-buffer (get-buffer "*Shell Command Output*")))
3523 (progn (setq buffer-read-only nil) 3544 ;; (and shell-buffer (not (eq shell-buffer (current-buffer)))
3524 (delete-region (max start end) (point-max)) 3545 ;; (kill-buffer shell-buffer)))
3525 (delete-region (point-min) (min start end)) 3546 ;; Don't muck with mark unless REPLACE says we should.
3526 (setq exit-status 3547 (and replace swap (exchange-point-and-mark)))
3527 (call-process-region (point-min) (point-max) 3548 ;; No prefix argument: put the output in a temp buffer,
3528 shell-file-name t 3549 ;; replacing its entire contents.
3529 (if error-file 3550 (let ((buffer (get-buffer-create
3530 (list t error-file) 3551 (or output-buffer "*Shell Command Output*"))))
3531 t) 3552 (unwind-protect
3532 nil shell-command-switch 3553 (if (eq buffer (current-buffer))
3533 command))) 3554 ;; If the input is the same buffer as the output,
3534 ;; Clear the output buffer, then run the command with 3555 ;; delete everything but the specified region,
3535 ;; output there. 3556 ;; then replace that region with the output.
3536 (let ((directory default-directory)) 3557 (progn (setq buffer-read-only nil)
3537 (with-current-buffer buffer 3558 (delete-region (max start end) (point-max))
3538 (setq buffer-read-only nil) 3559 (delete-region (point-min) (min start end))
3539 (if (not output-buffer) 3560 (setq exit-status
3540 (setq default-directory directory)) 3561 (call-process-region (point-min) (point-max)
3541 (erase-buffer))) 3562 shell-file-name t
3542 (setq exit-status 3563 (if error-file
3543 (call-process-region start end shell-file-name nil 3564 (list t error-file)
3544 (if error-file 3565 t)
3545 (list buffer error-file) 3566 nil shell-command-switch
3546 buffer) 3567 command)))
3547 nil shell-command-switch command))) 3568 ;; Clear the output buffer, then run the command with
3548 ;; Report the output. 3569 ;; output there.
3549 (with-current-buffer buffer 3570 (let ((directory default-directory))
3550 (setq mode-line-process 3571 (with-current-buffer buffer
3551 (cond ((null exit-status) 3572 (setq buffer-read-only nil)
3552 " - Error") 3573 (if (not output-buffer)
3553 ((stringp exit-status) 3574 (setq default-directory directory))
3554 (format " - Signal [%s]" exit-status)) 3575 (erase-buffer)))
3555 ((not (equal 0 exit-status)) 3576 (setq exit-status
3556 (format " - Exit [%d]" exit-status))))) 3577 (call-process-region start end shell-file-name nil
3557 (if (with-current-buffer buffer (> (point-max) (point-min))) 3578 (if error-file
3558 ;; There's some output, display it 3579 (list buffer error-file)
3559 (display-message-or-buffer buffer) 3580 buffer)
3560 ;; No output; error? 3581 nil shell-command-switch command)))
3561 (let ((output 3582 ;; Report the output.
3562 (if (and error-file 3583 (with-current-buffer buffer
3563 (< 0 (nth 7 (file-attributes error-file)))) 3584 (setq mode-line-process
3564 (format "some error output%s" 3585 (cond ((null exit-status)
3565 (if shell-command-default-error-buffer 3586 " - Error")
3566 (format " to the \"%s\" buffer" 3587 ((stringp exit-status)
3567 shell-command-default-error-buffer) 3588 (format " - Signal [%s]" exit-status))
3568 "")) 3589 ((not (equal 0 exit-status))
3569 "no output"))) 3590 (format " - Exit [%d]" exit-status)))))
3570 (cond ((null exit-status) 3591 (if (with-current-buffer buffer (> (point-max) (point-min)))
3571 (message "(Shell command failed with error)")) 3592 ;; There's some output, display it
3572 ((equal 0 exit-status) 3593 (display-message-or-buffer buffer)
3573 (message "(Shell command succeeded with %s)" 3594 ;; No output; error?
3574 output)) 3595 (let ((output
3575 ((stringp exit-status) 3596 (if (and error-file
3576 (message "(Shell command killed by signal %s)" 3597 (< 0 (nth 7 (file-attributes error-file))))
3577 exit-status)) 3598 (format "some error output%s"
3578 (t 3599 (if shell-command-default-error-buffer
3579 (message "(Shell command failed with code %d and %s)" 3600 (format " to the \"%s\" buffer"
3580 exit-status output)))) 3601 shell-command-default-error-buffer)
3581 ;; Don't kill: there might be useful info in the undo-log. 3602 ""))
3582 ;; (kill-buffer buffer) 3603 "no output")))
3583 )))) 3604 (cond ((null exit-status)
3605 (message "(Shell command failed with error)"))
3606 ((equal 0 exit-status)
3607 (message "(Shell command succeeded with %s)"
3608 output))
3609 ((stringp exit-status)
3610 (message "(Shell command killed by signal %s)"
3611 exit-status))
3612 (t
3613 (message "(Shell command failed with code %d and %s)"
3614 exit-status output))))
3615 ;; Don't kill: there might be useful info in the undo-log.
3616 ;; (kill-buffer buffer)
3617 )))))
3584 3618
3585 (when (and error-file (file-exists-p error-file)) 3619 (when (and error-file (file-exists-p error-file))
3586 (if (< 0 (nth 7 (file-attributes error-file))) 3620 (if (< 0 (nth 7 (file-attributes error-file)))
@@ -5175,6 +5209,11 @@ also checks the value of `use-empty-active-region'."
5175 ;; region is active when there's no mark. 5209 ;; region is active when there's no mark.
5176 (progn (cl-assert (mark)) t))) 5210 (progn (cl-assert (mark)) t)))
5177 5211
5212(defun region-noncontiguous-p ()
5213 "Return non-nil if the region contains several pieces.
5214An example is a rectangular region handled as a list of
5215separate contiguous regions for each line."
5216 (> (length (funcall region-extract-function 'bounds)) 1))
5178 5217
5179(defvar redisplay-unhighlight-region-function 5218(defvar redisplay-unhighlight-region-function
5180 (lambda (rol) (when (overlayp rol) (delete-overlay rol)))) 5219 (lambda (rol) (when (overlayp rol) (delete-overlay rol))))
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el
index 464e3754eb9..f4d7fe7d9aa 100644
--- a/lisp/vc/diff-mode.el
+++ b/lisp/vc/diff-mode.el
@@ -1821,7 +1821,7 @@ With a prefix argument, try to REVERSE the hunk."
1821 "Kill all hunks that have already been applied starting at point." 1821 "Kill all hunks that have already been applied starting at point."
1822 (interactive) 1822 (interactive)
1823 (while (not (eobp)) 1823 (while (not (eobp))
1824 (pcase-let ((`(,buf ,line-offset ,pos ,src ,_dst ,switched) 1824 (pcase-let ((`(,_buf ,line-offset ,_pos ,_src ,_dst ,switched)
1825 (diff-find-source-location nil nil))) 1825 (diff-find-source-location nil nil)))
1826 (if (and line-offset switched) 1826 (if (and line-offset switched)
1827 (diff-hunk-kill) 1827 (diff-hunk-kill)
diff --git a/src/casefiddle.c b/src/casefiddle.c
index b94ea8e212e..6a2983ef018 100644
--- a/src/casefiddle.c
+++ b/src/casefiddle.c
@@ -306,14 +306,30 @@ See also `capitalize-region'. */)
306 return Qnil; 306 return Qnil;
307} 307}
308 308
309DEFUN ("downcase-region", Fdowncase_region, Sdowncase_region, 2, 2, "r", 309DEFUN ("downcase-region", Fdowncase_region, Sdowncase_region, 2, 3,
310 "(list (region-beginning) (region-end) (region-noncontiguous-p))",
310 doc: /* Convert the region to lower case. In programs, wants two arguments. 311 doc: /* Convert the region to lower case. In programs, wants two arguments.
311These arguments specify the starting and ending character numbers of 312These arguments specify the starting and ending character numbers of
312the region to operate on. When used as a command, the text between 313the region to operate on. When used as a command, the text between
313point and the mark is operated on. */) 314point and the mark is operated on. */)
314 (Lisp_Object beg, Lisp_Object end) 315 (Lisp_Object beg, Lisp_Object end, Lisp_Object region_noncontiguous_p)
315{ 316{
316 casify_region (CASE_DOWN, beg, end); 317 Lisp_Object bounds = Qnil;
318
319 if (!NILP (region_noncontiguous_p))
320 {
321 bounds = call1 (Fsymbol_value (intern ("region-extract-function")),
322 intern ("bounds"));
323
324 while (CONSP (bounds))
325 {
326 casify_region (CASE_DOWN, XCAR (XCAR (bounds)), XCDR (XCAR (bounds)));
327 bounds = XCDR (bounds);
328 }
329 }
330 else
331 casify_region (CASE_DOWN, beg, end);
332
317 return Qnil; 333 return Qnil;
318} 334}
319 335