In a sentence of more than one line at the end of the file, when the
cursor is placed in the first character of any line, except for the last
one, running '!)<cmd>' (bang + sentence forward movement + command)
won't affect the last line.  For example, in a file containing the
following at its end:

  Lorem ipsum dolor sit amet, consectetur adipiscing
  elit, sed do eiusmod tempor incididunt ut labore
  et dolore magna aliqua.
                         ^ EOF

Put the cursor in the 'L' of "Lorem" or in the 'e' of "elit"
and type:

  !)sed 's/ //g'

First case:

> Loremipsumdolorsitamet,consecteturadipiscing
  elit,seddoeiusmodtemporincididuntutlabore
  et dolore magna aliqua.

Second case:

  Lorem ipsum dolor sit amet, consectetur adipiscing
> elit,seddoeiusmodtemporincididuntutlabore
  et dolore magna aliqua.


This happens because sentence motions are exclusive, meaning the range
normally ends one character before the final destination.  The code
implements this by unconditionally decrementing the stop column
(--vp->m_stop.cno) at the end of a motion.  This is not desirable when a
sentence reaches EOF.  The last character or the entire last line in
certain states is excluded from the range passed to the filter
operator.

The following diff checks if the state is CS_EOF and the decrement is
skipped in this case.


Index: vi/v_sentence.c
===================================================================
RCS file: /cvs/src/usr.bin/vi/vi/v_sentence.c,v
diff -u -p -u -p -r1.11 v_sentence.c
--- vi/v_sentence.c     25 Apr 2026 19:30:59 -0000      1.11
+++ vi/v_sentence.c     6 May 2026 08:14:25 -0000
@@ -175,20 +175,34 @@ okret:    vp->m_stop.lno = cs.cs_lno;
         * range for motion commands.
         */
        if (ISMOTION(vp)) {
-               if (vp->m_start.cno == 0 &&
-                   (cs.cs_flags != 0 || vp->m_stop.cno == 0)) {
+               if (cs.cs_flags == CS_EOF) {
+                       /*
+                        * If EOF is reached, maintain the stop position
+                        * at the very end of the file (cs_len - 1) instead
+                        * of decrementing it.
+                        */
+                       vp->m_stop.lno = cs.cs_lno;
+                       vp->m_stop.cno = cs.cs_len > 0 ? cs.cs_len - 1 : 0;
+               } else if (vp->m_start.cno == 0 && (cs.cs_flags != 0 ||
+                   vp->m_stop.cno == 0)) {
+                       /*
+                        * If in line mode, adjust stop to the end of
+                        * the previous line.
+                        */
                        if (vp->m_start.lno < vp->m_stop.lno) {
-                               if (db_get(sp,
-                                   --vp->m_stop.lno, DBG_FATAL, NULL, &len))
+                               if (db_get(sp, --vp->m_stop.lno, DBG_FATAL,
+                                   NULL, &len))
                                        return (1);
                                vp->m_stop.cno = len ? len - 1 : 0;
                        }
                        F_SET(vp, VM_LMODE);
-               } else
+               } else if (vp->m_stop.cno > 0) {
                        --vp->m_stop.cno;
+               }
                vp->m_final = vp->m_start;
-       } else
+       } else {
                vp->m_final = vp->m_stop;
+       }
        return (0);
 }
 

-- 
Walter

Reply via email to