diff --git a/hindent.cabal b/hindent.cabal index 889e7a4e7..36698ee3a 100644 --- a/hindent.cabal +++ b/hindent.cabal @@ -93,8 +93,6 @@ library HIndent.Pretty.Combinators.String HIndent.Pretty.Combinators.Switch HIndent.Pretty.Combinators.Wrap - HIndent.Pretty.Import - HIndent.Pretty.Import.Sort HIndent.Pretty.NodeComments HIndent.Pretty.SigBindFamily HIndent.Pretty.Types diff --git a/src/HIndent/Ast/Import/Collection.hs b/src/HIndent/Ast/Import/Collection.hs index 39e31ad62..ae9927fa7 100644 --- a/src/HIndent/Ast/Import/Collection.hs +++ b/src/HIndent/Ast/Import/Collection.hs @@ -8,7 +8,11 @@ module HIndent.Ast.Import.Collection ) where import Control.Monad.RWS +import Data.Function +import Data.List import qualified GHC.Hs as GHC +import GHC.Stack +import qualified GHC.Types.SrcLoc as GHC import HIndent.Ast.Import import HIndent.Ast.NodeComments import HIndent.Ast.WithComments @@ -16,7 +20,6 @@ import HIndent.Config import qualified HIndent.GhcLibParserWrapper.GHC.Hs as GHC import HIndent.Pretty import HIndent.Pretty.Combinators -import HIndent.Pretty.Import import HIndent.Pretty.NodeComments import HIndent.Printer @@ -45,3 +48,44 @@ mkImportCollection GHC.HsModule {..} = hasImports :: ImportCollection -> Bool hasImports (ImportCollection xs) = not $ null xs + +-- | Extracts import declarations from the given module. Adjacent import +-- declarations are grouped as a single list. +extractImports' :: [GHC.LImportDecl GHC.GhcPs] -> [[GHC.LImportDecl GHC.GhcPs]] +extractImports' = groupImports . sortImportsByLocation + +-- | Combines adjacent import declarations into a single list. +groupImports :: [GHC.LImportDecl GHC.GhcPs] -> [[GHC.LImportDecl GHC.GhcPs]] +groupImports = groupImports' [] + where + groupImports' :: + [[GHC.LImportDecl GHC.GhcPs]] + -> [GHC.LImportDecl GHC.GhcPs] + -> [[GHC.LImportDecl GHC.GhcPs]] + groupImports' xs [] = xs + groupImports' [] (x:xs) = groupImports' [[x]] xs + groupImports' [[]] (x:xs) = groupImports' [[x]] xs + groupImports' ([]:x:xs) (y:ys) = groupImports' ([y] : x : xs) ys + groupImports' ((z:zs):xs) (y:ys) + | z `isAdjacentTo` y = groupImports' ((y : z : zs) : xs) ys + | otherwise = groupImports' ([y] : (z : zs) : xs) ys + a `isAdjacentTo` b = + GHC.srcSpanEndLine (sp a) + 1 == GHC.srcSpanStartLine (sp b) + || GHC.srcSpanEndLine (sp b) + 1 == GHC.srcSpanStartLine (sp a) + sp x = + case GHC.locA $ GHC.getLoc x of + GHC.RealSrcSpan x' _ -> x' + _ -> error "Src span unavailable." + +-- | This function sorts imports by their start line numbers. +sortImportsByLocation :: + [GHC.LImportDecl GHC.GhcPs] -> [GHC.LImportDecl GHC.GhcPs] +sortImportsByLocation = sortBy (flip compare `on` lineIdx) + where + lineIdx = startLine . GHC.locA . GHC.getLoc + +-- | This function returns the start line of the given 'SrcSpan'. If it is +-- not available, it raises an error. +startLine :: HasCallStack => GHC.SrcSpan -> Int +startLine (GHC.RealSrcSpan x _) = GHC.srcSpanStartLine x +startLine (GHC.UnhelpfulSpan _) = error "The src span is unavailable." diff --git a/src/HIndent/Pretty/Import.hs b/src/HIndent/Pretty/Import.hs deleted file mode 100644 index 930706ab7..000000000 --- a/src/HIndent/Pretty/Import.hs +++ /dev/null @@ -1,60 +0,0 @@ -{-# LANGUAGE CPP #-} - --- | Helper functions for dealing with import declarations. -module HIndent.Pretty.Import - ( importsExist - , extractImports - , extractImportsSorted - , extractImports' - , extractImportsSorted' - , groupImports - ) where - -import GHC.Types.SrcLoc -import HIndent.GhcLibParserWrapper.GHC.Hs -import HIndent.Pretty.Import.Sort - --- | Returns if the module has import declarations. -importsExist :: HsModule' -> Bool -importsExist = not . null . hsmodImports - --- | Extracts import declarations from the given module. Adjacent import --- declarations are grouped as a single list. -extractImports :: HsModule' -> [[LImportDecl GhcPs]] -extractImports = groupImports . sortImportsByLocation . hsmodImports - --- | Extracts import declarations from the given module. Adjacent import --- declarations are grouped as a single list. -extractImports' :: [LImportDecl GhcPs] -> [[LImportDecl GhcPs]] -extractImports' = groupImports . sortImportsByLocation - --- | Extracts import declarations from the given module and sorts them by --- their names. Adjacent import declarations are grouped as a single list. -extractImportsSorted :: HsModule' -> [[LImportDecl GhcPs]] -extractImportsSorted = fmap sortImportsByName . extractImports - --- | Extracts import declarations from the given module and sorts them by --- their names. Adjacent import declarations are grouped as a single list. -extractImportsSorted' :: [LImportDecl GhcPs] -> [[LImportDecl GhcPs]] -extractImportsSorted' = fmap sortImportsByName . extractImports' - --- | Combines adjacent import declarations into a single list. -groupImports :: [LImportDecl GhcPs] -> [[LImportDecl GhcPs]] -groupImports = groupImports' [] - where - groupImports' :: - [[LImportDecl GhcPs]] -> [LImportDecl GhcPs] -> [[LImportDecl GhcPs]] - groupImports' xs [] = xs - groupImports' [] (x:xs) = groupImports' [[x]] xs - groupImports' [[]] (x:xs) = groupImports' [[x]] xs - groupImports' ([]:x:xs) (y:ys) = groupImports' ([y] : x : xs) ys - groupImports' ((z:zs):xs) (y:ys) - | z `isAdjacentTo` y = groupImports' ((y : z : zs) : xs) ys - | otherwise = groupImports' ([y] : (z : zs) : xs) ys - a `isAdjacentTo` b = - srcSpanEndLine (sp a) + 1 == srcSpanStartLine (sp b) - || srcSpanEndLine (sp b) + 1 == srcSpanStartLine (sp a) - sp x = - case locA $ getLoc x of - RealSrcSpan x' _ -> x' - _ -> error "Src span unavailable." diff --git a/src/HIndent/Pretty/Import/Sort.hs b/src/HIndent/Pretty/Import/Sort.hs deleted file mode 100644 index fb2554d6e..000000000 --- a/src/HIndent/Pretty/Import/Sort.hs +++ /dev/null @@ -1,134 +0,0 @@ -{-# LANGUAGE CPP #-} - --- | Import declaration sorting for pretty-printing. -module HIndent.Pretty.Import.Sort - ( sortImportsByName - , sortImportsByLocation - ) where - -import Data.Char -import Data.Function -import Data.List -import Data.Maybe -import GHC.Hs -import GHC.Stack -import GHC.Types.SrcLoc -import HIndent.Pretty.Combinators.Outputable - --- | The letter type of a 'Char'. --- --- The order of constructors is important. HIndent sorts explicit imports --- from ones starting from a capital letter (e.g., data constructors), --- symbol identifiers, and functions. -data LetterType - = Capital - | Symbol - | Lower - deriving (Eq, Ord) - --- | This function sorts import declarations and explicit imports in them --- by their names. -sortImportsByName :: [LImportDecl GhcPs] -> [LImportDecl GhcPs] -sortImportsByName = fmap sortExplicitImportsInDecl . sortByModuleName - --- | This function sorts imports by their start line numbers. -sortImportsByLocation :: [LImportDecl GhcPs] -> [LImportDecl GhcPs] -sortImportsByLocation = sortBy (flip compare `on` lineIdx) - where - lineIdx = startLine . locA . getLoc - --- | This function sorts import declarations by their module names. -sortByModuleName :: [LImportDecl GhcPs] -> [LImportDecl GhcPs] -sortByModuleName = sortBy (compare `on` unLoc . ideclName . unLoc) - --- | This function sorts explicit imports in the given import declaration --- by their names. -sortExplicitImportsInDecl :: LImportDecl GhcPs -> LImportDecl GhcPs -#if MIN_VERSION_ghc_lib_parser(9,6,1) -sortExplicitImportsInDecl (L l d@ImportDecl {ideclImportList = Just (x, imports)}) = - L l d {ideclImportList = Just (x, sorted)} - where - sorted = fmap (fmap sortVariants . sortExplicitImports) imports -#else -sortExplicitImportsInDecl (L l d@ImportDecl {ideclHiding = Just (x, imports)}) = - L l d {ideclHiding = Just (x, sorted)} - where - sorted = fmap (fmap sortVariants . sortExplicitImports) imports -#endif -sortExplicitImportsInDecl x = x - --- | This function sorts the given explicit imports by their names. -sortExplicitImports :: [LIE GhcPs] -> [LIE GhcPs] -sortExplicitImports = sortBy compareImportEntities - --- | This function sorts variants (e.g., data constructors and class --- methods) in the given explicit import by their names. -sortVariants :: LIE GhcPs -> LIE GhcPs -sortVariants (L l (IEThingWith x x' x'' xs)) = - L l $ IEThingWith x x' x'' (sortWrappedNames xs) - where - sortWrappedNames = sortBy (compare `on` showOutputable) -sortVariants x = x - --- | This function compares two import declarations by their module names. -compareImportEntities :: LIE GhcPs -> LIE GhcPs -> Ordering -compareImportEntities (L _ a) (L _ b) = - fromMaybe LT $ compareIdentifier <$> moduleName a <*> moduleName b - --- | This function returns a 'Just' value with the module name extracted --- from the import declaration. Otherwise, it returns a 'Nothing'. -moduleName :: IE GhcPs -> Maybe String -moduleName (IEVar _ wrapped) = Just $ showOutputable wrapped -moduleName (IEThingAbs _ wrapped) = Just $ showOutputable wrapped -moduleName (IEThingAll _ wrapped) = Just $ showOutputable wrapped -moduleName (IEThingWith _ wrapped _ _) = Just $ showOutputable wrapped -moduleName _ = Nothing - --- | This function compares two identifiers in order of capitals, symbols, --- and lowers. -compareIdentifier :: String -> String -> Ordering -compareIdentifier as@(a:_) bs@(b:_) = - case compareChar a b of - EQ -> compareSameIdentifierType as bs - x -> x -compareIdentifier _ _ = error "Either identifier is an empty string." - --- | Almost similar to 'compare' but ignores parentheses for symbol --- identifiers as they are enclosed by parentheses. -compareSameIdentifierType :: String -> String -> Ordering -compareSameIdentifierType "" "" = EQ -compareSameIdentifierType "" _ = LT -compareSameIdentifierType _ "" = GT -compareSameIdentifierType ('(':as) bs = compareSameIdentifierType as bs -compareSameIdentifierType (')':as) bs = compareSameIdentifierType as bs -compareSameIdentifierType as ('(':bs) = compareSameIdentifierType as bs -compareSameIdentifierType as (')':bs) = compareSameIdentifierType as bs -compareSameIdentifierType (a:as) (b:bs) = - case compare a b of - EQ -> compareSameIdentifierType as bs - x -> x - --- | This function compares two characters by their types (capital, symbol, --- and lower). If both are the same type, then it compares them by the --- usual ordering. -compareChar :: Char -> Char -> Ordering -compareChar a b = - case compare at bt of - EQ -> compare a b - x -> x - where - at = charToLetterType a - bt = charToLetterType b - --- | This function returns a 'LetterType' based on the given character. -charToLetterType :: Char -> LetterType -charToLetterType c - | isLower c = Lower - | isUpper c = Capital - | otherwise = Symbol - --- | This function returns the start line of the given 'SrcSpan'. If it is --- not available, it raises an error. -startLine :: HasCallStack => SrcSpan -> Int -startLine (RealSrcSpan x _) = srcSpanStartLine x -startLine (UnhelpfulSpan _) = error "The src span is unavailable."