Skip to content

Commit 421ad24

Browse files
Saizanfendor
andauthored
adding whole-project loading (#5002)
* [Fix #5005] Add whole-project loading support renamed "sessionLoading" config to "componentsLoading" deprecated old values in favor of: "single", "multi: needed-only", "multi: whole-project" Upgrade to hie-bios 0.21.0.0 * Introduce dedicated backwards compatibility type LegacySessionLoadingPreferenceConfig * Prefer explicit config value over callback for config initialisation --------- Co-authored-by: fendor <fendor@posteo.de>
1 parent 0097132 commit 421ad24

16 files changed

Lines changed: 267 additions & 116 deletions

File tree

cabal.project

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ packages:
66
./hls-plugin-api
77
./hls-test-utils
88

9-
index-state: 2026-07-11T00:00:00Z
9+
index-state: 2026-07-30T14:41:11Z
1010

1111
tests: True
1212
test-show-details: direct

docs/configuration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ Here is a list of the additional settings currently supported by `haskell-langua
4545
- Max completions (`haskell.maxCompletions`, default 40): maximum number of completions sent to the LSP client.
4646
- Check project (`haskell.checkProject`, default true): whether to typecheck the entire project on initial load. As it is activated by default could drive to bad performance in large projects.
4747
- Check parents (`haskell.checkParents`, default `CheckOnSave`): when to typecheck reverse dependencies of a file; one of `NeverCheck`, `CheckOnSave` (means dependent/parent modules will only be checked when you save), or `AlwaysCheck` (means re-typechecking them on every change).
48-
- Session loading preference (`haskell.sessionLoading`, default `multipleComponents`): how to load sessions; one of `singleComponent` (means always loading only a single component when a new component is discovered) or `multipleComponents` (means always preferring loading multiple components in the cradle at once). `multipleComponents` might not be always possible, if the tool doesn't support multiple components loading. The cradle can decide how to handle these situations, and whether to honour the preference at all.
48+
- Components loading preference (`haskell.componentsLoading`, default `multi: needed-only`): which components to choose when loading sessions; one of `single` (means always loading only a single component when a new component is discovered), `multi: needed-only` (means preferring loading multiple components in the cradle at once, but only those needed for documents opened), `multi: whole-project` (means preferring loading all the components discovered from the cradle/project; see also `componentsToLoad` cradle config field). `multi: ...` might not be always possible, if the tool doesn't support multiple components loading. The cradle can decide how to handle these situations, and whether to honour the preference at all.
4949

5050
#### Generic plugin configuration
5151

ghcide-test/exe/CradleTests.hs

Lines changed: 161 additions & 64 deletions
Large diffs are not rendered by default.

ghcide/ghcide.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ library
7474
, Glob
7575
, haddock-library >=1.8 && <1.12
7676
, hashable
77-
, hie-bios ^>= 0.19.0
77+
, hie-bios ^>= 0.21.0
7878
, hiedb ^>= 0.8.0.0
7979
, hls-graph == 2.14.0.0
8080
, hls-plugin-api == 2.14.0.0

ghcide/session-loader/Development/IDE/Session.hs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ import Ide.Logger (Pretty (pretty),
7272
vcat, viaShow, (<+>))
7373
import Ide.Types (Config,
7474
SessionLoadingPreferenceConfig (..),
75-
sessionLoading)
75+
componentsLoading)
7676
import Language.LSP.Protocol.Message
7777
import Language.LSP.Server
7878
import System.Directory
@@ -622,11 +622,11 @@ didSessionLoadingPreferenceConfigChange s = do
622622
mLoadingConfig <- liftIO $ readVar biosSessionLoadingVar
623623
case mLoadingConfig of
624624
Nothing -> do
625-
liftIO $ writeVar biosSessionLoadingVar (Just (sessionLoading clientConfig))
625+
liftIO $ writeVar biosSessionLoadingVar (Just (componentsLoading clientConfig))
626626
pure False
627627
Just loadingConfig -> do
628-
liftIO $ writeVar biosSessionLoadingVar (Just (sessionLoading clientConfig))
629-
pure (loadingConfig /= sessionLoading clientConfig)
628+
liftIO $ writeVar biosSessionLoadingVar (Just (componentsLoading clientConfig))
629+
pure (loadingConfig /= componentsLoading clientConfig)
630630

631631
newSessionState :: IO SessionState
632632
newSessionState = do
@@ -1047,7 +1047,7 @@ loadCradleWithNotifications recorder sessionState hieYaml cfp = do
10471047
let progMsg = "Setting up " <> T.pack (takeBaseName (cradleRootDir cradle))
10481048
<> " (for " <> T.pack lfpLog <> ")"
10491049

1050-
sessionPref <- asks (sessionLoading . sessionClientConfig)
1050+
sessionPref <- asks (componentsLoading . sessionClientConfig)
10511051
extraToLoads <- liftIO $ getExtraFilesToLoad sessionState hieYaml cfp
10521052
-- Start loading the file!
10531053
eopts <- mRunLspTCallback lspEnv (\act -> withIndefiniteProgress progMsg Nothing NotCancellable (const act)) $
@@ -1069,7 +1069,7 @@ cradleToOptsAndLibDir recorder loadConfig cradle file old_fps = do
10691069
-- noneCradleFoundMessage f = T.pack $ "none cradle found for " <> f <> ", ignoring the file"
10701070
-- Start off by getting the session options
10711071
logWith recorder Debug $ LogCradle cradle
1072-
cradleRes <- HieBios.getCompilerOptions file loadStyle cradle
1072+
cradleRes <- HieBios.getCompilerOptions (TargetWithContext file old_fps) loadStyle cradle
10731073
case cradleRes of
10741074
CradleSuccess r -> do
10751075
-- Now get the GHC lib dir
@@ -1090,8 +1090,9 @@ cradleToOptsAndLibDir recorder loadConfig cradle file old_fps = do
10901090

10911091
where
10921092
loadStyle = case loadConfig of
1093-
PreferSingleComponentLoading -> LoadFile
1094-
PreferMultiComponentLoading -> LoadWithContext old_fps
1093+
PreferSingleComponentLoading -> LoadFile
1094+
PreferMultiComponentLoading -> LoadFileWithContext
1095+
PreferMultiWholeProjectLoading -> LoadUnitsFromCradle
10951096

10961097
-- ----------------------------------------------------------------------------
10971098
-- Utilities

ghcide/session-loader/Development/IDE/Session/Implicit.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,12 @@ stackCradle fp = do
7878
pkgsWithComps <- liftIO $ catMaybes <$> mapM (nestedPkg fp) pkgs
7979
let yaml = fp </> "stack.yaml"
8080
pure $ (,fp) $ case pkgsWithComps of
81-
[] -> Stack (StackType Nothing (Just yaml))
81+
[] -> Stack (StackType Nothing (Just yaml) Nothing)
8282
ps -> StackMulti mempty $ do
8383
Package n cs <- ps
8484
c <- cs
8585
let (prefix, comp) = Implicit.stackComponent n c
86-
pure (prefix, StackType (Just comp) (Just yaml))
86+
pure (prefix, StackType (Just comp) (Just yaml) Nothing)
8787

8888
-- | By default, we generate a simple cabal cradle which is equivalent to the
8989
-- following hie.yaml:
@@ -95,7 +95,7 @@ stackCradle fp = do
9595
--
9696
-- Note, this only works reliable for reasonably modern cabal versions >= 3.2.
9797
simpleCabalCradle :: FilePath -> (CradleTree a, FilePath)
98-
simpleCabalCradle fp = (Cabal $ CabalType Nothing Nothing, fp)
98+
simpleCabalCradle fp = (Cabal $ CabalType Nothing Nothing Nothing, fp)
9999

100100
cabalExecutable :: MaybeT IO FilePath
101101
cabalExecutable = MaybeT $ findExecutable "cabal"

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,7 @@ loadGhcSession recorder ghcSessionDepsConfig = do
741741
[ B.encode (hash (sessionVersion res))
742742
-- When the session version changes, reload all session
743743
-- hsc env sessions
744-
, B.encode (show (sessionLoading config))
744+
, B.encode (show (componentsLoading config))
745745
-- The loading config affects session loading.
746746
-- Invalidate all build nodes.
747747
-- Changing the session loading config will increment

hls-plugin-api/src/Ide/Plugin/Config.hs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,18 @@ module Ide.Plugin.Config
1010
, CheckParents(..)
1111
) where
1212

13-
import Control.Lens (preview)
14-
import Data.Aeson hiding (Error)
15-
import qualified Data.Aeson as A
16-
import Data.Aeson.Lens (_String)
17-
import qualified Data.Aeson.Types as A
13+
import Control.Applicative
14+
import Control.Lens (preview)
15+
import Data.Aeson hiding (Error)
16+
import qualified Data.Aeson as A
17+
import Data.Aeson.Lens (_String)
18+
import qualified Data.Aeson.Types as A
1819
import Data.Default
19-
import qualified Data.Map.Strict as Map
20-
import Data.Maybe (fromMaybe)
21-
import qualified Data.Text as T
22-
import GHC.Exts (toList)
20+
import Data.Functor ((<&>))
21+
import qualified Data.Map.Strict as Map
22+
import Data.Maybe (fromMaybe)
23+
import qualified Data.Text as T
24+
import GHC.Exts (toList)
2325
import Ide.Types
2426

2527
-- ---------------------------------------------------------------------
@@ -42,11 +44,16 @@ parseConfig idePlugins defValue = A.withObject "settings" $ \o ->
4244
<*> o .:? "formattingProvider" .!= formattingProvider defValue
4345
<*> o .:? "cabalFormattingProvider" .!= cabalFormattingProvider defValue
4446
<*> o .:? "maxCompletions" .!= maxCompletions defValue
45-
<*> o .:? "sessionLoading" .!= sessionLoading defValue
47+
<*> loadingPref o
4648
<*> o .:? "linkSourceTo" .!= linkSourceTo defValue
47-
<*> o .:? "linkDocTo" .!=
48-
linkDocTo defValue
49+
<*> o .:? "linkDocTo" .!= linkDocTo defValue
4950
<*> A.explicitParseFieldMaybe (parsePlugins idePlugins) o "plugin" .!= plugins defValue
51+
where
52+
loadingPref o =
53+
-- We can support "componentsLoading" and the legacy "sessionLoading" option.
54+
(o .:? "componentsLoading" .!= componentsLoading defValue)
55+
<|> (o .:? "sessionLoading" .!= LegacySessionLoadingPreferenceConfig (componentsLoading defValue)
56+
<&> getLegacySessionLoadingPreferenceConfig)
5057

5158
-- | Parse the 'PluginConfig'.
5259
-- Since we need to fall back to default values if we do not find one in the input,

hls-plugin-api/src/Ide/Types.hs

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ module Ide.Types
2222
, IdeNotification(..)
2323
, IdePlugins(IdePlugins, ipMap)
2424
, DynFlagsModifications(..)
25-
, Config(..), PluginConfig(..), CheckParents(..), SessionLoadingPreferenceConfig(..)
25+
, Config(..), PluginConfig(..), CheckParents(..)
26+
, SessionLoadingPreferenceConfig(..)
27+
, LegacySessionLoadingPreferenceConfig(..)
28+
, getLegacySessionLoadingPreferenceConfig
2629
, OptLinkTo(..)
2730
, ConfigDescriptor(..), defaultConfigDescriptor, configForPlugin
2831
, CustomConfig(..), mkCustomConfig
@@ -179,7 +182,7 @@ data Config =
179182
, formattingProvider :: !T.Text
180183
, cabalFormattingProvider :: !T.Text
181184
, maxCompletions :: !Int
182-
, sessionLoading :: !SessionLoadingPreferenceConfig
185+
, componentsLoading :: !SessionLoadingPreferenceConfig
183186
, linkSourceTo :: !OptLinkTo
184187
, linkDocTo :: !OptLinkTo
185188
, plugins :: !(Map.Map PluginId PluginConfig)
@@ -192,7 +195,7 @@ instance ToJSON Config where
192195
, "formattingProvider" .= formattingProvider
193196
, "cabalFormattingProvider" .= cabalFormattingProvider
194197
, "maxCompletions" .= maxCompletions
195-
, "sessionLoading" .= sessionLoading
198+
, "componentsLoading" .= componentsLoading
196199
, "linkSourceTo" .= linkSourceTo
197200
, "linkDocTo" .= linkDocTo
198201
, "plugin" .= Map.mapKeysMonotonic (\(PluginId p) -> p) plugins
@@ -208,7 +211,7 @@ instance Default Config where
208211
-- , cabalFormattingProvider = "cabal-fmt"
209212
-- this string value needs to kept in sync with the value provided in HlsPlugins
210213
, maxCompletions = 40
211-
, sessionLoading = PreferMultiComponentLoading
214+
, componentsLoading = PreferMultiComponentLoading
212215
, linkSourceTo = LinkToHackage
213216
, linkDocTo = LinkToHackage
214217
, plugins = mempty
@@ -240,24 +243,67 @@ data SessionLoadingPreferenceConfig
240243
--
241244
-- The cradle can decide how to handle these situations, and whether
242245
-- to honour the preference at all.
246+
| PreferMultiWholeProjectLoading
247+
-- ^ Prefer loading all the components specified in the cradle, if possible.
243248
deriving stock (Eq, Ord, Show, Generic)
244249

245250
instance Pretty SessionLoadingPreferenceConfig where
246-
pretty PreferSingleComponentLoading = "Prefer Single Component Loading"
247-
pretty PreferMultiComponentLoading = "Prefer Multiple Components Loading"
251+
pretty PreferSingleComponentLoading = "Prefer Single Component Loading"
252+
pretty PreferMultiComponentLoading = "Prefer Multiple Components Loading"
253+
pretty PreferMultiWholeProjectLoading = "Prefer Whole Project Loading"
254+
255+
-- | Labels for @SessionLoadingPreferenceConfig@ json format.
256+
--
257+
-- 'singleComponent' and 'multipleComponents' are outdated but are maintained here
258+
-- for backwards compatibility.
259+
-- We prefer 'single', 'multiNeededOnly' and 'multiWholeProject'
260+
singleComponent, multipleComponents, single, multiNeededOnly, multiWholeProject :: T.Text
261+
singleComponent = "singleComponent"
262+
multipleComponents = "multipleComponents"
263+
single = "single"
264+
multiNeededOnly = "multi: needed-only"
265+
multiWholeProject = "multi: whole-project"
266+
267+
-- | Historical artefact!
268+
-- Before HLS 2.15.0.0, the 'SessionLoadingPreferenceConfig' option was called `sessionLoading` with the values
269+
-- `multipleComponents` and `singleComponent`.
270+
--
271+
-- With HLS 2.15.0.0, we renamed these options and also added some new ones!
272+
-- For backwards compatibility, we support the old naming as well using
273+
-- this backwards compatibility newtype.
274+
newtype LegacySessionLoadingPreferenceConfig = LegacySessionLoadingPreferenceConfig SessionLoadingPreferenceConfig
275+
276+
getLegacySessionLoadingPreferenceConfig :: LegacySessionLoadingPreferenceConfig -> SessionLoadingPreferenceConfig
277+
getLegacySessionLoadingPreferenceConfig (LegacySessionLoadingPreferenceConfig conf) = conf
248278

249279
instance ToJSON SessionLoadingPreferenceConfig where
250280
toJSON PreferSingleComponentLoading =
251-
String "singleComponent"
281+
String single
252282
toJSON PreferMultiComponentLoading =
253-
String "multipleComponents"
283+
String multiNeededOnly
284+
toJSON PreferMultiWholeProjectLoading =
285+
String multiWholeProject
254286

255287
instance FromJSON SessionLoadingPreferenceConfig where
256-
parseJSON (String val) = case val of
257-
"singleComponent" -> pure PreferSingleComponentLoading
258-
"multipleComponents" -> pure PreferMultiComponentLoading
259-
_ -> A.prependFailure "parsing SessionLoadingPreferenceConfig failed, "
260-
(A.parseFail $ "Expected one of \"singleComponent\" or \"multipleComponents\" but got " <> T.unpack val )
288+
parseJSON (String val)
289+
| single == val = pure PreferSingleComponentLoading
290+
| multiNeededOnly == val = pure PreferMultiComponentLoading
291+
| multiWholeProject == val = pure PreferMultiWholeProjectLoading
292+
| otherwise = A.prependFailure "parsing SessionLoadingPreferenceConfig failed, "
293+
(A.parseFail $ unwords ["Expected one of " ++ expected ++ " but got", T.unpack val] )
294+
where
295+
expected = T.unpack $ T.intercalate ", " $ map (\ t -> "\'" <> t <> "\'") [single, multiNeededOnly, multiWholeProject]
296+
parseJSON o = A.prependFailure "parsing SessionLoadingPreferenceConfig failed, "
297+
(A.typeMismatch "String" o)
298+
299+
instance FromJSON LegacySessionLoadingPreferenceConfig where
300+
parseJSON (String val)
301+
| singleComponent == val = pure $ LegacySessionLoadingPreferenceConfig PreferSingleComponentLoading
302+
| multipleComponents == val = pure $ LegacySessionLoadingPreferenceConfig PreferMultiComponentLoading
303+
| otherwise = A.prependFailure "parsing SessionLoadingPreferenceConfig failed, "
304+
(A.parseFail $ "Expected one of " ++ expected ++ " but got " <> T.unpack val )
305+
where
306+
expected = T.unpack $ T.intercalate ", " $ map (\ t -> "\'" <> t <> "\'") [singleComponent, multipleComponents]
261307
parseJSON o = A.prependFailure "parsing SessionLoadingPreferenceConfig failed, "
262308
(A.typeMismatch "String" o)
263309

stack-lts24.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ extra-deps:
2121
- hiedb-0.8.0.0
2222
- hie-compat-0.3.1.2
2323
- implicit-hie-0.1.4.0
24-
- hie-bios-0.19.0
24+
- hie-bios-0.21.0
2525
- hw-fingertree-0.1.2.1
2626
- monad-dijkstra-0.1.1.5
2727
- unordered-containers-0.2.21

0 commit comments

Comments
 (0)