From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Tino Calancha Newsgroups: gmane.emacs.devel Subject: Re: [patch] Run occur command restricted to a region Date: Tue, 3 Jan 2017 19:19:35 +0900 (JST) Message-ID: References: <87vau3jl6f.fsf@gmail.com> <87shp6uwvj.fsf@mail.linkov.net> <83h95lua2f.fsf@gnu.org> <878tqxm1wh.fsf@mail.linkov.net> <87r34ozq20.fsf@gmail.com> <87inq0xhiw.fsf@mail.linkov.net> <87d1g55h8d.fsf@mail.linkov.net> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: text/plain; format=flowed; charset=US-ASCII X-Trace: blaine.gmane.org 1483438798 8548 195.159.176.226 (3 Jan 2017 10:19:58 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Tue, 3 Jan 2017 10:19:58 +0000 (UTC) User-Agent: Alpine 2.20 (DEB 67 2015-01-07) Cc: Emacs developers , Tino Calancha To: Juri Linkov Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Tue Jan 03 11:19:53 2017 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1cOMC8-0001TP-Gr for ged-emacs-devel@m.gmane.org; Tue, 03 Jan 2017 11:19:52 +0100 Original-Received: from localhost ([::1]:32926 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cOMCA-0001lj-Tj for ged-emacs-devel@m.gmane.org; Tue, 03 Jan 2017 05:19:54 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:56570) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cOMC0-0001kE-E8 for emacs-devel@gnu.org; Tue, 03 Jan 2017 05:19:46 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cOMBv-0001bX-Bo for emacs-devel@gnu.org; Tue, 03 Jan 2017 05:19:44 -0500 Original-Received: from mail-pg0-x243.google.com ([2607:f8b0:400e:c05::243]:33926) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1cOMBv-0001bC-24 for emacs-devel@gnu.org; Tue, 03 Jan 2017 05:19:39 -0500 Original-Received: by mail-pg0-x243.google.com with SMTP id b1so33141655pgc.1 for ; Tue, 03 Jan 2017 02:19:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:date:to:cc:subject:in-reply-to:message-id:references :user-agent:mime-version; bh=/7YFoZDNMpXl+9byIuRkCjuNZZzrvz1nle7SZ8vlTmk=; b=pWubeIvDTY0ORUZ6IE0ekMLqDZ9Ov8Y/va9Zj6MAHt77Tzb31Su43TFewKwM5WGOYR HJP/dvwFbZ20kmiWGhvR5R+88HEfuZT8IuAFupQRXRgBm6kX5abh7qUNKDodXWLFTSPa 7LEF0CwUgHVv+RXOzIZyV2w28U9cicO/ivrskeKvxslQIngTr0jgcY/oS23pcJOr3UJr adYP5ZjWbSnUxHwqiyxdnycDCiiVgxaiQbdtQbvyPs8NN0EdRqpKOi9SMW/DlzYYfz6n lFKjZ5ase7/SD2kHcdMm0XhUtvepe0P+KE5U2slU8EHpYqbVDTlqnmIHo/Ap5lIwHdHr +WjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:date:to:cc:subject:in-reply-to:message-id :references:user-agent:mime-version; bh=/7YFoZDNMpXl+9byIuRkCjuNZZzrvz1nle7SZ8vlTmk=; b=tbvRc7u0ObaYx5epzWIAvP5DVHrcx/KnnDyVABgxzAzqUT7A66FDgWuG/vXaeWBY/H mPVF1z/FY2CDc/+ul9kvYMeilD4sHBK0iBgGivKAEqYOVbFmjMKiIV7tzuqcPUYSG/2a a+LyyjpsGInjnRSDixAnfmbLCQZMzALwuh2lA1AiyzNujyO3UXxnbCMK+YtesMX5P+/c gKcaYVeyL9C6I549y7YZ5dKAo8VrWw8oHT4wkfebAnpXtYM6ZUrK3JK7YSqs8rN2Yl13 pJ9Y1KKCYf4qZSkVCBgboVd7RhivZ2pjVYe1GQfNTl1EYcLS5cUI6tSHBHVNSitx6kQA 3muw== X-Gm-Message-State: AIkVDXJWOzeCxWlrHMsDmnCc4l+RHG1q7gLU3RyUTy30R4x2POtW5A/JpJJD1lQhLVplPQ== X-Received: by 10.84.132.35 with SMTP id 32mr134921881ple.44.1483438778109; Tue, 03 Jan 2017 02:19:38 -0800 (PST) Original-Received: from calancha-pc (217.225.128.101.dy.bbexcite.jp. [101.128.225.217]) by smtp.gmail.com with ESMTPSA id u3sm138742386pfk.3.2017.01.03.02.19.36 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 03 Jan 2017 02:19:37 -0800 (PST) X-Google-Original-From: Tino Calancha X-X-Sender: calancha@calancha-pc In-Reply-To: <87d1g55h8d.fsf@mail.linkov.net> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2607:f8b0:400e:c05::243 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:211076 Archived-At: (Re-send with emacs-devel@gnu.org as CC) On Mon, 2 Jan 2017, Juri Linkov wrote: >>> If you need to see whether matching lines are before or after >>> the current line, then what do you think about showing and highlighting >>> the current line in the output *Occur* buffer? >> Inserting the current line, which might not match the regexp, in *Occur*; >> i am not sure about it. >> Please, try the second part of the patch, and let me know if looks >> useful for you. > > I tried it, and I'm not sure - the problem is that it's not obvious > that the highlighted line is the current line. Maybe better to put the cursor > in *Occur* on the first matching line after the current line? But I doubt whether > users will like this change in cursor positioning. See the updated patch. Play with it and let me know how you see it. In my case, just the first part of the patch, i.e., to run `occur' restricted to the region is all that i need. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; >From d2334a303db81f69b09dfe09d3332905dab6be68 Mon Sep 17 00:00:00 2001 From: Tino Calancha Date: Tue, 3 Jan 2017 19:00:08 +0900 Subject: [PATCH 1/3] Allow occur command to operate on the region See discussion in: https://lists.gnu.org/archive/html/emacs-devel/2016-12/msg01084.html * lisp/replace.el (occur--region-start, occur--region-end) (occur--matches-threshold): New variables. (occur-engine): Use them. (occur): Idem. Add optional args START, END; if non-nil occur applies in that region. * doc/lispintro/emacs-lisp-intro.texi (Keybindings): Update manual * doc/emacs/search.texi (Other Repeating Search: Idem. ; etc/NEWS: Add entry to announce the change. --- doc/emacs/search.texi | 3 +++ doc/lispintro/emacs-lisp-intro.texi | 8 ++++--- etc/NEWS | 2 ++ lisp/replace.el | 48 +++++++++++++++++++++++++++++++------ 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/doc/emacs/search.texi b/doc/emacs/search.texi index b728258973..28e25bec43 100644 --- a/doc/emacs/search.texi +++ b/doc/emacs/search.texi @@ -1672,6 +1672,9 @@ Other Repeating Search no upper-case letters and @code{case-fold-search} is non-@code{nil}. Aside from @code{occur} and its variants, all operate on the text from point to the end of the buffer, or on the region if it is active. +The command @code{occur} will operate on the region if +it is active as well; when the region is not active, @code{occur} +operates in the whole buffer. @findex list-matching-lines @findex occur diff --git a/doc/lispintro/emacs-lisp-intro.texi b/doc/lispintro/emacs-lisp-intro.texi index 830c072cf5..36d767737d 100644 --- a/doc/lispintro/emacs-lisp-intro.texi +++ b/doc/lispintro/emacs-lisp-intro.texi @@ -17151,9 +17151,11 @@ Keybindings @findex occur The @code{occur} command shows all the lines in the current buffer -that contain a match for a regular expression. Matching lines are -shown in a buffer called @file{*Occur*}. That buffer serves as a menu -to jump to occurrences. +that contain a match for a regular expression. When the region is +active, @code{occur} restricts matches to such region. Otherwise it +uses the entire buffer. +Matching lines are shown in a buffer called @file{*Occur*}. +That buffer serves as a menu to jump to occurrences. @findex global-unset-key @cindex Unbinding key diff --git a/etc/NEWS b/etc/NEWS index d91204b21b..cb01e03971 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -301,6 +301,8 @@ substituted by a home directory by writing it as "/foo:/:/~/file". * Editing Changes in Emacs 26.1 + +** The 'occur' command can now operate on the region. +++ ** New bindings for 'query-replace-map'. 'undo', undo the last replacement; bound to 'u'. diff --git a/lisp/replace.el b/lisp/replace.el index ff91734445..cedf8e0c67 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -1360,7 +1360,12 @@ occur-rename-buffer "*") (or unique-p (not interactive-p))))) -(defun occur (regexp &optional nlines) +;; Region limits when `occur' applies on a region. +(defvar occur--region-start nil) +(defvar occur--region-end nil) +(defvar occur--matches-threshold nil) + +(defun occur (regexp &optional nlines start end) "Show all lines in the current buffer containing a match for REGEXP. If a match spreads across multiple lines, all those lines are shown. @@ -1369,6 +1374,13 @@ occur NLINES defaults to `list-matching-lines-default-context-lines'. Interactively it is the prefix arg. +Optional args START and END, if non-nil, mean restrict search to the +specified region. + When START is non-nil and END is nil, END defaults to the last + accessible position in the current buffer. + When END is non-nil and START is nil, START defaults to the first + accessible position in the current buffer. + The lines are shown in a buffer named `*Occur*'. It serves as a menu to find any of the occurrences in this buffer. \\\\[describe-mode] in that buffer will explain how. @@ -1386,8 +1398,23 @@ occur program. When there is no parenthesized subexpressions in REGEXP the entire match is collected. In any case the searched buffer is not modified." - (interactive (occur-read-primary-args)) - (occur-1 regexp nlines (list (current-buffer)))) + (interactive + (nconc (occur-read-primary-args) + (list (and (use-region-p) (region-beginning)) + (and (use-region-p) (region-end))))) + (let ((in-region-p (or start end))) + (when in-region-p + (or start (setq start (point-min))) + (or end (setq end (point-max)))) + (let ((occur--region-start start) + (occur--region-end end) + (occur--matches-threshold + (and in-region-p + (line-number-at-pos (min start end))))) + (save-excursion ; If no matches `occur-1' doesn't restore the point. + (and in-region-p (narrow-to-region start end)) + (occur-1 regexp nlines (list (current-buffer))) + (and in-region-p (widen)))))) (defvar ido-ignore-item-temp-list) @@ -1545,13 +1572,15 @@ occur-engine (let ((global-lines 0) ;; total count of matching lines (global-matches 0) ;; total count of matches (coding nil) - (case-fold-search case-fold)) + (case-fold-search case-fold) + (in-region-p (and occur--region-start occur--region-end))) ;; Map over all the buffers (dolist (buf buffers) (when (buffer-live-p buf) (let ((lines 0) ;; count of matching lines (matches 0) ;; count of matches - (curr-line 1) ;; line count + (curr-line ;; line count + (or occur--matches-threshold 1)) (prev-line nil) ;; line number of prev match endpt (prev-after-lines nil) ;; context lines of prev match (matchbeg 0) @@ -1684,7 +1713,7 @@ occur-engine (let ((beg (point)) end) (insert (propertize - (format "%d match%s%s%s in buffer: %s\n" + (format "%d match%s%s%s in buffer: %s%s\n" matches (if (= matches 1) "" "es") ;; Don't display the same number of lines ;; and matches in case of 1 match per line. @@ -1694,7 +1723,12 @@ occur-engine ;; Don't display regexp for multi-buffer. (if (> (length buffers) 1) "" (occur-regexp-descr regexp)) - (buffer-name buf)) + (buffer-name buf) + (if in-region-p + (format " within region: %d-%d" + occur--region-start + occur--region-end) + "")) 'read-only t)) (setq end (point)) (add-text-properties beg end `(occur-title ,buf)) -- 2.11.0 >From 5a27c052cc1469c72a57d624010a2a8d0c0543b9 Mon Sep 17 00:00:00 2001 From: Tino Calancha Date: Tue, 3 Jan 2017 19:00:29 +0900 Subject: [PATCH 2/3] Show current line highlighted in *Occur* buffer * lisp/replace.el (occur--orig-line, occur--orig-line-str): New variables. (occur, occur-engine): Use them. --- lisp/replace.el | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/lisp/replace.el b/lisp/replace.el index cedf8e0c67..448ac2cf3c 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -1364,6 +1364,8 @@ occur-rename-buffer (defvar occur--region-start nil) (defvar occur--region-end nil) (defvar occur--matches-threshold nil) +(defvar occur--orig-line nil) +(defvar occur--orig-line-str nil) (defun occur (regexp &optional nlines start end) "Show all lines in the current buffer containing a match for REGEXP. @@ -1410,7 +1412,13 @@ occur (occur--region-end end) (occur--matches-threshold (and in-region-p - (line-number-at-pos (min start end))))) + (line-number-at-pos (min start end)))) + (occur--orig-line + (line-number-at-pos (point))) + (occur--orig-line-str + (buffer-substring-no-properties + (line-beginning-position) + (line-end-position)))) (save-excursion ; If no matches `occur-1' doesn't restore the point. (and in-region-p (narrow-to-region start end)) (occur-1 regexp nlines (list (current-buffer))) @@ -1581,6 +1589,9 @@ occur-engine (matches 0) ;; count of matches (curr-line ;; line count (or occur--matches-threshold 1)) + (orig-line occur--orig-line) + (orig-line-str occur--orig-line-str) + (orig-line-shown-p) (prev-line nil) ;; line number of prev match endpt (prev-after-lines nil) ;; context lines of prev match (matchbeg 0) @@ -1687,6 +1698,12 @@ occur-engine (nth 0 ret)))) ;; Actually insert the match display data (with-current-buffer out-buf + (when (and (not orig-line-shown-p) + (>= curr-line orig-line)) + (insert + (concat + (propertize orig-line-str 'face 'query-replace) "\n")) + (setq orig-line-shown-p t)) (insert data))) (goto-char endpt)) (if endpt @@ -1700,6 +1717,12 @@ occur-engine (forward-line 1)) (goto-char (point-max))) (setq prev-line (1- curr-line))) + ;; Insert original line if haven't done yet. + (unless orig-line-shown-p + (with-current-buffer out-buf + (insert + (concat + (propertize orig-line-str 'face 'query-replace) "\n")))) ;; Flush remaining context after-lines. (when prev-after-lines (with-current-buffer out-buf -- 2.11.0 >From 366d7e5118d1411b7b43925c3a95bcb50a04db90 Mon Sep 17 00:00:00 2001 From: Tino Calancha Date: Tue, 3 Jan 2017 19:00:47 +0900 Subject: [PATCH 3/3] occur: Set point on the first matching line after the current one * lisp/replace.el (occur-current-line-face): New face. (occur--final-pos): New variable. (occur-1): Use it. (occur-engine): Idem. Show the current line with 'occur-current-line-face'. --- lisp/replace.el | 45 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/lisp/replace.el b/lisp/replace.el index 448ac2cf3c..0c67fe3640 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -1081,6 +1081,12 @@ occur-mode-find-occurrence-hook :type 'hook :group 'matching) +(defface occur-current-line-face + '((t (:inherit lazy-highlight))) + "Face for highlighting the current line in *Occur* buffer." + :group 'matching + :version "26.1") + (put 'occur-mode 'mode-class 'special) (define-derived-mode occur-mode special-mode "Occur" "Major mode for output from \\[occur]. @@ -1366,6 +1372,7 @@ occur--region-end (defvar occur--matches-threshold nil) (defvar occur--orig-line nil) (defvar occur--orig-line-str nil) +(defvar occur--final-pos nil) (defun occur (regexp &optional nlines start end) "Show all lines in the current buffer containing a match for REGEXP. @@ -1517,7 +1524,8 @@ occur-1 (occur-mode)) (let ((inhibit-read-only t) ;; Don't generate undo entries for creation of the initial contents. - (buffer-undo-list t)) + (buffer-undo-list t) + (occur--final-pos nil)) (erase-buffer) (let ((count (if (stringp nlines) @@ -1569,6 +1577,10 @@ occur-1 (if (= count 0) (kill-buffer occur-buf) (display-buffer occur-buf) + (when occur--final-pos + (set-window-point + (get-buffer-window occur-buf 'all-frames) + occur--final-pos)) (setq next-error-last-buffer occur-buf) (setq buffer-read-only t) (set-buffer-modified-p nil) @@ -1581,7 +1593,8 @@ occur-engine (global-matches 0) ;; total count of matches (coding nil) (case-fold-search case-fold) - (in-region-p (and occur--region-start occur--region-end))) + (in-region-p (and occur--region-start occur--region-end)) + (multi-occur-p (cdr buffers))) ;; Map over all the buffers (dolist (buf buffers) (when (buffer-live-p buf) @@ -1598,6 +1611,7 @@ occur-engine (origpt nil) (begpt nil) (endpt nil) + (finalpt nil) (marker nil) (curstring "") (ret nil) @@ -1698,12 +1712,17 @@ occur-engine (nth 0 ret)))) ;; Actually insert the match display data (with-current-buffer out-buf - (when (and (not orig-line-shown-p) + (when (and (not multi-occur-p) + (not orig-line-shown-p) (>= curr-line orig-line)) (insert (concat - (propertize orig-line-str 'face 'query-replace) "\n")) - (setq orig-line-shown-p t)) + (propertize + (format "%7d:%s" orig-line orig-line-str) + 'face 'occur-current-line-face + 'mouse-face 'mode-line-highlight + 'help-echo "Current line") "\n")) + (setq orig-line-shown-p t finalpt (point))) (insert data))) (goto-char endpt)) (if endpt @@ -1718,11 +1737,16 @@ occur-engine (goto-char (point-max))) (setq prev-line (1- curr-line))) ;; Insert original line if haven't done yet. - (unless orig-line-shown-p + (when (and (not multi-occur-p) + (not orig-line-shown-p)) (with-current-buffer out-buf (insert (concat - (propertize orig-line-str 'face 'query-replace) "\n")))) + (propertize + (format "%7d:%s" orig-line orig-line-str) + 'face 'occur-current-line-face + 'mouse-face 'mode-line-highlight + 'help-echo "Current line") "\n")))) ;; Flush remaining context after-lines. (when prev-after-lines (with-current-buffer out-buf @@ -1756,8 +1780,11 @@ occur-engine (setq end (point)) (add-text-properties beg end `(occur-title ,buf)) (when title-face - (add-face-text-property beg end title-face))) - (goto-char (point-min))))))) + (add-face-text-property beg end title-face)) + (goto-char (if finalpt + (setq occur--final-pos + (cl-incf finalpt (- end beg))) + (point-min))))))))) ;; Display total match count and regexp for multi-buffer. (when (and (not (zerop global-lines)) (> (length buffers) 1)) (goto-char (point-min)) -- 2.11.0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; In GNU Emacs 26.0.50.1 (x86_64-pc-linux-gnu, GTK+ Version 3.22.5) of 2017-01-03 Repository revision: 975b2acfe6a4e246631c372063d7bdef0f832d3d