GNU bug report logs - #77722
[PATCH] Test and fix term-buffer-vertical-motion

Please note: This is a static page, with minimal formatting, updated once a day.
Click here to see this page with the latest information and nicer formatting.

Package: emacs; Reported by: Stephane Zermatten <szermatt@HIDDEN>; Keywords: patch; dated Fri, 11 Apr 2025 07:01:02 UTC; Maintainer for emacs is bug-gnu-emacs@HIDDEN.

Message received at submit <at> debbugs.gnu.org:


Received: (at submit) by debbugs.gnu.org; 11 Apr 2025 07:00:29 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Fri Apr 11 03:00:29 2025
Received: from localhost ([127.0.0.1]:48364 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1u38NI-0003KR-3Z
	for submit <at> debbugs.gnu.org; Fri, 11 Apr 2025 03:00:29 -0400
Received: from lists.gnu.org ([2001:470:142::17]:36150)
 by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.84_2) (envelope-from <szermatt@HIDDEN>)
 id 1u2vsT-0002FF-7k
 for submit <at> debbugs.gnu.org; Thu, 10 Apr 2025 13:39:50 -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 <szermatt@HIDDEN>)
 id 1u2vsF-00058P-M2
 for bug-gnu-emacs@HIDDEN; Thu, 10 Apr 2025 13:39:36 -0400
Received: from mail-wm1-x32f.google.com ([2a00:1450:4864:20::32f])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128)
 (Exim 4.90_1) (envelope-from <szermatt@HIDDEN>)
 id 1u2vsC-0006Sl-Lg
 for bug-gnu-emacs@HIDDEN; Thu, 10 Apr 2025 13:39:35 -0400
Received: by mail-wm1-x32f.google.com with SMTP id
 5b1f17b1804b1-43cf05f0c3eso8534015e9.0
 for <bug-gnu-emacs@HIDDEN>; Thu, 10 Apr 2025 10:39:30 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=gmail.com; s=20230601; t=1744306769; x=1744911569; darn=gnu.org;
 h=mime-version:message-id:date:subject:to:from:from:to:cc:subject
 :date:message-id:reply-to;
 bh=Uy280Kv619JyNomjZb0AMww8NXByGprG+ZdPUvKXCx4=;
 b=YEyIInlbAN5Um6+KhI+Wf9VLTP2gD0c+K7gbF+d03Cxvpw9dA29XvFCDppM+8xbkE0
 PQW2sbkjGiMfix+yoEHOpTBMQ2g5UdJ69sTBdNs0JEjonKrz23orl2sTQ8eb4NEDWLD1
 wBy8LJ8TnJH2Alifi9zHxui9CkdxkUxwoauzpHZJP53UoGAAyng+PT89tDRoSAzoFmSs
 gLhCxw8sl+Yoice9tsSyPGKzXTU5+aXjnFMPOar2pKs8GqXOz6zROW57REsdrYt6YZ2w
 TyMuFi+ICYwhV4jv40QceddMxJvHLn9FR0RBjJOCfRle01fBb8Lf7mlUOLDixemkQjZK
 9Mxg==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=1e100.net; s=20230601; t=1744306769; x=1744911569;
 h=mime-version:message-id:date:subject:to:from:x-gm-message-state
 :from:to:cc:subject:date:message-id:reply-to;
 bh=Uy280Kv619JyNomjZb0AMww8NXByGprG+ZdPUvKXCx4=;
 b=ORmAulKKaxpdEZK8lgN8APvemHz6HloeL+uXz0AZ/kWH9IEsws+Sv6sLbFof2VbXMp
 8HUQoMsxCk44Ke76DGSVvMRf8syaZfikitYU9ZJ/3As3+YgXhaCRTbSObGd9qEIZZ6PT
 I+AJX3ZojQKu8lyN7FtXD/iNdAcWmYV5CgW67Wmay9nUEAU8Gbl8yvWhTfTgWrsNB8Fr
 /mBXNG7FFTsLjEB9+MFl08gwVSY9qDFdYnDUpiv7+jWOnTMflcVA4/tCS7/RJ55JLblJ
 /joyNZ0M3CHvBPHNJJ2PrnYCvfGPG4ADox/EuV7unvRrlW+67cLPqGwVSLzfxJ/jwF7S
 2t2A==
