@@ -42,6 +42,7 @@ import qualified Elm.Details as Details
4242import qualified Elm.Docs as Docs
4343import qualified Elm.Interface as I
4444import qualified Elm.ModuleName as ModuleName
45+import qualified Elm.Outline as Outline
4546import qualified Elm.Package as Pkg
4647import qualified File
4748import qualified Json.Encode as E
@@ -65,21 +66,46 @@ data Env =
6566 { _key :: Reporting.BKey
6667 , _root :: FilePath
6768 , _project :: Parse.ProjectType
68− , _srcDirs :: [FilePath]
69+ , _srcDirs :: [AbsoluteSrcDir]
6970 , _buildID :: Details.BuildID
7071 , _locals :: Map.Map ModuleName.Raw Details.Local
7172 , _foreigns :: Map.Map ModuleName.Raw Details.Foreign
7273 }
7374
7475
75−makeEnv :: Reporting.BKey -> FilePath -> Details.Details -> Env
76+makeEnv :: Reporting.BKey -> FilePath -> Details.Details -> IO Env
7677makeEnv key root (Details.Details _ validOutline buildID locals foreigns _) =
7778 case validOutline of
78− Details.ValidApp srcDirs ->
79− Env key root Parse.Application (NE.toList srcDirs) buildID locals foreigns
79+ Details.ValidApp givenSrcDirs ->
80+ do srcDirs <- traverse (toAbsoluteSrcDir root) (NE.toList givenSrcDirs)
81+ return $ Env key root Parse.Application srcDirs buildID locals foreigns
8082
8183 Details.ValidPkg pkg _ _ ->
82− Env key root (Parse.Package pkg) ["src"] buildID locals foreigns
84+ do srcDir <- toAbsoluteSrcDir root (Outline.RelativeSrcDir "src")
85+ return $ Env key root (Parse.Package pkg) [srcDir] buildID locals foreigns
86+
87+
88+
89+-- SOURCE DIRECTORY
90+
91+
92+newtype AbsoluteSrcDir =
93+ AbsoluteSrcDir FilePath
94+
95+
96+toAbsoluteSrcDir :: FilePath -> Outline.SrcDir -> IO AbsoluteSrcDir
97+toAbsoluteSrcDir root srcDir =
98+ AbsoluteSrcDir <$> Dir.canonicalizePath
99+ (
100+ case srcDir of
101+ Outline.AbsoluteSrcDir dir -> dir
102+ Outline.RelativeSrcDir dir -> root </> dir
103+ )
104+
105+
106+addRelative :: AbsoluteSrcDir -> FilePath -> FilePath
107+addRelative (AbsoluteSrcDir srcDir) path =
108+ srcDir </> path
83109
84110
85111
@@ -110,7 +136,7 @@ forkWithKey func dict =
110136fromExposed :: Reporting.Style -> FilePath -> Details.Details -> DocsGoal docs -> NE.List ModuleName.Raw -> IO (Either Exit.BuildProblem docs)
111137fromExposed style root details docsGoal exposed@(NE.List e es) =
112138 Reporting.trackBuild style $ \key ->
113− do let env = makeEnv key root details
139+ do env <- makeEnv key root details
114140 dmvar <- Details.loadInterfaces root details
115141
116142 -- crawl
@@ -161,7 +187,7 @@ type Dependencies =
161187fromMains :: Reporting.Style -> FilePath -> Details.Details -> NE.List FilePath -> IO (Either Exit.BuildProblem Artifacts)
162188fromMains style root details paths =
163189 Reporting.trackBuild style $ \key ->
164− do let env = makeEnv key root details
190+ do env <- makeEnv key root details
165191
166192 elmains <- findMains env paths
167193 case elmains of
@@ -241,9 +267,8 @@ crawlDeps env mvar deps blockedValue =
241267crawlModule :: Env -> MVar StatusDict -> DocsNeed -> ModuleName.Raw -> IO Status
242268crawlModule env@(Env _ root projectType srcDirs buildID locals foreigns) mvar docsNeed name =
243269 do let fileName = ModuleName.toFilePath name <.> "elm"
244− let inRoot path = File.exists (root </> path)
245270
246− paths <- filterM inRoot (map (</> fileName) srcDirs)
271+ paths <- filterM File.exists (map (`addRelative` fileName) srcDirs)
247272
248273 case paths of
249274 [path] ->
@@ -263,7 +288,7 @@ crawlModule env@(Env _ root projectType srcDirs buildID locals foreigns) mvar do
263288 else crawlDeps env mvar deps (SCached local)
264289
265290 p1:p2:ps ->
266− return $ SBadImport $ Import.AmbiguousLocal p1 p2 ps
291+ return $ SBadImport $ Import.AmbiguousLocal (FP.makeRelative root p1) (FP.makeRelative root p2) (map (FP.makeRelative root) ps)
267292
268293 [] ->
269294 case Map.lookup name foreigns of
@@ -856,35 +881,33 @@ data ReplArtifacts =
856881
857882fromRepl :: FilePath -> Details.Details -> B.ByteString -> IO (Either Exit.Repl ReplArtifacts)
858883fromRepl root details source =
859− let
860− env@(Env _ _ projectType _ _ _ _) = makeEnv Reporting.ignorer root details
861− in
862− case Parse.fromByteString projectType source of
863− Left syntaxError ->
864− return $ Left $ Exit.ReplBadInput source $ Error.BadSyntax syntaxError
884+ do env@(Env _ _ projectType _ _ _ _) <- makeEnv Reporting.ignorer root details
885+ case Parse.fromByteString projectType source of
886+ Left syntaxError ->
887+ return $ Left $ Exit.ReplBadInput source $ Error.BadSyntax syntaxError
865888
866− Right modul@(Src.Module _ _ _ imports _ _ _ _ _) ->
867− do dmvar <- Details.loadInterfaces root details
889+ Right modul@(Src.Module _ _ _ imports _ _ _ _ _) ->
890+ do dmvar <- Details.loadInterfaces root details
868891
869− let deps = map Src.getImportName imports
870− mvar <- newMVar Map.empty
871− crawlDeps env mvar deps ()
892+ let deps = map Src.getImportName imports
893+ mvar <- newMVar Map.empty
894+ crawlDeps env mvar deps ()
872895
873− statuses <- traverse readMVar =<< readMVar mvar
874− midpoint <- checkMidpoint dmvar statuses
896+ statuses <- traverse readMVar =<< readMVar mvar
897+ midpoint <- checkMidpoint dmvar statuses
875898
876− case midpoint of
877− Left problem ->
878− return $ Left $ Exit.ReplProjectProblem problem
899+ case midpoint of
900+ Left problem ->
901+ return $ Left $ Exit.ReplProjectProblem problem
879902
880− Right foreigns ->
881− do rmvar <- newEmptyMVar
882− resultMVars <- forkWithKey (checkModule env foreigns rmvar) statuses
883− putMVar rmvar resultMVars
884− results <- traverse readMVar resultMVars
885− writeDetails root details results
886− depsStatus <- checkDeps root resultMVars deps 0
887− finalizeReplArtifacts env source modul depsStatus resultMVars results
903+ Right foreigns ->
904+ do rmvar <- newEmptyMVar
905+ resultMVars <- forkWithKey (checkModule env foreigns rmvar) statuses
906+ putMVar rmvar resultMVars
907+ results <- traverse readMVar resultMVars
908+ writeDetails root details results
909+ depsStatus <- checkDeps root resultMVars deps 0
910+ finalizeReplArtifacts env source modul depsStatus resultMVars results
888911
889912
890913finalizeReplArtifacts :: Env -> B.ByteString -> Src.Module -> DepsStatus -> ResultDict -> Map.Map ModuleName.Raw Result -> IO (Either Exit.Repl ReplArtifacts)
@@ -997,45 +1020,39 @@ findLoc (Env _ root _ srcDirs _ _ _) path absolutePath =
9971020 return $ Left $ Exit.BP_WithBadExtension path
9981021 else
9991022 let
1000− roots = FP.splitDirectories root
1001− segments = FP.splitDirectories dirs ++ [final]
1023+ absoluteSegments = FP.splitDirectories dirs ++ [final]
10021024 in
1003− case dropPrefix roots segments of
1004− Nothing ->
1025+ case Maybe.mapMaybe (isInsideSrcDirByPath absoluteSegments) srcDirs of
1026+ [] ->
10051027 return $ Right $ Location absolutePath path (LOutside path)
10061028
1007− Just relativeSegments ->
1008− case Maybe.mapMaybe (isInsideSrcDirByPath relativeSegments) srcDirs of
1009− [] ->
1010− return $ Right $ Location absolutePath path (LOutside path)
1011−
1012− [(_, Right names)] ->
1013− do let name = Name.fromChars (List.intercalate "." names)
1014− matchingDirs <- filterM (isInsideSrcDirByName root names) srcDirs
1015− case matchingDirs of
1016− d1:d2:_ ->
1017− do let p1 = d1 </> FP.joinPath names <.> "elm"
1018− let p2 = d2 </> FP.joinPath names <.> "elm"
1019− return $ Left $ Exit.BP_MainNameDuplicate name p1 p2
1029+ [(_, Right names)] ->
1030+ do let name = Name.fromChars (List.intercalate "." names)
1031+ matchingDirs <- filterM (isInsideSrcDirByName names) srcDirs
1032+ case matchingDirs of
1033+ d1:d2:_ ->
1034+ do let p1 = addRelative d1 (FP.joinPath names <.> "elm")
1035+ let p2 = addRelative d2 (FP.joinPath names <.> "elm")
1036+ return $ Left $ Exit.BP_MainNameDuplicate name p1 p2
10201037
1021− _ ->
1022− return $ Right $ Location absolutePath path (LInside name)
1038+ _ ->
1039+ return $ Right $ Location absolutePath path (LInside name)
10231040
1024− [(s, Left names)] ->
1025− return $ Left $ Exit.BP_MainNameInvalid path s names
1041+ [(s, Left names)] ->
1042+ return $ Left $ Exit.BP_MainNameInvalid path s names
10261043
1027− (s1,_):(s2,_):_ ->
1028− return $ Left $ Exit.BP_WithAmbiguousSrcDir path s1 s2
1044+ (s1,_):(s2,_):_ ->
1045+ return $ Left $ Exit.BP_WithAmbiguousSrcDir path s1 s2
10291046
10301047
10311048
1032−isInsideSrcDirByName :: FilePath -> [String] -> FilePath -> IO Bool
1033−isInsideSrcDirByName root names srcDir =
1034− File.exists (root </> srcDir </> FP.joinPath names <.> "elm")
1049+isInsideSrcDirByName :: [String] -> AbsoluteSrcDir -> IO Bool
1050+isInsideSrcDirByName names srcDir =
1051+ File.exists (addRelative srcDir (FP.joinPath names <.> "elm"))
10351052
10361053
1037−isInsideSrcDirByPath :: [String] -> FilePath -> Maybe (FilePath, Either [String] [String])
1038−isInsideSrcDirByPath segments srcDir =
1054+isInsideSrcDirByPath :: [String] -> AbsoluteSrcDir -> Maybe (FilePath, Either [String] [String])
1055+isInsideSrcDirByPath segments (AbsoluteSrcDir srcDir) =
10391056 case dropPrefix (FP.splitDirectories srcDir) segments of
10401057 Nothing ->
10411058 Nothing
@@ -1056,20 +1073,18 @@ isGoodName name =
10561073 Char.isUpper char && all (\c -> Char.isAlphaNum c || c == '_') chars
10571074
10581075
1076+-- INVARIANT: Dir.canonicalizePath has been run on both inputs
1077+--
10591078dropPrefix :: [FilePath] -> [FilePath] -> Maybe [FilePath]
10601079dropPrefix roots paths =
10611080 case roots of
10621081 [] ->
10631082 Just paths
10641083
1065− ".":rs ->
1066− dropPrefix rs paths
1067−
10681084 r:rs ->
10691085 case paths of
1070− [] -> Nothing
1071− ".":ps -> dropPrefix roots ps
1072− p:ps -> if r == p then dropPrefix rs ps else Nothing
1086+ [] -> Nothing
1087+ p:ps -> if r == p then dropPrefix rs ps else Nothing
10731088
10741089
10751090