-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tab completion menu causes inconsistent editor state in emacs #71
Comments
I'm pretty sure that having to hit TAB twice there is normal, but I don't have access to the Korn Shell book right now to verify it. edit: It's in sh.1: "When using a tab for completion that does not yield a unique match, a subsequent tab will provide a numbered list of matching alternatives."
Ah. And it also wrongly moves the cursor two positions to the right of the file name instead of one position. Which would explain why you can't backspace past the first character. In fact you are deleting it, the terminal display just isn't reflecting it because the cursor is positioned wrong. I'll try to hunt down that bug in the coming days. Thanks for the report. |
Just to confirm that ksh2020 also has this bug, so there is no bugfix from there or the preceding 93v- beta to backport. And it looks like the menu completion in vi mode is also buggy, but in a different way; an inconsistent state occurs if you cause the menu to appear with two TABs (which also puts you in command mode) but then re-enter insert mode (e.g. with
|
Is this one part of the same issue or is it a new issue? Found this on the old ast-users ML, apparently the user complaining about his issue got told it was normal behavior, but in testing that, I found this:
hit TAB
Four spaces get inserted. Why? Hit TAB again
Huh? This time it did the filename completion. Hit TAB again
And now it displays the completion list menu? There's some confusion going on here between what KSH does if it's trying to complete a command (the first token on the line) and trying to complete a filename (everything else). The original thread: https://www.mail-archive.com/ast-users@lists.research.att.com/msg00419.html |
Thanks for the links. They correctly report another bug with menu tab completion. According to my testing, the linked bug can only be reproduced in emacs mode and not in vi mode, and ksh2020 still has this bug so no fix is available to backport. Shame the original reporter was ignored back in 2013. I'll try to find a fix for it as part of this issue. Re your own observations...
What was inserted was a literal tab character (note that it takes only one backspace to remove it), because "JH" does not match any command. ksh falls back to a literal tab if there is no match, which would have been the behaviour without any edit mode active.
That's correct behaviour (although your surprise at it does illustrate that the literal tab behaviour above is ill thought out). A command name has been entered and followed by a blank separator (the literal tab character), so at this point it doesn't matter if the command exists or not; you're now completing file names for a By the same token, displaying the file completion menu after a second TAB is also normal behaviour. |
I can only reproduce this bug when the multiline option is enabled. If multiline editing is disabled with |
I wrote:
While this does seem to work correctly in vi mode, it turns out to be very broken in emacs mode. If you try to type a second literal tab, you get a completion menu with all possible commands, filling your scrollback buffer. So that's another bug. |
Confirmed. I didn't think of that. |
In the emacs editor: 1. press ESC 2. change the size of your terminal window and your screen is mysteriously cleared. (Until recent fixes, the shell probably also crashed somewhere in the job control code.) The cause is the way SIGWINCH is handled by ed_read() in edit.c. For the emacs editor, it sends a Ctrl+L character to the input buffer. The Ctrl+L command refreshes the command line. And it so happens that ESC plus Ctrl+L is a command to clear the screen in the emacs editor. With the exeption of vi insert/command mode for which it uses a shared flag, edit.c does not know the state of the editor, because their data are internal to emacs.c and vi.c. So it doesn't know whether you're in some mode that treats keyboard input specially. Which means this way of dealing with SIGWINCH is fundamentally misdesigned and is not worth fixing. It gets sillier: in addition to sending keyboard commands, edit.c was also communicating directly with emacs.c and vi.c via a flag, e_nocrnl, which means 'please don't make Ctrl+L emit a linefeed' (it normally refreshes on a new line but that is undesirable for SIGWINCH). So there is already a hack that breaks the barrier between edit.c and emacs.c/vi.c. Let's do that properly instead. As of this commit, ed_read() does not send any fake keystrokes. Instead, two extern functions, emacs_redraw() and vi_redraw(), are defined for redrawing the command line. These are put in emacs.c and vi.c so they have access to relevant static data and functions. Then, instead of sending keyboard commands to the editor and returning, ed_read() simply calls the redraw function for the active editor, then continues and waits for input. Much cleaner. src/cmd/ksh93/include/edit.h: - Remove e_nocrnl flag from Edit_t struct. - Define externs emacs_redraw() and vi_redraw(). Since Emacs_t and Vi_t types are not known here, we have to declare void* pointers and the functions will have to use typecasts. src/cmd/ksh93/edit/edit.c: - ed_read(): Call emacs_redraw() or vi_redraw() as per above. - ed_getchar(): Remove comment about a nonexistent while loop. src/cmd/ksh93/edit/emacs.c: - Updates corresponding to removal of e_nocrnl flag. - Add emacs_redraw(). This one is pretty simple. Refresh the command line, then ed_flush() to update the cursor display. src/cmd/ksh93/edit/vi.c: - Updates corresponding to removal of e_nocrnl flag. Also remove a similar internal 'nonewline' flag which is now equally redundant. - Move the Ctrl+L handling code (minus writing the newline) into the vi_redraw() function. - Change two cases where vi set nonewline and sent Ctrl+L to itself into simple vi_redraw() calls. - Add vi_redraw(). This is more complicated as it incorporates the previous Ctrl+L code. It needs an added refresh() call with a check whether we're currently in command or insert mode, as those use different refresh methods. Luckily edit.c already maintains an *e_vi_insert flag in ed_getchar() that we can use. Since vi's refresh() already calls ed_flush(), we don't need to add that.
So, I did many fruitless attempts to debug the completion code, only to find the bug may not in the completion code at all. It could be in these two lines in ksh/src/cmd/ksh93/edit/emacs.c Lines 1556 to 1557 in caf7ab6
The following diff seems to fix it, but I don't understand exactly why yet nor do have I fully tested for possible side effects… edit: obsolete patch, do not apply diff --git a/src/cmd/ksh93/edit/emacs.c b/src/cmd/ksh93/edit/emacs.c
index aa6fcd3..091e7fc 100644
--- a/src/cmd/ksh93/edit/emacs.c
+++ b/src/cmd/ksh93/edit/emacs.c
@@ -1554,7 +1559,7 @@ static void draw(register Emacs_t *ep,Draw_t option)
#endif /* SHOPT_MULTIBYTE */
}
if(ep->ed->e_multiline && option == REFRESH)
- ed_setcursor(ep->ed, ep->screen, ep->cursor-ep->screen, ep->ed->e_peol, -1);
+ ed_setcursor(ep->ed, ep->screen, ep->cursor-ep->screen, ep->ed->e_peol - 1, -1);
/****************** This could mean that there is an off-by-one error in this |
|
No off-by-one value appears to be stored in |
The diff below fixes the bug reported in the top comment for me, and I haven't been able to discover any breakage introduced by it, which doesn't mean there isn't any. I'm still not quite sure what's going on or if this is the correct fix. Nonetheless, please test this and try to break it. The edit: obsolete patch, do not apply diff --git a/src/cmd/ksh93/edit/edit.c b/src/cmd/ksh93/edit/edit.c
index 3338bd3..01b6afd 100644
--- a/src/cmd/ksh93/edit/edit.c
+++ b/src/cmd/ksh93/edit/edit.c
@@ -1338,8 +1338,9 @@ int ed_setcursor(register Edit_t *ep,genchar *physical,register int old,register
delta = new-first;
}
}
- while(delta-->0)
- ed_putchar(ep,physical[old++]);
+ if(!clear)
+ while(delta-- > 0)
+ ed_putchar(ep,physical[old++]);
return(new);
}
#endif /* SHOPT_ESH || SHOPT_VSH */ |
Meanwhile here's another diff I'm more sure of. It should fix the buggy emacs literal-tab behaviour detailed here and here and make ksh act like bash, mksh, zsh, etc. – to insert a literal tab you have to use your diff --git a/src/cmd/ksh93/edit/emacs.c b/src/cmd/ksh93/edit/emacs.c
index aa6fcd3..4b21379 100644
--- a/src/cmd/ksh93/edit/emacs.c
+++ b/src/cmd/ksh93/edit/emacs.c
@@ -371,6 +371,8 @@ int ed_emacsread(void *context, int fd,char *buff,int scend, int reedit)
}
ep->ed->e_tabcount = 0;
}
+ beep();
+ continue;
do_default_processing:
default:
|
The fix for the mailing list issue is also a fairly simple one. ksh/src/cmd/ksh93/edit/completion.c Lines 194 to 204 in caf7ab6
The menu is printed here, with the ksh/src/cmd/ksh93/edit/completion.c Lines 346 to 361 in caf7ab6
So the fix is simply to check even in diff --git a/src/cmd/ksh93/edit/completion.c b/src/cmd/ksh93/edit/completion.c
index 1efc09b..a6086f9 100644
--- a/src/cmd/ksh93/edit/completion.c
+++ b/src/cmd/ksh93/edit/completion.c
@@ -343,6 +343,8 @@ int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count)
}
if(mode=='\\' && out[-1]=='/' && narg>1)
mode = '=';
+ else if(mode=='=' && narg<2)
+ mode = '\\'; /* no filename menu if there is only one choice */
if(mode=='=')
{
if (strip && !cmd_completion) |
That leaves the fourth bug in this issue, the vi one. I've no clue where to look for the cause yet -- it may or may not even be in vi.c. Any ideas anyone? @JohnoKing @hyenias @lkujaw @hvdijk |
Oh, man, number 3 is so cool, it's like a pain you don't know how to get rid of until it's suddenly just... gone. What crazy person EVER thought the original behavior was a correct one? Ahem. Ok, number 1... yup, fixes it. behavior is correct now with multiline on or off... no extra space and no hanging characters. For number 2, also fixes it, just get a terminal beep now with a TAB rather than a jump to the next tabstop and then a menu full of everything. |
The last fix for number 1 may accidentally have worked, but is not correct. It did not sit right with me that the same bug did not occur in vi mode even though it uses that same These two lines are the wrong way around. The cursor needs to be set for the internal screen buffer after increasing that pair of pointers, not before. Setting it before caused this
diff --git a/src/cmd/ksh93/edit/emacs.c b/src/cmd/ksh93/edit/emacs.c
index aa6fcd3..6d6502f 100644
--- a/src/cmd/ksh93/edit/emacs.c
+++ b/src/cmd/ksh93/edit/emacs.c
@@ -1540,8 +1542,8 @@ static void draw(register Emacs_t *ep,Draw_t option)
sptr++;
continue;
}
- setcursor(ep,sptr-ep->screen,*nptr);
*sptr++ = *nptr++;
+ setcursor(ep,sptr-ep->screen,*nptr);
#if SHOPT_MULTIBYTE
while(*nptr==MARKER)
{ |
What little I can quickly find without trying to understand the code: with the following debug patch: --- a/src/cmd/ksh93/sh/lex.c
+++ b/src/cmd/ksh93/sh/lex.c
@@ -397,6 +397,7 @@ int sh_lex(Lex_t* lp)
fcseek(-LEN);
goto breakloop;
case S_EOF:
+ write(2, "S_EOF\n", 6);
sp = fcfile();
if((n=lexfill(lp)) > 0)
{ After the Enter after tab completion, the After the Enter without tab compltion, the Hopefully this will speed up a deeper investigation by someone who actually understands the code. |
Which, as far as I can tell, is no one. I'm not convinced Korn understood his own code by 2009 or so. That's what you get for not commenting stuff properly. Thanks, though, this will probably help me get started on that one. |
Here's another reproducer of the vi bug, suggesting the problem is a write past the end of the screen buffer (which also explains the
And from there on, if you press
to
to an outright crash. |
On the lex side, the reason |
Yes. That looks like correct behaviour on the lexer side of things. The bug is most likely somewhere in edit/vi.c, possibly in this block or a function called by it. |
The patch referenced in #71 (comment): With this patch, ksh no longer deletes on-screen characters when you backspace, the cursor just moves back over them. They ARE being removed from the input buffer. |
Thanks for catching that. I'd noticed that symptom but I mislead myself into thinking it was due to wonky terminal settings. :-/ So those two lines were not the wrong way around. I still think the correct fix is something in that general direction. Current hypothesis: we need an extra Undo the previous diff and try: diff --git a/src/cmd/ksh93/edit/emacs.c b/src/cmd/ksh93/edit/emacs.c
index aa6fcd3..7b7eab1 100644
--- a/src/cmd/ksh93/edit/emacs.c
+++ b/src/cmd/ksh93/edit/emacs.c
@@ -1552,6 +1555,7 @@ static void draw(register Emacs_t *ep,Draw_t option)
ep->cursor++;
}
#endif /* SHOPT_MULTIBYTE */
+ setcursor(ep,sptr-ep->screen,*nptr);
}
if(ep->ed->e_multiline && option == REFRESH)
ed_setcursor(ep->ed, ep->screen, ep->cursor-ep->screen, ep->ed->e_peol, -1); |
|
Sorry, but... this is with patches 2&3 and the latest patch 1...
|
For clariity, with only this patch:
the test failures above occur. |
Yup. Thanks, again. Try this instead... and I did run the tests this time and found no regressions. Sorry about that. diff --git a/src/cmd/ksh93/edit/emacs.c b/src/cmd/ksh93/edit/emacs.c
index aa6fcd3..9922ff5 100644
--- a/src/cmd/ksh93/edit/emacs.c
+++ b/src/cmd/ksh93/edit/emacs.c
@@ -1554,7 +1556,10 @@ static void draw(register Emacs_t *ep,Draw_t option)
#endif /* SHOPT_MULTIBYTE */
}
if(ep->ed->e_multiline && option == REFRESH)
+ {
+ setcursor(ep, sptr - ep->screen, *nptr); /* need extra sync to avoid off-by-one cursor display */
ed_setcursor(ep->ed, ep->screen, ep->cursor-ep->screen, ep->ed->e_peol, -1);
+ }
/****************** |
Nope. That one gives a strange side effect when you do a completion at the bottom line of the terminal -- your terminal scrolls up and you get a number of empty lines at the bottom. I'm about ready to pull my hair out here. None of the fixes that seem correct actually work. This one seems to work without any side effects, but it just seems wrong. If there is a bug in that function, it should also occur in vi mode and it does not. Obviously, I have no idea what I'm doing, and have no business maintaining this project. |
@McDutchie: Just hang in there. I, for one, know that you are doing a good job with this new ksh93 endeavor. Please take a break and get some rest. Perhaps take a day off and relax. Because, there is other fruit to pick from the issues tree. |
Thanks for the encouragement. :) OK, since I can't sleep, I'm back to my original hypothesis that there is somehow an off-by-one error related to the For reference, this is the original version of that call in emacs.c: ksh/src/cmd/ksh93/edit/emacs.c Lines 1556 to 1557 in df2b9bf
There is a corresponding call in the vi.c refresh() function (which does the same thing as draw() in emacs.c), where the third (old ) and fourth (new ) arguments are actually identical: Lines 2086 to 2087 in df2b9bf
The expectation for this particular call is in fact that they should be identical, so that a delta of zero is calculated in that function. Delta not being zero is what causes the cursor to be positioned wrong. In vi.c, That leaves the question of why vi.c adds 1 to both So, this patch makes emacs.c do it the same way vi.c does. Let's make the third argument identical to the fourth. My brief testing shows the bug is fixed, and the regression tests yield no failures. This fix is also the most specific change possible, so there are few opportunities for side effects (I hope). Please test… diff --git a/src/cmd/ksh93/edit/emacs.c b/src/cmd/ksh93/edit/emacs.c
index aa6fcd3..3f56625 100644
--- a/src/cmd/ksh93/edit/emacs.c
+++ b/src/cmd/ksh93/edit/emacs.c
@@ -1554,7 +1556,7 @@ static void draw(register Emacs_t *ep,Draw_t option)
#endif /* SHOPT_MULTIBYTE */
}
if(ep->ed->e_multiline && option == REFRESH)
- ed_setcursor(ep->ed, ep->screen, ep->cursor-ep->screen, ep->ed->e_peol, -1);
+ ed_setcursor(ep->ed, ep->screen, ep->ed->e_peol, ep->ed->e_peol, -1);
/****************** |
Not sure if this helps, but I noticed that if you comment out the following in completion.c (normally responsible for adding a space after a non-directory is printed), the problem also goes away.
|
A little hint: if you select multiple lines on a blob page like https://github.com/ksh93/ksh/blob/master/src/cmd/ksh93/edit/completion.c, then choose ksh/src/cmd/ksh93/edit/completion.c Lines 471 to 481 in df2b9bf
Anyway, that's an interesting datapoint. Commenting that out breaks things in an informative way. |
Thanks for the tip. It seems like that's the only time the two values diverge (ep->cursor-ep->screen being one less) in my very limited testing. |
Based on Lines 1246 to 1260 in df2b9bf
It seems like setting old and new to the same value would normally be a noop (if it wasn't set to clear.) Otherwise, the following segment gets run:
|
I did a bit of research on this, and I think the fix to have the Emacs editing mode do the same as Vi is correct. From RELEASE: Here's a fragment from the completion.c of the venerable but dated CDE DtKsh:
Noticeably missing is the code to add a space after file name completions. So, it seems plausible that if multiline editing mode was added beforehand,the ep->ed->p_eol != ep->cursor-ep->screen case might never have occurred during testing. Setting the 'first' parameter to -1 seems to be a pretty explicit indicator that the author(s) intended the line clearing code to run, hence the entry in RELASE. The real issue is that if we update the cursor by calling ed_setcursor on line 1554 with old != new, the later call to setcursor on line 1583, here:
will use outdated screen information to call setcursor, which, coincidentally, calls ed_setcursor. Well, that was quite the adventure... Could we add issue IDs/bug numbers to the regression tests, perhaps? It seems like useful information to catalog the defect delta from the last AST release, considering the effort going into this project. |
Many thanks Lev. That does properly make sense of this bug. I'm not sure yet how to go about a regression test for this one. For seeing what was fixed by what commit, |
I can't seem to get a regression test working for this bug, and I'm ready to mark this issue resolved. If anyone feels there should be a regression test, please feel free to have a shot at it. I'd be happy to accept a pull request for a working one. The existing ones are in tests/pty.sh. Use |
Line editing is set thus:
Start typing, get to bin/p, hit TAB. Nothing happens. Hit TAB again. (why do I have to hit TAB twice?) Get:
Hit '2', hit TAB again, shell fills out bin/pscal.
[45] mbp13 $ bin/pscal
Now, backspace over the command line...
I'm left with the 'b', which isn't actually there, I can hit ENTER and not get an error message, and it's not stored in history.
10.15.5, ksh93u+m, ksh93u+ does it too.
The text was updated successfully, but these errors were encountered: