How are keyboard inputs transmitted through Windows terminal and WSL2 - emacs

I'm using WSL (WSL2 in fact) for a few days and I'm a bit confuse about keyboard combination redirection and/or interception.
I have to say that my favorite tool to edit text is emacs, that use a lot of keyword combinations.
My stack is this one :
Under windows, I run Windows terminal (WT.exe)
In this terminal I open a tab which (afaik magically) is a shell on a linux subsystem. This subsystem is a Debian
In this shell I run emacs.
The whole thing looks like that :
At this point, when I enter a key combination, it may be passed to emacs, or it may be intercepted/interpreted by any other layer of the stack.
For instance :
ctrl-e is interpreted by emacs : it move the caret to the end of file
ctrl-z is interpreted by wsl2 (afaik) : it pauses emacs as a background job
ctrl-tab is interpreted by windows terminal : it changes tab
alt-f4 is interpreted by windows : it closes windows terminal
And some of them are a mystery for me. For instance ctrl-_ (ctrl underscore), which is very useful on default emacs configuration, is not interpreted by emacs. It is translated at some point as "delete". In this configuration typing "delete" or "ctrl-_" is the very same thing : it removes the first character before the caret.
Very specifically and synthetically stated, my question would be : "How do I configure this stack to make emacs receive ctrl-_ ?"
Of course more generally, I really would like to know, how each layer works and is parameterized. For instance, is there a configuration file for WT and/or WSL2, that says which combination are forwarded, which one are interpreted and which one are ignored.
Additional Data :
If I run WSL2 through WT, but don't start emacs, ctrl-_ is also interpreted as "delete"
If I run another type of "tab" in WT, like cmd.exe or powershell, ctrl-_ is not interpreted as "delete". I don't know if it is interpreted at all
If I run WSL2 without WT, Ctrl-_ is not interpreted at all.
I didn't found any mention of ctrl-_ in WT config shortcuts.
Additional Data :
I tried to run following python program in both stacks : WT/WSL and WT/CMD.EXE
import keyboard
while(True):
c = keyboard.read_key()
print(c)
On WT/CMD.EXE, the output is :
ctrl
_
_
ctrl
Which means it receive the keyboard info.
On WT/WSL2, it fails to run. Apparently "keyboard" is not compatible with WSL2. But this is not the question here.

A useful tool for debugging input on linux is showkey -a. You can use that to help debug what keys the client application is receiving. If you press a key and it doesn't display anything in the output, then there's a good chance it's being intercepted by the Terminal.
There's probably an open issue for this on the Terminal repo. #8458 looks similar.
is there a configuration file for WT and/or WSL2, that says which combination are forwarded, which one are interpreted and which one are ignored.
defaults.json is the file that contains all the keybindings that the Windows Terminal binds by default.
What WSL does is more complicated. IIRC, the inputrc file on linux is used readline (for bash, etc) to specify how certain keys are handled by the shell. But then there are also keys configured in stty that control other parts of the TTY interface. That part I understand less.
alt-f4 is interpreted by windows : it closes windows terminal
FWIW That's actually a application-level keybinding. The Terminal handles that key manually, so that people can optionally disable it. See defaults.json

Related

Can a command be send to VSCode, directly from the integrated terminal (integrated terminal -> VSCode API)?

