Skip to content

Commit

Permalink
RearrangeCode: match clauses
Browse files Browse the repository at this point in the history
  • Loading branch information
auduchinok committed May 12, 2021
1 parent 1b08174 commit b34e6d2
Show file tree
Hide file tree
Showing 34 changed files with 357 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module FSharpGlobalUtil =

let someUnit = Some ()

let (|IsNonNull|_|) value =
let (|NotNull|_|) value =
if isNotNull value then Some value else None


Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,41 @@
module JetBrains.ReSharper.Plugins.FSharp.Psi.Features.RearrangeCode.RearrangeCode

open System
open JetBrains.Diagnostics
open JetBrains.ReSharper.Feature.Services.RearrangeCode
open JetBrains.ReSharper.Plugins.FSharp.Psi
open JetBrains.ReSharper.Plugins.FSharp.Psi.Impl.Tree
open JetBrains.ReSharper.Plugins.FSharp.Psi.Parsing
open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree
open JetBrains.ReSharper.Psi.ExtensionsAPI.Tree
open JetBrains.ReSharper.Psi.Tree
open JetBrains.ReSharper.Psi.Util
open JetBrains.ReSharper.Resources.Shell

// todo: move xmldoc
type FSharpRearrangeableSimpleSwap<'TNode, 'TParent when
'TNode: not struct and 'TNode :> ITreeNode and
'TParent: not struct and 'TParent :> ITreeNode>(title, direction, parentFunc,
childrenFunc: 'TParent -> TreeNodeEnumerable<'TNode>) =
inherit RearrangeableSimpleSwap<'TNode>.SimpleSwapType<'TParent>(title, direction, Func<_,_>(parentFunc),
Func<_,_>(childrenFunc >> Seq.cast))

type RearrangeableCaseFieldDeclaration(field: ICaseFieldDeclaration) =
inherit RearrangeableElementSwap<ICaseFieldDeclaration>(field, "case field declaration", Direction.LeftRight)

override this.GetSiblings() =
UnionCaseFieldDeclarationListNavigator.GetByField(field).NotNull().Fields :> _

[<RearrangeableElementType>]
type RearrangeableCaseFieldDeclarationProvider() =
inherit RearrangeableSingleElementBase<ICaseFieldDeclaration>.TypeBase()

override this.CreateElement(field: ICaseFieldDeclaration): IRearrangeable =
RearrangeableCaseFieldDeclaration(field) :> _
inherit FSharpRearrangeableSimpleSwap<ICaseFieldDeclaration, IUnionCaseFieldDeclarationList>(
"case field declaration", Direction.LeftRight, UnionCaseFieldDeclarationListNavigator.GetByField,
fun l -> l.FieldsEnumerable)


type RearrangeableRecordFieldDeclaration(field: IRecordFieldDeclaration) =
inherit RearrangeableElementSwap<IRecordFieldDeclaration>(field, "record field declaration", Direction.All)

override this.GetSiblings() =
RecordFieldDeclarationListNavigator.GetByFieldDeclaration(field).NotNull().FieldDeclarations :> _

[<RearrangeableElementType>]
type RearrangeableRecordFieldDeclarationProvider() =
inherit RearrangeableSingleElementBase<IRecordFieldDeclaration>.TypeBase()

override this.CreateElement(field: IRecordFieldDeclaration): IRearrangeable =
RearrangeableRecordFieldDeclaration(field) :> _
inherit FSharpRearrangeableSimpleSwap<IRecordFieldDeclaration, IRecordFieldDeclarationList>(
"record field declaration", Direction.All, RecordFieldDeclarationListNavigator.GetByFieldDeclaration,
fun l -> l.FieldDeclarationsEnumerable)


type RearrangeableEnumCaseLikeDeclaration(caseDeclaration: IEnumCaseLikeDeclaration) =
inherit RearrangeableElementSwap<IEnumCaseLikeDeclaration>(caseDeclaration, "enum case like declaration", Direction.All)
type RearrangeableEnumCaseLikeDeclaration(decl: IEnumCaseLikeDeclaration) =
inherit RearrangeableElementSwap<IEnumCaseLikeDeclaration>(decl, "enum case like declaration", Direction.All)

let addBarIfNeeded (caseDeclaration: IEnumCaseLikeDeclaration) =
if isNull caseDeclaration.Bar && isNotNull caseDeclaration.FirstChild then
Expand All @@ -51,14 +46,14 @@ type RearrangeableEnumCaseLikeDeclaration(caseDeclaration: IEnumCaseLikeDeclarat
] |> ignore

override this.GetSiblings() =
match caseDeclaration with
match decl with
| :? IUnionCaseDeclaration as caseDeclaration ->
UnionRepresentationNavigator.GetByUnionCase(caseDeclaration).NotNull().UnionCases |> Seq.cast

| :? IEnumCaseDeclaration as caseDeclaration ->
EnumRepresentationNavigator.GetByEnumCase(caseDeclaration).NotNull().EnumCases |> Seq.cast

| _ -> failwithf $"Unexpected declaration: {caseDeclaration}"
| _ -> failwithf $"Unexpected declaration: {decl}"

override this.Swap(child, target) =
addBarIfNeeded child
Expand All @@ -72,3 +67,98 @@ type RearrangeableEnumCaseLikeDeclarationProvider() =

override this.CreateElement(caseDeclaration: IEnumCaseLikeDeclaration): IRearrangeable =
RearrangeableEnumCaseLikeDeclaration(caseDeclaration) :> _


type RearrangeableMatchClause(matchClause: IMatchClause, matchExpr: IMatchLikeExpr) =
inherit RearrangeableElementSwap<IMatchClause>(matchClause, "match clause", Direction.All)

