Replies: 1 comment 2 replies
-
An additional part of this, to address problem (3) I identified, is to write our own mapping to a Roslyn language-agnostic syntax tree in the VS F# tools layer. This would let us leverage things like annotating a ndoe to be renambed and light up specific refactorings. There may also be a different path that implies a change on the Roslyn end to incorporate some F#-isms. But either way, we don't have the tools to enable certain features. |
Beta Was this translation helpful? Give feedback.
2 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
The F# tools offer plenty of features today, and although plenty more could be added with the current system we have in place, it has gaps that realistically prevent several kinds of features to be written.
I identified in #3750 (comment) that we have 3 fundamental gaps in F# tooling for VS today:
What this means, concretely
These really hold for all tooling though, not just F# tools in VS. The following kinds of features are blocked by this issue:
[<Extension>]
attributeignore
to expressionnew
keyword in object construction[<Struct>]
to make a struct a reference typefilter
-->map
with achoose
call)In theory, every one of these features could be built in the F# tools today. In fact, the implement interface code fix does this today. However, the complexity of doing so would be incredibly high. Implementing these would be horribly error-prone and unmaintainable because we're working at the wrong abstraction level. Using the interface implementation code fix as an example, it is several hundred lines of code operating at a document level to identify where to insert new strings. This is simply the wrong layer of abstraction.
Fundamentally, these constructs are about manipulating a syntax tree. We need to be able to add or remove nodes at particular positions, then pass the updated tree to a routine or set of routines that understands how to write it into a document form. Code that directly manipulates a document, like we have today, is horribly complicated.
Things get even more complicated when you consider that formatting matters here. Let's say that you want to scaffold out a new record with the record scaffolding refactoring. This record has 200 fields (which we've seen a customer have). Do you really want that all to be on one line? Of course not? Unfortunately, it now gets complicated because we need to place fields on new lines and make sure all the columns line up correctly. Now repeat this for every single feature that implies some amount of code formatting that adheres to the rest of the user's code. The complexity is just too high to handle this at the document level.
What we'd like to do
@TIHan and I spoke at length about this, since he had prototyped a few things that can help us build features like I previously mentioned.
We're planning on picking one of the more complicated features - union case generation - and using it to help drive the infrastructure improvements that have to be added to the F# compiler service to enable the features to be written in a maintainable way. The feature would include:
If done correctly, this will result in several capabilities added to the F# compiler service that can be used to implement more of the previously-mentioned features. That would then allow all F# editing environments to surface these features and wire them up to their own editor bindings (i.e., we would not push Roslyn APIs into this).
Additionally, this would allow us to rewrite several code fixes and refactorings we have already to be less code and no longer directly manipulate a document.
Prior art
The rider tools for F# have their own lexer/parser that constructs a ReSharper tree that backs the user's source code. It contains APIs to add/remove nodes, identify if things like a newline needs to be added, etc.
Roslyn refactorings and code fixes are all based on syntax trees. There is no direct document manipulation. Any time a refactoring adds or changes code, it changes the tree and passes the changed tree to an engine that knows how to serialize it to disk. Note that Roslyn has a smart diffing engine that doesn't redundantly re-create trees every time they operate on them.
The Visual F# Power Tools enabled a subset of these features for VS 2015. However, since the same limitations existed then, it was a lot of code (600+ lines per feature) of logic to traverse trees and manipuate strings, in addition to requiring their own smaller set of abstractions to make that work a bit easier. We would be unlikely to use their approach.
Beta Was this translation helpful? Give feedback.
All reactions