emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

keep-lines, flush-lines, how-many


From: Luc Teirlinck
Subject: keep-lines, flush-lines, how-many
Date: Wed, 22 Jun 2005 20:50:00 -0500 (CDT)

I propose the following changes to replace.el.

The main one is that when Transient Mark mode is enabled and a region
is specified that contains partial lines, then `keep-lines' _never_
deletes those partial lines. That was already the case for an initial
partial line, but not for a final partial line.  Prior to the patch
below, `keep-lines' often deleted _part_ of the final line.  This
contradicted the docs and seemed unintentional (we already agreed on
that).  In the new version, `keep-lines' can only delete a line if its
accessible (in case of narrowing) part is entirely contained in the
region, including any accessible newline at its end.

Secondly, beginning-of-line is now _everywhere_ replaced with
(forward-line 0) to correctly handle fields.  (This was already done
at some places, but not everywhere, which is inconsistent and makes no
sense.)

The following changes are independent.

Thirdly, the docs of all three commands specify that a Transient Mark
mode region _only_ can get taken into account when called
interactively, but, in practice, it does get taken into account even
when called from Lisp.  I checked that no code included with the Emacs
distribution relies on this "feature".  It is non-standard.  Most of
the time, the region gets specified with the `r' interactive code,
which is disregarded when called from Lisp.  The patch below makes the
behavior match the docs.  It does so using an INTERACTIVE arg, so that
Lisp functions can still get the old behavior by setting that to t and
so that keyboard macros still get the old behavior automatically.

Fourthly, it makes all markers point nowhere after they are no longer
used.  Is there some reason why this was not done?

Fifthly, it makes START and END in how-many interchangeable (the
smallest one no longer needs to come first).  This makes it consistent
with `keep-lines', `flush-lines' and most other Emacs functions
specifying a region this way.

The sixth change is to make `how-many' not just print, but return the
number of matches.  From Lisp, it would just return the number and no
longer print it, but again, the INTERACTIVE arg can override that.  In
the Emacs sources, there are no calls to `how-many' outside
replace.el.  But at least three functions (actually four, but one is
no longer used) try to implement their own version:
comint-how-many-region, term-how-many-region and diff-count-matches.
All three would appear to go into an infinite loop if called with a
regexp that can match an empty string and all three fail to warn about
that.  After this problem would be corrected, their code would be
completely identical with the main loop of the `how-many' code, which
is that code's only time consuming part.  The only reason why the
other three functions might be more efficient is that they use numbers
instead of markers.  But why does the `how-many' code use markers
instead of numbers?  The patch below does not replace these markers
with numbers, but I could easily do that.  Then the three functions
could be eliminated or declared obsolete and there would be no need
for even more of such identical functions in the future.

The whitespace change is for the sake of `outline-minor-mode'.  It
makes the comment follow a double-whitespace convention we agreed upon
some time ago.

===File ~/replace.el-diff===================================
*** replace.el  08 Jun 2005 19:17:29 -0500      1.212
--- replace.el  22 Jun 2005 19:15:03 -0500      
***************
*** 516,536 ****
  Prompt for a regexp with PROMPT.
  Value is a list, (REGEXP)."
    (list (read-from-minibuffer prompt nil nil nil
!                             'regexp-history nil t)))
  
! (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.
  
  Second and third arg RSTART and REND specify the region to operate on.
  
  Interactively, in Transient Mark mode when the mark is active, operate
! on the contents of the region.  Otherwise, operate from point to the
! end of the buffer."
  
    (interactive
     (progn
--- 516,547 ----
  Prompt for a regexp with PROMPT.
  Value is a list, (REGEXP)."
    (list (read-from-minibuffer prompt nil nil nil
!                             'regexp-history nil t)
!       nil nil t))
  
! (defun keep-lines (regexp &optional rstart rend interactive)
    "Delete all lines except those containing matches for REGEXP.
  A match split across lines preserves all the lines it lies in.
! When called from Lisp (and usually interactively as well, see below)
! applies to all lines starting after point.
  
  If REGEXP contains upper case characters (excluding those preceded by `\\'),
  the matching is case-sensitive.
  
  Second and third arg RSTART and REND specify the region to operate on.
