{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RecordWildCards #-}

module Ormolu.Imports.Grouping
  ( Import (..),
    prepareExistingGroups,
    groupImports,
  )
where

import Data.Bifunctor (Bifunctor (..))
import Data.Foldable (toList)
import Data.Function (on)
import Data.List (groupBy, minimumBy, sortOn)
import Data.List.NonEmpty (NonEmpty)
import Data.List.NonEmpty qualified as NonEmpty
import Data.Set (Set)
import Data.Set qualified as Set
import Distribution.ModuleName qualified as Cabal
import GHC.Hs (GhcPs, getLocA)
import Language.Haskell.Syntax (LImportDecl, ModuleName, moduleNameString)
import Ormolu.Config (ImportGroup (..), ImportGroupRule (..), ImportGrouping (..), ImportModuleMatcher (..))
import Ormolu.Config qualified as Config
import Ormolu.Utils (ghcModuleNameToCabal, groupBy', separatedByBlank)
import Ormolu.Utils.Glob (matchesGlob)

newtype ImportGroups = ImportGroups (NonEmpty ImportGroup)

data Import = Import
  { Import -> ModuleName
importName :: ModuleName,
    Import -> Bool
importQualified :: Bool
  }

importGroupSingleStrategy :: ImportGroups
importGroupSingleStrategy :: ImportGroups
importGroupSingleStrategy =
  NonEmpty ImportGroup -> ImportGroups
ImportGroups (NonEmpty ImportGroup -> ImportGroups)
-> NonEmpty ImportGroup -> ImportGroups
forall a b. (a -> b) -> a -> b
$
    ImportGroup -> NonEmpty ImportGroup
forall a. a -> NonEmpty a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
      ImportGroup
        { igName :: Maybe String
igName = Maybe String
forall a. Maybe a
Nothing,
          igRules :: NonEmpty ImportGroupRule
igRules = ImportGroupRule -> NonEmpty ImportGroupRule
forall a. a -> NonEmpty a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ImportGroupRule
matchAllImportRule
        }

importGroupByQualifiedStrategy :: ImportGroups
importGroupByQualifiedStrategy :: ImportGroups
importGroupByQualifiedStrategy =
  NonEmpty ImportGroup -> ImportGroups
ImportGroups (NonEmpty ImportGroup -> ImportGroups)
-> NonEmpty ImportGroup -> ImportGroups
forall a b. (a -> b) -> a -> b
$
    [ImportGroup] -> NonEmpty ImportGroup
forall a. HasCallStack => [a] -> NonEmpty a
NonEmpty.fromList
      [ ImportGroup
          { igName :: Maybe String
igName = Maybe String
forall a. Maybe a
Nothing,
            igRules :: NonEmpty ImportGroupRule
igRules = ImportGroupRule -> NonEmpty ImportGroupRule
forall a. a -> NonEmpty a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ImportGroupRule -> NonEmpty ImportGroupRule)
-> ImportGroupRule -> NonEmpty ImportGroupRule
forall a b. (a -> b) -> a -> b
$ ImportGroupRule -> ImportGroupRule
withUnqualifiedOnly ImportGroupRule
matchAllImportRule
          },
        ImportGroup
          { igName :: Maybe String
igName = Maybe String
forall a. Maybe a
Nothing,
            igRules :: NonEmpty ImportGroupRule
igRules = ImportGroupRule -> NonEmpty ImportGroupRule
forall a. a -> NonEmpty a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ImportGroupRule -> NonEmpty ImportGroupRule)
-> ImportGroupRule -> NonEmpty ImportGroupRule
forall a b. (a -> b) -> a -> b
$ ImportGroupRule -> ImportGroupRule
withQualifiedOnly ImportGroupRule
matchAllImportRule
          }
      ]

importGroupByScopeStrategy :: ImportGroups
importGroupByScopeStrategy :: ImportGroups
importGroupByScopeStrategy =
  NonEmpty ImportGroup -> ImportGroups
ImportGroups (NonEmpty ImportGroup -> ImportGroups)
-> NonEmpty ImportGroup -> ImportGroups
forall a b. (a -> b) -> a -> b
$
    [ImportGroup] -> NonEmpty ImportGroup
forall a. HasCallStack => [a] -> NonEmpty a
NonEmpty.fromList
      [ ImportGroup
          { igName :: Maybe String
igName = Maybe String
forall a. Maybe a
Nothing,
            igRules :: NonEmpty ImportGroupRule
igRules = ImportGroupRule -> NonEmpty ImportGroupRule
forall a. a -> NonEmpty a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ImportGroupRule
matchAllImportRule
          },
        ImportGroup
          { igName :: Maybe String
igName = Maybe String
forall a. Maybe a
Nothing,
            igRules :: NonEmpty ImportGroupRule
igRules = ImportGroupRule -> NonEmpty ImportGroupRule
forall a. a -> NonEmpty a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ImportGroupRule
matchLocalModulesRule
          }
      ]

