Skip to content
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

Split function annotation into argument and return type annotations #202

Open
wants to merge 8 commits into
base: net211
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
**/.DS_Store
NuGet.Config
.gradle/
.gradle/
ReSharper.Fsharp.sln.DotSettings.user
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@
<Compile Include="src\Generate\GenerateProvider.fs" />
<Compile Include="src\Intentions\DataProviders.fs" />
<Compile Include="src\Intentions\FSharpContextActionBase.fs" />
<Compile Include="src\Intentions\SpecifyTypes.fs" />
<Compile Include="src\Intentions\ToRecursiveLetBindingsAction.fs" />
<Compile Include="src\Intentions\ToMultilineRecord.fs" />
<Compile Include="src\Intentions\ToRecursiveModuleAction.fs" />
Expand All @@ -131,11 +132,12 @@
<Compile Include="src\Intentions\IfToElifAction.fs" />
<Compile Include="src\Intentions\NegateConditionActions.fs" />
<Compile Include="src\Intentions\ToMutableAction.fs" />
<Compile Include="src\Intentions\FunctionAnnotationAction.fs" />
<Compile Include="src\Intentions\ToLiteralAction.fs" />
<Compile Include="src\Intentions\SetNameAction.fs" />
<Compile Include="src\Intentions\LetToUseAction.fs" />
<Compile Include="src\Intentions\RenameFileToMatchTypeNameAction.fs" />
<Compile Include="src\Intentions\FunctionReturnTypeAnnotationAction.fs" />
<Compile Include="src\Intentions\FunctionArgumentTypesAnnotationAction.fs" />
<ErrorsGen Include="src\Daemon\Highlightings\Errors.xml">
<Mode>QUICKFIX</Mode>
<Namespace>JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Daemon.QuickFixes</Namespace>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Intentions

open JetBrains.ReSharper.Feature.Services.ContextActions
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree

[<ContextAction(Name = "AnnotateFunctionArgumentTypes", Group = "F#",
Description = "Annotate function with parameter types")>]
type FunctionArgumentTypesAnnotationAction(dataProvider: FSharpContextActionDataProvider) =
inherit SpecifyTypes.FunctionAnnotationActionBase(dataProvider)

override this.IsAnnotated (binding: IBinding) =
match binding.HeadPattern with
| :? IParametersOwnerPat as parametersOwner ->
parametersOwner.ParametersEnumerable |> Seq.forall (fun pat -> pat.IgnoreInnerParens() :? ITypedPat)
| _ -> true

override this.Text = "Add parameter type annotations"
override this.ApplyFunctionAnnotation parametersOwner binding mfv displayContext =
if isNotNull parametersOwner then
SpecifyTypes.specifyArgumentTypes parametersOwner binding mfv displayContext
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Intentions

open JetBrains.ReSharper.Feature.Services.ContextActions
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree

[<ContextAction(Name = "AnnotateFunctionReturnType", Group = "F#",
Description = "Annotate function with return type")>]
type FunctionReturnTypeAnnotationAction(dataProvider: FSharpContextActionDataProvider) =
inherit SpecifyTypes.FunctionAnnotationActionBase(dataProvider)

override this.IsAnnotated (binding: IBinding) = isNotNull binding.ReturnTypeInfo

override x.Text = "Add return type annotation"
override this.ApplyFunctionAnnotation _parametersOwner binding mfv displayContext =
if isNull binding.ReturnTypeInfo then
SpecifyTypes.specifyBindingReturnType binding mfv displayContext
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

open FSharp.Compiler.SourceCodeServices
open JetBrains.Application.Settings
open JetBrains.ReSharper.Feature.Services.ContextActions
open JetBrains.ReSharper.Plugins.FSharp.Psi
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Util
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl
Expand All @@ -12,49 +11,50 @@ open JetBrains.ReSharper.Plugins.FSharp.Services.Formatter
open JetBrains.ReSharper.Psi.ExtensionsAPI
open JetBrains.ReSharper.Psi.ExtensionsAPI.Tree
open JetBrains.ReSharper.Psi.Tree
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Intentions
open JetBrains.ReSharper.Resources.Shell

module SpecifyTypes =
let specifyBindingReturnType (binding: IBinding) (mfv: FSharpMemberOrFunctionOrValue) displayContext =
let typeString =
let fullType = mfv.FullType
if fullType.IsFunctionType then
let specifiedTypesCount =
match binding.HeadPattern with
| :? IParametersOwnerPat as pat -> pat.Parameters.Count
| _ -> 0

let types = FcsTypesUtil.getFunctionTypeArgs fullType
if types.Length <= specifiedTypesCount then mfv.ReturnParameter.Type.Format(displayContext) else

