]> code.delx.au - gnu-emacs/blobdiff - lisp/replace.el
(lisp-mode-shared-map): Bind `backspace' to `backward-delete-char-untabify'.
[gnu-emacs] / lisp / replace.el
index 418c3398e68f1ae232eeb6d200cc06ea1a37cf5a..2a0735cfe5a4ac7ca4feaa0c8a60be9617e33962 100644 (file)
@@ -279,83 +279,133 @@ What you probably want is a loop like this:
 which will run faster and will not set the mark or print anything."
   (interactive (query-replace-read-args "Replace regexp" t))
   (perform-replace regexp to-string start end nil t delimited))
+
 \f
 (defvar regexp-history nil
   "History list for some commands that read regular expressions.")
 
+
 (defalias 'delete-non-matching-lines 'keep-lines)
-(defun keep-lines (regexp)
+(defalias 'delete-matching-lines 'flush-lines)
+(defalias 'count-matches 'how-many)
+
+
+(defun keep-lines-read-args (prompt)
+  "Read arguments for `keep-lines' and friends.
+Prompt for a regexp with PROMPT.
+
+Value is a list (REGEXP START END).
+
+If in Transient Mark node, and the mark is active, START is the
+start of the region, and end is a marker for the end of the region.
+Otherwise, START is the current point, and END is `point-max-marker'."
+  (let ((regexp (read-from-minibuffer prompt nil nil nil
+                                     'regexp-history nil t))
+       start end)
+    (if (and transient-mark-mode mark-active)
+       (setq start (region-beginning)
+             end (save-excursion (goto-char (region-end)) (point-marker)))
+      (setq start (point)
+           end (point-max-marker)))
+    (list regexp start end)))
+
+
+(defun keep-lines (regexp &optional rstart rend)
   "Delete all lines except those containing matches for REGEXP.
 A match split across lines preserves all the lines it lies in.
 Applies to all lines after point.
 
 If REGEXP contains upper case characters (excluding those preceded by `\\'),
-the matching is case-sensitive."
-  (interactive (list (read-from-minibuffer
-                     "Keep lines (containing match for regexp): "
-                     nil nil nil 'regexp-history nil t)))
+the matching is case-sensitive.
+
+Second and third arg RSTART and REND specify the region to operate on.
+
+In Transient Mark mode, if the mark is active, operate on the contents
+of the region.  Otherwise, operate from point to the end of the buffer."
+  (interactive
+   (keep-lines-read-args "Keep lines (containing match for regexp): "))
+  (if rstart
+      (goto-char (min rstart rend))
+    (setq rstart (point) rend (point-max-marker)))
   (save-excursion
     (or (bolp) (forward-line 1))
     (let ((start (point))
          (case-fold-search  (and case-fold-search
                                  (isearch-no-upper-case-p regexp t))))
-      (while (not (eobp))
+      (while (< (point) rend)
        ;; Start is first char not preserved by previous match.
-       (if (not (re-search-forward regexp nil 'move))
-           (delete-region start (point-max))
+       (if (not (re-search-forward regexp rend 'move))
+           (delete-region start rend)
          (let ((end (save-excursion (goto-char (match-beginning 0))
                                     (beginning-of-line)
                                     (point))))
            ;; Now end is first char preserved by the new match.
            (if (< start end)
                (delete-region start end))))
-       (setq start (save-excursion (forward-line 1)
-                                   (point)))
+       
+       (setq start (save-excursion (forward-line 1) (point)))
        ;; If the match was empty, avoid matching again at same place.
-       (and (not (eobp)) (= (match-beginning 0) (match-end 0))
+       (and (< (point) rend)
+            (= (match-beginning 0) (match-end 0))
             (forward-char 1))))))
 
-(defalias 'delete-matching-lines 'flush-lines)
-(defun flush-lines (regexp)
+
+(defun flush-lines (regexp &optional rstart rend)
   "Delete lines containing matches for REGEXP.
 If a match is split across lines, all the lines it lies in are deleted.
 Applies to lines after point.
 
 If REGEXP contains upper case characters (excluding those preceded by `\\'),
-the matching is case-sensitive."
-  (interactive (list (read-from-minibuffer
-                     "Flush lines (containing match for regexp): "
-                     nil nil nil 'regexp-history nil t)))
+the matching is case-sensitive.
+
+Second and third arg RSTART and REND specify the region to operate on.
+
+In Transient Mark mode, if the mark is active, operate on the contents
+of the region.  Otherwise, operate from point to the end of the buffer."
+  (interactive
+   (keep-lines-read-args "Flush lines (containing match for regexp): "))
+  (if rstart
+      (goto-char (min rstart rend))
+    (setq rstart (point) rend (point-max-marker)))
   (let ((case-fold-search (and case-fold-search
                               (isearch-no-upper-case-p regexp t))))
     (save-excursion
-      (while (and (not (eobp))
-                 (re-search-forward regexp nil t))
+      (while (and (< (point) rend)
+                 (re-search-forward regexp rend t))
        (delete-region (save-excursion (goto-char (match-beginning 0))
                                       (beginning-of-line)
                                       (point))
                       (progn (forward-line 1) (point)))))))
 
-(defalias 'count-matches 'how-many)
-(defun how-many (regexp)
+
+(defun how-many (regexp &optional rstart rend)
   "Print number of matches for REGEXP following point.
 
 If REGEXP contains upper case characters (excluding those preceded by `\\'),
-the matching is case-sensitive."
-  (interactive (list (read-from-minibuffer
-                     "How many matches for (regexp): "
-                     nil nil nil 'regexp-history nil t)))
-  (let ((count 0) opoint
-       (case-fold-search  (and case-fold-search
-                               (isearch-no-upper-case-p regexp t))))
+the matching is case-sensitive.
+
+Second and third arg RSTART and REND specify the region to operate on.
+
+In Transient Mark mode, if the mark is active, operate on the contents
+of the region.  Otherwise, operate from point to the end of the buffer."
+  (interactive
+   (keep-lines-read-args "How many matches for (regexp): "))
+  (if rstart
+      (goto-char (min rstart rend))
+    (setq rstart (point) rend (point-max-marker)))
+  (let ((count 0)
+       opoint
+       (case-fold-search (and case-fold-search
+                              (isearch-no-upper-case-p regexp t))))
     (save-excursion
-     (while (and (not (eobp))
+     (while (and (< (point) rend)
                 (progn (setq opoint (point))
-                       (re-search-forward regexp nil t)))
+                       (re-search-forward regexp rend t)))
        (if (= opoint (point))
           (forward-char 1)
         (setq count (1+ count))))
      (message "%d occurrences" count))))
+
 \f
 (defvar occur-mode-map ())
 (if occur-mode-map
@@ -512,24 +562,38 @@ the matching is case-sensitive."
                (setq input default))
           input)
         current-prefix-arg))
-  (let ((nlines (if nlines
-                   (prefix-numeric-value nlines)
-                 list-matching-lines-default-context-lines))
-       (first t)
-       ;;flag to prevent printing separator for first match
-       (occur-num-matches 0)
-       (buffer (current-buffer))
-       (dir default-directory)
-       (linenum 1)
-       (prevpos
-        ;;position of most recent match
-        (point-min))
-       (case-fold-search  (and case-fold-search
-                               (isearch-no-upper-case-p regexp t)))
-       (final-context-start
-        ;; Marker to the start of context immediately following
-        ;; the matched text in *Occur*.
-        (make-marker)))
+  (let* ((nlines (if nlines
+                    (prefix-numeric-value nlines)
+                  list-matching-lines-default-context-lines))
+        (current-tab-width tab-width)
+        ;; Minimum width of line number plus trailing colon.
+        (min-line-number-width 6)
+        ;; Width of line number prefix without the colon.  Choose a
+        ;; width that's a multiple of `tab-width' in the original
+        ;; buffer so that lines in *Occur* appear right.
+        (line-number-width (1- (* (/ (- (+ min-line-number-width
+                                           tab-width)
+                                        1)
+                                     tab-width)
+                                  tab-width)))
+        ;; Format string for line numbers.
+        (line-number-format (format "%%%dd" line-number-width))
+        (empty (make-string line-number-width ?\ ))
+        (first t)
+        ;;flag to prevent printing separator for first match
+        (occur-num-matches 0)
+        (buffer (current-buffer))
+        (dir default-directory)
+        (linenum 1)
+        (prevpos
+         ;;position of most recent match
+         (point-min))
+        (case-fold-search  (and case-fold-search
+                                (isearch-no-upper-case-p regexp t)))
+        (final-context-start
+         ;; Marker to the start of context immediately following
+         ;; the matched text in *Occur*.
+         (make-marker)))
 ;;;    (save-excursion
 ;;;      (beginning-of-line)
 ;;;      (setq linenum (1+ (count-lines (point-min) (point))))
@@ -567,32 +631,27 @@ the matching is case-sensitive."
                (setq linenum (+ linenum (count-lines prevpos (point)))))
              (setq prevpos (point))
              (goto-char (match-end 0))
-             (let* ((start
-                     ;;start point of text in source buffer to be put
-                     ;;into *Occur*
-                     (save-excursion
+             (let* (;;start point of text in source buffer to be put
+                    ;;into *Occur*
+                    (start (save-excursion
                              (goto-char (match-beginning 0))
                              (forward-line (if (< nlines 0)
                                                nlines
                                              (- nlines)))
                              (point)))
-                    (end
                      ;; end point of text in source buffer to be put
                      ;; into *Occur*
-                     (save-excursion
-                       (goto-char (match-end 0))
-                       (if (> nlines 0)
-                           (forward-line (1+ nlines))
-                         (forward-line 1))
-                       (point)))
-                    (match-beg
+                    (end (save-excursion
+                           (goto-char (match-end 0))
+                           (if (> nlines 0)
+                               (forward-line (1+ nlines))
+                             (forward-line 1))
+                           (point)))
                      ;; Amount of context before matching text
-                     (- (match-beginning 0) start))
-                    (match-len
+                    (match-beg (- (match-beginning 0) start))
                      ;; Length of matching text
-                     (- (match-end 0) (match-beginning 0)))
-                    (tag (format "%5d" linenum))
-                    (empty (make-string (length tag) ?\ ))
+                    (match-len (- (match-end 0) (match-beginning 0)))
+                    (tag (format line-number-format linenum))
                     tem
                     insertion-start
                     ;; Number of lines of context to show for current match.
@@ -605,8 +664,7 @@ the matching is case-sensitive."
                     (text-end
                      ;; Marker pointing to end of text for one match
                      ;; in *Occur*.
-                     (make-marker))
-                    )
+                     (make-marker)))
                (save-excursion
                  (setq occur-marker (make-marker))
                  (set-marker occur-marker (point))
@@ -615,6 +673,9 @@ the matching is case-sensitive."
                  (or first (zerop nlines)
                      (insert "--------\n"))
                  (setq first nil)
+                 (save-excursion
+                   (set-buffer "*Occur*")
+                   (setq tab-width current-tab-width))
 
                  ;; Insert matching text including context lines from
                  ;; source buffer into *Occur*
@@ -667,7 +728,7 @@ the matching is case-sensitive."
                  (let ((this-linenum linenum))
                    (while (< (point) final-context-start)
                      (if (null tag)
-                         (setq tag (format "%5d" this-linenum)))
+                         (setq tag (format line-number-format this-linenum)))
                      (insert tag ?:)
                      (forward-line 1)
                      (setq tag nil)
@@ -736,6 +797,7 @@ The valid answers include `act', `skip', `act-and-show',
 (define-key query-replace-map "n" 'skip)
 (define-key query-replace-map "Y" 'act)
 (define-key query-replace-map "N" 'skip)
+(define-key query-replace-map "e" 'edit-replacement)
 (define-key query-replace-map "E" 'edit-replacement)
 (define-key query-replace-map "," 'act-and-show)
 (define-key query-replace-map "q" 'exit)
@@ -903,15 +965,20 @@ which will run faster and probably do exactly what you want."
          (setq nonempty-match
                (/= (nth 0 real-match-data) (nth 1 real-match-data)))
 
-         ;; If the match is empty, record that the next one can't be adjacent.
+         ;; If the match is empty, record that the next one can't be
+         ;; adjacent.
+
          ;; Otherwise, if matching a regular expression, do the next
          ;; match now, since the replacement for this match may
          ;; affect whether the next match is adjacent to this one.
+         ;; If that match is empty, don't use it.
          (setq match-again
                (and nonempty-match
                     (or (not regexp-flag)
                         (and (looking-at search-string)
-                             (match-data)))))
+                             (let ((match (match-data)))
+                               (and (/= (nth 0 match) (nth 1 match))
+                                    match))))))
 
          ;; Calculate the replacement string, if necessary.
          (when replacements