packages feed

weeder-0.1: src/Util.hs

module Util(
    PackageName, ModuleName, IdentName,
    parseHanging,
    (?:),
    isHaskellSymbol,
    reachable,
    isPathsModule
    ) where

import Data.Char
import Data.Monoid
import Data.Hashable
import Data.List.Extra
import Data.Tuple.Extra
import qualified Data.HashSet as Set
import Prelude


type PackageName = String
type ModuleName = String
type IdentName = String


-- | Return the first non-empty argument in a left-to-right manner
(?:) :: (Eq a, Monoid a) => a -> a -> a
a ?: b = if a == mempty then b else a

-- | Parse a hanging lines of lines.
parseHanging :: [String] -> [(String, [String])]
parseHanging = repeatedly (\(x:xs) -> first (\a -> (x, unindent a)) $ span (\x -> null x || " " `isPrefixOf` x) xs)

unindent :: [String] -> [String]
unindent xs = map (drop n) xs
    where
        n = minimum $ top : map f xs
        f x = let (a,b) = span isSpace x in if null b then top else length a
        top = 1000

-- | Is the character a member of possible Haskell symbol characters,
--   according to the Haskell report.
isHaskellSymbol :: Char -> Bool
isHaskellSymbol x =
    x `elem` "!#$%&*+./<=>?@\\^|-~" ||
    (isSymbol x && x `notElem` "\"'_(),;[]`{}")


-- | Given a list of mappings, and an initial set, find which items can be reached
reachable :: (Eq k, Hashable k) => (k -> [k]) -> [k] -> Set.HashSet k
reachable follow = f Set.empty
    where
        f done [] = done
        f done (x:xs)
            | x `Set.member` done = f done xs
            | otherwise = f (Set.insert x done) $ follow x ++ xs

-- | Is a given module name the specially generated cabal Paths_foo module
isPathsModule :: ModuleName -> Bool
isPathsModule = isPrefixOf "Paths_"