From debbugs-submit-bounces@debbugs.gnu.org Mon Jun 16 07:12:00 2025 Received: (at submit) by debbugs.gnu.org; 16 Jun 2025 11:12:00 +0000 Received: from localhost ([127.0.0.1]:43264 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uR7ks-0001w5-Qr for submit@debbugs.gnu.org; Mon, 16 Jun 2025 07:12:00 -0400 Received: from lists.gnu.org ([2001:470:142::17]:59734) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1uR7kp-0001vW-Sg for submit@debbugs.gnu.org; Mon, 16 Jun 2025 07:11:57 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uR7kj-0002jL-KE for bug-gnu-emacs@gnu.org; Mon, 16 Jun 2025 07:11:49 -0400 Received: from mailscanner.iro.umontreal.ca ([132.204.25.50]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uR7kg-00042i-CB for bug-gnu-emacs@gnu.org; Mon, 16 Jun 2025 07:11:49 -0400 Received: from pmg2.iro.umontreal.ca (localhost.localdomain [127.0.0.1]) by pmg2.iro.umontreal.ca (Proxmox) with ESMTP id 3D03780962 for ; Mon, 16 Jun 2025 07:11:44 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=iro.umontreal.ca; s=mail; t=1750072299; bh=idUOIclskF3QF2iaDTboHYv3xWqXWr9nXHQYglypQO8=; h=From:To:Subject:Date:From; b=IY46TjD4FPC0/eIj2AzNUW0Zv8isdgjnhi4604QbFeFHjB+29nD7nwKKwtkTUI1o7 thEOnJx2rZ+rO+ZrOUGkPjSUIOVK7Dq21Q7z427HecW9B30VKITQhoqiO4g7S9GwBq DpM0puYEhvWv3s/PgG8j1Vos8DRcaNK0oGG12gS06J4AhJ8GEiR5H5YnKfPECFaePe z0n9h7xYkgTxl9JIJJonxd02pgI/ASg9G5x8YyxXM3Vuk0AJt/HFjHd9cIBmkqIqH9 j7wrFE9WuG6yrFhM/+iKzghELetWk10ZF2vS/dlYB6uXiFMa0tM/K51rX86Th3DAH7 kfLUUxiXpHwCw== Received: from mail01.iro.umontreal.ca (unknown [172.31.2.1]) by pmg2.iro.umontreal.ca (Proxmox) with ESMTP id 9C0DE807D7 for ; Mon, 16 Jun 2025 07:11:39 -0400 (EDT) Received: from asado (unknown [104.247.225.139]) by mail01.iro.umontreal.ca (Postfix) with ESMTPSA id 7C57A1201A9 for ; Mon, 16 Jun 2025 07:11:39 -0400 (EDT) From: Stefan Monnier To: bug-gnu-emacs@gnu.org Subject: [PATCH] (smerge-refine-region): Add support for a virtual "other cursor" X-Debbugs-Cc: , Stefan Monnier , Stefan Monnier Date: Mon, 16 Jun 2025 07:11:24 -0400 Message-ID: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-SPAM-INFO: Spam detection results: 0 ALL_TRUSTED -1 Passed through trusted hosts only via SMTP AWL -0.342 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's domain DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from domain X-SPAM-LEVEL: Received-SPF: pass client-ip=132.204.25.50; envelope-from=monnier@iro.umontreal.ca; helo=mailscanner.iro.umontreal.ca X-Spam_score_int: -42 X-Spam_score: -4.3 X-Spam_bar: ---- X-Spam_report: (-4.3 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: submit X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain Tags: patch The patch below adds support to `smerge-refine-region` for keeping a kind of "virtual cursor" that shows the "corresponding" point in the other part of a diff. I.e. it shows where `smerge-refine-exchange-point` would jump. The patch is somewhat incomplete in that it works only after the user explicitly calls `M-x cursor-sensor-mode RET` (and it lacks a NEWS entry). I'm thinking of adding a global `smerge-refine-shadow-cursor-mode` minor mode to enable this feature and am thinking of enabling it by default. Comments, objections? Stefan In GNU Emacs 31.0.50 (build 1, i686-pc-linux-gnu, GTK+ Version 3.24.49, cairo version 1.18.4) of 2025-06-06 built on asado Repository revision: 8c3dd116ce3e4ef235618502512045d47a3f0329 Repository branch: work Windowing system distributor 'The X.Org Foundation', version 11.0.12101016 System Description: Debian GNU/Linux 13 (trixie) Configured using: 'configure -C --enable-checking --enable-check-lisp-object-type --with-modules --with-cairo --with-tiff=ifavailable 'CFLAGS=-Wall -g3 -Og -Wno-pointer-sign' PKG_CONFIG_PATH=/home/monnier/lib/pkgconfig' --=-=-= Content-Type: text/patch Content-Disposition: attachment; filename=0001-smerge-refine-region-Add-support-for-a-virtual-other.patch >From e049fa40680526d7f225bf2fc37059975bbf805c Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Mon, 16 Jun 2025 07:05:11 -0400 Subject: [PATCH] (smerge-refine-region): Add support for a virtual "other cursor" * lisp/vc/smerge-mode.el (smerge-refine-regions): Add `cursor-sensor-functions` property to the covering overlays. (smerge--refine-at-right-margin-p, smerge--refine-shadow-cursor): New functions. (smerge--refine-other-pos): New function, extracted from `smerge-refine-exchange-point`. (smerge-refine-exchange-point): Use it. (smerge--refine-highlight-change): Add thin highlighted space for insertion/deletion positions. * lisp/emacs-lisp/cursor-sensor.el (cursor-sensor--detect): Run functions for `moved` events. Demote errors. (cursor-sensor-mode): Adjust docstring accordingly. --- lisp/emacs-lisp/cursor-sensor.el | 126 ++++++++++++++++--------------- lisp/vc/smerge-mode.el | 110 ++++++++++++++++++++------- 2 files changed, 150 insertions(+), 86 deletions(-) diff --git a/lisp/emacs-lisp/cursor-sensor.el b/lisp/emacs-lisp/cursor-sensor.el index b0ace3ce8f0..87bc73e06b0 100644 --- a/lisp/emacs-lisp/cursor-sensor.el +++ b/lisp/emacs-lisp/cursor-sensor.el @@ -141,63 +141,70 @@ cursor-intangible-mode ;;; Detect cursor movement. (defun cursor-sensor--detect (&optional window) - (with-current-buffer (window-buffer window) - (unless cursor-sensor-inhibit - (let* ((point (window-point window)) - ;; It's often desirable to make the - ;; cursor-sensor-functions property non-sticky on both - ;; ends, but that means get-pos-property might never - ;; see it. - (new (or (get-char-property point 'cursor-sensor-functions) - (unless (<= (point-min) point) - (get-char-property (1- point) - 'cursor-sensor-functions)))) - (old (window-parameter window 'cursor-sensor--last-state)) - (oldposmark (car old)) - (oldpos (or (if oldposmark (marker-position oldposmark)) - (point-min))) - (start (min oldpos point)) - (end (max oldpos point))) - (unless (or (null old) (eq (marker-buffer oldposmark) (current-buffer))) - ;; `window' does not display the same buffer any more! - (setcdr old nil)) - (if (or (and (null new) (null (cdr old))) - (and (eq new (cdr old)) - (eq (next-single-char-property-change - start 'cursor-sensor-functions nil end) - end))) - ;; Clearly nothing to do. - nil - ;; Maybe something to do. Let's see exactly what needs to run. - (let* ((missing-p - (lambda (f) - "Non-nil if F is missing somewhere between START and END." - (let ((pos start) - (missing nil)) - (while (< pos end) - (setq pos (next-single-char-property-change - pos 'cursor-sensor-functions - nil end)) - (unless (memq f (get-char-property - pos 'cursor-sensor-functions)) - (setq missing t))) - missing))) - (window (selected-window))) - (dolist (f (cdr old)) - (unless (and (memq f new) (not (funcall missing-p f))) - (funcall f window oldpos 'left))) - (dolist (f new) - (unless (and (memq f (cdr old)) (not (funcall missing-p f))) - (funcall f window oldpos 'entered))))) - - ;; Remember current state for next time. - ;; Re-read cursor-sensor-functions since the functions may have moved - ;; window-point! - (if old - (progn (move-marker (car old) point) - (setcdr old new)) - (set-window-parameter window 'cursor-sensor--last-state - (cons (copy-marker point) new))))))) + (with-demoted-errors "cursor-sensor--detect: %S" + (with-current-buffer (window-buffer window) + (unless cursor-sensor-inhibit + (let* ((point (window-point window)) + ;; It's often desirable to make the + ;; cursor-sensor-functions property non-sticky on both + ;; ends, but that means get-pos-property might never + ;; see it. + ;; FIXME: Should we combine properties from the various + ;; covering overlays? + (new (or (get-char-property point 'cursor-sensor-functions) + (unless (<= (point-min) point) + (get-char-property (1- point) + 'cursor-sensor-functions)))) + (old (window-parameter window 'cursor-sensor--last-state)) + (oldposmark (car old)) + (oldpos (or (if oldposmark (marker-position oldposmark)) + (point-min))) + (start (min oldpos point)) + (end (max oldpos point))) + (unless (or (null old) + (eq (marker-buffer oldposmark) (current-buffer))) + ;; `window' does not display the same buffer any more! + (setcdr old nil)) + (if (or (and (null new) (null (cdr old))) + ;; (and (eq new (cdr old)) + ;; (eq (next-single-char-property-change + ;; start 'cursor-sensor-functions nil end) + ;; end)) + ) + ;; Clearly nothing to do. + nil + ;; Maybe something to do. Let's see exactly what needs to run. + (let* ((missing-p + (lambda (f) + "Non-nil if F is missing somewhere between START and END." + (let ((pos start) + (missing nil)) + (while (< pos end) + (setq pos (next-single-char-property-change + pos 'cursor-sensor-functions + nil end)) + (unless (memq f (get-char-property + pos 'cursor-sensor-functions)) + (setq missing t))) + missing))) + (window (selected-window))) + (dolist (f (cdr old)) + (unless (and (memq f new) (not (funcall missing-p f))) + (funcall f window oldpos 'left))) + (dolist (f new) + (funcall f window oldpos + (if (or (not (memq f (cdr old))) (funcall missing-p f)) + 'entered + 'moved))))) + + ;; Remember current state for next time. + ;; Re-read cursor-sensor-functions since the functions may have moved + ;; window-point! + (if old + (progn (move-marker (car old) point) + (setcdr old new)) + (set-window-parameter window 'cursor-sensor--last-state + (cons (copy-marker point) new)))))))) ;;;###autoload (define-minor-mode cursor-sensor-mode @@ -205,8 +212,9 @@ cursor-sensor-mode This property should hold a list of functions which react to the motion of the cursor. They're called with three arguments (WINDOW OLDPOS DIR) where WINDOW is the affected window, OLDPOS is the last known position of -the cursor and DIR can be `entered' or `left' depending on whether the cursor -is entering the area covered by the text-property property or leaving it." +the cursor and DIR can be `entered', `left', or `moved' depending on whether +the cursor is entering the area covered by the text-property property, +leaving it, or just moving inside of it." :global nil (cond (cursor-sensor-mode diff --git a/lisp/vc/smerge-mode.el b/lisp/vc/smerge-mode.el index d1b27f6763b..1cc2e57a6b3 100644 --- a/lisp/vc/smerge-mode.el +++ b/lisp/vc/smerge-mode.el @@ -1089,10 +1089,14 @@ smerge--refine-highlight-change ;; (list match-num1 match-num2 startline)) (overlay-put ol 'evaporate t) (dolist (x props) - (when (or (> end beg) - ;; Don't highlight the char we cover artificially. - (not (memq (car-safe x) '(face font-lock-face)))) - (overlay-put ol (car x) (cdr x)))) + (if (or (> end beg) + (not (memq (car-safe x) '(face font-lock-face)))) + (overlay-put ol (car x) (cdr x)) + ;; Don't highlight the char we cover artificially. + (overlay-put ol (if (= beg olbeg) 'before-string 'after-string) + (propertize + " " (car-safe x) (cdr-safe x) + 'display '(space :width 0.5))))) ol))))) ;;;###autoload @@ -1124,7 +1128,9 @@ smerge-refine-regions (ol2 (make-overlay beg2 end2 nil ;; Make it shrink rather than spread when editing. 'front-advance nil)) - (common-props '((evaporate . t) (smerge--refine-region . t)))) + (common-props '((evaporate . t) (smerge--refine-region . t) + (cursor-sensor-functions + smerge--refine-shadow-cursor)))) (dolist (prop (or props-a props-c)) (when (and (not (memq (car prop) '(face font-lock-face))) (member prop (or props-r props-c)) @@ -1215,6 +1221,54 @@ smerge-refine-regions (define-obsolete-function-alias 'smerge-refine-subst #'smerge-refine-regions "26.1") +(defun smerge--refine-at-right-margin-p (pos window) + ;; FIXME: `posn-at-point' seems to be costly/slow. + (when-let* ((posn (posn-at-point pos window)) + (xy (nth 2 posn)) + (x (car-safe xy)) + (_ (numberp x))) + (> (+ x (with-selected-window window (string-pixel-width " "))) + (car (window-text-pixel-size window))))) + +(defun smerge--refine-shadow-cursor (window _oldpos dir) + (let ((ol (window-parameter window 'smerge--refine-shadow-cursor))) + (if (not (memq dir '(entered moved))) + (if ol (delete-overlay ol)) + (with-current-buffer (window-buffer window) + (let* ((cursor (window-point window)) + (other-beg (ignore-errors (smerge--refine-other-pos cursor)))) + (if (not other-beg) + (if ol (delete-overlay ol)) + (let ((other-end (min (point-max) (1+ other-beg)))) + ;; If other-beg/end covers a "wide" char like TAB or LF, the + ;; resulting shadow cursor doesn't look like a cursor, so try + ;; and convert it to a before-string space. + (when (or (and (eq ?\n (char-after other-beg)) + (not (smerge--refine-at-right-margin-p + other-beg window))) + (and (eq ?\t (char-after other-beg)) + ;; FIXME: `posn-at-point' seems to be costly/slow. + (when-let* ((posn (posn-at-point other-beg window)) + (xy (nth 2 posn)) + (x (car-safe xy)) + (_ (numberp x))) + (< (1+ (% x tab-width)) tab-width)))) + (setq other-end other-beg)) + ;; FIXME: Doesn't obey `cursor-in-non-selected-windows'. + (if ol (move-overlay ol other-beg other-end) + (setq ol (make-overlay other-beg other-end nil t nil)) + (setf (window-parameter window 'smerge--refine-shadow-cursor) + ol) + (overlay-put ol 'window window) + ;; FIXME: `region', really? + (overlay-put ol 'face 'region)) + ;; When the shadow cursor needs to be at EOB (or TAB or EOL), + ;; "draw" it as a pseudo space character. + (overlay-put ol 'before-string + (if (= other-beg other-end) + (eval-when-compile + (propertize " " 'face 'region))))))))))) + (defun smerge-refine (&optional part) "Highlight the words of the conflict that are different. For 3-way conflicts, highlights only two of the three parts. @@ -1265,56 +1319,58 @@ smerge-refine (unless smerge-use-changed-face '((smerge . refine) (font-lock-face . smerge-refined-added)))))) -(defun smerge-refine-exchange-point () - "Go to the matching position in the other chunk." - (interactive) +(defun smerge--refine-other-pos (pos) (let* ((covering-ol - (let ((ols (overlays-at (point)))) + (let ((ols (overlays-at pos))) (while (and ols (not (overlay-get (car ols) 'smerge--refine-region))) (pop ols)) (or (car ols) (user-error "Not inside a refined region")))) (ref-pos - (if (or (get-char-property (point) 'smerge--refine-other) - (get-char-property (1- (point)) 'smerge--refine-other)) - (point) + (if (or (get-char-property pos 'smerge--refine-other) + (get-char-property (1- pos) 'smerge--refine-other)) + pos (let ((next (next-single-char-property-change - (point) 'smerge--refine-other nil + pos 'smerge--refine-other nil (overlay-end covering-ol))) (prev (previous-single-char-property-change - (point) 'smerge--refine-other nil + pos 'smerge--refine-other nil (overlay-start covering-ol)))) (cond ((and (> prev (overlay-start covering-ol)) (or (>= next (overlay-end covering-ol)) - (> (- next (point)) (- (point) prev)))) + (> (- next pos) (- pos prev)))) prev) ((< next (overlay-end covering-ol)) next) (t (user-error "No \"other\" position info found")))))) (boundary (cond - ((< ref-pos (point)) + ((< ref-pos pos) (let ((adjust (get-char-property (1- ref-pos) 'smerge--refine-adjust))) - (min (point) (+ ref-pos (or (cdr adjust) 0))))) - ((> ref-pos (point)) + (min pos (+ ref-pos (or (cdr adjust) 0))))) + ((> ref-pos pos) (let ((adjust (get-char-property ref-pos 'smerge--refine-adjust))) - (max (point) (- ref-pos (or (car adjust) 0))))) + (max pos (- ref-pos (or (car adjust) 0))))) (t ref-pos))) (other-forw (get-char-property ref-pos 'smerge--refine-other)) (other-back (get-char-property (1- ref-pos) 'smerge--refine-other)) (other (or other-forw other-back)) - (dist (- boundary (point)))) + (dist (- boundary pos))) (if (not (overlay-start other)) (user-error "The \"other\" position has vanished") - (goto-char - (- (if other-forw - (- (overlay-start other) - (or (car (overlay-get other 'smerge--refine-adjust)) 0)) - (+ (overlay-end other) - (or (cdr (overlay-get other 'smerge--refine-adjust)) 0))) - dist))))) + (- (if other-forw + (- (overlay-start other) + (or (car (overlay-get other 'smerge--refine-adjust)) 0)) + (+ (overlay-end other) + (or (cdr (overlay-get other 'smerge--refine-adjust)) 0))) + dist)))) + +(defun smerge-refine-exchange-point () + "Go to the matching position in the other chunk." + (interactive) + (goto-char (smerge--refine-other-pos (point)))) (defun smerge-swap () ;; FIXME: Extend for diff3 to allow swapping the middle end as well. -- 2.47.2 --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Sat Jun 21 00:02:09 2025 Received: (at 78806) by debbugs.gnu.org; 21 Jun 2025 04:02:09 +0000 Received: from localhost ([127.0.0.1]:59412 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uSpQb-0002jd-1p for submit@debbugs.gnu.org; Sat, 21 Jun 2025 00:02:08 -0400 Received: from mailscanner.iro.umontreal.ca ([132.204.25.50]:23824) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1uSpQV-0002hm-9G for 78806@debbugs.gnu.org; Sat, 21 Jun 2025 00:02:01 -0400 Received: from pmg1.iro.umontreal.ca (localhost.localdomain [127.0.0.1]) by pmg1.iro.umontreal.ca (Proxmox) with ESMTP id 3CFBD10013E; Sat, 21 Jun 2025 00:01:53 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=iro.umontreal.ca; s=mail; t=1750478511; bh=hCyfyQBwJcIjxqZgTaI568jj9lZi/c+gprcEbPf7evI=; h=From:To:Subject:In-Reply-To:References:Date:From; b=GTNUFUEzx783y1TGHAU6sD7hBtLFPMYDq1keIDdxKTjU+aGEvJfRrijFkCmJtZRJ2 l7L18wqW4RwAkYxIh128ygxHZRo1kxv35EYmC1HimmubMseVic1TNAdq74J0eQdAW3 lfGzAV9g4HXSX/je2zoSlVnZCef+93hh3E+VdsWPSEnPNL6SM+H48Unk7PHuzp5hVb jK60zz5dnKRh0MbBCIkAylJKkFlxoFhQl6FZVSt5CzxbXMkcLktSVQtvcGk5aXJ5Yk CH5/exRenkbRqdkIcTe+dc/Xhi4SGXVceCLwhGUZdJa8rf+ed5oXleVxGY/QJ1c8NK DOKOpCb6J9VtA== Received: from mail01.iro.umontreal.ca (unknown [172.31.2.1]) by pmg1.iro.umontreal.ca (Proxmox) with ESMTP id 08BA2100029; Sat, 21 Jun 2025 00:01:51 -0400 (EDT) Received: from pastel (unknown [104.247.225.139]) by mail01.iro.umontreal.ca (Postfix) with ESMTPSA id D3C4A120170; Sat, 21 Jun 2025 00:01:50 -0400 (EDT) From: Stefan Monnier To: 78806@debbugs.gnu.org Subject: Re: bug#78806: [PATCH] (smerge-refine-region): Add support for a virtual "other cursor" In-Reply-To: Message-ID: References: Date: Sat, 21 Jun 2025 00:01:50 -0400 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-SPAM-INFO: Spam detection results: 0 ALL_TRUSTED -1 Passed through trusted hosts only via SMTP AWL -0.326 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's domain DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from domain X-SPAM-LEVEL: X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 78806 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) --=-=-= Content-Type: text/plain > I'm thinking of adding a global `smerge-refine-shadow-cursor-mode` minor > mode to enable this feature and am thinking of enabling it by default. I made it a defcustom instead. See new patch below. AFAIC, it's ready for `master`. Stefan --=-=-= Content-Type: text/x-diff Content-Disposition: inline; filename=0001-smerge-refine-shadow-cursor-New-variable-and-face-bu.patch >From 8675aa101c9ed1a4add85b6445917b9c8a3b4697 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Mon, 16 Jun 2025 07:05:11 -0400 Subject: [PATCH] (smerge-refine-shadow-cursor): New variable and face (bug#78806) * lisp/vc/smerge-mode.el (smerge-refine-shadow-cursor): New variable and face. (smerge-refine-regions): Add `cursor-sensor-functions` property to the covering overlays. (smerge--refine-at-right-margin-p, smerge--refine-shadow-cursor): New functions. (smerge--refine-other-pos): New function, extracted from `smerge-refine-exchange-point`. (smerge-refine-exchange-point): Use it. (smerge--refine-highlight-change): Add thin highlighted space for insertion/deletion positions. * lisp/emacs-lisp/cursor-sensor.el (cursor-sensor--detect): Run functions for `moved` events. Demote errors. (cursor-sensor-mode): Adjust docstring accordingly. * doc/lispref/text.texi (Special Properties) : Mention the new `moved` direction. --- doc/lispref/text.texi | 11 ++- etc/NEWS | 13 +++- lisp/emacs-lisp/cursor-sensor.el | 126 ++++++++++++++++--------------- lisp/vc/smerge-mode.el | 122 +++++++++++++++++++++++------- 4 files changed, 181 insertions(+), 91 deletions(-) diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi index 75b2b1c3d60..a23033f2ab0 100644 --- a/doc/lispref/text.texi +++ b/doc/lispref/text.texi @@ -3986,10 +3986,13 @@ Special Properties This special property records a list of functions that react to cursor motion. Each function in the list is called, just before redisplay, with 3 arguments: the affected window, the previous known position of -the cursor, and one of the symbols @code{entered} or @code{left}, -depending on whether the cursor is entering the text that has this -property or leaving it. The functions are called only when the minor -mode @code{cursor-sensor-mode} is turned on. +the cursor, and a symbol indicating the direction of the movement. +The movement can be @code{entered} or @code{left}, depending on whether +the cursor is entering the text that has this property or leaving it, or +@code{moved} when the cursor moved within that text. +Other values for the direction should be ignored. +The functions are called only when the minor mode +@code{cursor-sensor-mode} is turned on. When the variable @code{cursor-sensor-inhibit} is non-@code{nil}, the @code{cursor-sensor-functions} property is ignored. diff --git a/etc/NEWS b/etc/NEWS index e3c5f56216f..eefada996e6 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -912,7 +912,8 @@ are discarded, which matches the behavior of physical terminals and other terminal emulators. Control sequences and escape sequences are still processed correctly regardless of margin position. -** Smerge +--- +** SMerge *** New command 'smerge-extend' extends a conflict over surrounding lines. @@ -921,6 +922,16 @@ When used inside a refined chunk, it jumps to the matching position in the "other" side of the refinement: if you're in the new text, it jumps to the corresponding position in the old text and vice versa. +*** New variable 'smerge-refine-shadow-cursor'. +A "shadow cursor" is now drawn when point is inside a refined diff or +refined conflict area, that corresponds to point's position in +the other side of the diff or conflict, i.e. the position to which +'smerge-refine-exchange-point' would jump. + +** Cursor-Sensor mode ++++ +*** New direction 'moved' used when the cursor moved within the active area. + ** Image Dired *** 'image-dired-show-all-from-dir' takes the same first argument as 'dired'. diff --git a/lisp/emacs-lisp/cursor-sensor.el b/lisp/emacs-lisp/cursor-sensor.el index b0ace3ce8f0..87bc73e06b0 100644 --- a/lisp/emacs-lisp/cursor-sensor.el +++ b/lisp/emacs-lisp/cursor-sensor.el @@ -141,63 +141,70 @@ cursor-intangible-mode ;;; Detect cursor movement. (defun cursor-sensor--detect (&optional window) - (with-current-buffer (window-buffer window) - (unless cursor-sensor-inhibit - (let* ((point (window-point window)) - ;; It's often desirable to make the - ;; cursor-sensor-functions property non-sticky on both - ;; ends, but that means get-pos-property might never - ;; see it. - (new (or (get-char-property point 'cursor-sensor-functions) - (unless (<= (point-min) point) - (get-char-property (1- point) - 'cursor-sensor-functions)))) - (old (window-parameter window 'cursor-sensor--last-state)) - (oldposmark (car old)) - (oldpos (or (if oldposmark (marker-position oldposmark)) - (point-min))) - (start (min oldpos point)) - (end (max oldpos point))) - (unless (or (null old) (eq (marker-buffer oldposmark) (current-buffer))) - ;; `window' does not display the same buffer any more! - (setcdr old nil)) - (if (or (and (null new) (null (cdr old))) - (and (eq new (cdr old)) - (eq (next-single-char-property-change - start 'cursor-sensor-functions nil end) - end))) - ;; Clearly nothing to do. - nil - ;; Maybe something to do. Let's see exactly what needs to run. - (let* ((missing-p - (lambda (f) - "Non-nil if F is missing somewhere between START and END." - (let ((pos start) - (missing nil)) - (while (< pos end) - (setq pos (next-single-char-property-change - pos 'cursor-sensor-functions - nil end)) - (unless (memq f (get-char-property - pos 'cursor-sensor-functions)) - (setq missing t))) - missing))) - (window (selected-window))) - (dolist (f (cdr old)) - (unless (and (memq f new) (not (funcall missing-p f))) - (funcall f window oldpos 'left))) - (dolist (f new) - (unless (and (memq f (cdr old)) (not (funcall missing-p f))) - (funcall f window oldpos 'entered))))) - - ;; Remember current state for next time. - ;; Re-read cursor-sensor-functions since the functions may have moved - ;; window-point! - (if old - (progn (move-marker (car old) point) - (setcdr old new)) - (set-window-parameter window 'cursor-sensor--last-state - (cons (copy-marker point) new))))))) + (with-demoted-errors "cursor-sensor--detect: %S" + (with-current-buffer (window-buffer window) + (unless cursor-sensor-inhibit + (let* ((point (window-point window)) + ;; It's often desirable to make the + ;; cursor-sensor-functions property non-sticky on both + ;; ends, but that means get-pos-property might never + ;; see it. + ;; FIXME: Should we combine properties from the various + ;; covering overlays? + (new (or (get-char-property point 'cursor-sensor-functions) + (unless (<= (point-min) point) + (get-char-property (1- point) + 'cursor-sensor-functions)))) + (old (window-parameter window 'cursor-sensor--last-state)) + (oldposmark (car old)) + (oldpos (or (if oldposmark (marker-position oldposmark)) + (point-min))) + (start (min oldpos point)) + (end (max oldpos point))) + (unless (or (null old) + (eq (marker-buffer oldposmark) (current-buffer))) + ;; `window' does not display the same buffer any more! + (setcdr old nil)) + (if (or (and (null new) (null (cdr old))) + ;; (and (eq new (cdr old)) + ;; (eq (next-single-char-property-change + ;; start 'cursor-sensor-functions nil end) + ;; end)) + ) + ;; Clearly nothing to do. + nil + ;; Maybe something to do. Let's see exactly what needs to run. + (let* ((missing-p + (lambda (f) + "Non-nil if F is missing somewhere between START and END." + (let ((pos start) + (missing nil)) + (while (< pos end) + (setq pos (next-single-char-property-change + pos 'cursor-sensor-functions + nil end)) + (unless (memq f (get-char-property + pos 'cursor-sensor-functions)) + (setq missing t))) + missing))) + (window (selected-window))) + (dolist (f (cdr old)) + (unless (and (memq f new) (not (funcall missing-p f))) + (funcall f window oldpos 'left))) + (dolist (f new) + (funcall f window oldpos + (if (or (not (memq f (cdr old))) (funcall missing-p f)) + 'entered + 'moved))))) + + ;; Remember current state for next time. + ;; Re-read cursor-sensor-functions since the functions may have moved + ;; window-point! + (if old + (progn (move-marker (car old) point) + (setcdr old new)) + (set-window-parameter window 'cursor-sensor--last-state + (cons (copy-marker point) new)))))))) ;;;###autoload (define-minor-mode cursor-sensor-mode @@ -205,8 +212,9 @@ cursor-sensor-mode This property should hold a list of functions which react to the motion of the cursor. They're called with three arguments (WINDOW OLDPOS DIR) where WINDOW is the affected window, OLDPOS is the last known position of -the cursor and DIR can be `entered' or `left' depending on whether the cursor -is entering the area covered by the text-property property or leaving it." +the cursor and DIR can be `entered', `left', or `moved' depending on whether +the cursor is entering the area covered by the text-property property, +leaving it, or just moving inside of it." :global nil (cond (cursor-sensor-mode diff --git a/lisp/vc/smerge-mode.el b/lisp/vc/smerge-mode.el index d1b27f6763b..73b1a61469d 100644 --- a/lisp/vc/smerge-mode.el +++ b/lisp/vc/smerge-mode.el @@ -1089,12 +1089,25 @@ smerge--refine-highlight-change ;; (list match-num1 match-num2 startline)) (overlay-put ol 'evaporate t) (dolist (x props) - (when (or (> end beg) - ;; Don't highlight the char we cover artificially. - (not (memq (car-safe x) '(face font-lock-face)))) - (overlay-put ol (car x) (cdr x)))) + (if (or (> end beg) + (not (memq (car-safe x) '(face font-lock-face)))) + (overlay-put ol (car x) (cdr x)) + ;; Don't highlight the char we cover artificially. + (overlay-put ol (if (= beg olbeg) 'before-string 'after-string) + (propertize + " " (car-safe x) (cdr-safe x) + 'display '(space :width 0.5))))) ol))))) +(defcustom smerge-refine-shadow-cursor t + "If non-nil, display a shadow cursor on the other side of smerge refined regions." + :type 'boolean + :version "31.1") + +(defface smerge-refine-shadow-cursor + '((t :box (:line-width (-2 . -2)))) + "Face used to highlight the shadow cursor.") + ;;;###autoload (defun smerge-refine-regions (beg1 end1 beg2 end2 props-c &optional preproc props-r props-a) "Show fine differences in the two regions BEG1..END1 and BEG2..END2. @@ -1124,7 +1137,11 @@ smerge-refine-regions (ol2 (make-overlay beg2 end2 nil ;; Make it shrink rather than spread when editing. 'front-advance nil)) - (common-props '((evaporate . t) (smerge--refine-region . t)))) + (common-props '((evaporate . t) (smerge--refine-region . t) + (cursor-sensor-functions + smerge--refine-shadow-cursor)))) + (when smerge-refine-shadow-cursor + (cursor-sensor-mode 1)) (dolist (prop (or props-a props-c)) (when (and (not (memq (car prop) '(face font-lock-face))) (member prop (or props-r props-c)) @@ -1215,6 +1232,55 @@ smerge-refine-regions (define-obsolete-function-alias 'smerge-refine-subst #'smerge-refine-regions "26.1") +(defun smerge--refine-at-right-margin-p (pos window) + ;; FIXME: `posn-at-point' seems to be costly/slow. + (when-let* ((posn (posn-at-point pos window)) + (xy (nth 2 posn)) + (x (car-safe xy)) + (_ (numberp x))) + (> (+ x (with-selected-window window (string-pixel-width " "))) + (car (window-text-pixel-size window))))) + +(defun smerge--refine-shadow-cursor (window _oldpos dir) + (let ((ol (window-parameter window 'smerge--refine-shadow-cursor))) + (if (not (and smerge-refine-shadow-cursor + (memq dir '(entered moved)))) + (if ol (delete-overlay ol)) + (with-current-buffer (window-buffer window) + (let* ((cursor (window-point window)) + (other-beg (ignore-errors (smerge--refine-other-pos cursor)))) + (if (not other-beg) + (if ol (delete-overlay ol)) + (let ((other-end (min (point-max) (1+ other-beg)))) + ;; If other-beg/end covers a "wide" char like TAB or LF, the + ;; resulting shadow cursor doesn't look like a cursor, so try + ;; and convert it to a before-string space. + (when (or (and (eq ?\n (char-after other-beg)) + (not (smerge--refine-at-right-margin-p + other-beg window))) + (and (eq ?\t (char-after other-beg)) + ;; FIXME: `posn-at-point' seems to be costly/slow. + (when-let* ((posn (posn-at-point other-beg window)) + (xy (nth 2 posn)) + (x (car-safe xy)) + (_ (numberp x))) + (< (1+ (% x tab-width)) tab-width)))) + (setq other-end other-beg)) + ;; FIXME: Doesn't obey `cursor-in-non-selected-windows'. + (if ol (move-overlay ol other-beg other-end) + (setq ol (make-overlay other-beg other-end nil t nil)) + (setf (window-parameter window 'smerge--refine-shadow-cursor) + ol) + (overlay-put ol 'window window) + (overlay-put ol 'face 'smerge-refine-shadow-cursor)) + ;; When the shadow cursor needs to be at EOB (or TAB or EOL), + ;; "draw" it as a pseudo space character. + (overlay-put ol 'before-string + (when (= other-beg other-end) + (eval-when-compile + (propertize + " " 'face 'smerge-refine-shadow-cursor))))))))))) + (defun smerge-refine (&optional part) "Highlight the words of the conflict that are different. For 3-way conflicts, highlights only two of the three parts. @@ -1265,56 +1331,58 @@ smerge-refine (unless smerge-use-changed-face '((smerge . refine) (font-lock-face . smerge-refined-added)))))) -(defun smerge-refine-exchange-point () - "Go to the matching position in the other chunk." - (interactive) +(defun smerge--refine-other-pos (pos) (let* ((covering-ol - (let ((ols (overlays-at (point)))) + (let ((ols (overlays-at pos))) (while (and ols (not (overlay-get (car ols) 'smerge--refine-region))) (pop ols)) (or (car ols) (user-error "Not inside a refined region")))) (ref-pos - (if (or (get-char-property (point) 'smerge--refine-other) - (get-char-property (1- (point)) 'smerge--refine-other)) - (point) + (if (or (get-char-property pos 'smerge--refine-other) + (get-char-property (1- pos) 'smerge--refine-other)) + pos (let ((next (next-single-char-property-change - (point) 'smerge--refine-other nil + pos 'smerge--refine-other nil (overlay-end covering-ol))) (prev (previous-single-char-property-change - (point) 'smerge--refine-other nil + pos 'smerge--refine-other nil (overlay-start covering-ol)))) (cond ((and (> prev (overlay-start covering-ol)) (or (>= next (overlay-end covering-ol)) - (> (- next (point)) (- (point) prev)))) + (> (- next pos) (- pos prev)))) prev) ((< next (overlay-end covering-ol)) next) (t (user-error "No \"other\" position info found")))))) (boundary (cond - ((< ref-pos (point)) + ((< ref-pos pos) (let ((adjust (get-char-property (1- ref-pos) 'smerge--refine-adjust))) - (min (point) (+ ref-pos (or (cdr adjust) 0))))) - ((> ref-pos (point)) + (min pos (+ ref-pos (or (cdr adjust) 0))))) + ((> ref-pos pos) (let ((adjust (get-char-property ref-pos 'smerge--refine-adjust))) - (max (point) (- ref-pos (or (car adjust) 0))))) + (max pos (- ref-pos (or (car adjust) 0))))) (t ref-pos))) (other-forw (get-char-property ref-pos 'smerge--refine-other)) (other-back (get-char-property (1- ref-pos) 'smerge--refine-other)) (other (or other-forw other-back)) - (dist (- boundary (point)))) + (dist (- boundary pos))) (if (not (overlay-start other)) (user-error "The \"other\" position has vanished") - (goto-char - (- (if other-forw - (- (overlay-start other) - (or (car (overlay-get other 'smerge--refine-adjust)) 0)) - (+ (overlay-end other) - (or (cdr (overlay-get other 'smerge--refine-adjust)) 0))) - dist))))) + (- (if other-forw + (- (overlay-start other) + (or (car (overlay-get other 'smerge--refine-adjust)) 0)) + (+ (overlay-end other) + (or (cdr (overlay-get other 'smerge--refine-adjust)) 0))) + dist)))) + +(defun smerge-refine-exchange-point () + "Go to the matching position in the other chunk." + (interactive) + (goto-char (smerge--refine-other-pos (point)))) (defun smerge-swap () ;; FIXME: Extend for diff3 to allow swapping the middle end as well. -- 2.39.5 --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Sat Jun 21 03:35:15 2025 Received: (at 78806) by debbugs.gnu.org; 21 Jun 2025 07:35:15 +0000 Received: from localhost ([127.0.0.1]:60837 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uSskt-0007rA-8e for submit@debbugs.gnu.org; Sat, 21 Jun 2025 03:35:15 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:56942) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1uSskq-0007nk-4X for 78806@debbugs.gnu.org; Sat, 21 Jun 2025 03:35:12 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uSskk-0004Rm-FG; Sat, 21 Jun 2025 03:35:06 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date: mime-version; bh=oyXScmTh9eA4K1GPBZCqqNfoKex/N5/6zOZX9rHIblY=; b=OH/jSJKTM6ta uYIuiihp9PL4Dl7qxXqY8xZ+OelX96SdyoW7QuBPK4PRooycyNup3mknKuwxoAqcBME+9I8bkedI+ jjx6EgCQbajfK1vj2Td8kr0rb1UWam0GYf2O/9/KC7e5UOYPfKdL+3AN/VVvgIKWi9M35UgWXX7Fx Hg4gMoOHqAOuTZxysSG6zOqwzIx2oI7LuDzszhok3m+9yqM3iwYXl08tNS+60+nu2eeQYmyvyMV2A 0NuPlhamBkMXFePqifVPSZ4uIA+2y4jF79luS0lS5gNECitOmiAWb4L9wezjZQcv015oNUXvCTprn IvEDt0n5LOM0RlIzypjITg==; Date: Sat, 21 Jun 2025 10:35:03 +0300 Message-Id: <86ikkpk0ns.fsf@gnu.org> From: Eli Zaretskii To: Stefan Monnier In-Reply-To: (bug-gnu-emacs@gnu.org) Subject: Re: bug#78806: [PATCH] (smerge-refine-region): Add support for a virtual "other cursor" References: X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 78806 Cc: 78806@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > Date: Sat, 21 Jun 2025 00:01:50 -0400 > From: Stefan Monnier via "Bug reports for GNU Emacs, > the Swiss army knife of text editors" > > > I'm thinking of adding a global `smerge-refine-shadow-cursor-mode` minor > > mode to enable this feature and am thinking of enabling it by default. > > I made it a defcustom instead. See new patch below. > AFAIC, it's ready for `master`. Thanks. I'm only a seasoned user of SMerge, but this description confuses me so completely that I have no idea what will this "shadow cursor" do for me: +A "shadow cursor" is now drawn when point is inside a refined diff or +refined conflict area, that corresponds to point's position in +the other side of the diff or conflict, i.e. the position to which +'smerge-refine-exchange-point' would jump. I guess this alludes to too much of other terms or features whose names don't tell enough: "the other side of the diff"? "where smerge-refine-exchange-point would jump"? Is it possible to provide something less cryptic? And there's no other documentation of the feature, not even in the doc strings of the new option and face you added, which just mention "the shadow cursor" without any other information. Is it perhaps high time to have this mode better documented in the user manual? > + (with-demoted-errors "cursor-sensor--detect: %S" I'd appreciate a comment explaining why we demote these errors. > +(defcustom smerge-refine-shadow-cursor t > + "If non-nil, display a shadow cursor on the other side of smerge refined regions." Please say more about the shadow cursor. How about saying what it looks like, not only where? > +(defface smerge-refine-shadow-cursor > + '((t :box (:line-width (-2 . -2)))) > + "Face used to highlight the shadow cursor.") Why "highlight"? Isn't this the cursor itself, not its highlight? From debbugs-submit-bounces@debbugs.gnu.org Sat Jun 21 11:31:51 2025 Received: (at 78806) by debbugs.gnu.org; 21 Jun 2025 15:31:51 +0000 Received: from localhost ([127.0.0.1]:42106 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uT0C6-0001l9-W7 for submit@debbugs.gnu.org; Sat, 21 Jun 2025 11:31:51 -0400 Received: from mailscanner.iro.umontreal.ca ([132.204.25.50]:58210) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1uT0C3-0001kN-4b for 78806@debbugs.gnu.org; Sat, 21 Jun 2025 11:31:48 -0400 Received: from pmg2.iro.umontreal.ca (localhost.localdomain [127.0.0.1]) by pmg2.iro.umontreal.ca (Proxmox) with ESMTP id A159B803BD; Sat, 21 Jun 2025 11:31:41 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=iro.umontreal.ca; s=mail; t=1750519900; bh=R5mnnL2Z6rYp8s40xftNuUhbkgzEhSAJ98IUDYvY3x0=; h=From:To:Cc:Subject:In-Reply-To:References:Date:From; b=g2M7jEHB319PNbTq4Ga3MPbXHfA75oW7Ki39RfvyFD3X7wDJW/MQKstn+BufAuMyp B39yDQYQugfFM7JWS5m6GSUjglvgSp2G8yPLm5/DY00b+13YcnduPIeIAAUb46zEf8 4n7lHTnrRJLMhEGdsnAjgd7ENmtZqugIfx69ur7kDIyCipMfjQqOBTx2n0WoOiVqIf p/9SVoaxmDlv+gaDWkVHlvhaCqbAqsXKfKvogbnJ8oF2PPteVgt7WLFguxkqkThc+w hBdQvdpBE2vxLQ/wgrNIy+vcfYszFQ1qFuzpowhuDBXHZ2GiRLNo4se3lfPu1XrzlS N8RYDmvUTiHVw== Received: from mail01.iro.umontreal.ca (unknown [172.31.2.1]) by pmg2.iro.umontreal.ca (Proxmox) with ESMTP id 18A318089D; Sat, 21 Jun 2025 11:31:40 -0400 (EDT) Received: from pastel (unknown [104.247.225.139]) by mail01.iro.umontreal.ca (Postfix) with ESMTPSA id E2B1E120B56; Sat, 21 Jun 2025 11:31:39 -0400 (EDT) From: Stefan Monnier To: Eli Zaretskii Subject: Re: bug#78806: [PATCH] (smerge-refine-region): Add support for a virtual "other cursor" In-Reply-To: <86ikkpk0ns.fsf@gnu.org> Message-ID: References: <86ikkpk0ns.fsf@gnu.org> Date: Sat, 21 Jun 2025 11:31:38 -0400 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: text/plain X-SPAM-INFO: Spam detection results: 0 ALL_TRUSTED -1 Passed through trusted hosts only via SMTP AWL -0.333 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's domain DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from domain X-SPAM-LEVEL: X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 78806 Cc: 78806@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > Thanks. I'm only a seasoned user of SMerge, but this description > confuses me so completely that I have no idea what will this "shadow > cursor" do for me: > > +A "shadow cursor" is now drawn when point is inside a refined diff or > +refined conflict area, that corresponds to point's position in > +the other side of the diff or conflict, i.e. the position to which > +'smerge-refine-exchange-point' would jump. I don't really know how to describe it. I think it becomes obvious when you see it. Here's another attempt: `smerge-refine` does a "fine diff" between two regions of text, which works by finding similarities between those two regions. So when point is in one of the those two regions, the "shadow cursor" is placed in the other region, at the location that the fine-diff considered as "most similar". > I guess this alludes to too much of other terms or features whose > names don't tell enough: "the other side of the diff"? "where > smerge-refine-exchange-point would jump"? Is it possible to provide > something less cryptic? Does the above clarify? If so, do you have any suggestion how to turn it into a form better adapted to NEWS and docstrings? >> + (with-demoted-errors "cursor-sensor--detect: %S" > I'd appreciate a comment explaining why we demote these errors. OK. >> +(defcustom smerge-refine-shadow-cursor t >> + "If non-nil, display a shadow cursor on the other side of smerge refined regions." > Please say more about the shadow cursor. How about saying what it > looks like, not only where? I'll add a reference to the face. >> +(defface smerge-refine-shadow-cursor >> + '((t :box (:line-width (-2 . -2)))) >> + "Face used to highlight the shadow cursor.") > Why "highlight"? Isn't this the cursor itself, not its highlight? I don't know how to say it clearly: this is the face used to highlight a character where the highlighting itself is "the cursor". Maybe: "Face placed on a character to highlight it as the shadow cursor." ? Stefan From debbugs-submit-bounces@debbugs.gnu.org Sat Jun 21 15:22:03 2025 Received: (at 78806) by debbugs.gnu.org; 21 Jun 2025 19:22:04 +0000 Received: from localhost ([127.0.0.1]:43291 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uT3mq-0003CO-Fs for submit@debbugs.gnu.org; Sat, 21 Jun 2025 15:22:03 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:35524) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1uT3ml-0003B9-Nj for 78806@debbugs.gnu.org; Sat, 21 Jun 2025 15:21:58 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uT3mg-00086h-8Q; Sat, 21 Jun 2025 15:21:50 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date: mime-version; bh=PSclW3NjlaffouCzcaZkyhoXYFqLr4Jruz8RIiO0R6I=; b=LEUVj2hdahsT L8IxZMJN2moLiTQdDVj0vtK93MJDcYBKX0x+XlrkWI0fPch9fjK6tWnmB1W7VWsawC9VpIxO+vmUr 2qOhyegdRhkmRb7tYTmWztnyF60VQ+A7Erav9dVJyR49CM1whVSaENSKrmY6vhbN5+gejM3xeXWDK kvW9fA171z0JvdEXUejVv4nEapSAzLJyq7l+NepDwozWqg0EEl6iwp9OEwccgzf3BxJQ5bmgNgBKG fzEJnzrvjvH6AdR98gOTXappXA3XhBKC8TjJxSD3OLBBgifFMfBLiUalxI2UqwbUtszMs0cZ13TeX Im1OTgH0NkWciFhO8epLmQ==; Date: Sat, 21 Jun 2025 22:21:47 +0300 Message-Id: <86h608j3xw.fsf@gnu.org> From: Eli Zaretskii To: Stefan Monnier In-Reply-To: (message from Stefan Monnier on Sat, 21 Jun 2025 11:31:38 -0400) Subject: Re: bug#78806: [PATCH] (smerge-refine-region): Add support for a virtual "other cursor" References: <86ikkpk0ns.fsf@gnu.org> X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 78806 Cc: 78806@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Stefan Monnier > Cc: 78806@debbugs.gnu.org > Date: Sat, 21 Jun 2025 11:31:38 -0400 > > > Thanks. I'm only a seasoned user of SMerge, but this description > > confuses me so completely that I have no idea what will this "shadow > > cursor" do for me: > > > > +A "shadow cursor" is now drawn when point is inside a refined diff or > > +refined conflict area, that corresponds to point's position in > > +the other side of the diff or conflict, i.e. the position to which > > +'smerge-refine-exchange-point' would jump. > > I don't really know how to describe it. I think it becomes obvious when > you see it. Here's another attempt: > > `smerge-refine` does a "fine diff" between two regions of text, which > works by finding similarities between those two regions. So when point > is in one of the those two regions, the "shadow cursor" is placed in the > other region, at the location that the fine-diff considered as "most > similar". How about When 'smerge-refine' shows the conflict diffs at word granularity, a "shadow cursor" is now displayed in the "lower" version when point is in the "upper" version, and vice versa. The "shadow cursor" is just the character corresponding to the position where 'smerge-refine-exchange-point' would jump, shown in a new distinct face 'smerge-refine-shadow-cursor', by default a box face. > >> +(defface smerge-refine-shadow-cursor > >> + '((t :box (:line-width (-2 . -2)))) > >> + "Face used to highlight the shadow cursor.") > > Why "highlight"? Isn't this the cursor itself, not its highlight? > > I don't know how to say it clearly: this is the face used to highlight > a character where the highlighting itself is "the cursor". Maybe: > > "Face placed on a character to highlight it as the shadow cursor." Yes, much better. But I think the doc string should also include a reference to 'smerge-refine-shadow-cursor'. Thanks. From debbugs-submit-bounces@debbugs.gnu.org Mon Jun 23 00:04:27 2025 Received: (at 78806-done) by debbugs.gnu.org; 23 Jun 2025 04:04:27 +0000 Received: from localhost ([127.0.0.1]:52005 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uTYPz-00088g-DI for submit@debbugs.gnu.org; Mon, 23 Jun 2025 00:04:27 -0400 Received: from mailscanner.iro.umontreal.ca ([132.204.25.50]:38695) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1uTYPw-00088K-Rp for 78806-done@debbugs.gnu.org; Mon, 23 Jun 2025 00:04:25 -0400 Received: from pmg3.iro.umontreal.ca (localhost [127.0.0.1]) by pmg3.iro.umontreal.ca (Proxmox) with ESMTP id C5BB5441290; Mon, 23 Jun 2025 00:04:18 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=iro.umontreal.ca; s=mail; t=1750651453; bh=x+GYGWU4vitBashKMo+qqe9N/EVjlS0MfdsONvf1XUI=; h=From:To:Cc:Subject:In-Reply-To:References:Date:From; b=D+z8WngZA5d93rGByRXJF8VJjC8WJ7LGVcP9KmYSbOakXjJ8HW+txvxe6RMadUb4N eG72b7d9gDT/onEiA0ZwNADc1Dgl4unHZ+MtyOCTyV/lo/oOmP/nhPLLBHMRkiEy8K l7b9vZvD+BEEXXm2BXdVl/9TrT00Zq2GOVp8CtilRpqsWbGVqMkprZOgeo9zdo0rWs bBMqb6NR7T0TWyd9wq6EST+yJesLjzX93HcJh3oMI+iDc1LXuJydtKCFxq3+qZ0Pkk cGqkJKx5/l80qEqP7lupoITUZ9PNgBTIwtMM+4CpFBYVktm6E49IKgWil2SuFKu9gZ yl36PNCE7eymw== Received: from mail01.iro.umontreal.ca (unknown [172.31.2.1]) by pmg3.iro.umontreal.ca (Proxmox) with ESMTP id B30C14411F0; Mon, 23 Jun 2025 00:04:13 -0400 (EDT) Received: from pastel (unknown [104.247.225.139]) by mail01.iro.umontreal.ca (Postfix) with ESMTPSA id 87674120798; Mon, 23 Jun 2025 00:04:13 -0400 (EDT) From: Stefan Monnier To: Eli Zaretskii Subject: Re: bug#78806: [PATCH] (smerge-refine-region): Add support for a virtual "other cursor" In-Reply-To: <86h608j3xw.fsf@gnu.org> Message-ID: References: <86ikkpk0ns.fsf@gnu.org> <86h608j3xw.fsf@gnu.org> Date: Mon, 23 Jun 2025 00:04:06 -0400 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: text/plain X-SPAM-INFO: Spam detection results: 0 ALL_TRUSTED -1 Passed through trusted hosts only via SMTP AWL -0.311 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's domain DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from domain X-SPAM-LEVEL: X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 78806-done Cc: 78806-done@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) Thanks, pushed with the suggested improvements, Stefan From debbugs-submit-bounces@debbugs.gnu.org Thu Jun 26 11:09:07 2025 Received: (at 78806) by debbugs.gnu.org; 26 Jun 2025 15:09:07 +0000 Received: from localhost ([127.0.0.1]:52144 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uUoDq-0005Jr-CF for submit@debbugs.gnu.org; Thu, 26 Jun 2025 11:09:07 -0400 Received: from mail.hostpark.net ([212.243.197.30]:48136) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1uUoDn-0005JF-4y for 78806@debbugs.gnu.org; Thu, 26 Jun 2025 11:09:04 -0400 Received: from localhost (localhost [127.0.0.1]) by mail.hostpark.net (Postfix) with ESMTP id ACCA2162D1; Thu, 26 Jun 2025 17:09:00 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=bernoul.li; h= content-type:content-type:mime-version:user-agent:message-id :date:date:references:in-reply-to:subject:subject:from:from; s= sel2011a; t=1750950540; bh=+8JIbSbbt8RZVkKWFkr0wPxdJUFzTj0y5gNkt 5ub0yI=; b=zSV3bOxbY+fPeIvDlYKiALZuWBgfPzPhaCmfpl4VHtnteUIXx+ul1 w8wZv83GdvPhqcYXKR1bTeqjCXZqLR1ou5fkx5uCOaEyjPFuhDxsZUM9kHgrw/aw Ml26/sgRqnJdB07GqLteKCp8THo0y5FWTPP7PPTmD27toTeVR52zLk= X-Virus-Scanned: by Hostpark/NetZone Mailprotection at hostpark.net Received: from mail.hostpark.net ([127.0.0.1]) by localhost (mail1.hostpark.net [127.0.0.1]) (amavis, port 10224) with ESMTP id a3XhiG9zYOqm; Thu, 26 Jun 2025 17:09:00 +0200 (CEST) Received: from customer (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (prime256v1) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mail.hostpark.net (Postfix) with ESMTPSA id 2932B1626A; Thu, 26 Jun 2025 17:08:59 +0200 (CEST) From: Jonas Bernoulli To: Stefan Monnier Subject: Re: bug#78806: [PATCH] (smerge-refine-region): Add support for a virtual "other cursor" In-Reply-To: References: Date: Thu, 26 Jun 2025 17:08:59 +0200 Message-ID: <87v7oi8rqs.fsf@bernoul.li> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: text/plain X-Spam-Score: -0.7 (/) X-Debbugs-Envelope-To: 78806 Cc: 78806@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.7 (-) Stefan Monnier writes: > The patch below adds support to `smerge-refine-region` for keeping > a kind of "virtual cursor" that shows the "corresponding" point in the > other part of a diff. Neat! Unfortunately the before-string added by smerge--refine-highlight-change result in misalignment, making it hard to be confident about the correctness of whitespace changes when looking at diffs only. Using (space :width 0.1) instead of (space :width 0.5) helps a lot, but if there are multiple instances on a single line, their widths add up. If there are multiple such items on a line, it also gets quite noisy, to the point of being distracting. Maybe I'll get used to that after a while; the feature is too new to tell. I am undecided whether this is needed at all, and suspect it is not just a matter of familiarity but also personal taste. I.e., the width should probably be customizable and zero should be one of the valid values. I have experimented with using an "incomplete box" instead of a before- string. - (overlay-put ol (if (= beg olbeg) 'before-string 'after-string) - (propertize - " " (car-safe x) (cdr-safe x) - 'display '(space :width 0.5))))) + (overlay-put ol 'face '(:box (:line-width (-2 . 0) :style nil))))) Unfortunately that draws two lines. It would be nice if the widths of the four sides of a box could be set individually, not just in pairs. (Adding two new overlay properties before-line and after-line, which handle negative values the same as the :line-width of :box, might also be an option.) I am bringing such lines up in the context of this new feature, but this is not the first time I wished something like this existed. Cheers, Jonas From debbugs-submit-bounces@debbugs.gnu.org Thu Jun 26 12:53:18 2025 Received: (at 78806) by debbugs.gnu.org; 26 Jun 2025 16:53:18 +0000 Received: from localhost ([127.0.0.1]:53262 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1uUpqf-0008HK-Sd for submit@debbugs.gnu.org; Thu, 26 Jun 2025 12:53:18 -0400 Received: from mailscanner.iro.umontreal.ca ([132.204.25.50]:28990) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1uUpqc-0008GO-BA for 78806@debbugs.gnu.org; Thu, 26 Jun 2025 12:53:15 -0400 Received: from pmg1.iro.umontreal.ca (localhost.localdomain [127.0.0.1]) by pmg1.iro.umontreal.ca (Proxmox) with ESMTP id A1DFF10013E; Thu, 26 Jun 2025 12:53:08 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=iro.umontreal.ca; s=mail; t=1750956787; bh=WT7rVZZnGpjD52cLN8s/9qul8Ocd7hhp/UqvTiTU8EI=; h=From:To:Cc:Subject:In-Reply-To:References:Date:From; b=g4N0Py00msaTUHq/QG6pZ1ioIwsGDwoBVT6iug9coqPdiizlgyHLE4rRvl6IonCIx UPoM5rURxb0TfbNY4lX1Cl9n3igUPDwu6eQVft4q3fzVkvJaa9KFCGeju3/U8eRBX1 IgN5QAh9+e5wH3e+ukGB2YbMYkwwOCOBSjsG526JzgJXt++LtjTOac20a1HfaTs/DZ 2BQgNI00nYJdln1yoKMuyKu/iw2WF1AlUQHq++wTQcnGxvx8MMbdi9ktmLphsE9n6u aEUtA9m173ETQFGLJTv40diNyI5HH6t6amNLeR31uUv6cOnnUG/8Ryg3oP59ghZc9n OjfopkPABXYBw== Received: from mail01.iro.umontreal.ca (unknown [172.31.2.1]) by pmg1.iro.umontreal.ca (Proxmox) with ESMTP id CB03310002E; Thu, 26 Jun 2025 12:53:07 -0400 (EDT) Received: from lechazo (lechon.iro.umontreal.ca [132.204.27.242]) by mail01.iro.umontreal.ca (Postfix) with ESMTPSA id B935C1202E2; Thu, 26 Jun 2025 12:53:07 -0400 (EDT) From: Stefan Monnier To: Jonas Bernoulli Subject: Re: bug#78806: [PATCH] (smerge-refine-region): Add support for a virtual "other cursor" In-Reply-To: <87v7oi8rqs.fsf@bernoul.li> Message-ID: References: <87v7oi8rqs.fsf@bernoul.li> Date: Thu, 26 Jun 2025 12:52:59 -0400 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-SPAM-INFO: Spam detection results: 0 ALL_TRUSTED -1 Passed through trusted hosts only via SMTP AWL 0.094 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's domain DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from domain X-SPAM-LEVEL: X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 78806 Cc: 78806@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > Unfortunately the before-string added by smerge--refine-highlight-change > result in misalignment, making it hard to be confident about the > correctness of whitespace changes when looking at diffs only. Oh, my, I hadn't noticed that I included this part of my local hacks. It's not directly related to the shadow-cursor (well, except that I coded it at the same time some months ago). The misalignment makes it definitely not acceptable (at least for the default config). I've now commented out that code. > Using (space :width 0.1) instead of (space :width 0.5) helps a lot, but It's still jarring. > I am undecided whether this is needed at all, It's not. I personally want(ed?) some way to highlight the "insertion" place, and that is the state of that current experiment. It was not meant for `master`. > I have experimented with using an "incomplete box" instead of a before- > string. > > - (overlay-put ol (if (=3D beg olbeg) 'before-string 'after-= string) > - (propertize > - " " (car-safe x) (cdr-safe x) > - 'display '(space :width 0.5))))) > + (overlay-put ol 'face '(:box (:line-width (-2 . 0) :style = nil))))) > > Unfortunately that draws two lines. Yup. =F0=9F=99=81 > It would be nice if the widths of the four sides of a box could be set > individually, not just in pairs. (Adding two new overlay properties > before-line and after-line, which handle negative values the same as > the :line-width of :box, might also be an option.) I am bringing such > lines up in the context of this new feature, but this is not the first > time I wished something like this existed. That would be a nice way to provide the feature I want without introducing the kind of misalignment that my current hack causes, yes. It should be fairly easy to extend `:line-width` to allow 4 distinct numbers. Stefan From unknown Thu Sep 18 22:55:31 2025 Received: (at fakecontrol) by fakecontrolmessage; To: internal_control@debbugs.gnu.org From: Debbugs Internal Request Subject: Internal Control Message-Id: bug archived. Date: Fri, 25 Jul 2025 11:24:05 +0000 User-Agent: Fakemail v42.6.9 # This is a fake control message. # # The action: # bug archived. thanks # This fakemail brought to you by your local debbugs # administrator