@@ -42,6 +42,7 @@ import qualified Elm.Details as Details
42import qualified Elm.Docs as Docs
42import qualified Elm.Docs as Docs
43import qualified Elm.Interface as I
43import qualified Elm.Interface as I
44import qualified Elm.ModuleName as ModuleName
44import qualified Elm.ModuleName as ModuleName
45+import qualified Elm.Outline as Outline
45import qualified Elm.Package as Pkg
46import qualified Elm.Package as Pkg
46import qualified File
47import qualified File
47import qualified Json.Encode as E
48import qualified Json.Encode as E
@@ -65,21 +66,46 @@ data Env =
65 { _key :: Reporting.BKey
66 { _key :: Reporting.BKey
66 , _root :: FilePath
67 , _root :: FilePath
67 , _project :: Parse.ProjectType
68 , _project :: Parse.ProjectType
68− , _srcDirs :: [FilePath]
69+ , _srcDirs :: [AbsoluteSrcDir]
69 , _buildID :: Details.BuildID
70 , _buildID :: Details.BuildID
70 , _locals :: Map.Map ModuleName.Raw Details.Local
71 , _locals :: Map.Map ModuleName.Raw Details.Local
71 , _foreigns :: Map.Map ModuleName.Raw Details.Foreign
72 , _foreigns :: Map.Map ModuleName.Raw Details.Foreign
75−makeEnv :: Reporting.BKey -> FilePath -> Details.Details -> Env
76+makeEnv :: Reporting.BKey -> FilePath -> Details.Details -> IO Env
76makeEnv key root (Details.Details _ validOutline buildID locals foreigns _) =
77makeEnv key root (Details.Details _ validOutline buildID locals foreigns _) =
77 case validOutline of
78 case validOutline of
78− Details.ValidApp srcDirs ->
79+ Details.ValidApp givenSrcDirs ->
80+ do srcDirs <- traverse (toAbsoluteSrcDir root) (NE.toList givenSrcDirs)
79− Env key root Parse.Application (NE.toList srcDirs) buildID locals foreigns
81+ return $ Env key root Parse.Application srcDirs buildID locals foreigns
81 Details.ValidPkg pkg _ _ ->
83 Details.ValidPkg pkg _ _ ->
84+ do srcDir <- toAbsoluteSrcDir root (Outline.RelativeSrcDir "src")
82− Env key root (Parse.Package pkg) ["src"] buildID locals foreigns
85+ return $ Env key root (Parse.Package pkg) [srcDir] buildID locals foreigns
92+newtype AbsoluteSrcDir =
93+ AbsoluteSrcDir FilePath
96+toAbsoluteSrcDir :: FilePath -> Outline.SrcDir -> IO AbsoluteSrcDir
97+toAbsoluteSrcDir root srcDir =
98+ AbsoluteSrcDir <$> Dir.canonicalizePath
101+ Outline.AbsoluteSrcDir dir -> dir
102+ Outline.RelativeSrcDir dir -> root </> dir
106+addRelative :: AbsoluteSrcDir -> FilePath -> FilePath
107+addRelative (AbsoluteSrcDir srcDir) path =
@@ -110,7 +136,7 @@ forkWithKey func dict =
110fromExposed :: Reporting.Style -> FilePath -> Details.Details -> DocsGoal docs -> NE.List ModuleName.Raw -> IO (Either Exit.BuildProblem docs)
136fromExposed :: Reporting.Style -> FilePath -> Details.Details -> DocsGoal docs -> NE.List ModuleName.Raw -> IO (Either Exit.BuildProblem docs)
111fromExposed style root details docsGoal exposed@(NE.List e es) =
137fromExposed style root details docsGoal exposed@(NE.List e es) =
112 Reporting.trackBuild style $ \key ->
138 Reporting.trackBuild style $ \key ->
113− do let env = makeEnv key root details
139+ do env <- makeEnv key root details
114 dmvar <- Details.loadInterfaces root details
140 dmvar <- Details.loadInterfaces root details
@@ -161,7 +187,7 @@ type Dependencies =
161fromMains :: Reporting.Style -> FilePath -> Details.Details -> NE.List FilePath -> IO (Either Exit.BuildProblem Artifacts)
187fromMains :: Reporting.Style -> FilePath -> Details.Details -> NE.List FilePath -> IO (Either Exit.BuildProblem Artifacts)
162fromMains style root details paths =
188fromMains style root details paths =
163 Reporting.trackBuild style $ \key ->
189 Reporting.trackBuild style $ \key ->
164− do let env = makeEnv key root details
190+ do env <- makeEnv key root details
166 elmains <- findMains env paths
192 elmains <- findMains env paths
167 case elmains of
193 case elmains of
@@ -241,9 +267,8 @@ crawlDeps env mvar deps blockedValue =
241crawlModule :: Env -> MVar StatusDict -> DocsNeed -> ModuleName.Raw -> IO Status
267crawlModule :: Env -> MVar StatusDict -> DocsNeed -> ModuleName.Raw -> IO Status
242crawlModule env@(Env _ root projectType srcDirs buildID locals foreigns) mvar docsNeed name =
268crawlModule env@(Env _ root projectType srcDirs buildID locals foreigns) mvar docsNeed name =
243 do let fileName = ModuleName.toFilePath name <.> "elm"
269 do let fileName = ModuleName.toFilePath name <.> "elm"
244− let inRoot path = File.exists (root </> path)
246− paths <- filterM inRoot (map (</> fileName) srcDirs)
271+ paths <- filterM File.exists (map (`addRelative` fileName) srcDirs)
248 case paths of
273 case paths of
249 [path] ->
274 [path] ->
@@ -263,7 +288,7 @@ crawlModule env@(Env _ root projectType srcDirs buildID locals foreigns) mvar do
263 else crawlDeps env mvar deps (SCached local)
288 else crawlDeps env mvar deps (SCached local)
265 p1:p2:ps ->
290 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)
269 case Map.lookup name foreigns of
294 case Map.lookup name foreigns of
@@ -856,35 +881,33 @@ data ReplArtifacts =
857fromRepl :: FilePath -> Details.Details -> B.ByteString -> IO (Either Exit.Repl ReplArtifacts)
882fromRepl :: FilePath -> Details.Details -> B.ByteString -> IO (Either Exit.Repl ReplArtifacts)
858fromRepl root details source =
883fromRepl root details source =
860− env@(Env _ _ projectType _ _ _ _) = makeEnv Reporting.ignorer root details
884+ do env@(Env _ _ projectType _ _ _ _) <- makeEnv Reporting.ignorer root details
862− case Parse.fromByteString projectType source of
885+ case Parse.fromByteString projectType source of
863− Left syntaxError ->
886+ Left syntaxError ->
864− return $ Left $ Exit.ReplBadInput source $ Error.BadSyntax syntaxError
887+ return $ Left $ Exit.ReplBadInput source $ Error.BadSyntax syntaxError
866− Right modul@(Src.Module _ _ _ imports _ _ _ _ _) ->
889+ Right modul@(Src.Module _ _ _ imports _ _ _ _ _) ->
867− do dmvar <- Details.loadInterfaces root details
890+ do dmvar <- Details.loadInterfaces root details
869− let deps = map Src.getImportName imports
892+ let deps = map Src.getImportName imports
870− mvar <- newMVar Map.empty
893+ mvar <- newMVar Map.empty
871− crawlDeps env mvar deps ()
894+ crawlDeps env mvar deps ()
873− statuses <- traverse readMVar =<< readMVar mvar
896+ statuses <- traverse readMVar =<< readMVar mvar
874− midpoint <- checkMidpoint dmvar statuses
897+ midpoint <- checkMidpoint dmvar statuses
876− case midpoint of
899+ case midpoint of
877− Left problem ->
900+ Left problem ->
878− return $ Left $ Exit.ReplProjectProblem problem
901+ return $ Left $ Exit.ReplProjectProblem problem
880− Right foreigns ->
903+ Right foreigns ->
881− do rmvar <- newEmptyMVar
904+ do rmvar <- newEmptyMVar
882− resultMVars <- forkWithKey (checkModule env foreigns rmvar) statuses
905+ resultMVars <- forkWithKey (checkModule env foreigns rmvar) statuses
883− putMVar rmvar resultMVars
906+ putMVar rmvar resultMVars
884− results <- traverse readMVar resultMVars
907+ results <- traverse readMVar resultMVars
885− writeDetails root details results
908+ writeDetails root details results
886− depsStatus <- checkDeps root resultMVars deps 0
909+ depsStatus <- checkDeps root resultMVars deps 0
887− finalizeReplArtifacts env source modul depsStatus resultMVars results
910+ finalizeReplArtifacts env source modul depsStatus resultMVars results
890finalizeReplArtifacts :: Env -> B.ByteString -> Src.Module -> DepsStatus -> ResultDict -> Map.Map ModuleName.Raw Result -> IO (Either Exit.Repl ReplArtifacts)
913finalizeReplArtifacts :: 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 =
997 return $ Left $ Exit.BP_WithBadExtension path
1020 return $ Left $ Exit.BP_WithBadExtension path
1000− roots = FP.splitDirectories root
1001− segments = FP.splitDirectories dirs ++ [final]
1023+ absoluteSegments = FP.splitDirectories dirs ++ [final]
1003− case dropPrefix roots segments of
1025+ case Maybe.mapMaybe (isInsideSrcDirByPath absoluteSegments) srcDirs of
1004− Nothing ->
1026+ [] ->
1005 return $ Right $ Location absolutePath path (LOutside path)
1027 return $ Right $ Location absolutePath path (LOutside path)
1007− Just relativeSegments ->
1008− case Maybe.mapMaybe (isInsideSrcDirByPath relativeSegments) srcDirs of
1010− return $ Right $ Location absolutePath path (LOutside path)
1012− [(_, Right names)] ->
1029+ [(_, Right names)] ->
1013− do let name = Name.fromChars (List.intercalate "." names)
1030+ do let name = Name.fromChars (List.intercalate "." names)
1014− matchingDirs <- filterM (isInsideSrcDirByName root names) srcDirs
1031+ matchingDirs <- filterM (isInsideSrcDirByName names) srcDirs
1015− case matchingDirs of
1032+ case matchingDirs of
1016− d1:d2:_ ->
1033+ d1:d2:_ ->
1017− do let p1 = d1 </> FP.joinPath names <.> "elm"
1034+ do let p1 = addRelative d1 (FP.joinPath names <.> "elm")
1018− let p2 = d2 </> FP.joinPath names <.> "elm"
1035+ let p2 = addRelative d2 (FP.joinPath names <.> "elm")
1019− return $ Left $ Exit.BP_MainNameDuplicate name p1 p2
1036+ return $ Left $ Exit.BP_MainNameDuplicate name p1 p2
1022− return $ Right $ Location absolutePath path (LInside name)
1039+ return $ Right $ Location absolutePath path (LInside name)
1024− [(s, Left names)] ->
1041+ [(s, Left names)] ->
1025− return $ Left $ Exit.BP_MainNameInvalid path s names
1042+ return $ Left $ Exit.BP_MainNameInvalid path s names
1027− (s1,_):(s2,_):_ ->
1044+ (s1,_):(s2,_):_ ->
1028− return $ Left $ Exit.BP_WithAmbiguousSrcDir path s1 s2
1045+ return $ Left $ Exit.BP_WithAmbiguousSrcDir path s1 s2
1032−isInsideSrcDirByName :: FilePath -> [String] -> FilePath -> IO Bool
1049+isInsideSrcDirByName :: [String] -> AbsoluteSrcDir -> IO Bool
1033−isInsideSrcDirByName root names srcDir =
1050+isInsideSrcDirByName names srcDir =
1034− File.exists (root </> srcDir </> FP.joinPath names <.> "elm")
1051+ File.exists (addRelative srcDir (FP.joinPath names <.> "elm"))
1037−isInsideSrcDirByPath :: [String] -> FilePath -> Maybe (FilePath, Either [String] [String])
1054+isInsideSrcDirByPath :: [String] -> AbsoluteSrcDir -> Maybe (FilePath, Either [String] [String])
1038−isInsideSrcDirByPath segments srcDir =
1055+isInsideSrcDirByPath segments (AbsoluteSrcDir srcDir) =
1039 case dropPrefix (FP.splitDirectories srcDir) segments of
1056 case dropPrefix (FP.splitDirectories srcDir) segments of
1040 Nothing ->
1057 Nothing ->
@@ -1056,20 +1073,18 @@ isGoodName name =
1056 Char.isUpper char && all (\c -> Char.isAlphaNum c || c == '_') chars
1073 Char.isUpper char && all (\c -> Char.isAlphaNum c || c == '_') chars
1076+-- INVARIANT: Dir.canonicalizePath has been run on both inputs
1059dropPrefix :: [FilePath] -> [FilePath] -> Maybe [FilePath]
1078dropPrefix :: [FilePath] -> [FilePath] -> Maybe [FilePath]
1060dropPrefix roots paths =
1079dropPrefix roots paths =
1061 case roots of
1080 case roots of
1063 Just paths
1082 Just paths
1066− dropPrefix rs paths
1069 case paths of
1085 case paths of
1070− [] -> Nothing
1086+ [] -> Nothing
1071− ".":ps -> dropPrefix roots ps
1072− p:ps -> if r == p then dropPrefix rs ps else Nothing
1087+ p:ps -> if r == p then dropPrefix rs ps else Nothing