Skip to content
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

binding keys #1008

Closed
teamblubee opened this issue Nov 12, 2018 · 27 comments
Closed

binding keys #1008

teamblubee opened this issue Nov 12, 2018 · 27 comments
Labels

Comments

@teamblubee
Copy link

How can I bind the keyboard shortcut ^L to call the clear function?

pressing Ctrl+C clears the line
pressing Ctrl+L just prints ^L to the console.

I looked into creating .xinputrc but that's not working.

How can I bind ^L to call the clear function?

@dannyweldon
Copy link

Questions such as this are better sent to Stack Overflow:
https://stackoverflow.com/questions/tagged/ksh

But something like (press ctrl-v, ctrl-l to insert the ^L):

trap '[[ ${.sh.edchar} == "^L" ]] && clear' KEYBD

@teamblubee
Copy link
Author

Questions such as this are better sent to Stack Overflow:
https://stackoverflow.com/questions/tagged/ksh

But something like (press ctrl-v, ctrl-l to insert the ^L):

trap '[[ ${.sh.edchar} == "^L" ]] && clear' KEYBD

I did look at the stackoverflow community and found a few answers that all seem to be related to using emacs; such as this one: https://unix.stackexchange.com/questions/294592/change-the-key-that-show-previous-command-lines-in-ksh

I have set -o vi in my local .kshrc file as well as a few alias and EDITOR

even after trying the above command in my terminal; pressing CTRL + L just shows ^L and pressing enter just say : not found

Googling around for trap ksh I find this article: https://docstore.mik.ua/orelly/unix3/korn/ch08_04.htm
trap 'print "You hit control-C!"' INT

Ant that successfully remaps CTRL + C to print that message but Its above my head why it works and the other one does not.

@kusalananda
Copy link

kusalananda commented Nov 16, 2018

The ^L in the command must be entered by pressing Ctrl+V followed by Ctrl+L, as @dannyweldon mentioned. You may also want to replace the character with a newline once the test has succeeded:

trap '[[ ${.sh.edchar} == "^L" ]] && .sh.edchar="^M" && clear' KEYBD

Again, the ^M here is entered by pressing Ctrl+V followed by Enter.

The unix.stackexchange.com site may be a better fit for these kinds of questions (than StackOverflow).

@teamblubee
Copy link
Author

The ^L in the command must be entered by pressing Ctrl+V followed by Ctrl+L, as @dannyweldon mentioned. You may also want to replace the character with a newline once the test has succeeded:

trap '[[ ${.sh.edchar} == "^L" ]] && .sh.edchar="^M" clear' KEYBD

Again, the ^M here is entered by pressing Ctrl+V followed by Enter.

The unix.stackexchange.com site may be a better fit for these kinds of questions (than StackOverflow).

I think that I'm missing something here.

Typically in the terminal pressing CTRL+C will cause whatever you've currently typed to get cleared;
if you press CTRL+L would clear the entire terminal buffer

i'm running

ksh --version
  version         sh (AT&T Research) 2017.0.0-devel-2040-g36a76aca

If I press CTRL+C the current line gets cleared out but if I press CTRL+L
^L get's printed to the terminal screen.

I figured out that typing

clear

will clear the entire terminal screen.

I'd like to bind the function clear to the key combination CTRL+L

I haven't found any way to do that on my system, all the answers that I see on stackexchange community talks about setting editing mode to emacs; I've never used emacs and use vi.

The suggested answers above do not work, that's what I am trying to sort out. Can I do anything, provide addtional debugging info or something to get the desired behavior?

@kusalananda
Copy link

I can confirm that the code that I posted in my previous comment works in exactly the same development version that you are using, and that it works in Vi mode. If you are copying and pasting the code, then you have not read what we are writing. The ^L and the ^M must be literal control sequences, not ^ and L and ^ and M.

@teamblubee
Copy link
Author

