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: Fri, 20 Jan 2017 22:48:45 +0900 Message-ID: <87ziilc05e.fsf@gmail.com> 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> <87r33ywquz.fsf@mail.linkov.net> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Trace: blaine.gmane.org 1484921952 15737 195.159.176.226 (20 Jan 2017 14:19:12 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Fri, 20 Jan 2017 14:19:12 +0000 (UTC) User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.0.50 (gnu/linux) Cc: Emacs developers , Tino Calancha To: Juri Linkov Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Fri Jan 20 15:19:06 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 1cUa1q-0003Ai-Ht for ged-emacs-devel@m.gmane.org; Fri, 20 Jan 2017 15:18:58 +0100 Original-Received: from localhost ([::1]:55043 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cUa1v-0001U6-7e for ged-emacs-devel@m.gmane.org; Fri, 20 Jan 2017 09:19:03 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:57663) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cUZYo-0001CF-6m for emacs-devel@gnu.org; Fri, 20 Jan 2017 08:49:00 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cUZYk-0007S8-VS for emacs-devel@gnu.org; Fri, 20 Jan 2017 08:48:58 -0500 Original-Received: from mail-pg0-x242.google.com ([2607:f8b0:400e:c05::242]:32912) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1cUZYk-0007R5-KH for emacs-devel@gnu.org; Fri, 20 Jan 2017 08:48:54 -0500 Original-Received: by mail-pg0-x242.google.com with SMTP id 194so7009493pgd.0 for ; Fri, 20 Jan 2017 05:48:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:in-reply-to:references:user-agent:date :message-id:mime-version:content-transfer-encoding; bh=Yvwomq5zFGGpr1WYIDKmzNFxWcZrLC5Aih6BIW6yxf8=; b=KcSs7aJ96vui5YiKvDsRU+S11JWXLSv36pb0YNBMGsn4Tm9Jrqf64IRoXnbvbkcifh b944zFsyH2i+wENbWoG4un87MN9wu/hCkMCG1nhJbEvQXmcI4kwMv2vW05O2Q4x+3KMu jDwkkwdZGBJpYNsqshv97gzSZ9JSIXuRg4azI7pJLBtpxoEiikUuW2j/TwrkZAQcGjxQ LiwVxlXwGIdAKhR2D0uh5onjPU668y6/j7morvq6SrxMIo19tBcbdR8dOYI7Kg0xqCIL SWJWEK3TsC6lHVCkdFBHJBOfou4Egdgv/ZJ9m7HyVrdILVFSY/Pb1vviKUysyOewnKVG oyqg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:in-reply-to:references :user-agent:date:message-id:mime-version:content-transfer-encoding; bh=Yvwomq5zFGGpr1WYIDKmzNFxWcZrLC5Aih6BIW6yxf8=; b=N5LeHjhyI8pwGOiqz8qndW8Zx7QvF5DEuIOMYGxJ/Kv00okFD2yELCzoQRXW2xqjq0 Y9ciutY+Edn4U+pslNFBdEGnWiK3pi95F9/ll9BOur5iOnQhVm5b+l4xF+3CvUdZSJ0B igkEil0OZwt7nUM68h0hNp8WQsanTcZ9x4yQxGj1sOymUHxCCCq1l/k9yZfNxBr7WiCx 7MldVosdaigFgTznmDSd+K16T0jQ3zHOg8ESNv5MBMJ8dWkgxXs25oYqf9mNuA6mj4RH 9PfqrWgWugmXKf3awlQclYDAkjy3JbJfXk2N61fUW2H9H4oFxS+jN2tRZiZ/MXNnlKtM WLeQ== X-Gm-Message-State: AIkVDXI8aijZOdPBh0fSj/K0HlVlijXwWlc9EyhBawiQMhYcn0+Koz/F+7g6FoR5WtOjPQ== X-Received: by 10.98.36.134 with SMTP id k6mr16475692pfk.41.1484920131912; Fri, 20 Jan 2017 05:48:51 -0800 (PST) Original-Received: from calancha-pc (33.94.100.220.dy.bbexcite.jp. [220.100.94.33]) by smtp.gmail.com with ESMTPSA id w16sm17228331pgc.15.2017.01.20.05.48.49 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 20 Jan 2017 05:48:51 -0800 (PST) In-Reply-To: <87r33ywquz.fsf@mail.linkov.net> (Juri Linkov's message of "Fri, 20 Jan 2017 01:51:32 +0200") X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2607:f8b0:400e:c05::242 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:211425 Archived-At: Juri Linkov writes: >>> 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. >> >> do you have any preference on the proposed patches? > > Please see the thread regarding the region argument: > http://lists.gnu.org/archive/html/emacs-devel/2017-01/msg00044.html > i.e. a new convention is to add a single arg =E2=80=98region=E2=80=99 > instead of the =E2=80=98start=E2=80=99/=E2=80=98end=E2=80=99 pair. > > Could you try to update your patch accordingly? OK. Updated the patch. *) First patch allow `occur' handle the region with the new convention, i.e., region argument instead of BEG, END. **) The second patch add your suggestion on showing highlighted the current line. When there are matches after the current line, the point in *Occur* is set right after such line. I am fine with just adding *). In case we also want **),=20 an option to enable/disable that behaviour might be desirable. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;= ;;;; >From 5d69affd729ceed80de9f1ede37a5c7a541b23d4 Mon Sep 17 00:00:00 2001 From: Tino Calancha Date: Fri, 20 Jan 2017 22:11:11 +0900 Subject: [PATCH 1/2] Allow occur command to operate on the region See discussion in: https://lists.gnu.org/archive/html/emacs-devel/2016-12/msg01084.html * lisp/simple.el (region): New defun. * lisp/replace.el (occur--region-start, occur--region-end) (occur--matches-threshold): New variables. (occur-engine): Use them. (occur): Idem. Add optional arg REGION; 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 | 46 +++++++++++++++++++++++++++++++--= ---- lisp/simple.el | 3 +++ 5 files changed, 52 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. =20 @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 =20 @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. =20 @findex global-unset-key @cindex Unbinding key diff --git a/etc/NEWS b/etc/NEWS index 051b97e146..d4917f402f 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 =20 + +** 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..d9c3a4ff3e 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -1360,7 +1360,12 @@ occur-rename-buffer "*") (or unique-p (not interactive-p))))) =20 -(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 region) "Show all lines in the current buffer containing a match for REGEXP. If a match spreads across multiple lines, all those lines are shown. =20 @@ -1369,6 +1374,10 @@ occur NLINES defaults to `list-matching-lines-default-context-lines'. Interactively it is the prefix arg. =20 +Optional arg REGION, if non-nil, mean restrict search to the +specified region. Otherwise search the entire buffer. +When REGION is non-nil, it must be a cons (START . END). + 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 +1395,24 @@ 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) + (and (use-region-p) (region)))) + (let* ((start (car region)) + (end (cdr region)) + (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)))))) =20 (defvar ido-ignore-item-temp-list) =20 @@ -1545,13 +1570,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 +1711,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 (=3D matches 1) "" "es") ;; Don't display the same number of lines ;; and matches in case of 1 match per line. @@ -1694,7 +1721,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)) diff --git a/lisp/simple.el b/lisp/simple.el index f798cd4384..56c4551490 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -1015,6 +1015,9 @@ region-extract-function If anything else, delete the region and return its content as a string, after filtering it with `filter-buffer-substring'.") =20 +(defun region () + (funcall region-extract-function 'bounds)) + (defvar region-insert-function (lambda (lines) (let ((first t)) --=20 2.11.0 >From 763ca506fed54afacc30ca1904f0727bf0bf2dbf Mon Sep 17 00:00:00 2001 From: Tino Calancha Date: Fri, 20 Jan 2017 22:14:39 +0900 Subject: [PATCH 2/2] Show current line highlighted in *Occur* buffer * lisp/replace.el (occur--orig-line, occur--orig-line-str): New variables. (occur, occur-engine): Use them. (occur--final-pos): New variable. (occur-1): Use it. (occur-engine): Idem. Show the current line with 'occur-current-line-face'. Set point on the first matching line after the current one. --- lisp/replace.el | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++-= ---- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/lisp/replace.el b/lisp/replace.el index d9c3a4ff3e..ed5cdb718b 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -1081,6 +1081,12 @@ occur-mode-find-occurrence-hook :type 'hook :group 'matching) =20 +(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]. @@ -1364,6 +1370,9 @@ 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) +(defvar occur--final-pos nil) =20 (defun occur (regexp &optional nlines region) "Show all lines in the current buffer containing a match for REGEXP. @@ -1408,7 +1417,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))) @@ -1507,7 +1522,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) @@ -1559,6 +1575,10 @@ occur-1 (if (=3D 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) @@ -1571,7 +1591,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) @@ -1579,12 +1600,16 @@ 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) (origpt nil) (begpt nil) (endpt nil) + (finalpt nil) (marker nil) (curstring "") (ret nil) @@ -1685,6 +1710,17 @@ occur-engine (nth 0 ret)))) ;; Actually insert the match display data (with-current-buffer out-buf + (when (and (not multi-occur-p) + (not orig-line-shown-p) + (>=3D curr-line orig-line)) + (insert + (concat + (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 @@ -1698,6 +1734,17 @@ occur-engine (forward-line 1)) (goto-char (point-max))) (setq prev-line (1- curr-line))) + ;; Insert original line if haven't done yet. + (when (and (not multi-occur-p) + (not orig-line-shown-p)) + (with-current-buffer out-buf + (insert + (concat + (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 @@ -1731,8 +1778,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)) --=20 2.11.0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;= ;;;; In GNU Emacs 26.0.50.1 (x86_64-pc-linux-gnu, GTK+ Version 3.22.6) of 2017-01-20 Repository revision: 82a5e4dc889ecbfa35374616fe9c5edfa23f4504