Allow a key sequence binding like `j,k` to not prevent user from insert `j` · Issue #1701 · PowerShell/PSReadLine · GitHub
Skip to content

Allow a key sequence binding like j,k to not prevent user from insert j #1701

Open

Description

@dhatch48

Description of the new feature/enhancement

You can bind a key sequence like j,k to a function or script block by Set-PSReadLineKeyHandler -Key 'j,k' -ViMode Insert -Function ViCommandMode, but a user cannot insert a character j after that.

Proposed technical implementation details (optional)

Quoted from #1701 (comment)

In vim, jk or jj to exit insert mode is super common. Usually it works like this:

  1. You press j
  2. j is written to the buffer
  3. A timer starts
  4. If you press the next key in the sequence before the timer is up, the j is removed from the buffer and the command fires
  5. If the key you press next isn't in the sequence, or the timer is up then it's treated like a literal

Activity

  1. ghost added
    Needs-Triage 🔍It's a new issue that core contributor team needs to triage.
    on Jul 26, 2020
  2. daxian-dbw commented on Jul 26, 2020

    @daxian-dbw
    Member

    David Hatch (@dhatch48) You can do that by Set-PSReadLineKeyHandler -Key 'j,k' -ViMode Insert -Function ViCommandMode

  3. SeeminglyScience commented on Jul 26, 2020

    @SeeminglyScience
    Collaborator

    Dongbo Wang (@daxian-dbw) I can't get that to work right. I mean it works, but you can't type j anymore.

  4. daxian-dbw commented on Jul 26, 2020

    @daxian-dbw
    Member

    Yeah, j will be bound after that and won't be treated as a literal character. I think that's just an example. Key sequences should be chosen to not interfere with the regular input, like Set-PSReadlineKeyHandler -Chord 'Ctrl+d,Ctrl+c' -Function CaptureScreen

  5. SeeminglyScience commented on Jul 26, 2020

    @SeeminglyScience
    Collaborator

    Yeah, j will be bound after that and won't be treated as a literal character. I think that's just an example.

    Nah jk or jj (what I use) to exit insert mode is super common. Usually it works like this:

    1. You press j
    2. j is written to the buffer
    3. A timer starts
    4. If you press the next key in the sequence before the timer is up, the j is removed from the buffer and the command fires
    5. If the key you press next isn't in the sequence, or the timer is up then it's treated like a literal
  6. dhatch48 commented on Jul 27, 2020

    @dhatch48
    Author

    Yeah, j will be bound after that and won't be treated as a literal character. I think that's just an example. Key sequences should be chosen to not interfere with the regular input, like Set-PSReadlineKeyHandler -Chord 'Ctrl+d,Ctrl+c' -Function CaptureScreen

    I think Patrick Meinecke (@SeeminglyScience) explained it nicely. Many vim users have bindings of jj or jk to exit insert mode because it is so common to switch modes and this way it keeps your fingers on the home row vs. reaching for Esc key every time.

    Anyway, I suppose a sequence timer/timeout is needed to make this work.

  7. changed the title [-]Allow Set-PSReadLineKeyHandler to bind a key sequence[/-] [+]Allow a key sequence binding like `j,k` to not prevent user from insert `j`[/+] on Jul 27, 2020
  8. daxian-dbw commented on Jul 27, 2020

    @daxian-dbw
    Member

    I see. Thanks for clarifying it.
    I have changed the title and PR description to reflect what is really asked for.

  9. Drllap commented on Jan 23, 2022

    @Drllap

    This is what I added to my profile to make jk go to comand mode:

    $j_timer = New-Object System.Diagnostics.Stopwatch
    
    Set-PSReadLineKeyHandler -Key j -ViMode Insert -ScriptBlock {
        [Microsoft.PowerShell.PSConsoleReadLine]::Insert("j")
        $j_timer.Restart()
    }
    
    Set-PSReadLineKeyHandler -Key k -ViMode Insert -ScriptBlock {
        if (!$j_timer.IsRunning -or $j_timer.ElapsedMilliseconds -gt 1000) {
            [Microsoft.PowerShell.PSConsoleReadLine]::Insert("k")
        } else {
            [Microsoft.PowerShell.PSConsoleReadLine]::ViCommandMode()
            $line = $null
            $cursor = $null
            [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)
            [Microsoft.PowerShell.PSConsoleReadLine]::Delete($cursor, 1)
            [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor-1)
        }
    }
    
  10. slacksystem commented on Jan 6, 2023

    @slacksystem

    This is what I added to my profile to make jk go to comand mode:

    $j_timer = New-Object System.Diagnostics.Stopwatch
    
    Set-PSReadLineKeyHandler -Key j -ViMode Insert -ScriptBlock {
        [Microsoft.PowerShell.PSConsoleReadLine]::Insert("j")
        $j_timer.Restart()
    }
    
    Set-PSReadLineKeyHandler -Key k -ViMode Insert -ScriptBlock {
        if (!$j_timer.IsRunning -or $j_timer.ElapsedMilliseconds -gt 1000) {
            [Microsoft.PowerShell.PSConsoleReadLine]::Insert("k")
        } else {
            [Microsoft.PowerShell.PSConsoleReadLine]::ViCommandMode()
            $line = $null
            $cursor = $null
            [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)
            [Microsoft.PowerShell.PSConsoleReadLine]::Delete($cursor, 1)
            [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor-1)
        }
    }
    

    Since this is under not-planned, it seems, I'm trying to figure out how to change this so it's jj instead of jk but I can't figure it out. Do you have any ideas how I could do that?

  11. Drllap commented on Feb 25, 2023

    @Drllap

    Slack System (@slacksystem) try this:

    $j_timer = New-Object System.Diagnostics.Stopwatch
    Set-PSReadLineKeyHandler -Key j -ViMode Insert -ScriptBlock {
        if (!$j_timer.IsRunning -or $j_timer.ElapsedMilliseconds -gt 1000) {
            [Microsoft.PowerShell.PSConsoleReadLine]::Insert("j")
            $j_timer.Restart()
            return
        }
    
        [Microsoft.PowerShell.PSConsoleReadLine]::Insert("j")
        [Microsoft.PowerShell.PSConsoleReadLine]::ViCommandMode()
        $line = $null
        $cursor = $null
        [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor)
        [Microsoft.PowerShell.PSConsoleReadLine]::Delete($cursor-1, 2)
        [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor-2)
    }
    
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions