forked from haskell/haskell-language-server
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathImplicit.hs
More file actions
155 lines (132 loc) · 5.78 KB
/
Copy pathImplicit.hs
File metadata and controls
155 lines (132 loc) · 5.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
module Development.IDE.Session.Implicit
( loadImplicitCradle
) where
import Control.Applicative ((<|>))
import Control.Exception (handleJust)
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Trans.Maybe
import Data.Bifunctor
import Data.Functor ((<&>))
import Data.Maybe
import Data.Void
import System.Directory hiding (findFile)
import System.FilePath
import System.IO.Error
import Colog.Core (LogAction (..), WithSeverity (..))
import HIE.Bios.Config
import HIE.Bios.Cradle (defaultCradle, getCradle)
import HIE.Bios.Types hiding (ActionName (..))
import Hie.Cabal.Parser
import Hie.Locate
import qualified Hie.Yaml as Implicit
loadImplicitCradle :: Show a => LogAction IO (WithSeverity Log) -> FilePath -> IO (Cradle a)
loadImplicitCradle l wfile = do
is_dir <- doesDirectoryExist wfile
let wdir | is_dir = wfile
| otherwise = takeDirectory wfile
cfg <- runMaybeT (implicitConfig wdir)
case cfg of
Just bc -> getCradle l absurd bc
Nothing -> return $ defaultCradle l wdir
-- | Wraps up the cradle inferred by @inferCradleTree@ as a @CradleConfig@ with no dependencies
implicitConfig :: FilePath -> MaybeT IO (CradleConfig a, FilePath)
implicitConfig = (fmap . first) (CradleConfig noDeps) . inferCradleTree
where
noDeps :: [FilePath]
noDeps = []
inferCradleTree :: FilePath -> MaybeT IO (CradleTree a, FilePath)
inferCradleTree start_dir =
maybeItsBios
-- If we have both a config file (cabal.project/stack.yaml) and a work dir
-- (dist-newstyle/.stack-work), prefer that
<|> (cabalExecutable >> cabalConfigDir start_dir >>= \dir -> cabalWorkDir dir >> pure (simpleCabalCradle dir))
<|> (stackExecutable >> stackConfigDir start_dir >>= \dir -> stackWorkDir dir >> stackCradle dir)
-- If we have a cabal.project OR we have a .cabal and dist-newstyle, prefer cabal
<|> (cabalExecutable >> (cabalConfigDir start_dir <|> cabalFileAndWorkDir) <&> simpleCabalCradle)
-- If we have a stack.yaml, use stack
<|> (stackExecutable >> stackConfigDir start_dir >>= stackCradle)
-- If we have a cabal file, use cabal
<|> (cabalExecutable >> cabalFileDir start_dir <&> simpleCabalCradle)
where
maybeItsBios = (\wdir -> (Bios (Program $ wdir </> ".hie-bios") Nothing Nothing, wdir)) <$> biosWorkDir start_dir
cabalFileAndWorkDir = cabalFileDir start_dir >>= (\dir -> cabalWorkDir dir >> pure dir)
-- | Generate a stack cradle given a filepath.
--
-- Since we assume there was proof that this file belongs to a stack cradle
-- we look immediately for the relevant @*.cabal@ and @stack.yaml@ files.
-- We do not look for package.yaml, as we assume the corresponding .cabal has
-- been generated already.
--
-- We parse the @stack.yaml@ to find relevant @*.cabal@ file locations, then
-- we parse the @*.cabal@ files to generate a mapping from @hs-source-dirs@ to
-- component names.
stackCradle :: FilePath -> MaybeT IO (CradleTree a, FilePath)
stackCradle fp = do
pkgs <- stackYamlPkgs fp
pkgsWithComps <- liftIO $ catMaybes <$> mapM (nestedPkg fp) pkgs
let yaml = fp </> "stack.yaml"
pure $ (,fp) $ case pkgsWithComps of
[] -> Stack (StackType Nothing (Just yaml) Nothing)
ps -> StackMulti mempty $ do
Package n cs <- ps
c <- cs
let (prefix, comp) = Implicit.stackComponent n c
pure (prefix, StackType (Just comp) (Just yaml) Nothing)
-- | By default, we generate a simple cabal cradle which is equivalent to the
-- following hie.yaml:
--
-- @
-- cradle:
-- cabal:
-- @
--
-- Note, this only works reliable for reasonably modern cabal versions >= 3.2.
simpleCabalCradle :: FilePath -> (CradleTree a, FilePath)
simpleCabalCradle fp = (Cabal $ CabalType Nothing Nothing Nothing, fp)
cabalExecutable :: MaybeT IO FilePath
cabalExecutable = MaybeT $ findExecutable "cabal"
stackExecutable :: MaybeT IO FilePath
stackExecutable = MaybeT $ findExecutable "stack"
biosWorkDir :: FilePath -> MaybeT IO FilePath
biosWorkDir = findFileUpwards (".hie-bios" ==)
cabalWorkDir :: FilePath -> MaybeT IO ()
cabalWorkDir wdir = do
check <- liftIO $ doesDirectoryExist (wdir </> "dist-newstyle")
unless check $ fail "No dist-newstyle"
stackWorkDir :: FilePath -> MaybeT IO ()
stackWorkDir wdir = do
check <- liftIO $ doesDirectoryExist (wdir </> ".stack-work")
unless check $ fail "No .stack-work"
cabalConfigDir :: FilePath -> MaybeT IO FilePath
cabalConfigDir = findFileUpwards (\fp -> fp == "cabal.project" || fp == "cabal.project.local")
cabalFileDir :: FilePath -> MaybeT IO FilePath
cabalFileDir = findFileUpwards (\fp -> takeExtension fp == ".cabal")
stackConfigDir :: FilePath -> MaybeT IO FilePath
stackConfigDir = findFileUpwards isStack
where
isStack name = name == "stack.yaml"
-- | Searches upwards for the first directory containing a file to match
-- the predicate.
findFileUpwards :: (FilePath -> Bool) -> FilePath -> MaybeT IO FilePath
findFileUpwards p dir = do
cnts <-
liftIO
$ handleJust
-- Catch permission errors
(\(e :: IOError) -> if isPermissionError e then Just [] else Nothing)
pure
(findFile p dir)
case cnts of
[] | dir' == dir -> fail "No cabal files"
| otherwise -> findFileUpwards p dir'
_ : _ -> return dir
where dir' = takeDirectory dir
-- | Sees if any file in the directory matches the predicate
findFile :: (FilePath -> Bool) -> FilePath -> IO [FilePath]
findFile p dir = do
b <- doesDirectoryExist dir
if b then getFiles >>= filterM doesPredFileExist else return []
where
getFiles = filter p <$> getDirectoryContents dir
doesPredFileExist file = doesFileExist $ dir </> file