let remainingTypes = types |> List.skip specifiedTypesCount
remainingTypes
|> List.map (fun fcsType ->
let typeString = fcsType.Format(displayContext)
if fcsType.IsFunctionType then sprintf "(%s)" typeString else typeString)
|> String.concat " -> "
else
mfv.ReturnParameter.Type.Format(displayContext)

[<AbstractClass>]
type FunctionAnnotationActionBase(dataProvider: FSharpContextActionDataProvider) =
inherit FSharpContextActionBase(dataProvider)

abstract member IsAnnotated: IBinding -> bool
abstract member ApplyFunctionAnnotation: IParametersOwnerPat -> IBinding -> FSharpMemberOrFunctionOrValue -> FSharpDisplayContext -> unit

override this.IsAvailable _ =
let letBindings = dataProvider.GetSelectedElement<ILetBindings>()
if isNull letBindings then false else

let bindings = letBindings.Bindings
if bindings.Count <> 1 then false else

isAtLetExprKeywordOrNamedPat dataProvider letBindings &&
bindings |> Seq.exactlyOne |> this.IsAnnotated |> not
override this.ExecutePsiTransaction _ =
let letBindings = dataProvider.GetSelectedElement<ILetBindings>()
let binding = letBindings.Bindings |> Seq.exactlyOne

use writeCookie = WriteLockCookie.Create(binding.IsPhysical())
use disableFormatter = new DisableCodeFormatter()

let namedPat = binding.HeadPattern.As<INamedPat>()
if isNull namedPat then () else

let symbolUse = namedPat.GetFSharpSymbolUse()
if isNull symbolUse then () else

let mfv = symbolUse.Symbol :?> FSharpMemberOrFunctionOrValue
let displayContext = symbolUse.DisplayContext

let parametersOwner = namedPat.As<IParametersOwnerPat>()
this.ApplyFunctionAnnotation parametersOwner binding mfv displayContext
let specifyArgumentTypes
(parameterOwner: IParametersOwnerPat)
(binding: IBinding)
(mfv: FSharpMemberOrFunctionOrValue)
(displayContext: FSharpDisplayContext) =
let factory = binding.CreateElementFactory()
let typeUsage = factory.CreateTypeUsage(typeString)

let pat = binding.HeadPattern
let returnTypeInfo = ModificationUtil.AddChildAfter(pat, factory.CreateReturnTypeInfo(typeUsage))

let settingsStore = pat.GetSettingsStoreWithEditorConfig()
if settingsStore.GetValue(fun (key: FSharpFormatSettingsKey) -> key.SpaceBeforeColon) then
ModificationUtil.AddChildBefore(returnTypeInfo, Whitespace()) |> ignore


[<ContextAction(Name = "AnnotateFunction", Group = "F#",
Description = "Annotate function with parameter types and return type")>]
type FunctionAnnotationAction(dataProvider: FSharpContextActionDataProvider) =
inherit FSharpContextActionBase(dataProvider)

let specifyParameterTypes
(parameterOwner: IParametersOwnerPat) (factory: IFSharpElementFactory)
(mfv: FSharpMemberOrFunctionOrValue) displayContext =

let addParens pattern =
let parenPat = factory.CreateParenPat()
Expand All @@ -79,45 +79,36 @@ type FunctionAnnotationAction(dataProvider: FSharpContextActionDataProvider) =

replaceWithCopy parameter parenPat

let isAnnotated (binding: IBinding) =
isNotNull binding.ReturnTypeInfo &&

match binding.HeadPattern with
| :? IParametersOwnerPat as parametersOwner ->
parametersOwner.ParametersEnumerable |> Seq.forall (fun pat -> pat.IgnoreInnerParens() :? ITypedPat)
| _ -> true

override x.Text = "Add type annotations"

override x.IsAvailable _ =
let letBindings = dataProvider.GetSelectedElement<ILetBindings>()
if isNull letBindings then false else
let specifyBindingReturnType
(binding: IBinding)
(mfv: FSharpMemberOrFunctionOrValue)
(displayContext: FSharpDisplayContext) =
let typeString =
let fullType = mfv.FullType
if fullType.IsFunctionType then
let specifiedTypesCount =
match binding.HeadPattern with
| :? IParametersOwnerPat as pat -> pat.Parameters.Count
| _ -> 0

let bindings = letBindings.Bindings
if bindings.Count <> 1 then false else
let types = FcsTypesUtil.getFunctionTypeArgs fullType
if types.Length <= specifiedTypesCount then mfv.ReturnParameter.Type.Format(displayContext) else

