Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
59d94b7
Add parameter name hints
saul Apr 29, 2020
e955162
Resolve todo
saul Apr 29, 2020
74dd7ec
Use ManagedLanguageParameterNameHintsHighlightingStrategy
saul May 3, 2020
7ed9f73
Fully support argument matching
saul May 3, 2020
939e37f
Merge remote-tracking branch 'upstream/net202' into net202-arg-matching
saul May 3, 2020
0f07629
Fix issues post merge
saul May 3, 2020
83ee9ea
Add more tests
saul May 3, 2020
a28914b
Markups
saul May 4, 2020
c0a621a
Cache arguments on tree nodes
saul May 4, 2020
f944fa5
Named param recovery
saul May 4, 2020
3cba158
Cache matching parameter on FSharpExpressionBase
saul May 4, 2020
4af1694
Revert "Cache matching parameter on FSharpExpressionBase"
saul May 4, 2020
2818642
Moved tests
saul May 4, 2020
ae476c2
Remove todo - no need to cache this
saul May 4, 2020
b72b7e3
Add tests for passing unit arguments
saul May 4, 2020
e29d5fe
Merge remote-tracking branch 'upstream/net202' into net202-arg-matching
saul May 9, 2020
08b7381
Fix test compile error
saul May 9, 2020
c3036f3
Merge remote-tracking branch 'upstream/net202' into net202-arg-matching
saul May 9, 2020
bbe951a
Merge remote-tracking branch 'upstream/net202' into net202-arg-matching
saul May 12, 2020
58a2eb8
Merge branch 'net202-arg-matching' into net202-parameter-name-hints
saul May 12, 2020
35659e6
Fixup errors
saul May 14, 2020
1cf54fb
Merge remote-tracking branch 'upstream/net202' into net202-parameter-…
saul May 17, 2020
314e589
Update tests
saul May 17, 2020
c1a7657
Merge remote-tracking branch 'upstream/net202' into net202-parameter-…
saul May 20, 2020
f7a7c0a
Move test files
saul May 20, 2020
e45dd50
Add more tests
saul May 22, 2020
8f26d72
Merge remote-tracking branch 'upstream/net203' into net202-parameter-…
saul Sep 30, 2020
2c34101
Bring up to date
saul Oct 2, 2020
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
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ let unitTypeName = clrTypeName "Microsoft.FSharp.Core.Unit"


