Skip to content

Commit fb8fcc6

Browse files
committed
generate linkables only when Evaluating
In addition, we tune the newness check of the redefined NeedsCompilation rule so that the generated linkables are not thrown away unnecessarily, as described in: ndmitchell/shake#794
1 parent 4118111 commit fb8fcc6

File tree

3 files changed

+57
-15
lines changed

3 files changed

+57
-15
lines changed

ghcide/src/Development/IDE/Core/Shake.hs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -871,17 +871,25 @@ usesWithStale key files = do
871871
data RuleBody k v
872872
= Rule (k -> NormalizedFilePath -> Action (Maybe BS.ByteString, IdeResult v))
873873
| RuleNoDiagnostics (k -> NormalizedFilePath -> Action (Maybe BS.ByteString, Maybe v))
874-
874+
| RuleWithCustomNewnessCheck
875+
{ newnessCheck :: BS.ByteString -> BS.ByteString -> Bool
876+
, build :: k -> NormalizedFilePath -> Action (Maybe BS.ByteString, Maybe v)
877+
}
875878

876879
-- | Define a new Rule with early cutoff
877880
defineEarlyCutoff
878881
:: IdeRule k v
879882
=> RuleBody k v
880883
-> Rules ()
881884
defineEarlyCutoff (Rule op) = addRule $ \(Q (key, file)) (old :: Maybe BS.ByteString) mode -> otTracedAction key file mode traceA $ do
882-
defineEarlyCutoff' True key file old mode $ op key file
885+
defineEarlyCutoff' True (==) key file old mode $ op key file
883886
defineEarlyCutoff (RuleNoDiagnostics op) = addRule $ \(Q (key, file)) (old :: Maybe BS.ByteString) mode -> otTracedAction key file mode traceA $ do
884-
defineEarlyCutoff' False key file old mode $ second (mempty,) <$> op key file
887+
defineEarlyCutoff' False (==) key file old mode $ second (mempty,) <$> op key file
888+
defineEarlyCutoff RuleWithCustomNewnessCheck{..} =
889+
addRule $ \(Q (key, file)) (old :: Maybe BS.ByteString) mode ->
890+
otTracedAction key file mode traceA $
891+
defineEarlyCutoff' False newnessCheck key file old mode $
892+
second (mempty,) <$> build key file
885893

886894
defineNoFile :: IdeRule k v => (k -> Action v) -> Rules ()
887895
defineNoFile f = defineNoDiagnostics $ \k file -> do
@@ -896,13 +904,15 @@ defineEarlyCutOffNoFile f = defineEarlyCutoff $ RuleNoDiagnostics $ \k file -> d
896904
defineEarlyCutoff'
897905
:: IdeRule k v
898906
=> Bool -- ^ update diagnostics
907+
-- | compare previous and current for freshness
908+
-> (BS.ByteString -> BS.ByteString -> Bool)
899909
-> k
900910
-> NormalizedFilePath
901911
-> Maybe BS.ByteString
902912
-> RunMode
903913
-> Action (Maybe BS.ByteString, IdeResult v)
904914
-> Action (RunResult (A (RuleResult k)))
905-
defineEarlyCutoff' doDiagnostics key file old mode action = do
915+
defineEarlyCutoff' doDiagnostics cmp key file old mode action = do
906916
extras@ShakeExtras{state, progress, logger, dirtyKeys} <- getShakeExtras
907917
options <- getIdeOptions
908918
(if optSkipProgress options key then id else inProgress progress file) $ do
@@ -947,8 +957,8 @@ defineEarlyCutoff' doDiagnostics key file old mode action = do
947957
then updateFileDiagnostics file (Key key) extras $ map (\(_,y,z) -> (y,z)) diags
948958
else forM_ diags $ \d -> liftIO $ logWarning logger $ showDiagnosticsColored [d]
949959
let eq = case (bs, fmap decodeShakeValue old) of
950-
(ShakeResult a, Just (ShakeResult b)) -> a == b
951-
(ShakeStale a, Just (ShakeStale b)) -> a == b
960+
(ShakeResult a, Just (ShakeResult b)) -> cmp a b
961+
(ShakeStale a, Just (ShakeStale b)) -> cmp a b
952962
-- If we do not have a previous result
953963
-- or we got ShakeNoCutoff we always return False.
954964
_ -> False

