Index | Thread | Search

From:
Pascal Stumpf <pascal@stumpf.co>
Subject:
Re: ksh vi mode: stop 'P' command from moving the cursor
To:
Ingo Schwarze <schwarze@usta.de>
Cc:
Walter Alejandro Iglesias <wai@roquesor.com>, Anton Lindqvist <anton@basename.se>, millert@openbsd.org, tech@openbsd.org
Date:
Thu, 24 Apr 2025 22:46:53 +0200

Download raw body.

Thread
Hi Ingo,

On Thu, 24 Apr 2025 17:39:56 +0200, Ingo Schwarze wrote:
> Hello Walter,
> 
> we have now three options what to do with 'P' in ksh(1) VI mode; one
> of them also changes the behaviour of 'p'.  So i'm asking people:
> which one of the three do you prefer?
> 
> If people prefer one of the two solutions proposed by Walter, i'll
> polish, test, and commit that.  Otherwise, i'll commit the diff i
> posted earlier.  Something needs to be committed because right now,
> the 'P' command misbehaves with UTF-8.
> 
> Walter Alejandro Iglesias wrote on Thu, Apr 24, 2025 at 10:49:02AM +0200:
> > On Mon, Apr 21, 2025 at 11:38:01PM +0200, Ingo Schwarze wrote:
> 
> >> here is a patch to change the behaviour of the "paste before" (P) command
> >> in the VI command line editing mode of ksh(1).
> 
> > With your diff, after pasting with the 'P' command the cursor lands one
> > character *after* the last character in the string pasted.
> 
> Correct.
> That is also the character the cursor is on before pasting.
> 
> > I'm not saying this is wrong, maybe it's more convenient, but it's not
> > what the 'p' command does
> 
> Correct.
> In our ksh(1), 'p' puts the cursor on the last character inserted.
> 
> > or what other popular applications (vim, bash) that
> > have adopted moving the cursor to the end of the pasted string do.
> > 
> > The two diffs below are *NOT TESTED* (but considering I'm not modifying
> > functions in this case, they probably won't cause regressions.)  The
> > idea is to show the difference between two behaviors with practical
> > examples; you'll decide which one you like best.
> 
> I do not feel strongly about which cursor positioning after 'p' or 'P'
> might be most useful.  Opinions from people who actually use VI mode
> in our ksh(1) would be particularly appreciated.  Opinions from
> developers would be helpful even if they do not use VI mode.
> 
> If nobody voicces a preference, i tend to use the diff i posted
> because it is least intrusive, and *if* there is limited interest,
> least intrusive is arguably best.
> 
> > The first behavior (vi(1), nvi2 from ports, FreeBSD sh):
> > 
> >   After pasting text with 'p' or 'P' the cursor lands on the first
> >   character of the string pasted.
> 
> I can confirm this is what our vi(1) does.  I did not test nvi2
> or FreeBSD.
> 
> > The second behavior (vim, bash and other shells):
> > 
> >   After pasting text with 'p' or 'P' the cursor lands on the last
> >   character of the string pasted.
> 
> I did not test vim or bash, but there is one potential (weak) argument
> supporting this behabiour: in our ksh(1), that's where the cursor
> lands when you insert text with 'a' or 'i' and press ESCAPE.
> So arguably, this option slightly improves consistency of ksh(1) VI mode.
> 
> Looking at the ksh(1) manual page, i do not see any other VI mode
> commands that could be looked at as a model for what 'p' and 'P'
> should do.
> 
> > (By the way, I found a bug on vi(1) paste command.  I'll report it in
> > other thread in the future.)
> 
> Thanks, keeping different issues in different, well-named threads
> helps everyone.
> 
> > FIRST BEHAVIOR (vi like)  # go to the first inserted character
> > 
> > Index: vi.c
> > ===================================================================
> > RCS file: /cvs/src/bin/ksh/vi.c,v
> > diff -u -p -r1.61 vi.c
> > --- vi.c	21 Apr 2025 20:06:15 -0000	1.61
> > +++ vi.c	24 Apr 2025 08:36:19 -0000
> > @@ -837,8 +837,10 @@ vi_cmd(int argcnt, const char *cmd)
> >  			while (es->cursor < es->linelen)
> >  				if (!isu8cont(es->cbuf[++es->cursor]))
> >  					break;
> > +			any = es->cursor;
> >  			while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0)
> >  				;
> > +			es->cursor = any + 1;
> >  			while (es->cursor > 0)
> >  				if (!isu8cont(es->cbuf[--es->cursor]))
> >  					break;
> 
> From code inspection, this looks likely to do what you want.
> Probably, it would be even better to replace the last four lines
> with simply
> 
> 	es->cursor = any;
> 
> because if the first inserted character is valid UTF-8, that does the
> same, and if the first inserted character is isu8cont (which shouldn't
> normally happen), backing up to the last character before the insertion
> feels like a dubious choice.
> 
> > @@ -848,11 +850,10 @@ vi_cmd(int argcnt, const char *cmd)
> >  
> >  		case 'P':
> >  			modified = 1; hnum = hlast;
> > -			any = 0;
> > +			any = es->cursor;
> >  			while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0)
> > -				any = 1;
> > -			if (any && es->cursor != 0)
> > -				es->cursor--;
> > +				continue;
> > +			es->cursor = any;
> >  			if (argcnt != 0)
> >  				return -1;
> >  			break;
> 
> Yes, that might do what you want.
> 
> > SECOND BEHAVIOR (vim like)  # go to the last inserted character
> > 
> > Index: vi.c
> > ===================================================================
> > RCS file: /cvs/src/bin/ksh/vi.c,v
> > diff -u -p -r1.61 vi.c
> > --- vi.c	21 Apr 2025 20:06:15 -0000	1.61
> > +++ vi.c	24 Apr 2025 07:34:50 -0000
> > @@ -848,11 +848,9 @@ vi_cmd(int argcnt, const char *cmd)
> >  
> >  		case 'P':
> >  			modified = 1; hnum = hlast;
> > -			any = 0;
> >  			while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0)
> > -				any = 1;
> > -			if (any && es->cursor != 0)
> > -				es->cursor--;
> > +				continue;
> > +			es->cursor--;
> 
> Not quite, instead of es->cursor--, here we would likely need
> 
> 	while (es->cursor > 0)
> 		if (!isu8cont(es->cbuf[--es->cursor]))
> 			break;
> 
> such that we back up a complete character and not just a single byte.
> 
> So, which behaviour do people want?
> 
>  0) minimally invasive, 1P unchanged:
>     p  unchanged -> to last character inserted
>     1P unchanged -> after last character inserted
>     2P changed to match 1P -> after last character inserted
> 
>  1) meximally invasive -> always to first charcter inserted
>     like in our vi(1)
> 
>  2) minimally invasive, 2P unchanged -> always to last character inserted
>     p  unchanged
>     1P changed to match 2P
>     2P unchanged

As a user of vi mode, I must say that I prefer option 2).  It is
consistent among both the p and P commands, and also the behaviour of
the ksh93 package, as well as all historical ksh versions I tested on
sdf.org.  That is probably also the reason bash behaves this way.
pdksh is the one that has an inaccuracy here in its implementation.

> Yours,
>   Ingo
>