This is an archive of the discontinued LLVM Phabricator instance.

[lldb][Editline] Support ctrl+left/right arrow word navigation.
ClosedPublic

Authored by rupprecht on Nov 12 2019, 11:45 AM.

Details

Summary

This adds several 5C/5D escape codes that allow moving forward/backward words similar to bash command line navigation.

On my terminal, ctrl+v ctrl+<left arrow> prints ^[[1;5D. However, it seems inputrc also maps other escape variants of this to forward/backward word, so I've included those too. Similar for 5C = ctrl+right arrow.

Diff Detail

Event Timeline

rupprecht created this revision.Nov 12 2019, 11:45 AM
Herald added a project: Restricted Project. · View Herald TranscriptNov 12 2019, 11:45 AM

Build result: pass - 59972 tests passed, 0 failed and 763 were skipped.
Log files: console-log.txt, CMakeCache.txt

This is very nice, thanks!

Any chance we could test this with a pexpect test?

rupprecht updated this revision to Diff 228972.Nov 12 2019, 3:03 PM
  • Add a pexpect test

This is very nice, thanks!

Any chance we could test this with a pexpect test?

Absolutely. It was waaay more complicated than I expected due to funny escaping somewhere between pexpect and lldb, but testing an actual command seems to work. Hopefully I've captured that in the test doc comment.

Build result: pass - 59972 tests passed, 0 failed and 763 were skipped.
Log files: console-log.txt, CMakeCache.txt

JDevlieghere accepted this revision.Nov 12 2019, 3:45 PM

This is very nice, thanks!

Any chance we could test this with a pexpect test?

Absolutely. It was waaay more complicated than I expected due to funny escaping somewhere between pexpect and lldb, but testing an actual command seems to work. Hopefully I've captured that in the test doc comment.

Ha, I can imagine... Thanks for taking the time to make it work!

LGTM!

This revision is now accepted and ready to land.Nov 12 2019, 3:45 PM
labath accepted this revision.Nov 13 2019, 6:21 AM

Looks good. Thanks for adding the test. See inline comment for possible simplification.

lldb/packages/Python/lldbsuite/test/terminal/TestEditline.py
23

There are several reasons why doing that doesn't work well. The first is that the "console" output would differ depending on whether lldb/libedit has had a chance to disable echo or not. Assuming you got past that, the second problem would be that the output would contain a lot of ansi cursor movement escape sequences, and redraws, and you'd have to encode in the test the exact way in which libedit chose to implement the line editing operations. Checking for the effect of the command indeed seems like the best way to exercise this functionality.

28
el ommand[]
el []ommand
38–39

The buffer isn't exactly "cleared", but each "expect" command should only match from the end of the previous match, so repeating the same command should not be a problem. What the other (few) pexpect tests do is put an self.expect_prompt() to ensure all output from the previous command is ignored, and lldb is ready to receive a new command. You could do that too. In fact, you could probably use the helper "expect" command which does all of this for you:
self.expect("el ommand{L}c{L}{L}h{R}p".format(...), substrs=["Syntax: command"])

rupprecht marked an inline comment as done.
  • Fix test comment
  • Use expect helper from lldbpexpect
rupprecht marked an inline comment as done.Nov 14 2019, 10:38 AM
rupprecht added inline comments.
lldb/packages/Python/lldbsuite/test/terminal/TestEditline.py
38–39

The expect helper is nice, thanks!

but each "expect" command should only match from the end of the previous match

I am not able to reproduce that. If I change the expect to the static string "Syntax: print" (not % cmd), then the second case (which types "help step") passes. Which implies it's checking the entire buffer.

The third case ("help exit") fails because the buffer does not contain the print help text anymore. But that means this behavior is dependent on the relation between help text length and buffer size. For now, I'll leave this as separate help commands.

Build result: pass - 60015 tests passed, 0 failed and 722 were skipped.
Log files: console-log.txt, CMakeCache.txt

This revision was automatically updated to reflect the committed changes.
labath added inline comments.Nov 15 2019, 4:02 AM
lldb/packages/Python/lldbsuite/test/terminal/TestEditline.py
38–39

That's very odd. It's definitely not how things behave on my end (the second check fails straight away), and it's not clear to me why would that differ for you. Looking at the code, it's pretty obvious https://github.com/llvm/llvm-project/blob/b5f14326b447e5a97b3d7654448c36d7745a6882/lldb/third_party/Python/module/pexpect-4.6/pexpect/expect.py#L32 that pexpect intends to reset the internal buffer to include only the text after the last match.

I'd like to get to the bottom of this. Can you dig around (maybe step through the pexpect code) to see what's happening in your case? One thing that's been causing confusion in the past was the leftover third_party/Python/module/pexpect-2.X folder from the time before we updated pexpect. However, I find it unlikely that this would be the case here, as this should have been the pexpect behavior since forever...

rupprecht marked 3 inline comments as done.Nov 15 2019, 10:44 AM
rupprecht added inline comments.
lldb/packages/Python/lldbsuite/test/terminal/TestEditline.py
38–39

Figured out what was going on and mailed D70324 to prevent this from happening to the next person this bites -- which could be me :)

I originally had:

self.child.send("help command\n")
self.child.expect_exact("Syntax: command")

And based on your suggestion, changed it to:

self.expect("help command\n", substrs=["Syntax: command"])

Which is wrong, because self.expect() calls self.child.sendline(cmd), so it's running "help command\n\n" == "help command\nhelp command\n".
The expect_prompt() helper will match the first prompt it sees, so the second run of the command is still in the match buffer. Checking for "Syntax: command" would still pass even though it was not intentionally ran twice.

I also tried changing expect_prompt() to continuously match until the last prompt it sees, but it's simpler to just disallow \n in the command.