Closed cartermp closed 7 years ago
About keywords, do not forget to add the proper tag, see http://source.roslyn.io/#Microsoft.CodeAnalysis.Features/Completion/CommonCompletionService.cs,36
One other bit is member suggestions when inside a ctor (sort of F# equivalent to object initialiser syntax). This has never worked in VS2015.
@cartermp about special behavior on the pipe and function composition operators, I don't agree, because completion should not behave differently for those and user defined "pipe-like" operators, like >>=
, >=>
and |>>
.
Also we should distinguish two issues with completion:
.
typed)The latter one is not as obvious, see what C# editor does http://source.roslyn.io/#Microsoft.CodeAnalysis.Features/Completion/CommonCompletionProvider.cs,54b4ce43ce7c9a05,references
@cartermp about special behavior on the pipe and function composition operators, I don't agree, because completion should not behave differently for those and user defined "pipe-like" operators, like >>=, >=> and |>>.
I think |>
is prominent enough that we should consider it separately and as a special case. It occurs like 100x more often than even >>
Pipe-left and compose-left operators ...
It's well-known I'm not a fan of the pipe-left operator except in relatively rare circumstances, for reasons I won't go into here, but mostly to do with readability and beginners-not-having-a-clue-what's-going-on. So I would not recommend any special tooling effort for these unless it drops out of something more general
I think the priorities here would be
Want to add that snippets or rather intelligent code generation (like Resharper does) for class members would be great. I can't recall the correct way to define CLI events or even properties with get/set (in various shapes), mainly because I don't use those often, but I'd like the tooling to help me introduce properties, interface implementation (not like current codefix which implies I know how to add the beginning of interface implementation), events, with wizard based workflow.
For completion, I'd like the items proposed to be enhanced with preference to those that are of valid types for insertion point (Resharper has smart completion https://www.jetbrains.com/help/resharper/2016.3/Coding_Assistance__Code_Completion__Smart.html).
Resharper is also experimenting with postfix completion (https://www.jetbrains.com/help/resharper/2016.3/Reference__Options__Environment__Postfix_Templates.html), and I believe there are opportunities for tooling to go even beyond that (that feature of Resharper is based on templates right now).
Function application should provide better feedback in terms of remaining parameters or indicating the function is curried.
Generally speaking Intellisense ideally should kick in automatically in every situation, the only exception being when declaring an identifier. Because all else is reusing already declared things. We can only take away from that ideal for performance reasons. That's how I see it.
Updated the original text quite a bit. @vasily-kirichenko I'd love some more feedback on the keyword section w.r.t what you're planning to do.
@dsyme Regarding the left pipes, I certainly wouldn't say it's a high priority to get good completion, but since we do have typing information needed to present a good completion list, I think it's worth getting it in there.
@dsyme Regarding the left pipes...
Well, I have to admit that I dislike the over-use of left-pipes so much that I would actively advocate deliberately not doing any tooling to support their use :)
I really, really don't like the routine over-use of left-piping that we sometimes see in F# code. I almost regret putting the operator in the F# core library. I would also advocate deprecating <||
and <|||
altogether :)
Thinking about two things I'd like with match
:
match
propose me a list of things available locally that can be matched, and put the with
already, when I pick an item from the list, if possible, propose to fill up the cases for mevalue.match
if I complete that it does the same as what I described above but picking value
Xamarin Studio's F# snippets can be found here https://github.com/mono/monodevelop/blob/8b17d75ff2dc26c5aa1a6246f6a2ce7d4f65e3be/main/external/fsharpbinding/MonoDevelop.FSharpBinding/Templates/FSharp-templates.xml
"IntelliSense for Pipes" straddles the line between useful and completely untoolable. For example, no meaningful completion can be provided for the following:
let foo xs = xs |>
What would be very handy after I type |>
would be to just show me the type that you know about. In your first example, showing me something like 'a
would be just fine, or Seq<'a>
, or whatever it is you've got. Information, not really autocompletion.
This may be annoying if you have a codebase with lots of one- or two-letter identifiers
You just described the typical F# codebase, IMHO. In general, you have to play very well with one-letter identifiers, and letter-plus-s.
Additionally, we should preselect a local in completion. Examples:
let (>>=) intOpt f = i // Preselect
intOpt
in the completion list here.let foo x y = let resultOfX = x |> ProcessX let resultOfY = y |> ProcessY
r // Preselect `resultOfX`, since it's a local and alphabetically before the other one.
This seems problematic. If the user types m
at that point, you can't put up autocomplete for variables starting with m
since it's likely to be match
. Likewise l
doesn't work.
All F# keywords should show up in autocompletion. Some may object to this and think it's noisy.
No no no no no no a thousand times no no no no. Please no. I beg you. You cannot put up autocomplete for let.
This makes the notion of showing autocompletion difficult, and it would be annoying for people who know they're writing a type of the first kind, but they're getting completion for types and type aliases. Thus, I think no completion should be offered.
I don't know how far you're extending the idea of "after of", but it would be nice to get autocompletion in those types:
| BoxOfStuff of widgets: SomeWidgetT<show autocomplete for SomeWidgetTypeThatIsReallyLong>
FYI, our older code doesn't include identifiers, but we'll almost always include them in newer code. Easier to get to from C#.
Thinking about two things I'd like with match:
if I type match propose me a list of things available locally that can be matched, and put the with already, when I pick an item from the list, if possible, propose to fill up the cases for me
+1 to @smoothdeveloper 's match suggestion. If I start with
let x: Foo // Where Foo is from your example above
match x with <suggest inserting match statements | Bar(bar) | Baz(baz)>
And
let x: Foo // Where Foo is from your example above
match x with
| Bar(bar) -> ...
| <suggest a list of Baz(baz) etc, and prioritize the types that don't have any match case yet>
The second one seems harder, but a heuristic of prioritizing anything that hasn't shown up in any form in a match case (ignoring guards) seems useful.
Annotations at the declaration site, constrainted to only type names and aliases (triggered by :).
A refinement of this that would be nice is to further constrain the types to what's already known (this might be what you meant, of course):
type SomeBigLongType
...
let applyLeftHandBlivetWrench (a: SomeBigLongType) =
let b = a
If I put my cursor at right after the b in let b, then type :
, I'd like the type SomeBigLongTime suggested first, followed by its parent types (ignoring obj).
I don't know if you include this in intellisense, but it'd be nice to just select the line and hit something that says "fill in the explicit type for my identifier for me." Scala on Intellij does this; it's useful. Same for the function declaration; if the cursor's in a function, hit something that fills in the return type of the function.
@banshee - Just to be clear on terminology, Autocompletion doesn't mean "filling in stuff for me". It's the various ways that the Completion List (typically referred to as IntelliSense) can be automatically brought up. Thanks for the feedback. Here are my replies:
What would be very handy after I type
|>
would be to just show me the type that you know about. In your first example, showing me something like 'a would be just fine, or Seq<'a>, or whatever it is you've got. Information, not really autocompletion.
I think what you're getting at is "make a best effort to show me something valid". When it's a generic type, that's anything which can accept any value as input. The completion list could be constrained in some way, but there's simply no telling what should come next. It's no different than offering completion at the call site for passing a parameter to a generic function.
You just described the typical F# codebase, IMHO. In general, you have to play very well with one-letter identifiers, and letter-plus-s.
Can't say one way or another. We don't have, nor collect, data on this. There's also more of a skew towards library code in the open source, where identifiers can be shorter because of a high percentage of very generic code. Our own codebase runs the gamut between one-letter identifiers and very long identifiers. Autocompletion for the VisualFSharp repo would likely be a boon. This is also why the intention is to make it entirely toggle-able. Those who don't like it would be able to turn it off.
This seems problematic. If the user types
m
at that point, you can't put up autocomplete for variables starting withm
since it's likely to be match. Likewisel
doesn't work.
How would this be problematic? There are numerous situations in C# code where a local is preselected, but that local isn't the intended identifier. That's perfectly alright, because as you type, the completion list changes (and often preselection changes to what you may have intended). It's impossible to always select the correct identifier, but that doesn't mean we shouldn't make a best-effort guess in this circumstance.
No no no no no no a thousand times no no no no. Please no. I beg you. You cannot put up autocomplete for let.
Could you explain why?
I don't know how far you're extending the idea of "after of", but it would be nice to get autocompletion in those types:
| BoxOfStuff of widgets: SomeWidgetT<show autocomplete for SomeWidgetTypeThatIsReallyLong>
FYI, our older code doesn't include identifiers, but we'll almost always include them in newer code. Easier to get to from C#.
I've modified to specify that completion after <
in all circumstances should be offered. Good feedback!
+1 to @smoothdeveloper 's match suggestion. If I start with
let x: Foo // Where Foo is from your example above
match x with <suggest inserting match statements | Bar(bar) | Baz(baz)>
And
let x: Foo // Where Foo is from your example above
match x with
| Bar(bar) -> ...
| <suggest a list of Baz(baz) etc, and prioritize the types that don't have any match case yet>
The second one seems harder, but a heuristic of prioritizing anything that hasn't shown up in any form in a match case (ignoring guards) seems useful.
What's being described is a mixture of a template and code generation via codefix/lightbulb from the VisualFSharp Power Tools. Finding a way to get that to happen "with IntelliSense" is likely going to be a gesture along the lines of:
match
--> (autocompletion shows match
template) --> enter (cursor between match
and with
) --> x
--> ctrl+. --> Generate Case (if it is a DU) --> enter
Getting autocompletion to auto-fill cases may not be the intended behavior. It's perfectly valid for someone to use _
to catch the rest of the cases that don't matter, and they'd likely find that annoying. Having the option available as a codefix + having a template show up in autocompletion so you don't have to type the with
boilerplate is a good approach here.
A refinement of this that would be nice is to further constrain the types to what's already known (this might be what you meant, of course):
Yup, that's what I mean. We can't constrain to anything unknown at this time. Eventually we'd like to have a way to allow you to specify types in your solution which live in another project, and have it auto-import the namespace + add the project reference when you hit enter, but that would be a while from now and very much a v2 kind of concept. C#/VB are looking to support this sort of thing eventually as well.
If I put my cursor at right after the
b
inlet b
, then type:
, I'd like the typeSomeBigLongTime
suggested first, followed by its parent types (ignoringobj
).
That's actually quite a tall order which borders of predictive IntelliSense. Note that preselection (which I propose we do for locals) is a beginning step towards that, but analyzing the usage of things to further inform what shows in a Completion List is very much a v2 or v3 sort of thing. It also has the prerequisite that this sort of information can be computed very fast, because completion itself needs to be fast to be seen as useful. If we appear to lag, then autocompletion becomes very annoying.
I don't know if you include this in intellisense, but it'd be nice to just select the line and hit something that says "fill in the explicit type for my identifier for me." Scala on Intellij does this; it's useful. Same for the function declaration; if the cursor's in a function, hit something that fills in the return type of the function.
This actually sounds like a separate feature request which could be serviced by a lightbulb popping up. Could you create a separate issue for this? Thanks!
UpperCase Char can be used as filter to select the most likely words. "SenmenticModel" can be choosed after "SM" is typed. another c# feature :)
@Huqin-China AFAIK this works already (to some extent) in VS2015 and probably before.
@smoothdeveloper tks,i can see that in vs2017 rc4 now
@Huqin-China Yup! That kind of matching works in vs2017 today. One of the features we get for free 😄
Having good intellisense support for xml documentation would be great addition to come on parity with C#/VB.NET on that front.
@cartermp the feature uses the same PatternMatcher that we now use in Go to Everything (former Navigate To).
Just to be clear on terminology, Autocompletion doesn't mean "filling in stuff for me". It's the various ways that the Completion List (typically referred to as IntelliSense) can be automatically brought up.
Got it, I think I'm lumping things into IntelliSense that aren't part of it.
I think what you're getting at is "make a best effort to show me something valid". When it's a generic type, that's anything which can accept any value as input. The completion list could be constrained in some way, but there's simply no telling what should come next. It's no different than offering completion at the call site for passing a parameter to a generic function.
I think I want something here that isn't IntelliSense. If my cursor is after |>, I want a tooltip telling me the type to the left of the |>. I don't think there's much value in trying to figure out what I'm about to type next, since the decision space is so huge at that point.
How would this be problematic?
I'm not a fan of the way the current editors like to constantly pop up completion windows. Visual Studio for Mac is probably the most annoying editor I've had to work with for many years (and I was a Ruby NetBeans user way back in the day, so it has serious competition for that award). Maybe it wouldn't be so bad if it were right sometimes, but the C# editor in particular essentially spends its time pulling up random junk to suggest when I'm naming variables, and then inserting that random junk when I hit the spacebar. Drives me crazy.
Maybe I'd change my mind if the current editors were decent; I used to use Intellij/Android Studio too, and it pops up quite a few things, but the level of quality over there is so much higher than Visual Studio for Mac that it feels like the popups belong. (I actually think the F# editor today is much better than the C# editor; F# doesn't have as many features, but the C# editor compensates by implementing its features badly.)
No no no no no no a thousand times no no no no. Please no. I beg you. You cannot put up autocomplete for let. Could you explain why?
Prefilling 'let' or 'match' is just distracting me for no useful purpose; you're really trying to save me the fractions of a second it takes to finish typing let? I don't understand. Showing me a popup has a cost in my attention; please don't do it when it's not useful.
match --> (autocompletion shows match template) --> enter (cursor between match and with) --> x --> ctrl+. --> Generate Case (if it is a DU) --> enter
Getting autocompletion to auto-fill cases may not be the intended behavior. It's perfectly valid for someone to use _ to catch the rest of the cases that don't matter, and they'd likely find that annoying. Having the option available as a codefix + having a template show up in autocompletion so you don't have to type the with boilerplate is a good approach here.
Totally agree that I don't always want all of the cases filled out; it's one option that would be nice to suggest as a (snippet? not sure what they're called).
I think the part that perhaps fits into IntelliSense is suggesting elements from an ADT when you're matching against one:
match x with
| Bar(bar) -> ...
| **cursor here**
when my cursor is at cursor here, I'd really like you to pull up Baz and Bar. Intellisense would get a gold star if it could figure out that I've already used Bar, so it's less likely (but still possible, of course). It'd also be nice if it showed me Active Patterns that had a type that would be useful here.
A refinement of this that would be nice is to further constrain the types to what's already known (this might be what you meant, of course): Yup, that's what I mean. We can't constrain to anything unknown at this time.
Heh - I think I misphrased that.
I mean I'd like IntelliSense to use the type inference that's already there; basically, if I can hover over the identifier in a let
(with something already in the file off to the right that has a type), and get a tooltip window with the inferred type, it'd be nice to have that type filled in by IntelliSense if I type a :
after the identifier.
@banshee VFPT (and VS2017 thanks to contributors) has that match case filler completion, the drawback of suggestions that were brought in comments is they take long for to show up in editor, what I'm looking for is more like resharper live templates, foreach
for example will guide me with a bunch of tab and completions:
foreach
-> tabI agree with your sentiment on let
, but I really feel the experience on match with
could be closer to that resharper experience with foreach
, upfront, and it really helps beginner to pick-up the right syntax, make that construct appealing to newcomers.
Whenever I want to put a match with
, I'd like to focus on typing/picking the right value, and don't have to type the whole keywords / indentation all that often.
I agree with the sentiment of "too much completion", I've had many X509Certificate pop in my way when doing simple x =>
in C#.
I want completion to propose me prioritized values / members that match the type expected at insertion point, or at least have those being highlighted in some manner.
I agree with your sentiment on let, but I really feel the experience on match with could be closer to that resharper experience with foreach, upfront, and it really helps beginner to pick-up the right syntax, make that construct appealing to newcomers.
OK, I think you could sell me on match
. I'd probably prefer that it didn't kick in with just an m
or ma
(wait for matc
, maybe). Others are problematic though; d
shouldn't get me do
.
One very simple feature: when I type ``
, complete to ````
putting the carret in the middle.
Supporting auto-complete for type providers method with static parameters (a feature added in F# 4.0) is something we are still missing.
This would be desirable:
[<AbstractClass>]
type Bar() =
abstract Foo : int
let b = { new Bar() with member x.■ }
(missing autocomplete behavior at the black square)
Note that when 2nd member is being set/configured, the autocompletion behavior is working, but it would be nice to have the first one working. 😄
@smoothdeveloper mentioned: "it might be possible to reuse logic from VFPT which implements interface to have a codefix also, we could have a dialog showing list of overrideable members"
In places where a boolean is expected (like if or while, and possibly places taking a boolean value), resharper lists members returning bool
first:
This might be generalized to other types, but I think it is useful features.
Closing old discussion; this should really go into the RFC repo as a pull request.
Autocompletion for F# code in VS 2017 is one of the remaining and very visible areas where we're not at "parity" with C#/VB in VS. This is less of a problem in F# due to the brevity of many language constructs, but it is a very noticable part of using .NET in the IDE, and helps reduce typing time. @vasily-kirichenko took a quick crack at it in #1842 and the results already looked promising, but there is some more detailed behavior which we should nail down.
Autocompletion should be able to be turned off in settings. Different "levels" of automplete should also be configurable (such as for operators or keywords).
Below is an attempt to describe what I think some of the behavior of autocomplete should look like and hopefully start a discussion which hammers this out a bit further. Would love to get more suggestions, feedback, and dissenting opinions.
Literals
No completion for literals.
Identifiers - Locals and Parameters
Locals are straightforward. There should be completion when they are used, but no completion at the declaration site:
Additionally, we should preselect a local in completion. Examples:
This may be annoying if you have a codebase with lots of one- or two-letter identifiers, but it's quite a boon to everyone else. Like all of this, it should be able to be toggled.
Identifiers - Functions and Parameters
General behavior for functions and parameters should be pretty straightfoward.
Completion
space
in a curried arg list):
).:
).No completion
=
.Examples:
Types - Records and DUs
Completion
:
in Records, constrained to types and type aliasestype Foo<'T> = T of // 'T should be here
<
character)member
keyword.No Completion
of
in DUs..
in specifying members.Note that in DUs, it's perfectly valid to provide an identifier for the type (or each tupled type) as such:
Even though it's likely more common to not do so:
This makes the notion of showing autocompletion difficult, and it would be annoying for people who know they're writing a type of the first kind, but they're getting completion for types and type aliases. Thus, I think no completion should be offered.
Types - Classes, Abstract Classes, Interfaces, Structs
Completion
No completion
as
.
when defining membersKeywords
All F# keywords should show up in autocompletion. Some may object to this and think it's noisy. An example is VS for Mac:
I think that one way to handle this is to not show the monadic variants of keywords if they are not enclosed by a computation expression which has the appropriate member defined. For example (
|
indicates the caret position):Similarily:
Here some places where keywords should not show completion for keywords:
.
.typeof
is valid, e.g.let t = typeof<MyType>
Operators - infix ops and pipelines
Infix operators should show in completion after a valid identifier, but only if the left-hand type matches the input type for the operator. This should be relatively straightfoward if the types are annotated.
If input types to functions still have yet to be inferred, we should only show completion when one of the operators is in the process of being typed. Examples:
This could prove annoying, because there are plenty of operators with only one or two characters. However, one of the benefits of IntelliSense is a sort of guarantee that your code is being written in a way which will be accepted by the compiler. This may be particularly useful to newcomers who don't necessarily know the ins and outs of different parts of the language.
Any completion with operators should be optional. Conservatively, I think it should be turned off by default.
Pipelines and composition
"IntelliSense for Pipes" straddles the line between useful and completely untoolable. For example, no meaningful completion can be provided for the following:
Perhaps there are a few conventions which could be hard-coded into the engine (such as
xs
meaning a collection type), but we don't know any types which could give us a hint at what should come after the|>
. However, things change if a second pipe is introduced:We now know that
xs
and the output of thefilter
call is of typeint list
. Thus, we can provide some meaningful completions which match the input. That's easier said than done, though...For example, the
List
module name may most likely be expected at the top of the completion list after the second|>
because it's the module which the first pipe came from. Such behavior makes sense forList
,Seq
,Array
, and a few others, but it's a bit less obvious what the correct behavior is for the general case. For example:Placing
Bar
at the top of the completion list is definitely not the correct behavior. Given this, I propose that we don't try to be too "smart". Module names in autocompletion should be available, so if this pattern is used a lot in someone's code, then they still get some completion in the IDE.Similarily,
>>
can work the same way:We know that the left-hand side is
int list -> int list
, so we can compose that with anotherList
function. (Note that chainingSeq
functions with>>
can produce a value restriction [or as I like to call it, being too generic for your own good])Left pipe and composition operators
Pipe-left and compose-left operators should provide completion after they're typed, because the resultant "place" where they'll be "piped into" has a known type (aside from
%A
format strings). They have the same issue regarding Module names in the Completion list, but that's less of a problem. Example:For left pipes
<||
,<|||
is tricky. Because the input type is a tuple, it's perfectly valid to construct it ad-hoc rather than call a function which returns a correct tuple. I propose no completion in this case.Operators - Casting
There should be auotocompletion after typing a casting operator, constrained to a valid type. Examples:
Note that in the first example, I stated that the completion list should be constrained to Exception types and not just types. I'm positive that this is a more difficult problem to solve.
Snippets
Snippets can also be suggested for completion when typing certain constructs. For example, the
for
keyword can lead to three templates:for item in expression do
for identifier = start to finish do
for identifier = start downto finish do
These three snippets can be in the completion list, similar to VS for Mac:
This is obviously dependent on snippet support and the F# snippets being authored (which should include all of the snippets in VS for Mac, IMO - pinging @nosami).
Misc
%A
format strings. We should just show the default list sans any keywords.