From: Visuwesh <visuweshm@gmail.com>
To: Jim Porter <jporterbugs@gmail.com>
Cc: 70820@debbugs.gnu.org
Subject: bug#70820: [PATCH] Editable grep buffers
Date: Wed, 08 May 2024 10:41:10 +0530 [thread overview]
Message-ID: <87cypwlwgh.fsf@gmail.com> (raw)
In-Reply-To: <a7395437-0f93-f7fc-da14-41b7ca65d291@gmail.com> (Jim Porter's message of "Tue, 7 May 2024 21:11:42 -0700")
[-- Attachment #1: Type: text/plain, Size: 1899 bytes --]
[செவ்வாய் மே 07, 2024] Jim Porter wrote:
> On 5/7/2024 8:12 PM, Visuwesh wrote:
>> Thanks for your input. If I missed something from wgrep that you use,
>> please let me known. I will play around with wgrep in some time to
>> learn more about what it offers myself.
>
> In addition to the things you mentioned, here are the most important
> features from wgrep for me:
Thanks.
> * Mark all non-result parts of the buffer (the command string, file
> names, line numbers, etc) as read-only. This is especially valuable
> if you want to use 'query-replace' or similar to modify the
> results. Then you can't inadvertently edit those bits.
This is now done.
> * Fontify any results with changes (both in the grep-mode buffer and
> the original files). This is really useful for being able to see
> what I've changed.
I agree that it would be nice to have this in *grep* buffer but I would
like to avoid editing the file on-the-fly since it introduces typing lag
IME with occur-edit-mode. If you want to know the edits that were made,
you can use diff-buffer-with-file so I hope leaving out the highlighting
in the file would be okay.
> * Adding all the necessary hooks/functions so other grep-like modes
> can use this. For example, see this file from wgrep, which lets you
> use wgrep with ag.el:
> <https://github.com/mhayashi1120/Emacs-wgrep/blob/master/wgrep-ag.el>. (This
> matters to me partly because I'm the author of Urgrep - a "universal
> recursive grep" mode that can use any grep-like program to do
> searches. I've added my own support in Urgrep for wgrep.)
With grep-edit-minor-mode, as long as the compilation-message
text-property is present at the beginning of the line, you do not need
to do anything extra. I look at this to gain the information about the
filename and the line number.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: grep-edit.diff --]
[-- Type: text/x-diff, Size: 4328 bytes --]
diff --git a/lisp/progmodes/grep.el b/lisp/progmodes/grep.el
index 657349cbdff..517d1bd5ecd 100644
--- a/lisp/progmodes/grep.el
+++ b/lisp/progmodes/grep.el
@@ -31,6 +31,7 @@
(eval-when-compile (require 'cl-lib))
(require 'compile)
+(require 'track-changes)
(defgroup grep nil
"Run `grep' and display the results."
@@ -295,6 +296,8 @@ grep-mode-map
(define-key map "}" #'compilation-next-file)
(define-key map "\t" #'compilation-next-error)
(define-key map [backtab] #'compilation-previous-error)
+
+ (define-key map "e" #'grep-edit-minor-mode)
map)
"Keymap for grep buffers.
`compilation-minor-mode-map' is a cdr of this.")
@@ -1029,6 +1032,86 @@ grep
command-args)
#'grep-mode))
+(defvar-local grep-edit--tracker nil
+ "ID of the `track-changes' tracker.")
+
+(defun grep-edit--track-changes-signal (_id &optional _distance)
+ ;; Empty because we want to simply accumulate the changes at end
+ ;; when the user calls the finish function.
+ nil)
+
+(defun grep-edit--track-changes-finalise (beg end _before)
+ (let ((grep-buf (current-buffer))
+ (cur-res beg)) ; Point at current grep result.
+ (save-excursion
+ (while (<= cur-res end)
+ (goto-char cur-res)
+ (let* ((change-beg (next-single-char-property-change (pos-bol) 'compilation-message))
+ ;; `1-' is required to ignore the newline.
+ (change-end (1- (next-single-char-property-change (pos-eol) 'compilation-message)))
+ (loc (compilation--message->loc
+ (get-text-property (pos-bol) 'compilation-message)))
+ (line (compilation--loc->line loc))
+ (file (caar (compilation--loc->file-struct loc))))
+ (with-current-buffer (find-file-noselect file)
+ (save-excursion
+ (goto-char (point-min))
+ (forward-line (1- line))
+ (delete-region (pos-bol) (pos-eol))
+ (insert-buffer-substring-no-properties grep-buf change-beg change-end))))
+ (setq cur-res (next-single-property-change cur-res 'compilation-message))))))
+
+(defun grep-edit--mark-read-only ()
+ "Mark annotations and info regions read-only."
+ (save-excursion
+ (goto-char (point-min))
+ (let ((inhibit-read-only t)
+ match)
+ (while (setq match (text-property-search-forward 'compilation-annotation))
+ (add-text-properties (prop-match-beginning match) (prop-match-end match)
+ '(read-only t)))
+ (goto-char (point-min))
+ (while (setq match (text-property-search-forward 'compilation-message))
+ (add-text-properties (prop-match-beginning match) (prop-match-end match)
+ '(read-only t))))))
+
+(define-minor-mode grep-edit-minor-mode
+ "Minor mode for editing *grep* buffers.
+In this mode, changes to the *grep* buffer are applied to the
+originating files upon saving using \\[grep-save-changes]."
+ :lighter " Grep-Edit"
+ (if (null grep-edit-minor-mode)
+ (progn
+ (setq buffer-read-only t)
+ (buffer-disable-undo)
+ (use-local-map grep-mode-map))
+ (grep-edit--mark-read-only)
+ (unless grep-edit--tracker
+ (use-local-map grep-edit-minor-mode-map)
+ (setq buffer-read-only nil)
+ (buffer-enable-undo)
+ (setq grep-edit--tracker
+ (track-changes-register #'grep-edit--track-changes-signal
+ :disjoint t)))
+ (message (substitute-command-keys
+ "Editing: \\[grep-edit-save-changes] to return to Grep mode."))))
+
+(defun grep-edit-save-changes ()
+ "Save the changes made to the *grep* buffer."
+ (interactive)
+ (when (and grep-edit-minor-mode grep-edit--tracker)
+ (track-changes-fetch grep-edit--tracker #'grep-edit--track-changes-finalise)
+ (message "Applied edits, switching to Grep mode.")
+ (track-changes-unregister grep-edit--tracker)
+ (setq grep-edit--tracker nil)
+ (grep-edit-minor-mode -1)))
+
+(defvar grep-edit-minor-mode-map
+ (let ((map (make-sparse-keymap)))
+ (set-keymap-parent map text-mode-map)
+ (define-key map (kbd "C-c C-c") #'grep-edit-save-changes)
+ map)
+ "Keymap for `grep-edit-minor-mode'.")
;;;###autoload
(defun grep-find (command-args)
next prev parent reply other threads:[~2024-05-08 5:11 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-05-07 16:25 bug#70820: [PATCH] Editable grep buffers Visuwesh
2024-05-07 17:23 ` Jim Porter
2024-05-08 3:12 ` Visuwesh
2024-05-08 4:11 ` Jim Porter
2024-05-08 5:11 ` Visuwesh [this message]
2024-05-18 13:23 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-05-07 18:08 ` Eli Zaretskii
2024-05-08 3:22 ` Visuwesh
2024-05-08 11:58 ` Eli Zaretskii
2024-05-08 12:18 ` Visuwesh
2024-05-08 13:49 ` Eli Zaretskii
2024-05-09 10:32 ` Visuwesh
2024-05-12 4:45 ` Visuwesh
2024-05-18 9:28 ` Eli Zaretskii
2024-05-18 13:35 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-05-18 15:44 ` Eli Zaretskii
2024-05-18 16:27 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-05-18 16:28 ` Eli Zaretskii
2024-05-20 10:10 ` Visuwesh
2024-05-18 13:34 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-05-08 17:37 ` Jim Porter
2024-05-08 18:42 ` Eli Zaretskii
2024-05-08 19:19 ` Jim Porter
2024-05-08 19:23 ` Jim Porter
2024-05-09 4:41 ` Eli Zaretskii
2024-05-09 16:14 ` Jim Porter
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87cypwlwgh.fsf@gmail.com \
--to=visuweshm@gmail.com \
--cc=70820@debbugs.gnu.org \
--cc=jporterbugs@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).