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

Inlay hints/PatternTypeHintsStage: support match clause #756

Open
wants to merge 1 commit into
base: main
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
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@ type public Strings() =
static member FSharpTypeHints_TopLevelMembers_Description = Strings.ResourceManager.GetString("FSharpTypeHints_TopLevelMembers_Description")
static member FSharpTypeHints_LocalBindings_Description = Strings.ResourceManager.GetString("FSharpTypeHints_LocalBindings_Description")
static member FSharpTypeHints_ShowPipeReturnTypes_Description = Strings.ResourceManager.GetString("FSharpTypeHints_ShowPipeReturnTypes_Description")
static member FSharpTypeHints_HideSameLinePipe_Description = Strings.ResourceManager.GetString("FSharpTypeHints_HideSameLinePipe_Description")
static member FSharpTypeHints_HideSameLinePipe_Description = Strings.ResourceManager.GetString("FSharpTypeHints_HideSameLinePipe_Description")
static member FSharpTypeHints_MatchPatterns_Description = Strings.ResourceManager.GetString("FSharpTypeHints_MatchPatterns_Description")
static member FSharpTypeHints_MatchPatternsSettings_Header = Strings.ResourceManager.GetString("FSharpTypeHints_MatchPatternsSettings_Header")
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,10 @@
<data name="FSharpTypeHints_HideSameLinePipe_Description" xml:space="preserve">
<value>Hide when |&gt; is on same line as argument</value>
</data>
<data name="FSharpTypeHints_MatchPatterns_Description" xml:space="preserve">
<value>Type hints for match patterns</value>
</data>
<data name="FSharpTypeHints_MatchPatternsSettings_Header" xml:space="preserve">
<value>Match patterns</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,12 @@ type FSharpTypeHintOptions =
[<SettingsEntry(PushToHintMode.PushToShowHints,
DescriptionResourceType = typeof<Strings>,
DescriptionResourceName = nameof(Strings.FSharpTypeHints_LocalBindings_Description))>]
mutable ShowTypeHintsForLocalBindings: PushToHintMode }
mutable ShowTypeHintsForLocalBindings: PushToHintMode

[<SettingsEntry(PushToHintMode.PushToShowHints,
DescriptionResourceType = typeof<Strings>,
DescriptionResourceName = nameof(Strings.FSharpTypeHints_MatchPatterns_Description))>]
mutable ShowTypeHintsForMatchPatterns: PushToHintMode }
Copy link
Member

Choose a reason for hiding this comment

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

What's the idea behind having separate settings for patterns in local bindings and match clauses?



[<OptionsPage("FSharpOptionsPage", "F#", typeof<ProjectModelThemedIcons.Fsharp>)>]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ type public Strings() =
static member FSharpTypeHints_TopLevelMembersSettings_Comment = Strings.ResourceManager.GetString("FSharpTypeHints_TopLevelMembersSettings_Comment")
static member FSharpTypeHints_ShowPipeReturnTypes_Description = Strings.ResourceManager.GetString("FSharpTypeHints_ShowPipeReturnTypes_Description")
static member FSharpTypeHints_HideSameLinePipe_Description = Strings.ResourceManager.GetString("FSharpTypeHints_HideSameLinePipe_Description")
static member FSharpTypeHints_PipesSettings_Header = Strings.ResourceManager.GetString("FSharpTypeHints_PipesSettings_Header")
static member FSharpTypeHints_PipesSettings_Header = Strings.ResourceManager.GetString("FSharpTypeHints_PipesSettings_Header")
static member FSharpTypeHints_MatchPatternsSettings_Header = Strings.ResourceManager.GetString("FSharpTypeHints_MatchPatternsSettings_Header")
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,7 @@
<data name="FSharpTypeHints_PipesSettings_Header" xml:space="preserve">
<value>Pipe operators</value>
</data>
<data name="FSharpTypeHints_MatchPatternsSettings_Header" xml:space="preserve">
<value>Match patterns</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,7 @@ type FSharpTopLevelMembersTypeHintBulbActionsProvider private () =
type FSharpLocalBindingTypeHintBulbActionsProvider private () =
inherit FSharpTypeHintBulbActionsProvider((fun x -> x.ShowTypeHintsForLocalBindings), Strings.FSharpTypeHints_LocalBindingsSettings_Header)
static member val Instance = FSharpLocalBindingTypeHintBulbActionsProvider()

