Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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 argument types")>]
Copy link
Contributor

@saul saul Nov 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A parameter is a variable in a method definition. When a method is called, the arguments are the data you pass into the method's parameters.

Suggested change
Description = "Annotate function with argument types")>]
Description = "Annotate function 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 argument 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,49 @@ 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.head |> this.IsAnnotated |> not
override this.ExecutePsiTransaction _ =
let binding = dataProvider.GetSelectedElement<IBinding>()

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,44 +78,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 binding = dataProvider.GetSelectedElement<IBinding>()
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 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 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.

This file was deleted.

This file was deleted.

Loading