plugins/hls-eval-plugin/src/Ide/Plugin/Eval/CodeLens.hs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import Development.IDE (Action, GetDependencies (..),
5151
HiFileResult (hirHomeMod, hirModSummary),
5252
HscEnvEq, IdeState,
5353
ModSummaryResult (..),
54+
NeedsCompilation (NeedsCompilation),
5455
evalGhcEnv,
5556
hscEnvWithImportPaths,
5657
prettyPrint, runAction,
@@ -109,7 +110,10 @@ import UnliftIO.Temporary (withSystemTempFile)
109110
import GHC.Driver.Session (unitDatabases, unitState)
110111
import GHC.Types.SrcLoc (UnhelpfulSpanReason (UnhelpfulInteractive))
111112
#else
113+
import Development.IDE.Core.FileStore (setSomethingModified)
114+
import Development.IDE.Types.Shake (toKey)
112115
import DynFlags
116+
import Ide.Plugin.Eval.Rules (queueForEvaluation)
113117
#endif
114118

115119

@@ -196,6 +200,10 @@ runEvalCmd st EvalParams{..} =
196200
let nfp = toNormalizedFilePath' fp
197201
mdlText <- moduleText _uri
198202

203+
-- enable codegen
204+
liftIO $ queueForEvaluation st nfp
205+
liftIO $ setSomethingModified st [toKey NeedsCompilation nfp] "Eval"
206+
199207
session <- runGetSession st nfp
200208

201209
ms <- fmap msrModSummary $

plugins/hls-eval-plugin/src/Ide/Plugin/Eval/Rules.hs

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,34 @@
33
{-# LANGUAGE PatternSynonyms #-}
44
{-# LANGUAGE RecordWildCards #-}
55

6-
module Ide.Plugin.Eval.Rules (GetEvalComments(..), rules) where
6+
module Ide.Plugin.Eval.Rules (GetEvalComments(..), rules,queueForEvaluation) where
77

8+
import Control.Monad.IO.Class (MonadIO (liftIO))
9+
import Data.HashSet (HashSet)
10+
import qualified Data.HashSet as Set
11+
import Data.IORef
812
import qualified Data.Map.Strict as Map
913
import Data.String (fromString)
1014
import Development.IDE (GetModSummaryWithoutTimestamps (GetModSummaryWithoutTimestamps),
1115
GetParsedModuleWithComments (GetParsedModuleWithComments),
12-
IsFileOfInterest (IsFileOfInterest),
13-
IsFileOfInterestResult (NotFOI),
16+
IdeState,
1417
NeedsCompilation (NeedsCompilation),
18+
NormalizedFilePath,
1519
RuleBody (RuleNoDiagnostics),
1620
Rules, defineEarlyCutoff,
1721
encodeLinkableType,
1822
fromNormalizedFilePath,
1923
msrModSummary,
2024
realSrcSpanToRange,
21-
useWithStale_, use_)
25+
useWithStale_)
2226
import Development.IDE.Core.PositionMapping (toCurrentRange)
2327
import Development.IDE.Core.Rules (computeLinkableType,
2428
needsCompilationRule)
29+
import Development.IDE.Core.Shake (IsIdeGlobal,
30+
RuleBody (RuleWithCustomNewnessCheck),
31+
addIdeGlobal,
32+
getIdeGlobalAction,
33+
getIdeGlobalState)
2534
import Development.IDE.GHC.Compat
2635
import qualified Development.IDE.GHC.Compat as SrcLoc
2736
import qualified Development.IDE.GHC.Compat.Util as FastString
@@ -33,6 +42,15 @@ rules :: Rules ()
3342
rules = do
3443
evalParsedModuleRule
3544
redefinedNeedsCompilation
45+
addIdeGlobal . EvaluatingVar =<< liftIO(newIORef mempty)
46+
47+
newtype EvaluatingVar = EvaluatingVar (IORef (HashSet NormalizedFilePath))
48+
instance IsIdeGlobal EvaluatingVar
49+
50+
queueForEvaluation :: IdeState -> NormalizedFilePath -> IO ()
51+
queueForEvaluation ide nfp = do
52+
EvaluatingVar var <- getIdeGlobalState ide
53+
modifyIORef var (Set.insert nfp)
3654

3755
#if MIN_VERSION_ghc(9,0,0)
3856
pattern RealSrcSpanAlready :: SrcLoc.RealSrcSpan -> SrcLoc.RealSrcSpan
@@ -75,18 +93,24 @@ evalParsedModuleRule = defineEarlyCutoff $ RuleNoDiagnostics $ \GetEvalComments
7593
return (Just fingerPrint, Just comments)
7694

7795
-- Redefine the NeedsCompilation rule to set the linkable type to Just _
78-
-- whenever the module has Eval comments and is of interest.
96+
-- whenever the module is being evaluated
7997
-- This will ensure that the modules are loaded with linkables
8098
-- and the interactive session won't try to compile them on the fly,
8199
-- leading to much better performance of the evaluate code lens
82100
redefinedNeedsCompilation :: Rules ()
83-
redefinedNeedsCompilation = defineEarlyCutoff $ RuleNoDiagnostics $ \NeedsCompilation f -> do
84-
(comments, _) <- useWithStale_ GetEvalComments f
85-
isFOI <- use_ IsFileOfInterest f
86-
if nullComments comments || isFOI == NotFOI then needsCompilationRule f else do
101+
redefinedNeedsCompilation = defineEarlyCutoff $ RuleWithCustomNewnessCheck (<=) $ \NeedsCompilation f -> do
102+
EvaluatingVar var <- getIdeGlobalAction
103+
104+
isEvaluating <- liftIO $ (f `elem`) <$> readIORef var
105+
106+
if not isEvaluating then needsCompilationRule f else do
87107
ms <- msrModSummary . fst <$> useWithStale_ GetModSummaryWithoutTimestamps f
88108
let ms' = ms{ms_hspp_opts = df'}
89109
df' = xopt_set (ms_hspp_opts ms) LangExt.TemplateHaskell
90110
linkableType = computeLinkableType ms' [] []
91111
fp = encodeLinkableType linkableType
112+
113+
-- remove the module from the Evaluating state
114+
liftIO $ modifyIORef var (Set.delete f)
115+
92116
pure (Just fp, Just linkableType)

0 commit comments

Comments
 (0)