X-Gm-Message-State: AOJu0YwL1FfyJ/0kUetUzhIOLGQoZactF92xSaB1TMvXQd4tOrRGkBVZ
 W3qc0+2JieopzUCrZIeHyRQ6sWc/U6Ku9rrRS49Qait5gRD//Ky57diNOSwyT0VmtQ==
X-Gm-Gg: ASbGncuv9oTquuFBT/TZcreTrfgVIFuBCS1t3bkzJkjWWtl7Q+9xuY5EgELC+RzZyAF
 GG0ukCMVouRL47jc2acV5RJXl6HSoTEeQc10GewmfSQ7FGc+0jNssPLm60RovGCZczbfmRQG2Pr
 B7Tiz1EMVXyPpF2gCyR1QK5JagJS6tOyorjP3NhgCiWaE2Hkvfcui4pcqFB1LrvBTXua30S4nSm
 Q4T9nUIzIfLE7B/1WIPUE82A+eSBNNVt3G8klrCUJDZmB949c2v7V7i0jO0KN37e99+/qLwgFCw
 8ML2Tm3IFWAKaBONzvIBGGfra3B3Pd+p69050YdW43ip+QyMaICGEc4j9jXUhaGQoupXHg==
X-Google-Smtp-Source: AGHT+IFVnqDXtx1gJ0Q4mUxM1n+KNk1iWymJfCVJ8HZ6rCH6GJrRLblIhzf5aWL9T/c5Z++qGozm+w==
X-Received: by 2002:a05:600c:1550:b0:43d:1c3:cb2e with SMTP id
 5b1f17b1804b1-43f2ff0497cmr32104135e9.17.1744306768878; 
 Thu, 10 Apr 2025 10:39:28 -0700 (PDT)
Received: from boomer.zia ([62.74.10.127]) by smtp.gmail.com with ESMTPSA id
 ffacd0b85a97d-39d89378daasm5470375f8f.38.2025.04.10.10.39.27
 for <bug-gnu-emacs@HIDDEN>
 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
 Thu, 10 Apr 2025 10:39:28 -0700 (PDT)
From: Stephane Zermatten <szermatt@HIDDEN>
To: bug-gnu-emacs@HIDDEN
Subject: [PATCH] Test and fix term-buffer-vertical-motion
Date: Thu, 10 Apr 2025 20:39:26 +0300
Message-ID: <m2cydjq5kh.fsf@HIDDEN>
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="=-=-="
Received-SPF: pass client-ip=2a00:1450:4864:20::32f;
 envelope-from=szermatt@HIDDEN; helo=mail-wm1-x32f.google.com
X-Spam_score_int: -20
X-Spam_score: -2.1
X-Spam_bar: --
X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1,
 DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001,
 RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001,
 SPF_PASS=-0.001 autolearn=ham autolearn_force=no
X-Spam_action: no action
X-Spam-Score: 1.0 (+)
X-Debbugs-Envelope-To: submit
X-Mailman-Approved-At: Fri, 11 Apr 2025 03:00:25 -0400
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -0.0 (/)

--=-=-=
Content-Type: text/plain

Tags: patch


In my code, I'm using hidden terminal buffers created by term.el, then
periodically copy their content to another buffer. The term-mode buffer
is not normally shown on any window. https://github.com/szermatt/mistty

This doesn't work very well with the default
term-buffer-vertical-motion implementation. I get very strange
behavior from term in such a case that disappear if the term buffer is
shown on any window.