I can confirm that the code that I posted in my previous comment works in exactly the same development version that you are using, and that it works in Vi mode. If you are copying and pasting the code, then you have not read what we are writing. The ^L and the ^M must be literal control sequences, not ^ and L and ^ and M.

You're correct I misunderstood your original post. How do I get the control sequence that represents ^ and L

The only thing that I could think of doing is running xev and trying to get the key codes but after failing at that, how do I get what control sequence that ksh is looking for?

@krader1961
Copy link
Contributor

@teamblubee This has nothing to do with X keycodes. Shells like ksh work on streams of bytes which historically were given meaning by the ASCII standard. Also, despite what others have said it is a really bad idea to embed literal control characters in a ksh script (other than a handful like newline and tab). It's better to use a symbolic representation and convert it to the literal character where needed. There are several ways to do this. I happen to like printf:

trap '[[ ${.sh.edchar} == "$(printf "\cL")" ]] && { .sh.edchar=""; clear; }' KEYBD

@dannyweldon
Copy link

Also, despite what others have said it is a really bad idea to embed literal control characters in a ksh script

@krader1961 Agreed. I just couldn't remember the proper escape sequence and was being lazy. But thanks to your example, it can be simplified even further, but without the printf:

trap $'[[ ${.sh.edchar} == "\cl" ]] && { .sh.edchar="\cm"; clear; }' KEYBD

Note the leading $ before the trap string which enables that same quoting. I found that I had to set .sh.edchar to Ctrl-m for my prompt to display as @kusalananda did.

@teamblubee
Copy link
Author

@teamblubee This has nothing to do with X keycodes. Shells like ksh work on streams of bytes which historically were given meaning by the ASCII standard. Also, despite what others have said it is a really bad idea to embed literal control characters in a ksh script (other than a handful like newline and tab). It's better to use a symbolic representation and convert it to the literal character where needed. There are several ways to do this. I happen to like printf:

trap '[[ ${.sh.edchar} == "$(printf "\cL")" ]] && { .sh.edchar=""; clear; }' KEYBD

This seems to work, it clears the terminal buffer but then it freeses

Also, despite what others have said it is a really bad idea to embed literal control characters in a ksh script

@krader1961 Agreed. I just couldn't remember the proper escape sequence and was being lazy. But thanks to your example, it can be simplified even further, but without the printf:

trap $'[[ ${.sh.edchar} == "\cl" ]] && { .sh.edchar="\cm"; clear; }' KEYBD

Note the leading $ before the trap string which enables that same quoting. I found that I had to set .sh.edchar to Ctrl-m for my prompt to display as @kusalananda did.

This one works as expected.

Just to be clear I am not trying to put these in any shell scripts. I just want to configure my shell's behavior.

I think in bash it's the bind command.

I did read this: https://docstore.mik.ua/orelly/unix3/korn/ch10_03.htm

before asking the question but the example they give is a bit over my head.

      # Quoted from Page 98 of
      # The New KornShell Command and Programming Language

 1    typeset -A Keytable
 2    trap 'eval "${Keytable[${.sh.edchar}]}"' KEYBD
 3    function keybind # key [action]
 4    {
 5         typeset key=$(print -f "%q" "$2")
 6         case $# in
 7         2)      Keytable[$1]=' .sh.edchar=${.sh.edmode}'"$key"
 8                 ;;
 9         1)      unset Keytable[$1]
10                 ;;
11         *)      print -u2 "Usage: $0 key [action]"
12                 return 2 # usage errors return 2 by default
13                 ;;
14         esac
15    }

Just trying to wrap my head around the control sequence and where can I read more about them in relation to ksh?

@krader1961
Copy link
Contributor

This seems to work, it clears the terminal buffer but then it freeses

