Skip to content

Commit 376bf38

Browse files
committed
Parameter info: support custom operations
1 parent 70ce2d7 commit 376bf38

File tree

9 files changed

+85
-17
lines changed

9 files changed

+85
-17
lines changed

ReSharper.FSharp/src/FSharp.Common/src/Util/FSharpPredefinedType.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ let compilationMappingAttrTypeName = clrTypeName "Microsoft.FSharp.Core.Compilat
1919
[<CompiledName("CompilationRepresentationAttrTypeName")>]
2020
let compilationRepresentationAttrTypeName = clrTypeName "Microsoft.FSharp.Core.CompilationRepresentationAttribute"
2121

22+
[<CompiledName("CustomOperationAttrTypeName")>]
23+
let customOperationAttrTypeName = clrTypeName "Microsoft.FSharp.Core.CustomOperationAttribute"
24+
2225
[<CompiledName("AutoOpenAttrTypeName")>]
2326
let autoOpenAttrTypeName = clrTypeName "Microsoft.FSharp.Core.AutoOpenAttribute"
2427

ReSharper.FSharp/src/FSharp.Psi.Features/src/ParameterInfo/FSharpParameterInfo.fs

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ type FcsParameterInfoCandidateBase<'TSymbol, 'TParameter when 'TSymbol :> FSharp
165165
paramArrayIndex <- -1
166166

167167
let paramGroups = this.ParameterGroups
168-
let paramGroups =
168+
let paramGroups =
169169
if this.SkipLastGroupDescription then
170170
let paramGroups = List(paramGroups)
171171
paramGroups.RemoveAt(paramGroups.Count - 1)
@@ -194,7 +194,7 @@ type FcsParameterInfoCandidateBase<'TSymbol, 'TParameter when 'TSymbol :> FSharp
194194
if isNull parameter then () else
195195

196196
let name = parameter.ShortName
197-
197+
198198
let summary =
199199
if parameter.PresentationLanguage.Is<FSharpLanguage>() then
200200
// todo: implement providing xml in declared element, remove this code
@@ -206,7 +206,7 @@ type FcsParameterInfoCandidateBase<'TSymbol, 'TParameter when 'TSymbol :> FSharp
206206
| _ -> null
207207
else
208208
parameter.GetXMLDescriptionSummary(true)
209-
209+
210210
let description = XmlDocRichTextPresenter.Run(summary, false, CSharpLanguage.Instance)
211211
paramInfos[index] <- ParamPresentationInfo(Name = name, Description = description)
212212
)
@@ -217,7 +217,7 @@ type FcsParameterInfoCandidateBase<'TSymbol, 'TParameter when 'TSymbol :> FSharp
217217
let paramGroups = this.ParameterGroups
218218
if paramGroups.Count = 0 then RichText() else
219219

220-
let curriedParamsCount = paramGroups |> Seq.sumBy Seq.length
220+
let curriedParamsCount = paramGroups |> Seq.sumBy Seq.length
221221
let groupParameters = paramGroups.Count
222222

223223
// Add additional group parameters to highlight group ranges
@@ -357,11 +357,24 @@ type FcsParameterInfoCandidateBase<'TSymbol, 'TParameter when 'TSymbol :> FSharp
357357
type FcsMfvParameterInfoCandidate(mfv, symbolUse, fsContext) =
358358
inherit FcsParameterInfoCandidateBase<FSharpMemberOrFunctionOrValue, FSharpParameter>(mfv, symbolUse, fsContext)
359359

360+
let isCustomOp = mfv.Attributes.HasAttributeInstance(FSharpPredefinedType.customOperationAttrTypeName)
361+
360362
override val ExtendedType = if mfv.IsExtensionMember then Some mfv.ApparentEnclosingEntity else None
361-
override val ParameterGroups = mfv.CurriedParameterGroups
362363
override val ReturnType = if mfv.IsConstructor then None else Some mfv.ReturnParameter.Type
363364
override val XmlDoc = mfv.XmlDoc
364365

