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