No, it simply doesn't redraw the command line. You really do not want to replace the [ctrl-L] with [ctrl-M] as that causes the current command line, if any, to be executed. Type echo WTF, do not press [enter], then press [ctrl-L]. Notice that the echo is executed. Now imagine if you had some other partially entered command. Perhaps involving a command like rm with unwanted side-effects.

Just trying to wrap my head around the control sequence and where can I read more about them in relation to ksh?

I don't understand the question.

At the end of the day I can't really recommend ksh as an interactive shell. Other shells, like fish, have far saner out-of-the-box interactive behavior. And make it far easier to customize key bindings. Not to mention providing a way to force the command line to be redrawn. For example, fish's [ctrl-L] binding does commandline -f repaint after running clear.

@teamblubee
Copy link
Author

This seems to work, it clears the terminal buffer but then it freeses

No, it simply doesn't redraw the command line. You really do not want to replace the [ctrl-L] with [ctrl-M] as that causes the current command line, if any, to be executed. Type echo WTF, do not press [enter], then press [ctrl-L]. Notice that the echo is executed. Now imagine if you had some other partially entered command. Perhaps involving a command like rm with unwanted side-effects.

Just trying to wrap my head around the control sequence and where can I read more about them in relation to ksh?

I don't understand the question.

At the end of the day I can't really recommend ksh as an interactive shell. Other shells, like fish, have far saner out-of-the-box interactive behavior. And make it far easier to customize key bindings. Not to mention providing a way to force the command line to be redrawn. For example, fish's [ctrl-L] binding does commandline -f repaint after running clear.

Oh, I see that CRTL+M and the script above

trap $'[[ ${.sh.edchar} == "\cl" ]] && { .sh.edchar="\cm"; clear; }' KEYBD

does seem to clear the buffer but it also executes unfinished commands.

The reason why I settled on KSH is are;
it's POSIX compliant shell
it doesn't do a lot of forking in the background
it's lightweight for the features that it provides
it's easier to program compared to C shells and it's more light weight than BASH shells

If there are any lightweight posix shells I could start digging there.

I didn't realize that CTRL+M will clear the screen but also execute the partial commands and like you accurately point out, that is undesired behavior.

I think if I could bind CTRL+L to the command clear that would be sufficient.

I don't mind digging in deep to learn these things it's just a bit challenging to find where to start looking sometimes.

@dannyweldon
Copy link

The reason why I settled on KSH is are;
it's POSIX compliant shell
it doesn't do a lot of forking in the background
it's lightweight for the features that it provides
it's easier to program compared to C shells and it's more light weight than BASH shells

+1

I think I now understand the problem. The bash behaviour for CTRL+L is to clear the screen and redraw the prompt and command line state, whereas ksh just advances a line and reprints the current line:

^L        Line feed and print current line.

Personally, I prefer the ksh behaviour as I don't like it when shells clear the screen as I lose the record of what I was doing on the terminal.

However, ksh does have ALT-CTRL-L which clears the screen and reprints the command line:

M-^L      Clear the screen.

which is closer to the bash behaviour. One problem is that ALT-CTRL-L on my computer locks my screen, so to test, I had to use the Linux console.

After further investigation, I discovered that it is possible to map the CTRL-L to ALT-CTRL-L and it then works similar to bash:

trap $'[[ ${.sh.edchar} == "\cL" ]] && { .sh.edchar="\e\cL"; }' KEYBD

One problem/idiosyncrasy I noticed was that it is only reprinting the last line of a multi-line prompt (which is what I use), which might be fine for CTRL-L but doesn't seem quite right for ALT-CTRL-L. Try doing it with this:

PS1=$'$USER@$HOST $PWD\cj$ '

I don't mind digging in deep to learn these things it's just a bit challenging to find where to start looking sometimes.

I think this issue has highlighted the lack of freely available online documentation to augment the ksh man page.

@teamblubee
Copy link
Author

teamblubee commented Nov 18, 2018

