packages feed

halberd 0.1.1 → 0.1.2

raw patch · 11 files changed

+398/−215 lines, 11 filesdep +HUnitdep +halberddep +splitdep −filepath

Dependencies added: HUnit, halberd, split, test-framework, test-framework-hunit

Dependencies removed: filepath

Files

CHANGELOG view
@@ -1,2 +1,11 @@+0.1.2:+  * Actual order independent choosing.+  * Added test cases.+  * Refactored code.++0.1.1:+  * Include needed modules.+  * Smarter, order-independent auto-choosing.+ 0.1:   * Initial release
+ exe/Halberd.hs view
@@ -0,0 +1,50 @@+module Main where++import           Control.Applicative+import           Control.Monad hiding (forM_)+import           Data.Foldable (forM_)+import           Data.List+import           Data.Proxy+import           Distribution.HaskellSuite+import           Distribution.Simple.Compiler+import           Language.Haskell.Exts.Annotated+import           Language.Haskell.Names.Interfaces+import           System.Environment+import           System.Exit++import           Halberd.ChosenImports+import           Halberd.Suggestions+import           Halberd.UI++main :: IO ()+main =+  do args <- getArgs+     case args of+       [] -> do+         putStrLn "Usage: halberd <SOURCEFILE>"+         exitFailure+       (file:_) -> do+         (ParseOk module_) <- parseFile file+         pkgs <- concat <$> mapM (getInstalledPackages (Proxy :: Proxy NamesDB))+             [UserPackageDB, GlobalPackageDB, SpecificPackageDB "/Users/erik/doc/haskell-suite/.cabal-sandbox/x86_64-osx-haskell-names-0.1-packages.conf.d"]+         allSuggestions <- evalModuleT (suggestedImports module_) pkgs suffix readInterface++         let (suggestions, noSuggestions) = partition (not . null . snd) allSuggestions++         chosenImports <- askUserChoices suggestions++         when (not . null $ noSuggestions) $ do+           putStrLn "------------- Could not find import for -------------"+           forM_ noSuggestions $ \(q, _) -> do+             putStrLn $ " - " ++ prettyPrint q+           putStrLn ""++         when (not . isEmpty $ chosenImports) $ do+           putStrLn "-------- Insert these imports into your file --------"+           putStrLn ""+           putStrLn $ unlines (showChosenImports chosenImports)+  where+    suffix = "names"++askUserChoices :: [Suggestion] -> IO ChosenImports+askUserChoices = resolveAllSuggestions askUserChoice
+ exe/Halberd/UI.hs view
@@ -0,0 +1,38 @@+module Halberd.UI where++import           Control.Applicative+import           Control.Monad.State hiding (forM_)+import           Data.Foldable (forM_)+import qualified Distribution.Text                   as Cabal+import           Language.Haskell.Exts.Annotated+import           Language.Haskell.Names+import           Safe+import           System.IO++import Halberd.Types++askUserChoice :: MonadIO m => QName (Scoped SrcSpan) -> [CanonicalSymbol] -> m CanonicalSymbol+askUserChoice qname suggestions  = liftIO $+  do putStrLn $ prettyPrint qname ++ ":"+     forM_ (zip [1 :: Integer ..] suggestions) $ \(i, (_, modName, _)) -> putStrLn $ show i ++ ") " ++ Cabal.display modName+     putStrLn ""+     getChoice suggestions++getChoice :: [a] -> IO a+getChoice xs = withoutOutput go+  where+    go =+      do c <- getChar+         let mi = readMay [c]+         case (subtract 1) <$> mi >>= atMay xs of+           Nothing -> go+           Just x  -> return x+    withoutOutput action =+      do buffering <- hGetBuffering stdin+         echo <- hGetEcho stdout+         hSetBuffering stdin NoBuffering+         hSetEcho stdout False+         result <- action+         hSetBuffering stdin buffering+         hSetEcho stdout echo+         return result
halberd.cabal view
@@ -1,5 +1,5 @@ name:                halberd-version:             0.1.1+version:             0.1.2 license:             BSD3 license-file:        LICENSE author:              Erik Hesselink, Simon Meier, Tom Lokhorst, Roman Cheplyaka@@ -19,24 +19,59 @@ homepage:            http://github.com/haskell-suite/halberd/ extra-source-files:  CHANGELOG, README.md -executable halberd+library   build-depends:       base             >= 4.5    &&  < 4.7                      , containers       >= 0.4    &&  < 0.6                      , haskell-packages == 0.2.*                      , haskell-names    == 0.2.*                      , haskell-src-exts == 1.14.*                      , Cabal            >= 1.17   &&  < 1.19-                     , filepath         == 1.3.*                      , mtl              >= 2.0    &&  < 2.2-                     , tagged           >= 0.4    &&  < 0.8                      , safe             == 0.3.*                      , syb              >= 0.3    &&  < 0.5   hs-source-dirs:      src-  main-is:             Halberd.hs   ghc-options:         -Wall-  other-modules:       Halberd.ChosenImports+  exposed-modules:     Halberd.ChosenImports                      , Halberd.CollectNames+                     , Halberd.LookupTable+                     , Halberd.Suggestions+                     , Halberd.Types+  other-modules:       Data.Tuple.Utils                      , Language.Haskell.Exts.Utils++executable halberd+  build-depends:       base             >= 4.5    &&  < 4.7+                     , Cabal            >= 1.17   &&  < 1.19+                     , halberd          == 0.1.1+                     , haskell-names    == 0.2.*+                     , haskell-packages == 0.2.*+                     , haskell-src-exts == 1.14.*+                     , mtl              >= 2.0    &&  < 2.2+                     , safe             == 0.3.*+                     , tagged           >= 0.4    &&  < 0.8+  hs-source-dirs:      exe+  main-is:             Halberd.hs+  ghc-options:         -Wall+  other-modules:       Halberd.UI++Test-suite halberd-tests+  build-depends:       base                 >= 4.5    && < 4.7+                     , Cabal                >= 1.17   && < 1.19+                     , containers           >= 0.4    && < 0.6+                     , halberd              == 0.1.1+                     , HUnit                == 1.2.*+                     , test-framework       == 0.8.*+                     , test-framework-hunit == 0.3.*+                     , haskell-names        == 0.2.*+                     , haskell-packages     == 0.2.*+                     , haskell-src-exts     == 1.14.*+                     , tagged               >= 0.4    && < 0.8+                     , split                == 0.2.*+                     , mtl                  >= 2.0    && < 2.2+  hs-source-dirs:      tests+  main-is:             Runner.hs+  type:                exitcode-stdio-1.0+  ghc-options:         -Wall  Source-Repository head   Type:                git
+ src/Data/Tuple/Utils.hs view
@@ -0,0 +1,7 @@+module Data.Tuple.Utils where++snd3 :: (a, b, c) -> b+snd3 (_, y, _) = y++trd3 :: (a, b, c) -> c+trd3 (_, _, z) = z
− src/Halberd.hs
@@ -1,208 +0,0 @@-{-# LANGUAGE FlexibleInstances    #-}-{-# LANGUAGE ImplicitParams       #-}-{-# LANGUAGE OverlappingInstances #-}-{-# LANGUAGE TupleSections        #-}-{-# LANGUAGE DoAndIfThenElse      #-}--import           Control.Applicative-import           Control.Arrow-import           Control.Monad hiding (forM_)-import           Control.Monad.State hiding (forM_)-import           Data.Foldable (forM_)-import           Data.Function-import           Data.List-import           Data.Maybe-import           Data.Map                            (Map)-import qualified Data.Map                            as Map-import           Data.Monoid-import           Data.Ord-import           Data.Proxy-import           Data.Set                            (Set)-import qualified Data.Set                            as Set-import           Distribution.HaskellSuite-import qualified Distribution.InstalledPackageInfo   as Cabal-import qualified Distribution.ModuleName             as Cabal-import qualified Distribution.Package                as Cabal-import           Distribution.Simple.Compiler-import qualified Distribution.Text                   as Cabal-import           Language.Haskell.Exts.Annotated-import           Language.Haskell.Names-import           Language.Haskell.Names.Imports      ()-import           Language.Haskell.Names.Interfaces-import           Safe-import           System.Environment-import           System.Exit-import           System.IO--import           Halberd.ChosenImports-import           Halberd.CollectNames                (collectUnboundNames)-import           Language.Haskell.Exts.Utils--main :: IO ()-main =-  do args <- getArgs-     case args of-       [] -> do-         putStrLn "Usage: halberd <SOURCEFILE>"-         exitFailure-       (file:_) -> do-         (ParseOk module_) <- parseFile file-         pkgs <- concat <$>-           mapM-             (getInstalledPackages (Proxy :: Proxy NamesDB))-             [UserPackageDB, GlobalPackageDB, SpecificPackageDB "/Users/erik/doc/haskell-suite/.cabal-sandbox/x86_64-osx-haskell-names-0.1-packages.conf.d"]-         allSuggestions <- evalModuleT (suggestedImports module_) pkgs suffix readInterface--         let (suggestions, noSuggestions) = partition (not . null . snd) allSuggestions--         chosenImports <- askUserChoices suggestions--         when (not . null $ noSuggestions) $ do-           putStrLn "------------- Could not find import for -------------"-           forM_ noSuggestions $ \(q, _) -> do-             putStrLn $ " - " ++ prettyPrint q-           putStrLn ""--         when (not . isEmpty $ chosenImports) $ do-           putStrLn "-------- Insert these imports into your file --------"-           putStrLn ""-           putStrLn $ unlines (showChosenImports chosenImports)-  where-    suffix = "names"--type Suggestion = (QName (Scoped SrcSpan), [CanonicalSymbol])--suggestedImports :: Module SrcSpanInfo -> ModuleT Symbols IO [Suggestion]-suggestedImports module_ =-  do (unboundTypes, unboundValues) <- uniques <$> findUnbound module_-     (valueTable, typeTable) <- mkLookupTables-     let valueSuggestions = map (id &&& lookupDefinitions valueTable) unboundValues-         typeSuggestions  = map (id &&& lookupDefinitions typeTable ) unboundTypes-     return $ valueSuggestions ++ typeSuggestions-  where-    uniques = unique *** unique-    unique = nubBy ((==) `on` void)--askUserChoices :: [Suggestion] -> IO ChosenImports-askUserChoices suggestions = execStateT (go suggestions) mempty-  where-    go sugs = do-      remaining <- resolveSuggestions sugs-      case remaining of-        [] -> return []-        ((qname, modules):ss) -> do-          choice <- askUserChoice qname modules-          modify $ insertChoice qname (snd3 choice)-          go ss--resolveSuggestions :: [Suggestion] -> StateT ChosenImports IO [Suggestion]-resolveSuggestions suggestions = fmap catMaybes . forM suggestions $ \suggestion@(qname, modules) ->-  do chosenModules <- get-     if alreadyChosen qname modules chosenModules-     then-       return Nothing-     else do-       case hasSingleOption qname modules chosenModules of-         Nothing           -> return $ Just suggestion-         Just choice -> do-           modify $ insertChoice qname (snd3 choice)-           return Nothing-  where-    alreadyChosen qname modules chosenModules = fromMaybe False $-      do q <- getQualification qname-         module_ <- lookupQualified q chosenModules-         return $ module_ `elem` map snd3 modules-    hasSingleOption _        [module_] _             = Just module_-    hasSingleOption UnQual{} modules   chosenModules | singleOrigName modules =-      headMay $ filter ((`Map.member` unqualifieds chosenModules) . snd3) modules-    hasSingleOption _        _         _             = Nothing-    singleOrigName = allEqual . map trd3-    allEqual []     = True-    allEqual (x:xs) = all (== x) xs--askUserChoice :: MonadIO m => QName (Scoped SrcSpan) -> [CanonicalSymbol] -> m CanonicalSymbol-askUserChoice qname suggestions  = liftIO $-  do putStrLn $ prettyPrint qname ++ ":"-     forM_ (zip [1 :: Integer ..] suggestions) $ \(i, (_, modName, _)) -> putStrLn $ show i ++ ") " ++ Cabal.display modName-     putStrLn ""-     getChoice suggestions--getChoice :: [a] -> IO a-getChoice xs = withoutOutput go-  where-    go =-      do c <- getChar-         let mi = readMay [c]-         case (subtract 1) <$> mi >>= atMay xs of-           Nothing -> go-           Just x  -> return x-    withoutOutput action =-      do buffering <- hGetBuffering stdin-         echo <- hGetEcho stdout-         hSetBuffering stdin NoBuffering-         hSetEcho stdout False-         result <- action-         hSetBuffering stdin buffering-         hSetEcho stdout echo-         return result--type CanonicalSymbol = (PackageRef, Cabal.ModuleName, OrigName)--data PackageRef = PackageRef-  { installedPackageId :: Cabal.InstalledPackageId-  , sourcePackageId    :: Cabal.PackageId-  } deriving (Eq, Ord, Show)--toPackageRef :: Cabal.InstalledPackageInfo_ m -> PackageRef-toPackageRef pkgInfo =-    PackageRef { installedPackageId = Cabal.installedPackageId pkgInfo-               , sourcePackageId    = Cabal.sourcePackageId    pkgInfo-               }--findUnbound :: Module SrcSpanInfo -> ModuleT Symbols IO ([QName (Scoped SrcSpan)], [QName (Scoped SrcSpan)])-findUnbound module_ = collectUnboundNames <$> annotateModule Haskell98 [] (fmap srcInfoSpan module_)--type LookupTable = Map String [CanonicalSymbol]--mkLookupTables :: ModuleT Symbols IO (LookupTable, LookupTable)-mkLookupTables =-  do pkgs <- getPackages-     (valueDefs, typeDefs) <--       fmap mconcat $ forM pkgs $ \pkg ->-         fmap mconcat $ forM (Cabal.exposedModules pkg) $ \exposedModule -> do-            (Symbols values types) <- readModuleInfo (Cabal.libraryDirs pkg) exposedModule-            let mkDefs qname = Set.map ((toPackageRef pkg, exposedModule,) . origName) qname-            return (mkDefs values, mkDefs types)-     let valueTable = toLookupTable (gUnqual . trd3) valueDefs-         typeTable  = toLookupTable (gUnqual . trd3) typeDefs-     return (valueTable, typeTable)-  where-    gUnqual (OrigName _ (GName _ n))  = n---lookupDefinitions :: Map String [CanonicalSymbol] -> QName (Scoped SrcSpan) -> [CanonicalSymbol]-lookupDefinitions symbolTable qname = fromMaybe [] $-  do n <- unQName qname-     Map.lookup n symbolTable-  where-    unQName (Qual    _ _ n) = Just (strName n)-    unQName (UnQual  _   n) = Just (strName n)-    unQName (Special _ _  ) = Nothing--    strName (Ident  _ str)  = str-    strName (Symbol _ str)  = str---toLookupTable :: Ord k => (a -> k) -> Set a -> Map k [a]-toLookupTable key = Map.fromList-                  . map (fst . head &&& map snd)-                  . groupBy ((==) `on` fst)-                  . sortBy (comparing fst)-                  . map (key &&& id)-                  . Set.toList--snd3 :: (a, b, c) -> b-snd3 (_, y, _) = y--trd3 :: (a, b, c) -> c-trd3 (_, _, z) = z
src/Halberd/ChosenImports.hs view
@@ -12,7 +12,7 @@ data ChosenImports = ChosenImports   { qualifieds   :: Map (ModuleName ()) Cabal.ModuleName   , unqualifieds :: Map Cabal.ModuleName [Name ()]-  }+  } deriving (Show, Eq)  instance Monoid ChosenImports where   mempty = ChosenImports
+ src/Halberd/LookupTable.hs view
@@ -0,0 +1,60 @@+{-# LANGUAGE TupleSections #-}+module Halberd.LookupTable where++import           Control.Arrow+import           Control.Monad hiding (forM_)+import           Data.Function+import           Data.List+import           Data.Maybe+import           Data.Map                            (Map)+import qualified Data.Map                            as Map+import           Data.Monoid+import           Data.Ord+import           Data.Set                            (Set)+import qualified Data.Set                            as Set+import           Distribution.HaskellSuite+import qualified Distribution.InstalledPackageInfo   as Cabal+import           Language.Haskell.Exts.Annotated+import           Language.Haskell.Names++import Data.Tuple.Utils+import Halberd.Types++type LookupTable = Map String [CanonicalSymbol]++mkLookupTables :: ModuleT Symbols IO (LookupTable, LookupTable)+mkLookupTables =+  do pkgs <- getPackages+     (valueDefs, typeDefs) <-+       fmap mconcat $ forM pkgs $ \pkg ->+         fmap mconcat $ forM (Cabal.exposedModules pkg) $ \exposedModule -> do+            (Symbols values types) <- readModuleInfo (Cabal.libraryDirs pkg) exposedModule+            let mkDefs qname = Set.map ((toPackageRef pkg, exposedModule,) . origName) qname+            return (mkDefs values, mkDefs types)+     let valueTable = toLookupTable (gUnqual . trd3) valueDefs+         typeTable  = toLookupTable (gUnqual . trd3) typeDefs+     return (valueTable, typeTable)+  where+    gUnqual (OrigName _ (GName _ n))  = n+++lookupDefinitions :: LookupTable -> QName (Scoped SrcSpan) -> [CanonicalSymbol]+lookupDefinitions symbolTable qname = fromMaybe [] $+  do n <- unQName qname+     Map.lookup n symbolTable+  where+    unQName (Qual    _ _ n) = Just (strName n)+    unQName (UnQual  _   n) = Just (strName n)+    unQName (Special _ _  ) = Nothing++    strName (Ident  _ str)  = str+    strName (Symbol _ str)  = str+++toLookupTable :: Ord k => (a -> k) -> Set a -> Map k [a]+toLookupTable key = Map.fromList+                  . map (fst . head &&& map snd)+                  . groupBy ((==) `on` fst)+                  . sortBy (comparing fst)+                  . map (key &&& id)+                  . Set.toList
+ src/Halberd/Suggestions.hs view
@@ -0,0 +1,86 @@+{-# LANGUAGE DoAndIfThenElse  #-}+{-# LANGUAGE FlexibleContexts #-}+module Halberd.Suggestions where++import           Control.Applicative+import           Control.Arrow+import           Control.Monad hiding (forM_)+import           Control.Monad.State hiding (forM_)+import           Data.Function+import           Data.List+import           Data.Maybe+import qualified Data.Map                            as Map+import           Data.Monoid+import           Distribution.HaskellSuite+import           Language.Haskell.Exts.Annotated+import           Language.Haskell.Names+import           Safe++import Data.Tuple.Utils+import Halberd.ChosenImports+import Halberd.CollectNames+import Halberd.LookupTable+import Halberd.Types+import Language.Haskell.Exts.Utils++type Suggestion = (QName (Scoped SrcSpan), [CanonicalSymbol])++suggestedImports :: Module SrcSpanInfo -> ModuleT Symbols IO [Suggestion]+suggestedImports module_ =+  do (unboundTypes, unboundValues) <- uniques <$> findUnbound module_+     (valueTable, typeTable) <- mkLookupTables+     let valueSuggestions = map (id &&& lookupDefinitions valueTable) unboundValues+         typeSuggestions  = map (id &&& lookupDefinitions typeTable ) unboundTypes+     return $ valueSuggestions ++ typeSuggestions+  where+    uniques = unique *** unique+    unique = nubBy ((==) `on` void)++type ChooseExternal m = QName (Scoped SrcSpan) -> [CanonicalSymbol] -> m CanonicalSymbol++resolveAllSuggestions :: (Functor m, Monad m) => ChooseExternal m -> [Suggestion] -> m ChosenImports+resolveAllSuggestions chooseExternal suggestions = execStateT (go suggestions) mempty+  where+    go sugs = do+      remaining <- resolveSuggestions sugs+      case remaining of+        [] -> return []+        ((qname, modules):ss) -> do+          choice <- lift $ chooseExternal qname modules+          modify $ insertChoice qname (snd3 choice)+          go ss++resolveSuggestions :: (Functor m, MonadState ChosenImports m) => [Suggestion] -> m [Suggestion]+resolveSuggestions suggestions =+  do newSuggestions <- resolveSuggestionsOnePass suggestions+     if suggestions == newSuggestions+     then return newSuggestions+     else resolveSuggestions newSuggestions++resolveSuggestionsOnePass :: (Functor m, MonadState ChosenImports m) => [Suggestion] -> m [Suggestion]+resolveSuggestionsOnePass suggestions = fmap catMaybes . forM suggestions $ \suggestion@(qname, modules) ->+  do chosenModules <- get+     if alreadyChosen qname modules chosenModules+     then+       return Nothing+     else do+       case hasSingleOption qname modules chosenModules of+         Nothing           -> return $ Just suggestion+         Just choice -> do+           modify $ insertChoice qname (snd3 choice)+           return Nothing+  where+    alreadyChosen qname modules chosenModules = fromMaybe False $+      do q <- getQualification qname+         module_ <- lookupQualified q chosenModules+         return $ module_ `elem` map snd3 modules+    hasSingleOption _        [module_] _             = Just module_+    hasSingleOption UnQual{} modules   chosenModules | singleOrigName modules =+      headMay $ filter ((`Map.member` unqualifieds chosenModules) . snd3) modules+    hasSingleOption _        _         _             = Nothing+    singleOrigName = allEqual . map trd3+    allEqual []     = True+    allEqual (x:xs) = all (== x) xs++findUnbound :: Module SrcSpanInfo -> ModuleT Symbols IO ([QName (Scoped SrcSpan)], [QName (Scoped SrcSpan)])+findUnbound module_ = collectUnboundNames <$> annotateModule Haskell98 [] (fmap srcInfoSpan module_)
+ src/Halberd/Types.hs view
@@ -0,0 +1,19 @@+module Halberd.Types where++import qualified Distribution.InstalledPackageInfo   as Cabal+import qualified Distribution.ModuleName             as Cabal+import qualified Distribution.Package                as Cabal+import           Language.Haskell.Names++type CanonicalSymbol = (PackageRef, Cabal.ModuleName, OrigName)++data PackageRef = PackageRef+  { installedPackageId :: Cabal.InstalledPackageId+  , sourcePackageId    :: Cabal.PackageId+  } deriving (Eq, Ord, Show)++toPackageRef :: Cabal.InstalledPackageInfo_ m -> PackageRef+toPackageRef pkgInfo =+    PackageRef { installedPackageId = Cabal.installedPackageId pkgInfo+               , sourcePackageId    = Cabal.sourcePackageId    pkgInfo+               }
+ tests/Runner.hs view
@@ -0,0 +1,87 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE FlexibleInstances, OverloadedStrings #-}+import Control.Applicative+import Control.Monad+import Control.Monad.State+import Data.Char+import Data.Monoid+import Data.List.Split+import Data.Proxy+import Data.String+import Distribution.HaskellSuite+import Distribution.Simple.Compiler+import Language.Haskell.Exts.Annotated hiding (fileName)+import Language.Haskell.Names.Interfaces+import Test.Framework (defaultMain)+import Test.Framework.Providers.HUnit (testCase)+import Test.HUnit (assertEqual, Assertion)+import qualified Data.Map                as Map+import qualified Distribution.ModuleName as Cabal++import Halberd.ChosenImports (ChosenImports (..))+import Halberd.Suggestions++main :: IO ()+main = do+  pkgs <- concat <$> mapM (getInstalledPackages (Proxy :: Proxy NamesDB)) [UserPackageDB, GlobalPackageDB]+  defaultMain [ testCase "Collect unbound" (collectUnbound pkgs)+              , testCase "Resolve single option" (resolveSingleOption pkgs)+              , testCase "Resolve multiple options" (resolveMultiple pkgs)+              , testCase "Resolve multiple options (different order)" (resolveMultipleDifferentOrder pkgs)+              ]++collectUnbound :: Packages -> Assertion+collectUnbound pkgs = do+  (ParseOk module_) <- parseFile "tests/input/Unbound.hs"+  (unboundTypes, unboundValues) <- evalModuleT (findUnbound module_) pkgs "names" readInterface+  assertEqual "Unbound types"  (void <$> unboundTypes)  ["M.Map", "Int8"]+  assertEqual "Unbound values" (void <$> unboundValues) ["forM", "M.fromList"]++testResolution :: FilePath -> ChosenImports -> Packages -> Assertion+testResolution fileName expectedImports pkgs = do+  (ParseOk module_) <- parseFile fileName+  suggestions <- evalModuleT (suggestedImports module_) pkgs "names" readInterface+  let (newSuggestions, imports) = runState (resolveSuggestions suggestions) mempty+  assertEqual "resolved suggestions" [] newSuggestions+  assertEqual "chosen imports" expectedImports imports++resolveSingleOption :: Packages -> Assertion+resolveSingleOption = testResolution "tests/input/ResolveSingle.hs"+    ChosenImports { qualifieds   = Map.fromList []+                  , unqualifieds = Map.fromList [("Control.Applicative", ["pure"])]+                  }++resolveMultiple :: Packages -> Assertion+resolveMultiple = testResolution "tests/input/ResolveMultiple.hs"+    ChosenImports { qualifieds   = Map.fromList []+                  , unqualifieds = Map.fromList [("Control.Applicative", ["<$>", "pure"])]+                  }++resolveMultipleDifferentOrder :: Packages -> Assertion+resolveMultipleDifferentOrder = testResolution "tests/input/ResolveMultipleDifferentOrder.hs"+    ChosenImports { qualifieds   = Map.fromList []+                  , unqualifieds = Map.fromList [("Control.Applicative", ["<$>", "pure"])]+                  }++-- Some instances to make it easier to write down expected values in+-- assertions.++instance IsString (QName ()) where+  fromString str =+    let parts = splitOn "." str+    in  case parts of+          [nm]      -> UnQual () (fromString nm)+          [qual,nm] -> Qual () (fromString qual) (fromString nm)+          _         -> error "Too many dots in IsString Name."++instance IsString (ModuleName ()) where+  fromString = ModuleName ()++instance IsString (Name ()) where+  fromString str =+    if (isAlpha (head str) || head str == '_')+    then Ident  () str+    else Symbol () str++instance IsString Cabal.ModuleName where+  fromString = Cabal.fromString