microsoft / vscode

Visual Studio Code
https://code.visualstudio.com
MIT License
163.18k stars 28.84k forks source link

Bug: remap cmd+j to ctrl+j in vscode's terminal doesn't work? #210121

Closed tmpm697 closed 5 months ago

tmpm697 commented 5 months ago

Does this issue occur when all extensions are disabled?: Yes/No

Steps to Reproduce:

  1. have this map in your keybindings.json
    {
        "key": "cmd+j",
        "command": "workbench.action.terminal.sendSequence",
        "when": "terminalFocus",
        "args": {
          "text": "\u000A"
        }
    }
  2. comment all cmd+j or ctrl+j in your keybindings.json to have it default
  3. try git checkout com<cmd+j> and git checkout com<ctrl+j>
  4. git checkout com<ctrl+j> works, git checkout com<cmd+j> does not work.
  5. try another binding:
    {
        "key": "cmd+j",
        "command": "workbench.action.terminal.sendSequence",
        "when": "terminalFocus",
        "args": {
          "text": "\u0014"
        }
    },
  6. try shell prompt> <cmd+j> --> works.

git checkout com<cmd+j> does not work and return error: error: pathspec 'com' did not match any file(s) known to git

in this case, i'm trying to trigger cmd+j only when the 1st command is git

tmpm697 commented 5 months ago

@meganrogge : any reason why close this? u think this from zsh shell, not from vscode?

meganrogge commented 5 months ago

Yes @tmpm697

tmpm697 commented 5 months ago

Yes @tmpm697

can u give more details on this? If it's from zsh, I don't know how to change it, zle is simple like this:

_test_git_zle() {
  IFS=' ' read -A LBUF <<< "$LBUFFER"
  cmd=$LBUF[1]
  if [[ $cmd == "git" ]]; then
    echo "1\n2" | fzf
  fi
}
zle     -N   _test_git_zle
bindkey '^j' _test_git_zle
meganrogge commented 5 months ago

I am not a zsh expert, so wouldn't know how to do that.

tmpm697 commented 5 months ago

Thanks meganrogge, I reached out zsh experts in IRC and have some further checks on this:

    {
        "key": "cmd+j",
        "command": "workbench.action.terminal.sendSequence",
        "when": "terminalFocus",
        "args": {
          "text": "\uhello"
        }
    },

vscode will print out hello when trigger cmd+j which is expected.

but there's something wrong here, as u can see in this link: https://github.com/xtermjs/xterm.js/blob/master/src/common/data/EscapeSequences.ts#L31

vscode can send either ctrl+j or \n with '\x0a' --> probably with it sent \n instead of what i'm expecting ctrl+j

and indeed, when i press cmd+j when map to \u000A as in 1st post, it will always try to enter that line right away.

sending \u0014 is correct as in 1st post, due to it only has one map to ctrl+t: https://github.com/xtermjs/xterm.js/blob/master/src/common/data/EscapeSequences.ts#L51

@meganrogge do u now think it's related to vscode for sending wrong code? if yes can u re-open this issue?

Note that i use ctrl+j as it is in zsh config and do not re-map it to anhything else

meganrogge commented 5 months ago

Thanks for the info, @Tyriar will be better able to answer this.

My guess is this is by design.

tmpm697 commented 5 months ago

It should default to ^J instead, because most terminals will treat ^J like \n but if default to \n then how can I use shell to capture that code?

Tyriar commented 5 months ago

I'm not sure I understand, ^J is ctrl+j which is \n which is \x0a which is \u000a. Am I missing something?

jerch commented 5 months ago

I also dont get it - can you explain, what you'd expect it to send on ^J, and what you get instead?

You write:

It should default to ^J instead, because most terminals will treat ^J like \n but if default to \n then how can I use shell to capture that code?

As @Tyriar wrote, ^J is equal to \n (or 0x0a as byte), because J is the letter at position 10 in the ascii control code mapping (also see https://en.wikipedia.org/wiki/C0_and_C1_control_codes). The shell does now the following - it has several default actions bound to certain (control) bytes of the input stream (sidenote - most shells read the terminal in raw mode, thus get bytes immediately and skip termios translation). At that TTY input level ^J cannot be distinguished from \n, as it is the same control byte or literally the same input. To still separate the inputs at shell input, you'd have to use a different control byte or byte sequence, but this depends on the shell and whether it allows to remap the action you are looking for to different control bytes.

tmpm697 commented 5 months ago

i've discussed with zsh folks in IRC, not really capture 100% what they meant but seems to be zsh issue when zle function treat ^J same as \n because of same code but it should distinguish them somehow, link: https://www.zsh.org/mla/workers/2024/msg00453.html

jerch commented 5 months ago

I think the issue is not related to your issue, because you never leave the shell prompt as active foreground app (well during your input). Note the difference - in the issue you have linked, the xxd command is run interactively as foreground app, and within that the output of Ctrl-J, Ctrl-M, Return is tracked. That only shows if the termios settings are correctly applied to a subcommand (as stated by the zsh docs, the STTY var will be applied to stty for subcommands).

tmpm697 commented 5 months ago

Sorry that I don't understand much for what codes actually doing.

because you never leave the shell prompt as active foreground app

I used zle function to call function that will call fzf program for user to interact with, so it's still consider as not leaving the shell prompt? (fzf will also halt input for user to select)

but my result also different from his result:

~/s/nix ? STTY={-icanon,-echo} xxd -ps -l3
0a0a
~/s/nix ? STTY={-icanon,-echo,icrnl,inlcr} xxd -ps -l3
0d0d0d
~/s/nix ? STTY={-icanon,-echo} xxd -ps -l3
0a0a0a
~/s/nix ? 

this is result of pressing ctrl+j in vsocde's terminal

it's like a mystery for me to chase this issue as i'm not familiar with. i might give it up.

but for the last, what do u think problem here from? i just want to map cmd+j to ctrl+j which is an zsh's zle function.

jerch commented 5 months ago

I used zle function to call function that will call fzf program for user to interact with, so it's still consider as not leaving the shell prompt? (fzf will also halt input for user to select)

Yes, if a program "halts" for user input, than it runs in foreground. But that was not obvious from your description above (you only wrote about git checkout ..., which does not halt for additional input). And your output for the STTY stuff looks fine to me, it correctly resets in 3rd run (thats what the issue is about - there the 3rd run still gives 0x0d for ^J, but should be 0x0a again as in the first run, which indicates that INLCR gets not properly reset). So nope, that not your issue.

i just want to map cmd+j to ctrl+j which is an zsh's zle function.

Well - then just map cmd+j to output \n (NL) on keypress on xterm.js side? Though I'm not sure, if NL is really what you want, most POSIX installations would send \r (CR == ^M) to trigger a "newline action" from PTY master side, as ICRNL will be active in termios translating CR --> NL. (I also dont know what a zle function is...)

I admit that the terminal/TTY stuff is really confusing and has many ancient interface concepts. The problem here is that the TE (xterm.js) has keymapping support as well as the shell, and you'd have to bisect your issue to one of them. And if in doubt, you will have to track the bytes through the TE --> PTY --> shell stack (at the arrows).

tmpm697 commented 5 months ago

thanks @jerch, there's must be a fix from either vscode or zsh for this particular ctrl+j with code 0a correctly treated as ctrl+j under zsh.

zsh did not receive it as ctrl+j probably as vscode sent the correct code, but ppl from zsh in IRC insist on not understand the issue, i think i need to elaborate more but i gave up.

instead straight mapping ctrl+j to cmd+j, i re-map everything that need to use up ctrl+j to other mapping in zsh that zsh will understand if vscode sends it the code.