The reason why I settled on KSH is are;
it's POSIX compliant shell
it doesn't do a lot of forking in the background
it's lightweight for the features that it provides
it's easier to program compared to C shells and it's more light weight than BASH shells

+1

I think I now understand the problem. The bash behaviour for CTRL+L is to clear the screen and redraw the prompt and command line state, whereas ksh just advances a line and reprints the current line:

^L        Line feed and print current line.

Personally, I prefer the ksh behaviour as I don't like it when shells clear the screen as I lose the record of what I was doing on the terminal.

However, ksh does have ALT-CTRL-L which clears the screen and reprints the command line:

M-^L      Clear the screen.

which is closer to the bash behaviour. One problem is that ALT-CTRL-L on my computer locks my screen, so to test, I had to use the Linux console.

After further investigation, I discovered that it is possible to map the CTRL-L to ALT-CTRL-L and it then works similar to bash:

trap $'[[ ${.sh.edchar} == "\cL" ]] && { .sh.edchar="\e\cL"; }' KEYBD

One problem/idiosyncrasy I noticed was that it is only reprinting the last line of a multi-line prompt (which is what I use), which might be fine for CTRL-L but doesn't seem quite right for ALT-CTRL-L. Try doing it with this:

PS1=$'$USER@$HOST $PWD\cj$ '

I don't mind digging in deep to learn these things it's just a bit challenging to find where to start looking sometimes.

I think this issue has highlighted the lack of freely available online documentation to augment the ksh man page.

I will just get use to the current behavior.

Addtional documentation would be greatly appreciated.
I can tell KSH has a lot of advanced features but digging through the code to understand it all isn't practical right now.

[EDIT]
are there any addtional docs condensed somewhere?

@krader1961
Copy link
Contributor

are there any addtional docs condensed somewhere?

Not that I'm aware of. There is the classic book "The New Kornshell" but the documentation in the project has many problems. Not least because the source for the ksh "man" page has very different text for builtin commands than you will see if you run a_bltin_cmd --help. See issue #507.

@dannyweldon
Copy link

@teamblubee, did you try my last suggestion, which should make Ctrl-L clear the screen and refresh the prompt like you are after:

trap $'[[ ${.sh.edchar} == "\cL" ]] && { .sh.edchar="\e\cL"; }' KEYBD

@teamblubee
Copy link
Author

@teamblubee, did you try my last suggestion, which should make Ctrl-L clear the screen and refresh the prompt like you are after:

trap $'[[ ${.sh.edchar} == "\cL" ]] && { .sh.edchar="\e\cL"; }' KEYBD

I did try this and it creates quite a few bugs.
It doesn't work as expected; instead of clearing the entire terminal screen; it just clears the line and puts the cursor at the start but there's a bug in this because it seems like it partially executes a command as @krader1961 explained above.

I am not sure if I am unclear in the description of what I am trying to do.

ksh has a command 'clear' which clears out everything written to the terminal and puts the cursor at the top of the terminal window.

I'd just like to map that to the key sequence 'CTRL + L'
It might be possible, I am just not sure how to enable it.

@krader1961
Copy link
Contributor

krader1961 commented Nov 26, 2018

@teamblubee I'm betting you're using vi mode. The [meta][ctrl-L] ("\e\cL") sequence @dannyweldon mentioned only works in emacs mode. And, in fact, it does exactly what you want. And you can even remap that to a simple [ctrl-L] using the keyboard trap he suggested:

trap $'[[ ${.sh.edchar} == "\cL" ]] && { .sh.edchar="\e\cL"; }' KEYBD

But that keyboard trap doesn't work in vi mode because vi mode doesn't support that behavior. After looking at the code some more I don't think there is any way to get it to work the way you want. You'll need to set -o emacs if you want this to work.

If you look at the code in the src/cmd/ksh93/edit directory (the emacs.c and vi.c modules being relevant to this issue) I hope you'll agree with me that, like much of the ksh code, it needs to be refactored. And much of the functionality, such as forcing the command line to be redrawn, abstracted into functions that can be invoked from a ksh script by name rather than inserting magic, hard coded, sequences of bytes into the input stream.

