-
Notifications
You must be signed in to change notification settings - Fork 847
Improve record, union, enum completions #12351
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
Conversation
| | CopyOnUpdate of range: range * path: CompletionPath | ||
| | Constructor of typeName: string | ||
| | New of path: CompletionPath | ||
| | Declaration of isInIdentifier: bool |
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.
Should I remove the field and return Declaration only when the position is outside of the identifier, like in UnionCaseFieldsDeclaration? Or maybe I could get rid of all of this and just reuse CompletionContext.PatternType?
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.
A more general question would be, should there be cases for different kinds of contexts even when those contexts end up with no completions? Reusing CompletionContext.Invalid would yield the same result.
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 I please get an answer here? This is the only thing that's holding up the PR as far as I am concerned.
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 have a strong intuition about this. Could you get the PR green and then we can do a final end-to-end review? It looks very thorough and a great contribution. thanks
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.
Certainly, if you get it green you can leave it as you have it. It's enough. Add /// comments to describe any inconsistency between the cases in this union type
|
Hey @kerams, sorry for delay in reply
Yeah, we do have some completion tests here: fsharp/tests/service/EditorTests.fs Lines 328 to 345 in eccf87b
As well as here: fsharp/tests/FSharp.Compiler.Private.Scripting.UnitTests/CompletionTests.fs Lines 12 to 21 in eccf87b
There are also some VS-specific completion tests here: fsharp/vsintegration/tests/UnitTests/CompletionProviderTests.fs Lines 135 to 158 in eccf87b
and here: fsharp/vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs Lines 81 to 93 in eccf87b
|
|
@kerams This is fantastic work |
|
I agree, reading this made me realize that I didn't need my homemade completion context in FSAC for determining if we're in a string literal, I could just extend this same codebase. Nice inspiration, @kerams :) |
|
Are these tests sufficient? A couple of tests like this started failing: [<Test>]
member public this.``InDeclaration.Bug3176a``() =
AssertCtrlSpaceCompleteContains
[ "type T<'a> = { aaaa : 'a; bbbb : int } " ]
"aa" // marker
[ "aaaa" ] // should contain
[ "bbbb" ] // should not containI'm not sure what is being tested here. That if autocomplete is triggered in the middle of the identifier, the entire identifier appears in the completion list? Why would that be desirable? |
|
Right, but what use is it at declaration site? If I have |
Yes, you are correct, this shouldn't activate at a declaration site |
|
@kerams Could you add these cases These should show completions for a type though note the user may also be about to type |
|
@dsyme, completions for aliases should now only include type-like items. This is now ready for a final review. Prior to this PR, the main completion types looked like this: [<RequireQualifiedAccess>]
type RecordContext =
| CopyOnUpdate of range: range * path: CompletionPath
| Constructor of typeName: string
| New of path: CompletionPath
[<RequireQualifiedAccess>]
type CompletionContext =
/// Completion context cannot be determined due to errors
| Invalid
/// Completing something after the inherit keyword
| Inherit of context: InheritanceContext * path: CompletionPath
/// Completing records field
| RecordField of context: RecordContext
| RangeOperator
/// Completing named parameters\setters in parameter list of constructor\method calls
/// end of name ast node * list of properties\parameters that were already set
| ParameterList of pos * HashSet<string>
| AttributeApplication
| OpenDeclaration of isOpenType: bool
/// Completing pattern type (e.g. foo (x: |))
| PatternTypeThe XmlDoc for I've added Do we want to keep the completion context minimal and reuse I imagine a meticulous context (thinking solely about record, union, enum, type alias completions) would look like this: // NEW
[<RequireQualifiedAccess>]
type RecordDeclarationContext =
| FieldIdentifier
| FieldType
| Other // the caret is somewhere else in a record declaration
[<RequireQualifiedAccess>]
type RecordContext =
| CopyOnUpdate of range: range * path: CompletionPath
| Constructor of typeName: string
| New of path: CompletionPath
| Declaration of context: RecordDeclarationContext // NEW
// NEW
[<RequireQualifiedAccess>]
type UnionDeclarationContext =
| CaseIdentifier
| CaseType
| CaseFieldIdentifier
| Other // the caret is somewhere else in a union declaration
// NEW
[<RequireQualifiedAccess>]
type UnionContext =
| Declaration of context: UnionDeclarationContext
// this could be extended to account for the usage of a case constructor, but I'm not sure we're able to recognize that right now
[<RequireQualifiedAccess>]
type CompletionContext =
/// Completion context cannot be determined due to errors
| Invalid
/// Completing something after the inherit keyword
| Inherit of context: InheritanceContext * path: CompletionPath
/// Completing records field
| RecordField of context: RecordContext
| RangeOperator
/// Completing named parameters\setters in parameter list of constructor\method calls
/// end of name ast node * list of properties\parameters that were already set
| ParameterList of pos * HashSet<string>
| AttributeApplication
| OpenDeclaration of isOpenType: bool
/// Completing pattern type (e.g. foo (x: |))
| PatternType
| TypeAbbreviation // NEW
| Union of context: UnionContext // NEW |
I see, thank you! Perhaps for now add a comment at each of the places where we return an Invalid, saying what the context actually is. |
dsyme
left a comment
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've left two requests for additional comments, otherwise this is great and ready to go, so will mark as approved
| /// Completing union case fields declaration (e.g. 'A of stri|' but not 'B of tex|: string') | ||
| | UnionCaseFieldsDeclaration | ||
|
|
||
| | TypeAbbreviation |
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 add a comment that this covers both type abbreviation type X = int$ and single-case-unions-without-the-bar type X = AB$ , and perhaps rename to cover that.
|
I feel stupid for asking, but... Given that these changes appear to have been merged into main in Dec last year, shouldn't I be seeing them by today (in Visual Studio v17.1.4)? eg. with
$1 still shows 'let' at the top of the list. The reason for my interest is that this seems to be a hugely important area for improvement - and I dread to think how many people who have tried to learn F# have given up after intellisense has misdirected them. I know you have continued your work with #12873 but there seems much more to do. Take a very basic example where a record type has been defined and the coder is trying to use it: I find: |
|
Nope, you need 17.2., which is available as a preview. Also please note that this PR deals with completions when declaring types, therefore it isn't going to affect what you see in your second snippet. There's a lot of room for improvements, no doubt about that. |
|
Thanks for the response. I installed VS 2022 17.2 preview but no luck. Language tools installed appearing in this version appears to be the same as before, maybe that explains why no change: ? Visual F# Tools 17.1.0-beta.21610.4+07b5673e4f2fa7630e78abe37f16b372353a7242 |
|
I see 17.1.0-beta.22178.3+6da0245a7ce4bb8483b8d1f2993c8ecaea967ad9 in 17.2.0 Preview 3.0. The changes here have worked for me since Preview 1 I think. I only have the preview installed, so I have no idea how the 2 versions would interact. |
|
Sorry, didn't realize this was the case (adding a new field) you were trying out. That piece of the puzzle will be fixed with #12873. Try adding a union case though, that should already behave nicely. The shortcoming of this PR is the fact that the type already needs to be present for you not to get completions on the identifier. In other words, the improvement is currently only present when going back to edit an existing field's name. |
|
All good now, thanks so much. I went through all your examples and confirmed that they work as you describe. This work may be difficult (the compiler not always providing you with enough information), but I think it will be a HUGE benefit to people learning F#. In some ways, there is nothing better you can do for a developer than nudge them the right direction, and nothing worse you can do than lead them astray. |


This PR aims to disable autocompletion or provide filtered completion items in the context of (nominal) record, union and enum declarations.
Addressed issues:
#5519
#5974 (even though it's not talking about VS specifically)
Consider the following snippet, $1 - $8 represent places where autocompletion is triggered (i.e. ctrl + spacebar)
Right now, unfiltered completion items (including keywords) are provided at all of these positions. At position $1 the top completion item is the keyword
let, which is obviously wrong.With the changes in this PR, the completions are as follows
ModuleOrNamespaces,Types,UnqualifiedType,ExnCase)@vzarytovskii, are there any tests for completions?