swiftlang / swift-format

Formatting technology for Swift source code
Apache License 2.0
2.44k stars 222 forks source link

Add API to aid in restoring cursor position #662

Open AndrewPrifer opened 10 months ago

AndrewPrifer commented 10 months ago

Currently it seems like it'd be difficult to restore cursor position when formatting with swift-format, since that would require maintaining the cursor's location within the syntax tree so we can correctly place it in the formatted document. This doesn't seem easy to do without hacks though, since swift-format seems to be accepting and returning exclusively text buffers, it doesn't expose anything about its syntax tree.

As a workaround, we could parse the code into a syntax tree before and after formatting with swift-format, and try to determine an equivalence between the nodes in the two trees to see where the cursor ended up, but this is both very redundant processing (swift-format already maintains a syntax tree), and unreliable, since we can't maintain node references between the before/after syntax trees.

Since this is such an essential need, and I can understand the motivation behind not exposing too many of swift-format's internals, I think the best option would be to extend the API such that swift-format can receive the cursor position in the unformatted text, maintain its location in its internal syntax tree, and return the updated cursor position along with the formatted text.

allevato commented 9 months ago

This is an interesting idea, and if we just had pretty-printing to worry about it would be somewhat straightforward; we could insert a cursor token—a zero-length slug—into the stream at its original location and see where it ends up after formatting is done.

But the presence of the pipeline rules that do more comprehensive tree transformations make this more challenging, because they'll completely replace the node with a different subtree, and if the cursor is inside that subtree, it's not immediately obvious how to map it back into the new tree unless every rule becomes cursor-aware.

There might be a way to generalize it. If the cursor was always attached to a token, then we could probably approximate it based on that. We try to reuse existing tokens instead of crafting new ones whenever possible, and if there's a rule that deletes a token wholesale, maybe we can come up with a way to always shift it to some nearest token that's still left in the tree.

ahoppen commented 4 months ago

Tracked in Apple’s issue tracker as rdar://126948293