+ This command operates on (the accessible part of) all lines whose
+ accessible part is entirely contained in the region determined by RSTART
+ and REND.  (A newline ending a line counts as part of that line.)
  
  Interactively, in Transient Mark mode when the mark is active, operate
! on all lines whose accessible part is entirely contained in the region.
! Otherwise, the command applies to all lines starting after point.
! When calling this function from Lisp, you can pretend that it was
! called interactively by passing a non-nil INTERACTIVE argument.
! 
! This function starts looking for the next match from the end of
! the previous match.  Hence, it ignores matches that overlap
! a previously found match."
  
    (interactive
     (progn
***************
*** 539,548 ****
    (if rstart
        (progn
        (goto-char (min rstart rend))
!       (setq rend (copy-marker (max rstart rend))))
!     (if (and transient-mark-mode mark-active)
        (setq rstart (region-beginning)
!             rend (copy-marker (region-end)))
        (setq rstart (point)
            rend (point-max-marker)))
      (goto-char rstart))
--- 550,569 ----
    (if rstart
        (progn
        (goto-char (min rstart rend))
!       (setq rend
!             (progn
!               (save-excursion
!                 (goto-char (max rstart rend))
!                 (unless (or (bolp) (eobp))
!                   (forward-line 0))
!                 (point-marker)))))
!     (if (and interactive transient-mark-mode mark-active)
        (setq rstart (region-beginning)
!             rend (progn
!                    (goto-char (region-end))
!                    (unless (or (bolp) (eobp))
!                      (forward-line 0))
!                    (point-marker)))
        (setq rstart (point)
            rend (point-max-marker)))
      (goto-char rstart))
***************
*** 556,562 ****
        (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)
--- 577,583 ----
        (if (not (re-search-forward regexp rend 'move))
            (delete-region start rend)
          (let ((end (save-excursion (goto-char (match-beginning 0))
!                                    (forward-line 0)
                                     (point))))
            ;; Now end is first char preserved by the new match.
            (if (< start end)
***************
*** 566,587 ****
        ;; If the match was empty, avoid matching again at same place.
        (and (< (point) rend)
             (= (match-beginning 0) (match-end 0))
!            (forward-char 1))))))
  
  
! (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.
  
  Second and third arg RSTART and REND specify the region to operate on.
  
  Interactively, in Transient Mark mode when the mark is active, operate
  on the contents of the region.  Otherwise, operate from point to the
! end of the buffer."
  
    (interactive
     (progn
--- 587,620 ----
        ;; If the match was empty, avoid matching again at same place.
        (and (< (point) rend)
             (= (match-beginning 0) (match-end 0))
!            (forward-char 1)))))
!   (set-marker rend nil)
!   nil)
  
  
! (defun flush-lines (regexp &optional rstart rend interactive)
!  "Delete lines containing matches for REGEXP.
! When called from Lisp (and usually when called interactively as
! well, see below), applies to the part of the buffer after point.
! The line point is in is deleted if and only if it contains a
! match for regexp starting after point.
  
  If REGEXP contains upper case characters (excluding those preceded by `\\'),
  the matching is case-sensitive.
  
  Second and third arg RSTART and REND specify the region to operate on.
+ Lines partially contained in this region are deleted if and only if
+ they contain a match entirely contained in it.
  
  Interactively, in Transient Mark mode when the mark is active, operate
  on the contents of the region.  Otherwise, operate from point to the
! end of (the accessible portion of) the buffer.  When calling this function
! from Lisp, you can pretend that it was called interactively by passing
! a non-nil INTERACTIVE argument.
! 
! If a match is split across lines, all the lines it lies in are deleted.
! They are deleted _before_ looking for the next match.  Hence, a match
! starting on the same line at which another match ended is ignored."
  
    (interactive
     (progn
***************
*** 591,597 ****
        (progn
        (goto-char (min rstart rend))
        (setq rend (copy-marker (max rstart rend))))
!     (if (and transient-mark-mode mark-active)
        (setq rstart (region-beginning)
              rend (copy-marker (region-end)))
        (setq rstart (point)
--- 624,630 ----
        (progn
        (goto-char (min rstart rend))
        (setq rend (copy-marker (max rstart rend))))
!     (if (and interactive transient-mark-mode mark-active)
        (setq rstart (region-beginning)
              rend (copy-marker (region-end)))
        (setq rstart (point)
***************
*** 603,615 ****
        (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)))))))
  
  
! (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.
--- 636,653 ----
        (while (and (< (point) rend)
                  (re-search-forward regexp rend t))
        (delete-region (save-excursion (goto-char (match-beginning 0))
!                                      (forward-line 0)
                                       (point))
!                      (progn (forward-line 1) (point))))))
!   (set-marker rend nil)
!   nil)
  
  
! (defun how-many (regexp &optional rstart rend interactive)
!   "Print and return number of matches for REGEXP following point.
! When called from Lisp and INTERACTIVE is omitted or nil, just return
! the number, do not print it; if INTERACTIVE is t, the function behaves
! in all respects has if it had been called interactively.
  
  If REGEXP contains upper case characters (excluding those preceded by `\\'),
  the matching is case-sensitive.
***************
*** 618,631 ****
  
  Interactively, in Transient Mark mode when 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): "))
    (save-excursion
      (if rstart
!       (goto-char (min rstart rend))
!       (if (and transient-mark-mode mark-active)
          (setq rstart (region-beginning)
                rend (copy-marker (region-end)))
        (setq rstart (point)
--- 656,675 ----
  
  Interactively, in Transient Mark mode when the mark is active, operate
  on the contents of the region.  Otherwise, operate from point to the
! end of (the accessible portion of) the buffer.
! 
! This function starts looking for the next match from the end of
! the previous match.  Hence, it ignores matches that overlap
! a previously found match."
  
    (interactive
     (keep-lines-read-args "How many matches for (regexp): "))
    (save-excursion
      (if rstart
!       (progn
!         (goto-char (min rstart rend))
!         (setq rend (copy-marker (max rstart rend))))
!       (if (and interactive transient-mark-mode mark-active)
          (setq rstart (region-beginning)
                rend (copy-marker (region-end)))
        (setq rstart (point)
***************
*** 641,647 ****
        (if (= opoint (point))
            (forward-char 1)
          (setq count (1+ count))))
!       (message "%d occurrences" count))))
  
  
  (defvar occur-mode-map
--- 685,693 ----
        (if (= opoint (point))
            (forward-char 1)
          (setq count (1+ count))))
!       (set-marker rend nil)
!       (when interactive (message "%d occurrences" count))
!       count)))
  
  
  (defvar occur-mode-map
***************
*** 892,899 ****
  
  (defun occur (regexp &optional nlines)
    "Show all lines in the current buffer containing a match for REGEXP.
! 
! If a match spreads across multiple lines, all those lines are shown.
  
  Each line is displayed with NLINES lines before and after, or -NLINES
  before if NLINES is negative.
--- 938,944 ----
  
  (defun occur (regexp &optional nlines)
    "Show all lines in the current buffer containing a match for REGEXP.
! This function can not handle matches that span more than one line.
  
  Each line is displayed with NLINES lines before and after, or -NLINES
  before if NLINES is negative.
***************
*** 1603,1617 ****
                ;; Change markers to numbers in the match data
                ;; since lots of markers slow down editing.
                (push (list (point) replaced
! ;;; If the replacement has already happened, all we need is the
! ;;; current match start and end.  We could get this with a trivial
! ;;; match like
! ;;; (save-excursion (goto-char (match-beginning 0))
! ;;;               (search-forward (match-string 0))
! ;;;                 (match-data t))
! ;;; if we really wanted to avoid manually constructing match data.
! ;;; Adding current-buffer is necessary so that match-data calls can
! ;;; return markers which are appropriate for editing.
                            (if replaced
                                (list
                                 (match-beginning 0)
--- 1648,1662 ----
                ;; Change markers to numbers in the match data
                ;; since lots of markers slow down editing.
                (push (list (point) replaced
! ;;;  If the replacement has already happened, all we need is the
! ;;;  current match start and end.  We could get this with a trivial
! ;;;  match like
! ;;;  (save-excursion (goto-char (match-beginning 0))
! ;;;                (search-forward (match-string 0))
! ;;;                  (match-data t))
! ;;;  if we really wanted to avoid manually constructing match data.
! ;;;  Adding current-buffer is necessary so that match-data calls can
! ;;;  return markers which are appropriate for editing.
                            (if replaced
                                (list
                                 (match-beginning 0)
============================================================




reply via email to

[Prev in Thread] Current Thread [Next in Thread]