let predefinedFunctionTypes =
[| operatorsModuleTypeName, [| "not"; "id"; "ignore"; "|>"; "<|"; "<>"; "=" |]
[| operatorsModuleTypeName, [| "not"; "id"; "ignore"; "|>"; "<|"; "<>"; "="; "typeof" |]
intrinsicOperatorsTypeName, [| "||"; "&&" |] |]
|> Array.collect (fun (typeName, names) -> [| for name in names -> name, typeName |])
|> dict
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
<Compile Include="src\Daemon\Stages\PipeChainTypeHintStage.fs" />
<Compile Include="src\Daemon\Stages\InferredTypeCodeVisionProvider.fs" />
<Compile Include="src\Daemon\Stages\FSharpSyntaxHighlightingStage.fs" />
<Compile Include="src\Daemon\Stages\ParameterNameHintStage.fs" />
<Compile Include="src\Daemon\UsageChecking\FSharpUsageCheckingService.fs" />
<Compile Include="src\Daemon\Analyzers\ReSpellerHelper.fs" />
<Compile Include="src\Daemon\Analyzers\StringProblemsAnalyzer.fs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Daemon.Stages

open System
open FSharp.Compiler.SourceCodeServices
open JetBrains.ProjectModel
open JetBrains.ReSharper.Daemon.Stages
open JetBrains.ReSharper.Feature.Services.InlayHints
open JetBrains.ReSharper.Feature.Services.ParameterNameHints
open JetBrains.ReSharper.Feature.Services.ParameterNameHints.BlackList
open JetBrains.ReSharper.Feature.Services.ParameterNameHints.ManagedLanguage
open JetBrains.ReSharper.Plugins.FSharp
open JetBrains.ReSharper.Plugins.FSharp.Psi
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Util
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl
open JetBrains.ReSharper.Plugins.FSharp.Psi.Resolve
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
open JetBrains.ReSharper.Psi
open JetBrains.ReSharper.Feature.Services.Daemon
open JetBrains.ReSharper.Psi.Naming
open JetBrains.ReSharper.Psi.Naming.Impl
open JetBrains.ReSharper.Psi.Naming.Settings
open JetBrains.ReSharper.Psi.Tree
open JetBrains.UI.RichText
open JetBrains.Util.Logging

[<Language(typeof<FSharpLanguage>)>]
type FSharpParameterNameHintsOptionStore(optionsStore: ManagedLanguageParameterNameHintsOptionsStore) =
interface ILanguageSpecificParameterNameHintsOptionsStore with
// todo: should we have our own cache key?
override x.GetBlackListCacheKey() = optionsStore.GetBlackListCacheKey()
override x.GetCategory() = optionsStore.GetCategory()
override x.GetLanguageSpecificSettingsEntries(context) = optionsStore.GetLanguageSpecificSettingsEntries(context)
override x.GetGlobalSettingsEntries(context) = optionsStore.GetGlobalSettingsEntries(context)
override x.GetBlackList(settingsStore) = optionsStore.GetBlackList(settingsStore)
override x.PersistBlackList(settingsStore, value) = optionsStore.PersistBlackList(settingsStore, value)

[<Language(typeof<FSharpLanguage>)>]
type ParameterNameHintsHighlightingStrategy() =
inherit ManagedLanguageParameterNameHintsHighlightingStrategy()

override x.GetShortDescription(parameter, argument) = RichText parameter.ShortName

override x.IsShouldBeIgnored(argument: IArgument) =
match argument with
| :? IFSharpExpression as expr -> isNotNull (FSharpMethodInvocationUtil.tryGetNamedArg expr)
| _ -> false

override x.IsShouldBeIgnored(expression: IExpression) = false
override x.IsLast(parameter) = parameter.IsParameterArray || parameter.IsVarArg
override x.CanBeConsideredAsLiteral(argument, expression) =
match expression with
| :? ILiteralExpr -> true
| _ -> isNull argument.MatchingParameter

override x.IsLambdaExpression(context, expression) =
context.ShowForLambdaExpressions && x.IsLambdaExpression expression
override x.IsLambdaExpression(expression) =
match expression with
| :? ILambdaExpr -> true
| _ -> false

override x.IsConstOrEnumMemberReference(context, parameter, expression) =
if not context.ShowForConstantsAndEnumMembers then false else

let refExpr = expression.As<IReferenceExpr>()
if isNull refExpr then false else

let field = refExpr.Reference.Resolve().DeclaredElement.As<IField>()
if isNull field then false else

field.IsStatic && field.IsReadonly || field.IsConstant || field.IsEnumMember

override x.IsUnclearCreationExpression(context, expression) = false
override x.IsMethodInvocation(context, expression) =
match expression with
| :? IAppExpr -> true
| _ -> false

override x.IsIntentionOfArgumentClearFromReferencedElement(context, parameter, uniqueParameterParts, expression) =
if not context.HideIfIntentionOfArgumentIsClearFromUsage then false else

match expression with
| :? IReferenceExpr as refExpr ->
let namingRule = x.GetTypeNamingRule(context, refExpr.GetSourceFile())
NamesHelper.IsLike(context.NameParser, context.NamingPolicyProvider, namingRule, parameter, uniqueParameterParts, refExpr.ShortName)
| _ -> false

override x.IsDefaultExpression(expression) = false

override x.GetExpression(expression, getThroughInvocation, getThroughCast) =
let expr = expression.As<IFSharpExpression>()
if isNull expr then null else

match expr.IgnoreInnerParens() with
| :? ICastExpr as castExpr when getThroughCast ->
x.GetExpression(castExpr.Expression, getThroughInvocation, getThroughCast)
| :? IAppExpr as appExpr ->
x.GetExpression(appExpr.FunctionExpression, getThroughInvocation, getThroughCast)
| expr ->
// todo: unary ops
expr :> _

override x.IsIntentionOfArgumentClearFromInvocation(context, parameter, uniqueParameterParts, expression) =
if not context.HideIfIntentionOfArgumentIsClearFromUsage then false else

// todo: nameof
// todo: invocation e.g. foo.|GetBar|() ?

match expression with
| :? IReferenceExpr as refExpr when FSharpExpressionUtil.isPredefinedFunctionRef "typeof" refExpr ->
let namingRule = x.GetTypeNamingRule(context, refExpr.GetSourceFile())
NamesHelper.IsLike(context.NameParser, context.NamingPolicyProvider, namingRule, parameter, uniqueParameterParts, "type")
| _ -> false

override x.GetTypeNamingRule(context, sourceFile) =
context.NamingRulesCache.GetNamingRule(NamedElementKinds.TypesAndNamespaces, FSharpLanguage.Instance, sourceFile)

override x.GetParametersNamingRule(context, sourceFile) =
context.NamingRulesCache.GetNamingRule(NamedElementKinds.Parameters, FSharpLanguage.Instance, sourceFile)

override x.GetParameterOwnerNamingRule(context, sourceFile) =
context.NamingRulesCache.GetNamingRule(NamedElementKinds.Method, FSharpLanguage.Instance, sourceFile)

override x.IsIntentionOfArgumentClearFromExplicitConversion(context, parameter, uniqueParameterParts, expression) =
if not context.HideIfIntentionOfArgumentIsClearFromUsage then false else

match expression with
| :? ICastExpr as castExpr when isNotNull castExpr.TypeUsage ->
// todo: check type cast expr
false
| _ -> false

type ParameterNameHintHighlightingProcess
(logger: ILogger, fsFile, settings, daemonProcess, namingManager: NamingManager,
nameParser: NameParser, blackListMatcher: IParameterNameHintsBlackListMatcher,
strategy: ManagedLanguageParameterNameHintsHighlightingStrategy,
parameterNameHintsHighlightingProvider: IManagedLanguageParameterNameHintsHighlightingProvider,
customProviders: ICustomManagedLanguageParameterNameHintsHighlightingProvider seq) =
inherit FSharpDaemonStageProcessBase(fsFile, daemonProcess)

let policyProvider = fsFile.GetPsiServices().Naming.Policy.GetPolicyProvider(FSharpLanguage.Instance, fsFile.GetSourceFile(), settings)
let context = ParameterNameHintsHighlightingContext(settings, namingManager, nameParser, policyProvider, blackListMatcher, strategy, customProviders)

let resolveParamOwner (reference: FSharpSymbolReference) =
use compilationContextCookie = CompilationContextCookie.OverrideOrCreate(fsFile.GetResolveContext())
reference.Resolve().DeclaredElement.As<IParametersOwner>()

let resolveMethod (appExpr: IAppExpr) =
let refExpr = appExpr.FunctionExpression.As<IReferenceExpr>()
if isNull refExpr then null else resolveParamOwner refExpr.Reference

override x.Execute(committer) =
use _swc = logger.StopwatchCookie("Adorning parameter name hints", sprintf "sourceFile=%s" daemonProcess.SourceFile.Name)
let consumer = FilteringHighlightingConsumer(daemonProcess.SourceFile, fsFile, settings)
fsFile.ProcessThisAndDescendants(Processor(x, consumer))
committer.Invoke(DaemonStageResult(consumer.Highlightings))

override x.VisitPrefixAppExpr(prefixAppExpr, consumer) =
let mfv =
prefixAppExpr.Reference.TryGetFSharpSymbol()
|> Option.bind (function
| :? FSharpMemberOrFunctionOrValue as mfv -> Some mfv
| _ -> None)

match mfv with
| None -> ()
| Some mfv ->

let method = resolveMethod prefixAppExpr

let invokingExtensionMethod = mfv.IsExtensionMember && Some mfv.ApparentEnclosingEntity <> mfv.DeclaringEntity
let parametersOffset = if invokingExtensionMethod then 1 else 0

let highlightingRange = prefixAppExpr.ArgumentExpression.GetHighlightingRange()
let highlightings =
// todo: is this necessary/does it make a difference?
match method with
| :? IConstructor as constructor ->
parameterNameHintsHighlightingProvider.GetHighlightingsForConstructor(context, prefixAppExpr, constructor, highlightingRange, Action x.CheckForInterrupt)
| _ ->
parameterNameHintsHighlightingProvider.GetHighlightingsForMethod(context, prefixAppExpr, method, parametersOffset, highlightingRange, Action x.CheckForInterrupt)

for highlighting in highlightings do
consumer.AddHighlighting(highlighting)

override x.VisitAttribute(attribute, consumer) =
let constructor = resolveParamOwner(attribute.Reference).As<IConstructor>()
if isNull constructor then () else

let argumentsOwner = attribute :> IArgumentsOwner

let highlightingRange = attribute.ArgExpression.GetHighlightingRange()
let highlightings = parameterNameHintsHighlightingProvider.GetHighlightingsForConstructor(context, argumentsOwner, constructor, highlightingRange, Action x.CheckForInterrupt)

for highlighting in highlightings do
consumer.AddHighlighting(highlighting)

[<DaemonStage(StagesBefore = [| typeof<GlobalFileStructureCollectorStage> |])>]
type ParameterNameHintStage
(logger: ILogger, languageManager: ILanguageManager, namingManager: NamingManager,
nameParser: NameParser, parameterNameHintsOptionsStore: IParameterNameHintsOptionsStore,
blackListManager: IParameterNameHintsBlackListManager,
parameterNameHintsHighlightingProvider: IManagedLanguageParameterNameHintsHighlightingProvider,
customProviders: ICustomManagedLanguageParameterNameHintsHighlightingProvider seq) =
inherit FSharpDaemonStageBase()

override x.IsSupported(sourceFile, processKind) =
processKind = DaemonProcessKind.VISIBLE_DOCUMENT &&
base.IsSupported(sourceFile, processKind) &&
not (sourceFile.LanguageType.Is<FSharpSignatureProjectFileType>())

override x.CreateStageProcess(fsFile, settings, daemonProcess) =
if not (parameterNameHintsOptionsStore.IsEnabled settings) then null else

let blackListMatcher = blackListManager.GetMatcher(FSharpLanguage.Instance, settings)
let strategy = languageManager.GetService<ManagedLanguageParameterNameHintsHighlightingStrategy>(FSharpLanguage.Instance)
ParameterNameHintHighlightingProcess(logger, fsFile, settings, daemonProcess, namingManager, nameParser, blackListMatcher, strategy, parameterNameHintsHighlightingProvider, customProviders) :> _
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ protected FSharpDaemonStageProcessBase(IFSharpFile fsFile, IDaemonProcess daemon
SeldomInterruptChecker = new SeldomInterruptCheckerWithCheckTime(InterruptCheckTime);
}

public void CheckForInterrupt()
{
if (DaemonProcess.InterruptFlag)
throw new OperationCanceledException();
}

public IDaemonProcess DaemonProcess { get; }
public abstract void Execute(Action<DaemonStageResult> committer);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
open System.Drawing

Color.FromArgb(123, 150, 234) |> ignore
Color.FromArgb(123, blue = 234, green = 150) |> ignore
Color.FromArgb(green = 150, blue = 234, red = 123) |> ignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
open System.Drawing

Color.FromArgb(||(0)123, ||(1)150, ||(2)234) |> ignore
Color.FromArgb(||(3)123, blue = 234, green = 150) |> ignore
Color.FromArgb(green = 150, blue = 234, red = 123) |> ignore

---------------------------------------------------------
(0): ReSharper Parameter Name Hint: red, System.Drawing.Color.FromArgb
(1): ReSharper Parameter Name Hint: green, System.Drawing.Color.FromArgb
(2): ReSharper Parameter Name Hint: blue, System.Drawing.Color.FromArgb
(3): ReSharper Parameter Name Hint: red, System.Drawing.Color.FromArgb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
open System.Drawing

let green = 234
Color.FromArgb(123, green, 100) |> ignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
open System.Drawing

let green = 234
Color.FromArgb(||(0)123, green, ||(1)100) |> ignore

---------------------------------------------------------
(0): ReSharper Parameter Name Hint: red, System.Drawing.Color.FromArgb
(1): ReSharper Parameter Name Hint: blue, System.Drawing.Color.FromArgb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
open System.Drawing

let green = 234

Color.FromArgb((123), green, blue=100) |> ignore
Color.FromArgb(123, (green), blue=100) |> ignore
Color.FromArgb(123, green, blue=(100)) |> ignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
open System.Drawing

let green = 234

Color.FromArgb(||(0)(123), green, blue=100) |> ignore
Color.FromArgb(||(1)123, (green), blue=100) |> ignore
Color.FromArgb(||(2)123, green, blue=(100)) |> ignore

---------------------------------------------------------
(0): ReSharper Parameter Name Hint: red, System.Drawing.Color.FromArgb
(1): ReSharper Parameter Name Hint: red, System.Drawing.Color.FromArgb
(2): ReSharper Parameter Name Hint: red, System.Drawing.Color.FromArgb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
open System.IO

let recursive = false
Directory.Delete("", (recursive=false))
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
open System.IO

let recursive = false
Directory.Delete(||(0)"", (recursive=false))

---------------------------------------------------------
(0): ReSharper Parameter Name Hint: path, System.IO.Directory.Delete
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
open System.IO

DirectoryInfo("").Delete()
DirectoryInfo("").Delete ()
DirectoryInfo("").Delete true
DirectoryInfo("").Delete(true)
DirectoryInfo("").Delete (true)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
open System.IO

DirectoryInfo(||(0)"").Delete()
DirectoryInfo(||(1)"").Delete ()
DirectoryInfo(||(2)"").Delete ||(3)true
DirectoryInfo(||(4)"").Delete(||(5)true)
DirectoryInfo(||(6)"").Delete (||(7)true)

---------------------------------------------------------
(0): ReSharper Parameter Name Hint: path, System.IO.DirectoryInfo.DirectoryInfo
(1): ReSharper Parameter Name Hint: path, System.IO.DirectoryInfo.DirectoryInfo
(2): ReSharper Parameter Name Hint: path, System.IO.DirectoryInfo.DirectoryInfo
(3): ReSharper Parameter Name Hint: recursive, System.IO.DirectoryInfo.Delete
(4): ReSharper Parameter Name Hint: path, System.IO.DirectoryInfo.DirectoryInfo
(5): ReSharper Parameter Name Hint: recursive, System.IO.DirectoryInfo.Delete
(6): ReSharper Parameter Name Hint: path, System.IO.DirectoryInfo.DirectoryInfo
(7): ReSharper Parameter Name Hint: recursive, System.IO.DirectoryInfo.Delete
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
open System.IO

DirectoryInfo(path="") |> ignore
DirectoryInfo("").Delete(recursive=true)
DirectoryInfo("").Delete (recursive=true)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
open System.IO

DirectoryInfo(path="") |> ignore
DirectoryInfo(||(0)"").Delete(recursive=true)
DirectoryInfo(||(1)"").Delete (recursive=true)

---------------------------------------------------------
(0): ReSharper Parameter Name Hint: path, System.IO.DirectoryInfo.DirectoryInfo
(1): ReSharper Parameter Name Hint: path, System.IO.DirectoryInfo.DirectoryInfo
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
open System.IO

DirectoryInfo "" |> ignore
DirectoryInfo("") |> ignore
DirectoryInfo ("") |> ignore
(DirectoryInfo "") |> ignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
open System.IO

DirectoryInfo ||(0)"" |> ignore
DirectoryInfo(||(1)"") |> ignore
DirectoryInfo (||(2)"") |> ignore
(DirectoryInfo ||(3)"") |> ignore

---------------------------------------------------------
(0): ReSharper Parameter Name Hint: path, System.IO.DirectoryInfo.DirectoryInfo
(1): ReSharper Parameter Name Hint: path, System.IO.DirectoryInfo.DirectoryInfo
(2): ReSharper Parameter Name Hint: path, System.IO.DirectoryInfo.DirectoryInfo
(3): ReSharper Parameter Name Hint: path, System.IO.DirectoryInfo.DirectoryInfo
Loading