importGroupByScopeThenQualifiedStrategy :: ImportGroups
importGroupByScopeThenQualifiedStrategy :: ImportGroups
importGroupByScopeThenQualifiedStrategy =
  NonEmpty ImportGroup -> ImportGroups
ImportGroups (NonEmpty ImportGroup -> ImportGroups)
-> NonEmpty ImportGroup -> ImportGroups
forall a b. (a -> b) -> a -> b
$
    [ImportGroup] -> NonEmpty ImportGroup
forall a. HasCallStack => [a] -> NonEmpty a
NonEmpty.fromList
      [ ImportGroup
          { igName :: Maybe String
igName = Maybe String
forall a. Maybe a
Nothing,
            igRules :: NonEmpty ImportGroupRule
igRules = ImportGroupRule -> NonEmpty ImportGroupRule
forall a. a -> NonEmpty a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ImportGroupRule -> NonEmpty ImportGroupRule)
-> ImportGroupRule -> NonEmpty ImportGroupRule
forall a b. (a -> b) -> a -> b
$ ImportGroupRule -> ImportGroupRule
withQualified ImportGroupRule
matchModule
          }
      | ImportGroupRule
matchModule <- [ImportGroupRule
matchAllImportRule, ImportGroupRule
matchLocalModulesRule],
        ImportGroupRule -> ImportGroupRule
withQualified <- [ImportGroupRule -> ImportGroupRule
withUnqualifiedOnly, ImportGroupRule -> ImportGroupRule
withQualifiedOnly]
      ]

groupsFromConfig :: Config.ImportGrouping -> ImportGroups
groupsFromConfig :: ImportGrouping -> ImportGroups
groupsFromConfig =
  \case
    ImportGrouping
Config.ImportGroupLegacy -> ImportGroups
importGroupSingleStrategy
    ImportGrouping
Config.ImportGroupPreserve -> ImportGroups
importGroupSingleStrategy
    ImportGrouping
Config.ImportGroupSingle -> ImportGroups
importGroupSingleStrategy
    ImportGrouping
Config.ImportGroupByQualified -> ImportGroups
importGroupByQualifiedStrategy
    ImportGrouping
Config.ImportGroupByScope -> ImportGroups
importGroupByScopeStrategy
    ImportGrouping
Config.ImportGroupByScopeThenQualified -> ImportGroups
importGroupByScopeThenQualifiedStrategy
    Config.ImportGroupCustom NonEmpty ImportGroup
igs -> NonEmpty ImportGroup -> ImportGroups
ImportGroups NonEmpty ImportGroup
igs

matchAllImportRule :: ImportGroupRule
matchAllImportRule :: ImportGroupRule
matchAllImportRule =
  ImportGroupRule
    { igrModuleMatcher :: ImportModuleMatcher
igrModuleMatcher = ImportModuleMatcher
MatchAllModules,
      igrQualifiedMatcher :: QualifiedImportMatcher
igrQualifiedMatcher = QualifiedImportMatcher
Config.MatchBothQualifiedAndUnqualified,
      igrPriority :: ImportRulePriority
igrPriority = ImportRulePriority
Config.matchAllRulePriority
    }

matchLocalModulesRule :: ImportGroupRule
matchLocalModulesRule :: ImportGroupRule
matchLocalModulesRule =
  ImportGroupRule
    { igrModuleMatcher :: ImportModuleMatcher
igrModuleMatcher = ImportModuleMatcher
MatchLocalModules,
      igrQualifiedMatcher :: QualifiedImportMatcher
igrQualifiedMatcher = QualifiedImportMatcher
Config.MatchBothQualifiedAndUnqualified,
      igrPriority :: ImportRulePriority
igrPriority = ImportRulePriority
Config.matchLocalRulePriority
    }