type FSharpMatchClauseTypeHintBulbActionsProvider private () =
inherit FSharpTypeHintBulbActionsProvider((fun x -> x.ShowTypeHintsForMatchPatterns), Strings.FSharpTypeHints_MatchPatternsSettings_Header)
static member val Instance = FSharpMatchClauseTypeHintBulbActionsProvider()
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ type FSharpTypeHintsOptionsPage(lifetime: Lifetime, optionsPageContext: OptionsP
this.AddHeader(Strings.FSharpTypeHints_LocalBindingsSettings_Header) |> ignore
this.AddVisibilityOption(fun (s: FSharpTypeHintOptions) -> s.ShowTypeHintsForLocalBindings)

this.AddHeader(Strings.FSharpTypeHints_MatchPatternsSettings_Header) |> ignore
this.AddVisibilityOption(fun (s: FSharpTypeHintOptions) -> s.ShowTypeHintsForMatchPatterns)


this.AddHeader(Strings.FSharpTypeHints_PipesSettings_Header) |> ignore
this.AddBoolOption((fun (s: FSharpTypeHintOptions) -> s.ShowPipeReturnTypes),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Daemon.Highlightings
open JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Daemon.Stages
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
open JetBrains.ReSharper.Plugins.FSharp.Psi.Util
open JetBrains.ReSharper.Plugins.FSharp.Settings
open JetBrains.ReSharper.Plugins.FSharp.Util
open JetBrains.ReSharper.Psi.Tree
Expand All @@ -20,28 +21,39 @@ open JetBrains.ReSharper.Plugins.FSharp.Psi.Services.Util.TypeAnnotationsUtil
open JetBrains.TextControl.DocumentMarkup.Adornments.IntraTextAdornments

type private NodesRequiringHints =
{ TopLevelNodes: VisibilityConsumer<ITreeNode>; LocalNodes: VisibilityConsumer<ITreeNode> } with
{ TopLevelNodes: VisibilityConsumer<ITreeNode>
LocalNodes: VisibilityConsumer<ITreeNode>
MatchNodes: VisibilityConsumer<ITreeNode> } with

member x.HasVisibleItems =
x.TopLevelNodes.HasVisibleItems ||
x.LocalNodes.HasVisibleItems
x.LocalNodes.HasVisibleItems ||
x.MatchNodes.HasVisibleItems

type private FSharpTypeHintSettings =
{ TopLevelMembers: PushToHintMode; LocalBindings: PushToHintMode } with
{ TopLevelMembers: PushToHintMode
LocalBindings: PushToHintMode
MatchPatterns: PushToHintMode } with

static member Create(settingsStore: IContextBoundSettingsStore) =
{ TopLevelMembers = settingsStore.GetValue(fun (key: FSharpTypeHintOptions) -> key.ShowTypeHintsForTopLevelMembers)
.EnsureInlayHintsDefault(settingsStore)
LocalBindings = settingsStore.GetValue(fun (key: FSharpTypeHintOptions) -> key.ShowTypeHintsForLocalBindings)
.EnsureInlayHintsDefault(settingsStore) }
.EnsureInlayHintsDefault(settingsStore)
MatchPatterns = settingsStore.GetValue(fun (key: FSharpTypeHintOptions) -> key.ShowTypeHintsForMatchPatterns)
.EnsureInlayHintsDefault(settingsStore)}

member x.IsDisabled =
x.TopLevelMembers = PushToHintMode.Never &&
x.LocalBindings = PushToHintMode.Never
x.LocalBindings = PushToHintMode.Never &&
x.MatchPatterns = PushToHintMode.Never


type private MembersVisitor(settings) =
inherit TreeNodeVisitor<NodesRequiringHints>()
let disabledForTopBindings = settings.TopLevelMembers = PushToHintMode.Never
let disabledForLocalBindings = settings.LocalBindings = PushToHintMode.Never
let disabledForMatchPatterns = settings.MatchPatterns = PushToHintMode.Never

let isTopLevelMember (node: ITreeNode) =
match node with
Expand All @@ -50,12 +62,19 @@ type private MembersVisitor(settings) =
| :? IConstructorDeclaration -> true
| _ -> false

let isLocalBinding (node: ITreeNode) =
match node with
| :? ILocalBinding
| :? ILambdaExpr
| :? IForEachExpr -> true
| _ -> false

override x.VisitNode(node, context) =
if settings.LocalBindings = PushToHintMode.Never && isTopLevelMember node then () else
if disabledForLocalBindings && disabledForMatchPatterns && isTopLevelMember node then () else

for child in node.Children() do
if settings.TopLevelMembers = PushToHintMode.Never &&
isTopLevelMember child then x.VisitNode(child, context) else
if disabledForTopBindings && isTopLevelMember child || disabledForLocalBindings && isLocalBinding child
then x.VisitNode(child, context) else

match child with
| :? IFSharpTreeNode as treeNode -> treeNode.Accept(x, context)
Expand Down Expand Up @@ -103,6 +122,14 @@ type private MembersVisitor(settings) =

x.VisitNode(forEachExpr, context)

override x.VisitMatchClause(matchClause, context) =
if not disabledForMatchPatterns then
let result = collectTypeHintAnchorsForMatchClause matchClause
context.MatchNodes.AddRange(result)

x.VisitNode(matchClause, context)


type private PatternsHighlightingProcess(fsFile, settingsStore: IContextBoundSettingsStore, daemonProcess: IDaemonProcess, settings) =
inherit FSharpDaemonStageProcessBase(fsFile, daemonProcess)
static let defaultDisplayContext = FSharpDisplayContext.Empty.WithShortTypeNames(true)
Expand Down Expand Up @@ -153,18 +180,6 @@ type private PatternsHighlightingProcess(fsFile, settingsStore: IContextBoundSet

let rec getHintForPattern (pattern: IFSharpPattern) pushToHintMode actionsProvider =
match pattern with
| :? IParametersOwnerPat as pattern ->
let asPat = AsPatNavigator.GetByLeftPattern(pattern.IgnoreParentParens())
if isNull asPat then ValueNone else

let reference = pattern.Reference
if isNull reference then ValueNone else

match reference.GetFcsSymbol() with
| :? FSharpActivePatternCase ->
getHintForPattern (asPat.RightPattern.IgnoreInnerParens()) pushToHintMode actionsProvider
| _ -> ValueNone

| :? IReferencePat as refPat ->
let symbolUse = refPat.GetFcsSymbolUse()
if isNull symbolUse then ValueNone else
Expand All @@ -181,7 +196,25 @@ type private PatternsHighlightingProcess(fsFile, settingsStore: IContextBoundSet
createTypeHintHighlighting fcsType defaultDisplayContext range pushToHintMode actionsProvider false
|> ValueSome

| _ -> ValueNone
| :? IParametersOwnerPat as pattern ->
Copy link
Member

Choose a reason for hiding this comment

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

Any reason for this change?

let asPat = AsPatNavigator.GetByLeftPattern(pattern.IgnoreParentParens())
if isNull asPat then ValueNone else

let reference = pattern.Reference
if isNull reference then ValueNone else

match reference.GetFcsSymbol() with
| :? FSharpActivePatternCase ->
getHintForPattern (asPat.RightPattern.IgnoreInnerParens()) pushToHintMode actionsProvider
| _ -> ValueNone

| pattern ->
let fcsType = pattern.TryGetFcsType()
if isNull fcsType then ValueNone else

let range = pattern.GetDocumentRange().EndOffsetRange()
createTypeHintHighlighting fcsType defaultDisplayContext range pushToHintMode actionsProvider false
|> ValueSome

let rec getHighlighting (node: ITreeNode) pushToHintMode actionsProvider =
match node with
Expand All @@ -193,7 +226,10 @@ type private PatternsHighlightingProcess(fsFile, settingsStore: IContextBoundSet

| _ -> ValueNone

let adornNodes (topLevelNodes : ITreeNode ICollection) (localNodes : ITreeNode ICollection) =
let adornNodes
(topLevelNodes: ITreeNode ICollection)
(localNodes: ITreeNode ICollection)
(matchClauses: ITreeNode ICollection) =
let highlightingConsumer = FilteringHighlightingConsumer(daemonProcess.SourceFile, fsFile, settingsStore)

let inline adornNodes nodes pushToHintMode actionsProvider =
Expand All @@ -206,6 +242,7 @@ type private PatternsHighlightingProcess(fsFile, settingsStore: IContextBoundSet

adornNodes topLevelNodes settings.TopLevelMembers FSharpTopLevelMembersTypeHintBulbActionsProvider.Instance
adornNodes localNodes settings.LocalBindings FSharpLocalBindingTypeHintBulbActionsProvider.Instance
adornNodes matchClauses settings.MatchPatterns FSharpMatchClauseTypeHintBulbActionsProvider.Instance

highlightingConsumer.CollectHighlightings()

Expand All @@ -214,22 +251,26 @@ type private PatternsHighlightingProcess(fsFile, settingsStore: IContextBoundSet
// Intersect them to ensure commit doesn't throw
let documentRange = daemonProcess.Document.GetDocumentRange()
let visibleRange = daemonProcess.VisibleRange.Intersect(&documentRange)
let consumer = { TopLevelNodes = VisibilityConsumer(visibleRange, _.GetNavigationRange())
LocalNodes = VisibilityConsumer(visibleRange, _.GetNavigationRange()) }
let consumer = {
TopLevelNodes = VisibilityConsumer(visibleRange, _.GetNavigationRange())
Copy link
Member

Choose a reason for hiding this comment

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

Please reformat.

LocalNodes = VisibilityConsumer(visibleRange, _.GetNavigationRange())
MatchNodes = VisibilityConsumer(visibleRange, _.GetNavigationRange()) }
fsFile.Accept(MembersVisitor(settings), consumer)

let topLevelNodes = consumer.TopLevelNodes
let localNodes = consumer.LocalNodes
let matchNodes = consumer.MatchNodes

// Partition the expressions to adorn by whether they're visible in the viewport or not
let remainingHighlightings =
if consumer.HasVisibleItems then
// Adorn visible expressions first
let visibleHighlightings = adornNodes topLevelNodes.VisibleItems localNodes.VisibleItems
let visibleHighlightings =
adornNodes topLevelNodes.VisibleItems localNodes.VisibleItems matchNodes.VisibleItems
committer.Invoke(DaemonStageResult(visibleHighlightings, visibleRange))

// Finally adorn expressions that aren't visible in the viewport
adornNodes topLevelNodes.NonVisibleItems localNodes.NonVisibleItems
adornNodes topLevelNodes.NonVisibleItems localNodes.NonVisibleItems matchNodes.NonVisibleItems

committer.Invoke(DaemonStageResult remainingHighlightings)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
module JetBrains.ReSharper.Plugins.FSharp.Psi.Services.Util.TypeAnnotationsUtil

open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
open JetBrains.ReSharper.Psi.Tree

let rec private visitPattern (acc: ITreeNode list) (pattern: IFSharpPattern) =
let rec private tryVisitCompositePattern acc (pattern: IFSharpPattern) =
match pattern.IgnoreInnerParens() with
| :? IReferencePat -> acc
| _ -> visitPattern acc pattern

and private visitPattern (acc: ITreeNode list) (pattern: IFSharpPattern) =
match pattern with
| null
| :? IConstPat
Expand Down Expand Up @@ -41,6 +47,23 @@ let rec private visitPattern (acc: ITreeNode list) (pattern: IFSharpPattern) =
andPat.PatternsEnumerable
|> Seq.fold visitPattern acc

| :? IOrPat as orPat ->
visitPattern acc orPat.Pattern1

| :? IListConsPat as listConsPat ->
let rec visit (pat: IFSharpPattern) acc =
match pat.IgnoreInnerParens() with
| :? IListConsPat as pat -> visit pat.TailPattern (tryVisitCompositePattern acc pat.HeadPattern)
| pat -> if pat :? ITypedPat then acc else pat :: acc

visit listConsPat acc

| :? IArrayOrListPat as arrayOrListPat ->
let acc = (arrayOrListPat : ITreeNode) :: acc

arrayOrListPat.PatternsEnumerable
|> Seq.fold tryVisitCompositePattern acc

| _ -> acc

let private collectPatternsRequiringAnnotations acc (parametersOwner: IParameterOwnerMemberDeclaration) =
Expand Down Expand Up @@ -84,3 +107,7 @@ let collectTypeHintAnchorsForConstructor (ctor: IConstructorDeclaration) =
let collectTypeHintAnchorsForEachExpr (forEachExpr: IForEachExpr) =
if isNull forEachExpr.InExpression then []
else visitPattern [] forEachExpr.Pattern

let collectTypeHintAnchorsForMatchClause (matchClause: IMatchClause) =
if isNull matchClause.RArrow then []
else visitPattern [] matchClause.Pattern
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module Test

let _ = function x -> ()

match [Some 5] with
| [] -> ()
| [x] -> ()
| [x: int option] -> ()
| [x; y] -> ()
| [x; y: int option] -> ()
| [_; x] -> ()
| [_; _] -> ()
| [x; _; _] -> ()
| [x; _; Some 5] -> ()
| x :: tail -> ()
| _ :: (tail: int option list) -> ()
| x: int option :: tail -> ()
| x: int option :: _ -> ()
| x :: (tail: int option list) -> ()
| ((x :: tail): int option list) -> ()
| _ :: _ -> ()
| x :: _ -> ()
| _ :: x :: _ -> ()
| x :: _ :: _ -> ()
| x :: Some y :: _ -> ()
| x :: (y :: _) -> ()
| x :: y :: [] -> ()
| x :: [y] -> ()
| Some(x) :: tail -> ()
| [Some(x)]
| [_; Some(x)] when let x = 5 in true ->
let y = 5 in ()

match [[5]] with
| [[]] -> ()
| [[]; x] -> ()

match [|5|] with
| [||] -> ()
| [|x|] -> ()
| [|x; y|] -> ()

exception MyException of string
try () with | MyException(x) -> ()
Loading
Loading