mirror of
https://github.com/haskell/ghcide.git
synced 2025-01-06 01:44:13 +03:00
Write ifaces on save (#760)
* Write ifaces on save * Move isFileOfInterestRule to FileStore.hs and use real mtime for saved files * hlint * Add test * fix flaky tests * Only check for hie file in >= 8.6
This commit is contained in:
parent
1ed280be46
commit
15ab2ff3ac
@ -46,7 +46,7 @@ import System.FilePath
|
||||
import System.Time.Extra
|
||||
import Paths_ghcide
|
||||
import Development.GitRev
|
||||
import qualified Data.HashSet as HashSet
|
||||
import qualified Data.HashMap.Strict as HashMap
|
||||
import qualified Data.Aeson as J
|
||||
|
||||
import HIE.Bios.Cradle
|
||||
@ -144,7 +144,7 @@ main = do
|
||||
ide <- initialise def mainRule (pure $ IdInt 0) (showEvent lock) dummyWithProg (const (const id)) (logger logLevel) debouncer (defaultIdeOptions sessionLoader) vfs
|
||||
|
||||
putStrLn "\nStep 4/4: Type checking the files"
|
||||
setFilesOfInterest ide $ HashSet.fromList $ map toNormalizedFilePath' files
|
||||
setFilesOfInterest ide $ HashMap.fromList $ map ((, OnDisk) . toNormalizedFilePath') files
|
||||
results <- runAction "User TypeCheck" ide $ uses TypeCheck (map toNormalizedFilePath' files)
|
||||
let (worked, failed) = partition fst $ zip (map isJust results) files
|
||||
when (failed /= []) $
|
||||
|
@ -14,13 +14,15 @@ module Development.IDE.Core.FileStore(
|
||||
typecheckParents,
|
||||
VFSHandle,
|
||||
makeVFSHandle,
|
||||
makeLSPVFSHandle
|
||||
makeLSPVFSHandle,
|
||||
isFileOfInterestRule
|
||||
) where
|
||||
|
||||
import Development.IDE.GHC.Orphans()
|
||||
import Development.IDE.Core.Shake
|
||||
import Control.Concurrent.Extra
|
||||
import qualified Data.Map.Strict as Map
|
||||
import qualified Data.HashMap.Strict as HM
|
||||
import Data.Maybe
|
||||
import qualified Data.Text as T
|
||||
import Control.Monad.Extra
|
||||
@ -35,8 +37,9 @@ import System.IO.Error
|
||||
import qualified Data.ByteString.Char8 as BS
|
||||
import Development.IDE.Types.Diagnostics
|
||||
import Development.IDE.Types.Location
|
||||
import Development.IDE.Core.OfInterest (kick)
|
||||
import Development.IDE.Core.OfInterest (getFilesOfInterest, kick)
|
||||
import Development.IDE.Core.RuleTypes
|
||||
import Development.IDE.Types.Options
|
||||
import qualified Data.Rope.UTF16 as Rope
|
||||
import Development.IDE.Import.DependencyInformation
|
||||
|
||||
@ -92,6 +95,12 @@ makeLSPVFSHandle lspFuncs = VFSHandle
|
||||
}
|
||||
|
||||
|
||||
isFileOfInterestRule :: Rules ()
|
||||
isFileOfInterestRule = defineEarlyCutoff $ \IsFileOfInterest f -> do
|
||||
filesOfInterest <- getFilesOfInterest
|
||||
let res = maybe NotFOI IsFOI $ f `HM.lookup` filesOfInterest
|
||||
return (Just $ BS.pack $ show $ hash res, ([], Just res))
|
||||
|
||||
-- | Get the contents of a file, either dirty (if the buffer is modified) or Nothing to mean use from disk.
|
||||
type instance RuleResult GetFileContents = (FileVersion, Maybe T.Text)
|
||||
|
||||
@ -119,31 +128,31 @@ getModificationTimeRule vfs =
|
||||
if isDoesNotExistError e && not missingFileDiags
|
||||
then return (Nothing, ([], Nothing))
|
||||
else return (Nothing, ([diag], Nothing))
|
||||
where
|
||||
-- Dir.getModificationTime is surprisingly slow since it performs
|
||||
-- a ton of conversions. Since we do not actually care about
|
||||
-- the format of the time, we can get away with something cheaper.
|
||||
-- For now, we only try to do this on Unix systems where it seems to get the
|
||||
-- time spent checking file modifications (which happens on every change)
|
||||
-- from > 0.5s to ~0.15s.
|
||||
-- We might also want to try speeding this up on Windows at some point.
|
||||
-- TODO leverage DidChangeWatchedFile lsp notifications on clients that
|
||||
-- support them, as done for GetFileExists
|
||||
getModTime :: FilePath -> IO (Int64, Int64)
|
||||
getModTime f =
|
||||
|
||||
-- Dir.getModificationTime is surprisingly slow since it performs
|
||||
-- a ton of conversions. Since we do not actually care about
|
||||
-- the format of the time, we can get away with something cheaper.
|
||||
-- For now, we only try to do this on Unix systems where it seems to get the
|
||||
-- time spent checking file modifications (which happens on every change)
|
||||
-- from > 0.5s to ~0.15s.
|
||||
-- We might also want to try speeding this up on Windows at some point.
|
||||
-- TODO leverage DidChangeWatchedFile lsp notifications on clients that
|
||||
-- support them, as done for GetFileExists
|
||||
getModTime :: FilePath -> IO (Int64, Int64)
|
||||
getModTime f =
|
||||
#ifdef mingw32_HOST_OS
|
||||
do time <- Dir.getModificationTime f
|
||||
let !day = fromInteger $ toModifiedJulianDay $ utctDay time
|
||||
!dayTime = fromInteger $ diffTimeToPicoseconds $ utctDayTime time
|
||||
pure (day, dayTime)
|
||||
do time <- Dir.getModificationTime f
|
||||
let !day = fromInteger $ toModifiedJulianDay $ utctDay time
|
||||
!dayTime = fromInteger $ diffTimeToPicoseconds $ utctDayTime time
|
||||
pure (day, dayTime)
|
||||
#else
|
||||
withCString f $ \f' ->
|
||||
alloca $ \secPtr ->
|
||||
alloca $ \nsecPtr -> do
|
||||
Posix.throwErrnoPathIfMinus1Retry_ "getmodtime" f $ c_getModTime f' secPtr nsecPtr
|
||||
CTime sec <- peek secPtr
|
||||
CLong nsec <- peek nsecPtr
|
||||
pure (sec, nsec)
|
||||
withCString f $ \f' ->
|
||||
alloca $ \secPtr ->
|
||||
alloca $ \nsecPtr -> do
|
||||
Posix.throwErrnoPathIfMinus1Retry_ "getmodtime" f $ c_getModTime f' secPtr nsecPtr
|
||||
CTime sec <- peek secPtr
|
||||
CLong nsec <- peek nsecPtr
|
||||
pure (sec, nsec)
|
||||
|
||||
-- Sadly even unix’s getFileStatus + modificationTimeHiRes is still about twice as slow
|
||||
-- as doing the FFI call ourselves :(.
|
||||
@ -152,11 +161,14 @@ foreign import ccall "getmodtime" c_getModTime :: CString -> Ptr CTime -> Ptr CL
|
||||
|
||||
modificationTime :: FileVersion -> Maybe UTCTime
|
||||
modificationTime VFSVersion{} = Nothing
|
||||
modificationTime (ModificationTime large small) =
|
||||
modificationTime (ModificationTime large small) = Just $ internalTimeToUTCTime large small
|
||||
|
||||
internalTimeToUTCTime :: Int64 -> Int64 -> UTCTime
|
||||
internalTimeToUTCTime large small =
|
||||
#ifdef mingw32_HOST_OS
|
||||
Just (UTCTime (ModifiedJulianDay $ fromIntegral large) (picosecondsToDiffTime $ fromIntegral small))
|
||||
UTCTime (ModifiedJulianDay $ fromIntegral large) (picosecondsToDiffTime $ fromIntegral small)
|
||||
#else
|
||||
Just (systemToUTCTime $ MkSystemTime large (fromIntegral small))
|
||||
systemToUTCTime $ MkSystemTime large (fromIntegral small)
|
||||
#endif
|
||||
|
||||
getFileContentsRule :: VFSHandle -> Rules ()
|
||||
@ -182,7 +194,15 @@ ideTryIOException fp act =
|
||||
getFileContents :: NormalizedFilePath -> Action (UTCTime, Maybe T.Text)
|
||||
getFileContents f = do
|
||||
(fv, txt) <- use_ GetFileContents f
|
||||
modTime <- maybe (liftIO getCurrentTime) return $ modificationTime fv
|
||||
modTime <- case modificationTime fv of
|
||||
Just t -> pure t
|
||||
Nothing -> do
|
||||
foi <- use_ IsFileOfInterest f
|
||||
liftIO $ case foi of
|
||||
IsFOI Modified -> getCurrentTime
|
||||
_ -> do
|
||||
(large,small) <- getModTime $ fromNormalizedFilePath f
|
||||
pure $ internalTimeToUTCTime large small
|
||||
return (modTime, txt)
|
||||
|
||||
fileStoreRules :: VFSHandle -> Rules ()
|
||||
@ -190,7 +210,7 @@ fileStoreRules vfs = do
|
||||
addIdeGlobal vfs
|
||||
getModificationTimeRule vfs
|
||||
getFileContentsRule vfs
|
||||
|
||||
isFileOfInterestRule
|
||||
|
||||
-- | Notify the compiler service that a particular file has been modified.
|
||||
-- Use 'Nothing' to say the file is no longer in the virtual file system
|
||||
@ -205,13 +225,15 @@ setBufferModified state absFile contents = do
|
||||
-- | Note that some buffer for a specific file has been modified but not
|
||||
-- with what changes.
|
||||
setFileModified :: IdeState
|
||||
-> Bool -- ^ True indicates that we should also attempt to recompile
|
||||
-- modules which depended on this file. Currently
|
||||
-- it is true when saving but not on normal
|
||||
-- document modification events
|
||||
-> Bool -- ^ Was the file saved?
|
||||
-> NormalizedFilePath
|
||||
-> IO ()
|
||||
setFileModified state prop nfp = do
|
||||
setFileModified state saved nfp = do
|
||||
ideOptions <- getIdeOptionsIO $ shakeExtras state
|
||||
let checkParents = case optCheckParents ideOptions of
|
||||
AlwaysCheck -> True
|
||||
CheckOnSaveAndClose -> saved
|
||||
_ -> False
|
||||
VFSHandle{..} <- getIdeGlobalState state
|
||||
when (isJust setVirtualFileContents) $
|
||||
fail "setSomethingModified can't be called on this type of VFSHandle"
|
||||
@ -221,7 +243,7 @@ setFileModified state prop nfp = do
|
||||
void $ use GetSpanInfo nfp
|
||||
liftIO $ progressUpdate KickCompleted
|
||||
shakeRestart state [da]
|
||||
when prop $
|
||||
when checkParents $
|
||||
typecheckParents state nfp
|
||||
|
||||
typecheckParents :: IdeState -> NormalizedFilePath -> IO ()
|
||||
|
@ -9,7 +9,7 @@
|
||||
module Development.IDE.Core.OfInterest(
|
||||
ofInterestRules,
|
||||
getFilesOfInterest, setFilesOfInterest, modifyFilesOfInterest,
|
||||
kick
|
||||
kick, FileOfInterestStatus(..)
|
||||
) where
|
||||
|
||||
import Control.Concurrent.Extra
|
||||
@ -20,8 +20,8 @@ import GHC.Generics
|
||||
import Data.Typeable
|
||||
import qualified Data.ByteString.UTF8 as BS
|
||||
import Control.Exception
|
||||
import Data.HashSet (HashSet)
|
||||
import qualified Data.HashSet as HashSet
|
||||
import Data.HashMap.Strict (HashMap)
|
||||
import qualified Data.HashMap.Strict as HashMap
|
||||
import qualified Data.Text as T
|
||||
import Data.Tuple.Extra
|
||||
import Development.Shake
|
||||
@ -34,10 +34,10 @@ import Development.IDE.Core.Shake
|
||||
import Data.Maybe (mapMaybe)
|
||||
import GhcPlugins (HomeModInfo(hm_iface))
|
||||
|
||||
newtype OfInterestVar = OfInterestVar (Var (HashSet NormalizedFilePath))
|
||||
newtype OfInterestVar = OfInterestVar (Var (HashMap NormalizedFilePath FileOfInterestStatus))
|
||||
instance IsIdeGlobal OfInterestVar
|
||||
|
||||
type instance RuleResult GetFilesOfInterest = HashSet NormalizedFilePath
|
||||
type instance RuleResult GetFilesOfInterest = HashMap NormalizedFilePath FileOfInterestStatus
|
||||
|
||||
data GetFilesOfInterest = GetFilesOfInterest
|
||||
deriving (Eq, Show, Typeable, Generic)
|
||||
@ -49,7 +49,7 @@ instance Binary GetFilesOfInterest
|
||||
-- | The rule that initialises the files of interest state.
|
||||
ofInterestRules :: Rules ()
|
||||
ofInterestRules = do
|
||||
addIdeGlobal . OfInterestVar =<< liftIO (newVar HashSet.empty)
|
||||
addIdeGlobal . OfInterestVar =<< liftIO (newVar HashMap.empty)
|
||||
defineEarlyCutoff $ \GetFilesOfInterest _file -> assert (null $ fromNormalizedFilePath _file) $ do
|
||||
alwaysRerun
|
||||
filesOfInterest <- getFilesOfInterestUntracked
|
||||
@ -57,7 +57,7 @@ ofInterestRules = do
|
||||
|
||||
|
||||
-- | Get the files that are open in the IDE.
|
||||
getFilesOfInterest :: Action (HashSet NormalizedFilePath)
|
||||
getFilesOfInterest :: Action (HashMap NormalizedFilePath FileOfInterestStatus)
|
||||
getFilesOfInterest = useNoFile_ GetFilesOfInterest
|
||||
|
||||
|
||||
@ -67,10 +67,10 @@ getFilesOfInterest = useNoFile_ GetFilesOfInterest
|
||||
|
||||
-- | Set the files-of-interest - not usually necessary or advisable.
|
||||
-- The LSP client will keep this information up to date.
|
||||
setFilesOfInterest :: IdeState -> HashSet NormalizedFilePath -> IO ()
|
||||
setFilesOfInterest :: IdeState -> HashMap NormalizedFilePath FileOfInterestStatus -> IO ()
|
||||
setFilesOfInterest state files = modifyFilesOfInterest state (const files)
|
||||
|
||||
getFilesOfInterestUntracked :: Action (HashSet NormalizedFilePath)
|
||||
getFilesOfInterestUntracked :: Action (HashMap NormalizedFilePath FileOfInterestStatus)
|
||||
getFilesOfInterestUntracked = do
|
||||
OfInterestVar var <- getIdeGlobalAction
|
||||
liftIO $ readVar var
|
||||
@ -78,13 +78,13 @@ getFilesOfInterestUntracked = do
|
||||
-- | Modify the files-of-interest - not usually necessary or advisable.
|
||||
-- The LSP client will keep this information up to date.
|
||||
modifyFilesOfInterest
|
||||
:: IdeState
|
||||
-> (HashSet NormalizedFilePath -> HashSet NormalizedFilePath)
|
||||
-> IO ()
|
||||
:: IdeState
|
||||
-> (HashMap NormalizedFilePath FileOfInterestStatus -> HashMap NormalizedFilePath FileOfInterestStatus)
|
||||
-> IO ()
|
||||
modifyFilesOfInterest state f = do
|
||||
OfInterestVar var <- getIdeGlobalState state
|
||||
files <- modifyVar var $ pure . dupe . f
|
||||
logDebug (ideLogger state) $ "Set files of interest to: " <> T.pack (show $ HashSet.toList files)
|
||||
logDebug (ideLogger state) $ "Set files of interest to: " <> T.pack (show $ HashMap.toList files)
|
||||
|
||||
-- | Typecheck all the files of interest.
|
||||
-- Could be improved
|
||||
@ -95,7 +95,7 @@ kick = mkDelayedAction "kick" Debug $ do
|
||||
liftIO $ progressUpdate KickStarted
|
||||
|
||||
-- Update the exports map for the project
|
||||
results <- uses TypeCheck $ HashSet.toList files
|
||||
results <- uses TypeCheck $ HashMap.keys files
|
||||
ShakeExtras{exportsMap} <- getShakeExtras
|
||||
let modIfaces = mapMaybe (fmap (hm_iface . tmrModInfo)) results
|
||||
!exportsMap' = createExportsMap modIfaces
|
||||
|
@ -132,7 +132,19 @@ type instance RuleResult GetModIfaceFromDisk = HiFileResult
|
||||
-- | Get a module interface details, either from an interface file or a typechecked module
|
||||
type instance RuleResult GetModIface = HiFileResult
|
||||
|
||||
type instance RuleResult IsFileOfInterest = Bool
|
||||
data FileOfInterestStatus = OnDisk | Modified
|
||||
deriving (Eq, Show, Typeable, Generic)
|
||||
instance Hashable FileOfInterestStatus
|
||||
instance NFData FileOfInterestStatus
|
||||
instance Binary FileOfInterestStatus
|
||||
|
||||
data IsFileOfInterestResult = NotFOI | IsFOI FileOfInterestStatus
|
||||
deriving (Eq, Show, Typeable, Generic)
|
||||
instance Hashable IsFileOfInterestResult
|
||||
instance NFData IsFileOfInterestResult
|
||||
instance Binary IsFileOfInterestResult
|
||||
|
||||
type instance RuleResult IsFileOfInterest = IsFileOfInterestResult
|
||||
|
||||
-- | Generate a ModSummary that has enough information to be used to get .hi and .hie files.
|
||||
-- without needing to parse the entire source
|
||||
|
@ -190,7 +190,7 @@ getHomeHieFile f = do
|
||||
hsc <- hscEnv <$> use_ GhcSession f
|
||||
pm <- use_ GetParsedModule f
|
||||
source <- getSourceFileSource f
|
||||
typeCheckRuleDefinition hsc pm DoGenerateInterfaceFiles (Just source)
|
||||
typeCheckRuleDefinition hsc pm NotFOI (Just source)
|
||||
_ <- MaybeT $ liftIO $ timeout 1 wait
|
||||
ncu <- mkUpdater
|
||||
liftIO $ loadHieFile ncu hie_f
|
||||
@ -527,7 +527,9 @@ typeCheckRule = define $ \TypeCheck file -> do
|
||||
hsc <- hscEnv <$> use_ GhcSessionDeps file
|
||||
-- do not generate interface files as this rule is called
|
||||
-- for files of interest on every keystroke
|
||||
typeCheckRuleDefinition hsc pm SkipGenerationOfInterfaceFiles Nothing
|
||||
source <- getSourceFileSource file
|
||||
isFoi <- use_ IsFileOfInterest file
|
||||
typeCheckRuleDefinition hsc pm isFoi (Just source)
|
||||
|
||||
knownFilesRule :: Rules ()
|
||||
knownFilesRule = defineEarlyCutOffNoFile $ \GetKnownFiles -> do
|
||||
@ -541,11 +543,6 @@ getModuleGraphRule = defineNoFile $ \GetModuleGraph -> do
|
||||
rawDepInfo <- rawDependencyInformation (HashSet.toList fs)
|
||||
pure $ processDependencyInformation rawDepInfo
|
||||
|
||||
data GenerateInterfaceFiles
|
||||
= DoGenerateInterfaceFiles
|
||||
| SkipGenerationOfInterfaceFiles
|
||||
deriving (Show)
|
||||
|
||||
-- This is factored out so it can be directly called from the GetModIface
|
||||
-- rule. Directly calling this rule means that on the initial load we can
|
||||
-- garbage collect all the intermediate typechecked modules rather than
|
||||
@ -553,25 +550,28 @@ data GenerateInterfaceFiles
|
||||
typeCheckRuleDefinition
|
||||
:: HscEnv
|
||||
-> ParsedModule
|
||||
-> GenerateInterfaceFiles -- ^ Should generate .hi and .hie files ?
|
||||
-> IsFileOfInterestResult -- ^ Should generate .hi and .hie files ?
|
||||
-> Maybe BS.ByteString
|
||||
-> Action (IdeResult TcModuleResult)
|
||||
typeCheckRuleDefinition hsc pm generateArtifacts source = do
|
||||
typeCheckRuleDefinition hsc pm isFoi source = do
|
||||
setPriority priorityTypeCheck
|
||||
IdeOptions { optDefer = defer } <- getIdeOptions
|
||||
|
||||
addUsageDependencies $ liftIO $ do
|
||||
res <- typecheckModule defer hsc pm
|
||||
case res of
|
||||
(diags, Just (hsc,tcm))
|
||||
| DoGenerateInterfaceFiles <- generateArtifacts
|
||||
-- Don't save interface files for modules that compiled due to defering
|
||||
-- type errors, as we won't get proper diagnostics if we load these from
|
||||
-- disk
|
||||
, not $ tmrDeferedError tcm -> do
|
||||
diagsHie <- generateAndWriteHieFile hsc (tmrModule tcm) (fromMaybe "" source)
|
||||
diagsHi <- writeHiFile hsc tcm
|
||||
return (diags <> diagsHi <> diagsHie, Just tcm)
|
||||
(diags, Just (hsc,tcm)) -> do
|
||||
case isFoi of
|
||||
IsFOI Modified -> return (diags, Just tcm)
|
||||
_ -> do -- If the file is saved on disk, or is not a FOI, we write out ifaces
|
||||
diagsHie <- generateAndWriteHieFile hsc (tmrModule tcm) (fromMaybe "" source)
|
||||
-- Don't save interface files for modules that compiled due to defering
|
||||
-- type errors, as we won't get proper diagnostics if we load these from
|
||||
-- disk
|
||||
diagsHi <- if not $ tmrDeferedError tcm
|
||||
then writeHiFile hsc tcm
|
||||
else pure mempty
|
||||
return (diags <> diagsHi <> diagsHie, Just tcm)
|
||||
(diags, res) ->
|
||||
return (diags, snd <$> res)
|
||||
where
|
||||
@ -771,18 +771,18 @@ getModSummaryRule = do
|
||||
getModIfaceRule :: Rules ()
|
||||
getModIfaceRule = defineEarlyCutoff $ \GetModIface f -> do
|
||||
#if MIN_GHC_API_VERSION(8,6,0) && !defined(GHC_LIB)
|
||||
fileOfInterest <- use_ IsFileOfInterest f
|
||||
if fileOfInterest
|
||||
then do
|
||||
-- Never load from disk for files of interest
|
||||
tmr <- use TypeCheck f
|
||||
let !hiFile = extractHiFileResult tmr
|
||||
let fp = hiFileFingerPrint <$> hiFile
|
||||
return (fp, ([], hiFile))
|
||||
else do
|
||||
hiFile <- use GetModIfaceFromDisk f
|
||||
let fp = hiFileFingerPrint <$> hiFile
|
||||
return (fp, ([], hiFile))
|
||||
fileOfInterest <- use_ IsFileOfInterest f
|
||||
case fileOfInterest of
|
||||
IsFOI _ -> do
|
||||
-- Never load from disk for files of interest
|
||||
tmr <- use TypeCheck f
|
||||
let !hiFile = extractHiFileResult tmr
|
||||
let fp = hiFileFingerPrint <$> hiFile
|
||||
return (fp, ([], hiFile))
|
||||
NotFOI -> do
|
||||
hiFile <- use GetModIfaceFromDisk f
|
||||
let fp = hiFileFingerPrint <$> hiFile
|
||||
return (fp, ([], hiFile))
|
||||
#else
|
||||
tm <- use TypeCheck f
|
||||
let !hiFile = extractHiFileResult tm
|
||||
@ -813,7 +813,7 @@ regenerateHiFile sess f = do
|
||||
source <- getSourceFileSource f
|
||||
-- Invoke typechecking directly to update it without incurring a dependency
|
||||
-- on the parsed module and the typecheck rules
|
||||
(diags', tmr) <- typeCheckRuleDefinition hsc pm DoGenerateInterfaceFiles (Just source)
|
||||
(diags', tmr) <- typeCheckRuleDefinition hsc pm NotFOI (Just source)
|
||||
-- Bang pattern is important to avoid leaking 'tmr'
|
||||
let !res = extractHiFileResult tmr
|
||||
return (diags <> diags', res)
|
||||
@ -824,12 +824,6 @@ extractHiFileResult (Just tmr) =
|
||||
-- Bang patterns are important to force the inner fields
|
||||
Just $! tmr_hiFileResult tmr
|
||||
|
||||
isFileOfInterestRule :: Rules ()
|
||||
isFileOfInterestRule = defineEarlyCutoff $ \IsFileOfInterest f -> do
|
||||
filesOfInterest <- getFilesOfInterest
|
||||
let res = f `elem` filesOfInterest
|
||||
return (Just (if res then "1" else ""), ([], Just res))
|
||||
|
||||
-- | A rule that wires per-file rules together
|
||||
mainRule :: Rules ()
|
||||
mainRule = do
|
||||
@ -845,7 +839,6 @@ mainRule = do
|
||||
loadGhcSession
|
||||
getModIfaceFromDiskRule
|
||||
getModIfaceRule
|
||||
isFileOfInterestRule
|
||||
getModSummaryRule
|
||||
isHiFileStableRule
|
||||
getModuleGraphRule
|
||||
|
@ -23,6 +23,7 @@ import Development.IDE.Types.Options
|
||||
import Control.Monad.Extra
|
||||
import Data.Foldable as F
|
||||
import Data.Maybe
|
||||
import qualified Data.HashMap.Strict as M
|
||||
import qualified Data.HashSet as S
|
||||
import qualified Data.Text as Text
|
||||
|
||||
@ -39,32 +40,30 @@ setHandlersNotifications = PartialHandlers $ \WithMessage{..} x -> return x
|
||||
{LSP.didOpenTextDocumentNotificationHandler = withNotification (LSP.didOpenTextDocumentNotificationHandler x) $
|
||||
\_ ide (DidOpenTextDocumentParams TextDocumentItem{_uri,_version}) -> do
|
||||
updatePositionMapping ide (VersionedTextDocumentIdentifier _uri (Just _version)) (List [])
|
||||
IdeOptions{optCheckParents} <- getIdeOptionsIO $ shakeExtras ide
|
||||
whenUriFile _uri $ \file -> do
|
||||
modifyFilesOfInterest ide (S.insert file)
|
||||
let checkParents = optCheckParents == AlwaysCheck
|
||||
setFileModified ide checkParents file
|
||||
modifyFilesOfInterest ide (M.insert file OnDisk)
|
||||
setFileModified ide False file
|
||||
logInfo (ideLogger ide) $ "Opened text document: " <> getUri _uri
|
||||
|
||||
,LSP.didChangeTextDocumentNotificationHandler = withNotification (LSP.didChangeTextDocumentNotificationHandler x) $
|
||||
\_ ide (DidChangeTextDocumentParams identifier@VersionedTextDocumentIdentifier{_uri} changes) -> do
|
||||
updatePositionMapping ide identifier changes
|
||||
IdeOptions{optCheckParents} <- getIdeOptionsIO $ shakeExtras ide
|
||||
let checkParents = optCheckParents == AlwaysCheck
|
||||
whenUriFile _uri $ \file -> setFileModified ide checkParents file
|
||||
whenUriFile _uri $ \file -> do
|
||||
modifyFilesOfInterest ide (M.insert file Modified)
|
||||
setFileModified ide False file
|
||||
logInfo (ideLogger ide) $ "Modified text document: " <> getUri _uri
|
||||
|
||||
,LSP.didSaveTextDocumentNotificationHandler = withNotification (LSP.didSaveTextDocumentNotificationHandler x) $
|
||||
\_ ide (DidSaveTextDocumentParams TextDocumentIdentifier{_uri}) -> do
|
||||
IdeOptions{optCheckParents} <- getIdeOptionsIO $ shakeExtras ide
|
||||
let checkParents = optCheckParents >= CheckOnSaveAndClose
|
||||
whenUriFile _uri $ \file -> setFileModified ide checkParents file
|
||||
whenUriFile _uri $ \file -> do
|
||||
modifyFilesOfInterest ide (M.insert file OnDisk)
|
||||
setFileModified ide True file
|
||||
logInfo (ideLogger ide) $ "Saved text document: " <> getUri _uri
|
||||
|
||||
,LSP.didCloseTextDocumentNotificationHandler = withNotification (LSP.didCloseTextDocumentNotificationHandler x) $
|
||||
\_ ide (DidCloseTextDocumentParams TextDocumentIdentifier{_uri}) -> do
|
||||
whenUriFile _uri $ \file -> do
|
||||
modifyFilesOfInterest ide (S.delete file)
|
||||
modifyFilesOfInterest ide (M.delete file)
|
||||
-- Refresh all the files that depended on this
|
||||
IdeOptions{optCheckParents} <- getIdeOptionsIO $ shakeExtras ide
|
||||
when (optCheckParents >= CheckOnClose) $ typecheckParents ide file
|
||||
|
@ -56,7 +56,7 @@ import Test.Tasty.HUnit
|
||||
import Test.Tasty.QuickCheck
|
||||
import System.Time.Extra
|
||||
import Development.IDE.Plugin.CodeAction (typeSignatureCommandId, blockCommandId)
|
||||
import Development.IDE.Plugin.Test (TestRequest(BlockSeconds))
|
||||
import Development.IDE.Plugin.Test (TestRequest(BlockSeconds,GetInterfaceFilesDir))
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
@ -2992,6 +2992,21 @@ ifaceErrorTest = testCase "iface-error-test-1" $ withoutStackEnv $ runWithExtraF
|
||||
-- save so that we can that the error propogates to A
|
||||
sendNotification TextDocumentDidSave (DidSaveTextDocumentParams bdoc)
|
||||
|
||||
-- Check that we wrote the interfaces for B when we saved
|
||||
lid <- sendRequest (CustomClientMethod "hidir") $ GetInterfaceFilesDir bPath
|
||||
res <- skipManyTill (message :: Session WorkDoneProgressCreateRequest) $
|
||||
skipManyTill (message :: Session WorkDoneProgressBeginNotification) $
|
||||
responseForId lid
|
||||
liftIO $ case res of
|
||||
ResponseMessage{_result=Right hidir} -> do
|
||||
hi_exists <- doesFileExist $ hidir </> "B.hi"
|
||||
assertBool ("Couldn't find B.hi in " ++ hidir) hi_exists
|
||||
#if MIN_GHC_API_VERSION(8,6,0)
|
||||
hie_exists <- doesFileExist $ hidir </> "B.hie"
|
||||
assertBool ("Couldn't find B.hie in " ++ hidir) hie_exists
|
||||
#endif
|
||||
_ -> assertFailure $ "Got malformed response for CustomMessage hidir: " ++ show res
|
||||
|
||||
-- Check that the error propogates to A
|
||||
expectDiagnostics
|
||||
[("A.hs", [(DsError, (5, 4), "Couldn't match expected type 'Int' with actual type 'Bool'")])]
|
||||
|
Loading…
Reference in New Issue
Block a user