prompt-toolkit / prompt-toolkit/python-prompt-toolkit

vi mode: `d` deletes something other than the selected characters (block select)

Open
#1,661 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
10.6k
Forks
815
PR merge metrics
No merged PRs in 30d

Description

Hi, thanks for this library, it's handy.

I have written three tests to demonstrate this bug

  1. test_vi_delblock passes. This test runs vi to check my assumption that d should indeed delete a block selection.
  2. test_example_delblock fails. This is the problematic behavior. If you select the first character of all three lines and press d, you should still have the remaining characters on all three lines. Instead you lose every character on the first two lines, and the last line remains untouched
  3. test_example_cutblock passes. This shows prompt toolkit doing the right thing and deleting only the first character on each line. However, it should do this for both x and d, not just for x
import pexpect
import pyte, os
from textwrap import dedent

ROWS, COLS = 15, 40
screen = pyte.Screen(COLS, ROWS)
stream = pyte.Stream(screen)

def spawn_process(cmd):
    env = os.environ.copy()
    env.update({'LINES': str(ROWS), 'COLUMNS': str(COLS)})
    return pexpect.spawn(cmd, echo=False, encoding='utf-8', dimensions=(ROWS, COLS), env=env)

def emulate_ansi_terminal(raw_output):
    stream.feed(raw_output)
    lines = screen.display
    screen.reset()
    return '\n'.join(lines)

def test_vi_delblock():
    "you can use 'd' to delete a block selection in vi"

    p = spawn_process('vi')
    p.send("iabc\ndef\nghi")        # some text to edit
    p.sendcontrol("[")              # press esc
    p.send("0")                     # beginning of line
    p.sendcontrol("v")              # block select
    p.send("kkd")                   # this and two lines above, then delete
    p.expect(pexpect.TIMEOUT, timeout=2)
    output = emulate_ansi_terminal(p.before)
    print(output)
    assert '\n'.join(output.split()[0:3]) == dedent(
        """
        bc
        ef
        hi
        """
    ).strip()

def test_example_delblock():
    "but prompt toolkit ignores the block select when 'd' is pressed"

    p = spawn_process('sh -c "python $HOME/src/python-prompt-toolkit/examples/prompts/switch-between-vi-emacs.py"')
    p.send("abc\ndef\nghi")             # type something
    p.send(chr(27) + chr(79) + chr(83)) # switch to vi mode (F4, accoring to https://stackoverflow.com/a/73355658/1054322)
    p.sendcontrol("[")                  # press esc
    p.send("0")                         # beginning of line
    p.sendcontrol("v")                  # block select
    p.send("kkd")                       # this and two lines above, then delete
    p.expect(pexpect.TIMEOUT, timeout=2)
    output = emulate_ansi_terminal(p.before)
    print(output)

    # lines 1 & 2 say: WARNING: your terminal doesn't support cursor position requests (CPR)
    assert '\n'.join(map(lambda x: x.strip(), output.split("\n")[2:5])) == dedent(
        """
        > bc
        ef
        hi
        """
    ).strip()
    # fails, instead output has:

    #
    #
    # ghi

    # which is *almost* what would have happened if this had been `v` instead of `ctrl + v`

def test_example_cutblock():
    "you do, however, get the desired behavior for 'd' if you use 'x' instead"

    p = spawn_process('sh -c "python $HOME/src/python-prompt-toolkit/examples/prompts/switch-between-vi-emacs.py"')
    p.send("abc\ndef\nghi")             # type something
    p.send(chr(27) + chr(79) + chr(83)) # switch to vi mode (F4, accoring to https://stackoverflow.com/a/73355658/1054322)
    p.sendcontrol("[")                  # press esc
    p.send("0")                         # beginning of line
    p.sendcontrol("v")                  # block select
    p.send("kkx")                       # this and two lines above, then cut
    p.expect(pexpect.TIMEOUT, timeout=2)
    output = emulate_ansi_terminal(p.before)
    print(output)
    assert '\n'.join(map(lambda x: x.strip(), output.split("\n")[2:5])) == dedent(
        """
        > bc
        ef
        hi
        """
    ).strip()

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the reported test_example_delblock scenario against examples/prompts/switch-between-vi-emacs.py, then compare it with test_example_cutblock and test_vi_delblock. Trace the vi block-selection handling for d and x; done means d leaves bc, ef, and hi, matching the block behavior demonstrated by x.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
cli
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.