Hi I would like to know whether one could call a VSCode command from the integrated terminal. So basically is the terminal aware of VSCode and can they communicate (at least from terminal => VSCode)
My Usecase: I would like to have H and L, to move to editor tab left / right of the terminal (I am using the terminal in an editor tab). Additionally, I would like that to happen when I am in vim normal mode in my zsh.
So I would like, when I am in normal mode and press H that the terminal sends an editor.tabNext (or whatever the command is) to VSCode.
I think I found a workaround at least. There is an extension called Remote Control (https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-remote-control&ssr=false#review-details), with which I can send arbitrary commands to VSCode, so this seems to work. Proabably natively this is not possible, but maybe someone knows something.
EDIT1:
Here is my setup now:
if [ ! -z $VSCODE ]; then
_sendcmd() { echo "{ \"command\": \"workbench.action.$1\" }" | websocat ws://localhost:4242 }
# define commands and register them in zsh
editor_left() { _sendcmd previousEditor }; zle -N editor_left;
editor_right() { _sendcmd nextEditor }; zle -N editor_right;
bindkey -a H editor_left
bindkey -a L editor_right
fi
where $VSCODE is defined in VSCode through terminal.integrated.env = { "VSCODE": "1" }. May this make you as happy as it makes me happy.
Yes you can do this but it will require writing a simple extension and there is one flaw with it.
If you write a VSCode extension there is an API that will set environment variables in new terminals.
export function activate(context: ExtensionContext) {
...
context.environmentVariableCollection.replace("REMOTE_CONTROLL_EXTENSION_IPC_PATH", ipcPath);
What you do is create a Unix socket (or named pipe on Windows) and pass it into that environment variable. Then in your shell you can just send data to that socket.
This is much better than the approach used by the Remote Control extension that Dimfred mentioned - it's more secure and doesn't rely on known fixed ports, which means you're going to run into issues e.g. if they don't get closed properly or you run multiple copies of VSCode.
The only downside I've found with this approach is that if restart VSCode, then the socket will be closed but VSCode will try to restore shell sessions. Those shell sessions will be left with an old REMOTE_CONTROLL_EXTENSION_IPC_PATH value which points at a non-existent socket.
I'm not sure of a way around that yet.
Edit: Actually all you need to do is ensure your Unix socket path is tied to a workspace. At least this is what the built in Git extension does. See createIPCServer(). It uses storagePath which is specific to a workspace. It also unlinks the socket at that path (if any) when it starts.
I initially thought that might cause issues if you open the same workspace twice in two windows, but VSCode won't let you do that.

How to clear the entire terminal (PowerShell)

I had an issue. Using the clear or cls command in powershell clears only the visible portion of the terminal,I would like to know how to clear the entire terminal?
I use VSCode by the way.
To also clear the scrollback buffer, not just the visible portion of the terminal in Visual Studio Code's integrated terminal, use one of the following methods:
Use the command palette:
Press Ctrl+Shift+P and type tclear to match the Terminal: Clear command and press Enter
Use the integrated terminal's context menu:
Right-click in the terminal and select Clear from the context menu.
On Windows, you may have to enable the integrated terminal's context menu first, given that by default right-clicking pastes text from the clipboard:
Open the settings (Ctrl+,) and change setting terminal.integrated.rightClickBehavior to either default or selectWord (the latter selects the word under the cursor before showing the context menu).
Use a keyboard shortcut from inside the integrated terminal (current as of v1.71 of VSCode):
On macOS, a shortcut exists by default: Cmd+K
On Linux and Windows, you can define an analogous custom key binding, Ctrl+K, as follows, by directly editing file keybindings.json (command Preferences: Open Keyboard Shortcuts (JSON) from the command palette), and placing the following object inside the existing array ([ ... ]):
{
"key": "ctrl+k",
"command": "workbench.action.terminal.clear",
"when": "terminalFocus && terminalHasBeenCreated || terminalFocus && terminalProcessSupported"
}
Using a command you can invoke from a shell in the integrated terminal:
Note: A truly cross-platform solution would require executing the VSCode-internal workbench.action.terminal.clear command from a shell, but I don't know how to do that / if it is possible at all - do tell us if you know.
Linux (at least as observed on Ubuntu):
Use the standard clear utility (/usr/bin/clear), which also clears the scrollback buffer.
From PowerShell, you may also use Clear-Host or its built-in alias, cls.
By contrast, [Console]::Clear() does NOT clear the scrollback buffer and clear just one screenful.
macOS:
Unfortunately, neither /usr/bin/clear nor PowerShell's Clear-Host (cls) nor .NET's [Console]::Clear() clear the scrollback buffer - they all clear just one screenful.
Print the following ANSI control sequence: '\e[2J\e[3J\e[H' (\e represents the ESC char. (0x1b, 27); e.g., from bash: printf '\e[2J\e[3J\e[H'; from PowerShell: "`e[2J`e[3J`e[H"
You can easily wrap this call in a shell script for use from any shell: create a file named, say, cclear, in a directory listed in your system's PATH variable, then make it executable with chmod a+x; then save the following content to it:
#!/bin/bash
# Clears the terminal screen *and the scrollback buffer*.
# (Needed only on macOS, where /usr/bin/clear doesn't do the latter.)
printf '\e[2J\e[3J\e[H'
Windows:
NO solution that I'm aware of: cmd.exe's internal cls command and PowerShell's internal Clear-Host command clear only one screenful in the integrated terminal (not also the scrollback buffer - even though they also do the latter in a regular console window and in Windows Terminal).
Unfortunately, the escape sequence that works on macOS ("`e[2J`e[3J`e[H" or, for Windows PowerShell, "$([char]27)[2J$([char]27)[3J$([char]27)[H") is not effective: on Windows it just clears one screenful.
(By contrast, all of these methods do also clear the scrollback buffer in regular console windows and Windows Terminal.)
right click on the powershell button,
then select clear,
when you are at the command window, type "clear" command, to clear the terminal window.

emacs CUA-mode OK in X but not working console

I have been struggling to get emacs CUA-mode working in the console where it works fine in X (in an xterm using emacs -nw).
The main features I want are shift-movement selection and ^C,^V,^X but any more would be helpful. So far I am using a local keyboard and monitor but ultimate goal is to get all this working over ssh using something like putty.
So far I have been working on getting my terminal to output the same control sequences that emacs expects to see for shift-movement (specifically S-<down> or <S-down>) and this works in the console when shift-select-mode is set in .emacs. So far so good, so I can shift select text from any emacs invocation type: X-windows, terminal ($TERM=linux or $TERM=xterm). This site has been very useful.
Shift-<down> displays as follows using cat -v and emacs happily maps <S-down> to this control sequence.
cat -v (then type shift-down)
^[O2B
Here's the problem, switch on cua-mode and the shift-selection stops working but ^C,^V and ^X starts working !? So I can either have shift-selection or ^C,^V,^X but not both at the same time.
-------
Setup:
emacs 23
Raspberry Pi
Raspian (similar to Debian)
You might like to try a more recent version of Emacs, since cua-mode has been slowly changed to use the same code as shift-select-mode. In Emacs-24.4, cua-mode actually uses shift-select-mode directly.

What is the best way to open remote files with emacs and ssh

I connect to the remote machine with ssh user#192.168.1.5. When I need to open a file in the remote machine I do, e.g.,
emacs /usr/share/nginx/html/index.html
and that opens the index.html file in the shell. I noticed that some emacs commands work but others do not work. For instance, C-w does not work; M-< does not work. How can I fix this, and what is the best way to work with emacs and ssh?
I found this question but it made me more confused.
I generally prefer opening remote files from a local Emacs instance.
While running Emacs on your local machine, opening a remote file over ssh is not much different than opening any other file besides a slightly different syntax.
For ssh, you can type C-x C-f. Now, in the minubuffer you want to type /ssh:user#host:/path/to/file (Note that tab completion will work once you start typing a path. Also note the leading / character). See the full docs here.
In your example, that would be:
C-x C-f /ssh:user#192.168.1.5:/usr/share/nginx/html/index.html
Now you can edit remote files over ssh in Emacs while using your local configuration and any installed packages, etc...
Just to add to the answer above, you can write shortcuts for machines that you use
frequently:
(defun connect-remote ()
(interactive)
(dired "/user#192.168.1.5:/"))
This will open a dired buffer on a remote machine. You can navigate this buffer
as you would a local one.
If you have set up ssh keys for the remote machine, you don't even have to enter the password.
If you have a bunch of remote machines, you can give some recognizable name
to each function, e.g. connect-cupcake, connect-kitkat and use smex package for completion.
And to add to #abo-abo's post about "shortcuts" --
Use Emacs bookmarks. Just create bookmarks normally, when you visit a remote file or directory. Then just use C-x r b to jump to a remote bookmark, whose name you provide (with completion).
If you use Bookmark+ then remote bookmarks are highlighted specially in the *Bookmark List*, so you can recognize them more easily. And remote bookmarks that must be accessed by su or sudo (root) are highlighted differently.
If you use Dired+ then you can also quickly bookmark multiple remote files or directories, by visiting their containing remote directory in Dired, marking them, and hitting C-x b. No need to give the bookmarks names; they are named after the files. Even if you never use those bookmarks for navigating to the remote files, you can use them with Bookmark+ tags to organize the files and thus operate on subsets of them.
If you use Icicles then whenever you use a command to jump to a bookmark, you can narrow the completion candidates to those that are remote by hitting C-M-# during completion.
The original poster expressed interest in opening remote files as the root user. This can be done with the command:
C-x C-f /ssh:you#remotehost|sudo:remotehost:/path/to/file RET
More documentation can be found here: https://www.emacswiki.org/emacs/TrampMode#toc14
A Simple Answer that focuses on the remote machine:
If I plan to do all my emacs work on the remote machine, I use
ssh -X username#hostname
and then run emacs in the remote session, displaying back on my local machine. It's an old question but I wanted to throw this in for completeness. Granted there are some xhost / X config issues but in many networks this will work right off the bat!
SSH mode for emacs is what you're looking for.
Once you have it set up you just run
M-x ssh RET hostname RET
Then it prompts you for your password twice (once for the command line, once for loading files).
For the most part you can treat it like any other shell (non-interactive and a few minor differences, but that's it).
It keeps track of which directory you're in, so when you want to open a file from the directory you're looking at it automatically starts in the right directory and you just need to enter in the file name.
Emacs Wiki has more info too.

Why can't I set mark on emacs?

I am trying to run Emacs on a guest system (Ubuntu 12.04)
inside a Windows 8 command console.
This is done through vagrant + Virtual box.
It looks like this,
Guest system : Ubuntu 12.04
Host system : Windows 8
Environment : Virtual Box + Vagrant
Emacs works fine except I can not set mark on text (C-SPC).
I supposed there is some kind conflict with the key binding.
So I tried and added this to init.el,
(global-set-key (kbd "C-8") 'set-mark-command)
It doesn't work either.
Would someone please advice me?
If you're running Emacs in a terminal, then C-SPC and C-8 are unlikely to send what you want them to.
Use C-h c C-SPC to ask Emacs what it's seeing.
Try using C-# instead.
FYI, I found the Cygwin X server excellent for running GUI Emacs in a Windows-hosted VM. With GUI Emacs you can use many more key combinations than a terminal emulator will permit.
If you try that, start the server with startxwin rather than startx (the former enables the X server's windows to appear as regular Windows windows), and then connect from an xterm with ssh -X or ssh -Y (see the man page) to connect with X11 forwarding.
Thanks a lot for the hint.
I finally figured out what the problem really is.
From the answer #phils, this link clarifies the problem.
Why does Ctrl+. not work when I bind it to a command in Emacs?
In my case, all C-[any other key] bindings work, except for the set-mark-command.
I found a workaround.
By adding the following code to .emacs.d\init.el I can use F8 to set mark now.
(global-set-key [f8] (quote set-mark-command))
It is still confusing why in other command, Ctrl+key bindings works.