isAtLetExprKeywordOrNamedPat dataProvider letBindings && not (isAnnotated bindings.[0])
let remainingTypes = types |> List.skip specifiedTypesCount
remainingTypes
|> List.map (fun fcsType ->
let typeString = fcsType.Format(displayContext)
if fcsType.IsFunctionType then sprintf "(%s)" typeString else typeString)
|> String.concat " -> "
else
mfv.ReturnParameter.Type.Format(displayContext)

override x.ExecutePsiTransaction _ =
let letBindings = dataProvider.GetSelectedElement<ILetBindings>()
let binding = letBindings.Bindings |> Seq.exactlyOne
let factory = binding.CreateElementFactory()
let typeUsage = factory.CreateTypeUsage(typeString)

use writeCookie = WriteLockCookie.Create(binding.IsPhysical())
use disableFormatter = new DisableCodeFormatter()

let namedPat = binding.HeadPattern.As<INamedPat>()
if isNull namedPat then () else

let symbolUse = namedPat.GetFSharpSymbolUse()
if isNull symbolUse then () else

let mfv = symbolUse.Symbol :?> FSharpMemberOrFunctionOrValue
let displayContext = symbolUse.DisplayContext

let parametersOwner = namedPat.As<IParametersOwnerPat>()
if isNotNull parametersOwner then
specifyParameterTypes parametersOwner factory mfv displayContext
let pat = binding.HeadPattern
let returnTypeInfo = ModificationUtil.AddChildAfter(pat, factory.CreateReturnTypeInfo(typeUsage))

if isNull binding.ReturnTypeInfo then
SpecifyTypes.specifyBindingReturnType binding mfv displayContext
let settingsStore = pat.GetSettingsStoreWithEditorConfig()
if settingsStore.GetValue(fun (key: FSharpFormatSettingsKey) -> key.SpaceBeforeColon) then
ModificationUtil.AddChildBefore(returnTypeInfo, Whitespace()) |> ignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

let{caret} f (a: string) (b: int) = sprintf "%s %d" a b
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

let f{caret} (a : string) (b : int) = sprintf "%s %d" a b
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Module

do
let f{caret} (a: string) (b: int) = sprintf "%s %d" a b
()
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Module

let f{caret} ((a, b): int * int) =
a + b
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Module

let f{caret} (_: 'a) =
()
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Module

let f{caret} ([]: 'a list) =
()
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Module

let f{caret} (a as b: int) =
a + b
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Module

let f{caret} (Some 123: int option) =
()
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

let f{caret} (a: string) (b: int) = sprintf "%s %d" a b
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module Module

let f{caret} (a: string) (b: int * ('a * string)): string =
let f{caret} (a: string) (b: int * ('a * string)) =
let b1, (_, b3) = b
sprintf "%s %d %s" a b1 b3
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

let rec f{caret} (a: string) = a + ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Module

let f{caret} (a: seq<string>) =
String.concat ", " a
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

let f{caret} (a: 'a) (b: 'b) = a, b
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
do
{off}

let x{off} = 1
let{off} x{off}: int = 1

let{off} (x{off}: int): int = 1
let{off} ((x{off}: int)): int = 1
let{off} ((x{off}: string)): int = 1

let{on} foo{on} {off}x {off}= {off}()
(){off}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module Module

{off}

let x1{off} = 1
let{off} x2{off}: int = 1

let{off} (x3{off}: int): int = 1
let{off} ((x4{off}: int)): int = 1

{off}[<CompiledName{off}("Foo")>]{off}
let{on} foo{on} {off}x {off}= {off}()
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

let{caret} f (a) b = sprintf "%s %d" a b
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

let{caret} f (a) b: string = sprintf "%s %d" a b
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

let f{caret} (a) b = sprintf "%s %d" a b
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

let f{caret} (a) b : string = sprintf "%s %d" a b
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Module

do
let f{caret} (a: string) b = sprintf "%s %d" a b
()
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Module

do
let f{caret} (a: string) b: string = sprintf "%s %d" a b
()
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

let rec f{caret} a = a + ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

let rec f{caret} a: string = a + ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Module

let f{caret} a =
String.concat ", " a
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module Module

let f{caret} (a: seq<string>): string =
let f{caret} a: string =
String.concat ", " a
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

let f{caret} a b = a, b
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Module

let f{caret} a b: 'a * 'b = a, b
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Module

let f{caret} (a: string * seq<string>): string =
a ||> String.concat
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Module

type NumberPrinter(num) =
// Class members aren't supported yet
member x.print{off}() = sprintf{off} "%d" num

This file was deleted.

Loading