Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
DedSec256 committed Dec 16, 2024
1 parent 313392c commit c476a56
Show file tree
Hide file tree
Showing 21 changed files with 423 additions and 96 deletions.
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 }


[<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 ->
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())
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

0 comments on commit c476a56

Please sign in to comment.