Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-118878: Pyrepl: show completions menu below the current line #118939

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

danielhollas
Copy link
Contributor

@danielhollas danielhollas commented May 11, 2024

As proposed in #118878, it feels much more natural to me to display the completions menu below the current line, not above. The main advantage of this is that the UI is less janky,
since the current line that is being edited no longer jumps up and down.

This also allows fixing the behaviour of arrow keys when the menu is displayed, described in #119257.
The current behaviour is still not great when tab completing in the middle of multiline edit, but it is at least not worse as before AFAIK. I think ultimately this should be solved by separating out the completions menu, like ipython does it, but that seems outside of scope for this PR.

TODO:

  • Tests!
  • Add screencast to demonstrate the new behaviour

Closes #119257. Closes #118878

A crash of the new pyrepl was triggered by
pressing up arrow when tab completion menu was displayed.
screen[ly:ly] = self.cmpltn_menu
self.screeninfo[ly:ly] = [(0, [])]*len(self.cmpltn_menu)
self.cxy = self.cxy[0], self.cxy[1] + len(self.cmpltn_menu)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the big win here, we no longer need to mess with the cursor when displaying the menu!

The main advantage of this is that the behaviour is less janky,
since the current line no longer jumps up and down.

This also allows fixing the behaviour of arrow keys when the menu is
displayed.
This test does not work yet :-(
@@ -274,7 +274,7 @@ def do(self) -> None:
x, y = r.pos2xy()
new_y = y + 1

if new_y > r.max_row():
if r.eol() == len(b):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This an equivalent fix as in #118936

@danielhollas
Copy link
Contributor Author

@pablogsal would you mind adding the topic-repl label to this PR?

I am not sure there is enough time to get this in for next beta, but I am happy to work on this further if this is something people are happy with.

@pablogsal pablogsal added the topic-repl Related to the interactive shell label Jun 3, 2024
@pablogsal
Copy link
Member

CC @lysnikolaou as he was working on something like this for a bugfix

Copy link
Contributor

@lysnikolaou lysnikolaou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this @danielhollas!

@@ -258,10 +258,15 @@ def after_command(self, cmd: Command) -> None:
def calc_complete_screen(self) -> list[str]:
screen = super().calc_complete_screen()
if self.cmpltn_menu_visible:
ly = self.lxy[1]
# We display the completions menu below the current prompt
ly = self.lxy[1] + 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a version of this already in the works and was going to open a PR but delayed it for the Windows PR to get in first. In my version, I just do screen.extend and it works without the need for the hack below. Or am I missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure, would you mind posting the relevant part of your patch? The problem is not with screen but screeninfo, which is used to calculate cursor position. Have you tried pressing the left or down arrow while the completion menu is displayed? Without the hack below the cursor would jump down into the completions menu in my case, but perhaps you solution works better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this should be enough once #120042 lands.

diff --git a/Lib/_pyrepl/completing_reader.py b/Lib/_pyrepl/completing_reader.py
index c11d2dabdd2..db3c26c0627 100644
--- a/Lib/_pyrepl/completing_reader.py
+++ b/Lib/_pyrepl/completing_reader.py
@@ -258,10 +258,7 @@ def after_command(self, cmd: Command) -> None:
     def calc_complete_screen(self) -> list[str]:
         screen = super().calc_complete_screen()
         if self.cmpltn_menu_visible:
-            ly = self.lxy[1]
-            screen[ly:ly] = self.cmpltn_menu
-            self.screeninfo[ly:ly] = [(0, [])]*len(self.cmpltn_menu)
-            self.cxy = self.cxy[0], self.cxy[1] + len(self.cmpltn_menu)
+            screen.extend(self.cmpltn_menu)
         return screen
 
     def finish(self) -> None:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to have to look at that PR more closely, but just looking at this patch, it seems that the completions menu would always get appended at the very bottom of the screen right?

I thought about doing it this way as well, but I don't know if this will work well during multiline editing. You can imagine a corner case where you would be editing a large multiline code, and the completions menu will appear well below the current cursor position or even outside the visible screen.
(I am currently AFK so cannot test this)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to have to look at that PR more closely, but just looking at this patch, it seems that the completions menu would always get appended at the very bottom of the screen right?
I thought about doing it this way as well, but I don't know if this will work well during multiline editing. You can imagine a corner case where you would be editing a large multiline code, and the completions menu will appear well below the current cursor position or even outside the visible screen.

@lysnikolaou I have now tested your version and for large multiline editing it is indeed a problematic since the menu will always be displayed at the very bottom, even if you're editing a line at the very top of screen. For large enough editing context, the menu will not be visible at all (e.g. when editing a first line in a function with many lines. WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this problem still relevant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, this PR has no problem afaik. I was pointing out issues with the suggestion above.

@pablogsal
Copy link
Member

pablogsal commented Jun 4, 2024

This has the problem that if the cursor is on the last line of the screen, completions are not shown (only when editing a multi-line block)

@danielhollas
Copy link
Contributor Author

This has the problem that if the cursor is on the last line of the screen, completions are not shown (only when editing a multi-line block)

@pablogsal was your comment meant on my version of the code or on @lysnikolaou patch proposed in #118939 (comment)? I can reproduce the issue you describe with his patch but I cannot with the current version of PR.

I merged in main and fixed up a test, and things still seem to be working.

@@ -658,6 +659,33 @@ def test_updown_arrow_with_completion_menu(self):
# so we should end up where we were when we initiated tab completion.
output = multiline_input(reader, namespace)
self.assertEqual(output, "os.")
# TODO: The menu should be visible, but currently isn't?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this TODO still relevant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Essentially, I wanted to assert stuff while the tab completions menu is displayed, but I failed to figure out how to do it. I suspect the multiline_input function is not a good vehicle for that.

It would be nice to have a test that asserts that the menu is indeed displayed below and that the cursor behaves in a sane way when user presses various arrow keys while the menu is displayed. But I will not have time to figure it out in the next week I am afraid.

For now I've removed the TODO and the test below.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting review topic-repl Related to the interactive shell
Projects
None yet
Development

Successfully merging this pull request may close these issues.

pyrepl: Cursor behaviour during tab-completion Tab completion behaviour in new REPL
3 participants