withQualifiedOnly :: ImportGroupRule -> ImportGroupRule
withQualifiedOnly :: ImportGroupRule -> ImportGroupRule
withQualifiedOnly ImportGroupRule {ImportModuleMatcher
QualifiedImportMatcher
ImportRulePriority
igrModuleMatcher :: ImportGroupRule -> ImportModuleMatcher
igrQualifiedMatcher :: ImportGroupRule -> QualifiedImportMatcher
igrPriority :: ImportGroupRule -> ImportRulePriority
igrModuleMatcher :: ImportModuleMatcher
igrQualifiedMatcher :: QualifiedImportMatcher
igrPriority :: ImportRulePriority
..} =
  ImportGroupRule
    { igrQualifiedMatcher :: QualifiedImportMatcher
igrQualifiedMatcher = QualifiedImportMatcher
Config.MatchQualifiedOnly,
      ImportModuleMatcher
ImportRulePriority
igrModuleMatcher :: ImportModuleMatcher
igrPriority :: ImportRulePriority
igrModuleMatcher :: ImportModuleMatcher
igrPriority :: ImportRulePriority
..
    }

withUnqualifiedOnly :: ImportGroupRule -> ImportGroupRule
withUnqualifiedOnly :: ImportGroupRule -> ImportGroupRule
withUnqualifiedOnly ImportGroupRule {ImportModuleMatcher
QualifiedImportMatcher
ImportRulePriority
igrModuleMatcher :: ImportGroupRule -> ImportModuleMatcher
igrQualifiedMatcher :: ImportGroupRule -> QualifiedImportMatcher
igrPriority :: ImportGroupRule -> ImportRulePriority
igrModuleMatcher :: ImportModuleMatcher
igrQualifiedMatcher :: QualifiedImportMatcher
igrPriority :: ImportRulePriority
..} =
  ImportGroupRule
    { igrQualifiedMatcher :: QualifiedImportMatcher
igrQualifiedMatcher = QualifiedImportMatcher
Config.MatchUnqualifiedOnly,
      ImportModuleMatcher
ImportRulePriority
igrModuleMatcher :: ImportModuleMatcher
igrPriority :: ImportRulePriority
igrModuleMatcher :: ImportModuleMatcher
igrPriority :: ImportRulePriority
..
    }

matchesRule :: Set Cabal.ModuleName -> Import -> ImportGroupRule -> Bool
matchesRule :: Set ModuleName -> Import -> ImportGroupRule -> Bool
matchesRule Set ModuleName
localMods Import {Bool
ModuleName
importName :: Import -> ModuleName
importQualified :: Import -> Bool
importName :: ModuleName
importQualified :: Bool
..} ImportGroupRule {ImportModuleMatcher
QualifiedImportMatcher
ImportRulePriority
igrModuleMatcher :: ImportGroupRule -> ImportModuleMatcher
igrQualifiedMatcher :: ImportGroupRule -> QualifiedImportMatcher
igrPriority :: ImportGroupRule -> ImportRulePriority
igrModuleMatcher :: ImportModuleMatcher
igrQualifiedMatcher :: QualifiedImportMatcher
igrPriority :: ImportRulePriority
..} = Bool
matchesModules Bool -> Bool -> Bool
&& Bool
matchesQualified
  where
    matchesModules :: Bool
matchesModules = case ImportModuleMatcher
igrModuleMatcher of
      ImportModuleMatcher
MatchAllModules -> Bool
True
      ImportModuleMatcher
MatchLocalModules -> ModuleName -> ModuleName
ghcModuleNameToCabal ModuleName
importName ModuleName -> Set ModuleName -> Bool
forall a. Ord a => a -> Set a -> Bool
`Set.member` Set ModuleName
localMods
      MatchGlob Glob
gl -> ModuleName -> String
moduleNameString ModuleName
importName String -> Glob -> Bool
`matchesGlob` Glob
gl
    matchesQualified :: Bool
matchesQualified = case QualifiedImportMatcher
igrQualifiedMatcher of
      QualifiedImportMatcher
Config.MatchQualifiedOnly -> Bool
importQualified
      QualifiedImportMatcher
Config.MatchUnqualifiedOnly -> Bool -> Bool
not Bool
importQualified
      QualifiedImportMatcher
Config.MatchBothQualifiedAndUnqualified -> Bool
True

prepareExistingGroups :: ImportGrouping -> Bool -> [LImportDecl GhcPs] -> [[LImportDecl GhcPs]]
prepareExistingGroups :: ImportGrouping
-> Bool -> [LImportDecl GhcPs] -> [[LImportDecl GhcPs]]
prepareExistingGroups ImportGrouping
ig Bool
respectful =
  case ImportGrouping
ig of
    ImportGrouping
ImportGroupPreserve -> [LImportDecl GhcPs] -> [[LImportDecl GhcPs]]
[GenLocated SrcSpanAnnA (ImportDecl GhcPs)]
-> [[GenLocated SrcSpanAnnA (ImportDecl GhcPs)]]
forall {e}.
[GenLocated SrcSpanAnnA e] -> [[GenLocated SrcSpanAnnA e]]
preserveGroups
    ImportGrouping