P.S., Not that it really matters but FWIW the clear command is not a ksh builtin. It is an external command provided by the ncurses package in your distro. Run type clear to see which external command it runs (most likely /usr/bin/clear).

@krader1961
Copy link
Contributor

As I mentioned earlier, despite having used ksh88 then ksh93 as my primary interactive shell for ~15 years, I would never recommend using ksh as an interactive shell given the far friendlier alternatives like fish. On the other hand I wouldn't recommend using fish for scripting other than for making its interactive behavior do what you want. I'd love for ksh to be the best scripting shell that has good backward compatibility with scripts written 30+ years ago while simultaneously being a great interactive shell. But that isn't going to happen unless a large number of people who care about improving the situation begin contributing changes to the project.

@teamblubee
Copy link
Author

As I mentioned earlier, despite having used ksh88 then ksh93 as my primary interactive shell for ~15 years, I would never recommend using ksh as an interactive shell given the far friendlier alternatives like fish. On the other hand I wouldn't recommend using fish for scripting other than for making its interactive behavior do what you want. I'd love for ksh to be the best scripting shell that has good backward compatibility with scripts written 30+ years ago while simultaneously being a great interactive shell. But that isn't going to happen unless a large number of people who care about improving the situation begin contributing changes to the project.

I like this attitude.

Since you have all the experience with ksh and know the interactive shells. I never liked shells like zsh, they seem to do too much, I used csh, tcsh which are decently interactive but I cannot wrap my head around their parsing.

From your experienced perspective, what's the list of features/ todo that needs to get implemented?

p.s.
clear is provided /usr/bin/clear so yes, it's not built into ksh.

@krader1961
Copy link
Contributor

I switched from ksh to zsh after a job change then used zsh for ~5 years. Then abandoned zsh after encountering this issue. The main problem with zsh is that there don't appear to be any competent software engineers managing its evolution. Which means that every random feature someone wants implemented appears to get included.

From your experienced perspective, what's the list of features/ todo that needs to get implemented?

There are quite a few. In terms of enhancements perhaps the most important one is to extricate the documentation for builtin commands from the C source code. So that the man ksh output shows the same text for a builtin command as running a_bltin_cmd --help shows. Not to mention making it possible to produce a web friendly version of the documentation. See issue #507. That means a close second in priority is switching from the awful AST optget() code that implements a DocOpt style API to a simpler, easier to prove correct, use of the traditional getopt_long() interface.

With respect to bugs I would say that fixing all the use-after-free and similar failures when running the code under Valgrind, ASAN, or a debug malloc is critical. Memory leaks are a problem but rarely as serious as the use-after-free type of bug which can result in using incorrect data.

@teamblubee
Copy link
Author

teamblubee commented Nov 26, 2018

I switched from ksh to zsh after a job change then used zsh for ~5 years. Then abandoned zsh after encountering this issue. The main problem with zsh is that there don't appear to be any competent software engineers managing its evolution. Which means that every random feature someone wants implemented appears to get included.

From your experienced perspective, what's the list of features/ todo that needs to get implemented?

There are quite a few. In terms of enhancements perhaps the most important one is to extricate the documentation for builtin commands from the C source code. So that the man ksh output shows the same text for a builtin command as running a_bltin_cmd --help shows. Not to mention making it possible to produce a web friendly version of the documentation. See issue #507. That means a close second in priority is switching from the awful AST optget() code that implements a DocOpt style API to a simpler, easier to prove correct, use of the traditional getopt_long() interface.

With respect to bugs I would say that fixing all the use-after-free and similar failures when running the code under Valgrind, ASAN, or a debug malloc is critical. Memory leaks are a problem but rarely as serious as the use-after-free type of bug which can result in using incorrect data.

