-
Notifications
You must be signed in to change notification settings - Fork 247
[SR-15880] Add ability to swift-format to only re-format certain ranges of a file #297
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
Comments
CC @allevato |
This is the number one feature I'd like to add. Even though we require all code to have the canonical format before it's merged at Google (so we always do full file formats), supporting range formatting would make it easier for us to make targeted changes to the style guide and roll those out without introducing unrelated changes to people's code the first time they format it with the new version. But it's complicated by a few unique problems. I've thought about various approaches to going about it, but I've never had a solid block of time to explore it. I'll jot down some stream-of-consciousness here so there's a record of some of the issues that have been on my mind about it. The simplest case is if the selected line range covers a contiguous block of list-like-items—statements/CodeBlockItems, member decl list items, array/dictionary literal elements. Then each of those can just be formatted independently, seeding the indentation with whatever the indentation of the first line was. If a selection intersects multiple nodes, the desired behavior is less obvious. For example, if someone passes a line range that starts in the middle of a function's argument list and ends in the middle of some loop in the function:
One approach would be to find the nearest ancestor node of both anchors and format everything in that node, but that would modify lines that weren't in the range, which might be unacceptable. But our formatting approach is very context-sensitive—we need to know the indentation of the Another complication is the current model that we use in TokenStreamCreator/PrettyPrinter; a linear stream of formatting commands, which is constructed as the syntax tree is visited by calling methods that indicate what should happen before or after a particular node. This linear stream contains tokens to open/close groups, and those tokens have to be balanced. In most situations, the open/close tokens are symmetrically handled within a particular node's One thing I had hoped to be able to do was completely rewrite TokenStreamCreator/PrettyPrinter to get rid of the linear stream implementation, partly because of that fragility (and because, as special cases became necessary, it's become quite complicated to work with and I'm positive that it's a barrier for new contributions). I've experimented with a tree-based command model (heavily inspired of Javascript's Prettier) that maps more elegantly to the syntax tree, but it's still missing some critical pieces. I don't know if that model necessarily makes it easier to determine the desired behavior when the selected line range doesn't overlap cleanly with sensible syntax nodes, but I think it would solve the imbalance problem. |
Just mentioning that this functionality could be very nice to have 🙂 |
Noticing Saafo's issue, i just want to bump this up and mention that i think this is a very important feature to make an IDE like vscode useable for writing Swift code in existing codebases. |
…es) <#297>. The basic idea here is to insert `enableFormatting` and `disableFormatting` tokens into the print stream when we enter or leave the selection. When formatting is enabled, we print out the tokens as usual. When formatting is disabled, we turn off any output until the next `enableFormatting` token. When that token is hit, we write the original source text from the location of the last `disableFormatting` to the current location. Note that this means that all the APIs need the original source text to be passed in. This initial commit doesn't really have the formatting working well - we need to tune where those tokens are inserted into the stream a bit better. But this does seem to show that the approach should work.
Use nil for `enableFormatting` to indicate that we're done processing the file (otherwise a selection starting at the BOF would output the file twice!). Support selection ranges starting/finishing inside initial trivia.
Do a better job of getting the whitespace right when entering/leaving a selection. For trivia, only enable/disable around comments.
Allow marked text to use `➡️` and `⬅️` to deliniate the start/end of a range of a selection.
Make `Selection` and enum so that we don't need to use optionals everywhere. And use `Range<AbsolutePosition>` rather than a custom type (matching recent refactorings in swift-syntax).
…ted "start:end" pairs (for <#297>).
The basic idea here is to insert `enableFormatting` and `disableFormatting` tokens into the print stream when we enter or leave the selection. When formatting is enabled, we print out the tokens as usual. When formatting is disabled, we turn off any output until the next `enableFormatting` token. When that token is hit, we write the original source text from the location of the last `disableFormatting` to the current location. Note that this means that all the APIs need the original source text to be passed in. A `Selection` is represented as an enum with an `.infinite` case, and a `.ranges` case to indicate either selecting the entire file, or an array of start/end utf-8 offsets. The offset pairs are given with `Range<AbsolutePosition>`, matching the (now common) usage in swift-syntax. For testing, allow marked text to use `⏩` and `⏪` to deliniate the start/end of a range of a selection. The command line now takes an `--offsets` option of comma-separated "start:end" pairs to set the selection for formatting.
The basic idea here is to insert `enableFormatting` and `disableFormatting` tokens into the print stream when we enter or leave the selection. When formatting is enabled, we print out the tokens as usual. When formatting is disabled, we turn off any output until the next `enableFormatting` token. When that token is hit, we write the original source text from the location of the last `disableFormatting` to the current location. Note that this means that all the APIs need the original source text to be passed in. A `Selection` is represented as an enum with an `.infinite` case, and a `.ranges` case to indicate either selecting the entire file, or an array of start/end utf-8 offsets. The offset pairs are given with `Range<AbsolutePosition>`, matching the (now common) usage in swift-syntax. For testing, allow marked text to use `⏩` and `⏪` to deliniate the start/end of a range of a selection. The command line now takes an `--offsets` option of comma-separated "start:end" pairs to set the selection for formatting.
…ore test cases. For formatting a selection (<#297>).
Additional Detail from JIRA
md5: 55c33a2de9a258402756fbffd509321e
Issue Description:
swift-format currently only has the ability to re-format the entire file. Especially when working in projects whose current indentation cannot be fully described by swift-format configuration files and where re-formatting the entire source code is not an option, it makes swift-format basically unusable.
What I do enjoy is git-clang-format’s ability to only re-format the lines that I touched. It would be nice if swift-format had a similar functionality to only format certain ranges of a file.
The text was updated successfully, but these errors were encountered: