diff --git a/vsintegration/src/FSharp.Editor/Common/InternalOptionBuilder.fs b/vsintegration/src/FSharp.Editor/Common/InternalOptionBuilder.fs new file mode 100644 index 000000000000..66d78b7845c4 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/Common/InternalOptionBuilder.fs @@ -0,0 +1,13 @@ +module internal Microsoft.VisualStudio.FSharp.Editor.InternalOptionBuilder + +type InternalOptionBuilder() = + member _.Bind(x, f) = Option.bind f x + member _.Return(x) = Some x + member _.ReturnFrom(x) = x + member _.Zero() = Some() + +let internalOption = InternalOptionBuilder() +let inline (|>>) v f = Option.map f v +let inline (|>!) v f = Option.bind f v +let inline (>->) f g v = f v |>> g +let inline (>=>) f g v = f v >>= g diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 015d28448cb2..dd6104d84c8d 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -42,6 +42,7 @@ + diff --git a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitReturnType.fs b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitReturnType.fs index a5d0c6c90e03..02954e80388d 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitReturnType.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitReturnType.fs @@ -19,15 +19,16 @@ open Microsoft.CodeAnalysis.CodeRefactorings open Microsoft.CodeAnalysis.CodeActions open CancellableTasks open System.Diagnostics +open InternalOptionBuilder [] type internal AddExplicitReturnType [] () = inherit CodeRefactoringProvider() static member isValidMethodWithoutTypeAnnotation - (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) (parseFileResults: FSharpParseFileResults) + (funcOrValue: FSharpMemberOrFunctionOrValue) = let typeAnnotationRange = parseFileResults.TryRangeOfReturnTypeHint(symbolUse.Range.Start, false) @@ -36,10 +37,15 @@ type internal AddExplicitReturnType [] () = funcOrValue.IsFunction && parseFileResults.IsBindingALambdaAtPosition symbolUse.Range.Start - (not funcOrValue.IsValue || not isLambdaIfFunction) - && not (funcOrValue.ReturnParameter.Type.IsUnresolved) - && not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.Range.Start) - && not typeAnnotationRange.IsNone + let res = + (not funcOrValue.IsValue || not isLambdaIfFunction) + && not (funcOrValue.ReturnParameter.Type.IsUnresolved) + && not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.Range.Start) + && not typeAnnotationRange.IsNone + + match res with + | true -> Some funcOrValue + | false -> None static member refactor (context: CodeRefactoringContext) @@ -76,6 +82,11 @@ type internal AddExplicitReturnType [] () = do context.RegisterRefactoring(codeAction) + static member ofFSharpMemberOrFunctionOrValue(symbol: FSharpSymbol) = + match symbol with + | :? FSharpMemberOrFunctionOrValue as v -> Some v + | _ -> None + override _.ComputeRefactoringsAsync context = backgroundTask { let document = context.Document @@ -96,23 +107,26 @@ type internal AddExplicitReturnType [] () = |> CancellableTask.start ct let res = - lexerSymbol - |> Option.bind (fun lexer -> - checkFileResults.GetSymbolUseAtLocation( - fcsTextLineNumber, - lexer.Ident.idRange.EndColumn, - textLine.ToString(), - lexer.FullIsland - )) - |> Option.bind (fun symbolUse -> - match symbolUse.Symbol with - | :? FSharpMemberOrFunctionOrValue as v when - AddExplicitReturnType.isValidMethodWithoutTypeAnnotation v symbolUse parseFileResults - -> - Some(symbolUse, v) - | _ -> None) - |> Option.map (fun (symbolUse, memberFunc) -> - AddExplicitReturnType.refactor context (symbolUse, memberFunc, parseFileResults)) + internalOption { + let! lexerSymbol = lexerSymbol + + let! symbolUse = + checkFileResults.GetSymbolUseAtLocation( + fcsTextLineNumber, + lexerSymbol.Ident.idRange.EndColumn, + textLine.ToString(), + lexerSymbol.FullIsland + ) + + let! memberFunc = + symbolUse.Symbol |> AddExplicitReturnType.ofFSharpMemberOrFunctionOrValue + |>! AddExplicitReturnType.isValidMethodWithoutTypeAnnotation symbolUse parseFileResults + + let res = + AddExplicitReturnType.refactor context (symbolUse, memberFunc, parseFileResults) + + return res + } return res } diff --git a/vsintegration/src/FSharp.Editor/Refactor/RemoveExplicitReturnType.fs b/vsintegration/src/FSharp.Editor/Refactor/RemoveExplicitReturnType.fs index f0d35316fa77..299f5c12eb1a 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/RemoveExplicitReturnType.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/RemoveExplicitReturnType.fs @@ -19,6 +19,7 @@ open Microsoft.CodeAnalysis.CodeActions open CancellableTasks open System.Diagnostics open Microsoft.CodeAnalysis.Text +open InternalOptionBuilder [] type internal RemoveExplicitReturnType [] () = @@ -65,9 +66,9 @@ type internal RemoveExplicitReturnType [] () = newSpan static member isValidMethodWithoutTypeAnnotation - (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) (parseFileResults: FSharpParseFileResults) + (funcOrValue: FSharpMemberOrFunctionOrValue) = let returnTypeHintAlreadyPresent = parseFileResults.TryRangeOfReturnTypeHint(symbolUse.Range.Start, false) @@ -77,8 +78,13 @@ type internal RemoveExplicitReturnType [] () = funcOrValue.IsFunction && parseFileResults.IsBindingALambdaAtPosition symbolUse.Range.Start - (not funcOrValue.IsValue || not isLambdaIfFunction) - && returnTypeHintAlreadyPresent + let res = + (not funcOrValue.IsValue || not isLambdaIfFunction) + && returnTypeHintAlreadyPresent + + match res with + | true -> Some funcOrValue + | false -> None static member refactor (context: CodeRefactoringContext) @@ -114,6 +120,11 @@ type internal RemoveExplicitReturnType [] () = do context.RegisterRefactoring(codeAction) + static member ofFSharpMemberOrFunctionOrValue(symbol: FSharpSymbol) = + match symbol with + | :? FSharpMemberOrFunctionOrValue as v -> Some v + | _ -> None + override _.ComputeRefactoringsAsync context = backgroundTask { let document = context.Document @@ -134,23 +145,26 @@ type internal RemoveExplicitReturnType [] () = |> CancellableTask.start ct let res = - lexerSymbol - |> Option.bind (fun lexer -> - checkFileResults.GetSymbolUseAtLocation( - fcsTextLineNumber, - lexer.Ident.idRange.EndColumn, - textLine.ToString(), - lexer.FullIsland - )) - |> Option.bind (fun symbolUse -> - match symbolUse.Symbol with - | :? FSharpMemberOrFunctionOrValue as v when - RemoveExplicitReturnType.isValidMethodWithoutTypeAnnotation v symbolUse parseFileResults - -> - Some(v, symbolUse) - | _ -> None) - |> Option.map (fun (memberFunc, symbolUse) -> - RemoveExplicitReturnType.refactor context memberFunc parseFileResults symbolUse) + internalOption { + let! lexerSymbol = lexerSymbol + + let! symbolUse = + checkFileResults.GetSymbolUseAtLocation( + fcsTextLineNumber, + lexerSymbol.Ident.idRange.EndColumn, + textLine.ToString(), + lexerSymbol.FullIsland + ) + + let! memberFunc = + symbolUse.Symbol |> RemoveExplicitReturnType.ofFSharpMemberOrFunctionOrValue + |>! RemoveExplicitReturnType.isValidMethodWithoutTypeAnnotation symbolUse parseFileResults + + let res = + RemoveExplicitReturnType.refactor context memberFunc parseFileResults symbolUse + + return res + } return res }