diff --git a/.github/actions/setup-build/action.yml b/.github/actions/setup-build/action.yml index 246ec9794c..4ff772fc4d 100644 --- a/.github/actions/setup-build/action.yml +++ b/.github/actions/setup-build/action.yml @@ -31,7 +31,7 @@ runs: sudo chown -R $USER /usr/local/.ghcup shell: bash - - uses: haskell/actions/setup@v2.4.4 + - uses: haskell/actions/setup@v2.4.6 id: HaskEnvSetup with: ghc-version : ${{ inputs.ghc }} diff --git a/.github/workflows/flags.yml b/.github/workflows/flags.yml index b5e94ec584..f00af915f1 100644 --- a/.github/workflows/flags.yml +++ b/.github/workflows/flags.yml @@ -75,8 +75,8 @@ jobs: - name: Build `ghcide` with flags run: cabal v2-build ghcide --flags="ghc-patched-unboxed-bytecode test-exe executable bench-exe ekg" - # we have to clean up warnings for 9.0 and 9.2 before enable -Wall - - if: matrix.ghc != '9.0' && matrix.ghc != '9.2' + # wingman fails with flags on 9.0, so this can be removed when that's gone + - if: matrix.ghc != '9.0' name: Build with pedantic (-WError) run: cabal v2-build --flags="pedantic" diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index 3df6359a37..a0044d14d0 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -35,6 +35,11 @@ flag ekg default: False manual: True +flag pedantic + description: Enable -Werror + default: False + manual: True + library default-language: Haskell2010 build-depends: @@ -221,7 +226,6 @@ library ghc-options: -Wall - -Wno-name-shadowing -Wincomplete-uni-patterns -Wno-unticked-promoted-constructors -fno-ignore-asserts @@ -229,6 +233,27 @@ library if flag(ghc-patched-unboxed-bytecode) cpp-options: -DGHC_PATCHED_UNBOXED_BYTECODE + if flag(pedantic) + -- We eventually want to build with Werror fully, but we haven't + -- finished purging the warnings, so some are set to not be errors + -- for now + ghc-options: -Werror + -Wwarn=unused-packages + -Wwarn=unrecognised-pragmas + -Wwarn=dodgy-imports + -Wwarn=missing-signatures + -Wwarn=duplicate-exports + -Wwarn=dodgy-exports + -Wwarn=incomplete-patterns + -Wwarn=overlapping-patterns + -Wwarn=incomplete-record-updates + + -- ambiguous-fields is only understood by GHC >= 9.2, so we only disable it + -- then. The above comment goes for here too -- this should be understood to + -- be temporary until we can remove these warnings. + if impl(ghc >= 9.2) && flag(pedantic) + ghc-options: -Wwarn=ambiguous-fields + if impl(ghc >= 9) ghc-options: -Wunused-packages diff --git a/ghcide/session-loader/Development/IDE/Session.hs b/ghcide/session-loader/Development/IDE/Session.hs index 84d55c6787..6dfb9a7b01 100644 --- a/ghcide/session-loader/Development/IDE/Session.hs +++ b/ghcide/session-loader/Development/IDE/Session.hs @@ -38,9 +38,8 @@ import Data.Char (isLower) import Data.Default import Data.Either.Extra import Data.Function -import Data.Hashable +import Data.Hashable hiding (hash) import qualified Data.HashMap.Strict as HM -import Data.IORef import Data.List import Data.List.Extra (dropPrefix, split) import qualified Data.Map.Strict as Map @@ -51,11 +50,11 @@ import Data.Time.Clock import Data.Version import Development.IDE.Core.RuleTypes import Development.IDE.Core.Shake hiding (Log, Priority, - withHieDb) + knownTargets, withHieDb) import qualified Development.IDE.GHC.Compat as Compat import Development.IDE.GHC.Compat.Core hiding (Target, TargetFile, TargetModule, - Var, Warning) + Var, Warning, getOptions) import qualified Development.IDE.GHC.Compat.Core as GHC import Development.IDE.GHC.Compat.Env hiding (Logger) import Development.IDE.GHC.Compat.Units (UnitId) @@ -111,6 +110,12 @@ import HieDb.Utils import qualified System.Random as Random import System.Random (RandomGen) +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,4,0) +import Data.IORef +#endif + data Log = LogSettingInitialDynFlags | LogGetInitialGhcLibDirDefaultCradleFail !CradleError !FilePath !(Maybe FilePath) !(Cradle Void) @@ -148,21 +153,21 @@ instance Pretty Log where , "Cradle:" <+> viaShow cradle ] LogGetInitialGhcLibDirDefaultCradleNone -> "Couldn't load cradle. Cradle not found." - LogHieDbRetry delay maxDelay maxRetryCount e -> + LogHieDbRetry delay maxDelay retriesRemaining e -> nest 2 $ vcat [ "Retrying hiedb action..." , "delay:" <+> pretty delay , "maximum delay:" <+> pretty maxDelay - , "retries remaining:" <+> pretty maxRetryCount + , "retries remaining:" <+> pretty retriesRemaining , "SQLite error:" <+> pretty (displayException e) ] - LogHieDbRetriesExhausted baseDelay maxDelay maxRetryCount e -> + LogHieDbRetriesExhausted baseDelay maxDelay retriesRemaining e -> nest 2 $ vcat [ "Retries exhausted for hiedb action." , "base delay:" <+> pretty baseDelay , "maximum delay:" <+> pretty maxDelay - , "retries remaining:" <+> pretty maxRetryCount + , "retries remaining:" <+> pretty retriesRemaining , "Exception:" <+> pretty (displayException e) ] LogHieDbWriterThreadSQLiteError e -> nest 2 $ @@ -199,7 +204,7 @@ instance Pretty Log where "Cradle:" <+> viaShow cradle LogNewComponentCache componentCache -> "New component cache HscEnvEq:" <+> viaShow componentCache - LogHieBios log -> pretty log + LogHieBios msg -> pretty msg -- | Bump this version number when making changes to the format of the data stored in hiedb hiedbDataVersion :: String @@ -263,17 +268,16 @@ loadWithImplicitCradle mHieYaml rootDir = do getInitialGhcLibDirDefault :: Recorder (WithPriority Log) -> FilePath -> IO (Maybe LibDir) getInitialGhcLibDirDefault recorder rootDir = do - let log = logWith recorder hieYaml <- findCradle def rootDir cradle <- loadCradle def hieYaml rootDir libDirRes <- getRuntimeGhcLibDir (toCologActionWithPrio (cmapWithPrio LogHieBios recorder)) cradle case libDirRes of CradleSuccess libdir -> pure $ Just $ LibDir libdir CradleFail err -> do - log Error $ LogGetInitialGhcLibDirDefaultCradleFail err rootDir hieYaml cradle + logWith recorder Error $ LogGetInitialGhcLibDirDefaultCradleFail err rootDir hieYaml cradle pure Nothing CradleNone -> do - log Warning LogGetInitialGhcLibDirDefaultCradleNone + logWith recorder Warning LogGetInitialGhcLibDirDefaultCradleNone pure Nothing -- | Sets `unsafeGlobalDynFlags` on using the hie-bios cradle and returns the GHC libdir @@ -301,28 +305,26 @@ retryOnException -> g -- ^ random number generator -> m a -- ^ action that may throw exception -> m a -retryOnException exceptionPred recorder maxDelay !baseDelay !maxRetryCount rng action = do +retryOnException exceptionPred recorder maxDelay !baseDelay !maxTimesRetry rng action = do result <- tryJust exceptionPred action case result of Left e - | maxRetryCount > 0 -> do + | maxTimesRetry > 0 -> do -- multiply by 2 because baseDelay is midpoint of uniform range let newBaseDelay = min maxDelay (baseDelay * 2) let (delay, newRng) = Random.randomR (0, newBaseDelay) rng - let newMaxRetryCount = maxRetryCount - 1 + let newMaxTimesRetry = maxTimesRetry - 1 liftIO $ do - log Warning $ LogHieDbRetry delay maxDelay newMaxRetryCount (toException e) + logWith recorder Warning $ LogHieDbRetry delay maxDelay newMaxTimesRetry (toException e) threadDelay delay - retryOnException exceptionPred recorder maxDelay newBaseDelay newMaxRetryCount newRng action + retryOnException exceptionPred recorder maxDelay newBaseDelay newMaxTimesRetry newRng action | otherwise -> do liftIO $ do - log Warning $ LogHieDbRetriesExhausted baseDelay maxDelay maxRetryCount (toException e) + logWith recorder Warning $ LogHieDbRetriesExhausted baseDelay maxDelay maxTimesRetry (toException e) throwIO e Right b -> pure b - where - log = logWith recorder -- | in microseconds oneSecond :: Int @@ -377,21 +379,19 @@ runWithDb recorder fp k = do withAsync (writerThread withWriteDbRetryable chan) $ \_ -> do withHieDb fp (\readDb -> k (makeWithHieDbRetryable recorder rng readDb) chan) where - log = logWith recorder - writerThread :: WithHieDb -> IndexQueue -> IO () writerThread withHieDbRetryable chan = do -- Clear the index of any files that might have been deleted since the last run _ <- withHieDbRetryable deleteMissingRealFiles _ <- withHieDbRetryable garbageCollectTypeNames forever $ do - k <- atomically $ readTQueue chan + l <- atomically $ readTQueue chan -- TODO: probably should let exceptions be caught/logged/handled by top level handler - k withHieDbRetryable + l withHieDbRetryable `Safe.catch` \e@SQLError{} -> do - log Error $ LogHieDbWriterThreadSQLiteError e - `Safe.catchAny` \e -> do - log Error $ LogHieDbWriterThreadException e + logWith recorder Error $ LogHieDbWriterThreadSQLiteError e + `Safe.catchAny` \f -> do + logWith recorder Error $ LogHieDbWriterThreadException f getHieDbLoc :: FilePath -> IO FilePath @@ -520,7 +520,7 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} dir = do -- We will modify the unitId and DynFlags used for -- compilation but these are the true source of -- information. - + new_deps = RawComponentInfo (homeUnitId_ df) df targets cfp opts dep_info : maybe [] snd oldDeps -- Get all the unit-ids for things in this component @@ -532,7 +532,7 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} dir = do #if MIN_VERSION_ghc(9,3,0) let (df2, uids) = (rawComponentDynFlags, []) #else - let (df2, uids) = removeInplacePackages fakeUid inplace rawComponentDynFlags + let (df2, uids) = _removeInplacePackages fakeUid inplace rawComponentDynFlags #endif let prefix = show rawComponentUnitId -- See Note [Avoiding bad interface files] @@ -554,11 +554,11 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} dir = do -- scratch again (for now) -- It's important to keep the same NameCache though for reasons -- that I do not fully understand - log Info $ LogMakingNewHscEnv inplace - hscEnv <- emptyHscEnv ideNc libDir + logWith recorder Info $ LogMakingNewHscEnv inplace + hscEnvB <- emptyHscEnv ideNc libDir !newHscEnv <- -- Add the options for the current component to the HscEnv - evalGhcEnv hscEnv $ do + evalGhcEnv hscEnvB $ do _ <- setSessionDynFlags #if !MIN_VERSION_ghc(9,3,0) $ setHomeUnitId_ fakeUid @@ -595,7 +595,7 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} dir = do res <- loadDLL hscEnv "libm.so.6" case res of Nothing -> pure () - Just err -> log Error $ LogDLLLoadError err + Just err -> logWith recorder Error $ LogDLLLoadError err -- Make a map from unit-id to DynFlags, this is used when trying to @@ -637,21 +637,22 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} dir = do let cs_exist = catMaybes (zipWith (<$) cfps' mmt) modIfaces <- uses GetModIface cs_exist -- update exports map - extras <- getShakeExtras + shakeExtras <- getShakeExtras let !exportsMap' = createExportsMap $ mapMaybe (fmap hirModIface) modIfaces - liftIO $ atomically $ modifyTVar' (exportsMap extras) (exportsMap' <>) + liftIO $ atomically $ modifyTVar' (exportsMap shakeExtras) (exportsMap' <>) return (second Map.keys res) let consultCradle :: Maybe FilePath -> FilePath -> IO (IdeResult HscEnvEq, [FilePath]) consultCradle hieYaml cfp = do - lfp <- flip makeRelative cfp <$> getCurrentDirectory - log Info $ LogCradlePath lfp + lfpLog <- flip makeRelative cfp <$> getCurrentDirectory + logWith recorder Info $ LogCradlePath lfpLog when (isNothing hieYaml) $ - log Warning $ LogCradleNotFound lfp + logWith recorder Warning $ LogCradleNotFound lfpLog cradle <- loadCradle hieYaml dir + -- TODO: Why are we repeating the same command we have on line 646? lfp <- flip makeRelative cfp <$> getCurrentDirectory when optTesting $ mRunLspT lspEnv $ @@ -667,7 +668,7 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} dir = do addTag "result" (show res) return res - log Debug $ LogSessionLoadingResult eopts + logWith recorder Debug $ LogSessionLoadingResult eopts case eopts of -- The cradle gave us some options so get to work turning them -- into and HscEnv. @@ -727,11 +728,9 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} dir = do opts <- liftIO $ join $ mask_ $ modifyVar runningCradle $ \as -> do -- If the cradle is not finished, then wait for it to finish. void $ wait as - as <- async $ getOptions file - return (as, wait as) + asyncRes <- async $ getOptions file + return (asyncRes, wait asyncRes) pure opts - where - log = logWith recorder -- | Run the specific cradle on a specific FilePath via hie-bios. -- This then builds dependencies or whatever based on the cradle, gets the @@ -787,14 +786,14 @@ fromTargetId :: [FilePath] -- ^ import paths -> DependencyInfo -> IO [TargetDetails] -- For a target module we consider all the import paths -fromTargetId is exts (GHC.TargetModule mod) env dep = do - let fps = [i moduleNameSlashes mod -<.> ext <> boot +fromTargetId is exts (GHC.TargetModule modName) env dep = do + let fps = [i moduleNameSlashes modName -<.> ext <> boot | ext <- exts , i <- is , boot <- ["", "-boot"] ] locs <- mapM (fmap toNormalizedFilePath' . makeAbsolute) fps - return [TargetDetails (TargetModule mod) env dep locs] + return [TargetDetails (TargetModule modName) env dep locs] -- For a 'TargetFile' we consider all the possible module names fromTargetId _ _ (GHC.TargetFile f _) env deps = do nf <- toNormalizedFilePath' <$> makeAbsolute f @@ -1059,11 +1058,11 @@ getDependencyInfo :: [FilePath] -> IO DependencyInfo getDependencyInfo fs = Map.fromList <$> mapM do_one fs where - tryIO :: IO a -> IO (Either IOException a) - tryIO = Safe.try + safeTryIO :: IO a -> IO (Either IOException a) + safeTryIO = Safe.try do_one :: FilePath -> IO (FilePath, Maybe UTCTime) - do_one fp = (fp,) . eitherToMaybe <$> tryIO (getModificationTime fp) + do_one fp = (fp,) . eitherToMaybe <$> safeTryIO (getModificationTime fp) -- | This function removes all the -package flags which refer to packages we -- are going to deal with ourselves. For example, if a executable depends @@ -1073,12 +1072,12 @@ getDependencyInfo fs = Map.fromList <$> mapM do_one fs -- There are several places in GHC (for example the call to hptInstances in -- tcRnImports) which assume that all modules in the HPT have the same unit -- ID. Therefore we create a fake one and give them all the same unit id. -removeInplacePackages +_removeInplacePackages --Only used in ghc < 9.4 :: UnitId -- ^ fake uid to use for our internal component -> [UnitId] -> DynFlags -> (DynFlags, [UnitId]) -removeInplacePackages fake_uid us df = (setHomeUnitId_ fake_uid $ +_removeInplacePackages fake_uid us df = (setHomeUnitId_ fake_uid $ df { packageFlags = ps }, uids) where (uids, ps) = Compat.filterInplaceUnits us (packageFlags df) diff --git a/ghcide/src/Development/IDE/Core/Actions.hs b/ghcide/src/Development/IDE/Core/Actions.hs index 41b068cc0c..6b9004b0d5 100644 --- a/ghcide/src/Development/IDE/Core/Actions.hs +++ b/ghcide/src/Development/IDE/Core/Actions.hs @@ -67,7 +67,7 @@ getAtPoint file pos = runMaybeT $ do !pos' <- MaybeT (return $ fromCurrentPosition mapping pos) MaybeT $ liftIO $ fmap (first (toCurrentRange mapping =<<)) <$> AtPoint.atPoint opts hf dkMap env pos' --- | For each Loacation, determine if we have the PositionMapping +-- | For each Location, determine if we have the PositionMapping -- for the correct file. If not, get the correct position mapping -- and then apply the position mapping to the location. toCurrentLocations diff --git a/ghcide/src/Development/IDE/Core/Compile.hs b/ghcide/src/Development/IDE/Core/Compile.hs index 27932497b2..2b35563975 100644 --- a/ghcide/src/Development/IDE/Core/Compile.hs +++ b/ghcide/src/Development/IDE/Core/Compile.hs @@ -39,14 +39,15 @@ module Development.IDE.Core.Compile , shareUsages ) where +import Prelude hiding (mod) import Control.Monad.IO.Class import Control.Concurrent.Extra import Control.Concurrent.STM.Stats hiding (orElse) -import Control.DeepSeq (NFData (..), force, liftRnf, - rnf, rwhnf) +import Control.DeepSeq (NFData (..), force, + rnf) import Control.Exception (evaluate) import Control.Exception.Safe -import Control.Lens hiding (List, (<.>)) +import Control.Lens hiding (List, (<.>), pre) import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Trans.Except @@ -62,13 +63,10 @@ import Data.Generics.Aliases import Data.Generics.Schemes import qualified Data.HashMap.Strict as HashMap import Data.IntMap (IntMap) -import qualified Data.IntMap.Strict as IntMap import Data.IORef import Data.List.Extra -import Data.Map (Map) import qualified Data.Map.Strict as Map import Data.Proxy (Proxy(Proxy)) -import qualified Data.Set as Set import Data.Maybe import qualified Data.Text as T import Data.Time (UTCTime (..)) @@ -96,11 +94,10 @@ import Development.IDE.Types.Location import Development.IDE.Types.Options import GHC (ForeignHValue, GetDocsFailure (..), - GhcException (..), parsedSource) import qualified GHC.LanguageExtensions as LangExt import GHC.Serialized -import HieDb +import HieDb hiding (withHieDb) import qualified Language.LSP.Server as LSP import Language.LSP.Protocol.Types (DiagnosticTag (..)) import qualified Language.LSP.Protocol.Types as LSP @@ -108,44 +105,56 @@ import qualified Language.LSP.Protocol.Message as LSP import System.Directory import System.FilePath import System.IO.Extra (fixIO, newTempFileWithin) -import Unsafe.Coerce + +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,0,1) +import HscTypes +import TcSplice +#endif #if MIN_VERSION_ghc(9,0,1) import GHC.Tc.Gen.Splice +#endif + +#if MIN_VERSION_ghc(9,0,1) && !MIN_VERSION_ghc(9,2,1) +import GHC.Driver.Types +#endif + +#if !MIN_VERSION_ghc(9,2,0) +import qualified Data.IntMap.Strict as IntMap +#endif + +#if MIN_VERSION_ghc(9,2,0) +import qualified GHC as G +#endif + +#if MIN_VERSION_ghc(9,2,0) && !MIN_VERSION_ghc(9,3,0) +import GHC (ModuleGraph) +#endif #if MIN_VERSION_ghc(9,2,1) import GHC.Types.ForeignStubs import GHC.Types.HpcInfo import GHC.Types.TypeEnv -#else -import GHC.Driver.Types #endif -#else -import HscTypes -import TcSplice +#if !MIN_VERSION_ghc(9,3,0) +import Data.Map (Map) +import GHC (GhcException (..)) +import Unsafe.Coerce #endif -#if MIN_VERSION_ghc(9,2,0) -import GHC (Anchor (anchor), - EpaComment (EpaComment), - EpaCommentTok (EpaBlockComment, EpaLineComment), - ModuleGraph, epAnnComments, - mgLookupModule, - mgModSummaries, - priorComments) -import qualified GHC as G -import GHC.Hs (LEpaComment) -import qualified GHC.Types.Error as Error -import Development.IDE.Import.DependencyInformation +#if MIN_VERSION_ghc(9,3,0) +import qualified Data.Set as Set #endif #if MIN_VERSION_ghc(9,5,0) -import GHC.Driver.Config.CoreToStg.Prep -import GHC.Core.Lint.Interactive +import GHC.Driver.Config.CoreToStg.Prep +import GHC.Core.Lint.Interactive #endif ---Simple constansts to make sure the source is consistently named +--Simple constants to make sure the source is consistently named sourceTypecheck :: T.Text sourceTypecheck = "typecheck" sourceParser :: T.Text @@ -178,7 +187,7 @@ computePackageDeps env pkg = do newtype TypecheckHelpers = TypecheckHelpers - { getLinkables :: ([NormalizedFilePath] -> IO [LinkableResult]) -- ^ hls-graph action to get linkables for files + { getLinkables :: [NormalizedFilePath] -> IO [LinkableResult] -- ^ hls-graph action to get linkables for files } typecheckModule :: IdeDefer @@ -193,14 +202,14 @@ typecheckModule (IdeDefer defer) hsc tc_helpers pm = do (initPlugins hsc modSummary) case initialized of Left errs -> return (errs, Nothing) - Right (modSummary', hsc) -> do + Right (modSummary', hscEnv) -> do (warnings, etcm) <- withWarnings sourceTypecheck $ \tweak -> let - session = tweak (hscSetFlags dflags hsc) + session = tweak (hscSetFlags dflags hscEnv) -- TODO: maybe settings ms_hspp_opts is unnecessary? mod_summary'' = modSummary' { ms_hspp_opts = hsc_dflags session} in - catchSrcErrors (hsc_dflags hsc) sourceTypecheck $ do + catchSrcErrors (hsc_dflags hscEnv) sourceTypecheck $ do tcRnModule session tc_helpers $ demoteIfDefer pm{pm_mod_summary = mod_summary''} let errorPipeline = unDefer . hideDiag dflags . tagDiag diags = map errorPipeline warnings @@ -335,8 +344,8 @@ captureSplicesAndDeps TypecheckHelpers{..} env k = do ; moduleLocs <- readIORef (hsc_FC hsc_env) #endif ; lbs <- getLinkables [toNormalizedFilePath' file - | mod <- mods_transitive_list - , let ifr = fromJust $ lookupInstalledModuleEnv moduleLocs mod + | installedMod <- mods_transitive_list + , let ifr = fromJust $ lookupInstalledModuleEnv moduleLocs installedMod file = case ifr of InstalledFound loc _ -> fromJust $ ml_hs_file loc @@ -366,8 +375,8 @@ captureSplicesAndDeps TypecheckHelpers{..} env k = do -- We shouldn't get boot files here, but to be safe, never map them to an installed module -- because boot files don't have linkables we can load, and we will fail if we try to look -- for them - nodeKeyToInstalledModule (NodeKey_Module (ModNodeKeyWithUid (GWIB mod IsBoot) uid)) = Nothing - nodeKeyToInstalledModule (NodeKey_Module (ModNodeKeyWithUid (GWIB mod _) uid)) = Just $ mkModule uid mod + nodeKeyToInstalledModule (NodeKey_Module (ModNodeKeyWithUid (GWIB _ IsBoot) _)) = Nothing + nodeKeyToInstalledModule (NodeKey_Module (ModNodeKeyWithUid (GWIB moduleName _) uid)) = Just $ mkModule uid moduleName nodeKeyToInstalledModule _ = Nothing moduleToNodeKey :: Module -> NodeKey moduleToNodeKey mod = NodeKey_Module $ ModNodeKeyWithUid (GWIB (moduleName mod) NotBoot) (moduleUnitId mod) @@ -434,8 +443,8 @@ tcRnModule hsc_env tc_helpers pmod = do hsc_env_tmp = hscSetFlags (ms_hspp_opts ms) hsc_env ((tc_gbl_env', mrn_info), splices, mod_env) - <- captureSplicesAndDeps tc_helpers hsc_env_tmp $ \hsc_env_tmp -> - do hscTypecheckRename hsc_env_tmp ms $ + <- captureSplicesAndDeps tc_helpers hsc_env_tmp $ \hscEnvTmp -> + do hscTypecheckRename hscEnvTmp ms $ HsParsedModule { hpm_module = parsedSource pmod, hpm_src_files = pm_extra_src_files pmod, hpm_annotations = pm_annotations pmod } @@ -508,7 +517,6 @@ mkHiFileResultCompile mkHiFileResultCompile se session' tcm simplified_guts = catchErrs $ do let session = hscSetFlags (ms_hspp_opts ms) session' ms = pm_mod_summary $ tmrParsed tcm - tcGblEnv = tmrTypechecked tcm (details, guts) <- do -- write core file @@ -553,9 +561,9 @@ mkHiFileResultCompile se session' tcm simplified_guts = catchErrs $ do -- The serialized file however is much more compact and only requires a few -- hundred megabytes of memory total even in a large project with 1000s of -- modules - (core_file, !core_hash2) <- readBinCoreFile (mkUpdater $ hsc_NC session) core_fp + (coreFile, !core_hash2) <- readBinCoreFile (mkUpdater $ hsc_NC session) core_fp pure $ assert (core_hash1 == core_hash2) - $ Just (core_file, fingerprintToBS core_hash2) + $ Just (coreFile, fingerprintToBS core_hash2) -- Verify core file by roundtrip testing and comparison IdeOptions{optVerifyCoreFile} <- getIdeOptionsIO se @@ -594,8 +602,8 @@ mkHiFileResultCompile se session' tcm simplified_guts = catchErrs $ do (prepd_binds', _) #endif <- corePrep unprep_binds' data_tycons - let binds = noUnfoldings $ (map flattenBinds . (:[])) $ prepd_binds - binds' = noUnfoldings $ (map flattenBinds . (:[])) $ prepd_binds' + let binds = noUnfoldings $ (map flattenBinds . (:[])) prepd_binds + binds' = noUnfoldings $ (map flattenBinds . (:[])) prepd_binds' -- diffBinds is unreliable, sometimes it goes down the wrong track. -- This fixes the order of the bindings so that it is less likely to do so. @@ -849,9 +857,9 @@ generateHieAsts hscEnv tcm = where dflags = hsc_dflags hscEnv #if MIN_VERSION_ghc(9,0,0) - run ts = + run _ts = -- ts is only used in GHC 9.2 #if MIN_VERSION_ghc(9,2,0) && !MIN_VERSION_ghc(9,3,0) - fmap (join . snd) . liftIO . initDs hscEnv ts + fmap (join . snd) . liftIO . initDs hscEnv _ts #else id #endif @@ -910,8 +918,8 @@ indexHieFile se mod_summary srcPath !hash hf = do -- We are now in the worker thread -- Check if a newer index of this file has been scheduled, and if so skip this one newerScheduled <- atomically $ do - pending <- readTVar indexPending - pure $ case HashMap.lookup srcPath pending of + pendingOps <- readTVar indexPending + pure $ case HashMap.lookup srcPath pendingOps of Nothing -> False -- If the hash in the pending list doesn't match the current hash, then skip Just pendingHash -> pendingHash /= hash @@ -957,8 +965,8 @@ indexHieFile se mod_summary srcPath !hash hf = do progressPct :: LSP.UInt progressPct = floor $ 100 * progressFrac - whenJust (lspEnv se) $ \env -> whenJust tok $ \tok -> LSP.runLspT env $ - LSP.sendNotification LSP.SMethod_Progress $ LSP.ProgressParams tok $ + whenJust (lspEnv se) $ \env -> whenJust tok $ \token -> LSP.runLspT env $ + LSP.sendNotification LSP.SMethod_Progress $ LSP.ProgressParams token $ toJSON $ case style of Percentage -> LSP.WorkDoneProgressReport @@ -998,8 +1006,8 @@ indexHieFile se mod_summary srcPath !hash hf = do whenJust mdone $ \done -> modifyVar_ indexProgressToken $ \tok -> do whenJust (lspEnv se) $ \env -> LSP.runLspT env $ - whenJust tok $ \tok -> - LSP.sendNotification LSP.SMethod_Progress $ LSP.ProgressParams tok $ + whenJust tok $ \token -> + LSP.sendNotification LSP.SMethod_Progress $ LSP.ProgressParams token $ toJSON $ LSP.WorkDoneProgressEnd { _kind = LSP.AString @"end" @@ -1079,7 +1087,7 @@ mergeEnvs env mg ms extraMods envs = do -- Prefer non-boot files over non-boot files -- otherwise we can get errors like https://gitlab.haskell.org/ghc/ghc/-/issues/19816 -- if a boot file shadows over a non-boot file - combineModuleLocations a@(InstalledFound ml m) b | Just fp <- ml_hs_file ml, not ("boot" `isSuffixOf` fp) = a + combineModuleLocations a@(InstalledFound ml _) _ | Just fp <- ml_hs_file ml, not ("boot" `isSuffixOf` fp) = a combineModuleLocations _ b = b concatFC :: FinderCacheState -> [FinderCache] -> IO FinderCache @@ -1128,11 +1136,12 @@ getModSummaryFromImports -> UTCTime -> Maybe Util.StringBuffer -> ExceptT [FileDiagnostic] IO ModSummaryResult -getModSummaryFromImports env fp modTime contents = do - - (contents, opts, env, src_hash) <- preprocessor env fp contents +-- modTime is only used in GHC < 9.4 +getModSummaryFromImports env fp _modTime mContents = do +-- src_hash is only used in GHC >= 9.4 + (contents, opts, ppEnv, _src_hash) <- preprocessor env fp mContents - let dflags = hsc_dflags env + let dflags = hsc_dflags ppEnv -- The warns will hopefully be reported when we actually parse the module (_warns, L main_loc hsmod) <- parseHeader dflags fp contents @@ -1146,7 +1155,8 @@ getModSummaryFromImports env fp modTime contents = do (src_idecls, ord_idecls) = partition ((== IsBoot) . ideclSource.unLoc) imps -- GHC.Prim doesn't exist physically, so don't go looking for it. - (ordinary_imps, ghc_prim_imports) + -- ghc_prim_imports is only used in GHC >= 9.4 + (ordinary_imps, _ghc_prim_imports) = partition ((/= moduleName gHC_PRIM) . unLoc . ideclName . unLoc) ord_idecls @@ -1166,11 +1176,11 @@ getModSummaryFromImports env fp modTime contents = do msrImports = implicit_imports ++ imps #if MIN_VERSION_ghc (9,3,0) - rn_pkg_qual = renameRawPkgQual (hsc_unit_env env) + rn_pkg_qual = renameRawPkgQual (hsc_unit_env ppEnv) rn_imps = fmap (\(pk, lmn@(L _ mn)) -> (rn_pkg_qual mn pk, lmn)) srcImports = rn_imps $ map convImport src_idecls textualImports = rn_imps $ map convImport (implicit_imports ++ ordinary_imps) - ghc_prim_import = not (null ghc_prim_imports) + ghc_prim_import = not (null _ghc_prim_imports) #else srcImports = map convImport src_idecls textualImports = map convImport (implicit_imports ++ ordinary_imps) @@ -1188,7 +1198,7 @@ getModSummaryFromImports env fp modTime contents = do then mkHomeModLocation dflags (pathToModuleName fp) fp else mkHomeModLocation dflags mod fp - let modl = mkHomeModule (hscHomeUnit env) mod + let modl = mkHomeModule (hscHomeUnit ppEnv) mod sourceType = if "-boot" `isSuffixOf` takeExtension fp then HsBootFile else HsSrcFile msrModSummary2 = ModSummary @@ -1197,10 +1207,10 @@ getModSummaryFromImports env fp modTime contents = do #if MIN_VERSION_ghc(9,3,0) , ms_dyn_obj_date = Nothing , ms_ghc_prim_import = ghc_prim_import - , ms_hs_hash = src_hash + , ms_hs_hash = _src_hash #else - , ms_hs_date = modTime + , ms_hs_date = _modTime #endif , ms_hsc_src = sourceType -- The contents are used by the GetModSummary rule @@ -1216,7 +1226,7 @@ getModSummaryFromImports env fp modTime contents = do } msrFingerprint <- liftIO $ computeFingerprint opts msrModSummary2 - (msrModSummary, msrHscEnv) <- liftIO $ initPlugins env msrModSummary2 + (msrModSummary, msrHscEnv) <- liftIO $ initPlugins ppEnv msrModSummary2 return ModSummaryResult{..} where -- Compute a fingerprint from the contents of `ModSummary`, @@ -1304,7 +1314,7 @@ parseFileContents env customPreprocessor filename ms = do let preproc_warnings = diagFromStrings sourceParser DiagnosticSeverity_Warning preproc_warns (parsed', msgs) <- liftIO $ applyPluginsParsedResultAction env dflags ms hpm_annotations parsed psMessages - let (warns, errs) = renderMessages msgs + let (warns, errors) = renderMessages msgs -- Just because we got a `POk`, it doesn't mean there -- weren't errors! To clarify, the GHC parser @@ -1315,8 +1325,8 @@ parseFileContents env customPreprocessor filename ms = do -- further errors/warnings can be collected). Fatal -- errors are those from which a parse tree just can't -- be produced. - unless (null errs) $ - throwE $ diagFromErrMsgs sourceParser dflags errs + unless (null errors) $ + throwE $ diagFromErrMsgs sourceParser dflags errors -- To get the list of extra source files, we take the list @@ -1468,19 +1478,21 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do -- The source is modified if it is newer than the destination (iface file) -- A more precise check for the core file is performed later - let sourceMod = case mb_dest_version of + let _sourceMod = case mb_dest_version of -- sourceMod is only used in GHC < 9.4 Nothing -> SourceModified -- destination file doesn't exist, assume modified source Just dest_version | source_version <= dest_version -> SourceUnmodified | otherwise -> SourceModified - old_iface <- case mb_old_iface of + -- old_iface is only used in GHC >= 9.4 + _old_iface <- case mb_old_iface of Just iface -> pure (Just iface) Nothing -> do - let ncu = hsc_NC sessionWithMsDynFlags - read_dflags = hsc_dflags sessionWithMsDynFlags + -- ncu and read_dflags are only used in GHC >= 9.4 + let _ncu = hsc_NC sessionWithMsDynFlags + _read_dflags = hsc_dflags sessionWithMsDynFlags #if MIN_VERSION_ghc(9,3,0) - read_result <- liftIO $ readIface read_dflags ncu mod iface_file + read_result <- liftIO $ readIface _read_dflags _ncu mod iface_file #else read_result <- liftIO $ initIfaceCheck (text "readIface") sessionWithMsDynFlags $ readIface mod iface_file @@ -1495,11 +1507,11 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do -- given that the source is unmodified (recomp_iface_reqd, mb_checked_iface) #if MIN_VERSION_ghc(9,3,0) - <- liftIO $ checkOldIface sessionWithMsDynFlags ms old_iface >>= \case + <- liftIO $ checkOldIface sessionWithMsDynFlags ms _old_iface >>= \case UpToDateItem x -> pure (UpToDate, Just x) OutOfDateItem reason x -> pure (NeedsRecompile reason, x) #else - <- liftIO $ checkOldIface sessionWithMsDynFlags ms sourceMod mb_old_iface + <- liftIO $ checkOldIface sessionWithMsDynFlags ms _sourceMod mb_old_iface #endif let do_regenerate _reason = withTrace "regenerate interface" $ \setTag -> do @@ -1521,10 +1533,10 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do Just msg -> do_regenerate msg Nothing | isJust linkableNeeded -> handleErrs $ do - (core_file@CoreFile{cf_iface_hash}, core_hash) <- liftIO $ + (coreFile@CoreFile{cf_iface_hash}, core_hash) <- liftIO $ readBinCoreFile (mkUpdater $ hsc_NC session) core_file if cf_iface_hash == getModuleHash iface - then return ([], Just $ mkHiFileResult ms iface details runtime_deps (Just (core_file, fingerprintToBS core_hash))) + then return ([], Just $ mkHiFileResult ms iface details runtime_deps (Just (coreFile, fingerprintToBS core_hash))) else do_regenerate (recompBecause "Core file out of date (doesn't match iface hash)") | otherwise -> return ([], Just $ mkHiFileResult ms iface details runtime_deps Nothing) where handleErrs = flip catches @@ -1578,6 +1590,7 @@ checkLinkableDependencies hsc_env get_linkable_hashes runtime_deps = do _ -> pure $ Just $ recompBecause $ "out of date runtime dependencies: " ++ intercalate ", " (map show out_of_date) +recompBecause :: String -> RecompileRequired recompBecause = #if MIN_VERSION_ghc(9,3,0) NeedsRecompile . @@ -1622,15 +1635,15 @@ coreFileToCgGuts session iface details core_file = do }) core_binds <- initIfaceCheck (text "l") hsc_env' $ typecheckCoreFile this_mod types_var core_file -- Implicit binds aren't saved, so we need to regenerate them ourselves. - let implicit_binds = concatMap getImplicitBinds tyCons + let _implicit_binds = concatMap getImplicitBinds tyCons -- only used if GHC < 9.6 tyCons = typeEnvTyCons (md_types details) #if MIN_VERSION_ghc(9,5,0) -- In GHC 9.6, the implicit binds are tidied and part of core_binds pure $ CgGuts this_mod tyCons core_binds [] NoStubs [] mempty (emptyHpcInfo False) Nothing [] #elif MIN_VERSION_ghc(9,3,0) - pure $ CgGuts this_mod tyCons (implicit_binds ++ core_binds) [] NoStubs [] mempty (emptyHpcInfo False) Nothing [] + pure $ CgGuts this_mod tyCons (_implicit_binds ++ core_binds) [] NoStubs [] mempty (emptyHpcInfo False) Nothing [] #else - pure $ CgGuts this_mod tyCons (implicit_binds ++ core_binds) NoStubs [] [] (emptyHpcInfo False) Nothing [] + pure $ CgGuts this_mod tyCons (_implicit_binds ++ core_binds) NoStubs [] [] (emptyHpcInfo False) Nothing [] #endif coreFileToLinkable :: LinkableType -> HscEnv -> ModSummary -> ModIface -> ModDetails -> CoreFile -> UTCTime -> IO ([FileDiagnostic], Maybe HomeModInfo) @@ -1689,8 +1702,7 @@ getDocsBatch hsc_env _names = do #else Map.findWithDefault mempty name amap)) #endif - return $ map (first $ T.unpack . printOutputable) - $ res + return $ map (first $ T.unpack . printOutputable) res where compiled n = -- TODO: Find a more direct indicator. @@ -1706,7 +1718,7 @@ lookupName :: HscEnv -> IO (Maybe TyThing) lookupName _ name | Nothing <- nameModule_maybe name = pure Nothing -lookupName hsc_env name = handle $ do +lookupName hsc_env name = exceptionHandle $ do #if MIN_VERSION_ghc(9,2,0) mb_thing <- liftIO $ lookupType hsc_env name #else @@ -1726,7 +1738,7 @@ lookupName hsc_env name = handle $ do Util.Succeeded x -> return (Just x) _ -> return Nothing where - handle x = x `catch` \(_ :: IOEnvFailure) -> pure Nothing + exceptionHandle x = x `catch` \(_ :: IOEnvFailure) -> pure Nothing pathToModuleName :: FilePath -> ModuleName pathToModuleName = mkModuleName . map rep @@ -1734,3 +1746,32 @@ pathToModuleName = mkModuleName . map rep rep c | isPathSeparator c = '_' rep ':' = '_' rep c = c + +{- Note [Guidelines For Using CPP In GHCIDE Import Statements] + GHCIDE's interface with GHC is extensive, and unfortunately, because we have + to work with multiple versions of GHC, we have several files that need to use + a lot of CPP. In order to simplify the CPP in the import section of every file + we have a few specific guidelines for using CPP in these sections. + + - We don't want to nest CPP clauses, nor do we want to use else clauses. Both + nesting and else clauses end up drastically complicating the code, and require + significant mental stack to unwind. + + - CPP clauses should be placed at the end of the imports section. The clauses + should be ordered by the GHC version they target from earlier to later versions, + with negative if clauses coming before positive if clauses of the same + version. (If you think about which GHC version a clause activates for this + should make sense `!MIN_VERSION_GHC(9,0,0)` refers to 8.10 and lower which is + a earlier version than `MIN_VERSION_GHC(9,0,0)` which refers to versions 9.0 + and later). In addition there should be a space before and after each CPP + clause. + + - In if clauses that use `&&` and depend on more than one statement, the + positive statement should come before the negative statement. In addition the + clause should come after the single positive clause for that GHC version. + + - There shouldn't be multiple identical CPP statements. The use of odd or even + GHC numbers is identical, with the only preference being to use what is + already there. (i.e. (`MIN_VERSION_GHC(9,2,0)` and `MIN_VERSION_GHC(9,1,0)` + are functionally equivalent) +-} \ No newline at end of file diff --git a/ghcide/src/Development/IDE/Core/FileExists.hs b/ghcide/src/Development/IDE/Core/FileExists.hs index b7e568d0d6..7a3d9cdd60 100644 --- a/ghcide/src/Development/IDE/Core/FileExists.hs +++ b/ghcide/src/Development/IDE/Core/FileExists.hs @@ -95,8 +95,8 @@ data Log instance Pretty Log where pretty = \case - LogFileStore log -> pretty log - LogShake log -> pretty log + LogFileStore msg -> pretty msg + LogShake msg -> pretty msg -- | Grab the current global value of 'FileExistsMap' without acquiring a dependency getFileExistsMapUntracked :: Action FileExistsMap diff --git a/ghcide/src/Development/IDE/Core/FileStore.hs b/ghcide/src/Development/IDE/Core/FileStore.hs index 229aaecb96..315a078282 100644 --- a/ghcide/src/Development/IDE/Core/FileStore.hs +++ b/ghcide/src/Development/IDE/Core/FileStore.hs @@ -1,6 +1,5 @@ -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE CPP #-} {-# LANGUAGE TypeFamilies #-} module Development.IDE.Core.FileStore( @@ -28,16 +27,22 @@ import Control.Concurrent.STM.TQueue (writeTQueue) import Control.Exception import Control.Monad.Extra import Control.Monad.IO.Class +import qualified Data.Binary as B import qualified Data.ByteString as BS +import qualified Data.ByteString.Lazy as LBS import qualified Data.HashMap.Strict as HashMap import Data.IORef +import Data.List (foldl') import qualified Data.Text as T +import qualified Data.Text as Text import qualified Data.Text.Utf16.Rope as Rope import Data.Time import Data.Time.Clock.POSIX import Development.IDE.Core.FileUtils +import Development.IDE.Core.IdeConfiguration (isWorkspaceFile) import Development.IDE.Core.RuleTypes import Development.IDE.Core.Shake hiding (Log) +import qualified Development.IDE.Core.Shake as Shake import Development.IDE.GHC.Orphans () import Development.IDE.Graph import Development.IDE.Import.DependencyInformation @@ -45,24 +50,6 @@ import Development.IDE.Types.Diagnostics import Development.IDE.Types.Location import Development.IDE.Types.Options import HieDb.Create (deleteMissingRealFiles) -import Ide.Plugin.Config (CheckParents (..), - Config) -import System.IO.Error - -#ifdef mingw32_HOST_OS -import qualified System.Directory as Dir -#else -#endif - -import qualified Ide.Logger as L - -import Data.Aeson (ToJSON (toJSON)) -import qualified Data.Binary as B -import qualified Data.ByteString.Lazy as LBS -import Data.List (foldl') -import qualified Data.Text as Text -import Development.IDE.Core.IdeConfiguration (isWorkspaceFile) -import qualified Development.IDE.Core.Shake as Shake import Ide.Logger (Pretty (pretty), Priority (Info), Recorder, @@ -70,6 +57,9 @@ import Ide.Logger (Pretty (pretty), cmapWithPrio, logWith, viaShow, (<+>)) +import qualified Ide.Logger as L +import Ide.Plugin.Config (CheckParents (..), + Config) import Language.LSP.Protocol.Message (toUntypedRegistration) import qualified Language.LSP.Protocol.Message as LSP import Language.LSP.Protocol.Types (DidChangeWatchedFilesRegistrationOptions (DidChangeWatchedFilesRegistrationOptions), @@ -79,8 +69,10 @@ import qualified Language.LSP.Protocol.Types as LSP import qualified Language.LSP.Server as LSP import Language.LSP.VFS import System.FilePath +import System.IO.Error import System.IO.Unsafe + data Log = LogCouldNotIdentifyReverseDeps !NormalizedFilePath | LogTypeCheckingReverseDeps !NormalizedFilePath !(Maybe [NormalizedFilePath]) @@ -96,7 +88,7 @@ instance Pretty Log where <+> viaShow path <> ":" <+> pretty (fmap (fmap show) reverseDepPaths) - LogShake log -> pretty log + LogShake msg -> pretty msg addWatchedFileRule :: Recorder (WithPriority Log) -> (NormalizedFilePath -> Action Bool) -> Rules () addWatchedFileRule recorder isWatched = defineNoDiagnostics (cmapWithPrio LogShake recorder) $ \AddWatchedFile f -> do @@ -243,11 +235,10 @@ typecheckParents recorder state nfp = void $ shakeEnqueue (shakeExtras state) pa typecheckParentsAction :: Recorder (WithPriority Log) -> NormalizedFilePath -> Action () typecheckParentsAction recorder nfp = do revs <- transitiveReverseDependencies nfp <$> useNoFile_ GetModuleGraph - let log = logWith recorder case revs of - Nothing -> log Info $ LogCouldNotIdentifyReverseDeps nfp + Nothing -> logWith recorder Info $ LogCouldNotIdentifyReverseDeps nfp Just rs -> do - log Info $ LogTypeCheckingReverseDeps nfp revs + logWith recorder Info $ LogTypeCheckingReverseDeps nfp revs void $ uses GetModIface rs -- | Note that some keys have been modified and restart the session diff --git a/ghcide/src/Development/IDE/Core/FileUtils.hs b/ghcide/src/Development/IDE/Core/FileUtils.hs index 4725ed83bd..e8ff7299b4 100644 --- a/ghcide/src/Development/IDE/Core/FileUtils.hs +++ b/ghcide/src/Development/IDE/Core/FileUtils.hs @@ -6,6 +6,7 @@ module Development.IDE.Core.FileUtils( import Data.Time.Clock.POSIX + #ifdef mingw32_HOST_OS import qualified System.Directory as Dir #else diff --git a/ghcide/src/Development/IDE/Core/IdeConfiguration.hs b/ghcide/src/Development/IDE/Core/IdeConfiguration.hs index c59fb2fc9d..eb42450bde 100644 --- a/ghcide/src/Development/IDE/Core/IdeConfiguration.hs +++ b/ghcide/src/Development/IDE/Core/IdeConfiguration.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE DuplicateRecordFields #-} module Development.IDE.Core.IdeConfiguration ( IdeConfiguration(..) , registerIdeConfiguration @@ -13,13 +12,13 @@ module Development.IDE.Core.IdeConfiguration where import Control.Concurrent.Strict -import Control.Lens ((^.)) + import Control.Monad import Control.Monad.IO.Class import Data.Aeson.Types (Value) import Data.Hashable (Hashed, hashed, unhashed) import Data.HashSet (HashSet, singleton) -import Data.Text (Text, isPrefixOf) +import Data.Text (isPrefixOf) import Development.IDE.Core.Shake import Development.IDE.Graph import Development.IDE.Types.Location diff --git a/ghcide/src/Development/IDE/Core/OfInterest.hs b/ghcide/src/Development/IDE/Core/OfInterest.hs index 17858544c2..599947659b 100644 --- a/ghcide/src/Development/IDE/Core/OfInterest.hs +++ b/ghcide/src/Development/IDE/Core/OfInterest.hs @@ -55,7 +55,7 @@ data Log = LogShake Shake.Log instance Pretty Log where pretty = \case - LogShake log -> pretty log + LogShake msg -> pretty msg newtype OfInterestVar = OfInterestVar (Var (HashMap NormalizedFilePath FileOfInterestStatus)) diff --git a/ghcide/src/Development/IDE/Core/PluginUtils.hs b/ghcide/src/Development/IDE/Core/PluginUtils.hs index 2b2bf2192b..76c88421c9 100644 --- a/ghcide/src/Development/IDE/Core/PluginUtils.hs +++ b/ghcide/src/Development/IDE/Core/PluginUtils.hs @@ -1,5 +1,29 @@ {-# LANGUAGE GADTs #-} -module Development.IDE.Core.PluginUtils where +module Development.IDE.Core.PluginUtils +(-- Wrapped Action functions + runActionE +, runActionMT +, useE +, useMT +, usesE +, usesMT +, useWithStaleE +, useWithStaleMT +-- Wrapped IdeAction functions +, runIdeActionE +, runIdeActionMT +, useWithStaleFastE +, useWithStaleFastMT +, uriToFilePathE +-- Wrapped PositionMapping functions +, toCurrentPositionE +, toCurrentPositionMT +, fromCurrentPositionE +, fromCurrentPositionMT +, toCurrentRangeE +, toCurrentRangeMT +, fromCurrentRangeE +, fromCurrentRangeMT) where import Control.Monad.Extra import Control.Monad.IO.Class diff --git a/ghcide/src/Development/IDE/Core/PositionMapping.hs b/ghcide/src/Development/IDE/Core/PositionMapping.hs index b80e515cc2..82d8334c87 100644 --- a/ghcide/src/Development/IDE/Core/PositionMapping.hs +++ b/ghcide/src/Development/IDE/Core/PositionMapping.hs @@ -104,7 +104,7 @@ zeroMapping :: PositionMapping zeroMapping = PositionMapping idDelta -- | Compose two position mappings. Composes in the same way as function --- composition (ie the second argument is applyed to the position first). +-- composition (ie the second argument is applied to the position first). composeDelta :: PositionDelta -> PositionDelta -> PositionDelta @@ -219,9 +219,9 @@ deltaFromDiff (T.lines -> old) (T.lines -> new) = line' -> PositionExact (Position (fromIntegral line') col) -- Construct a mapping between lines in the diff - -- -1 for unsucessful mapping + -- -1 for unsuccessful mapping go :: [Diff T.Text] -> Int -> Int -> ([Int], [Int]) go [] _ _ = ([],[]) - go (Both _ _ : xs) !lold !lnew = bimap (lnew :) (lold :) $ go xs (lold+1) (lnew+1) - go (First _ : xs) !lold !lnew = first (-1 :) $ go xs (lold+1) lnew - go (Second _ : xs) !lold !lnew = second (-1 :) $ go xs lold (lnew+1) + go (Both _ _ : xs) !glold !glnew = bimap (glnew :) (glold :) $ go xs (glold+1) (glnew+1) + go (First _ : xs) !glold !glnew = first (-1 :) $ go xs (glold+1) glnew + go (Second _ : xs) !glold !glnew = second (-1 :) $ go xs glold (glnew+1) diff --git a/ghcide/src/Development/IDE/Core/Preprocessor.hs b/ghcide/src/Development/IDE/Core/Preprocessor.hs index 577e351678..24a754870d 100644 --- a/ghcide/src/Development/IDE/Core/Preprocessor.hs +++ b/ghcide/src/Development/IDE/Core/Preprocessor.hs @@ -30,9 +30,11 @@ import Development.IDE.Types.Location import qualified GHC.LanguageExtensions as LangExt import System.FilePath import System.IO.Extra + +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + #if MIN_VERSION_ghc(9,3,0) import GHC.Utils.Logger (LogFlags (..)) -import GHC.Utils.Outputable (renderWithContext) #endif -- | Given a file and some contents, apply any necessary preprocessors, @@ -54,17 +56,17 @@ preprocessor env filename mbContents = do !src_hash <- liftIO $ Util.fingerprintFromStringBuffer contents -- Perform cpp - (opts, env) <- ExceptT $ parsePragmasIntoHscEnv env filename contents - let dflags = hsc_dflags env - let logger = hsc_logger env - (isOnDisk, contents, opts, env) <- + (opts, pEnv) <- ExceptT $ parsePragmasIntoHscEnv env filename contents + let dflags = hsc_dflags pEnv + let logger = hsc_logger pEnv + (newIsOnDisk, newContents, newOpts, newEnv) <- if not $ xopt LangExt.Cpp dflags then - return (isOnDisk, contents, opts, env) + return (isOnDisk, contents, opts, pEnv) else do cppLogs <- liftIO $ newIORef [] let newLogger = pushLogHook (const (logActionCompat $ logAction cppLogs)) logger - contents <- ExceptT - $ (Right <$> (runCpp (putLogHook newLogger env) filename + con <- ExceptT + $ (Right <$> (runCpp (putLogHook newLogger pEnv) filename $ if isOnDisk then Nothing else Just contents)) `catch` ( \(e :: Util.GhcException) -> do @@ -73,25 +75,25 @@ preprocessor env filename mbContents = do [] -> throw e diags -> return $ Left diags ) - (opts, env) <- ExceptT $ parsePragmasIntoHscEnv env filename contents - return (False, contents, opts, env) + (options, hscEnv) <- ExceptT $ parsePragmasIntoHscEnv pEnv filename con + return (False, con, options, hscEnv) -- Perform preprocessor if not $ gopt Opt_Pp dflags then - return (contents, opts, env, src_hash) + return (newContents, newOpts, newEnv, src_hash) else do - contents <- liftIO $ runPreprocessor env filename $ if isOnDisk then Nothing else Just contents - (opts, env) <- ExceptT $ parsePragmasIntoHscEnv env filename contents - return (contents, opts, env, src_hash) + con <- liftIO $ runPreprocessor newEnv filename $ if newIsOnDisk then Nothing else Just newContents + (options, hscEnv) <- ExceptT $ parsePragmasIntoHscEnv newEnv filename con + return (con, options, hscEnv, src_hash) where logAction :: IORef [CPPLog] -> LogActionCompat logAction cppLogs dflags _reason severity srcSpan _style msg = do #if MIN_VERSION_ghc(9,3,0) - let log = CPPLog (fromMaybe SevWarning severity) srcSpan $ T.pack $ renderWithContext (log_default_user_context dflags) msg + let cppLog = CPPLog (fromMaybe SevWarning severity) srcSpan $ T.pack $ renderWithContext (log_default_user_context dflags) msg #else - let log = CPPLog severity srcSpan $ T.pack $ showSDoc dflags msg + let cppLog = CPPLog severity srcSpan $ T.pack $ showSDoc dflags msg #endif - modifyIORef cppLogs (log :) + modifyIORef cppLogs (cppLog :) @@ -118,12 +120,12 @@ diagsFromCPPLogs filename logs = -- informational log messages and attaches them to the initial log message. go :: [CPPDiag] -> [CPPLog] -> [CPPDiag] go acc [] = reverse $ map (\d -> d {cdMessage = reverse $ cdMessage d}) acc - go acc (CPPLog sev (RealSrcSpan span _) msg : logs) = - let diag = CPPDiag (realSrcSpanToRange span) (toDSeverity sev) [msg] - in go (diag : acc) logs - go (diag : diags) (CPPLog _sev (UnhelpfulSpan _) msg : logs) = - go (diag {cdMessage = msg : cdMessage diag} : diags) logs - go [] (CPPLog _sev (UnhelpfulSpan _) _msg : logs) = go [] logs + go acc (CPPLog sev (RealSrcSpan rSpan _) msg : gLogs) = + let diag = CPPDiag (realSrcSpanToRange rSpan) (toDSeverity sev) [msg] + in go (diag : acc) gLogs + go (diag : diags) (CPPLog _sev (UnhelpfulSpan _) msg : gLogs) = + go (diag {cdMessage = msg : cdMessage diag} : diags) gLogs + go [] (CPPLog _sev (UnhelpfulSpan _) _msg : gLogs) = go [] gLogs cppDiagToDiagnostic :: CPPDiag -> Diagnostic cppDiagToDiagnostic d = Diagnostic @@ -196,12 +198,12 @@ runLhs env filename contents = withTempDir $ \dir -> do -- | Run CPP on a file runCpp :: HscEnv -> FilePath -> Maybe Util.StringBuffer -> IO Util.StringBuffer -runCpp env0 filename contents = withTempDir $ \dir -> do +runCpp env0 filename mbContents = withTempDir $ \dir -> do let out = dir takeFileName filename <.> "out" let dflags1 = addOptP "-D__GHCIDE__" (hsc_dflags env0) let env1 = hscSetFlags dflags1 env0 - case contents of + case mbContents of Nothing -> do -- Happy case, file is not modified, so run CPP on it in-place -- which also makes things like relative #include files work @@ -225,21 +227,21 @@ runCpp env0 filename contents = withTempDir $ \dir -> do -- Fix up the filename in lines like: -- # 1 "C:/Temp/extra-dir-914611385186/___GHCIDE_MAGIC___" let tweak x - | Just x <- stripPrefix "# " x - , "___GHCIDE_MAGIC___" `isInfixOf` x - , let num = takeWhile (not . isSpace) x + | Just y <- stripPrefix "# " x + , "___GHCIDE_MAGIC___" `isInfixOf` y + , let num = takeWhile (not . isSpace) y -- important to use /, and never \ for paths, even on Windows, since then C escapes them -- and GHC gets all confused - = "# " <> num <> " \"" <> map (\x -> if isPathSeparator x then '/' else x) filename <> "\"" + = "# " <> num <> " \"" <> map (\z -> if isPathSeparator z then '/' else z) filename <> "\"" | otherwise = x Util.stringToStringBuffer . unlines . map tweak . lines <$> readFileUTF8' out -- | Run a preprocessor on a file runPreprocessor :: HscEnv -> FilePath -> Maybe Util.StringBuffer -> IO Util.StringBuffer -runPreprocessor env filename contents = withTempDir $ \dir -> do +runPreprocessor env filename mbContents = withTempDir $ \dir -> do let out = dir takeFileName filename <.> "out" - inp <- case contents of + inp <- case mbContents of Nothing -> return filename Just contents -> do let inp = dir takeFileName filename <.> "hs" diff --git a/ghcide/src/Development/IDE/Core/ProgressReporting.hs b/ghcide/src/Development/IDE/Core/ProgressReporting.hs index 598e4d649b..83d4670782 100644 --- a/ghcide/src/Development/IDE/Core/ProgressReporting.hs +++ b/ghcide/src/Development/IDE/Core/ProgressReporting.hs @@ -18,7 +18,7 @@ import Control.Concurrent.STM.Stats (TVar, atomicallyNamed, modifyTVar', newTVarIO, readTVarIO) import Control.Concurrent.Strict -import Control.Monad.Extra +import Control.Monad.Extra hiding (loop) import Control.Monad.IO.Class import Control.Monad.Trans.Class (lift) import Data.Aeson (ToJSON (toJSON)) @@ -112,7 +112,7 @@ delayedProgressReporting -> Maybe (LSP.LanguageContextEnv c) -> ProgressReportingStyle -> IO ProgressReporting -delayedProgressReporting before after Nothing optProgressStyle = noProgressReporting +delayedProgressReporting _before _after Nothing _optProgressStyle = noProgressReporting delayedProgressReporting before after (Just lspEnv) optProgressStyle = do inProgressState <- newInProgress progressState <- newVar NotStarted @@ -136,9 +136,9 @@ delayedProgressReporting before after (Just lspEnv) optProgressStyle = do ready <- waitBarrier b LSP.runLspT lspEnv $ for_ ready $ const $ bracket_ (start u) (stop u) (loop u 0) where - start id = LSP.sendNotification SMethod_Progress $ + start token = LSP.sendNotification SMethod_Progress $ LSP.ProgressParams - { _token = id + { _token = token , _value = toJSON $ WorkDoneProgressBegin { _kind = AString @"begin" , _title = "Processing" @@ -147,9 +147,9 @@ delayedProgressReporting before after (Just lspEnv) optProgressStyle = do , _percentage = Nothing } } - stop id = LSP.sendNotification SMethod_Progress + stop token = LSP.sendNotification SMethod_Progress LSP.ProgressParams - { _token = id + { _token = token , _value = toJSON $ WorkDoneProgressEnd { _kind = AString @"end" , _message = Nothing @@ -157,11 +157,11 @@ delayedProgressReporting before after (Just lspEnv) optProgressStyle = do } loop _ _ | optProgressStyle == NoProgress = forever $ liftIO $ threadDelay maxBound - loop id prevPct = do + loop token prevPct = do done <- liftIO $ readTVarIO doneVar todo <- liftIO $ readTVarIO todoVar liftIO $ sleep after - if todo == 0 then loop id 0 else do + if todo == 0 then loop token 0 else do let nextFrac :: Double nextFrac = fromIntegral done / fromIntegral todo @@ -170,7 +170,7 @@ delayedProgressReporting before after (Just lspEnv) optProgressStyle = do when (nextPct /= prevPct) $ LSP.sendNotification SMethod_Progress $ LSP.ProgressParams - { _token = id + { _token = token , _value = case optProgressStyle of Explicit -> toJSON $ WorkDoneProgressReport { _kind = AString @"report" @@ -186,7 +186,7 @@ delayedProgressReporting before after (Just lspEnv) optProgressStyle = do } NoProgress -> error "unreachable" } - loop id nextPct + loop token nextPct updateStateForFile inProgress file = actionBracket (f succ) (const $ f pred) . const -- This functions are deliberately eta-expanded to avoid space leaks. diff --git a/ghcide/src/Development/IDE/Core/Rules.hs b/ghcide/src/Development/IDE/Core/Rules.hs index 13ad47900a..1ce358fb88 100644 --- a/ghcide/src/Development/IDE/Core/Rules.hs +++ b/ghcide/src/Development/IDE/Core/Rules.hs @@ -61,26 +61,25 @@ module Development.IDE.Core.Rules( DisplayTHWarning(..), ) where +import Prelude hiding (mod) import Control.Applicative import Control.Concurrent.Async (concurrently) import Control.Concurrent.Strict import Control.DeepSeq import Control.Exception.Safe import Control.Exception (evaluate) -import Control.Monad.Extra -import Control.Monad.Reader -import Control.Monad.State +import Control.Monad.Extra hiding (msum) +import Control.Monad.Reader hiding (msum) +import Control.Monad.State hiding (msum) import Control.Monad.Trans.Except (ExceptT, except, runExceptT) import Control.Monad.Trans.Maybe -import Data.Aeson (Result (Success), - toJSON) -import qualified Data.Aeson.Types as A +import Data.Aeson (toJSON) import qualified Data.Binary as B import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as LBS import Data.Coerce -import Data.Foldable +import Data.Foldable hiding (msum) import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HashSet import Data.Hashable @@ -116,7 +115,7 @@ import Development.IDE.GHC.Compat hiding TargetId(..), loadInterface, Var, - (<+>)) + (<+>), settings) import qualified Development.IDE.GHC.Compat as Compat hiding (vcat, nest) import qualified Development.IDE.GHC.Compat.Util as Util import Development.IDE.GHC.Error @@ -146,9 +145,8 @@ import Ide.Plugin.Properties (HasProperty, Properties, ToHsType, useProperty) -import Ide.PluginUtils (configForPlugin) import Ide.Types (DynFlagsModifications (dynFlagsModifyGlobal, dynFlagsModifyParser), - PluginId, PluginDescriptor (pluginId), IdePlugins (IdePlugins)) + PluginId) import Control.Concurrent.STM.Stats (atomically) import Language.LSP.Server (LspT) import System.Info.Extra (isWindows) @@ -157,19 +155,23 @@ import Ide.Logger (Recorder, logWith, cmapWithPrio, WithPriority, Pretty (pretty import qualified Development.IDE.Core.Shake as Shake import qualified Ide.Logger as Logger import qualified Development.IDE.Types.Shake as Shake -import Development.IDE.GHC.CoreFile import Data.Time.Clock.POSIX (posixSecondsToUTCTime) import Control.Monad.IO.Unlift -import qualified Data.IntMap as IM -#if MIN_VERSION_ghc(9,3,0) -import GHC.Unit.Module.Graph -import GHC.Unit.Env + + +import GHC.Fingerprint + +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,3,0) +import GHC (mgModSummaries) #endif -#if MIN_VERSION_ghc(9,5,0) -import GHC.Unit.Home.ModInfo + +#if MIN_VERSION_ghc(9,3,0) +import qualified Data.IntMap as IM #endif -import GHC (mgModSummaries) -import GHC.Fingerprint + + data Log = LogShake Shake.Log @@ -182,7 +184,7 @@ data Log instance Pretty Log where pretty = \case - LogShake log -> pretty log + LogShake msg -> pretty msg LogReindexingHieFile path -> "Re-indexing hie file for" <+> pretty (fromNormalizedFilePath path) LogLoadingHieFile path -> @@ -334,10 +336,10 @@ getParsedModuleWithCommentsRule recorder = let ms' = withoutOption Opt_Haddock $ withOption Opt_KeepRawTokenStream ms modify_dflags <- getModifyDynFlags dynFlagsModifyParser - let ms = ms' { ms_hspp_opts = modify_dflags $ ms_hspp_opts ms' } + let ms'' = ms' { ms_hspp_opts = modify_dflags $ ms_hspp_opts ms' } reset_ms pm = pm { pm_mod_summary = ms' } - liftIO $ fmap (fmap reset_ms) $ snd <$> getParsedModuleDefinition hsc opt file ms + liftIO $ fmap (fmap reset_ms) $ snd <$> getParsedModuleDefinition hsc opt file ms'' getModifyDynFlags :: (DynFlagsModifications -> a) -> Action a getModifyDynFlags f = do @@ -370,7 +372,7 @@ getLocatedImportsRule recorder = let import_dirs = deps env_eq let dflags = hsc_dflags env isImplicitCradle = isNothing $ envImportPaths env_eq - dflags <- return $ if isImplicitCradle + dflags' <- return $ if isImplicitCradle then addRelativeImport file (moduleName $ ms_mod ms) dflags else dflags opt <- getIdeOptions @@ -391,7 +393,7 @@ getLocatedImportsRule recorder = | otherwise = return Nothing (diags, imports') <- fmap unzip $ forM imports $ \(isSource, (mbPkgName, modName)) -> do - diagOrImp <- locateModule (hscSetFlags dflags env) import_dirs (optExtensions opt) getTargetFor modName mbPkgName isSource + diagOrImp <- locateModule (hscSetFlags dflags' env) import_dirs (optExtensions opt) getTargetFor modName mbPkgName isSource case diagOrImp of Left diags -> pure (diags, Just (modName, Nothing)) Right (FileImport path) -> pure ([], Just (modName, Just path)) @@ -405,7 +407,7 @@ getLocatedImportsRule recorder = bootArtifact <- if boot == Just True then do let modName = ms_mod_name ms - loc <- liftIO $ mkHomeModLocation dflags modName (fromNormalizedFilePath bootPath) + loc <- liftIO $ mkHomeModLocation dflags' modName (fromNormalizedFilePath bootPath) return $ Just (noLoc modName, Just (ArtifactsLocation bootPath (Just loc) True)) else pure Nothing -} @@ -536,9 +538,8 @@ reportImportCyclesRule recorder = let cycles = mapMaybe (cycleErrorInFile fileId) (toList errs) -- Convert cycles of files into cycles of module names forM cycles $ \(imp, files) -> do - modNames <- forM files $ \fileId -> do - let file = idToPath depPathIdMap fileId - getModuleName file + modNames <- forM files $ + getModuleName . idToPath depPathIdMap pure $ toDiag imp $ sort modNames where cycleErrorInFile f (PartOfCycle imp fs) | f `elem` fs = Just (imp, fs) @@ -649,10 +650,9 @@ readHieFileFromDisk :: Recorder (WithPriority Log) -> FilePath -> ExceptT SomeEx readHieFileFromDisk recorder hie_loc = do nc <- asks ideNc res <- liftIO $ tryAny $ loadHieFile (mkUpdater nc) hie_loc - let log = (liftIO .) . logWith recorder case res of - Left e -> log Logger.Debug $ LogLoadingHieFileFail hie_loc e - Right _ -> log Logger.Debug $ LogLoadingHieFileSuccess hie_loc + Left e -> liftIO $ logWith recorder Logger.Debug $ LogLoadingHieFileFail hie_loc e + Right _ -> liftIO $ logWith recorder Logger.Debug $ LogLoadingHieFileSuccess hie_loc except res -- | Typechecks a module. @@ -852,7 +852,6 @@ getModIfaceFromDiskRule recorder = defineEarlyCutoff (cmapWithPrio LogShake reco Just session -> do linkableType <- getLinkableType f ver <- use_ GetModificationTime f - ShakeExtras{ideNc} <- getShakeExtras let m_old = case old of Shake.Succeeded (Just old_version) v -> Just (v, old_version) Shake.Stale _ (Just old_version) v -> Just (v, old_version) @@ -889,12 +888,12 @@ getModIfaceFromDiskAndIndexRule recorder = -- GetModIfaceFromDisk should have written a `.hie` file, must check if it matches version in db let ms = hirModSummary x hie_loc = Compat.ml_hie_file $ ms_location ms - hash <- liftIO $ Util.getFileHash hie_loc + fileHash <- liftIO $ Util.getFileHash hie_loc mrow <- liftIO $ withHieDb (\hieDb -> HieDb.lookupHieFileFromSource hieDb (fromNormalizedFilePath f)) hie_loc' <- liftIO $ traverse (makeAbsolute . HieDb.hieModuleHieFile) mrow case mrow of Just row - | hash == HieDb.modInfoHash (HieDb.hieModInfo row) + | fileHash == HieDb.modInfoHash (HieDb.hieModInfo row) && Just hie_loc == hie_loc' -> do -- All good, the db has indexed the file @@ -911,7 +910,7 @@ getModIfaceFromDiskAndIndexRule recorder = -- can just re-index the file we read from disk Right hf -> liftIO $ do logWith recorder Logger.Debug $ LogReindexingHieFile f - indexHieFile se ms f hash hf + indexHieFile se ms f fileHash hf return (Just x) @@ -955,8 +954,8 @@ getModSummaryRule displayTHWarning recorder = do Left diags -> return (Nothing, (diags, Nothing)) defineEarlyCutoff (cmapWithPrio LogShake recorder) $ RuleNoDiagnostics $ \GetModSummaryWithoutTimestamps f -> do - ms <- use GetModSummary f - case ms of + mbMs <- use GetModSummary f + case mbMs of Just res@ModSummaryResult{..} -> do let ms = msrModSummary { #if !MIN_VERSION_ghc(9,3,0) @@ -982,7 +981,7 @@ generateCoreRule recorder = getModIfaceRule :: Recorder (WithPriority Log) -> Rules () getModIfaceRule recorder = defineEarlyCutoff (cmapWithPrio LogShake recorder) $ Rule $ \GetModIface f -> do fileOfInterest <- use_ IsFileOfInterest f - res@(_,(_,mhmi)) <- case fileOfInterest of + res <- case fileOfInterest of IsFOI status -> do -- Never load from disk for files of interest tmr <- use_ TypeCheck f @@ -990,14 +989,14 @@ getModIfaceRule recorder = defineEarlyCutoff (cmapWithPrio LogShake recorder) $ hsc <- hscEnv <$> use_ GhcSessionDeps f let compile = fmap ([],) $ use GenerateCore f se <- getShakeExtras - (diags, !hiFile) <- writeCoreFileIfNeeded se hsc linkableType compile tmr - let fp = hiFileFingerPrint <$> hiFile - hiDiags <- case hiFile of + (diags, !mbHiFile) <- writeCoreFileIfNeeded se hsc linkableType compile tmr + let fp = hiFileFingerPrint <$> mbHiFile + hiDiags <- case mbHiFile of Just hiFile | OnDisk <- status , not (tmrDeferredError tmr) -> liftIO $ writeHiFile se hsc hiFile _ -> pure [] - return (fp, (diags++hiDiags, hiFile)) + return (fp, (diags++hiDiags, mbHiFile)) NotFOI -> do hiFile <- use GetModIfaceFromDiskAndIndex f let fp = hiFileFingerPrint <$> hiFile @@ -1029,22 +1028,22 @@ regenerateHiFile sess f ms compNeeded = do -- Embed haddocks in the interface file (diags, mb_pm) <- liftIO $ getParsedModuleDefinition hsc opt f (withOptHaddock ms) - (diags, mb_pm) <- + (diags', mb_pm') <- -- We no longer need to parse again if GHC version is above 9.0. https://github.com/haskell/haskell-language-server/issues/1892 if Compat.ghcVersion >= Compat.GHC90 || isJust mb_pm then do return (diags, mb_pm) else do -- if parsing fails, try parsing again with Haddock turned off - (diagsNoHaddock, mb_pm) <- liftIO $ getParsedModuleDefinition hsc opt f ms - return (mergeParseErrorsHaddock diagsNoHaddock diags, mb_pm) - case mb_pm of - Nothing -> return (diags, Nothing) + (diagsNoHaddock, mb_pm') <- liftIO $ getParsedModuleDefinition hsc opt f ms + return (mergeParseErrorsHaddock diagsNoHaddock diags, mb_pm') + case mb_pm' of + Nothing -> return (diags', Nothing) Just pm -> do -- Invoke typechecking directly to update it without incurring a dependency -- on the parsed module and the typecheck rules - (diags', mtmr) <- typeCheckRuleDefinition hsc pm + (diags'', mtmr) <- typeCheckRuleDefinition hsc pm case mtmr of - Nothing -> pure (diags', Nothing) + Nothing -> pure (diags'', Nothing) Just tmr -> do let compile = liftIO $ compileModule (RunSimplifier True) hsc (pm_mod_summary pm) $ tmrTypechecked tmr @@ -1052,7 +1051,7 @@ regenerateHiFile sess f ms compNeeded = do se <- getShakeExtras -- Bang pattern is important to avoid leaking 'tmr' - (diags'', !res) <- writeCoreFileIfNeeded se hsc compNeeded compile tmr + (diags''', !res) <- writeCoreFileIfNeeded se hsc compNeeded compile tmr -- Write hi file hiDiags <- case res of @@ -1061,22 +1060,22 @@ regenerateHiFile sess f ms compNeeded = do -- Write hie file. Do this before writing the .hi file to -- ensure that we always have a up2date .hie file if we have -- a .hi file - se <- getShakeExtras + se' <- getShakeExtras (gDiags, masts) <- liftIO $ generateHieAsts hsc tmr source <- getSourceFileSource f wDiags <- forM masts $ \asts -> - liftIO $ writeAndIndexHieFile hsc se (tmrModSummary tmr) f (tcg_exports $ tmrTypechecked tmr) asts source + liftIO $ writeAndIndexHieFile hsc se' (tmrModSummary tmr) f (tcg_exports $ tmrTypechecked tmr) asts source -- We don't write the `.hi` file if there are deferred errors, since we won't get -- accurate diagnostics next time if we do hiDiags <- if not $ tmrDeferredError tmr - then liftIO $ writeHiFile se hsc hiFile + then liftIO $ writeHiFile se' hsc hiFile else pure [] pure (hiDiags <> gDiags <> concat wDiags) Nothing -> pure [] - return (diags <> diags' <> diags'' <> hiDiags, res) + return (diags' <> diags'' <> diags''' <> hiDiags, res) -- | HscEnv should have deps included already @@ -1126,10 +1125,8 @@ getLinkableRule recorder = core_t <- liftIO $ getModTime core_file case hirCoreFp of Nothing -> error "called GetLinkable for a file without a linkable" - Just (bin_core, hash) -> do + Just (bin_core, fileHash) -> do session <- use_ GhcSessionDeps f - ShakeExtras{ideNc} <- getShakeExtras - let namecache_updater = mkUpdater ideNc linkableType <- getLinkableType f >>= \case Nothing -> error "called GetLinkable for a file which doesn't need compilation" Just t -> pure t @@ -1165,9 +1162,9 @@ getLinkableRule recorder = --just before returning it to be loaded. This has a substantial effect on recompile --times as the number of loaded modules and splices increases. -- - unload (hscEnv session) (map (\(mod, time) -> LM time mod []) $ moduleEnvToList to_keep) + unload (hscEnv session) (map (\(mod', time') -> LM time' mod' []) $ moduleEnvToList to_keep) return (to_keep, ()) - return (hash <$ hmi, (warns, LinkableResult <$> hmi <*> pure hash)) + return (fileHash <$ hmi, (warns, LinkableResult <$> hmi <*> pure fileHash)) -- | For now we always use bytecode unless something uses unboxed sums and tuples along with TH getLinkableType :: NormalizedFilePath -> Action (Maybe LinkableType) @@ -1224,11 +1221,11 @@ computeLinkableTypeForDynFlags d #if defined(GHC_PATCHED_UNBOXED_BYTECODE) || MIN_VERSION_ghc(9,2,0) = BCOLinkable #else - | unboxed_tuples_or_sums = ObjectLinkable + | _unboxed_tuples_or_sums = ObjectLinkable | otherwise = BCOLinkable #endif - where - unboxed_tuples_or_sums = + where -- unboxed_tuples_or_sums is only used in GHC < 9.2 + _unboxed_tuples_or_sums = xopt LangExt.UnboxedTuples d || xopt LangExt.UnboxedSums d -- | Tracks which linkables are current, so we don't need to unload them diff --git a/ghcide/src/Development/IDE/Core/Service.hs b/ghcide/src/Development/IDE/Core/Service.hs index e88dd341ab..3efbd7e2d5 100644 --- a/ghcide/src/Development/IDE/Core/Service.hs +++ b/ghcide/src/Development/IDE/Core/Service.hs @@ -52,9 +52,9 @@ data Log instance Pretty Log where pretty = \case - LogShake log -> pretty log - LogOfInterest log -> pretty log - LogFileExists log -> pretty log + LogShake msg -> pretty msg + LogOfInterest msg -> pretty msg + LogFileExists msg -> pretty msg ------------------------------------------------------------ -- Exposed API diff --git a/ghcide/src/Development/IDE/Core/Shake.hs b/ghcide/src/Development/IDE/Core/Shake.hs index c70315c2fe..c413729ab1 100644 --- a/ghcide/src/Development/IDE/Core/Shake.hs +++ b/ghcide/src/Development/IDE/Core/Shake.hs @@ -86,12 +86,14 @@ import Control.Concurrent.STM.Stats (atomicallyNamed) import Control.Concurrent.Strict import Control.DeepSeq import Control.Exception.Extra hiding (bracket_) +import Control.Lens ((&), (?~)) import Control.Monad.Extra import Control.Monad.IO.Class import Control.Monad.Reader import Control.Monad.Trans.Maybe import Data.Aeson (Result (Success), toJSON) +import qualified Data.Aeson.Types as A import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Char8 as BS8 import Data.Coerce (coerce) @@ -99,14 +101,13 @@ import Data.Default import Data.Dynamic import Data.EnumMap.Strict (EnumMap) import qualified Data.EnumMap.Strict as EM -import Data.Foldable (find, for_, toList) +import Data.Foldable (find, for_) import Data.Functor ((<&>)) import Data.Functor.Identity import Data.Hashable import qualified Data.HashMap.Strict as HMap import Data.HashSet (HashSet) import qualified Data.HashSet as HSet -import Data.IORef import Data.List.Extra (foldl', partition, takeEnd) import qualified Data.Map.Strict as Map @@ -130,14 +131,10 @@ import Development.IDE.Core.Tracing import Development.IDE.GHC.Compat (NameCache, NameCacheUpdater (..), initNameCache, - knownKeyNames, - mkSplitUniqSupply) -#if !MIN_VERSION_ghc(9,3,0) -import Development.IDE.GHC.Compat (upNameCache) -#endif -import qualified Data.Aeson.Types as A + knownKeyNames) import Development.IDE.GHC.Orphans () -import Development.IDE.Graph hiding (ShakeValue) +import Development.IDE.Graph hiding (ShakeValue, + action) import qualified Development.IDE.Graph as Shake import Development.IDE.Graph.Database (ShakeDatabase, shakeGetBuildStep, @@ -148,7 +145,7 @@ import Development.IDE.Graph.Database (ShakeDatabase, import Development.IDE.Graph.Rule import Development.IDE.Types.Action import Development.IDE.Types.Diagnostics -import Development.IDE.Types.Exports +import Development.IDE.Types.Exports hiding (exportsMapSize) import qualified Development.IDE.Types.Exports as ExportsMap import Development.IDE.Types.KnownTargets import Development.IDE.Types.Location @@ -167,18 +164,27 @@ import Ide.Types (IdePlugins (IdePlugins) PluginDescriptor (pluginId), PluginId) import Language.LSP.Diagnostics +import qualified Language.LSP.Protocol.Lens as L import Language.LSP.Protocol.Message import Language.LSP.Protocol.Types import qualified Language.LSP.Protocol.Types as LSP import qualified Language.LSP.Server as LSP import Language.LSP.VFS hiding (start) import qualified "list-t" ListT -import OpenTelemetry.Eventlog +import OpenTelemetry.Eventlog hiding (addEvent) import qualified StmContainers.Map as STM import System.FilePath hiding (makeRelative) import System.IO.Unsafe (unsafePerformIO) import System.Time.Extra +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,3,0) +import Data.IORef +import Development.IDE.GHC.Compat (mkSplitUniqSupply, + upNameCache) +#endif + data Log = LogCreateHieDbExportsMapStart | LogCreateHieDbExportsMapFinish !Int @@ -205,10 +211,10 @@ instance Pretty Log where , "Aborting previous build session took" <+> pretty (showDuration abortDuration) <+> pretty shakeProfilePath ] LogBuildSessionRestartTakingTooLong seconds -> "Build restart is taking too long (" <> pretty seconds <> " seconds)" - LogDelayedAction delayedAction duration -> + LogDelayedAction delayedAct seconds -> hsep - [ "Finished:" <+> pretty (actionName delayedAction) - , "Took:" <+> pretty (showDuration duration) ] + [ "Finished:" <+> pretty (actionName delayedAct) + , "Took:" <+> pretty (showDuration seconds) ] LogBuildSessionFinish e -> vcat [ "Finished build session" @@ -379,9 +385,9 @@ getIdeGlobalExtras ShakeExtras{globals} = do let typ = typeRep (Proxy :: Proxy a) x <- HMap.lookup (typeRep (Proxy :: Proxy a)) <$> readTVarIO globals case x of - Just x - | Just x <- fromDynamic x -> pure x - | otherwise -> errorIO $ "Internal error, getIdeGlobalExtras, wrong type for " ++ show typ ++ " (got " ++ show (dynTypeRep x) ++ ")" + Just y + | Just z <- fromDynamic y -> pure z + | otherwise -> errorIO $ "Internal error, getIdeGlobalExtras, wrong type for " ++ show typ ++ " (got " ++ show (dynTypeRep y) ++ ")" Nothing -> errorIO $ "Internal error, getIdeGlobalExtras, no entry for " ++ show typ getIdeGlobalAction :: forall a . (HasCallStack, IsIdeGlobal a) => Action a @@ -396,8 +402,8 @@ instance IsIdeGlobal GlobalIdeOptions getIdeOptions :: Action IdeOptions getIdeOptions = do GlobalIdeOptions x <- getIdeGlobalAction - env <- lspEnv <$> getShakeExtras - case env of + mbEnv <- lspEnv <$> getShakeExtras + case mbEnv of Nothing -> return x Just env -> do config <- liftIO $ LSP.runLspT env HLS.getClientConfig @@ -429,8 +435,8 @@ lastValueIO s@ShakeExtras{positionMapping,persistentKeys,state} k file = do Nothing -> atomicallyNamed "lastValueIO 1" $ do STM.focus (Focus.alter (alterValue $ Failed True)) (toKey k file) state return Nothing - Just (v,del,ver) -> do - actual_version <- case ver of + Just (v,del,mbVer) -> do + actual_version <- case mbVer of Just ver -> pure (Just $ VFSVersion ver) Nothing -> (Just . ModificationTime <$> getModTime (fromNormalizedFilePath file)) `catch` (\(_ :: IOException) -> pure Nothing) @@ -448,7 +454,7 @@ lastValueIO s@ShakeExtras{positionMapping,persistentKeys,state} k file = do atomicallyNamed "lastValueIO 4" (STM.lookup (toKey k file) state) >>= \case Nothing -> readPersistent - Just (ValueWithDiagnostics v _) -> case v of + Just (ValueWithDiagnostics value _) -> case value of Succeeded ver (fromDynamic -> Just v) -> atomicallyNamed "lastValueIO 5" $ Just . (v,) <$> mappingForVersion positionMapping file ver Stale del ver (fromDynamic -> Just v) -> @@ -599,8 +605,6 @@ shakeOpen recorder lspEnv defaultConfig idePlugins logger debouncer shakeProfileDir (IdeReportProgress reportProgress) ideTesting@(IdeTesting testing) withHieDb indexQueue opts monitoring rules = mdo - let log :: Logger.Priority -> Log -> IO () - log = logWith recorder #if MIN_VERSION_ghc(9,3,0) ideNc <- initNameCache 'r' knownKeyNames @@ -626,10 +630,10 @@ shakeOpen recorder lspEnv defaultConfig idePlugins logger debouncer -- lazily initialize the exports map with the contents of the hiedb -- TODO: exceptions can be swallowed here? _ <- async $ do - log Debug LogCreateHieDbExportsMapStart + logWith recorder Debug LogCreateHieDbExportsMapStart em <- createExportsMapHieDb withHieDb atomically $ modifyTVar' exportsMap (<> em) - log Debug $ LogCreateHieDbExportsMapFinish (ExportsMap.size em) + logWith recorder Debug $ LogCreateHieDbExportsMapFinish (ExportsMap.size em) progress <- do let (before, after) = if testing then (0,0.1) else (0.1,0.1) @@ -732,14 +736,13 @@ shakeRestart recorder IdeState{..} vfs reason acts = withMVar' shakeSession (\runner -> do - let log = logWith recorder - (stopTime,()) <- duration $ logErrorAfter 10 recorder $ cancelShakeSession runner + (stopTime,()) <- duration $ logErrorAfter 10 $ cancelShakeSession runner res <- shakeDatabaseProfile shakeDb backlog <- readTVarIO $ dirtyKeys shakeExtras queue <- atomicallyNamed "actionQueue - peek" $ peekInProgress $ actionQueue shakeExtras -- this log is required by tests - log Debug $ LogBuildSessionRestart reason queue backlog stopTime res + logWith recorder Debug $ LogBuildSessionRestart reason queue backlog stopTime res ) -- It is crucial to be masked here, otherwise we can get killed -- between spawning the new thread and updating shakeSession. @@ -747,8 +750,8 @@ shakeRestart recorder IdeState{..} vfs reason acts = (\() -> do (,()) <$> newSession recorder shakeExtras vfs shakeDb acts reason) where - logErrorAfter :: Seconds -> Recorder (WithPriority Log) -> IO () -> IO () - logErrorAfter seconds recorder action = flip withAsync (const action) $ do + logErrorAfter :: Seconds -> IO () -> IO () + logErrorAfter seconds action = flip withAsync (const action) $ do sleep seconds logWith recorder Error (LogBuildSessionRestartTakingTooLong seconds) @@ -761,8 +764,8 @@ shakeEnqueue :: ShakeExtras -> DelayedAction a -> IO (IO a) shakeEnqueue ShakeExtras{actionQueue, logger} act = do (b, dai) <- instantiateDelayedAction act atomicallyNamed "actionQueue - push" $ pushQueue dai actionQueue - let wait' b = - waitBarrier b `catches` + let wait' barrier = + waitBarrier barrier `catches` [ Handler(\BlockedIndefinitelyOnMVar -> fail $ "internal bug: forever blocked on MVar for " <> actionName act) @@ -993,9 +996,6 @@ usesWithStale_ key files = do newtype IdeAction a = IdeAction { runIdeActionT :: (ReaderT ShakeExtras IO) a } deriving newtype (MonadReader ShakeExtras, MonadIO, Functor, Applicative, Monad, Semigroup) --- https://hub.darcs.net/ross/transformers/issue/86 -deriving instance (Semigroup (m a)) => Semigroup (ReaderT r m a) - runIdeAction :: String -> ShakeExtras -> IdeAction a -> IO a runIdeAction _herald s i = runReaderT (runIdeActionT i) s @@ -1029,7 +1029,7 @@ useWithStaleFast' key file = do -- Async trigger the key to be built anyway because we want to -- keep updating the value in the key. - wait <- delayedAction $ mkDelayedAction ("C:" ++ show key ++ ":" ++ fromNormalizedFilePath file) Debug $ use key file + waitValue <- delayedAction $ mkDelayedAction ("C:" ++ show key ++ ":" ++ fromNormalizedFilePath file) Debug $ use key file s@ShakeExtras{state} <- askShake r <- liftIO $ atomicallyNamed "useStateFast" $ getValues state key file @@ -1040,13 +1040,13 @@ useWithStaleFast' key file = do res <- lastValueIO s key file case res of Nothing -> do - a <- wait + a <- waitValue pure $ FastResult ((,zeroMapping) <$> a) (pure a) - Just _ -> pure $ FastResult res wait + Just _ -> pure $ FastResult res waitValue -- Otherwise, use the computed value even if it's out of date. Just _ -> do res <- lastValueIO s key file - pure $ FastResult res wait + pure $ FastResult res waitValue useNoFile :: IdeRule k v => k -> Action (Maybe v) useNoFile key = use key emptyFilePath @@ -1144,7 +1144,7 @@ defineNoFile recorder f = defineNoDiagnostics recorder $ \k file -> do defineEarlyCutOffNoFile :: IdeRule k v => Recorder (WithPriority Log) -> (k -> Action (BS.ByteString, v)) -> Rules () defineEarlyCutOffNoFile recorder f = defineEarlyCutoff recorder $ RuleNoDiagnostics $ \k file -> do - if file == emptyFilePath then do (hash, res) <- f k; return (Just hash, Just res) else + if file == emptyFilePath then do (hashString, res) <- f k; return (Just hashString, Just res) else fail $ "Rule " ++ show k ++ " should always be called with the empty string for a file" defineEarlyCutoff' @@ -1158,14 +1158,14 @@ defineEarlyCutoff' -> RunMode -> (Value v -> Action (Maybe BS.ByteString, IdeResult v)) -> Action (RunResult (A (RuleResult k))) -defineEarlyCutoff' doDiagnostics cmp key file old mode action = do +defineEarlyCutoff' doDiagnostics cmp key file mbOld mode action = do ShakeExtras{state, progress, dirtyKeys} <- getShakeExtras options <- getIdeOptions (if optSkipProgress options key then id else inProgress progress file) $ do - val <- case old of + val <- case mbOld of Just old | mode == RunDependenciesSame -> do - v <- liftIO $ atomicallyNamed "define - read 1" $ getValues state key file - case v of + mbValue <- liftIO $ atomicallyNamed "define - read 1" $ getValues state key file + case mbValue of -- No changes in the dependencies and we have -- an existing successful result. Just (v@(Succeeded _ x), diags) -> do @@ -1185,19 +1185,19 @@ defineEarlyCutoff' doDiagnostics cmp key file old mode action = do Just (Succeeded ver v, _) -> Stale Nothing ver v Just (Stale d ver v, _) -> Stale d ver v Just (Failed b, _) -> Failed b - (bs, (diags, res)) <- actionCatch + (mbBs, (diags, mbRes)) <- actionCatch (do v <- action staleV; liftIO $ evaluate $ force v) $ \(e :: SomeException) -> do pure (Nothing, ([ideErrorText file $ T.pack $ show e | not $ isBadDependency e],Nothing)) - ver <- estimateFileVersionUnsafely key res file - (bs, res) <- case res of + ver <- estimateFileVersionUnsafely key mbRes file + (bs, res) <- case mbRes of Nothing -> do - pure (toShakeValue ShakeStale bs, staleV) - Just v -> pure (maybe ShakeNoCutoff ShakeResult bs, Succeeded ver v) + pure (toShakeValue ShakeStale mbBs, staleV) + Just v -> pure (maybe ShakeNoCutoff ShakeResult mbBs, Succeeded ver v) liftIO $ atomicallyNamed "define - write" $ setValues state key file res (Vector.fromList diags) doDiagnostics (vfsVersion =<< ver) diags - let eq = case (bs, fmap decodeShakeValue old) of + let eq = case (bs, fmap decodeShakeValue mbOld) of (ShakeResult a, Just (ShakeResult b)) -> cmp a b (ShakeStale a, Just (ShakeStale b)) -> cmp a b -- If we do not have a previous result @@ -1214,9 +1214,7 @@ defineEarlyCutoff' doDiagnostics cmp key file old mode action = do -- without creating a dependency on the GetModificationTime rule -- (and without creating cycles in the build graph). estimateFileVersionUnsafely - :: forall k v - . IdeRule k v - => k + :: k -> Maybe v -> NormalizedFilePath -> Action (Maybe FileVersion) @@ -1254,7 +1252,7 @@ updateFileDiagnostics recorder fp ver k ShakeExtras{diagnostics, hiddenDiagnosti addTagUnsafe :: String -> String -> String -> a -> a addTagUnsafe msg t x v = unsafePerformIO(addTag (msg <> t) x) `seq` v update :: (forall a. String -> String -> a -> a) -> [Diagnostic] -> STMDiagnosticStore -> STM [Diagnostic] - update addTagUnsafe new store = addTagUnsafe "count" (show $ Prelude.length new) $ setStageDiagnostics addTagUnsafe uri ver (renderKey k) new store + update addTagUnsafeMethod new store = addTagUnsafeMethod "count" (show $ Prelude.length new) $ setStageDiagnostics addTagUnsafeMethod uri ver (renderKey k) new store current = second diagsFromRule <$> current0 addTag "version" (show ver) mask_ $ do @@ -1264,11 +1262,11 @@ updateFileDiagnostics recorder fp ver k ShakeExtras{diagnostics, hiddenDiagnosti -- publishDiagnosticsNotification. newDiags <- liftIO $ atomicallyNamed "diagnostics - update" $ update (addTagUnsafe "shown ") (map snd currentShown) diagnostics _ <- liftIO $ atomicallyNamed "diagnostics - hidden" $ update (addTagUnsafe "hidden ") (map snd currentHidden) hiddenDiagnostics - let uri = filePathToUri' fp + let uri' = filePathToUri' fp let delay = if null newDiags then 0.1 else 0 - registerEvent debouncer delay uri $ withTrace ("report diagnostics " <> fromString (fromNormalizedFilePath fp)) $ \tag -> do + registerEvent debouncer delay uri' $ withTrace ("report diagnostics " <> fromString (fromNormalizedFilePath fp)) $ \tag -> do join $ mask_ $ do - lastPublish <- atomicallyNamed "diagnostics - publish" $ STM.focus (Focus.lookupWithDefault [] <* Focus.insert newDiags) uri publishedDiagnostics + lastPublish <- atomicallyNamed "diagnostics - publish" $ STM.focus (Focus.lookupWithDefault [] <* Focus.insert newDiags) uri' publishedDiagnostics let action = when (lastPublish /= newDiags) $ case lspEnv of Nothing -> -- Print an LSP event. logWith recorder Info $ LogDiagsDiffButNoLspEnv (map (fp, ShowDiag,) newDiags) @@ -1276,14 +1274,13 @@ updateFileDiagnostics recorder fp ver k ShakeExtras{diagnostics, hiddenDiagnosti liftIO $ tag "count" (show $ Prelude.length newDiags) liftIO $ tag "key" (show k) LSP.sendNotification SMethod_TextDocumentPublishDiagnostics $ - LSP.PublishDiagnosticsParams (fromNormalizedUri uri) (fmap fromIntegral ver) ( newDiags) + LSP.PublishDiagnosticsParams (fromNormalizedUri uri') (fmap fromIntegral ver) ( newDiags) return action where diagsFromRule :: Diagnostic -> Diagnostic diagsFromRule c@Diagnostic{_range} - | coerce ideTesting = c - {_relatedInformation = - Just $ [ + | coerce ideTesting = c & L.relatedInformation ?~ + [ DiagnosticRelatedInformation (Location (filePathToUri $ fromNormalizedFilePath fp) @@ -1291,7 +1288,6 @@ updateFileDiagnostics recorder fp ver k ShakeExtras{diagnostics, hiddenDiagnosti ) (T.pack $ show k) ] - } | otherwise = c diff --git a/ghcide/src/Development/IDE/Core/Tracing.hs b/ghcide/src/Development/IDE/Core/Tracing.hs index 924adefed8..ed30a174af 100644 --- a/ghcide/src/Development/IDE/Core/Tracing.hs +++ b/ghcide/src/Development/IDE/Core/Tracing.hs @@ -1,7 +1,3 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE PackageImports #-} -{-# LANGUAGE PatternSynonyms #-} -{-# HLINT ignore #-} module Development.IDE.Core.Tracing ( otTracedHandler diff --git a/ghcide/src/Development/IDE/GHC/CPP.hs b/ghcide/src/Development/IDE/GHC/CPP.hs index 1cb70cc174..87d25c7fa9 100644 --- a/ghcide/src/Development/IDE/GHC/CPP.hs +++ b/ghcide/src/Development/IDE/GHC/CPP.hs @@ -15,27 +15,33 @@ module Development.IDE.GHC.CPP(doCpp, addOptP) where -import Control.Monad import Development.IDE.GHC.Compat as Compat import Development.IDE.GHC.Compat.Util import GHC -#if MIN_VERSION_ghc(9,0,0) -import qualified GHC.Driver.Pipeline as Pipeline -import GHC.Settings -#elif MIN_VERSION_ghc (8,10,0) +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if MIN_VERSION_ghc (8,10,0) && !MIN_VERSION_ghc(9,0,0) import qualified DriverPipeline as Pipeline import ToolSettings #endif -#if MIN_VERSION_ghc(9,5,0) -import qualified GHC.SysTools.Cpp as Pipeline +#if MIN_VERSION_ghc(9,0,0) +import GHC.Settings #endif -#if MIN_VERSION_ghc(9,3,0) +#if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,3,0) +import qualified GHC.Driver.Pipeline as Pipeline +#endif + +#if MIN_VERSION_ghc(9,3,0) && !MIN_VERSION_ghc(9,5,0) import qualified GHC.Driver.Pipeline.Execute as Pipeline #endif +#if MIN_VERSION_ghc(9,5,0) +import qualified GHC.SysTools.Cpp as Pipeline +#endif + addOptP :: String -> DynFlags -> DynFlags addOptP f = alterToolSettings $ \s -> s { toolSettings_opt_P = f : toolSettings_opt_P s @@ -43,7 +49,7 @@ addOptP f = alterToolSettings $ \s -> s } where fingerprintStrings ss = fingerprintFingerprints $ map fingerprintString ss - alterToolSettings f dynFlags = dynFlags { toolSettings = f (toolSettings dynFlags) } + alterToolSettings g dynFlags = dynFlags { toolSettings = g (toolSettings dynFlags) } doCpp :: HscEnv -> FilePath -> FilePath -> IO () doCpp env input_fn output_fn = diff --git a/ghcide/src/Development/IDE/GHC/Compat.hs b/ghcide/src/Development/IDE/GHC/Compat.hs index 0f9069b006..3fade3a314 100644 --- a/ghcide/src/Development/IDE/GHC/Compat.hs +++ b/ghcide/src/Development/IDE/GHC/Compat.hs @@ -142,7 +142,7 @@ module Development.IDE.GHC.Compat( #endif ) where -import Data.Bifunctor +import Prelude hiding (mod) import Development.IDE.GHC.Compat.Core hiding (moduleUnitId) import Development.IDE.GHC.Compat.Env import Development.IDE.GHC.Compat.Iface @@ -156,53 +156,21 @@ import GHC hiding (HasSrcSpan, ModLocation, RealSrcSpan, exprType, getLoc, lookupName) - import Data.Coerce (coerce) import Data.String (IsString (fromString)) +import Compat.HieAst (enrichHie) +import Compat.HieBin +import Compat.HieTypes hiding (nodeAnnotations) +import qualified Compat.HieTypes as GHC (nodeAnnotations) +import Compat.HieUtils +import qualified Data.ByteString as BS +import Data.List (foldl') +import qualified Data.Map as Map +import qualified Data.Set as S +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] -#if MIN_VERSION_ghc(9,0,0) -#if MIN_VERSION_ghc(9,5,0) -import GHC.Core.Lint.Interactive (interactiveInScope) -import GHC.Driver.Config.Core.Lint.Interactive (lintInteractiveExpr) -import GHC.Driver.Config.Core.Opt.Simplify (initSimplifyExprOpts) -import GHC.Driver.Config.CoreToStg (initCoreToStgOpts) -import GHC.Driver.Config.CoreToStg.Prep (initCorePrepConfig) -#else -import GHC.Core.Lint (lintInteractiveExpr) -#endif -import qualified GHC.Core.Opt.Pipeline as GHC -import GHC.Core.Tidy (tidyExpr) -import GHC.CoreToStg.Prep (corePrepPgm) -import qualified GHC.CoreToStg.Prep as GHC -import GHC.Driver.Hooks (hscCompileCoreExprHook) -#if MIN_VERSION_ghc(9,2,0) -import GHC.Linker.Loader (loadExpr) -import GHC.Linker.Types (isObjectLinkable) -import GHC.Runtime.Context (icInteractiveModule) -import GHC.Unit.Home.ModInfo (HomePackageTable, - lookupHpt) -#if MIN_VERSION_ghc(9,3,0) -import GHC.Unit.Module.Deps (Dependencies(dep_direct_mods), Usage(..)) -#else -import GHC.Unit.Module.Deps (Dependencies(dep_mods), Usage(..)) -#endif -#else -import GHC.CoreToByteCode (coreExprToBCOs) -import GHC.Driver.Types (Dependencies (dep_mods), - HomePackageTable, - icInteractiveModule, - lookupHpt) -import GHC.Runtime.Linker (linkExpr) -#endif -import GHC.ByteCode.Asm (bcoFreeNames) -import GHC.Types.Annotations (AnnTarget (ModuleTarget), - Annotation (..), - extendAnnEnvList) -import GHC.Types.Unique.DFM as UniqDFM -import GHC.Types.Unique.DSet as UniqDSet -import GHC.Types.Unique.Set as UniqSet -#else +#if !MIN_VERSION_ghc(9,0,0) import Annotations (AnnTarget (ModuleTarget), Annotation (..), extendAnnEnvList) @@ -224,70 +192,101 @@ import UniqDSet import UniqSet import VarEnv (emptyInScopeSet, emptyTidyEnv, mkRnEnv2) +import FastString +import qualified Avail +import DynFlags hiding (ExposePackage) +import HscTypes +import MkIface hiding (writeIfaceFile) + +import StringBuffer (hPutStringBuffer) +import qualified SysTools #endif #if MIN_VERSION_ghc(9,0,0) +import qualified GHC.Core.Opt.Pipeline as GHC +import GHC.Core.Tidy (tidyExpr) +import GHC.CoreToStg.Prep (corePrepPgm) +import qualified GHC.CoreToStg.Prep as GHC +import GHC.Driver.Hooks (hscCompileCoreExprHook) + +import GHC.ByteCode.Asm (bcoFreeNames) +import GHC.Types.Annotations (AnnTarget (ModuleTarget), + Annotation (..), + extendAnnEnvList) +import GHC.Types.Unique.DFM as UniqDFM +import GHC.Types.Unique.DSet as UniqDSet +import GHC.Types.Unique.Set as UniqSet import GHC.Data.FastString import GHC.Core import GHC.Data.StringBuffer import GHC.Driver.Session hiding (ExposePackage) -import qualified GHC.Types.SrcLoc as SrcLoc import GHC.Types.Var.Env -import GHC.Utils.Error -#if MIN_VERSION_ghc(9,2,0) -import GHC.Driver.Env as Env -import GHC.Unit.Module.ModIface -import GHC.Unit.Module.ModSummary -#else -import GHC.Driver.Types -#endif -import GHC.Iface.Env import GHC.Iface.Make (mkIfaceExports) import qualified GHC.SysTools.Tasks as SysTools import qualified GHC.Types.Avail as Avail -#else -import FastString -import qualified Avail -import DynFlags hiding (ExposePackage) -import HscTypes -import MkIface hiding (writeIfaceFile) +#endif -import StringBuffer (hPutStringBuffer) -import qualified SysTools +#if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,2,0) +import GHC.Utils.Error +import GHC.CoreToByteCode (coreExprToBCOs) +import GHC.Runtime.Linker (linkExpr) +import GHC.Driver.Types #endif -import Compat.HieAst (enrichHie) -import Compat.HieBin -import Compat.HieTypes hiding (nodeAnnotations) -import qualified Compat.HieTypes as GHC (nodeAnnotations) -import Compat.HieUtils -import qualified Data.ByteString as BS -import Data.IORef +#if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,5,0) +import GHC.Core.Lint (lintInteractiveExpr) +#endif -import Data.List (foldl') -import qualified Data.Map as Map -import qualified Data.Set as S +#if !MIN_VERSION_ghc(9,2,0) +import Data.Bifunctor +#endif #if MIN_VERSION_ghc(9,2,0) +import GHC.Iface.Env +import qualified GHC.Types.SrcLoc as SrcLoc +import GHC.Linker.Loader (loadExpr) +import GHC.Runtime.Context (icInteractiveModule) +import GHC.Unit.Home.ModInfo (HomePackageTable, + lookupHpt) +import GHC.Driver.Env as Env +import GHC.Unit.Module.ModIface import GHC.Builtin.Uniques import GHC.ByteCode.Types import GHC.CoreToStg import GHC.Data.Maybe import GHC.Linker.Loader (loadDecls) -import GHC.Runtime.Interpreter import GHC.Stg.Pipeline import GHC.Stg.Syntax import GHC.StgToByteCode import GHC.Types.CostCentre import GHC.Types.IPE +#endif + +#if MIN_VERSION_ghc(9,2,0) && !MIN_VERSION_ghc(9,3,0) +import GHC.Unit.Module.Deps (Dependencies(dep_mods), Usage(..)) +import GHC.Linker.Types (isObjectLinkable) +import GHC.Unit.Module.ModSummary +import GHC.Runtime.Interpreter +#endif + +#if !MIN_VERSION_ghc(9,3,0) +import Data.IORef #endif #if MIN_VERSION_ghc(9,3,0) -import GHC.Types.Error +import GHC.Unit.Module.Deps (Dependencies(dep_direct_mods), Usage(..)) import GHC.Driver.Config.Stg.Pipeline -import GHC.Driver.Plugins (PsMessages (..)) #endif +#if MIN_VERSION_ghc(9,5,0) +import GHC.Core.Lint.Interactive (interactiveInScope) +import GHC.Driver.Config.Core.Lint.Interactive (lintInteractiveExpr) +import GHC.Driver.Config.Core.Opt.Simplify (initSimplifyExprOpts) +import GHC.Driver.Config.CoreToStg (initCoreToStgOpts) +import GHC.Driver.Config.CoreToStg.Prep (initCorePrepConfig) +#endif + + #if !MIN_VERSION_ghc(9,3,0) nonDetOccEnvElts :: OccEnv a -> [a] nonDetOccEnvElts = occEnvElts @@ -417,9 +416,9 @@ simplifyExpr _ = GHC.simplifyExpr corePrepExpr :: DynFlags -> HscEnv -> CoreExpr -> IO CoreExpr #if MIN_VERSION_ghc(9,5,0) -corePrepExpr _ env exp = do +corePrepExpr _ env expr = do cfg <- initCorePrepConfig env - GHC.corePrepExpr (Development.IDE.GHC.Compat.Env.hsc_logger env) cfg exp + GHC.corePrepExpr (Development.IDE.GHC.Compat.Env.hsc_logger env) cfg expr #else corePrepExpr _ = GHC.corePrepExpr #endif @@ -573,12 +572,12 @@ combineNodeInfo' :: Ord a => NodeInfo a -> NodeInfo a -> NodeInfo a NodeInfo (S.union as bs) (mergeSorted ai bi) (Map.unionWith (<>) ad bd) where mergeSorted :: Ord a => [a] -> [a] -> [a] - mergeSorted la@(a:as) lb@(b:bs) = case compare a b of - LT -> a : mergeSorted as lb - EQ -> a : mergeSorted as bs - GT -> b : mergeSorted la bs - mergeSorted as [] = as - mergeSorted [] bs = bs + mergeSorted la@(a:axs) lb@(b:bxs) = case compare a b of + LT -> a : mergeSorted axs lb + EQ -> a : mergeSorted axs bxs + GT -> b : mergeSorted la bxs + mergeSorted axs [] = axs + mergeSorted [] bxs = bxs #else diff --git a/ghcide/src/Development/IDE/GHC/Compat/Core.hs b/ghcide/src/Development/IDE/GHC/Compat/Core.hs index 3b516c6f40..b6067167e2 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Core.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Core.hs @@ -3,10 +3,8 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE ViewPatterns #-} --- TODO: remove -{-# OPTIONS -Wno-dodgy-imports -Wno-unused-imports #-} --- | Compat Core module that handles the GHC module hierarchy re-organisation +-- | Compat Core module that handles the GHC module hierarchy re-organization -- by re-exporting everything we care about. -- -- This module provides no other compat mechanisms, except for simple @@ -502,186 +500,16 @@ module Development.IDE.GHC.Compat.Core ( import qualified GHC -#if MIN_VERSION_ghc(9,3,0) -import GHC.Iface.Recomp (CompileReason(..)) -import GHC.Driver.Env.Types (hsc_type_env_vars) -import GHC.Driver.Env (hscUpdateHUG, hscUpdateHPT, hsc_HUG) -import GHC.Driver.Env.KnotVars -import GHC.Iface.Recomp -import GHC.Linker.Types -import GHC.Unit.Module.Graph -import GHC.Driver.Errors.Types -import GHC.Types.Unique.Map -import GHC.Types.Unique -import GHC.Utils.TmpFs -import GHC.Utils.Panic -import GHC.Unit.Finder.Types -import GHC.Unit.Env -import GHC.Driver.Phases -#endif +-- NOTE(ozkutuk): Cpp clashes Phase.Cpp, so we hide it. +-- Not the greatest solution, but gets the job done +-- (until the CPP extension is actually needed). +import GHC.LanguageExtensions.Type hiding (Cpp) -#if MIN_VERSION_ghc(9,0,0) -import GHC.Builtin.Names hiding (Unique, printName) -import GHC.Builtin.Types -import GHC.Builtin.Types.Prim -import GHC.Builtin.Utils -import GHC.Core.Class -import GHC.Core.Coercion -import GHC.Core.ConLike -import GHC.Core.DataCon hiding (dataConExTyCoVars) -import qualified GHC.Core.DataCon as DataCon -import GHC.Core.FamInstEnv hiding (pprFamInst) -import GHC.Core.InstEnv -import GHC.Types.Unique.FM hiding (UniqFM) -import qualified GHC.Types.Unique.FM as UniqFM -#if MIN_VERSION_ghc(9,3,0) -import qualified GHC.Driver.Config.Tidy as GHC -import qualified GHC.Data.Strict as Strict -#endif -#if MIN_VERSION_ghc(9,2,0) -import GHC.Data.Bag -import GHC.Core.Multiplicity (scaledThing) -#else -import GHC.Core.Ppr.TyThing hiding (pprFamInst) -import GHC.Core.TyCo.Rep (scaledThing) -#endif -import GHC.Core.PatSyn -import GHC.Core.Predicate -import GHC.Core.TyCo.Ppr -import qualified GHC.Core.TyCo.Rep as TyCoRep -import GHC.Core.TyCon -import GHC.Core.Type hiding (mkInfForAllTys) -import GHC.Core.Unify -import GHC.Core.Utils +import GHC.Hs.Binds +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] -#if MIN_VERSION_ghc(9,2,0) -import GHC.Driver.Env -#else -import GHC.Driver.Finder hiding (mkHomeModLocation) -import GHC.Driver.Types -import GHC.Driver.Ways -#endif -import GHC.Driver.CmdLine (Warn (..)) -import GHC.Driver.Hooks -import GHC.Driver.Main as GHC -import GHC.Driver.Monad -import GHC.Driver.Phases -import GHC.Driver.Pipeline -import GHC.Driver.Plugins -import GHC.Driver.Session hiding (ExposePackage) -import qualified GHC.Driver.Session as DynFlags -#if MIN_VERSION_ghc(9,2,0) -import GHC.Hs (HsModule (..), SrcSpanAnn') -import GHC.Hs.Decls hiding (FunDep) -import GHC.Hs.Doc -import GHC.Hs.Expr -import GHC.Hs.Extension -import GHC.Hs.ImpExp -import GHC.Hs.Pat -import GHC.Hs.Type -import GHC.Hs.Utils hiding (collectHsBindsBinders) -import qualified GHC.Hs.Utils as GHC -#endif -#if !MIN_VERSION_ghc(9,2,0) -import GHC.Hs hiding (HsLet, LetStmt) -#endif -import GHC.HsToCore.Docs -import GHC.HsToCore.Expr -import GHC.HsToCore.Monad -import GHC.Iface.Load -import GHC.Iface.Make (mkFullIface, mkPartialIface) -import GHC.Iface.Make as GHC -import GHC.Iface.Recomp -import GHC.Iface.Syntax -import GHC.Iface.Tidy as GHC -import GHC.IfaceToCore -import GHC.Parser -import GHC.Parser.Header hiding (getImports) -#if MIN_VERSION_ghc(9,2,0) -import qualified GHC.Linker.Loader as Linker -import GHC.Linker.Types -import GHC.Parser.Lexer hiding (initParserState, getPsMessages) -import GHC.Parser.Annotation (EpAnn (..)) -import GHC.Platform.Ways -import GHC.Runtime.Context (InteractiveImport (..)) -#else -import GHC.Parser.Lexer -import qualified GHC.Runtime.Linker as Linker -#endif -import GHC.Rename.Fixity (lookupFixityRn) -import GHC.Rename.Names -import GHC.Rename.Splice -import qualified GHC.Runtime.Interpreter as GHCi -import GHC.Tc.Instance.Family -import GHC.Tc.Module -import GHC.Tc.Types -import GHC.Tc.Types.Evidence hiding ((<.>)) -import GHC.Tc.Utils.Env -import GHC.Tc.Utils.Monad hiding (Applicative (..), IORef, - MonadFix (..), MonadIO (..), - allM, anyM, concatMapM, - mapMaybeM, (<$>)) -import GHC.Tc.Utils.TcType as TcType -import qualified GHC.Types.Avail as Avail -#if MIN_VERSION_ghc(9,2,0) -import GHC.Types.Avail (greNamePrintableName) -import GHC.Types.Fixity (LexicalFixity (..), Fixity (..), defaultFixity) -#endif -#if MIN_VERSION_ghc(9,2,0) -import GHC.Types.Meta -#endif -import GHC.Types.Basic -import GHC.Types.Id -import GHC.Types.Name hiding (varName) -import GHC.Types.Name.Cache -import GHC.Types.Name.Env -import GHC.Types.Name.Reader hiding (GRE, gre_name, gre_imp, gre_lcl, gre_par) -import qualified GHC.Types.Name.Reader as RdrName -#if MIN_VERSION_ghc(9,2,0) -import GHC.Types.Name.Set -import GHC.Types.SourceFile (HscSource (..), -#if !MIN_VERSION_ghc(9,3,0) - SourceModified(..) -#endif - ) -import GHC.Types.SourceText -import GHC.Types.Target (Target (..), TargetId (..)) -import GHC.Types.TyThing -import GHC.Types.TyThing.Ppr -#else -import GHC.Types.Name.Set -#endif -import GHC.Types.SrcLoc (BufPos, BufSpan, - SrcLoc (UnhelpfulLoc), - SrcSpan (UnhelpfulSpan)) -import qualified GHC.Types.SrcLoc as SrcLoc -import GHC.Types.Unique.Supply -import GHC.Types.Var (Var (varName), setTyVarUnique, - setVarUnique) -#if MIN_VERSION_ghc(9,2,0) -import GHC.Unit.Finder hiding (mkHomeModLocation) -import GHC.Unit.Home.ModInfo -#endif -import GHC.Unit.Info (PackageName (..)) -import GHC.Unit.Module hiding (ModLocation (..), UnitId, - addBootSuffixLocnOut, moduleUnit, - toUnitId) -import qualified GHC.Unit.Module as Module -#if MIN_VERSION_ghc(9,2,0) -import GHC.Unit.Module.Graph (mkModuleGraph) -import GHC.Unit.Module.Imported -import GHC.Unit.Module.ModDetails -import GHC.Unit.Module.ModGuts -import GHC.Unit.Module.ModIface (IfaceExport, ModIface (..), - ModIface_ (..), mi_fix) -import GHC.Unit.Module.ModSummary (ModSummary (..)) -#endif -import GHC.Unit.State (ModuleOrigin (..)) -import GHC.Utils.Error (Severity (..), emptyMessages) -import GHC.Utils.Panic hiding (try) -import qualified GHC.Utils.Panic.Plain as Plain -#else +#if !MIN_VERSION_ghc(9,0,0) import qualified Avail import BasicTypes hiding (Version) import Class @@ -711,7 +539,7 @@ import HscTypes import Id import IfaceSyn import InstEnv -import Lexer hiding (getSrcLoc) +import Lexer import qualified Linker import LoadIface import MkIface as GHC @@ -748,7 +576,6 @@ import TcRnMonad hiding (Applicative (..), IORef, mapMaybeM, (<$>)) import TcRnTypes import TcType -import qualified TcType import TidyPgm as GHC import qualified TyCoRep import TyCon @@ -766,40 +593,167 @@ import Coercion (coercionKind) import Predicate import SrcLoc (Located, SrcLoc (UnhelpfulLoc), SrcSpan (UnhelpfulSpan)) +import qualified Finder as GHC #endif +#if MIN_VERSION_ghc(9,0,0) +import GHC.Builtin.Names hiding (Unique, printName) +import GHC.Builtin.Types +import GHC.Builtin.Types.Prim +import GHC.Builtin.Utils +import GHC.Core.Class +import GHC.Core.Coercion +import GHC.Core.ConLike +import GHC.Core.DataCon hiding (dataConExTyCoVars) +import qualified GHC.Core.DataCon as DataCon +import GHC.Core.FamInstEnv hiding (pprFamInst) +import GHC.Core.InstEnv +import GHC.Types.Unique.FM hiding (UniqFM) +import qualified GHC.Types.Unique.FM as UniqFM +import GHC.Core.PatSyn +import GHC.Core.Predicate +import GHC.Core.TyCo.Ppr +import qualified GHC.Core.TyCo.Rep as TyCoRep +import GHC.Core.TyCon +import GHC.Core.Type hiding (mkInfForAllTys) +import GHC.Core.Unify +import GHC.Core.Utils +import GHC.Driver.CmdLine (Warn (..)) +import GHC.Driver.Hooks +import GHC.Driver.Main as GHC +import GHC.Driver.Monad +import GHC.Driver.Phases +import GHC.Driver.Pipeline +import GHC.Driver.Plugins +import GHC.Driver.Session hiding (ExposePackage) +import qualified GHC.Driver.Session as DynFlags +import GHC.HsToCore.Docs +import GHC.HsToCore.Expr +import GHC.HsToCore.Monad +import GHC.Iface.Load +import GHC.Iface.Make as GHC +import GHC.Iface.Recomp +import GHC.Iface.Syntax +import GHC.Iface.Tidy as GHC +import GHC.IfaceToCore +import GHC.Parser +import GHC.Parser.Header hiding (getImports) +import GHC.Rename.Fixity (lookupFixityRn) +import GHC.Rename.Names +import GHC.Rename.Splice +import qualified GHC.Runtime.Interpreter as GHCi +import GHC.Tc.Instance.Family +import GHC.Tc.Module +import GHC.Tc.Types +import GHC.Tc.Types.Evidence hiding ((<.>)) +import GHC.Tc.Utils.Env +import GHC.Tc.Utils.Monad hiding (Applicative (..), IORef, + MonadFix (..), MonadIO (..), + allM, anyM, concatMapM, + mapMaybeM, (<$>)) +import GHC.Tc.Utils.TcType as TcType +import qualified GHC.Types.Avail as Avail +import GHC.Types.Basic +import GHC.Types.Id +import GHC.Types.Name hiding (varName) +import GHC.Types.Name.Cache +import GHC.Types.Name.Env +import GHC.Types.Name.Reader hiding (GRE, gre_name, gre_imp, gre_lcl, gre_par) +import qualified GHC.Types.Name.Reader as RdrName +import GHC.Types.SrcLoc (BufPos, BufSpan, + SrcLoc (UnhelpfulLoc), + SrcSpan (UnhelpfulSpan)) +import qualified GHC.Types.SrcLoc as SrcLoc +import GHC.Types.Unique.Supply +import GHC.Types.Var (Var (varName), setTyVarUnique, + setVarUnique) +import GHC.Unit.Info (PackageName (..)) +import GHC.Unit.Module hiding (ModLocation (..), UnitId, + addBootSuffixLocnOut, moduleUnit, + toUnitId) +import qualified GHC.Unit.Module as Module +import GHC.Unit.State (ModuleOrigin (..)) +import GHC.Utils.Error (Severity (..), emptyMessages) +import GHC.Utils.Panic hiding (try) +import qualified GHC.Utils.Panic.Plain as Plain +#endif -import Data.List (isSuffixOf) -import System.FilePath - +#if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,2,0) +import GHC.Core.Ppr.TyThing hiding (pprFamInst) +import GHC.Core.TyCo.Rep (scaledThing) +import GHC.Driver.Finder hiding (mkHomeModLocation) +import GHC.Driver.Types +import GHC.Driver.Ways +import GHC.Hs hiding (HsLet, LetStmt) +import GHC.Parser.Lexer +import qualified GHC.Runtime.Linker as Linker +import GHC.Types.Name.Set +import qualified GHC.Driver.Finder as GHC +#endif #if MIN_VERSION_ghc(9,2,0) +import Data.Foldable (toList) +import GHC.Data.Bag +import GHC.Core.Multiplicity (scaledThing) +import GHC.Driver.Env +import GHC.Hs (HsModule (..), SrcSpanAnn') +import GHC.Hs.Decls hiding (FunDep) +import GHC.Hs.Doc +import GHC.Hs.Expr +import GHC.Hs.Extension +import GHC.Hs.ImpExp +import GHC.Hs.Pat +import GHC.Hs.Type +import GHC.Hs.Utils hiding (collectHsBindsBinders) +import qualified GHC.Linker.Loader as Linker +import GHC.Linker.Types +import GHC.Parser.Lexer hiding (initParserState, getPsMessages) +import GHC.Parser.Annotation (EpAnn (..)) +import GHC.Platform.Ways +import GHC.Runtime.Context (InteractiveImport (..)) +import GHC.Types.Avail (greNamePrintableName) +import GHC.Types.Fixity (LexicalFixity (..), Fixity (..), defaultFixity) +import GHC.Types.Meta +import GHC.Types.Name.Set +import GHC.Types.SourceFile (HscSource (..)) +import GHC.Types.SourceText +import GHC.Types.Target (Target (..), TargetId (..)) +import GHC.Types.TyThing +import GHC.Types.TyThing.Ppr +import GHC.Unit.Finder hiding (mkHomeModLocation) +import GHC.Unit.Home.ModInfo +import GHC.Unit.Module.Imported +import GHC.Unit.Module.ModDetails +import GHC.Unit.Module.ModGuts +import GHC.Unit.Module.ModIface (IfaceExport, ModIface (..), + ModIface_ (..), mi_fix) +import GHC.Unit.Module.ModSummary (ModSummary (..)) import Language.Haskell.Syntax hiding (FunDep) #endif -#if MIN_VERSION_ghc(9,3,0) -import GHC.Driver.Env as GHCi -#endif -import Data.Foldable (toList) +#if MIN_VERSION_ghc(9,2,0) && !MIN_VERSION_ghc(9,3,0) +import GHC.Types.SourceFile (SourceModified(..)) +import GHC.Unit.Module.Graph (mkModuleGraph) +import qualified GHC.Unit.Finder as GHC +#endif #if MIN_VERSION_ghc(9,3,0) +import GHC.Driver.Env.KnotVars +import GHC.Unit.Module.Graph +import GHC.Driver.Errors.Types +import GHC.Types.Unique.Map +import GHC.Types.Unique +import GHC.Utils.TmpFs +import GHC.Utils.Panic +import GHC.Unit.Finder.Types +import GHC.Unit.Env +import qualified GHC.Driver.Config.Tidy as GHC +import qualified GHC.Data.Strict as Strict +import GHC.Driver.Env as GHCi import qualified GHC.Unit.Finder as GHC import qualified GHC.Driver.Config.Finder as GHC -#elif MIN_VERSION_ghc(9,2,0) -import qualified GHC.Unit.Finder as GHC -#elif MIN_VERSION_ghc(9,0,0) -import qualified GHC.Driver.Finder as GHC -#else -import qualified Finder as GHC #endif --- NOTE(ozkutuk): Cpp clashes Phase.Cpp, so we hide it. --- Not the greatest solution, but gets the job done --- (until the CPP extension is actually needed). -import GHC.LanguageExtensions.Type hiding (Cpp) - -import GHC.Hs.Binds - mkHomeModLocation :: DynFlags -> ModuleName -> FilePath -> IO Module.ModLocation #if MIN_VERSION_ghc(9,3,0) mkHomeModLocation df mn f = pure $ GHC.mkHomeModLocation (GHC.initFinderOpts df) mn f @@ -1132,10 +1086,10 @@ makeSimpleDetails hsc_env = hsc_env #endif -mkIfaceTc hsc_env sf details ms tcGblEnv = +mkIfaceTc hsc_env sf details _ms tcGblEnv = -- ms is only used in GHC >= 9.4 GHC.mkIfaceTc hsc_env sf details #if MIN_VERSION_ghc(9,3,0) - ms + _ms #endif tcGblEnv @@ -1203,6 +1157,7 @@ groupOrigin = mg_ext #else mapLoc :: (a -> b) -> SrcLoc.GenLocated l a -> SrcLoc.GenLocated l b mapLoc = SrcLoc.mapLoc +groupOrigin :: MatchGroup p body -> Origin groupOrigin = mg_origin #endif diff --git a/ghcide/src/Development/IDE/GHC/Compat/Env.hs b/ghcide/src/Development/IDE/GHC/Compat/Env.hs index 25ea24123b..1cd9350945 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Env.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Env.hs @@ -53,57 +53,69 @@ module Development.IDE.GHC.Compat.Env ( Development.IDE.GHC.Compat.Env.platformDefaultBackend, ) where -import GHC (setInteractiveDynFlags) +import GHC (setInteractiveDynFlags) -#if MIN_VERSION_ghc(9,0,0) -#if MIN_VERSION_ghc(9,2,0) -import GHC.Driver.Backend as Backend -#if MIN_VERSION_ghc(9,3,0) -import GHC.Driver.Env (HscEnv) -#else -import GHC.Driver.Env (HscEnv, hsc_EPS) -#endif -import qualified GHC.Driver.Env as Env -import qualified GHC.Driver.Session as Session -import GHC.Platform.Ways hiding (hostFullWays) -import qualified GHC.Platform.Ways as Ways -import GHC.Runtime.Context -import GHC.Unit.Env (UnitEnv) -import GHC.Unit.Home as Home -import GHC.Utils.Logger -import GHC.Utils.TmpFs -#else -import qualified GHC.Driver.Session as DynFlags -import GHC.Driver.Types (HscEnv, InteractiveContext (..), hsc_EPS, - setInteractivePrintName) -import qualified GHC.Driver.Types as Env -import GHC.Driver.Ways hiding (hostFullWays) -import qualified GHC.Driver.Ways as Ways -#endif -import GHC.Driver.Hooks (Hooks) -import GHC.Driver.Session hiding (mkHomeModule) -#if __GLASGOW_HASKELL__ >= 905 -import Language.Haskell.Syntax.Module.Name -#else -import GHC.Unit.Module.Name -#endif -import GHC.Unit.Types (Module, Unit, UnitId, mkModule) -#else +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,0,0) import DynFlags import Hooks -import HscTypes as Env +import HscTypes as Env import Module #endif #if MIN_VERSION_ghc(9,0,0) -#if !MIN_VERSION_ghc(9,2,0) -import qualified Data.Set as Set +import GHC.Driver.Hooks (Hooks) +import GHC.Driver.Session hiding (mkHomeModule) +import GHC.Unit.Types (Module, UnitId) #endif + +#if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,2,0) +import qualified Data.Set as Set +import qualified GHC.Driver.Session as DynFlags +import GHC.Driver.Types (HscEnv, + InteractiveContext (..), + hsc_EPS, + setInteractivePrintName) +import qualified GHC.Driver.Types as Env +import GHC.Driver.Ways hiding (hostFullWays) +import qualified GHC.Driver.Ways as Ways +import GHC.Unit.Types (Unit, mkModule) #endif + +#if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,5,0) +import GHC.Unit.Module.Name +#endif + #if !MIN_VERSION_ghc(9,2,0) import Data.IORef #endif +#if MIN_VERSION_ghc(9,2,0) +import GHC.Driver.Backend as Backend +import qualified GHC.Driver.Env as Env +import qualified GHC.Driver.Session as Session +import GHC.Platform.Ways hiding (hostFullWays) +import qualified GHC.Platform.Ways as Ways +import GHC.Runtime.Context +import GHC.Unit.Env (UnitEnv) +import GHC.Unit.Home as Home +import GHC.Utils.Logger +import GHC.Utils.TmpFs +#endif + +#if MIN_VERSION_ghc(9,2,0) && !MIN_VERSION_ghc(9,3,0) +import GHC.Driver.Env (HscEnv, hsc_EPS) +#endif + +#if MIN_VERSION_ghc(9,3,0) +import GHC.Driver.Env (HscEnv) +#endif + +#if MIN_VERSION_ghc(9,5,0) +import Language.Haskell.Syntax.Module.Name +#endif + #if MIN_VERSION_ghc(9,3,0) hsc_EPS :: HscEnv -> UnitEnv hsc_EPS = hsc_unit_env @@ -276,13 +288,13 @@ hostFullWays = #endif setWays :: Ways -> DynFlags -> DynFlags -setWays ways flags = +setWays newWays flags = #if MIN_VERSION_ghc(9,2,0) - flags { Session.targetWays_ = ways} + flags { Session.targetWays_ = newWays} #elif MIN_VERSION_ghc(9,0,0) - flags {ways = ways} + flags {ways = newWays} #else - updateWays $ flags {ways = ways} + updateWays $ flags {ways = newWays} #endif -- ------------------------------------------------------- diff --git a/ghcide/src/Development/IDE/GHC/Compat/Iface.hs b/ghcide/src/Development/IDE/GHC/Compat/Iface.hs index 5df7eeff2d..c9531469bc 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Iface.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Iface.hs @@ -6,25 +6,32 @@ module Development.IDE.GHC.Compat.Iface ( cannotFindModule, ) where +import Development.IDE.GHC.Compat.Env +import Development.IDE.GHC.Compat.Outputable import GHC -#if MIN_VERSION_ghc(9,3,0) -import GHC.Driver.Session (targetProfile) + +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,0,0) +import Finder (FindResult) +import qualified Finder +import qualified MkIface #endif -#if MIN_VERSION_ghc(9,2,0) -import qualified GHC.Iface.Load as Iface -import GHC.Unit.Finder.Types (FindResult) -#elif MIN_VERSION_ghc(9,0,0) + +#if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,2,0) import qualified GHC.Driver.Finder as Finder import GHC.Driver.Types (FindResult) import qualified GHC.Iface.Load as Iface -#else -import Finder (FindResult) -import qualified Finder -import qualified MkIface #endif -import Development.IDE.GHC.Compat.Env -import Development.IDE.GHC.Compat.Outputable +#if MIN_VERSION_ghc(9,2,0) +import qualified GHC.Iface.Load as Iface +import GHC.Unit.Finder.Types (FindResult) +#endif + +#if MIN_VERSION_ghc(9,3,0) +import GHC.Driver.Session (targetProfile) +#endif writeIfaceFile :: HscEnv -> FilePath -> ModIface -> IO () #if MIN_VERSION_ghc(9,3,0) diff --git a/ghcide/src/Development/IDE/GHC/Compat/Logger.hs b/ghcide/src/Development/IDE/GHC/Compat/Logger.hs index d7bc9deadc..6c520dc2a7 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Logger.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Logger.hs @@ -13,17 +13,26 @@ import Development.IDE.GHC.Compat.Core import Development.IDE.GHC.Compat.Env as Env import Development.IDE.GHC.Compat.Outputable +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,0,0) +import DynFlags +import Outputable (queryQual) +#endif + #if MIN_VERSION_ghc(9,0,0) -import GHC.Driver.Session as DynFlags import GHC.Utils.Outputable +#endif + +#if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,2,0) +import GHC.Driver.Session as DynFlags +#endif + #if MIN_VERSION_ghc(9,2,0) import GHC.Driver.Env (hsc_logger) import GHC.Utils.Logger as Logger #endif -#else -import DynFlags -import Outputable (queryQual) -#endif + #if MIN_VERSION_ghc(9,3,0) import GHC.Types.Error #endif diff --git a/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs b/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs index c4f9cd57bd..c3d8fef64c 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs @@ -49,17 +49,36 @@ module Development.IDE.GHC.Compat.Outputable ( textDoc, ) where +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,0,0) +import Development.IDE.GHC.Compat.Core (GlobalRdrEnv) +import DynFlags +import ErrUtils hiding (mkWarnMsg) +import qualified ErrUtils as Err +import HscTypes +import Outputable as Out hiding + (defaultUserStyle) +import qualified Outputable as Out +import SrcLoc +#endif + +#if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,2,0) +import GHC.Driver.Session +import GHC.Driver.Types as HscTypes +import GHC.Types.Name.Reader (GlobalRdrEnv) +import GHC.Types.SrcLoc +import GHC.Utils.Error as Err hiding (mkWarnMsg) +import qualified GHC.Utils.Error as Err +import GHC.Utils.Outputable as Out hiding + (defaultUserStyle) +import qualified GHC.Utils.Outputable as Out +#endif #if MIN_VERSION_ghc(9,2,0) import GHC.Driver.Env import GHC.Driver.Ppr import GHC.Driver.Session -#if !MIN_VERSION_ghc(9,3,0) -import GHC.Parser.Errors -#else -import GHC.Parser.Errors.Types -#endif -import qualified GHC.Parser.Errors.Ppr as Ppr import qualified GHC.Types.Error as Error import GHC.Types.Name.Ppr import GHC.Types.Name.Reader @@ -71,34 +90,21 @@ import GHC.Utils.Outputable as Out hiding (defaultUserStyle) import qualified GHC.Utils.Outputable as Out import GHC.Utils.Panic -#elif MIN_VERSION_ghc(9,0,0) -import GHC.Driver.Session -import GHC.Driver.Types as HscTypes -import GHC.Types.Name.Reader (GlobalRdrEnv) -import GHC.Types.SrcLoc -import GHC.Utils.Error as Err hiding (mkWarnMsg) -import qualified GHC.Utils.Error as Err -import GHC.Utils.Outputable as Out hiding - (defaultUserStyle) -import qualified GHC.Utils.Outputable as Out -#else -import Development.IDE.GHC.Compat.Core (GlobalRdrEnv) -import DynFlags -import ErrUtils hiding (mkWarnMsg) -import qualified ErrUtils as Err -import HscTypes -import Outputable as Out hiding - (defaultUserStyle) -import qualified Outputable as Out -import SrcLoc #endif -#if MIN_VERSION_ghc(9,5,0) -import GHC.Driver.Errors.Types (GhcMessage) + +#if MIN_VERSION_ghc(9,2,0) && !MIN_VERSION_ghc(9,3,0) +import GHC.Parser.Errors +import qualified GHC.Parser.Errors.Ppr as Ppr #endif + #if MIN_VERSION_ghc(9,3,0) import Data.Maybe import GHC.Driver.Config.Diagnostic -import GHC.Utils.Logger +import GHC.Parser.Errors.Types +#endif + +#if MIN_VERSION_ghc(9,5,0) +import GHC.Driver.Errors.Types (GhcMessage) #endif #if MIN_VERSION_ghc(9,5,0) @@ -221,16 +227,18 @@ type WarnMsg = MsgEnvelope DecoratedSDoc #endif mkPrintUnqualifiedDefault :: HscEnv -> GlobalRdrEnv -> PrintUnqualified -mkPrintUnqualifiedDefault env = #if MIN_VERSION_ghc(9,5,0) +mkPrintUnqualifiedDefault env = mkNamePprCtx ptc (hsc_unit_env env) where ptc = initPromotionTickContext (hsc_dflags env) #elif MIN_VERSION_ghc(9,2,0) +mkPrintUnqualifiedDefault env = -- GHC 9.2 version -- mkPrintUnqualified :: UnitEnv -> GlobalRdrEnv -> PrintUnqualified mkPrintUnqualified (hsc_unit_env env) #else +mkPrintUnqualifiedDefault env = HscTypes.mkPrintUnqualified (hsc_dflags env) #endif diff --git a/ghcide/src/Development/IDE/GHC/Compat/Parser.hs b/ghcide/src/Development/IDE/GHC/Compat/Parser.hs index 2fd5b74efd..cb3cece8e1 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Parser.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Parser.hs @@ -45,46 +45,51 @@ module Development.IDE.GHC.Compat.Parser ( pattern EpaBlockComment ) where -#if MIN_VERSION_ghc(9,0,0) -#if !MIN_VERSION_ghc(9,2,0) -import qualified GHC.Driver.Types as GHC +import Development.IDE.GHC.Compat.Core +import Development.IDE.GHC.Compat.Util + +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,0,0) +import qualified ApiAnnotation as Anno +import qualified HscTypes as GHC +import Lexer +import qualified SrcLoc #endif + +#if MIN_VERSION_ghc(9,0,0) import qualified GHC.Parser.Annotation as Anno import qualified GHC.Parser.Lexer as Lexer import GHC.Types.SrcLoc (PsSpan (..)) +#endif + +#if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,2,0) +import qualified GHC.Driver.Types as GHC +#endif + +#if !MIN_VERSION_ghc(9,2,0) +import qualified Data.Map as Map +import qualified GHC +#endif + #if MIN_VERSION_ghc(9,2,0) -import GHC (Anchor (anchor), - EpAnnComments (priorComments), - EpaComment (EpaComment), - EpaCommentTok (..), - epAnnComments, +import GHC (EpaCommentTok (..), pm_extra_src_files, pm_mod_summary, pm_parsed_source) import qualified GHC -#if MIN_VERSION_ghc(9,3,0) -import qualified GHC.Driver.Config.Parser as Config -#else -import qualified GHC.Driver.Config as Config +import GHC.Hs (hpm_module, hpm_src_files) #endif -import GHC.Hs (LEpaComment, hpm_module, - hpm_src_files) -import GHC.Parser.Lexer hiding (initParserState) -#endif -#else -import qualified ApiAnnotation as Anno -import qualified HscTypes as GHC -import Lexer -import qualified SrcLoc + +#if MIN_VERSION_ghc(9,2,0) && !MIN_VERSION_ghc(9,3,0) +import qualified GHC.Driver.Config as Config #endif -import Development.IDE.GHC.Compat.Core -import Development.IDE.GHC.Compat.Util -#if !MIN_VERSION_ghc(9,2,0) -import qualified Data.Map as Map -import qualified GHC +#if MIN_VERSION_ghc(9,3,0) +import qualified GHC.Driver.Config.Parser as Config #endif + #if !MIN_VERSION_ghc(9,0,0) type ParserOpts = DynFlags #elif !MIN_VERSION_ghc(9,2,0) @@ -131,7 +136,7 @@ pattern HsParsedModule , hpm_annotations } <- ( (,()) -> (GHC.HsParsedModule{..}, hpm_annotations)) where - HsParsedModule hpm_module hpm_src_files hpm_annotations = + HsParsedModule hpm_module hpm_src_files _hpm_annotations = GHC.HsParsedModule hpm_module hpm_src_files #endif diff --git a/ghcide/src/Development/IDE/GHC/Compat/Plugins.hs b/ghcide/src/Development/IDE/GHC/Compat/Plugins.hs index 79e1602e02..9f5ea50ab7 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Plugins.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Plugins.hs @@ -19,33 +19,48 @@ module Development.IDE.GHC.Compat.Plugins ( getPsMessages ) where -#if MIN_VERSION_ghc(9,0,0) -#if MIN_VERSION_ghc(9,2,0) -import qualified GHC.Driver.Env as Env +import Development.IDE.GHC.Compat.Core +import Development.IDE.GHC.Compat.Env (hscSetFlags, hsc_dflags) +import Development.IDE.GHC.Compat.Parser as Parser + +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,0,0) +import qualified DynamicLoading as Loader +import Plugins #endif + +#if MIN_VERSION_ghc(9,0,0) import GHC.Driver.Plugins (Plugin (..), PluginWithArgs (..), StaticPlugin (..), defaultPlugin, withPlugins) +import qualified GHC.Runtime.Loader as Loader +#endif + +#if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,3,0) +import Development.IDE.GHC.Compat.Outputable as Out +#endif + +#if MIN_VERSION_ghc(9,2,0) +import qualified GHC.Driver.Env as Env +#endif + +#if MIN_VERSION_ghc(9,2,0) && !MIN_VERSION_ghc(9,3,0) +import Data.Bifunctor (bimap) +#endif + +#if !MIN_VERSION_ghc(9,3,0) +import Development.IDE.GHC.Compat.Util (Bag) +#endif + #if MIN_VERSION_ghc(9,3,0) import GHC.Driver.Plugins (ParsedResult (..), PsMessages (..), staticPlugins) import qualified GHC.Parser.Lexer as Lexer -#else -import Data.Bifunctor (bimap) #endif -import qualified GHC.Runtime.Loader as Loader -#else -import qualified DynamicLoading as Loader -import Plugins -#endif -import Development.IDE.GHC.Compat.Core -import Development.IDE.GHC.Compat.Env (hscSetFlags, hsc_dflags) -import Development.IDE.GHC.Compat.Outputable as Out -import Development.IDE.GHC.Compat.Parser as Parser -import Development.IDE.GHC.Compat.Util (Bag) #if !MIN_VERSION_ghc(9,3,0) @@ -53,7 +68,7 @@ type PsMessages = (Bag WarnMsg, Bag ErrMsg) #endif getPsMessages :: PState -> DynFlags -> PsMessages -getPsMessages pst dflags = +getPsMessages pst _dflags = --dfags is only used if GHC < 9.2 #if MIN_VERSION_ghc(9,3,0) uncurry PsMessages $ Lexer.getPsMessages pst #else @@ -62,12 +77,13 @@ getPsMessages pst dflags = #endif getMessages pst #if !MIN_VERSION_ghc(9,2,0) - dflags + _dflags #endif #endif applyPluginsParsedResultAction :: HscEnv -> DynFlags -> ModSummary -> Parser.ApiAnns -> ParsedSource -> PsMessages -> IO (ParsedSource, PsMessages) -applyPluginsParsedResultAction env dflags ms hpm_annotations parsed msgs = do +applyPluginsParsedResultAction env _dflags ms hpm_annotations parsed msgs = do + -- dflags is only used in GHC < 9.2 -- Apply parsedResultAction of plugins let applyPluginAction p opts = parsedResultAction p opts ms #if MIN_VERSION_ghc(9,3,0) @@ -80,7 +96,7 @@ applyPluginsParsedResultAction env dflags ms hpm_annotations parsed msgs = do #elif MIN_VERSION_ghc(9,2,0) env #else - dflags + _dflags #endif applyPluginAction #if MIN_VERSION_ghc(9,3,0) diff --git a/ghcide/src/Development/IDE/GHC/Compat/Units.hs b/ghcide/src/Development/IDE/GHC/Compat/Units.hs index 5b1b5e0c58..4c40f7f0cf 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Units.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Units.hs @@ -53,41 +53,19 @@ module Development.IDE.GHC.Compat.Units ( findImportedModule, ) where -import Control.Monad -import qualified Data.List.NonEmpty as NE -import qualified Data.Map.Strict as Map -#if MIN_VERSION_ghc(9,3,0) -import GHC.Unit.Home.ModInfo -#endif -#if MIN_VERSION_ghc(9,0,0) -#if MIN_VERSION_ghc(9,2,0) -import qualified GHC.Data.ShortText as ST -#if !MIN_VERSION_ghc(9,3,0) -import GHC.Driver.Env (hsc_unit_dbs) -#endif -import GHC.Driver.Ppr -import GHC.Unit.Env -import GHC.Unit.External -import GHC.Unit.Finder hiding - (findImportedModule) -#else -import GHC.Driver.Types -#endif -import GHC.Data.FastString -import qualified GHC.Driver.Session as DynFlags -import GHC.Types.Unique.Set -import qualified GHC.Unit.Info as UnitInfo -import GHC.Unit.State (LookupResult, UnitInfo, - UnitState (unitInfoMap)) -import qualified GHC.Unit.State as State -import GHC.Unit.Types hiding (moduleUnit, - toUnitId) -import qualified GHC.Unit.Types as Unit -import GHC.Utils.Outputable -#else +import Data.Either +import Data.Version +import Development.IDE.GHC.Compat.Core +import Development.IDE.GHC.Compat.Env +import Development.IDE.GHC.Compat.Outputable +import Prelude hiding (mod) + +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,0,0) import qualified DynFlags import FastString -import GhcPlugins (SDoc, showSDocForUser) +import qualified Finder as GHC import HscTypes import Module hiding (moduleUnitId) import qualified Module @@ -101,27 +79,52 @@ import Packages (InstalledPackageInfo (ha import qualified Packages #endif -import Development.IDE.GHC.Compat.Core -import Development.IDE.GHC.Compat.Env -import Development.IDE.GHC.Compat.Outputable +#if MIN_VERSION_ghc(9,0,0) +import GHC.Types.Unique.Set +import qualified GHC.Unit.Info as UnitInfo +import GHC.Unit.State (LookupResult, UnitInfo, + UnitState (unitInfoMap)) +import qualified GHC.Unit.State as State +import GHC.Unit.Types hiding (moduleUnit, + toUnitId) +import qualified GHC.Unit.Types as Unit +#endif + #if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,2,0) import Data.Map (Map) +import qualified GHC.Driver.Finder as GHC +import qualified GHC.Driver.Session as DynFlags +import GHC.Driver.Types #endif -import Data.Either -import Data.Version -import qualified GHC -#if MIN_VERSION_ghc(9,3,0) -import GHC.Types.PkgQual (PkgQual (NoPkgQual)) +#if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,3,0) +import GHC.Data.FastString + #endif -#if MIN_VERSION_ghc(9,1,0) + +#if MIN_VERSION_ghc(9,2,0) +import qualified GHC.Data.ShortText as ST +import GHC.Unit.External import qualified GHC.Unit.Finder as GHC -#elif MIN_VERSION_ghc(9,0,0) -import qualified GHC.Driver.Finder as GHC -#else -import qualified Finder as GHC #endif +#if MIN_VERSION_ghc(9,2,0) && !MIN_VERSION_ghc(9,3,0) +import GHC.Unit.Env +import GHC.Unit.Finder hiding + (findImportedModule) +#endif + +#if MIN_VERSION_ghc(9,3,0) +import Control.Monad +import qualified Data.List.NonEmpty as NE +import qualified Data.Map.Strict as Map +import qualified GHC +import qualified GHC.Driver.Session as DynFlags +import GHC.Types.PkgQual (PkgQual (NoPkgQual)) +import GHC.Unit.Home.ModInfo +#endif + + #if MIN_VERSION_ghc(9,0,0) type PreloadUnitClosure = UniqSet UnitId #if MIN_VERSION_ghc(9,2,0) diff --git a/ghcide/src/Development/IDE/GHC/Compat/Util.hs b/ghcide/src/Development/IDE/GHC/Compat/Util.hs index b0ef8e1217..4ad42cee8a 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Util.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Util.hs @@ -69,23 +69,9 @@ module Development.IDE.GHC.Compat.Util ( atEnd, ) where -#if MIN_VERSION_ghc(9,0,0) -import Control.Exception.Safe (MonadCatch, catch, try) -import GHC.Data.Bag -import GHC.Data.BooleanFormula -import GHC.Data.EnumSet +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] -import GHC.Data.FastString -import GHC.Data.Maybe -import GHC.Data.Pair -import GHC.Data.StringBuffer -import GHC.Types.Unique -import GHC.Types.Unique.DFM -import GHC.Utils.Fingerprint -import GHC.Utils.Misc -import GHC.Utils.Outputable (pprHsString) -import GHC.Utils.Panic hiding (try) -#else +#if !MIN_VERSION_ghc(9,0,0) import Bag import BooleanFormula import EnumSet @@ -102,6 +88,27 @@ import Unique import Util #endif +#if MIN_VERSION_ghc(9,0,0) +import Control.Exception.Safe (MonadCatch, catch, try) +import GHC.Data.Bag +import GHC.Data.BooleanFormula +import GHC.Data.EnumSet + +import GHC.Data.FastString +import GHC.Data.Maybe +import GHC.Data.Pair +import GHC.Data.StringBuffer +import GHC.Types.Unique +import GHC.Types.Unique.DFM +import GHC.Utils.Fingerprint +import GHC.Utils.Outputable (pprHsString) +import GHC.Utils.Panic hiding (try) +#endif + +#if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,3,0) +import GHC.Utils.Misc +#endif + #if MIN_VERSION_ghc(9,3,0) import GHC.Data.Bool #endif diff --git a/ghcide/src/Development/IDE/GHC/CoreFile.hs b/ghcide/src/Development/IDE/GHC/CoreFile.hs index ed11a26300..1702addf52 100644 --- a/ghcide/src/Development/IDE/GHC/CoreFile.hs +++ b/ghcide/src/Development/IDE/GHC/CoreFile.hs @@ -19,10 +19,25 @@ import Data.Foldable import Data.IORef import Data.List (isPrefixOf) import Data.Maybe -import qualified Data.Text as T +import qualified Data.Text as T +import Development.IDE.GHC.Compat +import qualified Development.IDE.GHC.Compat.Util as Util import GHC.Fingerprint +import Prelude hiding (mod) -import Development.IDE.GHC.Compat +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,0,0) +import Binary +import BinFingerprint (fingerprintBinMem) +import BinIface +import CoreSyn +import HscTypes +import IfaceEnv +import MkId +import TcIface +import ToIface +#endif #if MIN_VERSION_ghc(9,0,0) import GHC.Core @@ -33,29 +48,16 @@ import GHC.Iface.Recomp.Binary (fingerprintBinMem) import GHC.IfaceToCore import GHC.Types.Id.Make import GHC.Utils.Binary +#endif -#if MIN_VERSION_ghc(9,2,0) -import GHC.Types.TypeEnv -#else +#if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,2,0) import GHC.Driver.Types #endif -#else -import Binary -import BinFingerprint (fingerprintBinMem) -import BinIface -import CoreSyn -import HscTypes -import IdInfo -import IfaceEnv -import MkId -import TcIface -import ToIface -import Unique -import Var +#if MIN_VERSION_ghc(9,2,0) +import GHC.Types.TypeEnv #endif -import qualified Development.IDE.GHC.Compat.Util as Util -- | Initial ram buffer to allocate for writing interface files initBinMemSize :: Int @@ -118,7 +120,7 @@ writeBinCoreFile core_path fat_iface = do #if MIN_VERSION_ghc(9,2,0) QuietBinIFace #else - (const $ pure ()) + const $ pure () #endif putWithUserData quietTrace bh fat_iface @@ -141,7 +143,7 @@ codeGutsToCoreFile codeGutsToCoreFile hash CgGuts{..} = CoreFile (map (toIfaceTopBind1 cg_module) cg_binds) hash #else codeGutsToCoreFile hash CgGuts{..} = CoreFile (map (toIfaceTopBind1 cg_module) $ filter isNotImplictBind cg_binds) hash -#endif + -- | Implicit binds can be generated from the interface and are not tidied, -- so we must filter them out isNotImplictBind :: CoreBind -> Bool @@ -150,6 +152,7 @@ isNotImplictBind bind = any (not . isImplicitId) $ bindBindings bind bindBindings :: CoreBind -> [Var] bindBindings (NonRec b _) = [b] bindBindings (Rec bnds) = map fst bnds +#endif getImplicitBinds :: TyCon -> [CoreBind] getImplicitBinds tc = cls_binds ++ getTyConImplicitBinds tc @@ -167,14 +170,14 @@ getClassImplicitBinds cls | (op, val_index) <- classAllSelIds cls `zip` [0..] ] get_defn :: Id -> CoreBind -get_defn id = NonRec id (unfoldingTemplate (realIdUnfolding id)) +get_defn identifier = NonRec identifier (unfoldingTemplate (realIdUnfolding identifier)) toIfaceTopBndr1 :: Module -> Id -> IfaceId -toIfaceTopBndr1 mod id - = IfaceId (mangleDeclName mod $ getName id) - (toIfaceType (idType id)) - (toIfaceIdDetails (idDetails id)) - (toIfaceIdInfo (idInfo id)) +toIfaceTopBndr1 mod identifier + = IfaceId (mangleDeclName mod $ getName identifier) + (toIfaceType (idType identifier)) + (toIfaceIdDetails (idDetails identifier)) + (toIfaceIdInfo (idInfo identifier)) toIfaceTopBind1 :: Module -> Bind Id -> TopIfaceBinding IfaceId toIfaceTopBind1 mod (NonRec b r) = TopIfaceNonRec (toIfaceTopBndr1 mod b) (toIfaceExpr r) @@ -224,8 +227,8 @@ tcIfaceId = fmap getIfaceId . tcIfaceDecl False <=< unmangle_decl_name pure $ ifid{ ifName = name' } | otherwise = pure ifid -- invariant: 'IfaceId' is always a 'IfaceId' constructor - getIfaceId (AnId id) = id - getIfaceId _ = error "tcIfaceId: got non Id" + getIfaceId (AnId identifier) = identifier + getIfaceId _ = error "tcIfaceId: got non Id" tc_iface_bindings :: TopIfaceBinding Id -> IfL CoreBind tc_iface_bindings (TopIfaceNonRec v e) = do diff --git a/ghcide/src/Development/IDE/GHC/Error.hs b/ghcide/src/Development/IDE/GHC/Error.hs index a8f5e88ca3..8b5c9edc29 100644 --- a/ghcide/src/Development/IDE/GHC/Error.hs +++ b/ghcide/src/Development/IDE/GHC/Error.hs @@ -175,12 +175,12 @@ realSpan = \case -- diagnostics catchSrcErrors :: DynFlags -> T.Text -> IO a -> IO (Either [FileDiagnostic] a) catchSrcErrors dflags fromWhere ghcM = do - Compat.handleGhcException (ghcExceptionToDiagnostics dflags) $ - handleSourceError (sourceErrorToDiagnostics dflags) $ + Compat.handleGhcException ghcExceptionToDiagnostics $ + handleSourceError sourceErrorToDiagnostics $ Right <$> ghcM where - ghcExceptionToDiagnostics dflags = return . Left . diagFromGhcException fromWhere dflags - sourceErrorToDiagnostics dflags = return . Left . diagFromErrMsgs fromWhere dflags + ghcExceptionToDiagnostics = return . Left . diagFromGhcException fromWhere dflags + sourceErrorToDiagnostics = return . Left . diagFromErrMsgs fromWhere dflags #if MIN_VERSION_ghc(9,3,0) . fmap (fmap Compat.renderDiagnosticMessageWithHints) . Compat.getMessages #endif diff --git a/ghcide/src/Development/IDE/GHC/Orphans.hs b/ghcide/src/Development/IDE/GHC/Orphans.hs index 581ae70567..456d7f0f07 100644 --- a/ghcide/src/Development/IDE/GHC/Orphans.hs +++ b/ghcide/src/Development/IDE/GHC/Orphans.hs @@ -8,49 +8,57 @@ -- | Orphan instances for GHC. -- Note that the 'NFData' instances may not be law abiding. module Development.IDE.GHC.Orphans() where +import Development.IDE.GHC.Compat +import Development.IDE.GHC.Util -#if MIN_VERSION_ghc(9,2,0) -import GHC.Parser.Annotation +import Control.DeepSeq +import Control.Monad.Trans.Reader (ReaderT (..)) +import Data.Aeson +import Data.Hashable +import Data.String (IsString (fromString)) +import Data.Text (unpack) + +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,0,0) +import Bag +import ByteCodeTypes +import GhcPlugins +import qualified StringBuffer as SB +import Unique (getKey) #endif + #if MIN_VERSION_ghc(9,0,0) +import GHC.ByteCode.Types import GHC.Data.Bag import GHC.Data.FastString import qualified GHC.Data.StringBuffer as SB -import GHC.Types.Name.Occurrence import GHC.Types.SrcLoc -import GHC.Types.Unique (getKey) -import GHC.Unit.Info -import GHC.Utils.Outputable -#else -import Bag -import GhcPlugins -import qualified StringBuffer as SB -import Unique (getKey) -#endif +#endif -import Development.IDE.GHC.Compat -import Development.IDE.GHC.Util +#if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,3,0) +import GHC (ModuleGraph) +import GHC.Types.Unique (getKey) +#endif -import Control.DeepSeq -import Data.Aeson +#if MIN_VERSION_ghc(9,2,0) import Data.Bifunctor (Bifunctor (..)) -import Data.Hashable -import Data.String (IsString (fromString)) -import Data.Text (unpack) -#if MIN_VERSION_ghc(9,0,0) -import GHC.ByteCode.Types -import GHC (ModuleGraph) -#else -import ByteCodeTypes +import GHC.Parser.Annotation #endif + #if MIN_VERSION_ghc(9,3,0) import GHC.Types.PkgQual #endif + #if MIN_VERSION_ghc(9,5,0) import GHC.Unit.Home.ModInfo #endif +-- Orphan instance for Shake.hs +-- https://hub.darcs.net/ross/transformers/issue/86 +deriving instance (Semigroup (m a)) => Semigroup (ReaderT r m a) + -- Orphan instances for types from the GHC API. instance Show CoreModule where show = unpack . printOutputable instance NFData CoreModule where rnf = rwhnf @@ -241,7 +249,7 @@ instance NFData HomeModLinkable where rnf = rwhnf #endif -instance NFData (HsExpr (GhcPass 'Renamed)) where +instance NFData (HsExpr (GhcPass Renamed)) where rnf = rwhnf instance NFData Extension where diff --git a/ghcide/src/Development/IDE/GHC/Util.hs b/ghcide/src/Development/IDE/GHC/Util.hs index ca108ebc4d..c3cbf4c572 100644 --- a/ghcide/src/Development/IDE/GHC/Util.hs +++ b/ghcide/src/Development/IDE/GHC/Util.hs @@ -30,25 +30,6 @@ module Development.IDE.GHC.Util( getExtensions ) where -#if MIN_VERSION_ghc(9,2,0) -import GHC.Data.EnumSet -import GHC.Data.FastString -import GHC.Data.StringBuffer -import GHC.Driver.Env hiding (hscSetFlags) -import GHC.Driver.Monad -import GHC.Driver.Session hiding (ExposePackage) -import GHC.Parser.Lexer -import GHC.Runtime.Context -import GHC.Types.Name.Occurrence -import GHC.Types.Name.Reader -import GHC.Types.SrcLoc -import GHC.Unit.Module.ModDetails -import GHC.Unit.Module.ModGuts -import GHC.Utils.Fingerprint -import GHC.Utils.Outputable -#else -import Development.IDE.GHC.Compat.Util -#endif import Control.Concurrent import Control.Exception as E import Data.Binary.Put (Put, runPut) @@ -56,40 +37,43 @@ import qualified Data.ByteString as BS import Data.ByteString.Internal (ByteString (..)) import qualified Data.ByteString.Internal as BS import qualified Data.ByteString.Lazy as LBS -import Data.Data (Data) import Data.IORef import Data.List.Extra import Data.Maybe import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.Text.Encoding.Error as T -import Data.Time.Clock.POSIX (POSIXTime, getCurrentTime, - utcTimeToPOSIXSeconds) import Data.Typeable -import qualified Data.Unique as U -import Debug.Trace -import Development.IDE.GHC.Compat as GHC +import Development.IDE.GHC.Compat as GHC hiding (unitState) import qualified Development.IDE.GHC.Compat.Parser as Compat import qualified Development.IDE.GHC.Compat.Units as Compat import Development.IDE.Types.Location import Foreign.ForeignPtr import Foreign.Ptr import Foreign.Storable -import GHC hiding (ParsedModule (..)) +import GHC hiding (ParsedModule (..), + parser) import GHC.IO.BufferedIO (BufferedIO) import GHC.IO.Device as IODevice import GHC.IO.Encoding import GHC.IO.Exception import GHC.IO.Handle.Internals import GHC.IO.Handle.Types -import GHC.Stack import Ide.PluginUtils (unescape) -import System.Environment.Blank (getEnvDefault) import System.FilePath -import System.IO.Unsafe -import Text.Printf +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] +#if !MIN_VERSION_ghc(9,2,0) +import Development.IDE.GHC.Compat.Util +#endif + +#if MIN_VERSION_ghc(9,2,0) +import GHC.Data.EnumSet +import GHC.Data.FastString +import GHC.Data.StringBuffer +import GHC.Utils.Fingerprint +#endif ---------------------------------------------------------------------- -- GHC setup @@ -189,9 +173,9 @@ cgGutsToCoreModule safeMode guts modDetails = CoreModule -- Will produce an 8 byte unreadable ByteString. fingerprintToBS :: Fingerprint -> BS.ByteString fingerprintToBS (Fingerprint a b) = BS.unsafeCreate 8 $ \ptr -> do - ptr <- pure $ castPtr ptr - pokeElemOff ptr 0 a - pokeElemOff ptr 1 b + ptr' <- pure $ castPtr ptr + pokeElemOff ptr' 0 a + pokeElemOff ptr' 1 b -- | Take the 'Fingerprint' of a 'StringBuffer'. fingerprintFromStringBuffer :: StringBuffer -> IO Fingerprint diff --git a/ghcide/src/Development/IDE/Import/DependencyInformation.hs b/ghcide/src/Development/IDE/Import/DependencyInformation.hs index d255c3ac1e..6ae27e2912 100644 --- a/ghcide/src/Development/IDE/Import/DependencyInformation.hs +++ b/ghcide/src/Development/IDE/Import/DependencyInformation.hs @@ -1,5 +1,6 @@ -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 +{-# LANGUAGE CPP #-} module Development.IDE.Import.DependencyInformation ( DependencyInformation(..) @@ -33,7 +34,7 @@ import Control.DeepSeq import Data.Bifunctor import Data.Coerce import Data.Either -import Data.Graph +import Data.Graph hiding (edges, path) import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HMS import Data.IntMap (IntMap) @@ -48,13 +49,19 @@ import Data.Maybe import Data.Tuple.Extra hiding (first, second) import Development.IDE.GHC.Orphans () import GHC.Generics (Generic) +import Prelude hiding (mod) import Development.IDE.Import.FindImports (ArtifactsLocation (..)) import Development.IDE.Types.Diagnostics import Development.IDE.Types.Location +import Development.IDE.GHC.Compat + +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,3,0) import GHC -import Development.IDE.GHC.Compat +#endif -- | The imports for a given module. newtype ModuleImports = ModuleImports @@ -92,14 +99,14 @@ getPathId path m@PathIdMap{..} = case HMS.lookup (artifactFilePath path) pathToIdMap of Nothing -> let !newId = FilePathId nextFreshId - in (newId, insertPathId path newId m) - Just id -> (id, m) + in (newId, insertPathId newId ) + Just fileId -> (fileId, m) where - insertPathId :: ArtifactsLocation -> FilePathId -> PathIdMap -> PathIdMap - insertPathId path id PathIdMap{..} = + insertPathId :: FilePathId -> PathIdMap + insertPathId fileId = PathIdMap - (IntMap.insert (getFilePathId id) path idToPathMap) - (HMS.insert (artifactFilePath path) id pathToIdMap) + (IntMap.insert (getFilePathId fileId) path idToPathMap) + (HMS.insert (artifactFilePath path) fileId pathToIdMap) (succ nextFreshId) insertImport :: FilePathId -> Either ModuleParseError ModuleImports -> RawDependencyInformation -> RawDependencyInformation @@ -115,7 +122,7 @@ idToPath :: PathIdMap -> FilePathId -> NormalizedFilePath idToPath pathIdMap filePathId = artifactFilePath $ idToModLocation pathIdMap filePathId idToModLocation :: PathIdMap -> FilePathId -> ArtifactsLocation -idToModLocation PathIdMap{idToPathMap} (FilePathId id) = idToPathMap IntMap.! id +idToModLocation PathIdMap{idToPathMap} (FilePathId i) = idToPathMap IntMap.! i type BootIdMap = FilePathIdMap FilePathId @@ -137,7 +144,7 @@ data DependencyInformation = DependencyInformation { depErrorNodes :: !(FilePathIdMap (NonEmpty NodeError)) -- ^ Nodes that cannot be processed correctly. - , depModules :: !(FilePathIdMap ShowableModule) + , depModules :: !(FilePathIdMap ShowableModule) , depModuleDeps :: !(FilePathIdMap FilePathIdSet) -- ^ For a non-error node, this contains the set of module immediate dependencies -- in the same package. @@ -273,9 +280,9 @@ buildResultGraph g = propagatedErrors errorsForCycle files = IntMap.fromListWith (<>) $ coerce $ concatMap (cycleErrorsForFile files) files cycleErrorsForFile :: [FilePathId] -> FilePathId -> [(FilePathId,NodeResult)] - cycleErrorsForFile cycle f = - let entryPoints = mapMaybe (findImport f) cycle - in map (\imp -> (f, ErrorNode (PartOfCycle imp cycle :| []))) entryPoints + cycleErrorsForFile cycles' f = + let entryPoints = mapMaybe (findImport f) cycles' + in map (\imp -> (f, ErrorNode (PartOfCycle imp cycles' :| []))) entryPoints otherErrors = IntMap.map otherErrorsForFile g otherErrorsForFile :: Either ModuleParseError ModuleImports -> NodeResult otherErrorsForFile (Left err) = ErrorNode (ParseError err :| []) diff --git a/ghcide/src/Development/IDE/Import/FindImports.hs b/ghcide/src/Development/IDE/Import/FindImports.hs index a8e63bf4a1..506487415e 100644 --- a/ghcide/src/Development/IDE/Import/FindImports.hs +++ b/ghcide/src/Development/IDE/Import/FindImports.hs @@ -15,7 +15,6 @@ module Development.IDE.Import.FindImports import Control.DeepSeq import Development.IDE.GHC.Compat as Compat -import Development.IDE.GHC.Compat.Util import Development.IDE.GHC.Error as ErrUtils import Development.IDE.GHC.Orphans () import Development.IDE.Types.Diagnostics @@ -27,6 +26,13 @@ import Control.Monad.IO.Class import Data.List (isSuffixOf) import Data.Maybe import System.FilePath + +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,3,0) +import Development.IDE.GHC.Compat.Util +#endif + #if MIN_VERSION_ghc(9,3,0) import GHC.Types.PkgQual import GHC.Unit.State @@ -55,14 +61,14 @@ instance NFData Import where rnf PackageImport = () modSummaryToArtifactsLocation :: NormalizedFilePath -> Maybe ModSummary -> ArtifactsLocation -modSummaryToArtifactsLocation nfp ms = ArtifactsLocation nfp (ms_location <$> ms) source mod +modSummaryToArtifactsLocation nfp ms = ArtifactsLocation nfp (ms_location <$> ms) source mbMod where isSource HsSrcFile = True isSource _ = False source = case ms of - Nothing -> "-boot" `isSuffixOf` fromNormalizedFilePath nfp - Just ms -> isSource (ms_hsc_src ms) - mod = ms_mod <$> ms + Nothing -> "-boot" `isSuffixOf` fromNormalizedFilePath nfp + Just modSum -> isSource (ms_hsc_src modSum) + mbMod = ms_mod <$> ms -- | locate a module in the file system. Where we go from *daml to Haskell locateModuleFile :: MonadIO m @@ -89,7 +95,7 @@ locateModuleFile import_dirss exts targetFor isSource modName = do -- as they can never be imported into another package. #if MIN_VERSION_ghc(9,3,0) mkImportDirs :: HscEnv -> (UnitId, DynFlags) -> Maybe (UnitId, [FilePath]) -mkImportDirs env (i, flags) = Just (i, importPaths flags) +mkImportDirs _env (i, flags) = Just (i, importPaths flags) #else mkImportDirs :: HscEnv -> (UnitId, DynFlags) -> Maybe (PackageName, (UnitId, [FilePath])) mkImportDirs env (i, flags) = (, (i, importPaths flags)) <$> getUnitName env i @@ -130,7 +136,7 @@ locateModule env comp_info exts targetFor modName mbPkgName isSource = do | Just (uid, dirs) <- lookup (PackageName pkgName) import_paths -> lookupLocal uid dirs #endif - | otherwise -> lookupInPackageDB env + | otherwise -> lookupInPackageDB #if MIN_VERSION_ghc(9,3,0) NoPkgQual -> do #else @@ -139,7 +145,7 @@ locateModule env comp_info exts targetFor modName mbPkgName isSource = do mbFile <- locateModuleFile ((homeUnitId_ dflags, importPaths dflags) : other_imports) exts targetFor isSource $ unLoc modName case mbFile of - Nothing -> lookupInPackageDB env + Nothing -> lookupInPackageDB Just (uid, file) -> toModLocation uid file where dflags = hsc_dflags env @@ -160,7 +166,7 @@ locateModule env comp_info exts targetFor modName mbPkgName isSource = do hpt_deps :: [UnitId] hpt_deps = homeUnitDepends units #else - import_paths' + _import_paths' #endif -- first try to find the module as a file. If we can't find it try to find it in the package @@ -168,7 +174,7 @@ locateModule env comp_info exts targetFor modName mbPkgName isSource = do -- Here the importPaths for the current modules are added to the front of the import paths from the other components. -- This is particularly important for Paths_* modules which get generated for every component but unless you use it in -- each component will end up being found in the wrong place and cause a multi-cradle match failure. - import_paths' = + _import_paths' = -- import_paths' is only used in GHC < 9.4 #if MIN_VERSION_ghc(9,3,0) import_paths #else @@ -178,19 +184,19 @@ locateModule env comp_info exts targetFor modName mbPkgName isSource = do toModLocation uid file = liftIO $ do loc <- mkHomeModLocation dflags (unLoc modName) (fromNormalizedFilePath file) #if MIN_VERSION_ghc(9,0,0) - let mod = mkModule (RealUnit $ Definite uid) (unLoc modName) -- TODO support backpack holes + let genMod = mkModule (RealUnit $ Definite uid) (unLoc modName) -- TODO support backpack holes #else - let mod = mkModule uid (unLoc modName) + let genMod = mkModule uid (unLoc modName) #endif - return $ Right $ FileImport $ ArtifactsLocation file (Just loc) (not isSource) (Just mod) + return $ Right $ FileImport $ ArtifactsLocation file (Just loc) (not isSource) (Just genMod) lookupLocal uid dirs = do mbFile <- locateModuleFile [(uid, dirs)] exts targetFor isSource $ unLoc modName case mbFile of Nothing -> return $ Left $ notFoundErr env modName $ LookupNotFound [] - Just (uid, file) -> toModLocation uid file + Just (uid', file) -> toModLocation uid' file - lookupInPackageDB env = do + lookupInPackageDB = do case Compat.lookupModuleWithSuggestions env (unLoc modName) mbPkgName of LookupFound _m _pkgConfig -> return $ Right PackageImport reason -> return $ Left $ notFoundErr env modName reason diff --git a/ghcide/src/Development/IDE/LSP/HoverDefinition.hs b/ghcide/src/Development/IDE/LSP/HoverDefinition.hs index 3a10b7c26e..eefe1a14f4 100644 --- a/ghcide/src/Development/IDE/LSP/HoverDefinition.hs +++ b/ghcide/src/Development/IDE/LSP/HoverDefinition.hs @@ -40,7 +40,7 @@ gotoTypeDefinition = request "TypeDefinition" getTypeDefinition (InR $ InR Null) hover = request "Hover" getAtPoint (InR Null) foundHover documentHighlight = request "DocumentHighlight" highlightAtPoint (InR Null) InL -references :: PluginMethodHandler IdeState 'Method_TextDocumentReferences +references :: PluginMethodHandler IdeState Method_TextDocumentReferences references ide _ (ReferenceParams (TextDocumentIdentifier uri) pos _ _ _) = do nfp <- getNormalizedFilePathE uri liftIO $ logDebug (ideLogger ide) $ @@ -48,7 +48,7 @@ references ide _ (ReferenceParams (TextDocumentIdentifier uri) pos _ _ _) = do " in file: " <> T.pack (show nfp) InL <$> (liftIO $ runAction "references" ide $ refsAtPoint nfp pos) -wsSymbols :: PluginMethodHandler IdeState 'Method_WorkspaceSymbol +wsSymbols :: PluginMethodHandler IdeState Method_WorkspaceSymbol wsSymbols ide _ (WorkspaceSymbolParams _ _ query) = liftIO $ do logDebug (ideLogger ide) $ "Workspace symbols request: " <> query runIdeAction "WorkspaceSymbols" (shakeExtras ide) $ InL . fromMaybe [] <$> workspaceSymbols query diff --git a/ghcide/src/Development/IDE/LSP/LanguageServer.hs b/ghcide/src/Development/IDE/LSP/LanguageServer.hs index d20f85adc1..51ed44f17f 100644 --- a/ghcide/src/Development/IDE/LSP/LanguageServer.hs +++ b/ghcide/src/Development/IDE/LSP/LanguageServer.hs @@ -43,7 +43,6 @@ import Development.IDE.Core.Tracing import qualified Development.IDE.Session as Session import Development.IDE.Types.Shake (WithHieDb) import Ide.Logger -import qualified Ide.Logger as Logger import Language.LSP.Server (LanguageContextEnv, LspServerLog, type (<~>)) @@ -77,8 +76,8 @@ instance Pretty Log where "Reactor thread stopped" LogCancelledRequest requestId -> "Cancelled request" <+> viaShow requestId - LogSession log -> pretty log - LogLspServer log -> pretty log + LogSession msg -> pretty msg + LogLspServer msg -> pretty msg -- used to smuggle RankNType WithHieDb through dbMVar newtype WithHieDbShield = WithHieDbShield WithHieDb @@ -211,16 +210,16 @@ handleInit recorder getHieDbLoc getIdeState lifetime exitClientMsg clearReqId wa let initConfig = parseConfiguration params - log Info $ LogRegisteringIdeConfig initConfig + logWith recorder Info $ LogRegisteringIdeConfig initConfig registerIdeConfiguration (shakeExtras ide) initConfig let handleServerException (Left e) = do - log Error $ LogReactorThreadException e + logWith recorder Error $ LogReactorThreadException e exitClientMsg handleServerException (Right _) = pure () exceptionInHandler e = do - log Error $ LogReactorMessageActionException e + logWith recorder Error $ LogReactorMessageActionException e checkCancelled _id act k = flip finally (clearReqId _id) $ @@ -232,15 +231,15 @@ handleInit recorder getHieDbLoc getIdeState lifetime exitClientMsg clearReqId wa cancelOrRes <- race (waitForCancel _id) act case cancelOrRes of Left () -> do - log Debug $ LogCancelledRequest _id + logWith recorder Debug $ LogCancelledRequest _id k $ ResponseError (InL LSPErrorCodes_RequestCancelled) "" Nothing Right res -> pure res ) $ \(e :: SomeException) -> do exceptionInHandler e k $ ResponseError (InR ErrorCodes_InternalError) (T.pack $ show e) Nothing _ <- flip forkFinally handleServerException $ do - untilMVar lifetime $ runWithDb (cmapWithPrio LogSession recorder) dbLoc $ \withHieDb hieChan -> do - putMVar dbMVar (WithHieDbShield withHieDb,hieChan) + untilMVar lifetime $ runWithDb (cmapWithPrio LogSession recorder) dbLoc $ \withHieDb' hieChan' -> do + putMVar dbMVar (WithHieDbShield withHieDb',hieChan') forever $ do msg <- readChan clientMsgChan -- We dispatch notifications synchronously and requests asynchronously @@ -248,13 +247,9 @@ handleInit recorder getHieDbLoc getIdeState lifetime exitClientMsg clearReqId wa case msg of ReactorNotification act -> handle exceptionInHandler act ReactorRequest _id act k -> void $ async $ checkCancelled _id act k - log Info LogReactorThreadStopped + logWith recorder Info LogReactorThreadStopped pure $ Right (env,ide) - where - log :: Logger.Priority -> Log -> IO () - log = logWith recorder - -- | Runs the action until it ends or until the given MVar is put. -- Rethrows any exceptions. diff --git a/ghcide/src/Development/IDE/LSP/Notifications.hs b/ghcide/src/Development/IDE/LSP/Notifications.hs index 6674bd4b86..16301e57f7 100644 --- a/ghcide/src/Development/IDE/LSP/Notifications.hs +++ b/ghcide/src/Development/IDE/LSP/Notifications.hs @@ -49,8 +49,8 @@ data Log instance Pretty Log where pretty = \case - LogShake log -> pretty log - LogFileStore log -> pretty log + LogShake msg -> pretty msg + LogFileStore msg -> pretty msg whenUriFile :: Uri -> (NormalizedFilePath -> IO ()) -> IO () whenUriFile uri act = whenJust (LSP.uriToFilePath uri) $ act . toNormalizedFilePath' diff --git a/ghcide/src/Development/IDE/LSP/Outline.hs b/ghcide/src/Development/IDE/LSP/Outline.hs index 77e622dfe2..7859e0e95e 100644 --- a/ghcide/src/Development/IDE/LSP/Outline.hs +++ b/ghcide/src/Development/IDE/LSP/Outline.hs @@ -11,10 +11,8 @@ where import Control.Monad.IO.Class import Data.Functor -import Data.Foldable (toList) import Data.Generics hiding (Prefix) import Data.Maybe -import qualified Data.Text as T import Development.IDE.Core.Rules import Development.IDE.Core.Shake import Development.IDE.GHC.Compat @@ -29,12 +27,20 @@ import Language.LSP.Protocol.Types (DocumentSymbol (..), TextDocumentIdentifier (TextDocumentIdentifier), type (|?) (InL, InR), uriToFilePath) import Language.LSP.Protocol.Message + +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + #if MIN_VERSION_ghc(9,2,0) -import Data.List.NonEmpty (nonEmpty) +import Data.List.NonEmpty (nonEmpty) +import Data.Foldable (toList) +#endif + +#if !MIN_VERSION_ghc(9,3,0) +import qualified Data.Text as T #endif moduleOutline - :: PluginMethodHandler IdeState 'Method_TextDocumentDocumentSymbol + :: PluginMethodHandler IdeState Method_TextDocumentDocumentSymbol moduleOutline ideState _ DocumentSymbolParams{ _textDocument = TextDocumentIdentifier uri } = liftIO $ case uriToFilePath uri of Just (toNormalizedFilePath' -> fp) -> do @@ -89,13 +95,13 @@ documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (TyClD _ ClassDecl { tcdLNa , _detail = Just "class" , _children = Just $ - [ (defDocumentSymbol l :: DocumentSymbol) + [ (defDocumentSymbol l' :: DocumentSymbol) { _name = printOutputable n , _kind = SymbolKind_Method - , _selectionRange = realSrcSpanToRange l' + , _selectionRange = realSrcSpanToRange l'' } - | L (locA -> (RealSrcSpan l _)) (ClassOpSig _ False names _) <- tcdSigs - , L (locA -> (RealSrcSpan l' _)) n <- names + | L (locA -> (RealSrcSpan l' _)) (ClassOpSig _ False names _) <- tcdSigs + , L (locA -> (RealSrcSpan l'' _)) n <- names ] } documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (TyClD _ DataDecl { tcdLName = L _ name, tcdDataDefn = HsDataDefn { dd_cons } })) @@ -104,28 +110,28 @@ documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (TyClD _ DataDecl { tcdLNam , _kind = SymbolKind_Struct , _children = Just $ - [ (defDocumentSymbol l :: DocumentSymbol) +#if MIN_VERSION_ghc(9,2,0) + [ (defDocumentSymbol l'' :: DocumentSymbol) { _name = printOutputable n , _kind = SymbolKind_Constructor , _selectionRange = realSrcSpanToRange l' -#if MIN_VERSION_ghc(9,2,0) , _children = toList <$> nonEmpty childs } | con <- extract_cons dd_cons , let (cs, flds) = hsConDeclsBinders con , let childs = mapMaybe cvtFld flds , L (locA -> RealSrcSpan l' _) n <- cs - , let l = case con of - L (locA -> RealSrcSpan l _) _ -> l + , let l'' = case con of + L (locA -> RealSrcSpan l''' _) _ -> l''' _ -> l' ] } where cvtFld :: LFieldOcc GhcPs -> Maybe DocumentSymbol #if MIN_VERSION_ghc(9,3,0) - cvtFld (L (locA -> RealSrcSpan l _) n) = Just $ (defDocumentSymbol l :: DocumentSymbol) + cvtFld (L (locA -> RealSrcSpan l' _) n) = Just $ (defDocumentSymbol l' :: DocumentSymbol) #else - cvtFld (L (RealSrcSpan l _) n) = Just $ (defDocumentSymbol l :: DocumentSymbol) + cvtFld (L (RealSrcSpan l' _) n) = Just $ (defDocumentSymbol l' :: DocumentSymbol) #endif #if MIN_VERSION_ghc(9,3,0) { _name = printOutputable (unLoc (foLabel n)) @@ -136,21 +142,25 @@ documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (TyClD _ DataDecl { tcdLNam } cvtFld _ = Nothing #else + [ (defDocumentSymbol l'' :: DocumentSymbol) + { _name = printOutputable n + , _kind = SymbolKind_Constructor + , _selectionRange = realSrcSpanToRange l' , _children = conArgRecordFields (con_args x) } - | L (locA -> (RealSrcSpan l _ )) x <- dd_cons + | L (locA -> (RealSrcSpan l'' _ )) x <- dd_cons , L (locA -> (RealSrcSpan l' _)) n <- getConNames' x ] } where -- | Extract the record fields of a constructor conArgRecordFields (RecCon (L _ lcdfs)) = Just - [ (defDocumentSymbol l :: DocumentSymbol) + [ (defDocumentSymbol l' :: DocumentSymbol) { _name = printOutputable n , _kind = SymbolKind_Field } | L _ cdf <- lcdfs - , L (locA -> (RealSrcSpan l _)) n <- rdrNameFieldOcc . unLoc <$> cd_fld_names cdf + , L (locA -> (RealSrcSpan l' _)) n <- rdrNameFieldOcc . unLoc <$> cd_fld_names cdf ] conArgRecordFields _ = Nothing #endif diff --git a/ghcide/src/Development/IDE/LSP/Server.hs b/ghcide/src/Development/IDE/LSP/Server.hs index bdfe407d5b..28bba2d526 100644 --- a/ghcide/src/Development/IDE/LSP/Server.hs +++ b/ghcide/src/Development/IDE/LSP/Server.hs @@ -14,7 +14,7 @@ import Control.Monad.IO.Unlift (MonadUnliftIO) import Control.Monad.Reader import Development.IDE.Core.Shake import Development.IDE.Core.Tracing -import Ide.Types (HasTracing, traceWithSpan) +import Ide.Types import Language.LSP.Protocol.Message import Language.LSP.Server (Handlers, LspM) import qualified Language.LSP.Server as LSP @@ -30,7 +30,7 @@ newtype ServerM c a = ServerM { unServerM :: ReaderT (ReactorChan, IdeState) (Ls deriving (Functor, Applicative, Monad, MonadReader (ReactorChan, IdeState), MonadIO, MonadUnliftIO, LSP.MonadLsp c) requestHandler - :: forall (m :: Method ClientToServer Request) c. (HasTracing (MessageParams m)) => + :: forall m c. PluginMethod Request m => SMethod m -> (IdeState -> MessageParams m -> LspM c (Either ResponseError (MessageResult m))) -> Handlers (ServerM c) @@ -45,7 +45,7 @@ requestHandler m k = LSP.requestHandler m $ \TRequestMessage{_method,_id,_params writeChan chan $ ReactorRequest (SomeLspId _id) (trace $ LSP.runLspT env $ resp' =<< k ide _params) (LSP.runLspT env . resp' . Left) notificationHandler - :: forall (m :: Method ClientToServer Notification) c. (HasTracing (MessageParams m)) => + :: forall m c. PluginMethod Notification m => SMethod m -> (IdeState -> VFS -> MessageParams m -> LspM c ()) -> Handlers (ServerM c) diff --git a/ghcide/src/Development/IDE/Main.hs b/ghcide/src/Development/IDE/Main.hs index b440b4c2ff..b1eb16a8fe 100644 --- a/ghcide/src/Development/IDE/Main.hs +++ b/ghcide/src/Development/IDE/Main.hs @@ -1,6 +1,5 @@ -{-# LANGUAGE PackageImports #-} {-# OPTIONS_GHC -Wno-orphans #-} -{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE RankNTypes #-} module Development.IDE.Main (Arguments(..) ,defaultArguments @@ -12,6 +11,7 @@ module Development.IDE.Main ,testing ,Log(..) ) where + import Control.Concurrent.Extra (withNumCapabilities) import Control.Concurrent.STM.Stats (dumpSTMStats) import Control.Exception.Safe (SomeException, @@ -30,10 +30,8 @@ import Data.List.Extra (intercalate, import Data.Maybe (catMaybes, isJust) import qualified Data.Text as T import Development.IDE (Action, - GhcVersion (..), Priority (Debug, Error), - Rules, ghcVersion, - hDuplicateTo') + Rules, hDuplicateTo') import Development.IDE.Core.Debouncer (Debouncer, newAsyncDebouncer) import Development.IDE.Core.FileStore (isWatchSupported) @@ -77,14 +75,6 @@ import Development.IDE.Session (SessionLoadingOptions import qualified Development.IDE.Session as Session import Development.IDE.Types.Location (NormalizedUri, toNormalizedFilePath') -import Ide.Logger (Logger, - Pretty (pretty), - Priority (Info, Warning), - Recorder, - WithPriority, - cmapWithPrio, - logWith, nest, vsep, - (<+>)) import Development.IDE.Types.Monitoring (Monitoring) import Development.IDE.Types.Options (IdeGhcSession, IdeOptions (optCheckParents, optCheckProject, optReportProgress, optRunSubset), @@ -99,6 +89,14 @@ import GHC.IO.Encoding (setLocaleEncoding) import GHC.IO.Handle (hDuplicate) import HIE.Bios.Cradle (findCradle) import qualified HieDb.Run as HieDb +import Ide.Logger (Logger, + Pretty (pretty), + Priority (Info), + Recorder, + WithPriority, + cmapWithPrio, + logWith, nest, vsep, + (<+>)) import Ide.Plugin.Config (CheckParents (NeverCheck), Config, checkParents, checkProject, @@ -147,7 +145,7 @@ data Log instance Pretty Log where pretty = \case - LogHeapStats log -> pretty log + LogHeapStats msg -> pretty msg LogLspStart pluginIds -> nest 2 $ vsep [ "Starting LSP server..." @@ -160,13 +158,13 @@ instance Pretty Log where "shouldRunSubset:" <+> pretty shouldRunSubset LogSetInitialDynFlagsException e -> "setInitialDynFlags:" <+> pretty (displayException e) - LogService log -> pretty log - LogShake log -> pretty log - LogGhcIde log -> pretty log - LogLanguageServer log -> pretty log - LogSession log -> pretty log - LogPluginHLS log -> pretty log - LogRules log -> pretty log + LogService msg -> pretty msg + LogShake msg -> pretty msg + LogGhcIde msg -> pretty msg + LogLanguageServer msg -> pretty msg + LogSession msg -> pretty msg + LogPluginHLS msg -> pretty msg + LogRules msg -> pretty msg data Command = Check [FilePath] -- ^ Typecheck some paths and print diagnostics. Exit code is the number of failures @@ -281,9 +279,6 @@ testing recorder logger plugins = defaultMain :: Recorder (WithPriority Log) -> Arguments -> IO () defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats recorder) fun where - log :: Priority -> Log -> IO () - log = logWith recorder - fun = do setLocaleEncoding utf8 pid <- T.pack . show <$> getProcessID @@ -306,14 +301,14 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re case argCommand of LSP -> withNumCapabilities numCapabilities $ do - t <- offsetTime - log Info $ LogLspStart (pluginId <$> ipMap argsHlsPlugins) + ioT <- offsetTime + logWith recorder Info $ LogLspStart (pluginId <$> ipMap argsHlsPlugins) let getIdeState :: LSP.LanguageContextEnv Config -> Maybe FilePath -> WithHieDb -> IndexQueue -> IO IdeState getIdeState env rootPath withHieDb hieChan = do traverse_ IO.setCurrentDirectory rootPath - t <- t - log Info $ LogLspStartDuration t + t <- ioT + logWith recorder Info $ LogLspStartDuration t dir <- maybe IO.getCurrentDirectory return rootPath @@ -322,7 +317,7 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re _mlibdir <- setInitialDynFlags (cmapWithPrio LogSession recorder) dir argsSessionLoadingOptions -- TODO: should probably catch/log/rethrow at top level instead - `catchAny` (\e -> log Error (LogSetInitialDynFlagsException e) >> pure Nothing) + `catchAny` (\e -> logWith recorder Error (LogSetInitialDynFlagsException e) >> pure Nothing) sessionLoader <- loadSessionWithOptions (cmapWithPrio LogSession recorder) argsSessionLoadingOptions dir config <- LSP.runLspT env LSP.getConfig @@ -330,9 +325,9 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re -- disable runSubset if the client doesn't support watched files runSubset <- (optRunSubset def_options &&) <$> LSP.runLspT env isWatchSupported - log Debug $ LogShouldRunSubset runSubset + logWith recorder Debug $ LogShouldRunSubset runSubset - let options = def_options + let ideOptions = def_options { optReportProgress = clientSupportsProgress caps , optModifyDynFlags = optModifyDynFlags def_options <> pluginModifyDynflags plugins , optRunSubset = runSubset @@ -347,7 +342,7 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re (Just env) logger debouncer - options + ideOptions withHieDb hieChan monitoring @@ -370,11 +365,11 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re putStrLn $ "\nStep 1/4: Finding files to test in " ++ dir files <- expandFiles (argFiles ++ ["." | null argFiles]) -- LSP works with absolute file paths, so try and behave similarly - files <- nubOrd <$> mapM IO.canonicalizePath files - putStrLn $ "Found " ++ show (length files) ++ " files" + absoluteFiles <- nubOrd <$> mapM IO.canonicalizePath files + putStrLn $ "Found " ++ show (length absoluteFiles) ++ " files" putStrLn "\nStep 2/4: Looking for hie.yaml files that control setup" - cradles <- mapM findCradle files + cradles <- mapM findCradle absoluteFiles let ucradles = nubOrd cradles let n = length ucradles putStrLn $ "Found " ++ show n ++ " cradle" ++ ['s' | n /= 1] @@ -382,25 +377,25 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re putStrLn "\nStep 3/4: Initializing the IDE" sessionLoader <- loadSessionWithOptions (cmapWithPrio LogSession recorder) argsSessionLoadingOptions dir let def_options = argsIdeOptions argsDefaultHlsConfig sessionLoader - options = def_options + ideOptions = def_options { optCheckParents = pure NeverCheck , optCheckProject = pure False , optModifyDynFlags = optModifyDynFlags def_options <> pluginModifyDynflags plugins } - ide <- initialise (cmapWithPrio LogService recorder) argsDefaultHlsConfig argsHlsPlugins rules Nothing logger debouncer options hiedb hieChan mempty + ide <- initialise (cmapWithPrio LogService recorder) argsDefaultHlsConfig argsHlsPlugins rules Nothing logger debouncer ideOptions hiedb hieChan mempty shakeSessionInit (cmapWithPrio LogShake recorder) ide registerIdeConfiguration (shakeExtras ide) $ IdeConfiguration mempty (hashed Nothing) putStrLn "\nStep 4/4: Type checking the files" - setFilesOfInterest ide $ HashMap.fromList $ map ((,OnDisk) . toNormalizedFilePath') files - results <- runAction "User TypeCheck" ide $ uses TypeCheck (map toNormalizedFilePath' files) - _results <- runAction "GetHie" ide $ uses GetHieAst (map toNormalizedFilePath' files) - _results <- runAction "GenerateCore" ide $ uses GenerateCore (map toNormalizedFilePath' files) - let (worked, failed) = partition fst $ zip (map isJust results) files + setFilesOfInterest ide $ HashMap.fromList $ map ((,OnDisk) . toNormalizedFilePath') absoluteFiles + results <- runAction "User TypeCheck" ide $ uses TypeCheck (map toNormalizedFilePath' absoluteFiles) + _results <- runAction "GetHie" ide $ uses GetHieAst (map toNormalizedFilePath' absoluteFiles) + _results <- runAction "GenerateCore" ide $ uses GenerateCore (map toNormalizedFilePath' absoluteFiles) + let (worked, failed) = partition fst $ zip (map isJust results) absoluteFiles when (failed /= []) $ putStr $ unlines $ "Files that failed:" : map ((++) " * " . snd) failed - let nfiles xs = let n = length xs in if n == 1 then "1 file" else show n ++ " files" + let nfiles xs = let n' = length xs in if n' == 1 then "1 file" else show n' ++ " files" putStrLn $ "\nCompleted (" ++ nfiles worked ++ " worked, " ++ nfiles failed ++ " failed)" unless (null failed) (exitWith $ ExitFailure (length failed)) @@ -420,12 +415,12 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re runWithDb (cmapWithPrio LogSession recorder) dbLoc $ \hiedb hieChan -> do sessionLoader <- loadSessionWithOptions (cmapWithPrio LogSession recorder) argsSessionLoadingOptions "." let def_options = argsIdeOptions argsDefaultHlsConfig sessionLoader - options = def_options + ideOptions = def_options { optCheckParents = pure NeverCheck , optCheckProject = pure False , optModifyDynFlags = optModifyDynFlags def_options <> pluginModifyDynflags plugins } - ide <- initialise (cmapWithPrio LogService recorder) argsDefaultHlsConfig argsHlsPlugins rules Nothing logger debouncer options hiedb hieChan mempty + ide <- initialise (cmapWithPrio LogService recorder) argsDefaultHlsConfig argsHlsPlugins rules Nothing logger debouncer ideOptions hiedb hieChan mempty shakeSessionInit (cmapWithPrio LogShake recorder) ide registerIdeConfiguration (shakeExtras ide) $ IdeConfiguration mempty (hashed Nothing) c ide @@ -437,9 +432,9 @@ expandFiles = concatMapM $ \x -> do then return [x] else do let recurse "." = True - recurse x | "." `isPrefixOf` takeFileName x = False -- skip .git etc - recurse x = takeFileName x `notElem` ["dist", "dist-newstyle"] -- cabal directories - files <- filter (\x -> takeExtension x `elem` [".hs", ".lhs"]) <$> IO.listFilesInside (return . recurse) x + recurse y | "." `isPrefixOf` takeFileName y = False -- skip .git etc + recurse y = takeFileName y `notElem` ["dist", "dist-newstyle"] -- cabal directories + files <- filter (\y -> takeExtension y `elem` [".hs", ".lhs"]) <$> IO.listFilesInside (return . recurse) x when (null files) $ fail $ "Couldn't find any .hs/.lhs files inside directory: " ++ x return files diff --git a/ghcide/src/Development/IDE/Main/HeapStats.hs b/ghcide/src/Development/IDE/Main/HeapStats.hs index ac1af8f28e..0a19f6339b 100644 --- a/ghcide/src/Development/IDE/Main/HeapStats.hs +++ b/ghcide/src/Development/IDE/Main/HeapStats.hs @@ -19,7 +19,7 @@ data Log deriving Show instance Pretty Log where - pretty log = case log of + pretty = \case LogHeapStatsPeriod period -> "Logging heap statistics every" <+> pretty (toFormattedSeconds period) LogHeapStatsDisabled -> diff --git a/ghcide/src/Development/IDE/Monitoring/EKG.hs b/ghcide/src/Development/IDE/Monitoring/EKG.hs index e4d9f6d0ae..26414fdf04 100644 --- a/ghcide/src/Development/IDE/Monitoring/EKG.hs +++ b/ghcide/src/Development/IDE/Monitoring/EKG.hs @@ -3,6 +3,7 @@ module Development.IDE.Monitoring.EKG(monitoring) where import Development.IDE.Types.Monitoring (Monitoring (..)) import Ide.Logger (Logger) + #ifdef MONITORING_EKG import Control.Concurrent (killThread) import Control.Concurrent.Async (async, waitCatch) diff --git a/ghcide/src/Development/IDE/Monitoring/OpenTelemetry.hs b/ghcide/src/Development/IDE/Monitoring/OpenTelemetry.hs index 2a6efa3d2e..184a5c1ba9 100644 --- a/ghcide/src/Development/IDE/Monitoring/OpenTelemetry.hs +++ b/ghcide/src/Development/IDE/Monitoring/OpenTelemetry.hs @@ -15,9 +15,9 @@ monitoring :: IO Monitoring monitoring | userTracingEnabled = do actions <- newIORef [] - let registerCounter name read = do + let registerCounter name readA = do observer <- mkValueObserver (encodeUtf8 name) - let update = observe observer . fromIntegral =<< read + let update = observe observer . fromIntegral =<< readA atomicModifyIORef'_ actions (update :) registerGauge = registerCounter let start = do diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index 97c58131b1..e15655a3cc 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -11,11 +11,10 @@ module Development.IDE.Plugin.Completions import Control.Concurrent.Async (concurrently) import Control.Concurrent.STM.Stats (readTVarIO) -import Control.Lens ((&), (.~)) +import Control.Lens ((&), (.~), (?~)) import Control.Monad.IO.Class import Control.Monad.Trans.Except (ExceptT (ExceptT), withExceptT) -import Data.Aeson import qualified Data.HashMap.Strict as Map import qualified Data.HashSet as Set import Data.Maybe @@ -25,7 +24,8 @@ import Development.IDE.Core.PluginUtils import Development.IDE.Core.PositionMapping import Development.IDE.Core.RuleTypes import Development.IDE.Core.Service hiding (Log, LogShake) -import Development.IDE.Core.Shake hiding (Log) +import Development.IDE.Core.Shake hiding (Log, + knownTargets) import qualified Development.IDE.Core.Shake as Shake import Development.IDE.GHC.Compat import Development.IDE.GHC.Util @@ -50,17 +50,24 @@ import Language.LSP.Protocol.Message import Language.LSP.Protocol.Types import qualified Language.LSP.Server as LSP import Numeric.Natural +import Prelude hiding (mod) import Text.Fuzzy.Parallel (Scored (..)) import Development.IDE.Core.Rules (usePropertyAction) -import qualified GHC.LanguageExtensions as LangExt + import qualified Ide.Plugin.Config as Config +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if MIN_VERSION_ghc(9,2,0) +import qualified GHC.LanguageExtensions as LangExt +#endif + data Log = LogShake Shake.Log deriving Show instance Pretty Log where pretty = \case - LogShake log -> pretty log + LogShake msg -> pretty msg ghcideCompletionsPluginPriority :: Natural ghcideCompletionsPluginPriority = defaultPluginPriority @@ -79,8 +86,8 @@ produceCompletions :: Recorder (WithPriority Log) -> Rules () produceCompletions recorder = do define (cmapWithPrio LogShake recorder) $ \LocalCompletions file -> do let uri = fromNormalizedUri $ normalizedFilePathToUri file - pm <- useWithStale GetParsedModule file - case pm of + mbPm <- useWithStale GetParsedModule file + case mbPm of Just (pm, _) -> do let cdata = localCompletionsForParsedModule uri pm return ([], Just cdata) @@ -90,9 +97,9 @@ produceCompletions recorder = do -- synthesizing a fake module with an empty body from the buffer -- in the ModSummary, which preserves all the imports ms <- fmap fst <$> useWithStale GetModSummaryWithoutTimestamps file - sess <- fmap fst <$> useWithStale GhcSessionDeps file + mbSess <- fmap fst <$> useWithStale GhcSessionDeps file - case (ms, sess) of + case (ms, mbSess) of (Just ModSummaryResult{..}, Just sess) -> do let env = hscEnv sess -- We do this to be able to provide completions of items that are not restricted to the explicit list @@ -122,7 +129,7 @@ dropListFromImportDecl iDecl = let f x = x in f <$> iDecl -resolveCompletion :: ResolveFunction IdeState CompletionResolveData 'Method_CompletionItemResolve +resolveCompletion :: ResolveFunction IdeState CompletionResolveData Method_CompletionItemResolve resolveCompletion ide _pid comp@CompletionItem{_detail,_documentation,_data_} uri (CompletionResolveData _ needType (NameDetails mod occ)) = do file <- getNormalizedFilePathE uri @@ -137,8 +144,8 @@ resolveCompletion ide _pid comp@CompletionItem{_detail,_documentation,_data_} ur #endif mdkm <- liftIO $ runIdeAction "CompletionResolve.GetDocMap" (shakeExtras ide) $ useWithStaleFast GetDocMap file let (dm,km) = case mdkm of - Just (DKMap dm km, _) -> (dm,km) - Nothing -> (mempty, mempty) + Just (DKMap docMap kindMap, _) -> (docMap,kindMap) + Nothing -> (mempty, mempty) doc <- case lookupNameEnv dm name of Just doc -> pure $ spanDocToMarkdown doc Nothing -> liftIO $ spanDocToMarkdown <$> getDocumentationTryGhc (hscEnv sess) name @@ -155,13 +162,13 @@ resolveCompletion ide _pid comp@CompletionItem{_detail,_documentation,_data_} ur InR $ MarkupContent MarkupKind_Markdown $ T.intercalate sectionSeparator (old:doc) _ -> InR $ MarkupContent MarkupKind_Markdown $ T.intercalate sectionSeparator doc pure (comp & L.detail .~ (det1 <> _detail) - & L.documentation .~ Just doc1) + & L.documentation ?~ doc1) where stripForall ty = case splitForAllTyCoVars ty of (_,res) -> res -- | Generate code actions. -getCompletionsLSP :: PluginMethodHandler IdeState 'Method_TextDocumentCompletion +getCompletionsLSP :: PluginMethodHandler IdeState Method_TextDocumentCompletion getCompletionsLSP ide plId CompletionParams{_textDocument=TextDocumentIdentifier uri ,_position=position @@ -207,7 +214,7 @@ getCompletionsLSP ide plId Just (cci', parsedMod, bindMap) -> do let pfix = getCompletionPrefix position cnts case (pfix, completionContext) of - ((PosPrefixInfo _ "" _ _), Just CompletionContext { _triggerCharacter = Just "."}) + (PosPrefixInfo _ "" _ _, Just CompletionContext { _triggerCharacter = Just "."}) -> return (InL []) (_, _) -> do let clientCaps = clientCapabilities $ shakeExtras ide diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index d370b5142a..1ae75f1042 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -1,8 +1,8 @@ -{-# LANGUAGE CPP #-} +{-# LANGUAGE CPP #-} {-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE MultiWayIf #-} -{-# LANGUAGE OverloadedLabels #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE OverloadedLabels #-} -- Mostly taken from "haskell-ide-engine" module Development.IDE.Plugin.Completions.Logic ( @@ -15,7 +15,8 @@ module Development.IDE.Plugin.Completions.Logic ( ) where import Control.Applicative -import Control.Lens hiding (Context) +import Control.Lens hiding (Context, + parts) import Data.Char (isAlphaNum, isUpper) import Data.Default (def) import Data.Generics @@ -23,9 +24,10 @@ import Data.List.Extra as List hiding (stripPrefix) import qualified Data.Map as Map import Data.Row +import Prelude hiding (mod) -import Data.Maybe (catMaybes, fromMaybe, - isJust, isNothing, +import Data.Maybe (fromMaybe, isJust, + isNothing, listToMaybe, mapMaybe) import qualified Data.Text as T @@ -34,37 +36,22 @@ import qualified Text.Fuzzy.Parallel as Fuzzy import Control.Monad import Data.Aeson (ToJSON (toJSON)) import Data.Function (on) -import Data.Functor -import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HashSet import Data.Monoid (First (..)) import Data.Ord (Down (Down)) import qualified Data.Set as Set -import Development.IDE.Core.Compile import Development.IDE.Core.PositionMapping -import Development.IDE.GHC.Compat hiding (ppr) +import Development.IDE.GHC.Compat hiding (isQual, ppr) import qualified Development.IDE.GHC.Compat as GHC import Development.IDE.GHC.Compat.Util import Development.IDE.GHC.CoreFile (occNamePrefixes) import Development.IDE.GHC.Error import Development.IDE.GHC.Util import Development.IDE.Plugin.Completions.Types -import Development.IDE.Spans.Common -import Development.IDE.Spans.Documentation import Development.IDE.Spans.LocalBindings import Development.IDE.Types.Exports -import Development.IDE.Types.HscEnvEq import Development.IDE.Types.Options - -#if MIN_VERSION_ghc(9,2,0) -import GHC.Plugins (Depth (AllTheWay), - defaultSDocContext, - mkUserStyle, - neverQualify, - renderWithContext, - sdocStyle) -#endif import Ide.PluginUtils (mkLspCommand) import Ide.Types (CommandId (..), IdePlugins (..), @@ -76,10 +63,24 @@ import Text.Fuzzy.Parallel (Scored (score), original) import qualified Data.Text.Utf16.Rope as Rope -import Development.IDE +import Development.IDE hiding (line) import Development.IDE.Spans.AtPoint (pointCommand) +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if MIN_VERSION_ghc(9,2,0) +import GHC.Plugins (Depth (AllTheWay), + mkUserStyle, + neverQualify, + sdocStyle) +#endif + +#if MIN_VERSION_ghc(9,2,0) && !MIN_VERSION_ghc(9,3,0) +import GHC.Plugins (defaultSDocContext, + renderWithContext) +#endif + #if MIN_VERSION_ghc(9,5,0) import Language.Haskell.Syntax.Basic #endif @@ -144,12 +145,15 @@ getCContext pos pm | pos `isInsideSrcSpan` r = Just TypeContext goInline _ = Nothing +#if MIN_VERSION_ghc(9,5,0) importGo :: GHC.LImportDecl GhcPs -> Maybe Context importGo (L (locA -> r) impDecl) | pos `isInsideSrcSpan` r -#if MIN_VERSION_ghc(9,5,0) = importInline importModuleName (fmap (fmap reLoc) $ ideclImportList impDecl) #else + importGo :: GHC.LImportDecl GhcPs -> Maybe Context + importGo (L (locA -> r) impDecl) + | pos `isInsideSrcSpan` r = importInline importModuleName (fmap (fmap reLoc) $ ideclHiding impDecl) #endif <|> Just (ImportContext importModuleName) @@ -160,18 +164,24 @@ getCContext pos pm -- importInline :: String -> Maybe (Bool, GHC.Located [LIE GhcPs]) -> Maybe Context #if MIN_VERSION_ghc(9,5,0) importInline modName (Just (EverythingBut, L r _)) + | pos `isInsideSrcSpan` r = Just $ ImportHidingContext modName + | otherwise = Nothing #else importInline modName (Just (True, L r _)) -#endif | pos `isInsideSrcSpan` r = Just $ ImportHidingContext modName | otherwise = Nothing +#endif + #if MIN_VERSION_ghc(9,5,0) importInline modName (Just (Exactly, L r _)) + | pos `isInsideSrcSpan` r = Just $ ImportListContext modName + | otherwise = Nothing #else importInline modName (Just (False, L r _)) -#endif | pos `isInsideSrcSpan` r = Just $ ImportListContext modName | otherwise = Nothing +#endif + importInline _ _ = Nothing occNameToComKind :: OccName -> CompletionItemKind @@ -191,7 +201,7 @@ mkCompl :: Maybe PluginId -- ^ Plugin to use for the extend import command -> IdeOptions -> Uri -> CompItem -> CompletionItem mkCompl pId - IdeOptions {..} + _ideOptions uri CI { compKind, @@ -285,27 +295,27 @@ showForSnippet x = printOutputable x mkModCompl :: T.Text -> CompletionItem mkModCompl label = - (defaultCompletionItemWithLabel label) - { _kind = Just CompletionItemKind_Module } + defaultCompletionItemWithLabel label + & L.kind ?~ CompletionItemKind_Module mkModuleFunctionImport :: T.Text -> T.Text -> CompletionItem mkModuleFunctionImport moduleName label = - (defaultCompletionItemWithLabel label) - { _kind = Just CompletionItemKind_Function - , _detail = Just moduleName } + defaultCompletionItemWithLabel label + & L.kind ?~ CompletionItemKind_Function + & L.detail ?~ moduleName mkImportCompl :: T.Text -> T.Text -> CompletionItem mkImportCompl enteredQual label = - (defaultCompletionItemWithLabel m) - { _kind = Just CompletionItemKind_Module - , _detail = Just label } + defaultCompletionItemWithLabel m + & L.kind ?~ CompletionItemKind_Module + & L.detail ?~ label where m = fromMaybe "" (T.stripPrefix enteredQual label) mkExtCompl :: T.Text -> CompletionItem mkExtCompl label = - (defaultCompletionItemWithLabel label) - { _kind = Just CompletionItemKind_Keyword } + defaultCompletionItemWithLabel label + & L.kind ?~ CompletionItemKind_Keyword defaultCompletionItemWithLabel :: T.Text -> CompletionItem defaultCompletionItemWithLabel label = @@ -313,14 +323,14 @@ defaultCompletionItemWithLabel label = def def def def def def def def def fromIdentInfo :: Uri -> IdentInfo -> Maybe T.Text -> CompItem -fromIdentInfo doc id@IdentInfo{..} q = CI +fromIdentInfo doc identInfo@IdentInfo{..} q = CI { compKind= occNameToComKind name , insertText=rend , provenance = DefinedIn mod , label=rend , typeText = Nothing , isInfix=Nothing - , isTypeCompl= not (isDatacon id) && isUpper (T.head rend) + , isTypeCompl= not (isDatacon identInfo) && isUpper (T.head rend) , additionalTextEdits= Just $ ExtendImport { doc, @@ -332,8 +342,8 @@ fromIdentInfo doc id@IdentInfo{..} q = CI , nameDetails = Nothing , isLocalCompletion = False } - where rend = rendered id - mod = moduleNameText id + where rend = rendered identInfo + mod = moduleNameText identInfo cacheDataProducer :: Uri -> [ModuleName] -> Module -> GlobalRdrEnv-> GlobalRdrEnv -> [LImportDecl GhcPs] -> CachedCompletions cacheDataProducer uri visibleMods curMod globalEnv inScopeEnv limports = @@ -396,7 +406,7 @@ cacheDataProducer uri visibleMods curMod globalEnv inScopeEnv limports = in (unqual,QualCompls qual) toCompItem :: Parent -> Module -> T.Text -> Name -> Maybe (LImportDecl GhcPs) -> [CompItem] - toCompItem par m mn n imp' = + toCompItem par _ mn n imp' = -- docs <- getDocumentationTryGhc packageState curMod n let (mbParent, originName) = case par of NoParent -> (Nothing, nameOccName n) @@ -439,34 +449,34 @@ localCompletionsForParsedModule uri pm@ParsedModule{pm_parsed_source = L _ HsMod } where typeSigIds = Set.fromList - [ id + [ identifier | L _ (SigD _ (TypeSig _ ids _)) <- hsmodDecls - , L _ id <- ids + , L _ identifier <- ids ] hasTypeSig = (`Set.member` typeSigIds) . unLoc compls = concat [ case decl of SigD _ (TypeSig _ ids typ) -> - [mkComp id CompletionItemKind_Function (Just $ showForSnippet typ) | id <- ids] + [mkComp identifier CompletionItemKind_Function (Just $ showForSnippet typ) | identifier <- ids] ValD _ FunBind{fun_id} -> [ mkComp fun_id CompletionItemKind_Function Nothing | not (hasTypeSig fun_id) ] ValD _ PatBind{pat_lhs} -> - [mkComp id CompletionItemKind_Variable Nothing - | VarPat _ id <- listify (\(_ :: Pat GhcPs) -> True) pat_lhs] + [mkComp identifier CompletionItemKind_Variable Nothing + | VarPat _ identifier <- listify (\(_ :: Pat GhcPs) -> True) pat_lhs] TyClD _ ClassDecl{tcdLName, tcdSigs, tcdATs} -> mkComp tcdLName CompletionItemKind_Interface (Just $ showForSnippet tcdLName) : - [ mkComp id CompletionItemKind_Function (Just $ showForSnippet typ) + [ mkComp identifier CompletionItemKind_Function (Just $ showForSnippet typ) | L _ (ClassOpSig _ _ ids typ) <- tcdSigs - , id <- ids] ++ + , identifier <- ids] ++ [ mkComp fdLName CompletionItemKind_Struct (Just $ showForSnippet fdLName) | L _ (FamilyDecl{fdLName}) <- tcdATs] TyClD _ x -> - let generalCompls = [mkComp id cl (Just $ showForSnippet $ tyClDeclLName x) - | id <- listify (\(_ :: LIdP GhcPs) -> True) x - , let cl = occNameToComKind (rdrNameOcc $ unLoc id)] + let generalCompls = [mkComp identifier cl (Just $ showForSnippet $ tyClDeclLName x) + | identifier <- listify (\(_ :: LIdP GhcPs) -> True) x + , let cl = occNameToComKind (rdrNameOcc $ unLoc identifier)] -- here we only have to look at the outermost type recordCompls = findRecordCompl uri (Local pos) x in @@ -670,9 +680,9 @@ getCompletions plugins ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, | otherwise = ((qual,) <$> Map.findWithDefault [] prefixScope (getQualCompls qualCompls)) ++ map (\compl -> (notQual, compl (Just prefixScope))) anyQualCompls - filtListWith f list = + filtListWith f xs = [ fmap f label - | label <- Fuzzy.simpleFilter chunkSize maxC fullPrefix list + | label <- Fuzzy.simpleFilter chunkSize maxC fullPrefix xs , enteredQual `T.isPrefixOf` original label ] diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Types.hs b/ghcide/src/Development/IDE/Plugin/Completions/Types.hs index 9151e03955..8902475330 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Types.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Types.hs @@ -19,17 +19,24 @@ import Data.Text (Text) import Data.Typeable (Typeable) import Development.IDE.GHC.Compat import Development.IDE.Graph (RuleResult) -import Development.IDE.Spans.Common +import Development.IDE.Spans.Common () import GHC.Generics (Generic) import Ide.Plugin.Properties import Language.LSP.Protocol.Types (CompletionItemKind (..), Uri) import qualified Language.LSP.Protocol.Types as J + +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,0,0) +import qualified OccName as Occ +#endif + #if MIN_VERSION_ghc(9,0,0) import qualified GHC.Types.Name.Occurrence as Occ -#else -import qualified OccName as Occ #endif + + -- | Produce completions info for a file type instance RuleResult LocalCompletions = CachedCompletions type instance RuleResult NonLocalCompletions = CachedCompletions @@ -53,8 +60,8 @@ extendImportCommandId :: Text extendImportCommandId = "extendImport" properties :: Properties - '[ 'PropertyKey "autoExtendOn" 'TBoolean, - 'PropertyKey "snippetsOn" 'TBoolean] + '[ 'PropertyKey "autoExtendOn" TBoolean, + 'PropertyKey "snippetsOn" TBoolean] properties = emptyProperties & defineBooleanProperty #snippetsOn "Inserts snippets when using code completions" @@ -200,7 +207,7 @@ instance ToJSON NameDetails where instance Show NameDetails where show = show . toJSON --- | The data that is acutally sent for resolve support +-- | The data that is actually sent for resolve support -- We need the URI to be able to reconstruct the GHC environment -- in the file the completion was triggered in. data CompletionResolveData = CompletionResolveData diff --git a/ghcide/src/Development/IDE/Plugin/HLS.hs b/ghcide/src/Development/IDE/Plugin/HLS.hs index 7ef7eeed65..b70d19e0f2 100644 --- a/ghcide/src/Development/IDE/Plugin/HLS.hs +++ b/ghcide/src/Development/IDE/Plugin/HLS.hs @@ -91,7 +91,7 @@ noPluginEnabled recorder m fs' = do pluginNotEnabled method availPlugins = "No plugin enabled for " <> T.pack (show method) <> ", potentially available: " <> (T.intercalate ", " $ map (\(PluginId plid) -> plid) availPlugins) - + pluginDoesntExist :: PluginId -> Text pluginDoesntExist (PluginId pid) = "Plugin " <> pid <> " doesn't exist" @@ -232,9 +232,9 @@ executeCommandHandlers recorder ecs = requestHandler SMethod_WorkspaceExecuteCom -- --------------------------------------------------------------------- extensiblePlugins :: Recorder (WithPriority Log) -> [(PluginId, PluginDescriptor IdeState)] -> Plugin Config -extensiblePlugins recorder xs = mempty { P.pluginHandlers = handlers } +extensiblePlugins recorder plugins = mempty { P.pluginHandlers = handlers } where - IdeHandlers handlers' = foldMap bakePluginId xs + IdeHandlers handlers' = foldMap bakePluginId plugins bakePluginId :: (PluginId, PluginDescriptor IdeState) -> IdeHandlers bakePluginId (pid,pluginDesc) = IdeHandlers $ DMap.map (\(PluginHandler f) -> IdeHandler [(pid,pluginDesc,f pid)]) @@ -250,11 +250,11 @@ extensiblePlugins recorder xs = mempty { P.pluginHandlers = handlers } -- Clients generally don't display ResponseErrors so instead we log any that we come across case nonEmpty fs of Nothing -> liftIO $ noPluginEnabled recorder m ((\(x, _, _) -> x) <$> fs') - Just fs -> do - let handlers = fmap (\(plid,_,handler) -> (plid,handler)) fs - es <- runConcurrently exceptionInPlugin m handlers ide params + Just neFs -> do + let plidsAndHandlers = fmap (\(plid,_,handler) -> (plid,handler)) neFs + es <- runConcurrently exceptionInPlugin m plidsAndHandlers ide params caps <- LSP.getClientCapabilities - let (errs,succs) = partitionEithers $ toList $ join $ NE.zipWith (\(pId,_) -> fmap (first (pId,))) handlers es + let (errs,succs) = partitionEithers $ toList $ join $ NE.zipWith (\(pId,_) -> fmap (first (pId,))) plidsAndHandlers es liftIO $ unless (null errs) $ logErrors recorder errs case nonEmpty succs of Nothing -> do @@ -288,12 +288,12 @@ extensibleNotificationPlugins recorder xs = mempty { P.pluginHandlers = handlers case nonEmpty fs of Nothing -> do logWith recorder Warning (LogNoPluginForMethod $ Some m) - Just fs -> do + Just neFs -> do -- We run the notifications in order, so the core ghcide provider -- (which restarts the shake process) hopefully comes last mapM_ (\(pid,_,f) -> otTracedProvider pid (fromString $ show m) $ f ide vfs params `catchAny` -- See Note [Exception handling in plugins] - (\e -> logWith recorder Warning (ExceptionInPlugin pid (Some m) e))) fs + (\e -> logWith recorder Warning (ExceptionInPlugin pid (Some m) e))) neFs -- --------------------------------------------------------------------- @@ -344,14 +344,14 @@ newtype IdeNotificationHandlers = IdeNotificationHandlers (DMap IdeNotification instance Semigroup IdeHandlers where (IdeHandlers a) <> (IdeHandlers b) = IdeHandlers $ DMap.unionWithKey go a b where - go _ (IdeHandler a) (IdeHandler b) = IdeHandler (a <> b) + go _ (IdeHandler c) (IdeHandler d) = IdeHandler (c <> d) instance Monoid IdeHandlers where mempty = IdeHandlers mempty instance Semigroup IdeNotificationHandlers where (IdeNotificationHandlers a) <> (IdeNotificationHandlers b) = IdeNotificationHandlers $ DMap.unionWithKey go a b where - go _ (IdeNotificationHandler a) (IdeNotificationHandler b) = IdeNotificationHandler (a <> b) + go _ (IdeNotificationHandler c) (IdeNotificationHandler d) = IdeNotificationHandler (c <> d) instance Monoid IdeNotificationHandlers where mempty = IdeNotificationHandlers mempty diff --git a/ghcide/src/Development/IDE/Plugin/HLS/GhcIde.hs b/ghcide/src/Development/IDE/Plugin/HLS/GhcIde.hs index 1c1cb8c5b2..f85f0c8522 100644 --- a/ghcide/src/Development/IDE/Plugin/HLS/GhcIde.hs +++ b/ghcide/src/Development/IDE/Plugin/HLS/GhcIde.hs @@ -27,9 +27,9 @@ data Log instance Pretty Log where pretty = \case - LogNotifications log -> pretty log - LogCompletions log -> pretty log - LogTypeLenses log -> pretty log + LogNotifications msg -> pretty msg + LogCompletions msg -> pretty msg + LogTypeLenses msg -> pretty msg descriptors :: Recorder (WithPriority Log) -> [PluginDescriptor IdeState] descriptors recorder = @@ -59,7 +59,7 @@ descriptor plId = (defaultPluginDescriptor plId) -- --------------------------------------------------------------------- -hover' :: PluginMethodHandler IdeState 'Method_TextDocumentHover +hover' :: PluginMethodHandler IdeState Method_TextDocumentHover hover' ideState _ HoverParams{..} = do liftIO $ logDebug (ideLogger ideState) "GhcIde.hover entered (ideLogger)" -- AZ hover ideState TextDocumentPositionParams{..} diff --git a/ghcide/src/Development/IDE/Plugin/Test.hs b/ghcide/src/Development/IDE/Plugin/Test.hs index a90cd875fb..72a1d5b912 100644 --- a/ghcide/src/Development/IDE/Plugin/Test.hs +++ b/ghcide/src/Development/IDE/Plugin/Test.hs @@ -46,7 +46,6 @@ import Development.IDE.Types.Action import Development.IDE.Types.HscEnvEq (HscEnvEq (hscEnv)) import Development.IDE.Types.Location (fromUri) import GHC.Generics (Generic) -import Ide.Plugin.Config (CheckParents) import Ide.Plugin.Error import Ide.Types import Language.LSP.Protocol.Message diff --git a/ghcide/src/Development/IDE/Plugin/TypeLenses.hs b/ghcide/src/Development/IDE/Plugin/TypeLenses.hs index 0a6540bfe9..338cd118d3 100644 --- a/ghcide/src/Development/IDE/Plugin/TypeLenses.hs +++ b/ghcide/src/Development/IDE/Plugin/TypeLenses.hs @@ -24,8 +24,7 @@ import Data.Aeson.Types (toJSON) import qualified Data.Aeson.Types as A import Data.List (find) import qualified Data.Map as Map -import Data.Maybe (catMaybes, fromMaybe, - maybeToList) +import Data.Maybe (catMaybes, maybeToList) import qualified Data.Text as T import Development.IDE (GhcSession (..), HscEnvEq (hscEnv), @@ -87,7 +86,7 @@ data Log = LogShake Shake.Log deriving Show instance Pretty Log where pretty = \case - LogShake log -> pretty log + LogShake msg -> pretty msg typeLensCommandId :: T.Text @@ -103,7 +102,7 @@ descriptor recorder plId = , pluginConfigDescriptor = defaultConfigDescriptor {configCustomConfig = mkCustomConfig properties} } -properties :: Properties '[ 'PropertyKey "mode" ('TEnum Mode)] +properties :: Properties '[ 'PropertyKey "mode" (TEnum Mode)] properties = emptyProperties & defineEnumProperty #mode "Control how type lenses are shown" [ (Always, "Always displays type lenses of global bindings") @@ -314,11 +313,11 @@ gblBindingType (Just hsc) (Just gblEnv) = do showDoc = showDocRdrEnv hsc rdrEnv hasSig :: (Monad m) => Name -> m a -> m (Maybe a) hasSig name f = whenMaybe (name `elemNameSet` sigs) f - bindToSig id = do - let name = idName id + bindToSig identifier = do + let name = idName identifier hasSig name $ do env <- tcInitTidyEnv - let (_, ty) = tidyOpenType env (idType id) + let (_, ty) = tidyOpenType env (idType identifier) pure $ GlobalBindingTypeSig name (printName name <> " :: " <> showDoc (pprSigmaType ty)) (name `elemNameSet` exports) patToSig p = do let name = patSynName p diff --git a/ghcide/src/Development/IDE/Spans/AtPoint.hs b/ghcide/src/Development/IDE/Spans/AtPoint.hs index 54b1015cfd..70a36693f8 100644 --- a/ghcide/src/Development/IDE/Spans/AtPoint.hs +++ b/ghcide/src/Development/IDE/Spans/AtPoint.hs @@ -30,6 +30,7 @@ import Development.IDE.GHC.Orphans () import Development.IDE.Types.Location import Language.LSP.Protocol.Types hiding (SemanticTokenAbsolute (..)) +import Prelude hiding (mod) -- compiler and infrastructure import Development.IDE.Core.PositionMapping @@ -58,7 +59,8 @@ import Data.List.Extra (dropEnd1, nubOrd) import Data.Version (showVersion) import Development.IDE.Types.Shake (WithHieDb) -import HieDb hiding (pointCommand) +import HieDb hiding (pointCommand, + withHieDb) import System.Directory (doesFileExist) -- | Gives a Uri for the module, given the .hie file location and the the module info @@ -93,11 +95,11 @@ foiReferencesAtPoint file pos (FOIReferences asts) = Just (HAR _ hf _ _ _,mapping) -> let names = getNamesAtPoint hf pos mapping adjustedLocs = HM.foldr go [] asts - go (HAR _ _ rf tr _, mapping) xs = refs ++ typerefs ++ xs + go (HAR _ _ rf tr _, goMapping) xs = refs ++ typerefs ++ xs where - refs = mapMaybe (toCurrentLocation mapping . realSrcSpanToLocation . fst) + refs = mapMaybe (toCurrentLocation goMapping . realSrcSpanToLocation . fst) $ concat $ mapMaybe (\n -> M.lookup (Right n) rf) names - typerefs = mapMaybe (toCurrentLocation mapping . realSrcSpanToLocation) + typerefs = mapMaybe (toCurrentLocation goMapping . realSrcSpanToLocation) $ concat $ mapMaybe (`M.lookup` tr) names in (names, adjustedLocs,map fromNormalizedFilePath $ HM.keys asts) @@ -133,8 +135,8 @@ referencesAtPoint withHieDb nfp pos refs = do typeRefs <- forM names $ \name -> case nameModule_maybe name of Just mod | isTcClsNameSpace (occNameSpace $ nameOccName name) -> do - refs <- liftIO $ withHieDb (\hieDb -> findTypeRefs hieDb True (nameOccName name) (Just $ moduleName mod) (Just $ moduleUnit mod) exclude) - pure $ mapMaybe typeRowToLoc refs + refs' <- liftIO $ withHieDb (\hieDb -> findTypeRefs hieDb True (nameOccName name) (Just $ moduleName mod) (Just $ moduleUnit mod) exclude) + pure $ mapMaybe typeRowToLoc refs' _ -> pure [] pure $ nubOrd $ foiRefs ++ concat nonFOIRefs ++ concat typeRefs @@ -243,9 +245,9 @@ atPoint IdeOptions{} (HAR _ hf _ _ (kind :: HieKind hietype)) (DKMap dm km) env -- Check for evidence bindings isInternal :: (Identifier, IdentifierDetails a) -> Bool - isInternal (Right _, dets) = + isInternal (Right _, _dets) = -- dets is only used in GHC >= 9.0.1 #if MIN_VERSION_ghc(9,0,1) - any isEvidenceContext $ identInfo dets + any isEvidenceContext $ identInfo _dets #else False #endif @@ -270,7 +272,7 @@ atPoint IdeOptions{} (HAR _ hf _ _ (kind :: HieKind hietype)) (DKMap dm km) env prettyPackageName :: Name -> Maybe T.Text prettyPackageName n = do m <- nameModule_maybe n - pkgTxt <- packageNameWithVersion m env + pkgTxt <- packageNameWithVersion m pure $ "*(" <> pkgTxt <> ")*" -- Return the module text itself and @@ -279,14 +281,14 @@ atPoint IdeOptions{} (HAR _ hf _ _ (kind :: HieKind hietype)) (DKMap dm km) env packageNameForImportStatement mod = do mpkg <- findImportedModule env mod :: IO (Maybe Module) let moduleName = printOutputable mod - case mpkg >>= flip packageNameWithVersion env of + case mpkg >>= packageNameWithVersion of Nothing -> pure moduleName Just pkgWithVersion -> pure $ moduleName <> "\n\n" <> pkgWithVersion -- Return the package name and version of a module. -- For example, given module `Data.List`, it should return something like `base-4.x`. - packageNameWithVersion :: Module -> HscEnv -> Maybe T.Text - packageNameWithVersion m env = do + packageNameWithVersion :: Module -> Maybe T.Text + packageNameWithVersion m = do let pid = moduleUnit m conf <- lookupUnit env pid let pkgName = T.pack $ unitPackageNameString conf @@ -331,20 +333,20 @@ typeLocationsAtPoint withHieDb lookupModule _ideOptions pos (HAR _ ast _ _ hieKi unfold = map (arr A.!) getts x = nodeType ni ++ (mapMaybe identType $ M.elems $ nodeIdentifiers ni) where ni = nodeInfo' x - getTypes ts = flip concatMap (unfold ts) $ \case + getTypes' ts' = flip concatMap (unfold ts') $ \case HTyVarTy n -> [n] - HAppTy a (HieArgs xs) -> getTypes (a : map snd xs) - HTyConApp tc (HieArgs xs) -> ifaceTyConName tc : getTypes (map snd xs) - HForAllTy _ a -> getTypes [a] + HAppTy a (HieArgs xs) -> getTypes' (a : map snd xs) + HTyConApp tc (HieArgs xs) -> ifaceTyConName tc : getTypes' (map snd xs) + HForAllTy _ a -> getTypes' [a] #if MIN_VERSION_ghc(9,0,1) - HFunTy a b c -> getTypes [a,b,c] + HFunTy a b c -> getTypes' [a,b,c] #else - HFunTy a b -> getTypes [a,b] + HFunTy a b -> getTypes' [a,b] #endif - HQualTy a b -> getTypes [a,b] - HCastTy a -> getTypes [a] + HQualTy a b -> getTypes' [a,b] + HCastTy a -> getTypes' [a] _ -> [] - in fmap nubOrd $ concatMapM (fmap (fromMaybe []) . nameToLocation withHieDb lookupModule) (getTypes ts) + in fmap nubOrd $ concatMapM (fmap (fromMaybe []) . nameToLocation withHieDb lookupModule) (getTypes' ts) HieFresh -> let ts = concat $ pointCommand ast pos getts getts x = nodeType ni ++ (mapMaybe identType $ M.elems $ nodeIdentifiers ni) @@ -412,8 +414,8 @@ nameToLocation withHieDb lookupModule name = runMaybeT $ -- This is a hack to make find definition work better with ghcide's nascent multi-component support, -- where names from a component that has been indexed in a previous session but not loaded in this -- session may end up with different unit ids - erow <- liftIO $ withHieDb (\hieDb -> findDef hieDb (nameOccName name) (Just $ moduleName mod) Nothing) - case erow of + erow' <- liftIO $ withHieDb (\hieDb -> findDef hieDb (nameOccName name) (Just $ moduleName mod) Nothing) + case erow' of [] -> MaybeT $ pure Nothing xs -> lift $ mapMaybeM (runMaybeT . defRowToLocation lookupModule) xs xs -> lift $ mapMaybeM (runMaybeT . defRowToLocation lookupModule) xs diff --git a/ghcide/src/Development/IDE/Spans/Documentation.hs b/ghcide/src/Development/IDE/Spans/Documentation.hs index 0c7200c89b..72dbd52acb 100644 --- a/ghcide/src/Development/IDE/Spans/Documentation.hs +++ b/ghcide/src/Development/IDE/Spans/Documentation.hs @@ -29,13 +29,11 @@ import Development.IDE.GHC.Compat.Util import Development.IDE.GHC.Error import Development.IDE.GHC.Util (printOutputable) import Development.IDE.Spans.Common +import Language.LSP.Protocol.Types (filePathToUri, getUri) +import Prelude hiding (mod) import System.Directory import System.FilePath -import Language.LSP.Protocol.Types (filePathToUri, getUri) -#if MIN_VERSION_ghc(9,3,0) -import GHC.Types.Unique.Map -#endif mkDocMap :: HscEnv @@ -59,17 +57,17 @@ mkDocMap env rm this_mod = k <- foldrM getType (tcg_type_env this_mod) names pure $ DKMap d k where - getDocs n map - | maybe True (mod ==) $ nameModule_maybe n = pure map -- we already have the docs in this_docs, or they do not exist + getDocs n nameMap + | maybe True (mod ==) $ nameModule_maybe n = pure nameMap -- we already have the docs in this_docs, or they do not exist | otherwise = do doc <- getDocumentationTryGhc env n - pure $ extendNameEnv map n doc - getType n map + pure $ extendNameEnv nameMap n doc + getType n nameMap | isTcOcc $ occName n - , Nothing <- lookupNameEnv map n + , Nothing <- lookupNameEnv nameMap n = do kind <- lookupKind env n - pure $ maybe map (extendNameEnv map n) kind - | otherwise = pure map + pure $ maybe nameMap (extendNameEnv nameMap n) kind + | otherwise = pure nameMap names = rights $ S.toList idents idents = M.keysSet rm mod = tcg_mod this_mod @@ -85,8 +83,8 @@ getDocumentationTryGhc env n = getDocumentationsTryGhc :: HscEnv -> [Name] -> IO [SpanDoc] getDocumentationsTryGhc env names = do - res <- catchSrcErrors (hsc_dflags env) "docs" $ getDocsBatch env names - case res of + resOr <- catchSrcErrors (hsc_dflags env) "docs" $ getDocsBatch env names + case resOr of Left _ -> return [] Right res -> zipWithM unwrap res names where @@ -123,6 +121,9 @@ getDocumentation => [ParsedModule] -- ^ All of the possible modules it could be defined in. -> name -- ^ The name you want documentation for. -> [T.Text] +#if MIN_VERSION_ghc(9,2,0) +getDocumentation _sources _targetName = [] +#else -- This finds any documentation between the name you want -- documentation for and the one before it. This is only an -- approximately correct algorithm and there are easily constructed @@ -133,10 +134,7 @@ getDocumentation -- TODO : Implement this for GHC 9.2 with in-tree annotations -- (alternatively, just remove it and rely solely on GHC's parsing) getDocumentation sources targetName = fromMaybe [] $ do -#if MIN_VERSION_ghc(9,2,0) - Nothing -#else - -- Find the module the target is defined in. + -- Find the module the target is defined in. targetNameSpan <- realSpan $ getLoc targetName tc <- find ((==) (Just $ srcSpanFile targetNameSpan) . annotationFileName) diff --git a/ghcide/src/Development/IDE/Spans/Pragmas.hs b/ghcide/src/Development/IDE/Spans/Pragmas.hs index d41e68bc5d..d0ec2c1576 100644 --- a/ghcide/src/Development/IDE/Spans/Pragmas.hs +++ b/ghcide/src/Development/IDE/Spans/Pragmas.hs @@ -9,8 +9,8 @@ module Development.IDE.Spans.Pragmas , insertNewPragma , getFirstPragma ) where +import Control.Lens ((&), (.~)) import Data.Bits (Bits (setBit)) -import Data.Function ((&)) import qualified Data.List as List import qualified Data.Maybe as Maybe import Data.Text (Text, pack) @@ -18,17 +18,18 @@ import qualified Data.Text as Text import Development.IDE (srcSpanToRange, IdeState, NormalizedFilePath, GhcSession (..), getFileContents, hscEnv, runAction) import Development.IDE.GHC.Compat import Development.IDE.GHC.Compat.Util -import qualified Language.LSP.Protocol.Types as LSP -import Control.Monad.IO.Class (MonadIO (..)) -import Control.Monad.Trans.Except (ExceptT) -import Ide.Plugin.Error (PluginError) -import Ide.Types (PluginId(..)) -import qualified Data.Text as T -import Development.IDE.Core.PluginUtils +import qualified Language.LSP.Protocol.Types as LSP +import Control.Monad.IO.Class (MonadIO (..)) +import Control.Monad.Trans.Except (ExceptT) +import Ide.Plugin.Error (PluginError) +import Ide.Types (PluginId(..)) +import qualified Data.Text as T +import Development.IDE.Core.PluginUtils +import qualified Language.LSP.Protocol.Lens as L getNextPragmaInfo :: DynFlags -> Maybe Text -> NextPragmaInfo -getNextPragmaInfo dynFlags sourceText = - if | Just sourceText <- sourceText +getNextPragmaInfo dynFlags mbSourceText = + if | Just sourceText <- mbSourceText , let sourceStringBuffer = stringToStringBuffer (Text.unpack sourceText) , POk _ parserState <- parsePreDecl dynFlags sourceStringBuffer -> case parserState of @@ -46,7 +47,7 @@ showExtension NamedFieldPuns = "NamedFieldPuns" showExtension ext = pack (show ext) insertNewPragma :: NextPragmaInfo -> Extension -> LSP.TextEdit -insertNewPragma (NextPragmaInfo _ (Just (LineSplitTextEdits ins _))) newPragma = ins { LSP._newText = "{-# LANGUAGE " <> showExtension newPragma <> " #-}\n" } :: LSP.TextEdit +insertNewPragma (NextPragmaInfo _ (Just (LineSplitTextEdits ins _))) newPragma = ins & L.newText .~ "{-# LANGUAGE " <> showExtension newPragma <> " #-}\n" :: LSP.TextEdit insertNewPragma (NextPragmaInfo nextPragmaLine _) newPragma = LSP.TextEdit pragmaInsertRange $ "{-# LANGUAGE " <> showExtension newPragma <> " #-}\n" where pragmaInsertPosition = LSP.Position (fromIntegral nextPragmaLine) 0 @@ -98,8 +99,8 @@ isDownwardLineHaddock = List.isPrefixOf "-- |" -- need to merge tokens that are deleted/inserted into one TextEdit each -- to work around some weird TextEdits applied in reversed order issue updateLineSplitTextEdits :: LSP.Range -> String -> Maybe LineSplitTextEdits -> LineSplitTextEdits -updateLineSplitTextEdits tokenRange tokenString prevLineSplitTextEdits - | Just prevLineSplitTextEdits <- prevLineSplitTextEdits +updateLineSplitTextEdits tokenRange tokenString mbPrevLineSplitTextEdits + | Just prevLineSplitTextEdits <- mbPrevLineSplitTextEdits , let LineSplitTextEdits { lineSplitInsertTextEdit = prevInsertTextEdit , lineSplitDeleteTextEdit = prevDeleteTextEdit } = prevLineSplitTextEdits @@ -290,8 +291,8 @@ updateParserState token range prevParserState | otherwise = prevParserState where hasDeleteStartedOnSameLine :: Int -> Maybe LineSplitTextEdits -> Bool - hasDeleteStartedOnSameLine line lineSplitTextEdits - | Just lineSplitTextEdits <- lineSplitTextEdits + hasDeleteStartedOnSameLine line mbLineSplitTextEdits + | Just lineSplitTextEdits <- mbLineSplitTextEdits , let LineSplitTextEdits{ lineSplitDeleteTextEdit } = lineSplitTextEdits , let LSP.TextEdit deleteRange _ = lineSplitDeleteTextEdit , let LSP.Range _ deleteEndPosition = deleteRange diff --git a/ghcide/src/Development/IDE/Types/Exports.hs b/ghcide/src/Development/IDE/Types/Exports.hs index d8491c72e1..60ac50e7b4 100644 --- a/ghcide/src/Development/IDE/Types/Exports.hs +++ b/ghcide/src/Development/IDE/Types/Exports.hs @@ -23,21 +23,18 @@ module Development.IDE.Types.Exports import Control.DeepSeq (NFData (..), force, ($!!)) import Control.Monad -import Data.Bifunctor (Bifunctor (second)) import Data.Char (isUpper) import Data.Hashable (Hashable) -import Data.HashMap.Strict (HashMap, elems) -import qualified Data.HashMap.Strict as Map import Data.HashSet (HashSet) import qualified Data.HashSet as Set -import Data.List (foldl', isSuffixOf) +import Data.List (isSuffixOf) import Data.Text (Text, uncons) import Data.Text.Encoding (decodeUtf8, encodeUtf8) import Development.IDE.GHC.Compat import Development.IDE.GHC.Orphans () -import Development.IDE.GHC.Util import GHC.Generics (Generic) -import HieDb +import HieDb hiding (withHieDb) +import Prelude hiding (mod) data ExportsMap = ExportsMap @@ -46,7 +43,7 @@ data ExportsMap = ExportsMap } instance NFData ExportsMap where - rnf (ExportsMap a b) = foldOccEnv (\a b -> rnf a `seq` b) (seqEltsUFM rnf b) a + rnf (ExportsMap a b) = foldOccEnv (\c d -> rnf c `seq` d) (seqEltsUFM rnf b) a instance Show ExportsMap where show (ExportsMap occs mods) = @@ -63,13 +60,13 @@ instance Show ExportsMap where updateExportsMap :: ExportsMap -> ExportsMap -> ExportsMap updateExportsMap old new = ExportsMap { getExportsMap = delListFromOccEnv (getExportsMap old) old_occs `plusOccEnv` getExportsMap new -- plusOccEnv is right biased - , getModuleExportsMap = (getModuleExportsMap old) `plusUFM` (getModuleExportsMap new) -- plusUFM is right biased + , getModuleExportsMap = getModuleExportsMap old `plusUFM` getModuleExportsMap new -- plusUFM is right biased } where old_occs = concat [map name $ Set.toList (lookupWithDefaultUFM_Directly (getModuleExportsMap old) mempty m_uniq) | m_uniq <- nonDetKeysUFM (getModuleExportsMap new)] size :: ExportsMap -> Int -size = sum . map (Set.size) . nonDetOccEnvElts . getExportsMap +size = sum . map Set.size . nonDetOccEnvElts . getExportsMap mkVarOrDataOcc :: Text -> OccName mkVarOrDataOcc t = mkOcc $ mkFastStringByteString $ encodeUtf8 t @@ -144,8 +141,8 @@ mkIdentInfos mod (AvailFL fl) = mkIdentInfos mod (AvailTC parent (n:nn) flds) -- Following the GHC convention that parent == n if parent is exported | n == parent - = [ IdentInfo (nameOccName n) (Just $! nameOccName parent) mod - | n <- nn ++ map flSelector flds + = [ IdentInfo (nameOccName name) (Just $! nameOccName parent) mod + | name <- nn ++ map flSelector flds ] ++ [ IdentInfo (nameOccName n) Nothing mod] @@ -162,7 +159,7 @@ createExportsMap modIface = do where doOne modIFace = do let getModDetails = unpackAvail $ moduleName $ mi_module modIFace - concatMap (getModDetails) (mi_exports modIFace) + concatMap getModDetails (mi_exports modIFace) createExportsMapMg :: [ModGuts] -> ExportsMap createExportsMapMg modGuts = do @@ -202,7 +199,7 @@ unpackAvail mn | nonInternalModules mn = map f . mkIdentInfos mn | otherwise = const [] where - f id@IdentInfo {..} = (name, mn, Set.singleton id) + f identInfo@IdentInfo {..} = (name, mn, Set.singleton identInfo) identInfoToKeyVal :: IdentInfo -> (ModuleName, IdentInfo) diff --git a/ghcide/src/Development/IDE/Types/HscEnvEq.hs b/ghcide/src/Development/IDE/Types/HscEnvEq.hs index 623e1da691..bb8653ac77 100644 --- a/ghcide/src/Development/IDE/Types/HscEnvEq.hs +++ b/ghcide/src/Development/IDE/Types/HscEnvEq.hs @@ -22,7 +22,7 @@ import Data.Set (Set) import qualified Data.Set as Set import Data.Unique (Unique) import qualified Data.Unique as Unique -import Development.IDE.GHC.Compat +import Development.IDE.GHC.Compat hiding (newUnique) import qualified Development.IDE.GHC.Compat.Util as Maybes import Development.IDE.GHC.Error (catchSrcErrors) import Development.IDE.GHC.Util (lookupPackageConfig) diff --git a/ghcide/src/Development/IDE/Types/Location.hs b/ghcide/src/Development/IDE/Types/Location.hs index 6878c6f0f8..6939f2b27d 100644 --- a/ghcide/src/Development/IDE/Types/Location.hs +++ b/ghcide/src/Development/IDE/Types/Location.hs @@ -31,18 +31,22 @@ import Control.Monad import Data.Hashable (Hashable (hash)) import Data.Maybe (fromMaybe) import Data.String +import Language.LSP.Protocol.Types (Location (..), Position (..), + Range (..)) +import qualified Language.LSP.Protocol.Types as LSP +import Text.ParserCombinators.ReadP as ReadP + +-- See Note [Guidelines For Using CPP In GHCIDE Import Statements] + +#if !MIN_VERSION_ghc(9,0,0) +import FastString +import SrcLoc as GHC +#endif #if MIN_VERSION_ghc(9,0,0) import GHC.Data.FastString import GHC.Types.SrcLoc as GHC -#else -import FastString -import SrcLoc as GHC #endif -import Language.LSP.Protocol.Types (Location (..), Position (..), - Range (..)) -import qualified Language.LSP.Protocol.Types as LSP -import Text.ParserCombinators.ReadP as ReadP toNormalizedFilePath' :: FilePath -> LSP.NormalizedFilePath -- We want to keep empty paths instead of normalising them to "." diff --git a/ghcide/src/Development/IDE/Types/Options.hs b/ghcide/src/Development/IDE/Types/Options.hs index 17bf035439..1291e044f4 100644 --- a/ghcide/src/Development/IDE/Types/Options.hs +++ b/ghcide/src/Development/IDE/Types/Options.hs @@ -2,8 +2,7 @@ -- SPDX-License-Identifier: Apache-2.0 -- | Options -{-# LANGUAGE OverloadedLabels #-} -{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE RankNTypes #-} module Development.IDE.Types.Options ( IdeOptions(..) , IdePreprocessedSource(..) @@ -19,6 +18,7 @@ module Development.IDE.Types.Options , OptHaddockParse(..) , ProgressReportingStyle(..) ) where + import Control.Lens import qualified Data.Text as T import Data.Typeable @@ -30,6 +30,7 @@ import Ide.Plugin.Config import Ide.Types (DynFlagsModifications) import qualified Language.LSP.Protocol.Lens as L import qualified Language.LSP.Protocol.Types as LSP + data IdeOptions = IdeOptions { optPreprocessor :: GHC.ParsedSource -> IdePreprocessedSource -- ^ Preprocessor to run over all parsed source trees, generating a list of warnings diff --git a/ghcide/src/Generics/SYB/GHC.hs b/ghcide/src/Generics/SYB/GHC.hs index 8aaf99fa32..f0d600c87d 100644 --- a/ghcide/src/Generics/SYB/GHC.hs +++ b/ghcide/src/Generics/SYB/GHC.hs @@ -31,7 +31,7 @@ genericIsSubspan :: SrcSpan -> GenericQ (Maybe (Bool, ast)) genericIsSubspan _ dst = mkQ Nothing $ \case - (L span ast :: Located ast) -> Just (dst `isSubspanOf` span, ast) + (L srcSpan ast :: Located ast) -> Just (dst `isSubspanOf` srcSpan, ast) -- | Lift a function that replaces a value with several values into a generic diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index cc36e6aa5d..ce7acc2ff5 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -531,6 +531,9 @@ instance PluginMethod Request Method_CallHierarchyOutgoingCalls where -- This method has no URI parameter, thus no call to 'pluginResponsible' pluginEnabled _ _ pluginDesc conf = pluginEnabledConfig plcCallHierarchyOn (configForPlugin conf pluginDesc) +instance PluginMethod Request Method_WorkspaceExecuteCommand where + pluginEnabled _ _ _ _= True + instance PluginMethod Request (Method_CustomMethod m) where pluginEnabled _ _ _ _ = True diff --git a/plugins/hls-cabal-plugin/hls-cabal-plugin.cabal b/plugins/hls-cabal-plugin/hls-cabal-plugin.cabal index cdd614c653..c21f2f2639 100644 --- a/plugins/hls-cabal-plugin/hls-cabal-plugin.cabal +++ b/plugins/hls-cabal-plugin/hls-cabal-plugin.cabal @@ -29,6 +29,7 @@ library Ide.Plugin.Cabal.Diagnostics Ide.Plugin.Cabal.Completion.Completer.FilePath Ide.Plugin.Cabal.Completion.Completer.Module + Ide.Plugin.Cabal.Completion.Completer.Paths Ide.Plugin.Cabal.Completion.Completer.Simple Ide.Plugin.Cabal.Completion.Completer.Snippet Ide.Plugin.Cabal.Completion.Completer.Types diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/FilePath.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/FilePath.hs index b0681d467d..c7aa59f125 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/FilePath.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/FilePath.hs @@ -5,9 +5,11 @@ module Ide.Plugin.Cabal.Completion.Completer.FilePath where import Control.Exception (evaluate, try) import Control.Monad (filterM) -import Control.Monad.Extra (forM) +import Control.Monad.Extra (concatForM, forM) import qualified Data.Text as T +import Distribution.PackageDescription (GenericPackageDescription) import Ide.Logger +import Ide.Plugin.Cabal.Completion.Completer.Paths import Ide.Plugin.Cabal.Completion.Completer.Simple import Ide.Plugin.Cabal.Completion.Completer.Types import Ide.Plugin.Cabal.Completion.Types @@ -23,7 +25,7 @@ import qualified Text.Fuzzy.Parallel as Fuzzy filePathCompleter :: Completer filePathCompleter recorder cData = do let prefInfo = cabalPrefixInfo cData - complInfo = pathCompletionInfoFromCabalPrefixInfo prefInfo + complInfo = pathCompletionInfoFromCabalPrefixInfo "" prefInfo filePathCompletions <- listFileCompletions recorder complInfo let scored = Fuzzy.simpleFilter @@ -39,12 +41,44 @@ filePathCompleter recorder cData = do pure $ mkCompletionItem (completionRange prefInfo) fullFilePath fullFilePath ) +mainIsCompleter :: (Maybe StanzaName -> GenericPackageDescription -> [FilePath]) -> Completer +mainIsCompleter extractionFunction recorder cData = do + mGPD <- getLatestGPD cData + case mGPD of + Just gpd -> do + let srcDirs = extractionFunction sName gpd + concatForM srcDirs + (\dir' -> do + let dir = FP.normalise dir' + let pathInfo = pathCompletionInfoFromCabalPrefixInfo dir prefInfo + completions <- listFileCompletions recorder pathInfo + let scored = Fuzzy.simpleFilter + Fuzzy.defChunkSize + Fuzzy.defMaxResults + (pathSegment pathInfo) + (map T.pack completions) + forM + scored + ( \compl' -> do + let compl = Fuzzy.original compl' + fullFilePath <- mkFilePathCompletion pathInfo compl + pure $ mkCompletionItem (completionRange prefInfo) fullFilePath fullFilePath + ) + ) + Nothing -> do + logWith recorder Debug LogUseWithStaleFastNoResult + pure [] + where + sName = stanzaName cData + prefInfo = cabalPrefixInfo cData + + -- | Completer to be used when a directory can be completed for the field. -- Only completes directories. directoryCompleter :: Completer directoryCompleter recorder cData = do let prefInfo = cabalPrefixInfo cData - complInfo = pathCompletionInfoFromCabalPrefixInfo prefInfo + complInfo = pathCompletionInfoFromCabalPrefixInfo "" prefInfo directoryCompletions <- listDirectoryCompletions recorder complInfo let scored = Fuzzy.simpleFilter @@ -73,33 +107,6 @@ directoryCompleter recorder cData = do be used for file path completions to be written to the cabal file. -} --- | Information used to query and build path completions. --- --- Note that pathSegment combined with queryDirectory results in --- the original prefix. --- --- Example: --- When given the written prefix, @dir1\/dir2\/fi@, the --- resulting PathCompletionInfo would be: --- --- @ --- pathSegment = "fi" --- queryDirectory = "dir1\/dir2\/fi" --- ... --- @ -data PathCompletionInfo = PathCompletionInfo - { -- | partly written segment of the next part of the path - pathSegment :: T.Text, - -- | written part of path, platform dependent - queryDirectory :: FilePath, - -- | directory relative to which relative paths are interpreted, platform dependent - workingDirectory :: FilePath, - -- | Did the completion happen in the context of a string notation, - -- if yes, contains the state of the string notation - isStringNotationPath :: Maybe Apostrophe - } - deriving (Eq, Show) - -- | Takes a PathCompletionInfo and returns the list of files and directories -- in the directory which match the path completion info in posix style. -- @@ -126,18 +133,6 @@ listDirectoryCompletions recorder complInfo = do filepaths <- listFileCompletions recorder complInfo filterM (doesDirectoryExist . mkDirFromCWD complInfo) filepaths -pathCompletionInfoFromCabalPrefixInfo :: CabalPrefixInfo -> PathCompletionInfo -pathCompletionInfoFromCabalPrefixInfo ctx = - PathCompletionInfo - { pathSegment = T.pack pathSegment', - queryDirectory = queryDirectory', - workingDirectory = completionWorkingDir ctx, - isStringNotationPath = isStringNotation ctx - } - where - prefix = T.unpack $ completionPrefix ctx - (queryDirectory', pathSegment') = Posix.splitFileName prefix - -- | Returns the directory where files and directories can be queried from -- for the passed PathCompletionInfo. -- diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/Module.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/Module.hs index 4cae2dae5d..21dfbb9e1f 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/Module.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/Module.hs @@ -6,26 +6,16 @@ import Control.Monad (filterM) import Control.Monad.Extra (concatForM, forM) import Data.List (stripPrefix) -import qualified Data.List as List import Data.Maybe (fromMaybe) import qualified Data.Text as T -import Distribution.PackageDescription (Benchmark (..), - BuildInfo (..), - CondTree (condTreeData), - Executable (..), - GenericPackageDescription (..), - Library (..), - UnqualComponentName, - mkUnqualComponentName, - testBuildInfo) -import Distribution.Utils.Path (getSymbolicPath) +import Distribution.PackageDescription (GenericPackageDescription) import Ide.Logger (Priority (..), Recorder, WithPriority, logWith) -import Ide.Plugin.Cabal.Completion.Completer.FilePath (PathCompletionInfo (..), - listFileCompletions, +import Ide.Plugin.Cabal.Completion.Completer.FilePath (listFileCompletions, mkCompletionDirectory) +import Ide.Plugin.Cabal.Completion.Completer.Paths import Ide.Plugin.Cabal.Completion.Completer.Simple import Ide.Plugin.Cabal.Completion.Completer.Types import Ide.Plugin.Cabal.Completion.Types @@ -53,56 +43,6 @@ modulesCompleter extractionFunction recorder cData = do sName = stanzaName cData prefInfo = cabalPrefixInfo cData --- | Extracts the source directories of the library stanza. -sourceDirsExtractionLibrary :: Maybe StanzaName -> GenericPackageDescription -> [FilePath] -sourceDirsExtractionLibrary Nothing gpd = - -- we use condLibrary to get the information contained in the library stanza - -- since the library in PackageDescription is not populated by us - case libM of - Just lib -> do - map getSymbolicPath $ hsSourceDirs $ libBuildInfo $ condTreeData lib - Nothing -> [] - where - libM = condLibrary gpd -sourceDirsExtractionLibrary name gpd = extractRelativeDirsFromStanza name gpd condSubLibraries libBuildInfo - --- | Extracts the source directories of the executable stanza with the given name. -sourceDirsExtractionExecutable :: Maybe StanzaName -> GenericPackageDescription -> [FilePath] -sourceDirsExtractionExecutable name gpd = extractRelativeDirsFromStanza name gpd condExecutables buildInfo - --- | Extracts the source directories of the test suite stanza with the given name. -sourceDirsExtractionTestSuite :: Maybe StanzaName -> GenericPackageDescription -> [FilePath] -sourceDirsExtractionTestSuite name gpd = extractRelativeDirsFromStanza name gpd condTestSuites testBuildInfo - --- | Extracts the source directories of benchmark stanza with the given name. -sourceDirsExtractionBenchmark :: Maybe StanzaName -> GenericPackageDescription -> [FilePath] -sourceDirsExtractionBenchmark name gpd = extractRelativeDirsFromStanza name gpd condBenchmarks benchmarkBuildInfo - --- | Takes a possible stanza name, a GenericPackageDescription, --- a function to access the stanza information we are interested in --- and a function to access the build info from the specific stanza. --- --- Returns a list of relative source directory paths specified for the extracted stanza. -extractRelativeDirsFromStanza :: - Maybe StanzaName -> - GenericPackageDescription -> - (GenericPackageDescription -> [(UnqualComponentName, CondTree b c a)]) -> - (a -> BuildInfo) -> - [FilePath] -extractRelativeDirsFromStanza Nothing _ _ _ = [] -extractRelativeDirsFromStanza (Just name) gpd getStanza getBuildInfo - | Just stanza <- stanzaM = map getSymbolicPath $ hsSourceDirs $ getBuildInfo stanza - | otherwise = [] - where - stanzaM = fmap (condTreeData . snd) res - allStanzasM = getStanza gpd - res = - List.find - ( \(n, _) -> - n == mkUnqualComponentName (T.unpack name) - ) - allStanzasM - -- | Takes a list of source directories and returns a list of path completions -- relative to any of the passed source directories which fit the passed prefix info. filePathsForExposedModules :: Recorder (WithPriority Log) -> [FilePath] -> CabalPrefixInfo -> IO [T.Text] @@ -111,34 +51,36 @@ filePathsForExposedModules recorder srcDirs prefInfo = do srcDirs ( \dir' -> do let dir = FP.normalise dir' - let pInfo = - PathCompletionInfo - { isStringNotationPath = Nothing, - pathSegment = T.pack $ FP.takeFileName prefix, - queryDirectory = FP.addTrailingPathSeparator $ FP.takeDirectory prefix, - workingDirectory = completionWorkingDir prefInfo FP. dir - } - completions <- listFileCompletions recorder pInfo - validExposedCompletions <- filterM (isValidExposedModulePath pInfo) completions - let toMatch = pathSegment pInfo - scored = Fuzzy.simpleFilter Fuzzy.defChunkSize Fuzzy.defMaxResults toMatch (map T.pack validExposedCompletions) + pathInfo = pathCompletionInfoFromCabalPrefixInfo dir modPrefInfo + completions <- listFileCompletions recorder pathInfo + validExposedCompletions <- filterM (isValidExposedModulePath pathInfo) completions + let toMatch = pathSegment pathInfo + scored = Fuzzy.simpleFilter + Fuzzy.defChunkSize + Fuzzy.defMaxResults + toMatch + (map T.pack validExposedCompletions) forM scored ( \compl' -> do let compl = Fuzzy.original compl' - fullFilePath <- mkExposedModulePathCompletion pInfo $ T.unpack compl + fullFilePath <- mkExposedModulePathCompletion pathInfo $ T.unpack compl pure fullFilePath ) ) where prefix = - exposedModulePathToFp $ + T.pack $ exposedModulePathToFp $ completionPrefix prefInfo - -- \| Takes a PathCompletionInfo and a path segment and checks whether + -- build completion info relative to the source dir, + -- we overwrite the prefix written in the cabal file with its translation + -- to filepath syntax, since it is in exposed module syntax + modPrefInfo = prefInfo{completionPrefix=prefix} + + -- Takes a PathCompletionInfo and a path segment and checks whether -- the path segment can be completed for an exposed module. -- -- This is the case if the segment represents either a directory or a Haskell file. - -- isValidExposedModulePath :: PathCompletionInfo -> FilePath -> IO Bool isValidExposedModulePath pInfo path = do let dir = mkCompletionDirectory pInfo diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/Paths.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/Paths.hs new file mode 100644 index 0000000000..b067fa9e49 --- /dev/null +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/Paths.hs @@ -0,0 +1,118 @@ +module Ide.Plugin.Cabal.Completion.Completer.Paths where + +import qualified Data.List as List +import qualified Data.Text as T +import Distribution.PackageDescription (Benchmark (..), + BuildInfo (..), + CondTree (condTreeData), + Executable (..), + GenericPackageDescription (..), + Library (..), + UnqualComponentName, + mkUnqualComponentName, + testBuildInfo) +import Distribution.Utils.Path (getSymbolicPath) +import Ide.Plugin.Cabal.Completion.Types +import qualified System.FilePath as FP +import qualified System.FilePath.Posix as Posix + + +{- | Information used to query and build path completions. + + Note that pathSegment combined with queryDirectory results in + the original prefix. + + Example: + When given the written prefix, @dir1\/dir2\/fi@, the + resulting PathCompletionInfo would be: + + @ + pathSegment = "fi" + queryDirectory = "dir1\/dir2\/fi" + ... + @ +-} +data PathCompletionInfo = PathCompletionInfo + { pathSegment :: T.Text, + -- ^ Partly written segment of the next part of the path. + queryDirectory :: FilePath, + -- ^ Written part of path, in posix format. + workingDirectory :: FilePath, + -- ^ Directory relative to which relative paths are interpreted, platform dependent. + isStringNotationPath :: Maybe Apostrophe + -- ^ Did the completion happen in the context of a string notation, + -- if yes, contains the state of the string notation. + } + deriving (Eq, Show) + +{- | Takes an optional source subdirectory and a prefix info + and creates a path completion info accordingly. + + The source directory represents some subdirectory of the working directory such as a + path from the field @hs-source-dirs@. + + If the source subdirectory is empty, then the working directory is simply set to + the currently handled cabal file's directory. +-} +pathCompletionInfoFromCabalPrefixInfo :: FilePath -> CabalPrefixInfo -> PathCompletionInfo +pathCompletionInfoFromCabalPrefixInfo srcDir prefInfo = + PathCompletionInfo + { pathSegment = T.pack pathSegment', + queryDirectory = queryDirectory', + workingDirectory = completionWorkingDir prefInfo FP. srcDir, + isStringNotationPath = isStringNotation prefInfo + } + where + prefix = T.unpack $ completionPrefix prefInfo + (queryDirectory', pathSegment') = Posix.splitFileName prefix + +-- | Extracts the source directories of the library stanza. +sourceDirsExtractionLibrary :: Maybe StanzaName -> GenericPackageDescription -> [FilePath] +sourceDirsExtractionLibrary Nothing gpd = + -- we use condLibrary to get the information contained in the library stanza + -- since the library in PackageDescription is not populated by us + case libM of + Just lib -> do + map getSymbolicPath $ hsSourceDirs $ libBuildInfo $ condTreeData lib + Nothing -> [] + where + libM = condLibrary gpd +sourceDirsExtractionLibrary name gpd = extractRelativeDirsFromStanza name gpd condSubLibraries libBuildInfo + +-- | Extracts the source directories of the executable stanza with the given name. +sourceDirsExtractionExecutable :: Maybe StanzaName -> GenericPackageDescription -> [FilePath] +sourceDirsExtractionExecutable name gpd = extractRelativeDirsFromStanza name gpd condExecutables buildInfo + +-- | Extracts the source directories of the test suite stanza with the given name. +sourceDirsExtractionTestSuite :: Maybe StanzaName -> GenericPackageDescription -> [FilePath] +sourceDirsExtractionTestSuite name gpd = extractRelativeDirsFromStanza name gpd condTestSuites testBuildInfo + +-- | Extracts the source directories of benchmark stanza with the given name. +sourceDirsExtractionBenchmark :: Maybe StanzaName -> GenericPackageDescription -> [FilePath] +sourceDirsExtractionBenchmark name gpd = extractRelativeDirsFromStanza name gpd condBenchmarks benchmarkBuildInfo + +{- | Takes a possible stanza name, a GenericPackageDescription, + a function to access the stanza information we are interested in + and a function to access the build info from the specific stanza. + + Returns a list of relative source directory paths specified for the extracted stanza. +-} +extractRelativeDirsFromStanza :: + Maybe StanzaName -> + GenericPackageDescription -> + (GenericPackageDescription -> [(UnqualComponentName, CondTree b c a)]) -> + (a -> BuildInfo) -> + [FilePath] +extractRelativeDirsFromStanza Nothing _ _ _ = [] +extractRelativeDirsFromStanza (Just name) gpd getStanza getBuildInfo + | Just stanza <- stanzaM = map getSymbolicPath $ hsSourceDirs $ getBuildInfo stanza + | otherwise = [] + where + stanzaM = fmap (condTreeData . snd) res + allStanzasM = getStanza gpd + res = + List.find + ( \(n, _) -> + n == mkUnqualComponentName (T.unpack name) + ) + allStanzasM diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completions.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completions.hs index 840dc44e50..69c5fa6598 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completions.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completions.hs @@ -49,7 +49,7 @@ contextToCompleter (TopLevel, KeyWord kw) = contextToCompleter (Stanza s _, None) = case Map.lookup s stanzaKeywordMap of Nothing -> errorNoopCompleter (LogUnknownStanzaNameInContextError s) - Just l -> constantCompleter $ Map.keys l ++ Map.keys stanzaKeywordMap + Just l -> constantCompleter $ Map.keys l -- if we are in a stanza's keyword's context we can complete possible values of that keyword contextToCompleter (Stanza s _, KeyWord kw) = case Map.lookup s stanzaKeywordMap of diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Data.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Data.hs index 5c42dca708..24badfcfc5 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Data.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Data.hs @@ -14,6 +14,7 @@ import Distribution.CabalSpecVersion (CabalSpecVersio showCabalSpecVersion) import Ide.Plugin.Cabal.Completion.Completer.FilePath import Ide.Plugin.Cabal.Completion.Completer.Module +import Ide.Plugin.Cabal.Completion.Completer.Paths import Ide.Plugin.Cabal.Completion.Completer.Simple import Ide.Plugin.Cabal.Completion.Completer.Types (Completer) import Ide.Plugin.Cabal.Completion.Types @@ -92,16 +93,16 @@ libraryFields = executableFields :: Map KeyWordName Completer executableFields = Map.fromList - [ ("main-is:", filePathCompleter), + [ ("main-is:", mainIsCompleter sourceDirsExtractionExecutable), ("scope:", constantCompleter ["public", "private"]), - ("other-modules:", modulesCompleter (sourceDirsExtractionExecutable)) + ("other-modules:", modulesCompleter sourceDirsExtractionExecutable) ] testSuiteFields :: Map KeyWordName Completer testSuiteFields = Map.fromList [ ("type:", constantCompleter ["exitcode-stdio-1.0", "detailed-0.9"]), - ("main-is:", filePathCompleter), + ("main-is:", mainIsCompleter sourceDirsExtractionTestSuite), ("other-modules:", modulesCompleter sourceDirsExtractionTestSuite) ] @@ -109,8 +110,8 @@ benchmarkFields :: Map KeyWordName Completer benchmarkFields = Map.fromList [ ("type:", noopCompleter), - ("main-is:", filePathCompleter), - ("other-modules:", modulesCompleter (sourceDirsExtractionBenchmark)) + ("main-is:", mainIsCompleter sourceDirsExtractionBenchmark), + ("other-modules:", modulesCompleter sourceDirsExtractionBenchmark) ] foreignLibraryFields :: Map KeyWordName Completer diff --git a/plugins/hls-cabal-plugin/test/Completer.hs b/plugins/hls-cabal-plugin/test/Completer.hs index d2d28fc07b..594678ad71 100644 --- a/plugins/hls-cabal-plugin/test/Completer.hs +++ b/plugins/hls-cabal-plugin/test/Completer.hs @@ -3,15 +3,19 @@ module Completer where -import Control.Lens ((^.)) +import Control.Lens ((^.), (^?)) +import Control.Lens.Prism import qualified Data.ByteString as ByteString +import Data.Maybe (mapMaybe) import qualified Data.Text as T import Distribution.PackageDescription.Parsec (parseGenericPackageDescriptionMaybe) import Ide.Plugin.Cabal.Completion.Completer.FilePath import Ide.Plugin.Cabal.Completion.Completer.Module +import Ide.Plugin.Cabal.Completion.Completer.Paths import Ide.Plugin.Cabal.Completion.Completer.Types (CompleterData (..)) import Ide.Plugin.Cabal.Completion.Completions -import Ide.Plugin.Cabal.Completion.Types +import Ide.Plugin.Cabal.Completion.Types (CabalPrefixInfo (..), + StanzaName) import Ide.Plugin.Cabal.Parse (GenericPackageDescription) import qualified Language.LSP.Protocol.Lens as L import qualified Language.LSP.VFS as VFS @@ -23,7 +27,8 @@ completerTests :: TestTree completerTests = testGroup "Completer Tests" - [ fileCompleterTests, + [ basicCompleterTests, + fileCompleterTests, filePathCompletionContextTests, directoryCompleterTests, completionHelperTests, @@ -31,41 +36,59 @@ completerTests = exposedModuleCompleterTests ] +basicCompleterTests :: TestTree +basicCompleterTests = + testGroup + "Basic Completer Tests" + [ runCabalTestCaseSession "In stanza context - stanza should not be suggested" "" $ do + doc <- openDoc "completer.cabal" "cabal" + compls <- getCompletions doc (Position 11 7) + let complTexts = getTextEditTexts compls + liftIO $ assertBool "does not suggest library" $ "library" `notElem` complTexts + liftIO $ assertBool "suggests library keyword" $ "extra-libraries:" `elem` complTexts + , runCabalTestCaseSession "In top level context - stanza should be suggested" "" $ do + doc <- openDoc "completer.cabal" "cabal" + compls <- getCompletions doc (Position 8 2) + let complTexts = getTextEditTexts compls + liftIO $ assertBool "suggests benchmark" $ "benchmark" `elem` complTexts + , runCabalTestCaseSession "Main-is completions should be relative to hs-source-dirs of same stanza" "filepath-completions" $ do + doc <- openDoc "main-is.cabal" "cabal" + compls <- getCompletions doc (Position 10 12) + let complTexts = getTextEditTexts compls + liftIO $ assertBool "suggests f2" $ "./f2.hs" `elem` complTexts + liftIO $ assertBool "does not suggest" $ "./Content.hs" `notElem` complTexts + ] + where + getTextEditTexts :: [CompletionItem] -> [T.Text] + getTextEditTexts compls = mapMaybe (^? L.textEdit . _Just . _L . L.newText) compls + fileCompleterTests :: TestTree fileCompleterTests = testGroup "File Completer Tests" [ testCase "Current Directory" $ do - testDir <- getFilePathComplTestDir - completions <- completeFilePath "" testDir - completions @?== ["./.hidden", "./Content.hs", "./dir1/", "./dir2/", "./textfile.txt"], + completions <- completeFilePath "" filePathComplTestDir + completions @?== ["./.hidden", "./Content.hs", "./dir1/", "./dir2/", "./textfile.txt", "./main-is.cabal"], testCase "Current Directory - alternative writing" $ do - testDir <- getFilePathComplTestDir - completions <- completeFilePath "./" testDir - completions @?== ["./.hidden", "./Content.hs", "./dir1/", "./dir2/", "./textfile.txt"], + completions <- completeFilePath "./" filePathComplTestDir + completions @?== ["./.hidden", "./Content.hs", "./dir1/", "./dir2/", "./textfile.txt", "./main-is.cabal"], testCase "Current Directory - hidden file start" $ do - testDir <- getFilePathComplTestDir - completions <- completeFilePath "." testDir - completions @?== ["./Content.hs", "./.hidden", "./textfile.txt"], + completions <- completeFilePath "." filePathComplTestDir + completions @?== ["./Content.hs", "./.hidden", "./textfile.txt", "./main-is.cabal"], testCase "Current Directory - incomplete directory path written" $ do - testDir <- getFilePathComplTestDir - completions <- completeFilePath "di" testDir + completions <- completeFilePath "di" filePathComplTestDir completions @?== ["./dir1/", "./dir2/"], testCase "Current Directory - incomplete filepath written" $ do - testDir <- getFilePathComplTestDir - completions <- completeFilePath "te" testDir + completions <- completeFilePath "te" filePathComplTestDir completions @?== ["./Content.hs", "./textfile.txt"], testCase "Subdirectory" $ do - testDir <- getFilePathComplTestDir - completions <- completeFilePath "dir1/" testDir + completions <- completeFilePath "dir1/" filePathComplTestDir completions @?== ["dir1/f1.txt", "dir1/f2.hs"], testCase "Subdirectory - incomplete filepath written" $ do - testDir <- getFilePathComplTestDir - completions <- completeFilePath "dir2/dir3/MA" testDir + completions <- completeFilePath "dir2/dir3/MA" filePathComplTestDir completions @?== ["dir2/dir3/MARKDOWN.md"], testCase "Nonexistent directory" $ do - testDir <- getFilePathComplTestDir - completions <- completeFilePath "dir2/dir4/" testDir + completions <- completeFilePath "dir2/dir4/" filePathComplTestDir completions @?== [] ] where @@ -106,7 +129,6 @@ filePathCompletionContextTests = let complContext = getCabalPrefixInfo "" (simplePosPrefixInfo "\"fp.txt\" \"src fp2.txt" 0 13) completionPrefix complContext @?= "src", testCase "Current Directory" $ do - testDir <- getFilePathComplTestDir compls <- listFileCompletions mempty @@ -114,11 +136,10 @@ filePathCompletionContextTests = { isStringNotationPath = Nothing, pathSegment = "", queryDirectory = "", - workingDirectory = testDir + workingDirectory = filePathComplTestDir } - compls @?== [".hidden", "Content.hs", "dir1/", "dir2/", "textfile.txt"], + compls @?== [".hidden", "Content.hs", "dir1/", "dir2/", "textfile.txt", "main-is.cabal"], testCase "In directory" $ do - testDir <- getFilePathComplTestDir compls <- listFileCompletions mempty @@ -126,7 +147,7 @@ filePathCompletionContextTests = { isStringNotationPath = Nothing, pathSegment = "", queryDirectory = "dir1/", - workingDirectory = testDir + workingDirectory = filePathComplTestDir } compls @?== ["f1.txt", "f2.hs"] ] @@ -145,32 +166,25 @@ directoryCompleterTests = testGroup "Directory Completer Tests" [ testCase "Current Directory" $ do - testDir <- getFilePathComplTestDir - completions <- completeDirectory "" testDir + completions <- completeDirectory "" filePathComplTestDir completions @?== ["./dir1/", "./dir2/"], testCase "Current Directory - alternative writing" $ do - testDir <- getFilePathComplTestDir - completions <- completeDirectory "./" testDir + completions <- completeDirectory "./" filePathComplTestDir completions @?== ["./dir1/", "./dir2/"], testCase "Current Directory - incomplete directory path written" $ do - testDir <- getFilePathComplTestDir - completions <- completeDirectory "di" testDir + completions <- completeDirectory "di" filePathComplTestDir completions @?== ["./dir1/", "./dir2/"], testCase "Current Directory - incomplete filepath written" $ do - testDir <- getFilePathComplTestDir - completions <- completeDirectory "te" testDir + completions <- completeDirectory "te" filePathComplTestDir completions @?== [], testCase "Subdirectory - no more directories found" $ do - testDir <- getFilePathComplTestDir - completions <- completeDirectory "dir1/" testDir + completions <- completeDirectory "dir1/" filePathComplTestDir completions @?== [], testCase "Subdirectory - available subdirectory" $ do - testDir <- getFilePathComplTestDir - completions <- completeDirectory "dir2/" testDir + completions <- completeDirectory "dir2/" filePathComplTestDir completions @?== ["dir2/dir3/"], testCase "Nonexistent directory" $ do - testDir <- getFilePathComplTestDir - completions <- completeDirectory "dir2/dir4/" testDir + completions <- completeDirectory "dir2/dir4/" filePathComplTestDir completions @?== [] ] where @@ -241,8 +255,7 @@ filePathExposedModulesTests = where callFilePathsForExposedModules :: [FilePath] -> IO [T.Text] callFilePathsForExposedModules srcDirs = do - cwd <- getExposedTestDir - let prefInfo = simpleCabalPrefixInfoFromFp "" cwd + let prefInfo = simpleCabalPrefixInfoFromFp "" exposedTestDir filePathsForExposedModules mempty srcDirs prefInfo exposedModuleCompleterTests :: TestTree @@ -277,25 +290,21 @@ exposedModuleCompleterTests = CompleterData { cabalPrefixInfo = simpleExposedCabalPrefixInfo pref dir, getLatestGPD = do - testDir <- getTestDir - cabalContents <- ByteString.readFile $ testDir "exposed.cabal" + cabalContents <- ByteString.readFile $ testDataDir "exposed.cabal" pure $ parseGenericPackageDescriptionMaybe cabalContents, stanzaName = sName } callModulesCompleter :: Maybe StanzaName -> (Maybe StanzaName -> GenericPackageDescription -> [FilePath]) -> T.Text -> IO [T.Text] callModulesCompleter sName func prefix = do - cwd <- getTestDir - let cData = simpleCompleterData sName cwd prefix + let cData = simpleCompleterData sName testDataDir prefix completer <- modulesCompleter func mempty cData pure $ fmap extract completer mkCompleterData :: CabalPrefixInfo -> CompleterData mkCompleterData prefInfo = CompleterData {getLatestGPD = undefined, cabalPrefixInfo = prefInfo, stanzaName = Nothing} -getExposedTestDir :: IO FilePath -getExposedTestDir = do - testDir <- getTestDir - pure $ addTrailingPathSeparator $ testDir "src-modules" +exposedTestDir :: FilePath +exposedTestDir = addTrailingPathSeparator $ testDataDir "src-modules" simpleExposedCabalPrefixInfo :: T.Text -> FilePath -> CabalPrefixInfo simpleExposedCabalPrefixInfo prefix fp = diff --git a/plugins/hls-cabal-plugin/test/Context.hs b/plugins/hls-cabal-plugin/test/Context.hs index 356da51481..e2a7b0290e 100644 --- a/plugins/hls-cabal-plugin/test/Context.hs +++ b/plugins/hls-cabal-plugin/test/Context.hs @@ -4,17 +4,17 @@ module Context where -import Control.Monad.Trans.Maybe (runMaybeT) -import qualified Data.Text as T -import qualified Data.Text.Utf16.Rope as Rope +import Control.Monad.Trans.Maybe (runMaybeT) +import qualified Data.Text as T +import qualified Data.Text.Utf16.Rope as Rope import Ide.Plugin.Cabal -import Ide.Plugin.Cabal.Completion.Completer.FilePath +import Ide.Plugin.Cabal.Completion.Completer.Paths import Ide.Plugin.Cabal.Completion.Completions -import Ide.Plugin.Cabal.Completion.Types (Context, - FieldContext (KeyWord, None), - StanzaContext (Stanza, TopLevel)) +import Ide.Plugin.Cabal.Completion.Types (Context, + FieldContext (KeyWord, None), + StanzaContext (Stanza, TopLevel)) import Test.Hls -import Utils as T +import Utils as T cabalPlugin :: PluginTestDescriptor Ide.Plugin.Cabal.Log cabalPlugin = mkPluginTestDescriptor descriptor "cabal context" @@ -32,31 +32,25 @@ pathCompletionInfoFromCompletionContextTests = testGroup "Completion Info to Completion Context Tests" [ testCase "Current Directory" $ do - testDir <- getTestDir - let complInfo = pathCompletionInfoFromCabalPrefixInfo $ simpleCabalPrefixInfoFromFp "" testDir + let complInfo = pathCompletionInfoFromCabalPrefixInfo "" $ simpleCabalPrefixInfoFromFp "" testDataDir queryDirectory complInfo @?= "./" , testCase "Current Directory - partly written next" $ do - testDir <- getTestDir - let complInfo = pathCompletionInfoFromCabalPrefixInfo $ simpleCabalPrefixInfoFromFp "di" testDir + let complInfo = pathCompletionInfoFromCabalPrefixInfo "" $ simpleCabalPrefixInfoFromFp "di" testDataDir queryDirectory complInfo @?= "./" pathSegment complInfo @?= "di" , testCase "Current Directory - alternative writing" $ do - testDir <- getTestDir - let complInfo = pathCompletionInfoFromCabalPrefixInfo $ simpleCabalPrefixInfoFromFp "./" testDir + let complInfo = pathCompletionInfoFromCabalPrefixInfo "" $ simpleCabalPrefixInfoFromFp "./" testDataDir queryDirectory complInfo @?= "./" , testCase "Subdirectory" $ do - testDir <- getTestDir - let complInfo = pathCompletionInfoFromCabalPrefixInfo $ simpleCabalPrefixInfoFromFp "dir1/" testDir + let complInfo = pathCompletionInfoFromCabalPrefixInfo "" $ simpleCabalPrefixInfoFromFp "dir1/" testDataDir queryDirectory complInfo @?= "dir1/" pathSegment complInfo @?= "" , testCase "Subdirectory - partly written next" $ do - testDir <- getTestDir - let complInfo = pathCompletionInfoFromCabalPrefixInfo $ simpleCabalPrefixInfoFromFp "dir1/d" testDir + let complInfo = pathCompletionInfoFromCabalPrefixInfo "" $ simpleCabalPrefixInfoFromFp "dir1/d" testDataDir queryDirectory complInfo @?= "dir1/" pathSegment complInfo @?= "d" , testCase "Subdirectory - partly written next" $ do - testDir <- getTestDir - let complInfo = pathCompletionInfoFromCabalPrefixInfo $ simpleCabalPrefixInfoFromFp "dir1/dir2/d" testDir + let complInfo = pathCompletionInfoFromCabalPrefixInfo "" $ simpleCabalPrefixInfoFromFp "dir1/dir2/d" testDataDir queryDirectory complInfo @?= "dir1/dir2/" pathSegment complInfo @?= "d" ] @@ -172,6 +166,7 @@ getContextTests = -- ------------------------------------------------------------------------ -- Test Data -- ------------------------------------------------------------------------ + libraryStanzaData :: [T.Text] libraryStanzaData = [ "cabal-version: 3.0" diff --git a/plugins/hls-cabal-plugin/test/Main.hs b/plugins/hls-cabal-plugin/test/Main.hs index b2b32f6258..4ee8afac28 100644 --- a/plugins/hls-cabal-plugin/test/Main.hs +++ b/plugins/hls-cabal-plugin/test/Main.hs @@ -17,15 +17,12 @@ import Data.Either (isRight) import Data.Row import qualified Data.Text as T import qualified Data.Text as Text -import Ide.Plugin.Cabal import Ide.Plugin.Cabal.LicenseSuggest (licenseErrorSuggestion) import qualified Ide.Plugin.Cabal.Parse as Lib import qualified Language.LSP.Protocol.Lens as L import System.FilePath import Test.Hls - -cabalPlugin :: PluginTestDescriptor Ide.Plugin.Cabal.Log -cabalPlugin = mkPluginTestDescriptor descriptor "cabal" +import Utils main :: IO () main = do @@ -195,17 +192,3 @@ pluginTests = InR action@CodeAction{_title} <- codeActions guard (_title == "Replace with " <> license) pure action - --- ------------------------------------------------------------------------ --- Runner utils --- ------------------------------------------------------------------------ - -runCabalTestCaseSession :: TestName -> FilePath -> Session () -> TestTree -runCabalTestCaseSession title subdir = testCase title . runCabalSession subdir - -runCabalSession :: FilePath -> Session a -> IO a -runCabalSession subdir = - failIfSessionTimeout . runSessionWithServer cabalPlugin (testDataDir subdir) - -testDataDir :: FilePath -testDataDir = "test" "testdata" diff --git a/plugins/hls-cabal-plugin/test/Utils.hs b/plugins/hls-cabal-plugin/test/Utils.hs index 6974b9e188..078c05750a 100644 --- a/plugins/hls-cabal-plugin/test/Utils.hs +++ b/plugins/hls-cabal-plugin/test/Utils.hs @@ -5,11 +5,14 @@ module Utils where import Data.List (sort) import qualified Data.Text as T +import Ide.Plugin.Cabal (descriptor) +import qualified Ide.Plugin.Cabal import Ide.Plugin.Cabal.Completion.Types -import System.Directory (getCurrentDirectory) import System.FilePath import Test.Hls +cabalPlugin :: PluginTestDescriptor Ide.Plugin.Cabal.Log +cabalPlugin = mkPluginTestDescriptor descriptor "cabal" simpleCabalPrefixInfoFromPos :: Position -> T.Text -> CabalPrefixInfo simpleCabalPrefixInfoFromPos pos prefix = @@ -33,15 +36,18 @@ simpleCabalPrefixInfoFromFp prefix fp = , completionFileName = "test" } -getTestDir :: IO FilePath -getTestDir = do - cwd <- getCurrentDirectory - pure $ addTrailingPathSeparator $ cwd "test" "testdata" +filePathComplTestDir :: FilePath +filePathComplTestDir = addTrailingPathSeparator $ testDataDir "filepath-completions" -getFilePathComplTestDir :: IO FilePath -getFilePathComplTestDir = do - testDir <- getTestDir - pure $ addTrailingPathSeparator $ testDir "filepath-completions" +runCabalTestCaseSession :: TestName -> FilePath -> Session () -> TestTree +runCabalTestCaseSession title subdir = testCase title . runCabalSession subdir + +runCabalSession :: FilePath -> Session a -> IO a +runCabalSession subdir = + failIfSessionTimeout . runSessionWithServer cabalPlugin (testDataDir subdir) + +testDataDir :: FilePath +testDataDir = "test" "testdata" -- | list comparison where the order in the list is irrelevant (@?==) :: (HasCallStack, Ord a, Show a) => [a] -> [a] -> Assertion diff --git a/plugins/hls-cabal-plugin/test/testdata/completer.cabal b/plugins/hls-cabal-plugin/test/testdata/completer.cabal new file mode 100644 index 0000000000..cd7c697026 --- /dev/null +++ b/plugins/hls-cabal-plugin/test/testdata/completer.cabal @@ -0,0 +1,12 @@ +cabal-version: 3.4 +name: test-hls +version: 0.1.0.0 +maintainer: milky +synopsis: example cabal file :) +license: Apache-2.0 +build-type: Simple + +be + +library + lib diff --git a/plugins/hls-cabal-plugin/test/testdata/filepath-completions/main-is.cabal b/plugins/hls-cabal-plugin/test/testdata/filepath-completions/main-is.cabal new file mode 100644 index 0000000000..777f6ef769 --- /dev/null +++ b/plugins/hls-cabal-plugin/test/testdata/filepath-completions/main-is.cabal @@ -0,0 +1,11 @@ +cabal-version: 3.4 +name: test-hls +version: 0.1.0.0 +maintainer: milky +synopsis: example cabal file :) +license: Apache-2.0 +build-type: Simple + +executable exe + hs-source-dirs: ./dir1/ + main-is: