prompt-toolkit / python-prompt-toolkit

Library for building powerful interactive command line applications in Python
https://python-prompt-toolkit.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
9.37k stars 716 forks source link

Impossible to have different continuation prompt for hard and soft line wrap #493

Open asmeurer opened 7 years ago

asmeurer commented 7 years ago

It would be nice if get_continuation_prompt had enough information passed to it to have a different continuation prompt depending on if the line is hard wrapped or soft wrapped. As far as I can tell this is currently impossible (from the logic here, the get_continuation_prompt has no distinction between the different calls. Even if it were just passed n (for the n-th continuation prompt) I think this would be possible.

jonathanslenders commented 7 years ago

This is correct, and this feature request makes sense. In ptpython, this is possible because it uses a more low-level API (which is not guaranteed to remain stable).

I take this as a feature request.

asmeurer commented 7 years ago

I didn't know it was possible in ptpython. I'll take a look at how it works there.

asmeurer commented 7 years ago

Actually, what I really want is no continuation prompt for soft-wrapped text. Ideally, the text wouldn't even soft wrap, so that if I select it and copy it, there are no newlines. Is this possible? It works this way in readline shells.

jonathanslenders commented 7 years ago

For a single-line input, it is already the case that there is no continuation prompt.

About having one for hard line wraps, but no for soft wraps: this is not possible right now. I'm not sure what it would take to get this to work, but I'll keep it in mind.

asmeurer commented 7 years ago

For a single-line input, it is already the case that there is no continuation prompt.

Can you clarify what you mean here? I have a continuation prompt set up and it always shows when a single line of input fills the terminal width. If I could disable it for this case, even with hard wrapping, that would be a good start.

asmeurer commented 7 years ago

I can get roughly the behavior I want by setting wrap_lines to False, with the following diff

diff --git a/prompt_toolkit/renderer.py b/prompt_toolkit/renderer.py
index 7a8fde5..a2d0d57 100644
--- a/prompt_toolkit/renderer.py
+++ b/prompt_toolkit/renderer.py
@@ -118,7 +118,8 @@ def _output_screen_diff(output, screen, current_pos, previous_screen=None, last_
     # background threads, and it's hard for debugging if their output is not
     # wrapped.)
     if not previous_screen or not use_alternate_screen:
-        output.disable_autowrap()
+        pass
+        # output.disable_autowrap()

     # When the previous screen has a different size, redraw everything anyway.
     # Also when we are done. (We meight take up less rows, so clearing is important.)
diff --git a/prompt_toolkit/terminal/vt100_output.py b/prompt_toolkit/terminal/vt100_output.py
index b800aaa..17dc558 100644
--- a/prompt_toolkit/terminal/vt100_output.py
+++ b/prompt_toolkit/terminal/vt100_output.py
@@ -426,7 +426,7 @@ class Vt100_Output(Output):
             rows, columns = _get_size(stdout.fileno())
             # If terminal (incorrectly) reports its size as 0, pick a reasonable default.
             # See https://github.com/ipython/ipython/issues/10071
-            return Size(rows=(rows or 24), columns=(columns or 80))
+            return Size(rows=(rows or 24), columns=1000)

         return cls(stdout, get_size, true_color=true_color,
                    ansi_colors_only=ansi_colors_only, term=term)

Hacking the terminal size is not good, obviously, but I couldn't figure out where the columns actually gets used (everything I've tried just keeps writing over the last visible character, instead of wrapping).

jonathanslenders commented 6 years ago

Ok, I think I understand the issue. You don't want the \n to be rendered in the terminal, so that copy/pasting works.

Unfortunately this is still not possible, and very hard to implement right now, taking into account how the rendering engine works. We first render to an in-memory 2d canvas, then we diff that against the 2d canvas from the previous rendering, and we output the diff by doing both horizontal and vertical cursor movements. We never rely on the terminal to do line wrapping.

Not showing a continuation prompt for a soft wrap is possible by using the is_soft_wrap argument of get_continuation_prompt. But this will still render a \n.

Also, see this example: https://github.com/jonathanslenders/python-prompt-toolkit/blob/master/examples/prompts/get-multiline-input.py

asmeurer commented 6 years ago

Are there terminals that don't do soft-wrapping that would need to be handled in the code?

It sounds like the code would need to be abstracted a bit to differentiate between visual rows and physical rows, where there may be more visual rows because of wrapping. Calculations involving the terminal size should use visual rows but calculations involving cursor movements should use physical rows.

This sounds somewhat related to another feature that I would like to see, which is that when I execute a a prompt that is larger than the screen (sometimes I paste in large blocks of code), it would be nice if the terminal scrollback history showed the full input, not just what was visible onscreen when I pressed Enter. But I have no idea how hard that would be to implement.

jonathanslenders commented 6 years ago

This is actually really complex to get multiline rendering reliable on all terminals. The current algorithm has been battle tested for a couple of years now and seems to work fine. Changing that possibly also means changing how mouse events correspond to positions and how the CPR response needs to be handled. And further, and lot of effort has been put into making the rendering performance as efficient as possible.

Feel free to experiment with this, but unfortunately, I probably have other priorities with prompt_toolkit first.

asmeurer commented 6 years ago

OK, I might at some point. It helps to know how it works. Also, I don't think this sort of thing needs to be supported when mouse reporting is enabled. To me, that turns it into a real full-screen terminal application (even if it doesn't do a clear), and I have never seen a full screen application that doesn't do line wrapping (is it even possible?).

jonathanslenders commented 6 years ago

One other thing to keep into account I'm just thinking about is double width characters (+ maybe decomposed double width characters). I'm not sure all terminals handle it the same way during the line break. I remember certain inconsistencies. Further terminals like tmux do some tricks for getting auto reflow of to work. These are all things to keep in mind.