1-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
4{-# LANGUAGE TypeFamilies #-}
6module Development.IDE.State.FileStore(
7 getFileExists, getFileContents,
14import "ghc-lib-parser" StringBuffer
16import qualified Data.Map.Strict as Map
17import qualified Data.Text as T
19import Control.Monad.Extra
20import qualified System.Directory as Dir
21import Development.Shake
22import Development.Shake.Classes
23import Development.IDE.State.Shake
24import Development.IDE.UtilGHC
25import Control.Concurrent.Extra
26import Control.Exception
28import qualified Data.ByteString.Char8 as BS
29import Development.IDE.Types.Diagnostics
33-- This module stores the changed files in memory, and answers file system questions
34-- from either the memory changes OR the file system itself
36type DirtyFiles = Map.Map FilePath (UTCTime, StringBuffer) -- when it was modified, it's current value
38-- Store the DirtyFiles globally, so we can get at it through setBufferModified
39newtype GlobalDirtyFiles = GlobalDirtyFiles (Var DirtyFiles)
40instance IsIdeGlobal GlobalDirtyFiles
44-- | Get the modification time of a file.
45type instance RuleResult GetModificationTime = UTCTime
47-- | Get the contents of a file, either dirty (if the buffer is modified) or from disk.
48type instance RuleResult GetFileContents = (UTCTime, StringBuffer)
50-- | Does the file exist.
51type instance RuleResult GetFileExists = Bool
54data GetFileExists = GetFileExists
55 deriving (Eq, Show, Generic)
56instance Binary GetFileExists
57instance Hashable GetFileExists
58instance NFData GetFileExists
60data GetModificationTime = GetModificationTime
61 deriving (Eq, Show, Generic)
62instance Binary GetModificationTime
63instance Hashable GetModificationTime
64instance NFData GetModificationTime
66data GetFileContents = GetFileContents
67 deriving (Eq, Show, Generic)
68instance Binary GetFileContents
69instance Hashable GetFileContents
70instance NFData GetFileContents
73getFileExistsRule :: Var DirtyFiles -> Rules ()
74getFileExistsRule dirty =
75 defineEarlyCutoff $ \GetFileExists file -> do
77 res <- liftIO $ handle (\(_ :: IOException) -> return False) $
78 (Map.member file <$> readVar dirty) ||^
79 Dir.doesFileExist file
80 return (Just $ if res then BS.singleton '1' else BS.empty, ([], Just res))
83showTimePrecise :: UTCTime -> String
84showTimePrecise UTCTime{..} = show (toModifiedJulianDay utctDay, diffTimeToPicoseconds utctDayTime)
86getModificationTimeRule :: Var DirtyFiles -> Rules ()
87getModificationTimeRule dirty =
88 defineEarlyCutoff $ \GetModificationTime file -> do
90 res <- liftIO $ ideTryIOException file $ do
92 case Map.lookup file mp of
93 Just (time, _) -> return time
94 Nothing -> Dir.getModificationTime file
96 Left err -> return (Nothing, ([err], Nothing))
97 Right time -> return (Just $ BS.pack $ showTimePrecise time, ([], Just time))
100getFileContentsRule :: Var DirtyFiles -> Rules ()
101getFileContentsRule dirty =
102 define $ \GetFileContents file -> do
103 -- need to depend on modification time to introduce a dependency with Cutoff
104 time <- use_ GetModificationTime file
105 res <- liftIO $ ideTryIOException file $ do
107 case Map.lookup file mp of
108 Just (_, contents) -> return contents
109 Nothing -> hGetStringBuffer file
111 Left err -> return ([err], Nothing)
112 Right contents -> return ([], Just (time, contents))
115getFileContents :: FilePath -> Action (UTCTime, StringBuffer)
116getFileContents = use_ GetFileContents
118getFileExists :: FilePath -> Action Bool
120 -- we deliberately and intentionally wrap the file as an FilePath WITHOUT mkAbsolute
121 -- so that if the file doesn't exist, is on a shared drive that is unmounted etc we get a properly
122 -- cached 'No' rather than an exception in the wrong place
126fileStoreRules :: Rules ()
128 dirty <- liftIO $ newVar Map.empty
129 addIdeGlobal $ GlobalDirtyFiles dirty
130 getModificationTimeRule dirty
131 getFileContentsRule dirty
132 getFileExistsRule dirty
135strictPair :: a -> b -> (a, b)
136strictPair !a !b = (a,b)
139-- | Notify the compiler service of a modified buffer
140setBufferModified :: IdeState -> FilePath -> (Maybe T.Text, UTCTime) -> IO ()
141setBufferModified state absFile (mcontents, !time) = do
142 GlobalDirtyFiles envDirtyFiles <- getIdeGlobalState state
143 -- update vars synchronously
144 modifyVar_ envDirtyFiles $ evaluate . case mcontents of
145 Nothing -> Map.delete absFile
146 Just contents -> Map.insert absFile $ strictPair time (textToStringBuffer contents)
148 -- run shake to update results regarding the files of interest
149 void $ shakeRun state []
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list