@@ -18,7 +18,7 @@ module Development.IDE.Plugin.CodeAction
18
18
) where
19
19
20
20
import Language.Haskell.LSP.Types
21
- import Control.Monad (join )
21
+ import Control.Monad (join , guard )
22
22
import Development.IDE.Plugin
23
23
import Development.IDE.GHC.Compat
24
24
import Development.IDE.Core.Rules
@@ -57,6 +57,7 @@ import Data.Function
57
57
import Control.Arrow ((>>>) )
58
58
import Data.Functor
59
59
import Control.Applicative ((<|>) )
60
+ import Safe (atMay )
60
61
61
62
plugin :: Plugin c
62
63
plugin = codeActionPluginWithRules rules codeAction <> Plugin mempty setHandlersCodeLens
@@ -147,6 +148,7 @@ suggestAction dflags packageExports ideOptions parsedModule text diag = concat
147
148
, suggestReplaceIdentifier text diag
148
149
, suggestSignature True diag
149
150
, suggestConstraint text diag
151
+ , removeRedundantConstraints text diag
150
152
, suggestAddTypeAnnotationToSatisfyContraints text diag
151
153
] ++ concat
152
154
[ suggestNewDefinition ideOptions pm text diag
@@ -586,6 +588,83 @@ suggestFunctionConstraint contents Diagnostic{..} missingConstraint
586
588
actionTitle constraint typeSignatureName = " Add `" <> constraint
587
589
<> " ` to the context of the type signature for `" <> typeSignatureName <> " `"
588
590
591
+ -- | Suggests the removal of a redundant constraint for a type signature.
592
+ removeRedundantConstraints :: Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
593
+ removeRedundantConstraints mContents Diagnostic {.. }
594
+ -- • Redundant constraint: Eq a
595
+ -- • In the type signature for:
596
+ -- foo :: forall a. Eq a => a -> a
597
+ -- • Redundant constraints: (Monoid a, Show a)
598
+ -- • In the type signature for:
599
+ -- foo :: forall a. (Num a, Monoid a, Eq a, Show a) => a -> Bool
600
+ | Just contents <- mContents
601
+ -- Account for both "Redundant constraint" and "Redundant constraints".
602
+ , True <- " Redundant constraint" `T.isInfixOf` _message
603
+ , Just typeSignatureName <- findTypeSignatureName _message
604
+ , Just redundantConstraintList <- findRedundantConstraints _message
605
+ , Just constraints <- findConstraints contents typeSignatureName
606
+ = let constraintList = parseConstraints constraints
607
+ newConstraints = buildNewConstraints constraintList redundantConstraintList
608
+ typeSignatureLine = findTypeSignatureLine contents typeSignatureName
609
+ typeSignatureFirstChar = T. length $ typeSignatureName <> " :: "
610
+ startOfConstraint = Position typeSignatureLine typeSignatureFirstChar
611
+ endOfConstraint = Position typeSignatureLine $
612
+ typeSignatureFirstChar + T. length (constraints <> " => " )
613
+ range = Range startOfConstraint endOfConstraint
614
+ in [(actionTitle redundantConstraintList typeSignatureName, [TextEdit range newConstraints])]
615
+ | otherwise = []
616
+ where
617
+ parseConstraints :: T. Text -> [T. Text ]
618
+ parseConstraints t = t
619
+ & (T. strip >>> stripConstraintsParens >>> T. splitOn " ," )
620
+ <&> T. strip
621
+
622
+ stripConstraintsParens :: T. Text -> T. Text
623
+ stripConstraintsParens constraints =
624
+ if " (" `T.isPrefixOf` constraints
625
+ then constraints & T. drop 1 & T. dropEnd 1 & T. strip
626
+ else constraints
627
+
628
+ findRedundantConstraints :: T. Text -> Maybe [T. Text ]
629
+ findRedundantConstraints t = t
630
+ & T. lines
631
+ & head
632
+ & T. strip
633
+ & (`matchRegex` " Redundant constraints?: (.+)" )
634
+ <&> (head >>> parseConstraints)
635
+
636
+ -- If the type signature is not formatted as expected (arbitrary number of spaces,
637
+ -- line feeds...), just fail.
638
+ findConstraints :: T. Text -> T. Text -> Maybe T. Text
639
+ findConstraints contents typeSignatureName = do
640
+ constraints <- contents
641
+ & T. splitOn (typeSignatureName <> " :: " )
642
+ & (`atMay` 1 )
643
+ >>= (T. splitOn " => " >>> (`atMay` 0 ))
644
+ guard $ not $ " \n " `T.isInfixOf` constraints || T. strip constraints /= constraints
645
+ return constraints
646
+
647
+ formatConstraints :: [T. Text ] -> T. Text
648
+ formatConstraints [] = " "
649
+ formatConstraints [constraint] = constraint
650
+ formatConstraints constraintList = constraintList
651
+ & T. intercalate " , "
652
+ & \ cs -> " (" <> cs <> " )"
653
+
654
+ formatConstraintsWithArrow :: [T. Text ] -> T. Text
655
+ formatConstraintsWithArrow [] = " "
656
+ formatConstraintsWithArrow cs = cs & formatConstraints & (<> " => " )
657
+
658
+ buildNewConstraints :: [T. Text ] -> [T. Text ] -> T. Text
659
+ buildNewConstraints constraintList redundantConstraintList =
660
+ formatConstraintsWithArrow $ constraintList \\ redundantConstraintList
661
+
662
+ actionTitle :: [T. Text ] -> T. Text -> T. Text
663
+ actionTitle constraintList typeSignatureName =
664
+ " Remove redundant constraint" <> (if length constraintList == 1 then " " else " s" ) <> " `"
665
+ <> formatConstraints constraintList
666
+ <> " ` from the context of the type signature for `" <> typeSignatureName <> " `"
667
+
589
668
-------------------------------------------------------------------------------------------------
590
669
591
670
suggestNewImport :: PackageExportsMap -> ParsedModule -> Diagnostic -> [(T. Text , [TextEdit ])]
0 commit comments