Skip to content

Commit 050586d

Browse files
committed
WIP Add input rule constraints
1 parent 9f4d673 commit 050586d

File tree

5 files changed

+86
-53
lines changed

5 files changed

+86
-53
lines changed

ghcide/ghcide.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ library
135135
Development.IDE.Core.FileStore
136136
Development.IDE.Core.FileUtils
137137
Development.IDE.Core.IdeConfiguration
138+
Development.IDE.Core.InputPath
138139
Development.IDE.Core.OfInterest
139140
Development.IDE.Core.PluginUtils
140141
Development.IDE.Core.PositionMapping
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module Development.IDE.Core.InputPath where
2+
3+
import Development.IDE.Graph.Internal.RuleInput (Input)
4+
import Development.IDE (NormalizedFilePath)
5+
6+
newtype InputPath (i :: Input) =
7+
InputPath { unInputPath :: NormalizedFilePath }

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

Lines changed: 60 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
-- Copyright (c) 2019 The DAML Authors. All rights reserved.
22
-- SPDX-License-Identifier: Apache-2.0
33

4+
{-# LANGUAGE AllowAmbiguousTypes #-}
45
{-# LANGUAGE CPP #-}
56
{-# LANGUAGE DerivingStrategies #-}
67
{-# LANGUAGE DuplicateRecordFields #-}
@@ -121,6 +122,7 @@ import Data.Vector (Vector)
121122
import qualified Data.Vector as Vector
122123
import Development.IDE.Core.Debouncer
123124
import Development.IDE.Core.FileUtils (getModTime)
125+
import Development.IDE.Core.InputPath (InputPath (unInputPath, InputPath))
124126
import Development.IDE.Core.PositionMapping
125127
import Development.IDE.Core.ProgressReporting
126128
import Development.IDE.Core.RuleTypes
@@ -179,6 +181,7 @@ import System.FilePath hiding (makeRelative)
179181
import System.IO.Unsafe (unsafePerformIO)
180182
import System.Time.Extra
181183
import UnliftIO (MonadUnliftIO (withRunInIO))
184+
import Development.IDE.Graph.Internal.RuleInput (RuleInput, HasInput)
182185

183186

184187
data Log
@@ -342,7 +345,7 @@ type WithProgressFunc = forall a.
342345
type WithIndefiniteProgressFunc = forall a.
343346
T.Text -> LSP.ProgressCancellable -> IO a -> IO a
344347

345-
type GetStalePersistent = NormalizedFilePath -> IdeAction (Maybe (Dynamic,PositionDelta,Maybe Int32))
348+
type GetStalePersistent = InputPath i -> IdeAction (Maybe (Dynamic,PositionDelta,Maybe Int32))
346349

347350
getShakeExtras :: Action ShakeExtras
348351
getShakeExtras = do
@@ -384,7 +387,7 @@ getPluginConfigAction plId = do
384387
-- This is called when we don't already have a result, or computing the rule failed.
385388
-- The result of this function will always be marked as 'stale', and a 'proper' rebuild of the rule will
386389
-- be queued if the rule hasn't run before.
387-
addPersistentRule :: IdeRule k v => k -> (NormalizedFilePath -> IdeAction (Maybe (v,PositionDelta,Maybe Int32))) -> Rules ()
390+
addPersistentRule :: IdeRule k i is v => k -> (InputPath i -> IdeAction (Maybe (v,PositionDelta,Maybe Int32))) -> Rules ()
388391
addPersistentRule k getVal = do
389392
ShakeExtras{persistentKeys} <- getShakeExtrasRules
390393
void $ liftIO $ atomically $ modifyTVar' persistentKeys $ insertKeyMap (newKey k) (fmap (fmap (first3 toDyn)) . getVal)
@@ -452,7 +455,7 @@ getIdeOptionsIO ide = do
452455

453456
-- | Return the most recent, potentially stale, value and a PositionMapping
454457
-- for the version of that value.
455-
lastValueIO :: IdeRule k v => ShakeExtras -> k -> NormalizedFilePath -> IO (Maybe (v, PositionMapping))
458+
lastValueIO :: IdeRule k i is v => ShakeExtras -> k -> InputPath i -> IO (Maybe (v, PositionMapping))
456459
lastValueIO s@ShakeExtras{positionMapping,persistentKeys,state} k file = do
457460

458461
let readPersistent
@@ -498,7 +501,7 @@ lastValueIO s@ShakeExtras{positionMapping,persistentKeys,state} k file = do
498501

499502
-- | Return the most recent, potentially stale, value and a PositionMapping
500503
-- for the version of that value.
501-
lastValue :: IdeRule k v => k -> NormalizedFilePath -> Action (Maybe (v, PositionMapping))
504+
lastValue :: IdeRule k i is v => k -> InputPath i -> Action (Maybe (v, PositionMapping))
502505
lastValue key file = do
503506
s <- getShakeExtras
504507
liftIO $ lastValueIO s key file
@@ -513,9 +516,11 @@ mappingForVersion allMappings file (Just (VFSVersion ver)) = do
513516
return $ maybe zeroMapping snd $ EM.lookup ver =<< mapping
514517
mappingForVersion _ _ _ = pure zeroMapping
515518

516-
type IdeRule k v =
519+
type IdeRule k i is v =
517520
( Shake.RuleResult k ~ v
518521
, Shake.ShakeValue k
522+
, RuleInput k ~ is
523+
, HasInput i is
519524
, Show v
520525
, Typeable v
521526
, NFData v
@@ -581,10 +586,10 @@ shakeDatabaseProfileIO mbProfileDir = do
581586
shakeProfileDatabase shakeDb $ dir </> file
582587
return (dir </> file)
583588

584-
setValues :: IdeRule k v
589+
setValues :: IdeRule k i is v
585590
=> Values
586591
-> k
587-
-> NormalizedFilePath
592+
-> InputPath i
588593
-> Value v
589594
-> Vector FileDiagnostic
590595
-> STM ()
@@ -607,11 +612,11 @@ deleteValue ShakeExtras{state} key file = do
607612

608613
-- | We return Nothing if the rule has not run and Just Failed if it has failed to produce a value.
609614
getValues ::
610-
forall k v.
611-
IdeRule k v =>
615+
forall k i is v.
616+
IdeRule k i is v =>
612617
Values ->
613618
k ->
614-
NormalizedFilePath ->
619+
InputPath i ->
615620
STM (Maybe (Value v, Vector FileDiagnostic))
616621
getValues state key file = do
617622
STM.lookup (toKey key file) state >>= \case
@@ -1010,23 +1015,23 @@ preservedKeys checkParents = HSet.fromList $
10101015

10111016
-- | Define a new Rule without early cutoff
10121017
define
1013-
:: IdeRule k v
1014-
=> Recorder (WithPriority Log) -> (k -> NormalizedFilePath -> Action (IdeResult v)) -> Rules ()
1018+
:: IdeRule k i is v
1019+
=> Recorder (WithPriority Log) -> (k -> InputPath i -> Action (IdeResult v)) -> Rules ()
10151020
define recorder op = defineEarlyCutoff recorder $ Rule $ \k v -> (Nothing,) <$> op k v
10161021

10171022
defineNoDiagnostics
1018-
:: IdeRule k v
1019-
=> Recorder (WithPriority Log) -> (k -> NormalizedFilePath -> Action (Maybe v)) -> Rules ()
1023+
:: IdeRule k i is v
1024+
=> Recorder (WithPriority Log) -> (k -> InputPath i -> Action (Maybe v)) -> Rules ()
10201025
defineNoDiagnostics recorder op = defineEarlyCutoff recorder $ RuleNoDiagnostics $ \k v -> (Nothing,) <$> op k v
10211026

10221027
-- | Request a Rule result if available
1023-
use :: IdeRule k v
1024-
=> k -> NormalizedFilePath -> Action (Maybe v)
1028+
use :: IdeRule k i is v
1029+
=> k -> InputPath i -> Action (Maybe v)
10251030
use key file = runIdentity <$> uses key (Identity file)
10261031

10271032
-- | Request a Rule result, it not available return the last computed result, if any, which may be stale
1028-
useWithStale :: IdeRule k v
1029-
=> k -> NormalizedFilePath -> Action (Maybe (v, PositionMapping))
1033+
useWithStale :: IdeRule k i is v
1034+
=> k -> InputPath i -> Action (Maybe (v, PositionMapping))
10301035
useWithStale key file = runIdentity <$> usesWithStale key (Identity file)
10311036

10321037
-- |Request a Rule result, it not available return the last computed result
@@ -1036,8 +1041,8 @@ useWithStale key file = runIdentity <$> usesWithStale key (Identity file)
10361041
-- none available.
10371042
--
10381043
-- WARNING: Not suitable for PluginHandlers. Use `useWithStaleE` instead.
1039-
useWithStale_ :: IdeRule k v
1040-
=> k -> NormalizedFilePath -> Action (v, PositionMapping)
1044+
useWithStale_ :: IdeRule k i is v
1045+
=> k -> InputPath i -> Action (v, PositionMapping)
10411046
useWithStale_ key file = runIdentity <$> usesWithStale_ key (Identity file)
10421047

10431048
-- |Plural version of 'useWithStale_'
@@ -1046,7 +1051,7 @@ useWithStale_ key file = runIdentity <$> usesWithStale_ key (Identity file)
10461051
-- none available.
10471052
--
10481053
-- WARNING: Not suitable for PluginHandlers.
1049-
usesWithStale_ :: (Traversable f, IdeRule k v) => k -> f NormalizedFilePath -> Action (f (v, PositionMapping))
1054+
usesWithStale_ :: (Traversable f, IdeRule k i is v) => k -> f (InputPath i) -> Action (f (v, PositionMapping))
10501055
usesWithStale_ key files = do
10511056
res <- usesWithStale key files
10521057
case sequence res of
@@ -1077,11 +1082,11 @@ data FastResult a = FastResult { stale :: Maybe (a,PositionMapping), uptoDate ::
10771082
-- | Lookup value in the database and return with the stale value immediately
10781083
-- Will queue an action to refresh the value.
10791084
-- Might block the first time the rule runs, but never blocks after that.
1080-
useWithStaleFast :: IdeRule k v => k -> NormalizedFilePath -> IdeAction (Maybe (v, PositionMapping))
1085+
useWithStaleFast :: IdeRule k i is v => k -> InputPath i -> IdeAction (Maybe (v, PositionMapping))
10811086
useWithStaleFast key file = stale <$> useWithStaleFast' key file
10821087

10831088
-- | Same as useWithStaleFast but lets you wait for an up to date result
1084-
useWithStaleFast' :: IdeRule k v => k -> NormalizedFilePath -> IdeAction (FastResult v)
1089+
useWithStaleFast' :: IdeRule k i is v => k -> InputPath i -> IdeAction (FastResult v)
10851090
useWithStaleFast' key file = do
10861091
-- This lookup directly looks up the key in the shake database and
10871092
-- returns the last value that was computed for this key without
@@ -1108,7 +1113,7 @@ useWithStaleFast' key file = do
11081113
res <- lastValueIO s key file
11091114
pure $ FastResult res waitValue
11101115

1111-
useNoFile :: IdeRule k v => k -> Action (Maybe v)
1116+
useNoFile :: IdeRule k i is v => k -> Action (Maybe v)
11121117
useNoFile key = use key emptyFilePath
11131118

11141119
-- Requests a rule if available.
@@ -1117,10 +1122,10 @@ useNoFile key = use key emptyFilePath
11171122
-- none available.
11181123
--
11191124
-- WARNING: Not suitable for PluginHandlers. Use `useE` instead.
1120-
use_ :: IdeRule k v => k -> NormalizedFilePath -> Action v
1125+
use_ :: IdeRule k i is v => k -> InputPath i -> Action v
11211126
use_ key file = runIdentity <$> uses_ key (Identity file)
11221127

1123-
useNoFile_ :: IdeRule k v => k -> Action v
1128+
useNoFile_ :: IdeRule k i is v => k -> Action v
11241129
useNoFile_ key = use_ key emptyFilePath
11251130

11261131
-- |Plural version of `use_`
@@ -1129,47 +1134,47 @@ useNoFile_ key = use_ key emptyFilePath
11291134
-- none available.
11301135
--
11311136
-- WARNING: Not suitable for PluginHandlers. Use `usesE` instead.
1132-
uses_ :: (Traversable f, IdeRule k v) => k -> f NormalizedFilePath -> Action (f v)
1137+
uses_ :: (Traversable f, IdeRule k i is v) => k -> f (InputPath i) -> Action (f v)
11331138
uses_ key files = do
11341139
res <- uses key files
11351140
case sequence res of
11361141
Nothing -> liftIO $ throwIO $ BadDependency (show key)
11371142
Just v -> return v
11381143

11391144
-- | Plural version of 'use'
1140-
uses :: (Traversable f, IdeRule k v)
1141-
=> k -> f NormalizedFilePath -> Action (f (Maybe v))
1145+
uses :: (Traversable f, IdeRule k i is v)
1146+
=> k -> f (InputPath i) -> Action (f (Maybe v))
11421147
uses key files = fmap (\(A value) -> currentValue value) <$> apply (fmap (Q . (key,)) files)
11431148

11441149
-- | Return the last computed result which might be stale.
1145-
usesWithStale :: (Traversable f, IdeRule k v)
1146-
=> k -> f NormalizedFilePath -> Action (f (Maybe (v, PositionMapping)))
1150+
usesWithStale :: (Traversable f, IdeRule k i is v)
1151+
=> k -> f (InputPath i) -> Action (f (Maybe (v, PositionMapping)))
11471152
usesWithStale key files = do
11481153
_ <- apply (fmap (Q . (key,)) files)
11491154
-- We don't look at the result of the 'apply' since 'lastValue' will
11501155
-- return the most recent successfully computed value regardless of
11511156
-- whether the rule succeeded or not.
11521157
traverse (lastValue key) files
11531158

1154-
useWithoutDependency :: IdeRule k v
1155-
=> k -> NormalizedFilePath -> Action (Maybe v)
1159+
useWithoutDependency :: IdeRule k i is v
1160+
=> k -> InputPath i -> Action (Maybe v)
11561161
useWithoutDependency key file =
11571162
(\(Identity (A value)) -> currentValue value) <$> applyWithoutDependency (Identity (Q (key, file)))
11581163

1159-
data RuleBody k v
1160-
= Rule (k -> NormalizedFilePath -> Action (Maybe BS.ByteString, IdeResult v))
1161-
| RuleNoDiagnostics (k -> NormalizedFilePath -> Action (Maybe BS.ByteString, Maybe v))
1164+
data RuleBody k i v
1165+
= Rule (k -> InputPath i -> Action (Maybe BS.ByteString, IdeResult v))
1166+
| RuleNoDiagnostics (k -> InputPath i -> Action (Maybe BS.ByteString, Maybe v))
11621167
| RuleWithCustomNewnessCheck
11631168
{ newnessCheck :: BS.ByteString -> BS.ByteString -> Bool
1164-
, build :: k -> NormalizedFilePath -> Action (Maybe BS.ByteString, Maybe v)
1169+
, build :: k -> InputPath i -> Action (Maybe BS.ByteString, Maybe v)
11651170
}
1166-
| RuleWithOldValue (k -> NormalizedFilePath -> Value v -> Action (Maybe BS.ByteString, IdeResult v))
1171+
| RuleWithOldValue (k -> InputPath i -> Value v -> Action (Maybe BS.ByteString, IdeResult v))
11671172

11681173
-- | Define a new Rule with early cutoff
11691174
defineEarlyCutoff
1170-
:: IdeRule k v
1175+
:: IdeRule k i is v
11711176
=> Recorder (WithPriority Log)
1172-
-> RuleBody k v
1177+
-> RuleBody k i v
11731178
-> Rules ()
11741179
defineEarlyCutoff recorder (Rule op) = addRule $ \(Q (key, file)) (old :: Maybe BS.ByteString) mode -> otTracedAction key file mode traceA $ \traceDiagnostics -> do
11751180
extras <- getShakeExtras
@@ -1197,32 +1202,33 @@ defineEarlyCutoff recorder (RuleWithOldValue op) = addRule $ \(Q (key, file)) (o
11971202
updateFileDiagnostics recorder file ver (newKey key) extras . map (\(_,y,z) -> (y,z)) $ diags
11981203
defineEarlyCutoff' diagnostics (==) key file old mode $ op key file
11991204

1200-
defineNoFile :: IdeRule k v => Recorder (WithPriority Log) -> (k -> Action v) -> Rules ()
1205+
defineNoFile :: IdeRule k i is v => Recorder (WithPriority Log) -> (k -> Action v) -> Rules ()
12011206
defineNoFile recorder f = defineNoDiagnostics recorder $ \k file -> do
12021207
if file == emptyFilePath then do res <- f k; return (Just res) else
12031208
fail $ "Rule " ++ show k ++ " should always be called with the empty string for a file"
12041209

1205-
defineEarlyCutOffNoFile :: IdeRule k v => Recorder (WithPriority Log) -> (k -> Action (BS.ByteString, v)) -> Rules ()
1206-
defineEarlyCutOffNoFile recorder f = defineEarlyCutoff recorder $ RuleNoDiagnostics $ \k file -> do
1210+
defineEarlyCutOffNoFile :: IdeRule k i is v => Recorder (WithPriority Log) -> (k -> Action (BS.ByteString, v)) -> Rules ()
1211+
defineEarlyCutOffNoFile recorder f = defineEarlyCutoff recorder $ RuleNoDiagnostics $ \k (InputPath file) -> do
12071212
if file == emptyFilePath then do (hashString, res) <- f k; return (Just hashString, Just res) else
12081213
fail $ "Rule " ++ show k ++ " should always be called with the empty string for a file"
12091214

12101215
defineEarlyCutoff'
1211-
:: forall k v. IdeRule k v
1216+
:: forall k i is v. IdeRule k i is v
12121217
=> (Maybe Int32 -> [FileDiagnostic] -> Action ()) -- ^ update diagnostics
12131218
-- | compare current and previous for freshness
12141219
-> (BS.ByteString -> BS.ByteString -> Bool)
12151220
-> k
1216-
-> NormalizedFilePath
1221+
-> InputPath i
12171222
-> Maybe BS.ByteString
12181223
-> RunMode
12191224
-> (Value v -> Action (Maybe BS.ByteString, IdeResult v))
12201225
-> Action (RunResult (A (RuleResult k)))
12211226
defineEarlyCutoff' doDiagnostics cmp key file mbOld mode action = do
1227+
let rawFile = unInputPath file
12221228
ShakeExtras{state, progress, dirtyKeys} <- getShakeExtras
12231229
options <- getIdeOptions
12241230
let trans g x = withRunInIO $ \run -> g (run x)
1225-
(if optSkipProgress options key then id else trans (inProgress progress file)) $ do
1231+
(if optSkipProgress options key then id else trans (inProgress progress rawFile)) $ do
12261232
val <- case mbOld of
12271233
Just old | mode == RunDependenciesSame -> do
12281234
mbValue <- liftIO $ atomicallyNamed "define - read 1" $ getValues state key file
@@ -1249,7 +1255,7 @@ defineEarlyCutoff' doDiagnostics cmp key file mbOld mode action = do
12491255
(mbBs, (diags, mbRes)) <- actionCatch
12501256
(do v <- action staleV; liftIO $ evaluate $ force v) $
12511257
\(e :: SomeException) -> do
1252-
pure (Nothing, ([ideErrorText file $ T.pack $ show e | not $ isBadDependency e],Nothing))
1258+
pure (Nothing, ([ideErrorText rawFile $ T.pack $ show e | not $ isBadDependency e],Nothing))
12531259

12541260
ver <- estimateFileVersionUnsafely key mbRes file
12551261
(bs, res) <- case mbRes of
@@ -1270,7 +1276,7 @@ defineEarlyCutoff' doDiagnostics cmp key file mbOld mode action = do
12701276
-- this hook needs to be run in the same transaction as the key is marked clean
12711277
-- see Note [Housekeeping rule cache and dirty key outside of hls-graph]
12721278
setValues state key file res (Vector.fromList diags)
1273-
modifyTVar' dirtyKeys (deleteKeySet $ toKey key file)
1279+
modifyTVar' dirtyKeys (deleteKeySet $ toKey key rawFile)
12741280
return res
12751281
where
12761282
-- Highly unsafe helper to compute the version of a file
@@ -1279,10 +1285,10 @@ defineEarlyCutoff' doDiagnostics cmp key file mbOld mode action = do
12791285
estimateFileVersionUnsafely
12801286
:: k
12811287
-> Maybe v
1282-
-> NormalizedFilePath
1288+
-> InputPath i
12831289
-> Action (Maybe FileVersion)
12841290
estimateFileVersionUnsafely _k v fp
1285-
| fp == emptyFilePath = pure Nothing
1291+
| unInputPath fp == emptyFilePath = pure Nothing
12861292
| Just Refl <- eqT @k @GetModificationTime = pure v
12871293
-- GetModificationTime depends on these rules, so avoid creating a cycle
12881294
| Just Refl <- eqT @k @AddWatchedFile = pure Nothing
@@ -1457,9 +1463,10 @@ kickSignal testing lspEnv files msg = when testing $ liftIO $ mRunLspT lspEnv $
14571463
toJSON $ map fromNormalizedFilePath files
14581464

14591465
-- | Add kick start/done signal to rule
1460-
runWithSignal :: (KnownSymbol s0, KnownSymbol s1, IdeRule k v) => Proxy s0 -> Proxy s1 -> [NormalizedFilePath] -> k -> Action ()
1461-
runWithSignal msgStart msgEnd files rule = do
1466+
runWithSignal :: (KnownSymbol s0, KnownSymbol s1, IdeRule k i is v) => Proxy s0 -> Proxy s1 -> [InputPath i] -> k -> Action ()
1467+
runWithSignal msgStart msgEnd inputFiles rule = do
1468+
let files = map unInputPath inputFiles
14621469
ShakeExtras{ideTesting = Options.IdeTesting testing, lspEnv} <- getShakeExtras
14631470
kickSignal testing lspEnv files msgStart
1464-
void $ uses rule files
1471+
void $ uses rule inputFiles
14651472
kickSignal testing lspEnv files msgEnd

hls-graph/hls-graph.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ library
6060
Development.IDE.Graph.Internal.Key
6161
Development.IDE.Graph.Internal.Paths
6262
Development.IDE.Graph.Internal.Profile
63+
Development.IDE.Graph.Internal.RuleInput
6364
Development.IDE.Graph.Internal.Rules
6465
Development.IDE.Graph.Internal.Types
6566
Development.IDE.Graph.KeyMap
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{-# LANGUAGE TypeFamilies #-}
2+
module Development.IDE.Graph.Internal.RuleInput where
3+
4+
type ValidInputs = [Input]
5+
6+
data Input
7+
= ProjectHaskellFile
8+
| DependencyHaskellFile
9+
10+
type family RuleInput k :: ValidInputs
11+
12+
class HasInput (i :: Input) (is :: ValidInputs)
13+
14+
instance HasInput i (i : is)
15+
16+
instance {-# OVERLAPPABLE #-}
17+
HasInput i is => HasInput i (j : is)

0 commit comments

Comments
 (0)