366+
override val ParameterGroups =
367+
if not isCustomOp then
368+
mfv.CurriedParameterGroups
369+
else
370+
let firstGroup = mfv.CurriedParameterGroups[0]
371+
if firstGroup.IsEmpty() then [||] else
372+
373+
firstGroup
374+
|> Seq.tail
375+
|> Seq.map (fun p -> Array.singleton p :> IList<_>)
376+
|> Array.ofSeq :> _
377+
365378
override this.GetParamName(parameter) = parameter.Name
366379
override this.GetParamType(parameter) = parameter.Type
367380
override this.IsOptionalParam(parameter) = parameter.IsOptionalArg
@@ -451,7 +464,7 @@ type FcsActivePatternMfvParameterInfoCandidate(apc: FSharpActivePatternCase, mfv
451464
parameterGroups.RemoveAt(parameterGroups.Count - 1)
452465

453466
returnType |> Option.iter (fun returnType ->
454-
let returnGroup =
467+
let returnGroup =
455468
if returnType.IsTupleType then
456469
returnType.GenericArguments |> Seq.map (fun t -> None, t)
457470
else
@@ -676,11 +689,11 @@ type FSharpParameterInfoContextBase<'TNode when 'TNode :> IFSharpTreeNode>(caret
676689

677690
if removeParenRange then
678691
let parenExpr = argGroups[0].As<IParenExpr>()
679-
let range =
692+
let range =
680693
argGroups[0].Node.GetDocumentRange()
681694
.TrimLeft(if isNotNull parenExpr && isNull parenExpr.LeftParen then 0 else 1)
682695
.TrimRight(if isNotNull parenExpr && isNull parenExpr.RightParen then 0 else 1)
683-
range.Contains(caretOffset)
696+
range.Contains(caretOffset)
684697
else
685698
if allowAtLastArgEnd && offset <= lastArgEnd || offset < lastArgEnd then true else
686699

@@ -762,7 +775,7 @@ type FSharpPatternParameterInfoContext(caretOffset, pat: IFSharpPattern, referen
762775
| :? IParametersOwnerPat as parameterOwnerPat ->
763776
parameterOwnerPat.ParametersEnumerable
764777
|> List.ofSeq
765-
|> List.map ParameterInfoArgument.Pattern
778+
|> List.map ParameterInfoArgument.Pattern
766779
| _ -> []
767780

768781

@@ -817,7 +830,7 @@ type FSharpParameterInfoContextFactory() =
817830

818831
let rec isInsideComment checkPrevious (token: ITreeNode) =
819832
token.IsCommentToken() && caretOffset.Offset > token.GetTreeStartOffset().Offset ||
820-
checkPrevious && getTokenType token == FSharpTokenType.NEW_LINE && isInsideComment false token.PrevSibling
833+
checkPrevious && getTokenType token == FSharpTokenType.NEW_LINE && isInsideComment false token.PrevSibling
821834

822835
if isAutoPopup && isInsideComment true token then null else
823836

@@ -844,7 +857,7 @@ type FSharpParameterInfoContextFactory() =
844857
caretOffset.Offset >= contextRange.StartOffset.Offset &&
845858
caretOffset.ToDocumentCoords().Column > contextRange.StartOffset.ToDocumentCoords().Column
846859

847-
let rec tryCreateContext isAutoPopup (caretOffset: DocumentOffset) (expr: IFSharpExpression) =
860+
let rec tryCreateContext isAutoPopup (caretOffset: DocumentOffset) (expr: IFSharpExpression) =
848861
let range = expr.GetDocumentRange()
849862
let expr =
850863
match expr with
@@ -865,7 +878,7 @@ type FSharpParameterInfoContextFactory() =
865878
match expr with
866879
| :? IPrefixAppExpr as appExpr ->
867880
// todo: allow on non-refExpr invoked expressions (lambdas, other apps)
868-
let reference =
881+
let reference =
869882
match appExpr.InvokedReferenceExpression with
870883
| null -> null
871884
| refExpr -> refExpr.Reference
@@ -980,7 +993,7 @@ type FSharpParameterInfoContextFactory() =
980993

981994
match pat with
982995
| :? IParametersOwnerPat as parametersOwnerPat ->
983-
createFromPattern isAutoPopup caretOffset parametersOwnerPat
996+
createFromPattern isAutoPopup caretOffset parametersOwnerPat
984997

985998
| :? IReferencePat as refPat ->
986999
match ParametersOwnerPatNavigator.GetByParameter(refPat) with
@@ -1007,7 +1020,7 @@ type FSharpParameterInfoContextFactory() =
10071020
if caretOffset.Offset <= endOffset.Offset &&
10081021
(isNull argExpr || caretOffset.Offset < argExpr.GetTreeStartOffset().Offset) then
10091022
// Inside invoked type reference name, try to get context from a parent node instead
1010-
let parentExpr = context.GetContainingNode<IFSharpExpression>()
1023+
let parentExpr = context.GetContainingNode<IFSharpExpression>()
10111024
tryCreateFromParentExpr false caretOffset parentExpr else
10121025

10131026
match getSymbols reference with
@@ -1033,7 +1046,7 @@ type FSharpParameterInfoContextFactory() =
10331046

10341047
if caretOffset.Offset >= range.StartOffset.Offset && caretOffset.Offset < range.EndOffset.Offset ||
10351048
caretOffset = endOffset && isNotNull (PrefixAppExprNavigator.GetByArgumentExpression(appExpr)) then
1036-
// Inside invoked function name, try to get context from a parent expression instead
1049+
// Inside invoked function name, try to get context from a parent expression instead
10371050
tryCreateFromParentExpr isAutoPopup caretOffset appExpr else
10381051

10391052
let appExpr = getOutermostPrefixAppExpr contextExpr
@@ -1049,7 +1062,7 @@ type FSharpParameterInfoContextFactory() =
10491062
let typeInherit = TypeInheritNavigator.GetByCtorArgExpression(expr)
10501063
if isNotNull typeInherit then
10511064
typeInherit.Reference, typeInherit.CtorArgExpression else
1052-
1065+
10531066
let attribute = AttributeNavigator.GetByExpression(expr)
10541067
if isNotNull attribute then
10551068
attribute.Reference, attribute.Expression else
@@ -1105,7 +1118,7 @@ type FSharpParameterInfoContextFactory() =
11051118
else
11061119
// This is called again before requesting a new context on reparsed file
11071120
let offset = caretOffset.Offset
1108-
let shouldPopup =
1121+
let shouldPopup =
11091122
let token = getTokenAtOffset true true caretOffset solution
11101123
if isNull token then false else
11111124

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
type FooBuilder() =
2+
member _.Yield _ = 1
3+
4+
[<CustomOperation "create">]
5+
member _.Create(i1: int, s: string, i2: int) = 1
6+
7+
FooBuilder() {
8+
create {caret}
9+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
ParameterInfo is available
2+
--- All invocables: ----
3+
(s: string) (i2: int) : int
4+
Parameter: 0 's: string'
5+
6+
--- Accepted invocables: ---
7+
(s: string) (i2: int) : int
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
type FooBuilder() =
2+
member _.Yield _ = 1
3+
4+
[<CustomOperation "create">]
5+
member _.Create(i1: int, s: string, i2: int) = 1
6+
7+
FooBuilder() {
8+
create "" {caret}
9+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
ParameterInfo is available
2+
--- All invocables: ----
3+
(s: string) (i2: int) : int
4+
Parameter: 1 'i2: int'
5+
6+
--- Accepted invocables: ---
7+
(s: string) (i2: int) : int
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
type FooBuilder() =
2+
member _.Yield _ = 1
3+
4+
[<CustomOperation "create">]
5+
member _.Create(i1: int, s: string, (i2: int, i3: int)) = 1
6+
7+
FooBuilder() {
8+
create "" {caret}
9+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
ParameterInfo is available
2+
--- All invocables: ----
3+
(s: string) (int * int) : int
4+
Parameter: 1 'int * int'
5+
6+
--- Accepted invocables: ---
7+
(s: string) (int * int) : int

ReSharper.FSharp/test/src/FSharp.Tests/FSharpParameterInfoTest.fs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ type FSharpParameterInfoTest() =
1717
[<Test>] member x.``App - Curried - Comment 02``() = x.DoNamedTest()
1818
[<Test>] member x.``App - Curried - Comment 03``() = x.DoNamedTest()
1919

20+
[<Test>] member x.``App - Curried - Custom op 01``() = x.DoNamedTest()
21+
[<Test>] member x.``App - Curried - Custom op 02``() = x.DoNamedTest()
22+
[<Test>] member x.``App - Curried - Custom op 03``() = x.DoNamedTest()
23+
2024
[<Test>] member x.``App - Curried - Nested 01``() = x.DoNamedTest()
2125
[<Test>] member x.``App - Curried - Nested 02``() = x.DoNamedTest()
2226
[<Test>] member x.``App - Curried - Nested 03``() = x.DoNamedTest()

0 commit comments

Comments
 (0)