Skip to content

Commit

Permalink
Completion/postfix/with: enable template
Browse files Browse the repository at this point in the history
* Fixes copyExpr chooser popup highlighting
* Don't process fields in multiline expressions
  • Loading branch information
auduchinok committed Mar 21, 2023
1 parent 20df0fb commit caf2860
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ type RecordFieldRule() =
CLRLookupItemRelevance.ExpectedTypeMatch |||
CLRLookupItemRelevance.FieldsAndProperties

let fieldRelevance =
CLRLookupItemRelevance.FieldsAndProperties

let getRecordExprFromFieldReference (reference: FSharpSymbolReference) =
let referenceName = reference.GetElement().As<IExpressionReferenceName>()
let fieldBinding = RecordFieldBindingNavigator.GetByReferenceName(referenceName)
Expand Down Expand Up @@ -175,10 +172,7 @@ type RecordFieldRule() =
.WithMatcher(fun _ -> TextualMatcher(info) :> _)

let item =
if emphasize then
item.WithRelevance(if emphasize then matchedFieldRelevance else fieldRelevance)
else
item
if emphasize then item.WithRelevance(matchedFieldRelevance) else item

let tailNodeTypes =
[| FSharpTokenType.WHITESPACE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ open JetBrains.Util
module FSharpPostfixTemplates =
type FormatterImplHelper = JetBrains.ReSharper.Psi.Impl.CodeStyle.FormatterImplHelper

let isSingleLine (expr: IFSharpExpression) =
let formatter = expr.Language.LanguageServiceNotNull().CodeFormatter
not (FormatterImplHelper.HasLineFeedsTo(expr.FirstChild, expr.LastChild, formatter))

let isApplicableTypeUsage (typeUsage: ITypeUsage) =
let typeUsage = skipIntermediateParentsOfSameType<ITypeUsage> typeUsage
let typedExpr = TypedLikeExprNavigator.GetByTypeUsage(typeUsage)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace JetBrains.ReSharper.Plugins.FSharp.Psi.Features.PostfixTemplates

open JetBrains.Diagnostics
open JetBrains.Application.Threading
open JetBrains.ProjectModel
open JetBrains.ReSharper.Feature.Services.Navigation.CustomHighlighting
open JetBrains.ReSharper.Feature.Services.PostfixTemplates
Expand Down Expand Up @@ -33,13 +33,10 @@ module WithPostfixTemplate =
{ CopyExpr: IFSharpExpression
Fields: IReferenceExpr list }

let tryGetContext (refExpr: IReferenceExpr) =
// get 'expr' from 'expr.__' reparsed expr
let expr = refExpr.Qualifier.NotNull()

let tryGetContext (expr: IFSharpExpression) =
let rec loop (acc: RecordExprContext list) (expr: IFSharpExpression) =
match expr with
| :? IReferenceExpr as refExpr ->
| :? IReferenceExpr as refExpr when FSharpPostfixTemplates.isSingleLine refExpr ->
let qualifierExpr = refExpr.Qualifier
if isNotNull qualifierExpr && isRecord qualifierExpr then
let acc = { CopyExpr = qualifierExpr; Fields = refExpr :: acc.Head.Fields } :: acc
Expand Down Expand Up @@ -97,6 +94,8 @@ type WithPostfixTemplate() =
let qualifierExpr = refExpr.Qualifier
isNotNull qualifierExpr && WithPostfixTemplate.isRecord qualifierExpr

override this.IsEnabled _ = true


and WithPostfixTemplateInfo(expressionContext: PostfixExpressionContext) =
inherit PostfixTemplateInfo("with", expressionContext)
Expand All @@ -106,43 +105,53 @@ and WithPostfixTemplateBehavior(info) =
inherit FSharpPostfixTemplateBehaviorBase(info)

override x.ExpandPostfix(context) =
let node = context.Expression :?> IReferenceExpr
let factory = node.CreateElementFactory()
let psiServices = node.GetPsiServices()

let records = WithPostfixTemplate.tryGetContext node

let occurrences =
records
|> Array.ofList
|> Array.rev
|> Array.map (fun context ->
// todo: shorten texts
let range = context.CopyExpr.GetDocumentRange()
WorkflowPopupMenuOccurrence(RichText(context.CopyExpr.GetText()), null, [context], [range])
)

let selectedOccurrence =
let textControl = info.ExecutionContext.TextControl
let popupMenu = psiServices.Solution.GetComponent<WorkflowPopupMenu>()
popupMenu.ShowPopup(textControl.Lifetime, occurrences, CustomHighlightingKind.Other, textControl, null)
let node = context.Expression
node.GetPsiServices().Transactions.Execute(x.ExpandCommandName, fun _ ->
use writeCookie = WriteLockCookie.Create(node.IsPhysical())
use disableFormatter = new DisableCodeFormatter()

if isNull selectedOccurrence then null else

let record = Seq.head selectedOccurrence.Entities
let recordExpr = WithPostfixTemplate.createRecordExpr record.CopyExpr record.Fields factory

psiServices.Transactions.Execute(x.ExpandCommandName, fun _ ->
use writeCookie = WriteLockCookie.Create(node.IsPhysical())
use disableFormatter = new DisableCodeFormatter()

ModificationUtil.ReplaceChild(node, recordExpr) :> ITreeNode
x.GetExpression(context)
)

override x.AfterComplete(textControl, node, _) =
let recordExpr = node :?> IRecordExpr

let innermostExpr = WithPostfixTemplate.getInnermostRecordExpr recordExpr
let range = innermostExpr.GetNavigationRange()
textControl.Caret.MoveTo(range.EndOffset - 2, CaretVisualPlacement.DontScrollIfVisible)
textControl.RescheduleCompletion(node.GetSolution())
let expr = node :?> IFSharpExpression
let factory = expr.CreateElementFactory()
let psiServices = expr.GetPsiServices()
let solution = expr.GetSolution()

let records = WithPostfixTemplate.tryGetContext expr

solution.Locks.ExecuteOrQueueReadLockEx(nameof WithPostfixTemplate, fun _ ->
let occurrences =
records
|> Array.ofList
|> Array.rev
|> Array.map (fun context ->
// todo: shorten texts
let range = context.CopyExpr.GetDocumentRange()
WorkflowPopupMenuOccurrence(RichText(context.CopyExpr.GetText()), null, [context], [range])
)

let selectedOccurrence =
let textControl = info.ExecutionContext.TextControl
let popupMenu = psiServices.Solution.GetComponent<WorkflowPopupMenu>()
popupMenu.ShowPopup(textControl.Lifetime, occurrences, CustomHighlightingKind.Other, textControl, null)

if isNull selectedOccurrence then () else

let record = Seq.head selectedOccurrence.Entities
let recordExpr = WithPostfixTemplate.createRecordExpr record.CopyExpr record.Fields factory

let recordExpr =
psiServices.Transactions.Execute(nameof WithPostfixTemplate, fun _ ->
use writeCookie = WriteLockCookie.Create(expr.IsPhysical())
use disableFormatter = new DisableCodeFormatter()

ModificationUtil.ReplaceChild(expr, recordExpr)
)

let innermostExpr = WithPostfixTemplate.getInnermostRecordExpr recordExpr
let range = innermostExpr.GetNavigationRange()
textControl.Caret.MoveTo(range.EndOffset - 2, CaretVisualPlacement.DontScrollIfVisible)
textControl.RescheduleCompletion(solution)
) |> ignore
14 changes: 14 additions & 0 deletions ReSharper.FSharp/test/data/features/completion/postfix/With 08.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// ${COMPLETE_ITEM:with}
module Module

type R1 =
{ F1: int }

type R2 =
{ F2: R1 }

let r1 = { F1 = 1 }
let r2 = { F2 = r1 }

r2
.F2.{caret}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// ${COMPLETE_ITEM:with}
module Module

type R1 =
{ F1: int }

type R2 =
{ F2: R1 }

let r1 = { F1 = 1 }
let r2 = { F2 = r1 }

{ r2
.F2 with {caret} }
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ type FSharpPostfixCompletionTest() =
[<Test>] member x.``With 05``() = x.DoNamedTest()
[<Test>] member x.``With 06``() = x.DoNamedTest()
[<Test>] member x.``With 07``() = x.DoNamedTest()
[<Test>] member x.``With 08``() = x.DoNamedTest()


[<AbstractClass; FSharpTest>]
Expand Down

0 comments on commit caf2860

Please sign in to comment.