I had a feeling zsh was run like this, just from looking at all the "cool stuff it can do" I don't need core features to be cool, a solid core w/ addons would be more maintainable; in my opinion. But telling that to an established community will most likely get shouted out the room so why bother.

Okay, so it seems like we have somewhere to start and a few goals to shoot for.
I think running the code through a symbolic execution engine would highlight a lot of the bugs that should pick up more issues than valgrind.

Moving to a modern version of C17 would also highlight a lot of bugs in the code. Also please not C and not C++

I am sure just compiling the code with std=c17 would throw a lot of warning, possibly errors.

If you want to setup a wiki page and we can set some official todo's then the issues can get fixed in a way that shows progression.

@krader1961
Copy link
Contributor

Moving to a modern version of C17...

We can't mandate that as it needs to be possible to build ksh on much older platforms. We did, as a stepping stone, decide to require the C99 standard as a minimum. See issue #145. Also, mandating a newer compiler version isn't really needed to detect problems with the code. Tools like cppcheck, oclint, and Coverity Scan (all of which are currently enabled) detects a large number of potential problems.

If you want to setup a wiki page and we can set some official todo's then the issues can get fixed in a way that shows progression.

No need for anything like a wiki page. When I find problems with the code, whether or not identified by the aforementioned tools, I open a Github issue. That is what I would expect anyone interested in improving the code to do.

@dannyweldon
Copy link

Ah. vi mode. I used to use vi mode for many years before making the switch to emacs mode a number of years ago and never looked back. And, of course, I use the emacs navigation keys rather than just the arrow keys, as many newbies do.

And I'm not sure if I am missing something, but this is working for me in vi mode:

trap $'[[ ${.sh.edchar} == "\cL" ]] && { clear; }' KEYBD

Or, even printing clear's ansi escape sequence directly:

trap $'[[ ${.sh.edchar} == "\cL" ]] && { print -n "\e[3;J\e[H\e[2J"; }' KEYBD

@teamblubee, can you try one of those in vi mode and let us know how it goes?

@krader1961
Copy link
Contributor

krader1961 commented Nov 26, 2018

@dannyweldon Your solution doesn't work because it doesn't cause the command line to be refreshed after the external clear command is run. Or the equivalent explicit printing of the clear screen sequence. So it seems likely you are not really validating the behavior you think you are. What does set -o show with respect to emacs or vi mode being "on"?

@dannyweldon
Copy link

Yep. I double-checked and I'm definitely in vi mode and it's working perfectly for me. It's clearing the screen, redrawing the command line, including keeping the cursor in the exact same character position.

I was using the system standard ksh so I rechecked against an obs built ksh (not the latest) and it's the same. Perhaps there is another setting that is affecting this?

@krader1961
Copy link
Contributor

@dannyweldon Doing

trap $'[[ ${.sh.edchar} == "\cL" ]] && { clear; }' KEYBD

doesn't really work like the O.P. wants. Or that I would want. Yes, it runs clear when [ctrl-L] is seen. But it doesn't redraw the command line. Furthermore, it inserts the [ctrl-L] in the command line. Whereas doing

trap $'[[ ${.sh.edchar} == "\cL" ]] && { .sh.edchar="\e\cL"; }' KEYBD

when in emacs mode does exhibit the desired behavior. But in vi mode it just redraws the current command line without clearing the screen.

@dannyweldon
Copy link

@krader1961, I was wondering if the problems you were seeing was because you were in insert mode. This new version now seems to behave the same as bash whether in emacs mode or vi mode. Note that pressing [ctrl-L] in bash when in vi-insert mode simply inserts the [ctrl-L] and does not clear the screen.

trap $'[[ ${.sh.edchar} == "\cL" ]] && ! [[ ${.sh.edmode} == "\e" ]] && { print -n "\e[3;J\e[H\e[2J"; }' KEYBD

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants