-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
bpo-37903: IDLE: Shell sidebar with prompts #15474
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR @taleinat. I'm definitely onboard with the concept of this change, a dedicated sidebar prompt for the shell would be a nice addition.
From an initial review of the GUI, it looks like the font of the characters in the sidebar aren't scaling dynamically when the font is adjusted in the shell:
Opening the shell with a font size of 22 Arial (expected):
Opening the shell while the font size is set to 8 Arial and then adjusting it to 22 within the shell interface (Not expected):
Thanks for giving this a whirl, @aeros167!
Good catch! Fixed. |
|
Yes.
Yes, but final '\n' chars of input blocks are currently also tagged "stdin", and changing that would be fiddly.
Indeed. Note that these are tagged both "console" and "stdin". |
No problem. Feel free to @mention me or add me to the reviewers for any IDLE-related testing. GUI design isn't my main area of experience, but as a regular user of it I'm glad to help out with testing new changes. (: |
On second thought, removing the "stdin" tag from those newlines might be relatively simple. I seem to have things working rather well as they are right now, though. I can take a look at making such a change if you like, @terryjreedy, if we decide to move forward with this and everything else looks okay. |
I like the look of this and it seems to run smoothly.
|
I believe I would prefer removal of the final ... before the statement ending the blank line. However, it presence matches the console, though there is no option there. Not essential. (Note that I had to replace \t with spaces to look correct. b) Hit return to end statement without backspacing.
A return is inserted both before and after the smart indent. Instead, I think the smart indent should be removed, as if never inserted. In the Windows console, at least, return after a (manual) indent does not terminate a statement. I believe that this is why a newline is also inserted before the indent, so people do not have to hit return a third time to end the statement. This console behavior allows one to put visually blank lines in multiline statements, but I doubt people do this much. I have never seen a complaint not being able to do this in IDLE, and I think it a bit confusing.
|
We can bikeshed over whether to leave a blank line after experience both ways.
|
1b (important). Add a way to save Shell so that statements (or rather non-statements) are clearly marked. Being able to get executable saves is another reason for moving prompts away from code. At this point, I need to read the code so I have some idea of the difficulty of various possible changes. |
I would agree. Whitespace and formatting concerns are generally not particularly important when using the shell. |
@terryjreedy, perhaps I'm missing something, but most of the points you've just raised (1, 2, 3, and 1b) seem like further enhancements you'd like to make that could benefit from this change. As they are not directly related to this PR, perhaps they would be better discussed elsewhere? Regarding the background glitches (4), I'll try to take a look. |
2 & 3 were recorded here as I reviewed, but should be separate patches (and recorded on the main shell revamp issue). Sidebars clicks can also be a future issue. As to 1, we are both missing something. You apparent think of this as a standalone patch, whereas I was only thinking of it as part of getting rid of tab indents and matching editor space indents. That said, I really like having a proper even margin for multiline statements, even with the tabs still there. My concern is that I expect some people will object to just this change. Others might like it because the addition of '... ' secondary prompts makes Shell look more like the standard REPL. Others who have never used the latter will not know. As for 1b, Shell saves, the prompt removal is both a minus as far as finding code chunks but a plus as far as running code chunks. I would prefer to include something that is a complete plus. For 3.8.0rc1, scheduled Monday 9-30, this should be a moot point as both this and a couple of followups should be merged. For 3.7.5rc1, scheduled Monday 09-16, I am not so sure. Should this be released by itself, or should we consider not backporting to 3.7 until after. @aeros167 Do you use IDLE regularly? Would you want to see this in 3.7.5 even any followup patches? In the meanwhile, I will review this as feature-complete. |
Perhaps to get an idea of the popularity of the change, we could open a poll on https://discuss.python.org/c/users. The detailed feedback is more valuable, but the polls tend to draw in more participants. Probably because they require a very minimal time investment. However, I would think that most people who have used the IDLE Shell have also used the standard REPL at some point. This may be anecdotal, but I've not interacted with anyone who has used the IDLE Shell and not the REPL. From my experience, it tends to be the other way around. My interactions with other Python users in person has been primarily through college, which may slightly skew the demographics. But if anything, I would think that newer users of Python would be the ones more likely to have exclusively used the IDLE shell since the REPL has been around for longer.
I would personally be interested in seeing this change in 3.7.5. I typically tend to alternate between using the REPL for the most simplistic usages of Python (checking the length of a line, scientific calculator, etc), IDLE for more involved tasks or scripts, and the vscode python extension for projects. I always thought it was a little bit odd that the IDLE shell didn't have the ellipsis prompts, but never thought to open an issue for it. If I were to personally prioritize the followup patches, I would say the second one you mentioned would be the most significant to me:
This is just my personal preference though. Even if they're less impactful at the end of the day, the visual bugs (even minor ones) have a tendency to stand out to me. Especially for visual bugs that would occur more commonly, such as this one. |
Lib/idlelib/pyshell.py
Outdated
@@ -1222,15 +1274,16 @@ def recall(self, s, event): | |||
if prefix.rstrip().endswith(':'): | |||
self.newline_and_indent_event(event) | |||
prefix = self.text.get("insert linestart", "insert") | |||
self.text.insert("insert", lines[0].strip()) | |||
self.text.insert("insert", lines[0].strip(), "stdin") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the addition of 'stdin' tags has something to do with the background change I mentioned in point 3. But I am not concerned about it now.
|
||
prompt = self.prompt | ||
if self.sys_ps1 and prompt.endswith(self.sys_ps1): | ||
prompt = prompt[:-len(self.sys_ps1)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is pretty useless. PyShell is initialized with
self.sys_ps1 = sys.ps1 if hasattr(sys, 'ps1') else '>>> '
self.prompt_last_line = self.sys_ps1.split('\n')[-1]
self.prompt = self.sys_ps1 # Changes when debug active
The only way I know to either reset or delete sys.ps1 and start IDLE in the same process is to do so in python code and then import idlelib.idle
. Under normal circumstances, self.sys_ps1 exists, is not '', and and after the following code, equals self.prompt and self.prompt_last_line.
The new conditional will trigger once and clear the prompt to '' (or remove it from 'DEBUG ON\n'. The conditional will never trigger again. After it is cleared, the only possible 'prompt' within the text box is "DEBUG ON\n" . (I don't think it should be a repeated prompt -- another issue.) So we should remove sys_ps1. Until we remove the debug prompt, we should just initialize self.prompt it to ''.
self.prompt_last_line is more problematical. It should be set to '' or better deleted, but it is used (only) in EditorWindow to indicate "isinstance(self, PyShell)" and affect backspace and HOME. (The assumption is that it is never '' in Shell, but it will be if sys.ps1 ends with '\n'.) The uses of prompt_last_line need to be reviewed to remove uses of the string as a string and replace it with a boolean where the indicator is still needed. (I started on this.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, this is needed to support custom prompts, as currently used only by the debugger (and possibly by extensions).
I was aiming to change as little as possible. The places where prompt_last_line
is used are rather delicate, and changing them could requiring making other related changes... @terryjreedy, are you sure you want this as part of this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setting self.prompt directly to '' is less change that setting it to '>>>' and then correcting it to ''. Either way, the debugger will add and delete its notice.
prompt_last_line is a separate issue. Giving it a now incorrect value is what strikes me as dangerous. Maybe it never matters, maybe it will in certain situations.
def get_end_linenumber(text): | ||
"""Utility to get the last line's number in a Tk text widget.""" | ||
return int(float(text.index('end-1c'))) | ||
return get_lineno(text, 'end-1c') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstrings violate pep8 and only 1 function is needed.
def get_lineno(text, idex='end-1c'):
"Return line number in text of idex, which default to the last line"
return int(float(text.index(idex)))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it's a matter of style, but IMO having get_lineno()
return the last line number by default isn't an intuitive default behavior. It feels like favoring conciseness over clarity, which I usually try to avoid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstrings violate pep8
As far as I can tell, this would not be in violation of PEP-257's section on one line docstrings though. PEP-257 is main authority on docstrings. If anything, it indicates that triple quotes are preferable over single quotes:
Triple quotes are used even though the string fits on one line. This makes it easy to later expand it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PEP-257 says that docstrings should begin with a command verb, usually 'Return', occasionally something else.
"Return x"
is common in the stdlib. """Return x"""
takes 4 extra spaces, which might be needed, and to me is too heavy for a single line.
get_end_linenumber(text)
is only 5 chars shorter than get_lineno(text, 'end-1c')
. Hardly enough to justify a new function called 3 times. How about adding END = 'end-1c'
and making the calls get_lineno(text, END)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This module currently consistently uses triple-quotes even for single-line doc-strings.
I'd be happy to change that in a follow-up PR, but I think it's better to avoid making more widespread changes in this (already large) PR, and I wouldn't want to break the current consistency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leave the triple quote if you prefer and space allows. I am more concerned about an unneeded quarter-liner.
When you're done making the requested changes, leave the comment: |
@aeros167 Thank you for the response. If you like the missing dots back, I suspect others will too. I don't want opinions based on imagination. Best would be that a person use the new version for at least 15 minutes, with a variety of statements. Minimum would be looking at a picture of a session with at least a few statements entered. If you feel like it, it might be helpful if you could upload a screenshot of duplicate before (installed) and after (patched repository) sessions side by side. I may ask Ned Deily, the 3.7 release manager, for an opinion, or permission to add a patch after the release of the candidate. |
@taleinat "Removing prompts from the text widget made knowing where each statement started impossible, but the sidebar needs to know this. This PR adds the "console" tag to the newline before each statement to overcome this." A couple of question based on not having read the shell Q1: Does SSB keep track of where text is (apparently) frozen, so it only rescans new stuff, and not start at the beginning each time? |
About history recall: a glitch new to me of skipping 1,2 char name expressions remains after the patch.
Hit history-prev (Alt-P for me) several times and 'bb' is skipped, 'bbb' is shown, 'a+b' is shown, b is skipped, and 'a=1' is shown. |
Thanks for reviewing this, @terryjreedy! I've added comments clarifying the reasoning behind the changes that you commented on. Please note that I'm not trying to be defensive, just explaining my thinking. I'm happy to make any changes you request, @terryjreedy! |
No. The major reason for this is the soft-wrapping of lines, which can change when changing the font size, window width, etc. Also, outputs before the iomark can be squeezed/unsqueezed, so they are not totally "frozen". Note that the sidebar only scans the currently visible lines, not the entire text widget contents. Also note that getting the display info for lines in the text widget not currently displayed (outside the currently visible area) isn't possible with the current method ( To summarize, the currently implementation is straightfoward, robust, and in my testing performs rather well. It might be worth checking on machines with slower CPUs, though. |
IIRC such outputs are inserted before the final newline, therefore preserving the newline with the "console" tag. It's position may change due to the contents added before it, which is fine. Regardless, even if the tag were removed and then added back, the sidebar will "just work" as long as it receives a request to update itself, which the current mechanisms should ensure. |
Thanks for making the requested changes! @terryjreedy: please review the changes made to this pull request. |
Just did and found it to pass in all repeats (and much quicker!). Also, did you get a chance to look at the issues I raised in a comment further up? |
Thanks, great news!
Yes, I've taken a quick look. The first two seem to be related but I haven't gotten to the bottom of the issue yet. Regarding the scroll wheel, I haven't implemented any event handling on the sidebar yet, though I agree that not responding to scrolling is a glaring omission. Since this PR appears to be nearing readiness for merging, I'll try to get to that soon. |
Actually, this PR does include code to support scrolling, but it doesn't work on platforms which used mouse buttons 4 and 5 to signify scrolling up/down. I've added the missing bit to support those as well. |
Also note that there is currently much code duplication between the line numbers and shell sidebar. I've a got a refactoring of them ready, but I'd like to do that in a separate PR. |
@E-Paine, FYI for IDLE modules, to generate coverage data you should instead run e.g. |
With the latest addition to the tests, I get 89% coverage (including htests). About half of the remaining untested lines are dead lines that will be removed as part of the aforementioned refactoring I'll do in a separate PR. |
I've removed some dead code ( This brings coverage (with the htest) up to 96%. |
Some general comments.
In the current implementation, Colorizer.tagdefs includes "ERROR", which it never sets, because other modules depend on a) it being defined here (this could be fixed by defining "ERROR" in editor, along with "sel"), and more importantly, b) it being removed by colorizer when one hits a key after compile fails. Changing this would be harder and maybe not worthwhile. Colorizer.tagdefs should not define -- and sometimes remove -- structure tags managed by other modules. These include the PyShell tags. Search 'hit' should be in this category, but I'd rather leave verifying that its management is complete, without colorizer, to another time. (If there had been a separate colorizer tagdefs fix issue/pr, I would have checked.) The PyShell structure tags include "stdin", "stdout", and "stderr". These name are also used for corresponding rpc 'channels'. Is "stdin" used for entered code, input() response, or both? (An outstanding issue is that input response is visibly syntax marked.) I am starting to review live use of the patch again. First new bug is that input prompt and response is initially marked in the sidebar as a text continuation line with '...'.
|
Thanks for the reminder. I've run the coverage again exactly as specified in the README, and received 93% coverage. Most of non-covered sources lines will be removed via refactoring. I've updated the doc-string. (I made a Unix-y version of your batch script, @terryjreedy, let me know if you'd like me to put a copy of it somewhere.) |
See new PR GH-22682. I'm closing this PR in favor of that. |
For future reference: As far as I can tell, from all of the issues brought up in the comments here so far, the only unresolved issues requiring work in the context of this PR are:
EDIT:
|
Could we possibly add the issue with
Is a "part-2" PR a suitable place for such a refactor? |
Regarding the background color, here is a comparison of before/after this PR with the background for "normal code or text" changed to pink: It seems that "stdin" hasn't been using the default background for a while (it's the same on 3.8.5). ISTM that we should simply fix the background for the "stdin" tag. @terryjreedy? |
The "take 2" PR is just a continuation of this one. After that is merged, followup PRs fixing remaining issues would certainly be welcome. |
Yes, you're right, I missed that. Good catch! |
Actually, taking another look at this, I think the only problem in this example is the white bars until the end of the lines, which are caused by having the '\n' characters at the end of the lines have the "console" tag. Changing that to use the "normal" highlight config results in what I think is the expected behavior: |
Documenting testing IDLE is https://bugs.python.org/issue30934. Changing the tagging of stdout and stderr text is out of scope for this issue. I also, at least at the moment, disagree with the change. Consider
The 4 traceback lines are evenly tagged from margin to margin with, in my theme, a subtle but distinctive pink background. Without considerable testing, I think I would like a ragged background less. Ditto for truncating the background on just the last line, if the traceback happens to not have a terminating newline. I am continuing discussion on the new PR, starting with a copy of the 'unfinished'. |
This is a working implementation of a sidebar for IDLE's shell, removing the prompts from the main text widget (except for special prompts such as the one used by the debugger.) It works very well in my testing.
Please give this a try and let me know what you think!
Currently known to be missing:
Notes on the implementation:
recall()
method.https://bugs.python.org/issue37903