let isLastClause (clause: IMatchClause) =
clause == matchExpr.Clauses.Last()

let missingIndent (clause: IMatchClause) =
let expr = clause.Expression
isNotNull expr && clause.Indent = expr.Indent

let addIndent (clause: IMatchClause) =
if not (isLastClause clause) then () else

let expr = clause.Expression
if isNull expr || expr.Indent <> clause.Indent then () else

if expr.IsSingleLine then
let oldRange = TreeRange(clause.RArrow.NextSibling, expr.PrevSibling)
ModificationUtil.ReplaceChildRange(oldRange, TreeRange(Whitespace())) |> ignore
else
if clause.RArrow.StartLine.Plus1() < expr.StartLine then
let lineEnding = expr.GetLineEnding()
let oldRange = TreeRange(clause.RArrow.NextSibling, expr.PrevSibling)
ModificationUtil.DeleteChildRange(oldRange)
ModificationUtil.AddChildAfter(clause.RArrow, Whitespace(clause.Indent)) |> ignore
ModificationUtil.AddChildAfter(clause.RArrow, NewLine(lineEnding)) |> ignore

let indentSize = expr.GetIndentSize()
shiftWithWhitespaceBefore indentSize expr

let removeIndent (clause: IMatchClause) (expr: IFSharpExpression) =
let clauseIndent = clause.Indent
let indentDiff = expr.Indent - clauseIndent

shiftWithWhitespaceBefore -indentDiff expr

let arrow = clause.RArrow
let arrowStartLine = arrow.StartLine
let exprStartLine = expr.StartLine

if arrowStartLine.Plus1() >= exprStartLine then
let lineEnding = expr.GetLineEnding()
addNodesAfter arrow [
NewLine(lineEnding)
if arrowStartLine = exprStartLine then
NewLine(lineEnding)
Whitespace(clauseIndent)
] |> ignore

override this.Swap(child, target) =
addIndent child
addIndent target

base.Swap(child, target)

override this.GetSiblings() =
matchExpr.Clauses :> _

override this.MoveUnderPsiTransaction(direction) =
use writeLockCookie = WriteLockCookie.Create(matchClause.IsPhysical())

// todo: comments

match isLastClause matchClause, matchClause.Expression, missingIndent matchClause, direction with
| true, NotNull expr, _, Direction.Down ->
removeIndent matchClause expr
expr :> _

| true, NotNull expr, true, Direction.Up ->
addIndent matchClause
expr :> _

| _ -> base.MoveUnderPsiTransaction(direction)

override this.CanMove(direction) =
base.CanMove(direction) ||

match isLastClause matchClause, matchClause.Expression, missingIndent matchClause, direction with
| true, NotNull expr, _, Direction.Down ->
let arrow = matchClause.RArrow
isNotNull arrow && arrow.StartLine.Plus1() >= expr.StartLine
| true, NotNull _, true, Direction.Up -> true
| _ -> false

[<RearrangeableElementType>]
type RearrangeableMatchClauseProvider() =
inherit RearrangeableSingleElementBase<IMatchClause>.TypeBase()

override this.CreateElement(clause: IMatchClause): IRearrangeable =
let matchExpr = MatchExprNavigator.GetByClause(clause)
if isNull matchExpr then null else

RearrangeableMatchClause(clause, matchExpr) :> _
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// ${DIRECTION:Down}

do
match () with
| a -> ()
| b -> (){caret}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// ${DIRECTION:Down}

do
match () with
| a -> ()
| b ->

()
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// ${DIRECTION:Down}

do
match () with
| a -> ()
| b -> [1
2]{caret}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// ${DIRECTION:Down}

do
match () with
| a -> ()
| b ->

[1
2]
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// ${DIRECTION:Down}

do
match () with
| a -> ()
| b -> fun _ ->
(){caret}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// ${DIRECTION:Down}

do
match () with
| a -> ()
| b ->
(){caret}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// ${DIRECTION:Down}

do
match () with
| a -> ()
| b ->

()
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// ${DIRECTION:Down}

do
match () with
| a -> ()
| b ->

(){caret}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// ${DIRECTION:Down}

do
match () with
| a -> ()
| b ->

()
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// ${DIRECTION:Down}

do
match () with
| a -> ()
| b -> ignore
(){caret}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// ${DIRECTION:Down}

do
match () with
| a -> ()
| b ->

ignore
()
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// ${DIRECTION:Down}

do
match () with
| a -> ()
| b ->
(){caret}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// ${DIRECTION:Down}

do
match () with
| a -> ()
| b ->

()
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// ${DIRECTION:Down}

do
match () with
| a -> ()
| b -> 1{caret}
+ 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// ${DIRECTION:Down}

do
match () with
| a -> ()
| b ->

1
+ 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// ${DIRECTION:Down}

match () with
| a -> ()
| b -> 1{caret}
+ 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// ${DIRECTION:Down}

match () with
| a -> ()
| b ->

1
+ 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// ${DIRECTION:Up}

do
match () with
| a -> ()
| b ->

(){caret}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// ${DIRECTION:Up}

do
match () with
| a -> ()
| b -> ()
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// ${DIRECTION:Up}

match () with
| a -> ()
| b -> (){caret}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// ${DIRECTION:Up}

match () with
| b -> ()
| a -> ()
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// ${DIRECTION:Up}

do
match () with
| a -> ()
| b ->

[1
2]{caret}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// ${DIRECTION:Up}

do
match () with
| a -> ()
| b ->
[1
2]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// ${DIRECTION:Up}

do
match () with
| a -> ()
| b ->

ignore
(){caret}
Loading

0 comments on commit b34e6d2

Please sign in to comment.