@@ -10,13 +10,21 @@ module Development.IDE.Core.Actions
10
10
, lookupMod
11
11
) where
12
12
13
+ import Control.Concurrent.MVar (MVar , newEmptyMVar ,
14
+ putMVar , readMVar )
15
+ import Control.Concurrent.STM (atomically )
16
+ import Control.Concurrent.STM.TQueue (unGetTQueue )
17
+ import Control.Monad (unless )
13
18
import Control.Monad.Extra (mapMaybeM )
14
19
import Control.Monad.Reader
15
20
import Control.Monad.Trans.Maybe
21
+ import qualified Data.ByteString as BS
22
+ import Data.Function ((&) )
16
23
import qualified Data.HashMap.Strict as HM
17
24
import Data.Maybe
18
25
import qualified Data.Text as T
19
26
import Data.Tuple.Extra
27
+ import Development.IDE.Core.Compile (loadHieFile )
20
28
import Development.IDE.Core.OfInterest
21
29
import Development.IDE.Core.PluginUtils
22
30
import Development.IDE.Core.PositionMapping
@@ -33,18 +41,92 @@ import Language.LSP.Protocol.Types (DocumentHighlight (..),
33
41
SymbolInformation (.. ),
34
42
normalizedFilePathToUri ,
35
43
uriToNormalizedFilePath )
44
+ import Language.LSP.Server (resRootPath )
45
+ import System.Directory (createDirectoryIfMissing ,
46
+ doesFileExist ,
47
+ getPermissions ,
48
+ setOwnerExecutable ,
49
+ setOwnerWritable ,
50
+ setPermissions )
51
+ import System.FilePath (takeDirectory , (<.>) ,
52
+ (</>) )
36
53
37
54
38
- -- | Eventually this will lookup/generate URIs for files in dependencies, but not in the
39
- -- project. Right now, this is just a stub.
55
+ -- | Generates URIs for files in dependencies, but not in the
56
+ -- project. Dependency files are produced from an HIE file and
57
+ -- placed in the .hls/dependencies directory.
40
58
lookupMod
41
59
:: HieDbWriter -- ^ access the database
42
60
-> FilePath -- ^ The `.hie` file we got from the database
43
61
-> ModuleName
44
62
-> Unit
45
63
-> Bool -- ^ Is this file a boot file?
46
64
-> MaybeT IdeAction Uri
47
- lookupMod _dbchan _hie_f _mod _uid _boot = MaybeT $ pure Nothing
65
+ lookupMod HieDbWriter {indexQueue} hieFile moduleName uid _boot = MaybeT $ do
66
+ -- We need the project root directory to determine where to put
67
+ -- the .hls directory.
68
+ mProjectRoot <- (resRootPath =<< ) <$> asks lspEnv
69
+ case mProjectRoot of
70
+ Nothing -> pure Nothing
71
+ Just projectRoot -> do
72
+ -- Database writes happen asynchronously. We use an MVar to mark
73
+ -- completion of the database update.
74
+ completionToken <- liftIO $ newEmptyMVar
75
+ -- Write out the contents of the dependency source to the
76
+ -- .hls/dependencies directory, generate a URI for that
77
+ -- location, and update the HieDb database with the source
78
+ -- file location.
79
+ moduleUri <- writeAndIndexSource projectRoot completionToken
80
+ -- Wait for the database update to be completed.
81
+ -- Reading the completionToken is blocked until it has
82
+ -- a value.
83
+ liftIO $ readMVar completionToken
84
+ pure $ Just moduleUri
85
+ where
86
+ writeAndIndexSource :: FilePath -> MVar () -> IdeAction Uri
87
+ writeAndIndexSource projectRoot completionToken = do
88
+ fileExists <- liftIO $ doesFileExist writeOutPath
89
+ -- No need to write out the file if it already exists.
90
+ unless fileExists $ do
91
+ nc <- asks ideNc
92
+ liftIO $ do
93
+ -- Create the directory where we will put the source.
94
+ createDirectoryIfMissing True $ takeDirectory writeOutPath
95
+ -- Load a raw Bytestring of the source from the HIE file.
96
+ moduleSource <- hie_hs_src <$> loadHieFile (mkUpdater nc) hieFile
97
+ -- Write the source into the .hls/dependencies directory.
98
+ BS. writeFile writeOutPath moduleSource
99
+ fileDefaultPermissions <- getPermissions writeOutPath
100
+ let filePermissions = fileDefaultPermissions
101
+ & setOwnerWritable False
102
+ & setOwnerExecutable False
103
+ -- Set the source file to readonly permissions.
104
+ setPermissions writeOutPath filePermissions
105
+ liftIO $ atomically $
106
+ unGetTQueue indexQueue $ \ withHieDb -> do
107
+ withHieDb $ \ db ->
108
+ -- Add a source file to the database row for
109
+ -- the HIE file.
110
+ HieDb. addSrcFile db hieFile writeOutPath False
111
+ -- Mark completion of the database update.
112
+ putMVar completionToken ()
113
+ pure $ moduleUri
114
+ where
115
+ -- The source will be written out in a directory from the
116
+ -- name and hash of the package the dependency module is
117
+ -- found in. The name and hash are both parts of the UnitId.
118
+ writeOutDir :: FilePath
119
+ writeOutDir = projectRoot </> " .hls" </> " dependencies" </> show uid
120
+ -- The module name is separated into directories, with the
121
+ -- last part of the module name giving the name of the
122
+ -- haskell file with a .hs extension.
123
+ writeOutFile :: FilePath
124
+ writeOutFile = moduleNameSlashes moduleName <.> " hs"
125
+ writeOutPath :: FilePath
126
+ writeOutPath = writeOutDir </> writeOutFile
127
+ moduleUri :: Uri
128
+ moduleUri = AtPoint. toUri writeOutPath
129
+
48
130
49
131
50
132
-- IMPORTANT NOTE : make sure all rules `useWithStaleFastMT`d by these have a "Persistent Stale" rule defined,
0 commit comments