ImportGroupLegacy | Bool
respectful -> [LImportDecl GhcPs] -> [[LImportDecl GhcPs]]
[GenLocated SrcSpanAnnA (ImportDecl GhcPs)]
-> [[GenLocated SrcSpanAnnA (ImportDecl GhcPs)]]
forall {e}.
[GenLocated SrcSpanAnnA e] -> [[GenLocated SrcSpanAnnA e]]
preserveGroups
    ImportGrouping
_ -> [LImportDecl GhcPs] -> [[LImportDecl GhcPs]]
[GenLocated SrcSpanAnnA (ImportDecl GhcPs)]
-> [[GenLocated SrcSpanAnnA (ImportDecl GhcPs)]]
forall {a}. a -> [a]
flattenGroups
  where
    preserveGroups :: [GenLocated SrcSpanAnnA e] -> [[GenLocated SrcSpanAnnA e]]
preserveGroups = (NonEmpty (GenLocated SrcSpanAnnA e) -> [GenLocated SrcSpanAnnA e])
-> [NonEmpty (GenLocated SrcSpanAnnA e)]
-> [[GenLocated SrcSpanAnnA e]]
forall a b. (a -> b) -> [a] -> [b]
map NonEmpty (GenLocated SrcSpanAnnA e) -> [GenLocated SrcSpanAnnA e]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList ([NonEmpty (GenLocated SrcSpanAnnA e)]
 -> [[GenLocated SrcSpanAnnA e]])
-> ([GenLocated SrcSpanAnnA e]
    -> [NonEmpty (GenLocated SrcSpanAnnA e)])
-> [GenLocated SrcSpanAnnA e]
-> [[GenLocated SrcSpanAnnA e]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (GenLocated SrcSpanAnnA e -> GenLocated SrcSpanAnnA e -> Bool)
-> [GenLocated SrcSpanAnnA e]
-> [NonEmpty (GenLocated SrcSpanAnnA e)]
forall a. (a -> a -> Bool) -> [a] -> [NonEmpty a]
groupBy' (\GenLocated SrcSpanAnnA e
x GenLocated SrcSpanAnnA e
y -> Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (GenLocated SrcSpanAnnA e -> SrcSpan)
-> GenLocated SrcSpanAnnA e -> GenLocated SrcSpanAnnA e -> Bool
forall a. (a -> SrcSpan) -> a -> a -> Bool
separatedByBlank GenLocated SrcSpanAnnA e -> SrcSpan
forall a e. HasLoc a => GenLocated a e -> SrcSpan
getLocA GenLocated SrcSpanAnnA e
x GenLocated SrcSpanAnnA e
y)
    flattenGroups :: a -> [a]
flattenGroups = a -> [a]
forall {a}. a -> [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure

groupImports :: forall x. ImportGrouping -> Set Cabal.ModuleName -> (x -> Import) -> [x] -> [[x]]
groupImports :: forall x.
ImportGrouping -> Set ModuleName -> (x -> Import) -> [x] -> [[x]]
groupImports ImportGrouping
ig Set ModuleName
localModules x -> Import
fToImport = [(Int, x)] -> [[x]]
regroup ([(Int, x)] -> [[x]]) -> ([x] -> [(Int, x)]) -> [x] -> [[x]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (x -> (Int, x)) -> [x] -> [(Int, x)]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (([(Int, [ImportGroupRule])], x) -> (Int, x)
breakTies (([(Int, [ImportGroupRule])], x) -> (Int, x))
-> (x -> ([(Int, [ImportGroupRule])], x)) -> x -> (Int, x)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. x -> ([(Int, [ImportGroupRule])], x)
matchRules)
  where
    ImportGroups NonEmpty ImportGroup
igs = ImportGrouping -> ImportGroups
groupsFromConfig ImportGrouping
ig

    indexedGroupRules :: [(Int, [ImportGroupRule])]
    indexedGroupRules :: [(Int, [ImportGroupRule])]
indexedGroupRules = [Int] -> [[ImportGroupRule]] -> [(Int, [ImportGroupRule])]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int
0 ..] (NonEmpty ImportGroupRule -> [ImportGroupRule]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList (NonEmpty ImportGroupRule -> [ImportGroupRule])
-> (ImportGroup -> NonEmpty ImportGroupRule)
-> ImportGroup
-> [ImportGroupRule]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ImportGroup -> NonEmpty ImportGroupRule
igRules (ImportGroup -> [ImportGroupRule])
-> [ImportGroup] -> [[ImportGroupRule]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty ImportGroup -> [ImportGroup]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList NonEmpty ImportGroup
igs)

    matchRules :: x -> ([(Int, [ImportGroupRule])], x)
    matchRules :: x -> ([(Int, [ImportGroupRule])], x)
matchRules x
x =
      let imp :: Import
imp = x -> Import
fToImport x
x
          testRule :: (a, t ImportGroupRule) -> Bool
testRule (a
_, t ImportGroupRule
rules) = (ImportGroupRule -> Bool) -> t ImportGroupRule -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Set ModuleName -> Import -> ImportGroupRule -> Bool
matchesRule Set ModuleName
localModules Import
imp) t ImportGroupRule
rules
       in (((Int, [ImportGroupRule]) -> Bool)
-> [(Int, [ImportGroupRule])] -> [(Int, [ImportGroupRule])]
forall a. (a -> Bool) -> [a] -> [a]
filter (Int, [ImportGroupRule]) -> Bool
forall {t :: * -> *} {a}.
Foldable t =>
(a, t ImportGroupRule) -> Bool
testRule [(Int, [ImportGroupRule])]
indexedGroupRules, x
x)

    breakTies :: ([(Int, [ImportGroupRule])], x) -> (Int, x)
    breakTies :: ([(Int, [ImportGroupRule])], x) -> (Int, x)
breakTies ([], x
x) =
      (Int
forall a. Bounded a => a
maxBound, x
x) -- Any non-matched imports will be in the last group
    breakTies ([(Int, [ImportGroupRule])]
matches, x
x) =
      ((Int, ImportRulePriority) -> Int
forall a b. (a, b) -> a
fst ((Int, ImportRulePriority) -> Int)
-> ([(Int, ImportRulePriority)] -> (Int, ImportRulePriority))
-> [(Int, ImportRulePriority)]
-> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Int, ImportRulePriority)
 -> (Int, ImportRulePriority) -> Ordering)