To reproduce one issue:

 - Open term.el, go to term-emulate-terminal and replace:
             (setf term-vertical-motion
               (if (eq (window-buffer) (current-buffer))
                   'vertical-motion
                 'term-buffer-vertical-motion))
   with:
              (setf term-vertical-motion 'term-buffer-vertical-motion)

   (It is cheating a bit but much easier than trying to control a term
   buffer that's not tied to a window.)

 - Start zsh with M-x term (because it supports right prompts)

 - Type RPS1='< right'

 - Now type any command, such as "echo ok" and press enter. The
   command disappear with no output. The output never leaves the
   current line.

 - Type RPS1='' to go back to normal

That's with term-suppress-hard-newline left to nil, the default value.

The problem, I've found, is that term-buffer-vertical-motion has
issues with some corner cases, one of which is line whose width
correspond exactly to the line width.

It's especially annoying given that vertical-motion with
term-suppress-hard-newline nil could very well just be replaced by
forward-line, and I'm doing just that as a workaround:

(cl-letf (((symbol-function 'term-buffer-vertical-motion)
          (lambda (count)
            (let ((start-point (point))
                  (res (forward-line count)))
              (setq res (- count res))
              (when (and (> count 0)
                         (= (point) (point-max))
                         (> (point) start-point)
                         (not (eq ?\n (char-before (point-max)))))
                (cl-decf res))
              res))))

In this patch, however, to be nicer in the case where
term-suppress-hard-newline is non-nil, I propose to test
term-buffer-vertical-motion and fix the issues.
term-buffer-vertical-motion should always behave like
vertical-motion (assuming no display shenanigans or characters with
different width), so I used that for comparison.

In GNU Emacs 30.1 (build 2, x86_64-apple-darwin23.6.0, NS appkit-2487.70
 Version 14.7.4 (Build 23H420)) of 2025-03-24 built on boomer.zia
Windowing system distributor 'Apple', version 10.3.2487
System Description:  macOS 14.7.4

Configured using:
 'configure --disable-dependency-tracking --disable-silent-rules
 --enable-locallisppath=/usr/local/share/emacs/site-lisp
 --infodir=/usr/local/Cellar/emacs-plus@30/30.1/share/info/emacs
 --prefix=/usr/local/Cellar/emacs-plus@30/30.1
 --with-native-compilation=aot --with-xml2 --with-gnutls
 --without-compress-install --without-dbus --without-imagemagick
 --with-modules --with-rsvg --with-webp --with-ns
 --disable-ns-self-contained 'CFLAGS=-O2 -DFD_SETSIZE=10000
 -DDARWIN_UNLIMITED_SELECT -I/usr/local/opt/sqlite/include
 -I/usr/local/opt/gcc/include -I/usr/local/opt/libgccjit/include'
 'LDFLAGS=-L/usr/local/opt/sqlite/lib -L/usr/local/lib/gcc/14
 -I/usr/local/opt/gcc/include -I/usr/local/opt/libgccjit/include''


--=-=-=
Content-Type: text/patch
Content-Disposition: attachment;
 filename=0001-Test-and-fix-term-buffer-vertical-motion.patch

From 807237230038126f52ae1ac5b5f5023c80d660df Mon Sep 17 00:00:00 2001
From: Stephane Zermatten <szermatt@HIDDEN>
Date: Wed, 2 Apr 2025 18:44:59 +0300
Subject: [PATCH] Test and fix term-buffer-vertical-motion

term-buffer-vertical-motion simulates vertical-motion when no window is
available. This change makes sure that the simulation is correct when
point is at the beginning or end of lines. The fix is relevant even when
term-suppress-hard-newline is nil.

* lisp/term.el
---
 lisp/term.el            |  81 ++++++-----
 test/lisp/term-tests.el | 303 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 348 insertions(+), 36 deletions(-)

diff --git a/lisp/term.el b/lisp/term.el
index 862103d88e6..1defafc3ad1 100644
--- a/lisp/term.el
+++ b/lisp/term.el
@@ -2858,42 +2858,51 @@ term-vertical-motion
 
 (defun term-buffer-vertical-motion (count)
   (cond ((= count 0)
-	 (move-to-column (* term-width (/ (current-column) term-width)))
-	 0)
-	((> count 0)
-	 (let ((H)
-	       (todo (+ count (/ (current-column) term-width))))
-	   (end-of-line)
-	   ;; The loop iterates over buffer lines;
-	   ;; H is the number of screen lines in the current line, i.e.
-	   ;; the ceiling of dividing the buffer line width by term-width.
-	   (while (and (<= (setq H (max (/ (+ (current-column) term-width -1)
-					   term-width)
-					1))
-			   todo)
-		       (not (eobp)))
-	     (setq todo (- todo H))
-	     (forward-char) ;; Move past the ?\n
-	     (end-of-line)) ;; and on to the end of the next line.
-	   (if (and (>= todo H) (> todo 0))
-	       (+ (- count todo) H -1) ;; Hit end of buffer.
-	     (move-to-column (* todo term-width))
-	     count)))
-	(t ;; (< count 0) ;; Similar algorithm, but for upward motion.
-	 (let ((H)
-	       (todo (- count)))
-	   (while (and (<= (setq H (max (/ (+ (current-column) term-width -1)
-					   term-width)
-					1))
-			   todo)
-		       (progn (beginning-of-line)
-			      (not (bobp))))
-	     (setq todo (- todo H))
-	     (backward-char)) ;; Move to end of previous line.
-	   (if (and (>= todo H) (> todo 0))
-	       (+ count todo (- 1 H)) ;; Hit beginning of buffer.
-	     (move-to-column (* (- H todo 1) term-width))
-	     count)))))
+         (let ((col (current-column)))
+           (when (and (eq ?\n (char-after (point))) (> col 0))
+             (decf col))
+           (move-to-column (* term-width (/ col term-width))))
+         0)
+        ((> count 0)
+         (let ((H)
+               (todo (if (eolp)
+                         count
+                       (+ count (/ (current-column) term-width)))))
+           (end-of-line)
+           ;; The loop iterates over buffer lines;
+           ;; H is the number of screen lines in the current line, i.e.
+           ;; the ceiling of dividing the buffer line width by term-width.
+           (while (and (<= (setq H (max (/ (+ (current-column) term-width -1)
+                                           term-width)
+                                        1))
+                           todo)
+                       (not (eobp)))
+             (setq todo (- todo H))
+             (forward-char) ;; Move past the ?\n
+             (end-of-line)) ;; and on to the end of the next line.
+           (if (and (>= todo H) (> todo 0))
+               (+ (- count todo) H -1) ;; Hit end of buffer.
+             (move-to-column (* todo term-width))
+             count)))
+        (t ;; (< count 0) ;; Similar algorithm, but for upward motion.
+         (let ((H)
+               (todo (- count)))
+           (while (and (<= (setq H (max (/ (+ (current-column)
+                                              term-width
+                                              (if (eolp) -1 0))
+                                           term-width)
+                                        1))
+                           todo)
+                       (progn
+                         (beginning-of-line)
+
+                         (not (bobp))))
+             (setq todo (- todo H))
+             (backward-char)) ;; Move to end of previous line.
+           (if (and (>= todo H) (> todo 0))
+               (+ count todo (- 1 H)) ;; Hit beginning of buffer.
+             (move-to-column (* (- H todo 1) term-width))
+             count)))))
 
 ;; The term-start-line-column variable is used as a cache.
 (defun term-start-line-column ()
diff --git a/test/lisp/term-tests.el b/test/lisp/term-tests.el
index 5ef8c1174df..bc40b3acab0 100644
--- a/test/lisp/term-tests.el
+++ b/test/lisp/term-tests.el
@@ -102,6 +102,46 @@ term-test-screen-from-input
       (if return-var (buffer-local-value return-var (current-buffer))
         (buffer-substring (point-min) (point-max))))))
 
+(defun term-test-vertical-motion (count text start-point)
+  "Run `term-buffer-vertical-motion' COUNT and return the result.
+
+This function sets up a buffer with TEXT as content, move the point to
+START-POINT, then call `term-buffer-vertical-motion' and return a CONS
+containing its return value and the final point.
+
+`term-width' must be set for`term-buffer-vertical-motion' to work.
+
+Additionally, if `term-width' correspond to `window-max-chars-per-line',
+which under batch test is 79, this function compares the result with
+`vertical-motion' and reports any inconsistencies as a special return
+value meant to fail the tests. This is basically a 3-way test.
+
+The result from `vertical-motion' is always the correct one, since
+`term-buffer-vertical-motion' simulates it."
+  (ert-with-test-buffer ()
+    (insert text)
+    (let ((actual (save-excursion
+                    (goto-char start-point)
+                    (cons (term-buffer-vertical-motion count)
+                          (point)))))
+      (if (= (window-max-chars-per-line) term-width)
+          ;; Since term-width allows it, compare with vertical-motion,
+          ;; which term-buffer-vertical-motion simulates. If the two
+          ;; report inconsistent result, the returned value cause the
+          ;; test to fail.
+          (let ((goal (if (equal (window-max-chars-per-line)
+                                 term-width)
+                          (save-excursion
+                            (goto-char start-point)
+                            (cons (vertical-motion count)
+                                  (point))))))
+            (if (equal actual goal)
+                actual
+            `(inconsistent-result actual ,actual goal ,goal)))
+
+        ;; The result from term-buffer-vertical-motion
+        actual))))
+
 (ert-deftest term-simple-lines ()
   (skip-when (memq system-type '(windows-nt ms-dos)))
   (let ((str "\
@@ -418,6 +458,269 @@ term-undecodable-input
                    (term-test-screen-from-input
                     40 1 bytes)))))
 
+(ert-deftest term-buffer-vertical-motion-0-unwrapped-line ()
+  (let* ((term-width 79) ; usual win body width in batch mode
+         (line (concat (make-string term-width ?1) "\n")))
+    ;; vertical-motion 0 goes to the beginning of the line
+    (should
+     (equal '(0 . 1)
+            ;; from the beginning of the line
+            (term-test-vertical-motion 0 line 1)))
+    (should
+     (equal '(0 . 1)
+            ;; from the middle of the line
+            (term-test-vertical-motion 0 line 5)))
+    (should
+     (equal '(0 . 1)
+            ;; from the \n
+            (term-test-vertical-motion 0 line 80)))))
+
+(ert-deftest term-buffer-vertical-motion-0-wrapped-line ()
+  (let* ((term-width 79) ; usual win body width in batch mode
+         (line (concat (make-string term-width ?1)
+                       (make-string term-width ?2)
+                       (make-string term-width ?3)
+                       "\n"))
+         (line-1-bol 1)
+         (line-2-bol (+ line-1-bol term-width))
+         (line-3-bol (+ line-2-bol term-width))
+         (final-newline (+ line-3-bol term-width)))
+    ;; vertical-motion 0 goes to the beginning of the line, taking
+    ;; wrapping into account.
+
+    ;; vertical-motion 0 from line 1
+    (should
+     (equal (cons 0 line-1-bol)
+            (term-test-vertical-motion 0 line line-1-bol)))
+    (should
+     (equal (cons 0 line-1-bol)
+            (term-test-vertical-motion 0 line (+ line-1-bol 5))))
+    (should
+     (equal (cons 0 line-1-bol)
+            (term-test-vertical-motion 0 line (+ line-1-bol 9))))
+
+    ;; vertical-motion 0 from line 2
+    (should
+     (equal (cons 0 line-2-bol)
+            (term-test-vertical-motion 0 line line-2-bol)))
+    (should
+     (equal (cons 0 line-2-bol)
+            (term-test-vertical-motion 0 line (+ line-2-bol 5))))
+    (should
+     (equal (cons 0 line-2-bol)
+            (term-test-vertical-motion 0 line (+ line-2-bol 9))))
+
+    ;; vertical-motion 0 from line 3
+    (should
+     (equal (cons 0 line-3-bol)
+            (term-test-vertical-motion 0 line line-3-bol)))
+    (should
+     (equal (cons 0 line-3-bol)
+            (term-test-vertical-motion 0 line (+ line-3-bol 5))))
+    (should
+     (equal (cons 0 line-3-bol)
+            (term-test-vertical-motion 0 line (+ line-3-bol 9))))
+    (should
+     (equal (cons 0 line-3-bol)
+            (term-test-vertical-motion 0 line final-newline)))))
+
+(ert-deftest term-buffer-vertical-motion-down-unwrapped-lines ()
+  (let* ((term-width 79) ; usual win body width in batch mode
+         (text (concat (make-string 79 ?1) "\n"
+                       (make-string 79 ?2) "\n"))
+         (line-1-bol 1)
+         (line-1-middle 5)
+         (line-1-newline (+ line-1-bol term-width))
+         (line-2-bol (+ line-1-bol term-width 1))
+         (line-3-bol (+ line-2-bol term-width 1)))
+    ;; vertical-motion count, with count >0, goes down count lines to
+    ;; the beginning of that line.
+
+    ;; vertical-motion 1 from line 1
+    (should
+     (equal (cons 1 line-2-bol)
+            (term-test-vertical-motion 1 text line-1-bol)))
+    (should
+     (equal (cons 1 line-2-bol)
+            (term-test-vertical-motion 1 text line-1-middle)))
+    (should
+     (equal (cons 1 line-2-bol)
+            (term-test-vertical-motion 1 text line-1-newline)))
+
+    ;; vertical-motion 2 from line 1
+    (should
+     (equal (cons 2 line-3-bol)
+            (term-test-vertical-motion 2 text line-1-bol)))
+    (should
+     (equal (cons 2 line-3-bol)
+            (term-test-vertical-motion 2 text line-1-middle)))
+    (should
+     (equal (cons 2 line-3-bol)
+            (term-test-vertical-motion 2 text line-1-newline)))
+
+    ;; vertical motion 3 from line 1
+    (should
+     (equal (cons 2 line-3-bol)
+            (term-test-vertical-motion 3 text line-1-bol)))
+    (should
+     (equal (cons 2 line-3-bol)
+            (term-test-vertical-motion 3 text line-1-middle)))
+    (should
+     (equal (cons 2 line-3-bol)
+            (term-test-vertical-motion 3 text line-1-newline)))))
+
+(ert-deftest term-buffer-vertical-motion-down-wrapped-lines ()
+  (let* ((term-width 79) ; usual win body width in batch mode
+         (text (concat (make-string term-width ?1)
+                       (make-string term-width ?2)
+                       "\n"
+                       (make-string term-width ?3)
+                       "\n"))
+        (line-1-bol 1)
+        (line-1-middle 5)
+        (line-1-eol (+ line-1-bol term-width -1))
+        (line-2-bol (+ line-1-bol term-width))
+        (line-3-bol (+ line-2-bol term-width 1))
+        (final-newline (1+ (length text))))
+    ;; vertical-motion count, with count >0, goes down count lines to
+    ;; the beginning of that line, taking wrapping into account.
+
+    ;; vertical-motion 1 from line 1
+    (should
+     (equal (cons 1 line-2-bol)
+            (term-test-vertical-motion 1 text line-1-bol)))
+    (should
+     (equal (cons 1 line-2-bol)
+            (term-test-vertical-motion 1 text line-1-middle)))
+    (should
+     (equal (cons 1 line-2-bol)
+            (term-test-vertical-motion 1 text line-1-eol)))
+
+    ;; vertical-motion 2 from line 1
+    (should
+     (equal (cons 2 line-3-bol)
+            (term-test-vertical-motion 2 text line-1-bol)))
+    (should
+     (equal (cons 2 line-3-bol)
+            (term-test-vertical-motion 2 text line-1-middle)))
+    (should
+     (equal (cons 2 line-3-bol)
+            (term-test-vertical-motion 2 text line-1-eol)))
+
+    ;; vertical-motion 3 from line 1
+    (should
+     (equal (cons 3 final-newline)
+            (term-test-vertical-motion 3 text line-1-bol)))
+    (should
+     (equal (cons 3 final-newline)
+            (term-test-vertical-motion 3 text line-1-middle)))
+    (should
+     (equal (cons 3 final-newline)
+            (term-test-vertical-motion 3 text line-1-eol)))
+
+    ;; vertical-motion 4 from line 1
+    (should
+     (equal (cons 3 final-newline)
+            (term-test-vertical-motion 4 text line-1-bol)))
+    (should
+     (equal (cons 3 final-newline)
+            (term-test-vertical-motion 4 text line-1-middle)))
+    (should
+     (equal (cons 3 final-newline)
+            (term-test-vertical-motion 4 text line-1-eol)))))
+
+(ert-deftest term-buffer-vertical-motion-up-unwrapped-lines ()
+  (let* ((term-width 79) ; usual win body width in batch mode
+         (text (concat (make-string 79 ?1) "\n"
+                       (make-string 79 ?2) "\n"
+                       (make-string 79 ?3) "\n"))
+         (line-1-bol 1)
+         (line-2-bol (+ line-1-bol term-width 1))
+         (line-3-bol (+ line-2-bol term-width 1))
+         (line-3-middle (+ line-3-bol 5))
+         (line-3-newline (+ line-3-bol term-width 1)))
+    ;; vertical-motion count, with count <0, goes up count lines to
+    ;; the beginning of that line.
+
+    ;; vertical-motion -1 from line 3
+    (should
+     (equal (cons -1 line-2-bol)
+            (term-test-vertical-motion -1 text line-3-bol)))
+    (should
+     (equal (cons -1 line-2-bol)
+            (term-test-vertical-motion -1 text line-3-middle)))
+    (should
+     (equal (cons -1 line-3-bol)
+            (term-test-vertical-motion -1 text line-3-newline)))
+
+    ;; vertical-motion -2 from line 3
+    (should
+     (equal (cons -2 line-1-bol)
+            (term-test-vertical-motion -2 text line-3-bol)))
+    (should
+     (equal (cons -2 line-1-bol)
+            (term-test-vertical-motion -2 text line-3-middle)))
+    (should
+     (equal (cons -2 line-2-bol)
+            (term-test-vertical-motion -2 text line-3-newline)))
+
+    ;; vertical-motion -2 from line 3 goes to point-min
+    (should
+     (equal '(-2 . 1)
+            (term-test-vertical-motion -3 text line-3-bol)))
+    (should
+     (equal '(-2 . 1)
+            (term-test-vertical-motion -3 text line-3-middle)))
+    (should
+     (equal '(-3 . 1)
+            (term-test-vertical-motion -3 text line-3-newline)))))
+
+(ert-deftest term-buffer-vertical-motion-up-wrapped-lines ()
+  (let* ((term-width 79) ; usual win body width in batch mode
+         (text (concat (make-string 79 ?1)
+                       (make-string 79 ?2)
+                       (make-string 79 ?3) "\n"))
+         (line-1-bol 1)
+         (line-2-bol (+ line-1-bol term-width))
+         (line-3-bol (+ line-2-bol term-width))
+         (line-3-middle (+ line-3-bol 5))
+         (line-3-newline (+ line-3-bol term-width 1)))
+    ;; vertical-motion count, with count <0, goes up count lines to
+    ;; the beginning of that line, taking wrapping into account.
+
+    ;; vertical-motion -1 from line 3
+    (should
+     (equal (cons -1 line-3-bol)
+            (term-test-vertical-motion -1 text line-3-newline)))
+    (should
+     (equal (cons -1 line-2-bol)
+            (term-test-vertical-motion -1 text line-3-middle)))
+    (should
+     (equal (cons -1 line-2-bol)
+            (term-test-vertical-motion -1 text line-3-bol)))
+
+    ;; vertical-motion -2 from line 3
+    (should
+     (equal (cons -2 line-2-bol)
+            (term-test-vertical-motion -2 text line-3-newline)))
+    (should
+     (equal (cons -2 line-1-bol)
+            (term-test-vertical-motion -2 text line-3-middle)))
+    (should
+     (equal (cons -2 line-1-bol)
+            (term-test-vertical-motion -2 text line-3-bol)))
+
+    ;; vertical-motion -2 from line 3 goes to point-min
+    (should
+     (equal '(-3 . 1)
+            (term-test-vertical-motion -3 text line-3-newline)))
+    (should
+     (equal '(-2 . 1)
+            (term-test-vertical-motion -3 text line-3-middle)))
+    (should
+     (equal '(-2 . 1)
+            (term-test-vertical-motion -3 text line-3-bol)))))
+
 (provide 'term-tests)
 
 ;;; term-tests.el ends here
-- 
2.47.0


--=-=-=--




Acknowledgement sent to Stephane Zermatten <szermatt@HIDDEN>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs@HIDDEN. Full text available.
Report forwarded to bug-gnu-emacs@HIDDEN:
bug#77722; Package emacs. Full text available.
Please note: This is a static page, with minimal formatting, updated once a day.
Click here to see this page with the latest information and nicer formatting.
Last modified: Fri, 11 Apr 2025 07:15:02 UTC

GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997 nCipher Corporation Ltd, 1994-97 Ian Jackson.