-
Notifications
You must be signed in to change notification settings - Fork 17
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
read executed line to get indentation level and expand code #10
Conversation
This looks quite good. Especially the I'll use this for a bit and let you know if I find any bugs. |
Here's a bug. Put your cursor anywhere on the line
Also, I'm not sure if it's possible, but it would be great for |
@kylebarron thanks for testing!! So I checked, and it seems like the cursor is moved before hydrogen-python is called which messes up the knowledge of the plugin where it is. It would be nice if there was a way to intercept the events fired to hydrogen, so as to know what key combination/command was used... I shall have a look at this. Which action (run / run + move down) was called is crucial to knowing when to move the cursor further. But it shouldn't be too difficult. |
@kylebarron The So the displayed take of your example results in 'b' and moves down to the I haven't trialled the other |
In my atom it works. Are you sure its the right package loaded? |
Oh sorry, yesterday I disabled then uninstalled the package. Today when installing it again it stayed disabled. So I just had to enable it in the settings again. I'll let you know if I find any other bugs |
Another error: x = 2
if x == 2:
print('a')
print('b')
else:
print('c') If you put your cursor anywhere on the Uncaught TypeError: Cannot read property 'replace' of undefined
|
Another separate error. If there's any indented whitespace on a line inside an if/else block, it fails with a Python if x == 2:
print('a')
print('b')
# 4 spaces ^
#
# File "<ipython-input-17-42c9438ba923>", line 2
# print('a')
# ^
# IndentationError: expected an indented block |
Also fails when the class test(object):
def __init__(self, arg):
if x == 2:
print('a')
else:
print('c')
# Error when line after print('a') has 4, 8, or 12 spaces |
It also doesn't work generally if there's any space between the x = 2
if x != 2:
print('a')
else:
print('c') With |
I'm worried that any solution will be hacky and will have myriad edge cases that don't work. |
@mbroedl Thanks for taking a look at the indentation level issues! In addition to some of the edge cases that @kylebarron pointed out, I also want to mention the "run cell and move down" command (there's also plain "run cell" variant). Do you think you can get your approach to work with the full range of hydrogen commands? If you're having trouble figuring out which command triggered the code running, maybe something in the Even if the changes here only work for a particular workflow (e.g. one that avoids using certain commands), it may still be worth shipping them but they'd need to be hidden behind a configuration flag. I want to avoid users encountering these issues and then reporting them to the main hydrogen bug tracker. I think this has happened a few times already with bugs in code I wrote. Alternatively, might you be interested in modifying hydrogen a bit to get a cleaner solution? It should be possible to have the "code manager" file in hydrogen expose some options/callbacks to the plugin API, so that the plugin can operate on full information about which code is being run instead of having to piece things together after-the-fact. |
@kylebarron Thanks a lot for the elaborate bug reports! I shall have a look at them soon. Apart from the first one, all are indeed a bit more hacky. — You're totally right about the edge cases... but then anything that works better than now is an advancement. @nikitakit Is there any documentation what the if (plugins[grammar].findCodeBlock) {
return plugins[grammar].findCodeBlock(editor, row);
} But I'm probably overlooking a lot of complexity here? |
It would be a huge step forward to fix these issues that Hydrogen has with Python. However with Hydrogen proper, I always know that there's a way for me to run a specific section of code, and when unexpected bugs occur with There's an open issue about removing an equal amount of indentation from all lines here: nteract/hydrogen#862. Now that I read the thread again, I think a solution along the lines of nteract/hydrogen#862 (comment) would be ideal and such a PR could be accepted. I may try to delve into that and see how hard it would be. |
@mbroedl You may want to look at my PR for Hydrogen related to common leading whitespace: nteract/hydrogen#1363 I spent hours on a relatively in-depth solution. Then I discovered that removing 7 characters would fix it. |
@kylebarron thanks for taking the initiative. — As said the only problem is now: where to intercept with a possible API, as hydrogen-python can not anymore guess the indentation level. Also, one would want to strip the whitespace again after the expanded code is selected. |
Sorry if I confused you... I meant to say that instead of the in-depth solution of trying to remove common whitespace, if I just take off the The reason why the current Hydrogen fails is because it removes the leading whitespace from the beginning of the selection but nowhere else. |
My idea for expanding a selection based on if/elif/else is:
This is my idea for what might support indented I might first try to implement this in a my init script, having it create the full selection that I would then send to Hydrogen. |
@kylebarron let nextRowText = e.lineTextForBufferRow(lastRow + 1);
const expandCode = [' '].concat(atom.config.get('hydrogen-python.expandCode')).join('|');
while (nextRowText.match(new RegExp(`^(${indent}(${expandCode})|\s*#|\s$)`))) {
code += `\n${nextRowText}`;
lastRow += 1;
nextRowText = e.lineTextForBufferRow(lastRow + 1);
} With the default for the setting being: |
I was just describing creating a cursor selection before sending off to Hydrogen because I'm not yet familiar with the Hydrogen extension APIs. That chunk does look similar to what I had in mind. Are there changes you want to make to this PR given the changed functionality in Hydrogen? |
The change actually caused some trouble because the right trim was gone, and it took me a while to realise. I believe it works well now! I included a default=false setting to enable code expansion. So make sure you tick that box before testing. Imo, the next step would be now to fiddle into the hydrogen API that the plugin is asked what code execute before it is executed. |
Yes, there was some discussion whether to keep the right trim or not, but the merge removed the right trim as well. So the plugin isn't currently able to change the code before executing? At what point does it get control from Hydrogen? |
Well, it changes the code before executing, but it doesn't seem to be able to tell hydrogen that the code has changed. Does that make sense? Just tracing the plugin thing through the hydrogen files: It seems as if the middleware intercepts right before sending the code to the kernel. |
Thanks, @kylebarron . I could reproduce the error, put in a check for the last line when filtering for regex + breakpoints, and now the error disappeared. |
Thanks for keeping up with this, and I'm happy that the trim changes have been merged into hydrogen core! I'll take a closer look at the code and try running it this weekend. I appreciate that you put this feature behind a default-false config, which should make it possible to ship this out faster and see what people think. |
I hit another "Uncaught TypeError: Cannot destructure property lineText of 'undefined' or 'null'.". This time it occurs specifically when running code that has been folded. So it works when my cursor is on the class test(object):
def __init__(self, arg):
super(test, self).__init__()
self.arg = arg
def helloworld():
x = 2
if x == 2:
print('hello world')
else:
print('not 2')
def test2():
try:
pass
except Exception as e:
raise
if __name__ == '__main__':
main() But it fails when the class test(object):
def __init__(self, arg):
# folded
def helloworld():
# folded
def test2():
# folded
if __name__ == '__main__':
main() I'm guessing that this has something to deal with using |
Thanks for the detailed report @kylebarron . I also added |
I think that's all. The regex extensibility is great. I added
without having to select the lines. |
Oh, that's sweet! I hope it won't ever surprise you! |
Yeah it could be a nice user-defined example |
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 just tested this out (with the master version of hydrogen) and everything is working as expected so far!
The next step before merging would be to clean up the code and make sure the README/config docs are up-to-date. I have some non-exhaustive suggestions in my review.
At some point we'll also need to remove "Feature: Strip common leading whitespace" from the README, since the hydrogen patch is mostly taking care of that. Would you like to take care of that in this PR?
README.md
Outdated
|
||
(Pull requests are welcome to fix the first issue; the second probably can't be fixed without changing the Hydrogen package) | ||
There is also an experimental option to expand the code passed on by hydrogen to include else/elif or closing brackets, which until now is only possible when selecting the code. | ||
However, the tick mark can not be moved to the right location as of yet. |
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.
Can you add some examples to the readme, and document the relevant config options needed to enable this feature?
import React from "react"; | ||
import ReactDOM from "react-dom"; | ||
import v4 from "uuid/v4"; | ||
import React from 'react'; |
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.
What is changing all the quotes here? Is it an automated tool you're running, or did you actually replace all of these manually?
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.
That is the eslint airbnb base spec that you defined in the package.json. That spec enforces single quotes, and my atom therefore auto-corrected it while saving (I always tried to not commit these parts, but I seem to have missed that once).
package.json
Outdated
}, | ||
"configSchema": { | ||
"expandCode": { | ||
"title": "[experimental] expand code", |
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 needs a clearer name and description. Right now the name seems a bit cryptic and doesn't directly relate to how people would want to use it.
Possible names I'm thinking of include "Extend code to execute", though that still isn't quite right. Any thoughts?
Also, the title should be capitalized in the same way as other settings in Atom.
package.json
Outdated
"title": "Code to expand", | ||
"type": "array", | ||
"default": ["else", "elif", "except", "finally", "\\}", "\\]", "\\)"], | ||
"description": "If the above is enabled, this list will be passed on to a regex, so experts may define their own tokens. In the default setting, else, elif, as well as all closing braces are expanded on." |
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'd say "you" instead of "experts"
lib/main.js
Outdated
const selection = e.getSelectedBufferRange(); | ||
const isSelection = !selection.start.isEqual(selection.end); | ||
if (atom.config.get('hydrogen-python.expandCode') && !isSelection) { | ||
// trim right to avoid empty lines at the end of the selection |
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 a very long block of code that only executes if the particular setting is enabled. I recommend moving it into its own function.
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 wanted to do that, but I couldn't figure out what to do with the next variable. As in: would I make it part of the export and use next as first argument, or would I rather create a separate function? Please let me know what you'd prefer.
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.
My original thought was to have a function that accepts the code (as a string) and returns a modified string.
lib/main.js
Outdated
code = code.trimRight(); | ||
|
||
// read editor settings | ||
const isRunWithoutMove = code.includes(e.lineTextForBufferRow(selection.start.row).trim()) || e.lineTextForBufferRow(selection.start.row).match(/^\s+#/); |
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.
e.lineTextForBufferRow(selection.start.row)
is used twice here, and once more in the code below. Can you make it a variable?
lib/main.js
Outdated
// trim right to avoid empty lines at the end of the selection | ||
code = code.trimRight(); | ||
|
||
// read editor settings |
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.
Comment about reading settings doesn't seem to apply to the code below
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.
Nevermind. Though if you read the config setting to a variable instead of inlining it, the code might be more readable.
lib/main.js
Outdated
const expandCode = [' '].concat(atom.config.get('hydrogen-python.expandCodeList')).join('|'); | ||
const re = new RegExp(`^(${indent}(${expandCode})|\s*#|\s*$)`); | ||
const isBreakpoint = rownum => | ||
e.tokenizedBuffer.tokenizedLines[rownum].tokens.reduce((l, t) => |
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.
Are there better variable names than l,t,ll,tt? (For that matter, e
is probably not the best choice for naming the editor, either)
One more note (doesn't have to be in this PR): if this feature were extended to handle regexes before the currently-selected code, as opposed to just after, it would also be able to properly attach decorators to functions. See nteract/hydrogen#77 for details. |
@nikitakit Thanks for the elaborate review. Will work on them next week! |
@mbroedl This might be the same bug. If I have my cursor at for (Token, in_line), log_line in zip(syn_chunks, log):
# Split log_line on \r\n.
if str(Token) != 'Token.MatchingBracket.Other':
# Since I'm sending one line at a time, and since it's not a
# block, the first line should equal the text sent
# The assert is just a sanity check for now.
log_l = log_line.split('\r\n')
assert log_l[0] == in_line
log_all.extend(log_l[1:])
log_all.append('')
log
syn_chunks
def export_graph(self):
"""
NOTE: unclear whether I actually need to save all the graphs individually, or if I can just overwrite one. Since I'm just writing them to load them into Python, and the next graph shouldn't start writing until I'm done reading the previous one into Python, I can probably just overwrite the same file.
"""
# Export graph to file
cmd = 'qui graph export `"{0}/graph{1}.{2}"\' , as({2}) replace'.format(
self.cache_dir, self.graph_counter, self.graph_format)
self.do(cmd) |
@nikitakit I've worked through your comments and now it should be adjusted accordingly. @kylebarron Thanks for the detailed bug report. After fixing the first one, I could not reproduce the second one,so I assume it's been fixed now as well! |
@nikitakit The latest commit also includes decorators in the code expansion. |
package.json
Outdated
"description": "If the above setting is enabled, this list will be passed to a regex. Any of these items in the list need to occur on the same intentation level as the first line. You may define your own custom elements to modify the code to your preferred behaviour. In the default setting, else, elif, except, finally, as well as all closing braces are expanded on." | ||
}, | ||
"prependCodeList": { | ||
"title": "Code to Expand Before the Execution", |
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.
Selection
makes more sense to me than Execution
here
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 don't think "Selection" is quite right. If there is code selected, the expansion features won't necessarily activate.
Just had a chance to test the feature out out today. Everything appears to work as advertised, and the decorator handling is a nice and useful addition! I'm going to merge this now and put out a new release soon. Unfortunately I didn't get the chance to test this super-thoroughly because I don't have any active projects where I'm using hydrogen every day. I hope that you'd be willing to take a look if any github issues are filed after this feature is out! I understand that there are some limitations because hydrogen doesn't provide the right API for this sort of feature. For example, having a selection and calling run-and-move-down will result in the selection going away. My view on this is there are diminishing returns to building increasingly complex workarounds, and any further effort might be better spent on improving hydrogen core to fix the core problem. The two directions I'd consider exploring are:
|
Thanks for merging @nikitakit .
I believe this is the default behaviour of hydrogen, rather than hydrogen-python. I agree that hydrogen should be the target for major future improvements! |
Should solve #3 in line with what @nikitakit mentioned in nteract/hydrogen#862.
It reads the first line of the selection and appends the indentation to the code passed.
This removes both limitations mentioned in the README.
Further solves nteract/hydrogen#1341 and nteract/hydrogen#1344 I believe.
However, I couldn't find a way to move the tick bubble to the correct line (except for adding a watch and moving it by cheating).
It expands the code line by line if certain (configurable) keywords are found.
Maybe you have an idea about the tickbox?