-> [(Int, ImportRulePriority)] -> (Int, ImportRulePriority)
forall (t :: * -> *) a.
Foldable t =>
(a -> a -> Ordering) -> t a -> a
minimumBy (ImportRulePriority -> ImportRulePriority -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (ImportRulePriority -> ImportRulePriority -> Ordering)
-> ((Int, ImportRulePriority) -> ImportRulePriority)
-> (Int, ImportRulePriority)
-> (Int, ImportRulePriority)
-> Ordering
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` (Int, ImportRulePriority) -> ImportRulePriority
forall a b. (a, b) -> b
snd) ([(Int, ImportRulePriority)] -> Int)
-> [(Int, ImportRulePriority)] -> Int
forall a b. (a -> b) -> a -> b
$ ([ImportGroupRule] -> ImportRulePriority)
-> (Int, [ImportGroupRule]) -> (Int, ImportRulePriority)
forall b c a. (b -> c) -> (a, b) -> (a, c)
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second ([ImportRulePriority] -> ImportRulePriority
forall a. Ord a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
minimum ([ImportRulePriority] -> ImportRulePriority)
-> ([ImportGroupRule] -> [ImportRulePriority])
-> [ImportGroupRule]
-> ImportRulePriority
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ImportGroupRule -> ImportRulePriority)
-> [ImportGroupRule] -> [ImportRulePriority]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ImportGroupRule -> ImportRulePriority
igrPriority) ((Int, [ImportGroupRule]) -> (Int, ImportRulePriority))
-> [(Int, [ImportGroupRule])] -> [(Int, ImportRulePriority)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(Int, [ImportGroupRule])]
matches, x
x)

    regroup :: [(Int, x)] -> [[x]]
    regroup :: [(Int, x)] -> [[x]]
regroup = ([(Int, x)] -> [x]) -> [[(Int, x)]] -> [[x]]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (((Int, x) -> x) -> [(Int, x)] -> [x]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Int, x) -> x
forall a b. (a, b) -> b
snd) ([[(Int, x)]] -> [[x]])
-> ([(Int, x)] -> [[(Int, x)]]) -> [(Int, x)] -> [[x]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Int, x) -> (Int, x) -> Bool) -> [(Int, x)] -> [[(Int, x)]]
forall a. (a -> a -> Bool) -> [a] -> [[a]]
groupBy (Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
(==) (Int -> Int -> Bool)
-> ((Int, x) -> Int) -> (Int, x) -> (Int, x) -> Bool
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` (Int, x) -> Int
forall a b. (a, b) -> a
fst) ([(Int, x)] -> [[(Int, x)]])
-> ([(Int, x)] -> [(Int, x)]) -> [(Int, x)] -> [[(Int, x)]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Int, x) -> Int) -> [(Int, x)] -> [(Int, x)]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn (Int, x) -> Int
forall a b. (a, b) -> a
fst