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) ============================================================ _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://lists.gnu.org/mailman/listinfo/emacs-devel