-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Preserve trailing semicolon for Notebooks #8590
Conversation
Current dependencies on/for this PR: This stack of pull requests is managed by Graphite. |
|
783e240
to
3ed4a2f
Compare
#[test] | ||
fn test_notebook_trailing_semicolon() { | ||
let fixtures = Path::new("resources").join("test").join("fixtures"); | ||
let unformatted = fs::read(fixtures.join("trailing_semicolon.ipynb")).unwrap(); | ||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME)) |
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'm not sure if this is the correct place for this test case. We would need to refactor the formatter test suite to include notebook files.
SuiteKind::TopLevel => NodeLevel::TopLevel, | ||
SuiteKind::TopLevel => NodeLevel::TopLevel( | ||
iter.clone() | ||
.next() | ||
.map_or(TopLevelStatementPosition::Last, |_| { | ||
TopLevelStatementPosition::Other | ||
}), | ||
), |
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.
We could avoid cloning the iterator by making it peekable but that involves updating the function signature wherever this iterator is being passed. This is because it expects std::slice::Iter
.
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 clone is effectively free (we just clone the slice reference), so this is fine. The peakable things with the function signatures is annoying though, i had also already hit that
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.
Would it simplify the implementation to insert the semicolon at
following.format().fmt(f)?; |
instead? Then we shouldn't need TopLevelStatementPosition
SuiteKind::TopLevel => NodeLevel::TopLevel, | ||
SuiteKind::TopLevel => NodeLevel::TopLevel( | ||
iter.clone() | ||
.next() | ||
.map_or(TopLevelStatementPosition::Last, |_| { | ||
TopLevelStatementPosition::Other | ||
}), | ||
), |
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 clone is effectively free (we just clone the slice reference), so this is fine. The peakable things with the function signatures is annoying though, i had also already hit that
I'm assuming what you're recommending is to check at the linked position if this is the last statement and then preserve the semicolon, if any. Is this correct? I think that'll involve matching against the relevant nodes where the semicolon needs to be preserved as it's only required for a subset of nodes - |
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 prefer to avoid using the context
whenever possible because it is harder to reason about where the context value is set/changed.
An alternative to the context would have been to parameterized FormatStmt
, FormatStmtAssign
etc (by implementing FormatWithOptions) and explicitly pass a value whether it is the last statement.
SuiteKind::TopLevel => NodeLevel::TopLevel( | ||
iter.clone() | ||
.next() | ||
.map_or(TopLevelStatementPosition::Last, |_| { | ||
TopLevelStatementPosition::Other | ||
}), | ||
), |
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 think this can be simplified to
SuiteKind::TopLevel => NodeLevel::TopLevel(if statements.len() < 2 {
TopLevelStatementPosition::Last
} else {
TopLevelStatementPosition::Other
}),
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.
Yes, that can be done.
I thought about this but I felt like this kind of information is suited well for the context. And, it already provided the node level information. |
Summary
This PR updates the formatter to preserve trailing semicolon for Jupyter Notebooks.
The motivation behind the change is that semicolons in notebooks are typically used to hide the output, for example when plotting. This is highlighted in the linked issue.
The conditions required as to when the trailing semicolon should be preserved are:
Test Plan
Add a new integration test in
ruff_cli
. The test notebook basically acts as a document as to which trailing semicolons are to be preserved.fixes: #8254