diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index bf88a55ed3..241a1b764a 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -190,6 +190,7 @@ library Development.IDE.Spans.Pragmas Development.IDE.Types.Diagnostics Development.IDE.Types.Exports + Development.IDE.Types.Path Development.IDE.Types.HscEnvEq Development.IDE.Types.KnownTargets Development.IDE.Types.Location diff --git a/ghcide/session-loader/Development/IDE/Session.hs b/ghcide/session-loader/Development/IDE/Session.hs index a1d778ab0e..85fccde9a4 100644 --- a/ghcide/session-loader/Development/IDE/Session.hs +++ b/ghcide/session-loader/Development/IDE/Session.hs @@ -65,6 +65,7 @@ import Development.IDE.Types.Exports import Development.IDE.Types.HscEnvEq (HscEnvEq, newHscEnvEq) import Development.IDE.Types.Location import Development.IDE.Types.Options +import Development.IDE.Types.Path import GHC.ResponseFile import qualified HIE.Bios as HieBios import HIE.Bios.Environment hiding (getCacheDir) @@ -135,7 +136,7 @@ data Log | LogHieDbWriterThreadSQLiteError !SQLError | LogHieDbWriterThreadException !SomeException | LogInterfaceFilesCacheDir !FilePath - | LogKnownFilesUpdated !(HashMap Target (HashSet NormalizedFilePath)) + | LogKnownFilesUpdated !(HashMap Target (HashSet (Path Abs NormalizedFilePath))) | LogMakingNewHscEnv ![UnitId] | LogDLLLoadError !String | LogCradlePath !FilePath @@ -196,7 +197,7 @@ instance Pretty Log where nest 2 $ vcat [ "Known files updated:" - , viaShow $ (HM.map . Set.map) fromNormalizedFilePath targetToPathsMap + , viaShow $ (HM.map . Set.map) fromAbsPath targetToPathsMap ] LogMakingNewHscEnv inPlaceUnitIds -> "Making new HscEnv. In-place unit ids:" <+> pretty (map show inPlaceUnitIds) @@ -481,10 +482,10 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do -- If we don't generate a TargetFile for each potential location, we will only have -- 'TargetFile Foo.hs' in the 'knownTargetsVar', thus not find 'TargetFile Foo.hs-boot' -- and also not find 'TargetModule Foo'. - fs <- filterM (IO.doesFileExist . fromNormalizedFilePath) targetLocations + fs <- filterM (IO.doesFileExist . fromAbsPath) targetLocations pure $ map (\fp -> (TargetFile fp, Set.singleton fp)) (nubOrd (f:fs)) TargetModule _ -> do - found <- filterM (IO.doesFileExist . fromNormalizedFilePath) targetLocations + found <- filterM (IO.doesFileExist . fromAbsPath) targetLocations return [(targetTarget, Set.fromList found)] hasUpdate <- atomically $ do known <- readTVar knownTargetsVar @@ -497,7 +498,7 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do return $ toNoFileKey GetKnownTargets -- Create a new HscEnv from a hieYaml root and a set of options - let packageSetup :: (Maybe FilePath, NormalizedFilePath, ComponentOptions, FilePath) + let packageSetup :: (Maybe FilePath, Path Abs NormalizedFilePath, ComponentOptions, FilePath) -> IO ([ComponentInfo], [ComponentInfo]) packageSetup (hieYaml, cfp, opts, libDir) = do -- Parse DynFlags for the newly discovered component @@ -549,7 +550,7 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do pure (Map.insert hieYaml (NE.toList all_deps) m, (new,old)) - let session :: (Maybe FilePath, NormalizedFilePath, ComponentOptions, FilePath) + let session :: (Maybe FilePath, Path Abs NormalizedFilePath, ComponentOptions, FilePath) -> IO (IdeResult HscEnvEq,[FilePath]) session args@(hieYaml, _cfp, _opts, _libDir) = do (new_deps, old_deps) <- packageSetup args @@ -590,7 +591,7 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do -- Typecheck all files in the project on startup checkProject <- getCheckProject unless (null new_deps || not checkProject) $ do - cfps' <- liftIO $ filterM (IO.doesFileExist . fromNormalizedFilePath) (concatMap targetLocations all_targets) + cfps' <- liftIO $ filterM (IO.doesFileExist . fromAbsPath) (concatMap targetLocations all_targets) void $ shakeEnqueue extras $ mkDelayedAction "InitialLoad" Debug $ void $ do mmt <- uses GetModificationTime cfps' let cs_exist = catMaybes (zipWith (<$) cfps' mmt) @@ -634,12 +635,12 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do ((runTime, _):_) | compileTime == runTime -> do atomicModifyIORef' cradle_files (\xs -> (cfp:xs,())) - session (hieYaml, toNormalizedFilePath' cfp, opts, libDir) + session (hieYaml, mkAbsPath $ toNormalizedFilePath' cfp, opts, libDir) | otherwise -> return (([renderPackageSetupException cfp GhcVersionMismatch{..}], Nothing),[]) -- Failure case, either a cradle error or the none cradle Left err -> do dep_info <- getDependencyInfo (maybeToList hieYaml) - let ncfp = toNormalizedFilePath' cfp + let ncfp = mkAbsFromFp cfp let res = (map (\err' -> renderCradleError err' cradle ncfp) err, Nothing) void $ modifyVar' fileToFlags $ Map.insertWith HM.union hieYaml (HM.singleton ncfp (res, dep_info)) @@ -680,7 +681,7 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do v <- Map.findWithDefault HM.empty hieYaml <$> readVar fileToFlags let cfp = toAbsolutePath file - case HM.lookup (toNormalizedFilePath' cfp) v of + case HM.lookup (mkAbsFromFp cfp) v of Just (opts, old_di) -> do deps_ok <- checkDependencyInfo old_di if not deps_ok @@ -701,7 +702,7 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do -- before attempting to do so. let getOptions :: FilePath -> IO (IdeResult HscEnvEq, [FilePath]) getOptions file = do - let ncfp = toNormalizedFilePath' (toAbsolutePath file) + let ncfp = mkAbsFromFp file cachedHieYamlLocation <- HM.lookup ncfp <$> readVar filesMap hieYaml <- cradleLoc file sessionOpts (join cachedHieYamlLocation <|> hieYaml, file) `Safe.catch` \e -> @@ -758,7 +759,7 @@ data TargetDetails = TargetDetails targetTarget :: !Target, targetEnv :: !(IdeResult HscEnvEq), targetDepends :: !DependencyInfo, - targetLocations :: ![NormalizedFilePath] + targetLocations :: ![(Path Abs NormalizedFilePath)] } fromTargetId :: [FilePath] -- ^ import paths @@ -774,17 +775,17 @@ fromTargetId is exts (GHC.TargetModule modName) env dep = do , i <- is , boot <- ["", "-boot"] ] - let locs = fmap toNormalizedFilePath' fps + let locs = fmap mkAbsFromFp fps return [TargetDetails (TargetModule modName) env dep locs] -- For a 'TargetFile' we consider all the possible module names fromTargetId _ _ (GHC.TargetFile f _) env deps = do - let nf = toNormalizedFilePath' f + let nf = mkAbsFromFp f let other - | "-boot" `isSuffixOf` f = toNormalizedFilePath' (L.dropEnd 5 $ fromNormalizedFilePath nf) - | otherwise = toNormalizedFilePath' (fromNormalizedFilePath nf ++ "-boot") + | "-boot" `isSuffixOf` f = removeSuffix nf 5 + | otherwise = addSuffix nf "-boot" return [TargetDetails (TargetFile nf) env deps [nf, other]] -toFlagsMap :: TargetDetails -> [(NormalizedFilePath, (IdeResult HscEnvEq, DependencyInfo))] +toFlagsMap :: TargetDetails -> [(Path Abs NormalizedFilePath, (IdeResult HscEnvEq, DependencyInfo))] toFlagsMap TargetDetails{..} = [ (l, (targetEnv, targetDepends)) | l <- targetLocations] @@ -856,7 +857,7 @@ checkHomeUnitsClosed' ue home_id_set newComponentCache :: Recorder (WithPriority Log) -> [String] -- ^ File extensions to consider - -> NormalizedFilePath -- ^ Path to file that caused the creation of this component + -> (Path Abs NormalizedFilePath) -- ^ Path to file that caused the creation of this component -> HscEnv -- ^ An empty HscEnv -> [ComponentInfo] -- ^ New components to be loaded -> [ComponentInfo] -- ^ old, already existing components @@ -984,10 +985,10 @@ setCacheDirs recorder CacheDirs{..} dflags = do type DependencyInfo = Map.Map FilePath (Maybe UTCTime) type HieMap = Map.Map (Maybe FilePath) [RawComponentInfo] -- | Maps a "hie.yaml" location to all its Target Filepaths and options. -type FlagsMap = Map.Map (Maybe FilePath) (HM.HashMap NormalizedFilePath (IdeResult HscEnvEq, DependencyInfo)) +type FlagsMap = Map.Map (Maybe FilePath) (HM.HashMap (Path Abs NormalizedFilePath) (IdeResult HscEnvEq, DependencyInfo)) -- | Maps a Filepath to its respective "hie.yaml" location. -- It aims to be the reverse of 'FlagsMap'. -type FilesMap = HM.HashMap NormalizedFilePath (Maybe FilePath) +type FilesMap = HM.HashMap (Path Abs NormalizedFilePath) (Maybe FilePath) -- This is pristine information about a component data RawComponentInfo = RawComponentInfo @@ -998,7 +999,7 @@ data RawComponentInfo = RawComponentInfo -- | All targets of this components. , rawComponentTargets :: [GHC.Target] -- | Filepath which caused the creation of this component - , rawComponentFP :: NormalizedFilePath + , rawComponentFP :: Path Abs NormalizedFilePath -- | Component Options used to load the component. , rawComponentCOptions :: ComponentOptions -- | Maps cradle dependencies, such as `stack.yaml`, or `.cabal` file @@ -1015,7 +1016,7 @@ data ComponentInfo = ComponentInfo -- | All targets of this components. , componentTargets :: [GHC.Target] -- | Filepath which caused the creation of this component - , componentFP :: NormalizedFilePath + , componentFP :: Path Abs NormalizedFilePath -- | Component Options used to load the component. , componentCOptions :: ComponentOptions -- | Maps cradle dependencies, such as `stack.yaml`, or `.cabal` file @@ -1094,7 +1095,7 @@ addUnit unit_str = liftEwM $ do -- | Throws if package flags are unsatisfiable setOptions :: GhcMonad m - => NormalizedFilePath + => Path Abs NormalizedFilePath -> ComponentOptions -> DynFlags -> FilePath -- ^ root dir, see Note [Root Directory] @@ -1122,7 +1123,7 @@ setOptions cfp (ComponentOptions theOpts compRoot _) dflags rootDir = do -- -- If we don't end up with a target for the current file in the end, then -- we will report it as an error for that file - let abs_fp = toAbsolute rootDir (fromNormalizedFilePath cfp) + let abs_fp = toAbsolute rootDir (fromAbsPath cfp) let special_target = Compat.mkSimpleTarget df abs_fp pure $ (df, special_target : targets) :| [] where @@ -1223,6 +1224,6 @@ showPackageSetupException PackageSetupException{..} = unwords , "failed to load packages:", message <> "." , "\nPlease ensure that ghcide is compiled with the same GHC installation as the project."] -renderPackageSetupException :: FilePath -> PackageSetupException -> (NormalizedFilePath, ShowDiagnostic, Diagnostic) +renderPackageSetupException :: FilePath -> PackageSetupException -> (Path Abs NormalizedFilePath, ShowDiagnostic, Diagnostic) renderPackageSetupException fp e = - ideErrorWithSource (Just "cradle") (Just DiagnosticSeverity_Error) (toNormalizedFilePath' fp) (T.pack $ showPackageSetupException e) + ideErrorWithSource (Just "cradle") (Just DiagnosticSeverity_Error) (mkAbsPath $ toNormalizedFilePath' fp) (T.pack $ showPackageSetupException e) diff --git a/ghcide/session-loader/Development/IDE/Session/Diagnostics.hs b/ghcide/session-loader/Development/IDE/Session/Diagnostics.hs index a8e35e5965..951f041505 100644 --- a/ghcide/session-loader/Development/IDE/Session/Diagnostics.hs +++ b/ghcide/session-loader/Development/IDE/Session/Diagnostics.hs @@ -10,6 +10,7 @@ import Data.Maybe import qualified Data.Text as T import Development.IDE.Types.Diagnostics import Development.IDE.Types.Location +import Development.IDE.Types.Path import GHC.Generics import qualified HIE.Bios.Cradle as HieBios import HIE.Bios.Types hiding (Log) @@ -26,7 +27,7 @@ data CradleErrorDetails = the cradle error occurred (of the file we attempted to load). Depicts the cradle error in a user-friendly way. -} -renderCradleError :: CradleError -> Cradle a -> NormalizedFilePath -> FileDiagnostic +renderCradleError :: CradleError -> Cradle a -> Path Abs NormalizedFilePath -> FileDiagnostic renderCradleError (CradleError deps _ec ms) cradle nfp | HieBios.isCabalCradle cradle = let (fp, showDiag, diag) = ideErrorWithSource (Just "cradle") (Just DiagnosticSeverity_Error) nfp $ T.unlines $ map T.pack userFriendlyMessage in @@ -42,7 +43,7 @@ renderCradleError (CradleError deps _ec ms) cradle nfp mkUnknownModuleMessage :: Maybe [String] mkUnknownModuleMessage | any (isInfixOf "Failed extracting script block:") ms = - Just $ unknownModuleMessage (fromNormalizedFilePath nfp) + Just $ unknownModuleMessage (fromAbsPath nfp) | otherwise = Nothing fileMissingMessage :: Maybe [String] diff --git a/ghcide/src/Development/IDE/Core/Actions.hs b/ghcide/src/Development/IDE/Core/Actions.hs index 4c808f21d9..5b289ab526 100644 --- a/ghcide/src/Development/IDE/Core/Actions.hs +++ b/ghcide/src/Development/IDE/Core/Actions.hs @@ -27,13 +27,13 @@ import Development.IDE.Graph import qualified Development.IDE.Spans.AtPoint as AtPoint import Development.IDE.Types.HscEnvEq (hscEnv) import Development.IDE.Types.Location +import Development.IDE.Types.Path import qualified HieDb import Language.LSP.Protocol.Types (DocumentHighlight (..), SymbolInformation (..), normalizedFilePathToUri, uriToNormalizedFilePath) - -- | Eventually this will lookup/generate URIs for files in dependencies, but not in the -- project. Right now, this is just a stub. lookupMod @@ -54,7 +54,7 @@ lookupMod _dbchan _hie_f _mod _uid _boot = MaybeT $ pure Nothing -- block waiting for the rule to be properly computed. -- | Try to get hover text for the name under point. -getAtPoint :: NormalizedFilePath -> Position -> IdeAction (Maybe (Maybe Range, [T.Text])) +getAtPoint :: Path Abs NormalizedFilePath -> Position -> IdeAction (Maybe (Maybe Range, [T.Text])) getAtPoint file pos = runMaybeT $ do ide <- ask opts <- liftIO $ getIdeOptionsIO ide @@ -71,7 +71,7 @@ getAtPoint file pos = runMaybeT $ do -- and then apply the position mapping to the location. toCurrentLocations :: PositionMapping - -> NormalizedFilePath + -> Path Abs NormalizedFilePath -> [Location] -> IdeAction [Location] toCurrentLocations mapping file = mapMaybeM go @@ -82,7 +82,7 @@ toCurrentLocations mapping file = mapMaybeM go -- file than the one we are calling gotoDefinition from. -- So we check that the location file matches the file -- we are in. - if nUri == normalizedFilePathToUri file + if nUri == absToUri file -- The Location matches the file, so use the PositionMapping -- we have. then pure $ Location uri <$> toCurrentRange mapping range @@ -91,14 +91,14 @@ toCurrentLocations mapping file = mapMaybeM go else do otherLocationMapping <- fmap (fmap snd) $ runMaybeT $ do otherLocationFile <- MaybeT $ pure $ uriToNormalizedFilePath nUri - useWithStaleFastMT GetHieAst otherLocationFile + useWithStaleFastMT GetHieAst (mkAbsPath otherLocationFile) pure $ Location uri <$> (flip toCurrentRange range =<< otherLocationMapping) where nUri :: NormalizedUri nUri = toNormalizedUri uri -- | Goto Definition. -getDefinition :: NormalizedFilePath -> Position -> IdeAction (Maybe [Location]) +getDefinition :: Path Abs NormalizedFilePath -> Position -> IdeAction (Maybe [Location]) getDefinition file pos = runMaybeT $ do ide@ShakeExtras{ withHieDb, hiedbWriter } <- ask opts <- liftIO $ getIdeOptionsIO ide @@ -108,7 +108,7 @@ getDefinition file pos = runMaybeT $ do locations <- AtPoint.gotoDefinition withHieDb (lookupMod hiedbWriter) opts imports hf pos' MaybeT $ Just <$> toCurrentLocations mapping file locations -getTypeDefinition :: NormalizedFilePath -> Position -> IdeAction (Maybe [Location]) +getTypeDefinition :: Path Abs NormalizedFilePath -> Position -> IdeAction (Maybe [Location]) getTypeDefinition file pos = runMaybeT $ do ide@ShakeExtras{ withHieDb, hiedbWriter } <- ask opts <- liftIO $ getIdeOptionsIO ide @@ -117,7 +117,7 @@ getTypeDefinition file pos = runMaybeT $ do locations <- AtPoint.gotoTypeDefinition withHieDb (lookupMod hiedbWriter) opts hf pos' MaybeT $ Just <$> toCurrentLocations mapping file locations -highlightAtPoint :: NormalizedFilePath -> Position -> IdeAction (Maybe [DocumentHighlight]) +highlightAtPoint :: Path Abs NormalizedFilePath -> Position -> IdeAction (Maybe [DocumentHighlight]) highlightAtPoint file pos = runMaybeT $ do (HAR _ hf rf _ _,mapping) <- useWithStaleFastMT GetHieAst file !pos' <- MaybeT (return $ fromCurrentPosition mapping pos) @@ -125,7 +125,7 @@ highlightAtPoint file pos = runMaybeT $ do mapMaybe toCurrentHighlight <$>AtPoint.documentHighlight hf rf pos' -- Refs are not an IDE action, so it is OK to be slow and (more) accurate -refsAtPoint :: NormalizedFilePath -> Position -> Action [Location] +refsAtPoint :: Path Abs NormalizedFilePath -> Position -> Action [Location] refsAtPoint file pos = do ShakeExtras{withHieDb} <- getShakeExtras fs <- HM.keys <$> getFilesOfInterestUntracked diff --git a/ghcide/src/Development/IDE/Core/Compile.hs b/ghcide/src/Development/IDE/Core/Compile.hs index cb960dd2c9..99cc2bf689 100644 --- a/ghcide/src/Development/IDE/Core/Compile.hs +++ b/ghcide/src/Development/IDE/Core/Compile.hs @@ -75,8 +75,7 @@ import Development.IDE.Core.ProgressReporting (progressUpdate) import Development.IDE.Core.RuleTypes import Development.IDE.Core.Shake import Development.IDE.Core.Tracing (withTrace) -import Development.IDE.GHC.Compat hiding (assert, - loadInterface, +import Development.IDE.GHC.Compat hiding (loadInterface, parseHeader, parseModule, tcRnModule, @@ -92,9 +91,11 @@ import Development.IDE.GHC.Warnings import Development.IDE.Types.Diagnostics import Development.IDE.Types.Location import Development.IDE.Types.Options +import Development.IDE.Types.Path import GHC (ForeignHValue, GetDocsFailure (..), - parsedSource, ModLocation (..)) + ModLocation (..), + parsedSource) import qualified GHC.LanguageExtensions as LangExt import GHC.Serialized import HieDb hiding (withHieDb) @@ -109,7 +110,7 @@ import System.IO.Extra (fixIO, import qualified Data.Set as Set import qualified GHC as G -import qualified GHC.Runtime.Loader as Loader +import qualified GHC.Runtime.Loader as Loader import GHC.Tc.Gen.Splice import GHC.Types.ForeignStubs import GHC.Types.HpcInfo @@ -157,13 +158,13 @@ computePackageDeps -> IO (Either [FileDiagnostic] [UnitId]) computePackageDeps env pkg = do case lookupUnit env pkg of - Nothing -> return $ Left [ideErrorText (toNormalizedFilePath' noFilePath) $ + Nothing -> return $ Left [ideErrorText (mkAbsFromFp noFilePath) $ T.pack $ "unknown package: " ++ show pkg] Just pkgInfo -> return $ Right $ unitDepends pkgInfo newtype TypecheckHelpers = TypecheckHelpers - { getLinkables :: [NormalizedFilePath] -> IO [LinkableResult] -- ^ hls-graph action to get linkables for files + { getLinkables :: [Path Abs NormalizedFilePath] -> IO [LinkableResult] -- ^ hls-graph action to get linkables for files } typecheckModule :: IdeDefer @@ -284,7 +285,7 @@ captureSplicesAndDeps TypecheckHelpers{..} env k = do mapMaybe nodeKeyToInstalledModule $ Set.toList mods_transitive ; moduleLocs <- readIORef (fcModuleCache $ hsc_FC hsc_env) - ; lbs <- getLinkables [toNormalizedFilePath' file + ; lbs <- getLinkables [mkAbsFromFp file | installedMod <- mods_transitive_list , let ifr = fromJust $ lookupInstalledModuleEnv moduleLocs installedMod file = case ifr of @@ -727,7 +728,7 @@ atomicFileWrite se targetPath write = do let dir = takeDirectory targetPath createDirectoryIfMissing True dir (tempFilePath, cleanUp) <- newTempFileWithin dir - (write tempFilePath >>= \x -> renameFile tempFilePath targetPath >> atomically (resetInterfaceStore se (toNormalizedFilePath' targetPath)) >> pure x) + (write tempFilePath >>= \x -> renameFile tempFilePath targetPath >> atomically (resetInterfaceStore se (mkAbsFromFp targetPath)) >> pure x) `onException` cleanUp generateHieAsts :: HscEnv -> TcModuleResult -> IO ([FileDiagnostic], Maybe (HieASTs Type)) @@ -786,7 +787,7 @@ spliceExpressions Splices{..} = -- TVar to 0 in order to set it up for a fresh indexing session. Otherwise, we -- can just increment the 'indexCompleted' TVar and exit. -- -indexHieFile :: ShakeExtras -> ModSummary -> NormalizedFilePath -> Util.Fingerprint -> Compat.HieFile -> IO () +indexHieFile :: ShakeExtras -> ModSummary -> Path Abs NormalizedFilePath -> Util.Fingerprint -> Compat.HieFile -> IO () indexHieFile se mod_summary srcPath !hash hf = do atomically $ do pending <- readTVar indexPending @@ -809,7 +810,7 @@ indexHieFile se mod_summary srcPath !hash hf = do -- Using bracket, so even if an exception happen during withHieDb call, -- the `post` (which clean the progress indicator) will still be called. bracket_ pre post $ - withHieDb (\db -> HieDb.addRefsFromLoaded db targetPath (HieDb.RealFile $ fromNormalizedFilePath srcPath) hash hf') + withHieDb (\db -> HieDb.addRefsFromLoaded db targetPath (HieDb.RealFile $ fromAbsPath srcPath) hash hf') where mod_location = ms_location mod_summary targetPath = Compat.ml_hie_file mod_location @@ -829,10 +830,10 @@ indexHieFile se mod_summary srcPath !hash hf = do whenJust (lspEnv se) $ \env -> LSP.runLspT env $ when (coerce $ ideTesting se) $ LSP.sendNotification (LSP.SMethod_CustomMethod (Proxy @"ghcide/reference/ready")) $ - toJSON $ fromNormalizedFilePath srcPath + toJSON $ fromAbsPath srcPath whenJust mdone $ \_ -> progressUpdate indexProgressReporting ProgressCompleted -writeAndIndexHieFile :: HscEnv -> ShakeExtras -> ModSummary -> NormalizedFilePath -> [GHC.AvailInfo] -> HieASTs Type -> BS.ByteString -> IO [FileDiagnostic] +writeAndIndexHieFile :: HscEnv -> ShakeExtras -> ModSummary -> Path Abs NormalizedFilePath -> [GHC.AvailInfo] -> HieASTs Type -> BS.ByteString -> IO [FileDiagnostic] writeAndIndexHieFile hscEnv se mod_summary srcPath exports ast source = handleGenerationErrors dflags "extended interface write/compression" $ do hf <- runHsc hscEnv $ @@ -1211,8 +1212,8 @@ data RecompilationInfo m = RecompilationInfo { source_version :: FileVersion , old_value :: Maybe (HiFileResult, FileVersion) - , get_file_version :: NormalizedFilePath -> m (Maybe FileVersion) - , get_linkable_hashes :: [NormalizedFilePath] -> m [BS.ByteString] + , get_file_version :: Path Abs NormalizedFilePath -> m (Maybe FileVersion) + , get_linkable_hashes :: [Path Abs NormalizedFilePath] -> m [BS.ByteString] , regenerate :: Maybe LinkableType -> m ([FileDiagnostic], Maybe HiFileResult) -- ^ Action to regenerate an interface } @@ -1250,7 +1251,7 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do mb_dest_version <- case mb_old_version of Just ver -> pure $ Just ver - Nothing -> get_file_version (toNormalizedFilePath' iface_file) + Nothing -> get_file_version (mkAbsFromFp iface_file) -- The source is modified if it is newer than the destination (iface file) -- A more precise check for the core file is performed later @@ -1332,7 +1333,7 @@ parseRuntimeDeps anns = mkModuleEnv $ mapMaybe go anns -- the runtime dependencies of the module, to check if any of them are out of date -- Hopefully 'runtime_deps' will be empty if the module didn't actually use TH -- See Note [Recompilation avoidance in the presence of TH] -checkLinkableDependencies :: MonadIO m => HscEnv -> ([NormalizedFilePath] -> m [BS.ByteString]) -> ModuleEnv BS.ByteString -> m (Maybe RecompileRequired) +checkLinkableDependencies :: MonadIO m => HscEnv -> ([Path Abs NormalizedFilePath] -> m [BS.ByteString]) -> ModuleEnv BS.ByteString -> m (Maybe RecompileRequired) checkLinkableDependencies hsc_env get_linkable_hashes runtime_deps = do moduleLocs <- liftIO $ readIORef (fcModuleCache $ hsc_FC hsc_env) let go (mod, hash) = do @@ -1340,7 +1341,7 @@ checkLinkableDependencies hsc_env get_linkable_hashes runtime_deps = do case ifr of InstalledFound loc _ -> do hs <- ml_hs_file loc - pure (toNormalizedFilePath' hs,hash) + pure (mkAbsFromFp hs,hash) _ -> Nothing hs_files = mapM go (moduleEnvToList runtime_deps) case hs_files of diff --git a/ghcide/src/Development/IDE/Core/FileExists.hs b/ghcide/src/Development/IDE/Core/FileExists.hs index 280cd14028..913a9540ac 100644 --- a/ghcide/src/Development/IDE/Core/FileExists.hs +++ b/ghcide/src/Development/IDE/Core/FileExists.hs @@ -28,6 +28,7 @@ import qualified Development.IDE.Core.Shake as Shake import Development.IDE.Graph import Development.IDE.Types.Location import Development.IDE.Types.Options +import Development.IDE.Types.Path import Development.IDE.Types.Shake (toKey) import qualified Focus import Ide.Logger (Pretty (pretty), @@ -83,7 +84,7 @@ fast path by a check that the path also matches our watching patterns. -- | A map for tracking the file existence. -- If a path maps to 'True' then it exists; if it maps to 'False' then it doesn't exist'; and -- if it's not in the map then we don't know. -type FileExistsMap = STM.Map NormalizedFilePath Bool +type FileExistsMap = STM.Map (Path Abs NormalizedFilePath) Bool -- | A wrapper around a mutable 'FileExistsState' newtype FileExistsMapVar = FileExistsMapVar FileExistsMap @@ -107,7 +108,7 @@ getFileExistsMapUntracked = do return v -- | Modify the global store of file exists and return the keys that need to be marked as dirty -modifyFileExists :: IdeState -> [(NormalizedFilePath, FileChangeType)] -> IO [Key] +modifyFileExists :: IdeState -> [(Path Abs NormalizedFilePath, FileChangeType)] -> IO [Key] modifyFileExists state changes = do FileExistsMapVar var <- getIdeGlobalState state -- Masked to ensure that the previous values are flushed together with the map update @@ -133,7 +134,7 @@ fromChange FileChangeType_Changed = Nothing ------------------------------------------------------------------------------------- -- | Returns True if the file exists -getFileExists :: NormalizedFilePath -> Action Bool +getFileExists :: Path Abs NormalizedFilePath -> Action Bool getFileExists fp = use_ GetFileExists fp {- Note [Which files should we watch?] @@ -185,7 +186,7 @@ fileExistsRules recorder lspEnv = do isWatched = if supportsWatchedFiles then \f -> do isWF <- isWorkspaceFile f - return $ isWF && fpMatches (fromNormalizedFilePath f) + return $ isWF && fpMatches (fromAbsPath f) else const $ pure False if supportsWatchedFiles @@ -195,7 +196,7 @@ fileExistsRules recorder lspEnv = do fileStoreRules (cmapWithPrio LogFileStore recorder) isWatched -- Requires an lsp client that provides WatchedFiles notifications, but assumes that this has already been checked. -fileExistsRulesFast :: Recorder (WithPriority Log) -> (NormalizedFilePath -> Action Bool) -> Rules () +fileExistsRulesFast :: Recorder (WithPriority Log) -> (Path Abs NormalizedFilePath -> Action Bool) -> Rules () fileExistsRulesFast recorder isWatched = defineEarlyCutoff (cmapWithPrio LogShake recorder) $ RuleNoDiagnostics $ \GetFileExists file -> do isWF <- isWatched file @@ -220,7 +221,7 @@ For the VFS lookup, however, we won't get prompted to flush the result, so inste we use 'alwaysRerun'. -} -fileExistsFast :: NormalizedFilePath -> Action (Maybe BS.ByteString, Maybe Bool) +fileExistsFast :: Path Abs NormalizedFilePath -> Action (Maybe BS.ByteString, Maybe Bool) fileExistsFast file = do -- Could in principle use 'alwaysRerun' here, but it's too slwo, See Note [Invalidating file existence results] mp <- getFileExistsMapUntracked @@ -240,17 +241,17 @@ fileExistsRulesSlow :: Recorder (WithPriority Log) -> Rules () fileExistsRulesSlow recorder = defineEarlyCutoff (cmapWithPrio LogShake recorder) $ RuleNoDiagnostics $ \GetFileExists file -> fileExistsSlow file -fileExistsSlow :: NormalizedFilePath -> Action (Maybe BS.ByteString, Maybe Bool) +fileExistsSlow :: Path Abs NormalizedFilePath -> Action (Maybe BS.ByteString, Maybe Bool) fileExistsSlow file = do -- See Note [Invalidating file existence results] alwaysRerun exist <- getFileExistsVFS file pure (summarizeExists exist, Just exist) -getFileExistsVFS :: NormalizedFilePath -> Action Bool +getFileExistsVFS :: Path Abs NormalizedFilePath -> Action Bool getFileExistsVFS file = do vf <- getVirtualFile file if isJust vf then pure True else liftIO $ handle (\(_ :: IOException) -> return False) $ - Dir.doesFileExist (fromNormalizedFilePath file) + Dir.doesFileExist (fromAbsPath file) diff --git a/ghcide/src/Development/IDE/Core/FileStore.hs b/ghcide/src/Development/IDE/Core/FileStore.hs index 6c0cb875b0..cecd5562c5 100644 --- a/ghcide/src/Development/IDE/Core/FileStore.hs +++ b/ghcide/src/Development/IDE/Core/FileStore.hs @@ -46,6 +46,7 @@ import Development.IDE.Import.DependencyInformation import Development.IDE.Types.Diagnostics import Development.IDE.Types.Location import Development.IDE.Types.Options +import Development.IDE.Types.Path import Development.IDE.Types.Shake (toKey) import HieDb.Create (deleteMissingRealFiles) import Ide.Logger (Pretty (pretty), @@ -70,10 +71,9 @@ import System.FilePath import System.IO.Error import System.IO.Unsafe - data Log - = LogCouldNotIdentifyReverseDeps !NormalizedFilePath - | LogTypeCheckingReverseDeps !NormalizedFilePath !(Maybe [NormalizedFilePath]) + = LogCouldNotIdentifyReverseDeps !(Path Abs NormalizedFilePath) + | LogTypeCheckingReverseDeps !(Path Abs NormalizedFilePath) !(Maybe [(Path Abs NormalizedFilePath)]) | LogShake Shake.Log deriving Show @@ -88,7 +88,7 @@ instance Pretty Log where <+> pretty (fmap (fmap show) reverseDepPaths) LogShake msg -> pretty msg -addWatchedFileRule :: Recorder (WithPriority Log) -> (NormalizedFilePath -> Action Bool) -> Rules () +addWatchedFileRule :: Recorder (WithPriority Log) -> (Path Abs NormalizedFilePath -> Action Bool) -> Rules () addWatchedFileRule recorder isWatched = defineNoDiagnostics (cmapWithPrio LogShake recorder) $ \AddWatchedFile f -> do isAlreadyWatched <- isWatched f isWp <- isWorkspaceFile f @@ -97,7 +97,7 @@ addWatchedFileRule recorder isWatched = defineNoDiagnostics (cmapWithPrio LogSha ShakeExtras{lspEnv} <- getShakeExtras case lspEnv of Just env -> fmap Just $ liftIO $ LSP.runLspT env $ - registerFileWatches [fromNormalizedFilePath f] + registerFileWatches [fromAbsPath f] Nothing -> pure $ Just False @@ -107,10 +107,10 @@ getModificationTimeRule recorder = defineEarlyCutoff (cmapWithPrio LogShake reco getModificationTimeImpl :: Bool - -> NormalizedFilePath + -> (Path Abs NormalizedFilePath) -> Action (Maybe BS.ByteString, ([FileDiagnostic], Maybe FileVersion)) getModificationTimeImpl missingFileDiags file = do - let file' = fromNormalizedFilePath file + let file' = fromAbsPath file let wrap time = (Just $ LBS.toStrict $ B.encode $ toRational time, ([], Just $ ModificationTime time)) mbVf <- getVirtualFile file case mbVf of @@ -142,17 +142,17 @@ getModificationTimeImpl missingFileDiags file = do -- | Interface files cannot be watched, since they live outside the workspace. -- But interface files are private, in that only HLS writes them. -- So we implement watching ourselves, and bypass the need for alwaysRerun. -isInterface :: NormalizedFilePath -> Bool -isInterface f = takeExtension (fromNormalizedFilePath f) `elem` [".hi", ".hi-boot", ".hie", ".hie-boot", ".core"] +isInterface :: (Path Abs NormalizedFilePath) -> Bool +isInterface f = takeExtension (fromAbsPath f) `elem` [".hi", ".hi-boot", ".hie", ".hie-boot", ".core"] -- | Reset the GetModificationTime state of interface files -resetInterfaceStore :: ShakeExtras -> NormalizedFilePath -> STM [Key] +resetInterfaceStore :: ShakeExtras -> (Path Abs NormalizedFilePath) -> STM [Key] resetInterfaceStore state f = do deleteValue state GetModificationTime f -- | Reset the GetModificationTime state of watched files -- Assumes the list does not include any FOIs -resetFileStore :: IdeState -> [(NormalizedFilePath, LSP.FileChangeType)] -> IO [Key] +resetFileStore :: IdeState -> [(Path Abs NormalizedFilePath, LSP.FileChangeType)] -> IO [Key] resetFileStore ideState changes = mask $ \_ -> do -- we record FOIs document versions in all the stored values -- so NEVER reset FOIs to avoid losing their versions @@ -174,7 +174,7 @@ getFileContentsRule :: Recorder (WithPriority Log) -> Rules () getFileContentsRule recorder = define (cmapWithPrio LogShake recorder) $ \GetFileContents file -> getFileContentsImpl file getFileContentsImpl - :: NormalizedFilePath + :: (Path Abs NormalizedFilePath) -> Action ([FileDiagnostic], Maybe (FileVersion, Maybe T.Text)) getFileContentsImpl file = do -- need to depend on modification time to introduce a dependency with Cutoff @@ -186,7 +186,7 @@ getFileContentsImpl file = do -- | Returns the modification time and the contents. -- For VFS paths, the modification time is the current time. -getFileContents :: NormalizedFilePath -> Action (UTCTime, Maybe T.Text) +getFileContents :: (Path Abs NormalizedFilePath) -> Action (UTCTime, Maybe T.Text) getFileContents f = do (fv, txt) <- use_ GetFileContents f modTime <- case modificationTime fv of @@ -196,11 +196,11 @@ getFileContents f = do liftIO $ case foi of IsFOI Modified{} -> getCurrentTime _ -> do - posix <- getModTime $ fromNormalizedFilePath f + posix <- getModTime $ fromAbsPath f pure $ posixSecondsToUTCTime posix return (modTime, txt) -fileStoreRules :: Recorder (WithPriority Log) -> (NormalizedFilePath -> Action Bool) -> Rules () +fileStoreRules :: Recorder (WithPriority Log) -> ((Path Abs NormalizedFilePath) -> Action Bool) -> Rules () fileStoreRules recorder isWatched = do getModificationTimeRule recorder getFileContentsRule recorder @@ -212,7 +212,7 @@ setFileModified :: Recorder (WithPriority Log) -> VFSModified -> IdeState -> Bool -- ^ Was the file saved? - -> NormalizedFilePath + -> (Path Abs NormalizedFilePath) -> IO [Key] -> IO () setFileModified recorder vfs state saved nfp actionBefore = do @@ -222,17 +222,17 @@ setFileModified recorder vfs state saved nfp actionBefore = do AlwaysCheck -> True CheckOnSave -> saved _ -> False - restartShakeSession (shakeExtras state) vfs (fromNormalizedFilePath nfp ++ " (modified)") [] $ do + restartShakeSession (shakeExtras state) vfs (fromAbsPath nfp ++ " (modified)") [] $ do keys<-actionBefore return (toKey GetModificationTime nfp:keys) when checkParents $ typecheckParents recorder state nfp -typecheckParents :: Recorder (WithPriority Log) -> IdeState -> NormalizedFilePath -> IO () +typecheckParents :: Recorder (WithPriority Log) -> IdeState -> Path Abs NormalizedFilePath -> IO () typecheckParents recorder state nfp = void $ shakeEnqueue (shakeExtras state) parents where parents = mkDelayedAction "ParentTC" L.Debug (typecheckParentsAction recorder nfp) -typecheckParentsAction :: Recorder (WithPriority Log) -> NormalizedFilePath -> Action () +typecheckParentsAction :: Recorder (WithPriority Log) -> Path Abs NormalizedFilePath -> Action () typecheckParentsAction recorder nfp = do revs <- transitiveReverseDependencies nfp <$> useNoFile_ GetModuleGraph case revs of diff --git a/ghcide/src/Development/IDE/Core/IdeConfiguration.hs b/ghcide/src/Development/IDE/Core/IdeConfiguration.hs index eb42450bde..04cb0acf64 100644 --- a/ghcide/src/Development/IDE/Core/IdeConfiguration.hs +++ b/ghcide/src/Development/IDE/Core/IdeConfiguration.hs @@ -22,6 +22,7 @@ import Data.Text (isPrefixOf) import Development.IDE.Core.Shake import Development.IDE.Graph import Development.IDE.Types.Location +import Development.IDE.Types.Path import Language.LSP.Protocol.Types import System.FilePath (isRelative) @@ -76,16 +77,16 @@ modifyIdeConfiguration ide f = do IdeConfigurationVar var <- getIdeGlobalState ide void $ modifyVar' var f -isWorkspaceFile :: NormalizedFilePath -> Action Bool +isWorkspaceFile :: Path Abs NormalizedFilePath -> Action Bool isWorkspaceFile file = - if isRelative (fromNormalizedFilePath file) + if isRelative (fromAbsPath file) then return True else do IdeConfiguration {..} <- getIdeConfiguration let toText = getUri . fromNormalizedUri return $ any - (\root -> toText root `isPrefixOf` toText (filePathToUri' file)) + (\root -> toText root `isPrefixOf` toText (absToUri file)) workspaceFolders getClientSettings :: Action (Maybe Value) diff --git a/ghcide/src/Development/IDE/Core/OfInterest.hs b/ghcide/src/Development/IDE/Core/OfInterest.hs index 19e0f40e24..6660442818 100644 --- a/ghcide/src/Development/IDE/Core/OfInterest.hs +++ b/ghcide/src/Development/IDE/Core/OfInterest.hs @@ -39,6 +39,7 @@ import Development.IDE.Plugin.Completions.Types import Development.IDE.Types.Exports import Development.IDE.Types.Location import Development.IDE.Types.Options (IdeTesting (..)) +import Development.IDE.Types.Path import Development.IDE.Types.Shake (toKey) import GHC.TypeLits (KnownSymbol) import Ide.Logger (Pretty (pretty), @@ -57,7 +58,7 @@ instance Pretty Log where pretty = \case LogShake msg -> pretty msg -newtype OfInterestVar = OfInterestVar (Var (HashMap NormalizedFilePath FileOfInterestStatus)) +newtype OfInterestVar = OfInterestVar (Var (HashMap (Path Abs NormalizedFilePath) FileOfInterestStatus)) instance IsIdeGlobal OfInterestVar @@ -86,24 +87,24 @@ instance IsIdeGlobal GarbageCollectVar ------------------------------------------------------------ -- Exposed API -getFilesOfInterest :: IdeState -> IO( HashMap NormalizedFilePath FileOfInterestStatus) +getFilesOfInterest :: IdeState -> IO( HashMap (Path Abs NormalizedFilePath) FileOfInterestStatus) getFilesOfInterest state = do OfInterestVar var <- getIdeGlobalState state readVar var -- | Set the files-of-interest - not usually necessary or advisable. -- The LSP client will keep this information up to date. -setFilesOfInterest :: IdeState -> HashMap NormalizedFilePath FileOfInterestStatus -> IO () +setFilesOfInterest :: IdeState -> HashMap (Path Abs NormalizedFilePath) FileOfInterestStatus -> IO () setFilesOfInterest state files = do OfInterestVar var <- getIdeGlobalState state writeVar var files -getFilesOfInterestUntracked :: Action (HashMap NormalizedFilePath FileOfInterestStatus) +getFilesOfInterestUntracked :: Action (HashMap (Path Abs NormalizedFilePath) FileOfInterestStatus) getFilesOfInterestUntracked = do OfInterestVar var <- getIdeGlobalAction liftIO $ readVar var -addFileOfInterest :: IdeState -> NormalizedFilePath -> FileOfInterestStatus -> IO [Key] +addFileOfInterest :: IdeState -> Path Abs NormalizedFilePath -> FileOfInterestStatus -> IO [Key] addFileOfInterest state f v = do OfInterestVar var <- getIdeGlobalState state (prev, files) <- modifyVar var $ \dict -> do @@ -116,7 +117,7 @@ addFileOfInterest state f v = do return [toKey IsFileOfInterest f] else return [] -deleteFileOfInterest :: IdeState -> NormalizedFilePath -> IO [Key] +deleteFileOfInterest :: IdeState -> Path Abs NormalizedFilePath -> IO [Key] deleteFileOfInterest state f = do OfInterestVar var <- getIdeGlobalState state files <- modifyVar' var $ HashMap.delete f @@ -138,7 +139,7 @@ kick = do signal msg = when testing $ liftIO $ mRunLspT lspEnv $ LSP.sendNotification (LSP.SMethod_CustomMethod msg) $ - toJSON $ map fromNormalizedFilePath files + toJSON $ map fromAbsPath files signal (Proxy @"kick/start") liftIO $ progressUpdate progress ProgressNewStarted diff --git a/ghcide/src/Development/IDE/Core/PluginUtils.hs b/ghcide/src/Development/IDE/Core/PluginUtils.hs index 76c88421c9..e3428f8253 100644 --- a/ghcide/src/Development/IDE/Core/PluginUtils.hs +++ b/ghcide/src/Development/IDE/Core/PluginUtils.hs @@ -42,6 +42,7 @@ import Development.IDE.GHC.Orphans () import Development.IDE.Graph hiding (ShakeValue) import Development.IDE.Types.Location (NormalizedFilePath) import qualified Development.IDE.Types.Location as Location +import Development.IDE.Types.Path import qualified Ide.Logger as Logger import Ide.Plugin.Error import qualified Language.LSP.Protocol.Types as LSP @@ -63,30 +64,30 @@ runActionMT herald ide act = join $ shakeEnqueue (shakeExtras ide) (mkDelayedAction herald Logger.Debug $ runMaybeT act) -- |ExceptT version of `use` that throws a PluginRuleFailed upon failure -useE :: IdeRule k v => k -> NormalizedFilePath -> ExceptT PluginError Action v +useE :: IdeRule k v => k -> Path Abs NormalizedFilePath -> ExceptT PluginError Action v useE k = maybeToExceptT (PluginRuleFailed (T.pack $ show k)) . useMT k -- |MaybeT version of `use` -useMT :: IdeRule k v => k -> NormalizedFilePath -> MaybeT Action v +useMT :: IdeRule k v => k -> Path Abs NormalizedFilePath -> MaybeT Action v useMT k = MaybeT . Shake.use k -- |ExceptT version of `uses` that throws a PluginRuleFailed upon failure -usesE :: (Traversable f, IdeRule k v) => k -> f NormalizedFilePath -> ExceptT PluginError Action (f v) +usesE :: (Traversable f, IdeRule k v) => k -> f (Path Abs NormalizedFilePath) -> ExceptT PluginError Action (f v) usesE k = maybeToExceptT (PluginRuleFailed (T.pack $ show k)) . usesMT k -- |MaybeT version of `uses` -usesMT :: (Traversable f, IdeRule k v) => k -> f NormalizedFilePath -> MaybeT Action (f v) +usesMT :: (Traversable f, IdeRule k v) => k -> f (Path Abs NormalizedFilePath) -> MaybeT Action (f v) usesMT k xs = MaybeT $ sequence <$> Shake.uses k xs -- |ExceptT version of `useWithStale` that throws a PluginRuleFailed upon -- failure useWithStaleE :: IdeRule k v - => k -> NormalizedFilePath -> ExceptT PluginError Action (v, PositionMapping) + => k -> Path Abs NormalizedFilePath -> ExceptT PluginError Action (v, PositionMapping) useWithStaleE key = maybeToExceptT (PluginRuleFailed (T.pack $ show key)) . useWithStaleMT key -- |MaybeT version of `useWithStale` useWithStaleMT :: IdeRule k v - => k -> NormalizedFilePath -> MaybeT Action (v, PositionMapping) + => k -> Path Abs NormalizedFilePath -> MaybeT Action (v, PositionMapping) useWithStaleMT key file = MaybeT $ runIdentity <$> Shake.usesWithStale key (Identity file) -- ---------------------------------------------------------------------------- @@ -103,11 +104,11 @@ runIdeActionMT _herald s i = MaybeT $ liftIO $ runReaderT (Shake.runIdeActionT $ -- |ExceptT version of `useWithStaleFast` that throws a PluginRuleFailed upon -- failure -useWithStaleFastE :: IdeRule k v => k -> NormalizedFilePath -> ExceptT PluginError IdeAction (v, PositionMapping) +useWithStaleFastE :: IdeRule k v => k -> Path Abs NormalizedFilePath -> ExceptT PluginError IdeAction (v, PositionMapping) useWithStaleFastE k = maybeToExceptT (PluginRuleFailed (T.pack $ show k)) . useWithStaleFastMT k -- |MaybeT version of `useWithStaleFast` -useWithStaleFastMT :: IdeRule k v => k -> NormalizedFilePath -> MaybeT IdeAction (v, PositionMapping) +useWithStaleFastMT :: IdeRule k v => k -> Path Abs NormalizedFilePath -> MaybeT IdeAction (v, PositionMapping) useWithStaleFastMT k = MaybeT . Shake.useWithStaleFast k -- ---------------------------------------------------------------------------- diff --git a/ghcide/src/Development/IDE/Core/Preprocessor.hs b/ghcide/src/Development/IDE/Core/Preprocessor.hs index 46fb03f191..f04fb135e0 100644 --- a/ghcide/src/Development/IDE/Core/Preprocessor.hs +++ b/ghcide/src/Development/IDE/Core/Preprocessor.hs @@ -27,6 +27,7 @@ import qualified Data.Text as T import Development.IDE.GHC.Error import Development.IDE.Types.Diagnostics import Development.IDE.Types.Location +import Development.IDE.Types.Path import qualified GHC.LanguageExtensions as LangExt import qualified GHC.Runtime.Loader as Loader import GHC.Utils.Logger (LogFlags (..)) @@ -104,7 +105,7 @@ data CPPDiag diagsFromCPPLogs :: FilePath -> [CPPLog] -> [FileDiagnostic] diagsFromCPPLogs filename logs = - map (\d -> (toNormalizedFilePath' filename, ShowDiag, cppDiagToDiagnostic d)) $ + map (\d -> (mkAbsPath $ toNormalizedFilePath' filename, ShowDiag, cppDiagToDiagnostic d)) $ go [] logs where -- On errors, CPP calls logAction with a real span for the initial log and diff --git a/ghcide/src/Development/IDE/Core/ProgressReporting.hs b/ghcide/src/Development/IDE/Core/ProgressReporting.hs index 3d8a2bf989..b0148fd4e8 100644 --- a/ghcide/src/Development/IDE/Core/ProgressReporting.hs +++ b/ghcide/src/Development/IDE/Core/ProgressReporting.hs @@ -33,6 +33,7 @@ import qualified Data.Text as T import Development.IDE.GHC.Orphans () import Development.IDE.Types.Location import Development.IDE.Types.Options +import Development.IDE.Types.Path import qualified Focus import Language.LSP.Protocol.Types import Language.LSP.Server (ProgressAmount (..), @@ -56,7 +57,7 @@ data ProgressReporting = ProgressReporting data PerFileProgressReporting = PerFileProgressReporting { - inProgress :: forall a. NormalizedFilePath -> IO a -> IO a, + inProgress :: forall a. Path Abs NormalizedFilePath -> IO a -> IO a, -- ^ see Note [ProgressReporting API and InProgressState] progressReportingInner :: ProgressReporting } @@ -127,13 +128,13 @@ data InProgressState todoVar :: TVar Int, -- | Number of files done doneVar :: TVar Int, - currentVar :: STM.Map NormalizedFilePath Int + currentVar :: STM.Map (Path Abs NormalizedFilePath) Int } newInProgress :: IO InProgressState newInProgress = InProgressState <$> newTVarIO 0 <*> newTVarIO 0 <*> STM.newIO -recordProgress :: InProgressState -> NormalizedFilePath -> (Int -> Int) -> IO () +recordProgress :: InProgressState -> Path Abs NormalizedFilePath -> (Int -> Int) -> IO () recordProgress InProgressState {..} file shift = do (prev, new) <- atomicallyNamed "recordProgress" $ STM.focus alterPrevAndNew file currentVar atomicallyNamed "recordProgress2" $ case (prev, new) of @@ -184,7 +185,7 @@ progressReporting (Just lspEnv) title optProgressStyle = do progressReportingInner <- progressReportingNoTrace (readTVar $ todoVar inProgressState) (readTVar $ doneVar inProgressState) (Just lspEnv) title optProgressStyle let - inProgress :: NormalizedFilePath -> IO a -> IO a + inProgress :: Path Abs NormalizedFilePath -> IO a -> IO a inProgress = updateStateForFile inProgressState return PerFileProgressReporting {..} where diff --git a/ghcide/src/Development/IDE/Core/RuleTypes.hs b/ghcide/src/Development/IDE/Core/RuleTypes.hs index 3d60669f5c..0ceea5004e 100644 --- a/ghcide/src/Development/IDE/Core/RuleTypes.hs +++ b/ghcide/src/Development/IDE/Core/RuleTypes.hs @@ -40,6 +40,7 @@ import Development.IDE.Import.FindImports (ArtifactsLocation import Development.IDE.Spans.Common import Development.IDE.Spans.LocalBindings import Development.IDE.Types.Diagnostics +import Development.IDE.Types.Path import GHC.Serialized (Serialized) import Ide.Logger (Pretty (..), viaShow) @@ -113,7 +114,7 @@ instance NFData GetImportMap type instance RuleResult GetImportMap = ImportMap newtype ImportMap = ImportMap - { importMap :: M.Map ModuleName NormalizedFilePath -- ^ Where are the modules imported by this file located? + { importMap :: M.Map ModuleName (Path Abs NormalizedFilePath) -- ^ Where are the modules imported by this file located? } deriving stock Show deriving newtype NFData diff --git a/ghcide/src/Development/IDE/Core/Rules.hs b/ghcide/src/Development/IDE/Core/Rules.hs index b0d61579cc..61094a8417 100644 --- a/ghcide/src/Development/IDE/Core/Rules.hs +++ b/ghcide/src/Development/IDE/Core/Rules.hs @@ -134,6 +134,7 @@ import Development.IDE.Types.Diagnostics as Diag import Development.IDE.Types.HscEnvEq import Development.IDE.Types.Location import Development.IDE.Types.Options +import Development.IDE.Types.Path import qualified Development.IDE.Types.Shake as Shake import qualified GHC.LanguageExtensions as LangExt import HIE.Bios.Ghc.Gap (hostIsDynamic) @@ -173,20 +174,20 @@ import GHC.Fingerprint data Log = LogShake Shake.Log - | LogReindexingHieFile !NormalizedFilePath - | LogLoadingHieFile !NormalizedFilePath + | LogReindexingHieFile !(Path Abs NormalizedFilePath) + | LogLoadingHieFile !(Path Abs NormalizedFilePath) | LogLoadingHieFileFail !FilePath !SomeException | LogLoadingHieFileSuccess !FilePath - | LogTypecheckedFOI !NormalizedFilePath + | LogTypecheckedFOI !(Path Abs NormalizedFilePath) deriving Show instance Pretty Log where pretty = \case LogShake msg -> pretty msg LogReindexingHieFile path -> - "Re-indexing hie file for" <+> pretty (fromNormalizedFilePath path) + "Re-indexing hie file for" <+> pretty (fromAbsPath path) LogLoadingHieFile path -> - "LOADING HIE FILE FOR" <+> pretty (fromNormalizedFilePath path) + "LOADING HIE FILE FOR" <+> pretty (fromAbsPath path) LogLoadingHieFileFail path e -> nest 2 $ vcat @@ -195,7 +196,7 @@ instance Pretty Log where LogLoadingHieFileSuccess path -> "SUCCEEDED LOADING HIE FILE FOR" <+> pretty path LogTypecheckedFOI path -> vcat - [ "Typechecked a file which is not currently open in the editor:" <+> pretty (fromNormalizedFilePath path) + [ "Typechecked a file which is not currently open in the editor:" <+> pretty (fromAbsPath path) , "This can indicate a bug which results in excessive memory usage." , "This may be a spurious warning if you have recently closed the file." , "If you haven't opened this file recently, please file a report on the issue tracker mentioning" @@ -218,20 +219,20 @@ toIdeResult = either (, Nothing) (([],) . Just) -- TODO: rename -- TODO: return text --> return rope -getSourceFileSource :: NormalizedFilePath -> Action BS.ByteString +getSourceFileSource :: Path Abs NormalizedFilePath -> Action BS.ByteString getSourceFileSource nfp = do (_, msource) <- getFileContents nfp case msource of - Nothing -> liftIO $ BS.readFile (fromNormalizedFilePath nfp) + Nothing -> liftIO $ BS.readFile (fromAbsPath nfp) Just source -> pure $ T.encodeUtf8 source -- | Parse the contents of a haskell file. -getParsedModule :: NormalizedFilePath -> Action (Maybe ParsedModule) +getParsedModule :: Path Abs NormalizedFilePath -> Action (Maybe ParsedModule) getParsedModule = use GetParsedModule -- | Parse the contents of a haskell file, -- ensuring comments are preserved in annotations -getParsedModuleWithComments :: NormalizedFilePath -> Action (Maybe ParsedModule) +getParsedModuleWithComments :: Path Abs NormalizedFilePath -> Action (Maybe ParsedModule) getParsedModuleWithComments = use GetParsedModuleWithComments ------------------------------------------------------------ @@ -298,10 +299,10 @@ getModifyDynFlags f = do getParsedModuleDefinition :: HscEnv -> IdeOptions - -> NormalizedFilePath + -> Path Abs NormalizedFilePath -> ModSummary -> IO ([FileDiagnostic], Maybe ParsedModule) getParsedModuleDefinition packageState opt file ms = do - let fp = fromNormalizedFilePath file + let fp = fromAbsPath file (diag, res) <- parseModule opt packageState fp ms case res of Nothing -> pure (diag, Nothing) @@ -367,7 +368,7 @@ execRawDepM act = -- | Given a target file path, construct the raw dependency results by following -- imports recursively. -rawDependencyInformation :: [NormalizedFilePath] -> Action (RawDependencyInformation, BootIdMap) +rawDependencyInformation :: [Path Abs NormalizedFilePath] -> Action (RawDependencyInformation, BootIdMap) rawDependencyInformation fs = do (rdi, ss) <- execRawDepM (goPlural fs) let bm = IntMap.foldrWithKey (updateBootMap rdi) IntMap.empty ss @@ -377,7 +378,7 @@ rawDependencyInformation fs = do mss <- lift $ (fmap.fmap) msrModSummary <$> uses GetModSummaryWithoutTimestamps ff zipWithM go ff mss - go :: NormalizedFilePath -- ^ Current module being processed + go :: Path Abs NormalizedFilePath -- ^ Current module being processed -> Maybe ModSummary -- ^ ModSummary of the module -> RawDepM FilePathId go f mbModSum = do @@ -421,7 +422,7 @@ rawDependencyInformation fs = do return fId - checkAlreadyProcessed :: NormalizedFilePath -> RawDepM FilePathId -> RawDepM FilePathId + checkAlreadyProcessed :: Path Abs NormalizedFilePath -> RawDepM FilePathId -> RawDepM FilePathId checkAlreadyProcessed nfp k = do (rawDepInfo, _) <- get maybe k return (lookupPathToId (rawPathIdMap rawDepInfo) nfp) @@ -455,15 +456,12 @@ rawDependencyInformation fs = do updateBootMap pm boot_mod_id ArtifactsLocation{..} bm = if not artifactIsSource then - let msource_mod_id = lookupPathToId (rawPathIdMap pm) (toNormalizedFilePath' $ dropBootSuffix $ fromNormalizedFilePath artifactFilePath) + let msource_mod_id = lookupPathToId (rawPathIdMap pm) (removeSuffix artifactFilePath 5) in case msource_mod_id of Just source_mod_id -> insertBootId source_mod_id (FilePathId boot_mod_id) bm Nothing -> bm else bm - dropBootSuffix :: FilePath -> FilePath - dropBootSuffix hs_src = reverse . drop (length @[] "-boot") . reverse $ hs_src - reportImportCyclesRule :: Recorder (WithPriority Log) -> Rules () reportImportCyclesRule recorder = defineEarlyCutoff (cmapWithPrio LogShake recorder) $ Rule $ \ReportImportCycles file -> fmap (\errs -> if null errs then (Just "1",([], Just ())) else (Nothing, (errs, Nothing))) $ do @@ -496,7 +494,7 @@ reportImportCyclesRule recorder = , _data_ = Nothing } where rng = fromMaybe noRange $ srcSpanToRange (getLoc imp) - fp = toNormalizedFilePath' $ fromMaybe noFilePath $ srcSpanToFilename (getLoc imp) + fp = mkAbsFromFp $ fromMaybe noFilePath $ srcSpanToFilename (getLoc imp) getModuleName file = do ms <- msrModSummary <$> use_ GetModSummaryWithoutTimestamps file pure (moduleNameString . moduleName . ms_mod $ ms) @@ -514,14 +512,14 @@ persistentHieFileRule recorder = addPersistentRule GetHieAst $ \file -> runMaybe res <- readHieFileForSrcFromDisk recorder file vfsRef <- asks vfsVar vfsData <- liftIO $ _vfsMap <$> readTVarIO vfsRef - (currentSource, ver) <- liftIO $ case M.lookup (filePathToUri' file) vfsData of - Nothing -> (,Nothing) . T.decodeUtf8 <$> BS.readFile (fromNormalizedFilePath file) + (currentSource, ver) <- liftIO $ case M.lookup (absToUri file) vfsData of + Nothing -> (,Nothing) . T.decodeUtf8 <$> BS.readFile (fromAbsPath file) Just vf -> pure (virtualFileText vf, Just $ virtualFileVersion vf) let refmap = Compat.generateReferencesMap . Compat.getAsts . Compat.hie_asts $ res del = deltaFromDiff (T.decodeUtf8 $ Compat.hie_hs_src res) currentSource pure (HAR (Compat.hie_module res) (Compat.hie_asts res) refmap mempty (HieFromDisk res),del,ver) -getHieAstRuleDefinition :: NormalizedFilePath -> HscEnv -> TcModuleResult -> Action (IdeResult HieAstResult) +getHieAstRuleDefinition :: Path Abs NormalizedFilePath -> HscEnv -> TcModuleResult -> Action (IdeResult HieAstResult) getHieAstRuleDefinition f hsc tmr = do (diags, masts) <- liftIO $ generateHieAsts hsc tmr se <- getShakeExtras @@ -531,7 +529,7 @@ getHieAstRuleDefinition f hsc tmr = do IsFOI Modified{firstOpen = False} -> do when (coerce $ ideTesting se) $ liftIO $ mRunLspT (lspEnv se) $ LSP.sendNotification (SMethod_CustomMethod (Proxy @"ghcide/reference/ready")) $ - toJSON $ fromNormalizedFilePath f + toJSON $ fromAbsPath f pure [] _ | Just asts <- masts -> do source <- getSourceFileSource f @@ -578,10 +576,10 @@ getDocMapRule recorder = persistentDocMapRule :: Rules () persistentDocMapRule = addPersistentRule GetDocMap $ \_ -> pure $ Just (DKMap mempty mempty, idDelta, Nothing) -readHieFileForSrcFromDisk :: Recorder (WithPriority Log) -> NormalizedFilePath -> MaybeT IdeAction Compat.HieFile +readHieFileForSrcFromDisk :: Recorder (WithPriority Log) -> Path Abs NormalizedFilePath -> MaybeT IdeAction Compat.HieFile readHieFileForSrcFromDisk recorder file = do ShakeExtras{withHieDb} <- ask - row <- MaybeT $ liftIO $ withHieDb (\hieDb -> HieDb.lookupHieFileFromSource hieDb $ fromNormalizedFilePath file) + row <- MaybeT $ liftIO $ withHieDb (\hieDb -> HieDb.lookupHieFileFromSource hieDb $ fromAbsPath file) let hie_loc = HieDb.hieModuleHieFile row liftIO $ logWith recorder Logger.Debug $ LogLoadingHieFile file exceptToMaybeT $ readHieFileFromDisk recorder hie_loc @@ -619,7 +617,7 @@ getModuleGraphRule recorder = defineEarlyCutOffNoFile (cmapWithPrio LogShake rec fs <- toKnownFiles <$> useNoFile_ GetKnownTargets dependencyInfoForFiles (HashSet.toList fs) -dependencyInfoForFiles :: [NormalizedFilePath] -> Action (BS.ByteString, DependencyInformation) +dependencyInfoForFiles :: [Path Abs NormalizedFilePath] -> Action (BS.ByteString, DependencyInformation) dependencyInfoForFiles fs = do (rawDepInfo, bm) <- rawDependencyInformation fs let (all_fs, _all_ids) = unzip $ HM.toList $ pathToIdMap $ rawPathIdMap rawDepInfo @@ -659,7 +657,7 @@ typeCheckRuleDefinition hsc pm = do r@(_, mtc) <- a forM_ mtc $ \tc -> do used_files <- liftIO $ readIORef $ tcg_dependent_files $ tmrTypechecked tc - void $ uses_ GetModificationTime (map toNormalizedFilePath' used_files) + void $ uses_ GetModificationTime (map mkAbsFromFp used_files) return r -- | Get all the linkables stored in the graph, i.e. the ones we *do not* need to unload. @@ -695,12 +693,12 @@ loadGhcSession recorder ghcSessionDepsConfig = do defineEarlyCutoff (cmapWithPrio LogShake recorder) $ Rule $ \GhcSession file -> do IdeGhcSession{loadSessionFun} <- useNoFile_ GhcSessionIO -- loading is always returning a absolute path now - (val,deps) <- liftIO $ loadSessionFun $ fromNormalizedFilePath file + (val,deps) <- liftIO $ loadSessionFun $ fromAbsPath file -- add the deps to the Shake graph let addDependency fp = do -- VSCode uses absolute paths in its filewatch notifications - let nfp = toNormalizedFilePath' fp + let nfp = mkAbsFromFp fp itExists <- getFileExists nfp when itExists $ void $ do use_ GetModificationTime nfp @@ -730,7 +728,7 @@ instance Default GhcSessionDepsConfig where ghcSessionDepsDefinition :: -- | full mod summary Bool -> - GhcSessionDepsConfig -> HscEnvEq -> NormalizedFilePath -> Action (Maybe HscEnvEq) + GhcSessionDepsConfig -> HscEnvEq -> Path Abs NormalizedFilePath -> Action (Maybe HscEnvEq) ghcSessionDepsDefinition fullModSummary GhcSessionDepsConfig{..} env file = do let hsc = hscEnv env @@ -818,7 +816,7 @@ getModIfaceFromDiskAndIndexRule recorder = let ms = hirModSummary x hie_loc = Compat.ml_hie_file $ ms_location ms fileHash <- liftIO $ Util.getFileHash hie_loc - mrow <- liftIO $ withHieDb (\hieDb -> HieDb.lookupHieFileFromSource hieDb (fromNormalizedFilePath f)) + mrow <- liftIO $ withHieDb (\hieDb -> HieDb.lookupHieFileFromSource hieDb (fromAbsPath f)) let hie_loc' = HieDb.hieModuleHieFile <$> mrow case mrow of Just row @@ -828,7 +826,7 @@ getModIfaceFromDiskAndIndexRule recorder = -- All good, the db has indexed the file when (coerce $ ideTesting se) $ liftIO $ mRunLspT (lspEnv se) $ LSP.sendNotification (SMethod_CustomMethod (Proxy @"ghcide/reference/ready")) $ - toJSON $ fromNormalizedFilePath f + toJSON $ fromAbsPath f -- Not in db, must re-index _ -> do ehf <- liftIO $ runIdeAction "GetModIfaceFromDiskAndIndex" se $ runExceptT $ @@ -862,7 +860,7 @@ getModSummaryRule displayTHWarning recorder = do modify_dflags <- getModifyDynFlags dynFlagsModifyGlobal let session = hscSetFlags (modify_dflags $ hsc_dflags session') session' (modTime, mFileContent) <- getFileContents f - let fp = fromNormalizedFilePath f + let fp = fromAbsPath f modS <- liftIO $ runExceptT $ getModSummaryFromImports session fp modTime (textToStringBuffer <$> mFileContent) case modS of @@ -888,7 +886,7 @@ getModSummaryRule displayTHWarning recorder = do return (Just fp, Just res{msrModSummary = ms}) Nothing -> return (Nothing, Nothing) -generateCore :: RunSimplifier -> NormalizedFilePath -> Action (IdeResult ModGuts) +generateCore :: RunSimplifier -> Path Abs NormalizedFilePath -> Action (IdeResult ModGuts) generateCore runSimplifier file = do packageState <- hscEnv <$> use_ GhcSessionDeps file tm <- use_ TypeCheck file @@ -941,7 +939,7 @@ incrementRebuildCount = do -- | Also generates and indexes the `.hie` file, along with the `.o` file if needed -- Invariant maintained is that if the `.hi` file was successfully written, then the -- `.hie` and `.o` file (if needed) were also successfully written -regenerateHiFile :: HscEnvEq -> NormalizedFilePath -> ModSummary -> Maybe LinkableType -> Action ([FileDiagnostic], Maybe HiFileResult) +regenerateHiFile :: HscEnvEq -> Path Abs NormalizedFilePath -> ModSummary -> Maybe LinkableType -> Action ([FileDiagnostic], Maybe HiFileResult) regenerateHiFile sess f ms compNeeded = do let hsc = hscEnv sess opt <- getIdeOptions @@ -1089,12 +1087,12 @@ getLinkableRule recorder = 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) +getLinkableType :: Path Abs NormalizedFilePath -> Action (Maybe LinkableType) getLinkableType f = use_ NeedsCompilation f -needsCompilationRule :: NormalizedFilePath -> Action (IdeResultNoDiagnosticsEarlyCutoff (Maybe LinkableType)) +needsCompilationRule :: Path Abs NormalizedFilePath -> Action (IdeResultNoDiagnosticsEarlyCutoff (Maybe LinkableType)) needsCompilationRule file - | "boot" `isSuffixOf` fromNormalizedFilePath file = + | "boot" `isSuffixOf` fromAbsPath file = pure (Just $ encodeLinkableType Nothing, Just Nothing) needsCompilationRule file = do graph <- useNoFile GetModuleGraph @@ -1210,7 +1208,7 @@ mainRule recorder RulesConfig{..} = do getLinkableRule recorder -- | Get HieFile for haskell file on NormalizedFilePath -getHieFile :: NormalizedFilePath -> Action (Maybe HieFile) +getHieFile :: Path Abs NormalizedFilePath -> Action (Maybe HieFile) getHieFile nfp = runMaybeT $ do HAR {hieAst} <- MaybeT $ use GetHieAst nfp tmr <- MaybeT $ use TypeCheck nfp diff --git a/ghcide/src/Development/IDE/Core/Shake.hs b/ghcide/src/Development/IDE/Core/Shake.hs index 921dfe3e6d..c03b2f83ce 100644 --- a/ghcide/src/Development/IDE/Core/Shake.hs +++ b/ghcide/src/Development/IDE/Core/Shake.hs @@ -5,6 +5,7 @@ {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE PackageImports #-} +{-# LANGUAGE QuantifiedConstraints #-} {-# LANGUAGE RecursiveDo #-} {-# LANGUAGE TypeFamilies #-} @@ -148,6 +149,7 @@ import Development.IDE.Types.KnownTargets import Development.IDE.Types.Location import Development.IDE.Types.Monitoring (Monitoring (..)) import Development.IDE.Types.Options +import Development.IDE.Types.Path import Development.IDE.Types.Shake import qualified Focus import GHC.Fingerprint @@ -192,7 +194,7 @@ data Log | LogLookupPersistentKey !T.Text | LogShakeGarbageCollection !T.Text !Int !Seconds -- * OfInterest Log messages - | LogSetFilesOfInterest ![(NormalizedFilePath, FileOfInterestStatus)] + | LogSetFilesOfInterest ![(Path Abs NormalizedFilePath, FileOfInterestStatus)] deriving Show instance Pretty Log where @@ -235,7 +237,7 @@ instance Pretty Log where pretty label <+> "of" <+> pretty number <+> "keys (took " <+> pretty (showDuration duration) <> ")" LogSetFilesOfInterest ofInterest -> "Set files of interst to" <> Pretty.line - <> indent 4 (pretty $ fmap (first fromNormalizedFilePath) ofInterest) + <> indent 4 (pretty $ fmap (first fromAbsPath) ofInterest) -- | We need to serialize writes to the database, so we send any function that -- needs to write to the database over the channel, where it will be picked up by @@ -243,7 +245,7 @@ instance Pretty Log where data HieDbWriter = HieDbWriter { indexQueue :: IndexQueue - , indexPending :: TVar (HMap.HashMap NormalizedFilePath Fingerprint) -- ^ Avoid unnecessary/out of date indexing + , indexPending :: TVar (HMap.HashMap (Path Abs NormalizedFilePath) Fingerprint) -- ^ Avoid unnecessary/out of date indexing , indexCompleted :: TVar Int -- ^ to report progress , indexProgressReporting :: ProgressReporting } @@ -283,7 +285,7 @@ data ShakeExtras = ShakeExtras -- ^ This represents the set of diagnostics that we have published. -- Due to debouncing not every change might get published. - ,semanticTokensCache:: STM.Map NormalizedFilePath SemanticTokens + ,semanticTokensCache:: STM.Map (Path Abs NormalizedFilePath) SemanticTokens -- ^ Cache of last response of semantic tokens for each file, -- so we can compute deltas for semantic tokens(SMethod_TextDocumentSemanticTokensFullDelta). -- putting semantic tokens cache and id in shakeExtras might not be ideal @@ -338,7 +340,7 @@ type WithProgressFunc = forall a. type WithIndefiniteProgressFunc = forall a. T.Text -> LSP.ProgressCancellable -> IO a -> IO a -type GetStalePersistent = NormalizedFilePath -> IdeAction (Maybe (Dynamic,PositionDelta,Maybe Int32)) +type GetStalePersistent = Path Abs NormalizedFilePath -> IdeAction (Maybe (Dynamic,PositionDelta,Maybe Int32)) getShakeExtras :: Action ShakeExtras getShakeExtras = do @@ -380,7 +382,7 @@ getPluginConfigAction plId = do -- This is called when we don't already have a result, or computing the rule failed. -- The result of this function will always be marked as 'stale', and a 'proper' rebuild of the rule will -- be queued if the rule hasn't run before. -addPersistentRule :: IdeRule k v => k -> (NormalizedFilePath -> IdeAction (Maybe (v,PositionDelta,Maybe Int32))) -> Rules () +addPersistentRule :: IdeRule k v => k -> (Path Abs NormalizedFilePath -> IdeAction (Maybe (v,PositionDelta,Maybe Int32))) -> Rules () addPersistentRule k getVal = do ShakeExtras{persistentKeys} <- getShakeExtrasRules void $ liftIO $ atomically $ modifyTVar' persistentKeys $ insertKeyMap (newKey k) (fmap (fmap (first3 toDyn)) . getVal) @@ -388,10 +390,10 @@ addPersistentRule k getVal = do class Typeable a => IsIdeGlobal a where -- | Read a virtual file from the current snapshot -getVirtualFile :: NormalizedFilePath -> Action (Maybe VirtualFile) +getVirtualFile :: Path Abs NormalizedFilePath -> Action (Maybe VirtualFile) getVirtualFile nf = do vfs <- fmap _vfsMap . liftIO . readTVarIO . vfsVar =<< getShakeExtras - pure $! Map.lookup (filePathToUri' nf) vfs -- Don't leak a reference to the entire map + pure $! Map.lookup (absToUri nf) vfs -- Don't leak a reference to the entire map -- Take a snapshot of the current LSP VFS vfsSnapshot :: Maybe (LSP.LanguageContextEnv a) -> IO VFS @@ -448,7 +450,7 @@ getIdeOptionsIO ide = do -- | Return the most recent, potentially stale, value and a PositionMapping -- for the version of that value. -lastValueIO :: IdeRule k v => ShakeExtras -> k -> NormalizedFilePath -> IO (Maybe (v, PositionMapping)) +lastValueIO :: IdeRule k v => ShakeExtras -> k -> Path Abs NormalizedFilePath -> IO (Maybe (v, PositionMapping)) lastValueIO s@ShakeExtras{positionMapping,persistentKeys,state} k file = do let readPersistent @@ -468,7 +470,7 @@ lastValueIO s@ShakeExtras{positionMapping,persistentKeys,state} k file = do Just (v,del,mbVer) -> do actual_version <- case mbVer of Just ver -> pure (Just $ VFSVersion ver) - Nothing -> (Just . ModificationTime <$> getModTime (fromNormalizedFilePath file)) + Nothing -> (Just . ModificationTime <$> getModTime (fromAbsPath file)) `catch` (\(_ :: IOException) -> pure Nothing) atomicallyNamed "lastValueIO 2" $ do STM.focus (Focus.alter (alterValue $ Stale (Just del) actual_version (toDyn v))) (toKey k file) state @@ -494,18 +496,18 @@ lastValueIO s@ShakeExtras{positionMapping,persistentKeys,state} k file = do -- | Return the most recent, potentially stale, value and a PositionMapping -- for the version of that value. -lastValue :: IdeRule k v => k -> NormalizedFilePath -> Action (Maybe (v, PositionMapping)) +lastValue :: IdeRule k v => k -> Path Abs NormalizedFilePath -> Action (Maybe (v, PositionMapping)) lastValue key file = do s <- getShakeExtras liftIO $ lastValueIO s key file mappingForVersion :: STM.Map NormalizedUri (EnumMap Int32 (a, PositionMapping)) - -> NormalizedFilePath + -> Path Abs NormalizedFilePath -> Maybe FileVersion -> STM PositionMapping mappingForVersion allMappings file (Just (VFSVersion ver)) = do - mapping <- STM.lookup (filePathToUri' file) allMappings + mapping <- STM.lookup (absToUri file) allMappings return $ maybe zeroMapping snd $ EM.lookup ver =<< mapping mappingForVersion _ _ _ = pure zeroMapping @@ -580,7 +582,7 @@ shakeDatabaseProfileIO mbProfileDir = do setValues :: IdeRule k v => Values -> k - -> NormalizedFilePath + -> Path Abs NormalizedFilePath -> Value v -> Vector FileDiagnostic -> STM () @@ -594,7 +596,7 @@ deleteValue :: Shake.ShakeValue k => ShakeExtras -> k - -> NormalizedFilePath + -> Path Abs NormalizedFilePath -> STM [Key] deleteValue ShakeExtras{state} key file = do STM.delete (toKey key file) state @@ -607,7 +609,7 @@ getValues :: IdeRule k v => Values -> k -> - NormalizedFilePath -> + Path Abs NormalizedFilePath -> STM (Maybe (Value v, Vector FileDiagnostic)) getValues state key file = do STM.lookup (toKey key file) state >>= \case @@ -1006,23 +1008,23 @@ preservedKeys checkParents = HSet.fromList $ -- | Define a new Rule without early cutoff define - :: IdeRule k v - => Recorder (WithPriority Log) -> (k -> NormalizedFilePath -> Action (IdeResult v)) -> Rules () + :: forall k v. (IdeRule k v, NFData (Path Abs NormalizedFilePath)) + => Recorder (WithPriority Log) -> (k -> Path Abs NormalizedFilePath -> Action (IdeResult v)) -> Rules () define recorder op = defineEarlyCutoff recorder $ Rule $ \k v -> (Nothing,) <$> op k v defineNoDiagnostics - :: IdeRule k v - => Recorder (WithPriority Log) -> (k -> NormalizedFilePath -> Action (Maybe v)) -> Rules () + :: forall k v. (IdeRule k v, NFData (Path Abs NormalizedFilePath)) + => Recorder (WithPriority Log) -> (k -> Path Abs NormalizedFilePath -> Action (Maybe v)) -> Rules () defineNoDiagnostics recorder op = defineEarlyCutoff recorder $ RuleNoDiagnostics $ \k v -> (Nothing,) <$> op k v -- | Request a Rule result if available use :: IdeRule k v - => k -> NormalizedFilePath -> Action (Maybe v) + => k -> Path Abs NormalizedFilePath -> Action (Maybe v) use key file = runIdentity <$> uses key (Identity file) -- | Request a Rule result, it not available return the last computed result, if any, which may be stale useWithStale :: IdeRule k v - => k -> NormalizedFilePath -> Action (Maybe (v, PositionMapping)) + => k -> Path Abs NormalizedFilePath -> Action (Maybe (v, PositionMapping)) useWithStale key file = runIdentity <$> usesWithStale key (Identity file) -- |Request a Rule result, it not available return the last computed result @@ -1033,7 +1035,7 @@ useWithStale key file = runIdentity <$> usesWithStale key (Identity file) -- -- WARNING: Not suitable for PluginHandlers. Use `useWithStaleE` instead. useWithStale_ :: IdeRule k v - => k -> NormalizedFilePath -> Action (v, PositionMapping) + => k -> Path Abs NormalizedFilePath -> Action (v, PositionMapping) useWithStale_ key file = runIdentity <$> usesWithStale_ key (Identity file) -- |Plural version of 'useWithStale_' @@ -1042,7 +1044,7 @@ useWithStale_ key file = runIdentity <$> usesWithStale_ key (Identity file) -- none available. -- -- WARNING: Not suitable for PluginHandlers. -usesWithStale_ :: (Traversable f, IdeRule k v) => k -> f NormalizedFilePath -> Action (f (v, PositionMapping)) +usesWithStale_ :: (Traversable f, IdeRule k v) => k -> f (Path Abs NormalizedFilePath) -> Action (f (v, PositionMapping)) usesWithStale_ key files = do res <- usesWithStale key files case sequence res of @@ -1073,11 +1075,11 @@ data FastResult a = FastResult { stale :: Maybe (a,PositionMapping), uptoDate :: -- | Lookup value in the database and return with the stale value immediately -- Will queue an action to refresh the value. -- Might block the first time the rule runs, but never blocks after that. -useWithStaleFast :: IdeRule k v => k -> NormalizedFilePath -> IdeAction (Maybe (v, PositionMapping)) +useWithStaleFast :: IdeRule k v => k -> Path Abs NormalizedFilePath -> IdeAction (Maybe (v, PositionMapping)) useWithStaleFast key file = stale <$> useWithStaleFast' key file -- | Same as useWithStaleFast but lets you wait for an up to date result -useWithStaleFast' :: IdeRule k v => k -> NormalizedFilePath -> IdeAction (FastResult v) +useWithStaleFast' :: IdeRule k v => k -> Path Abs NormalizedFilePath -> IdeAction (FastResult v) useWithStaleFast' key file = do -- This lookup directly looks up the key in the shake database and -- returns the last value that was computed for this key without @@ -1085,7 +1087,7 @@ useWithStaleFast' key file = do -- Async trigger the key to be built anyway because we want to -- keep updating the value in the key. - waitValue <- delayedAction $ mkDelayedAction ("C:" ++ show key ++ ":" ++ fromNormalizedFilePath file) Debug $ use key file + waitValue <- delayedAction $ mkDelayedAction ("C:" ++ show key ++ ":" ++ fromAbsPath file) Debug $ use key file s@ShakeExtras{state} <- askShake r <- liftIO $ atomicallyNamed "useStateFast" $ getValues state key file @@ -1105,7 +1107,7 @@ useWithStaleFast' key file = do pure $ FastResult res waitValue useNoFile :: IdeRule k v => k -> Action (Maybe v) -useNoFile key = use key emptyFilePath +useNoFile key = use key emptyAbsPath -- Requests a rule if available. -- @@ -1113,11 +1115,11 @@ useNoFile key = use key emptyFilePath -- none available. -- -- WARNING: Not suitable for PluginHandlers. Use `useE` instead. -use_ :: IdeRule k v => k -> NormalizedFilePath -> Action v +use_ :: IdeRule k v => k -> Path Abs NormalizedFilePath -> Action v use_ key file = runIdentity <$> uses_ key (Identity file) useNoFile_ :: IdeRule k v => k -> Action v -useNoFile_ key = use_ key emptyFilePath +useNoFile_ key = use_ key emptyAbsPath -- |Plural version of `use_` -- @@ -1125,7 +1127,7 @@ useNoFile_ key = use_ key emptyFilePath -- none available. -- -- WARNING: Not suitable for PluginHandlers. Use `usesE` instead. -uses_ :: (Traversable f, IdeRule k v) => k -> f NormalizedFilePath -> Action (f v) +uses_ :: (Traversable f, IdeRule k v) => k -> f (Path Abs NormalizedFilePath) -> Action (f v) uses_ key files = do res <- uses key files case sequence res of @@ -1134,12 +1136,12 @@ uses_ key files = do -- | Plural version of 'use' uses :: (Traversable f, IdeRule k v) - => k -> f NormalizedFilePath -> Action (f (Maybe v)) + => k -> f (Path Abs NormalizedFilePath) -> Action (f (Maybe v)) uses key files = fmap (\(A value) -> currentValue value) <$> apply (fmap (Q . (key,)) files) -- | Return the last computed result which might be stale. usesWithStale :: (Traversable f, IdeRule k v) - => k -> f NormalizedFilePath -> Action (f (Maybe (v, PositionMapping))) + => k -> f (Path Abs NormalizedFilePath) -> Action (f (Maybe (v, PositionMapping))) usesWithStale key files = do _ <- apply (fmap (Q . (key,)) files) -- We don't look at the result of the 'apply' since 'lastValue' will @@ -1148,22 +1150,22 @@ usesWithStale key files = do traverse (lastValue key) files useWithoutDependency :: IdeRule k v - => k -> NormalizedFilePath -> Action (Maybe v) + => k -> Path Abs NormalizedFilePath -> Action (Maybe v) useWithoutDependency key file = (\(Identity (A value)) -> currentValue value) <$> applyWithoutDependency (Identity (Q (key, file))) data RuleBody k v - = Rule (k -> NormalizedFilePath -> Action (Maybe BS.ByteString, IdeResult v)) - | RuleNoDiagnostics (k -> NormalizedFilePath -> Action (Maybe BS.ByteString, Maybe v)) + = Rule (k -> Path Abs NormalizedFilePath -> Action (Maybe BS.ByteString, IdeResult v)) + | RuleNoDiagnostics (k -> Path Abs NormalizedFilePath -> Action (Maybe BS.ByteString, Maybe v)) | RuleWithCustomNewnessCheck { newnessCheck :: BS.ByteString -> BS.ByteString -> Bool - , build :: k -> NormalizedFilePath -> Action (Maybe BS.ByteString, Maybe v) + , build :: k -> Path Abs NormalizedFilePath -> Action (Maybe BS.ByteString, Maybe v) } - | RuleWithOldValue (k -> NormalizedFilePath -> Value v -> Action (Maybe BS.ByteString, IdeResult v)) + | RuleWithOldValue (k -> Path Abs NormalizedFilePath -> Value v -> Action (Maybe BS.ByteString, IdeResult v)) -- | Define a new Rule with early cutoff defineEarlyCutoff - :: IdeRule k v + :: forall k v . (IdeRule k v, NFData (Path Abs NormalizedFilePath)) => Recorder (WithPriority Log) -> RuleBody k v -> Rules () @@ -1193,23 +1195,24 @@ defineEarlyCutoff recorder (RuleWithOldValue op) = addRule $ \(Q (key, file)) (o updateFileDiagnostics recorder file ver (newKey key) extras . map (\(_,y,z) -> (y,z)) $ diags defineEarlyCutoff' diagnostics (==) key file old mode $ op key file -defineNoFile :: IdeRule k v => Recorder (WithPriority Log) -> (k -> Action v) -> Rules () +defineNoFile :: forall k v . (IdeRule k v, NFData (Path Abs NormalizedFilePath)) => Recorder (WithPriority Log) -> (k -> Action v) -> Rules () defineNoFile recorder f = defineNoDiagnostics recorder $ \k file -> do - if file == emptyFilePath then do res <- f k; return (Just res) else + if file == emptyAbsPath then do res <- f k; return (Just res) else fail $ "Rule " ++ show k ++ " should always be called with the empty string for a file" -defineEarlyCutOffNoFile :: IdeRule k v => Recorder (WithPriority Log) -> (k -> Action (BS.ByteString, v)) -> Rules () +defineEarlyCutOffNoFile :: forall k v. (IdeRule k v, NFData (Path Abs NormalizedFilePath)) => Recorder (WithPriority Log) -> (k -> Action (BS.ByteString, v)) -> Rules () defineEarlyCutOffNoFile recorder f = defineEarlyCutoff recorder $ RuleNoDiagnostics $ \k file -> do - if file == emptyFilePath then do (hashString, res) <- f k; return (Just hashString, Just res) else + if file == emptyAbsPath 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' - :: forall k v. IdeRule k v + :: forall k v. (IdeRule k v, + NFData (Path Abs NormalizedFilePath)) => (Maybe Int32 -> [FileDiagnostic] -> Action ()) -- ^ update diagnostics -- | compare current and previous for freshness -> (BS.ByteString -> BS.ByteString -> Bool) -> k - -> NormalizedFilePath + -> Path Abs NormalizedFilePath -> Maybe BS.ByteString -> RunMode -> (Value v -> Action (Maybe BS.ByteString, IdeResult v)) @@ -1275,10 +1278,10 @@ defineEarlyCutoff' doDiagnostics cmp key file mbOld mode action = do estimateFileVersionUnsafely :: k -> Maybe v - -> NormalizedFilePath + -> Path Abs NormalizedFilePath -> Action (Maybe FileVersion) estimateFileVersionUnsafely _k v fp - | fp == emptyFilePath = pure Nothing + | fp == emptyAbsPath = pure Nothing | Just Refl <- eqT @k @GetModificationTime = pure v -- GetModificationTime depends on these rules, so avoid creating a cycle | Just Refl <- eqT @k @AddWatchedFile = pure Nothing @@ -1323,17 +1326,17 @@ traceA (A Succeeded{}) = "Success" updateFileDiagnostics :: MonadIO m => Recorder (WithPriority Log) - -> NormalizedFilePath + -> Path Abs NormalizedFilePath -> Maybe Int32 -> Key -> ShakeExtras -> [(ShowDiagnostic,Diagnostic)] -- ^ current results -> m () updateFileDiagnostics recorder fp ver k ShakeExtras{diagnostics, hiddenDiagnostics, publishedDiagnostics, debouncer, lspEnv, ideTesting} current0 = - liftIO $ withTrace ("update diagnostics " <> fromString(fromNormalizedFilePath fp)) $ \ addTag -> do + liftIO $ withTrace ("update diagnostics " <> fromString (fromAbsPath fp)) $ \ addTag -> do addTag "key" (show k) let (currentShown, currentHidden) = partition ((== ShowDiag) . fst) current - uri = filePathToUri' fp + uri = absToUri fp 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] @@ -1347,9 +1350,9 @@ 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' = absToUri 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 (fromAbsPath fp)) $ \tag -> do join $ mask_ $ do lastPublish <- atomicallyNamed "diagnostics - publish" $ STM.focus (Focus.lookupWithDefault [] <* Focus.insert newDiags) uri' publishedDiagnostics let action = when (lastPublish /= newDiags) $ case lspEnv of @@ -1368,7 +1371,7 @@ updateFileDiagnostics recorder fp ver k ShakeExtras{diagnostics, hiddenDiagnosti [ DiagnosticRelatedInformation (Location - (filePathToUri $ fromNormalizedFilePath fp) + (filePathToUri $ fromAbsPath fp) _range ) (T.pack $ show k) @@ -1422,7 +1425,7 @@ getAllDiagnostics :: STMDiagnosticStore -> STM [FileDiagnostic] getAllDiagnostics = - fmap (concatMap (\(k,v) -> map (fromUri k,ShowDiag,) $ getDiagnosticsFromStore v)) . ListT.toList . STM.listT + fmap (concatMap (\(k,v) -> map (mkAbsPath $ fromUri k,ShowDiag,) $ getDiagnosticsFromStore v)) . ListT.toList . STM.listT updatePositionMapping :: IdeState -> VersionedTextDocumentIdentifier -> [TextDocumentContentChangeEvent] -> STM () updatePositionMapping IdeState{shakeExtras = ShakeExtras{positionMapping}} VersionedTextDocumentIdentifier{..} changes = diff --git a/ghcide/src/Development/IDE/Core/Tracing.hs b/ghcide/src/Development/IDE/Core/Tracing.hs index 34839faaee..6c6077f88f 100644 --- a/ghcide/src/Development/IDE/Core/Tracing.hs +++ b/ghcide/src/Development/IDE/Core/Tracing.hs @@ -26,10 +26,10 @@ import Development.IDE.Graph.Rule import Development.IDE.Types.Diagnostics (FileDiagnostic, showDiagnostics) import Development.IDE.Types.Location (Uri (..)) +import Development.IDE.Types.Path import Ide.Logger import Ide.Types (PluginId (..)) -import Language.LSP.Protocol.Types (NormalizedFilePath, - fromNormalizedFilePath) +import Language.LSP.Protocol.Types (NormalizedFilePath) import OpenTelemetry.Eventlog (SpanInFlight (..), addEvent, beginSpan, endSpan, setTag, withSpan) @@ -91,7 +91,7 @@ otSetUri sp (Uri t) = setTag sp "uri" (encodeUtf8 t) otTracedAction :: Show k => k -- ^ The Action's Key - -> NormalizedFilePath -- ^ Path to the file the action was run for + -> Path Abs NormalizedFilePath -- ^ Path to the file the action was run for -> RunMode -> (a -> String) -> (([FileDiagnostic] -> Action ()) -> Action (RunResult a)) -- ^ The action @@ -101,7 +101,7 @@ otTracedAction key file mode result act generalBracket (do sp <- beginSpan (fromString (show key)) - setTag sp "File" (fromString $ fromNormalizedFilePath file) + setTag sp "File" (fromString $ fromAbsPath file) setTag sp "Mode" (fromString $ show mode) return sp ) diff --git a/ghcide/src/Development/IDE/Core/UseStale.hs b/ghcide/src/Development/IDE/Core/UseStale.hs index 498ea44bee..3324f22710 100644 --- a/ghcide/src/Development/IDE/Core/UseStale.hs +++ b/ghcide/src/Development/IDE/Core/UseStale.hs @@ -36,6 +36,7 @@ import qualified Development.IDE.Core.PositionMapping as P import qualified Development.IDE.Core.Shake as IDE import Development.IDE.GHC.Compat (RealSrcSpan, srcSpanFile) import Development.IDE.GHC.Compat.Util (unpackFS) +import Development.IDE.Types.Path ------------------------------------------------------------------------------ @@ -144,7 +145,7 @@ unsafeCopyAge _ = coerce -- | Request a Rule result, it not available return the last computed result, if any, which may be stale useWithStale :: IdeRule k v - => k -> NormalizedFilePath -> Action (Maybe (TrackedStale v)) + => k -> Path Abs NormalizedFilePath -> Action (Maybe (TrackedStale v)) useWithStale key file = do x <- IDE.useWithStale key file pure $ x <&> \(v, pm) -> @@ -153,7 +154,7 @@ useWithStale key file = do -- | Request a Rule result, it not available return the last computed result which may be stale. -- Errors out if none available. useWithStale_ :: IdeRule k v - => k -> NormalizedFilePath -> Action (TrackedStale v) + => k -> Path Abs NormalizedFilePath -> Action (TrackedStale v) useWithStale_ key file = do (v, pm) <- IDE.useWithStale_ key file pure $ TrackedStale (coerce v) (coerce pm) diff --git a/ghcide/src/Development/IDE/GHC/Error.hs b/ghcide/src/Development/IDE/GHC/Error.hs index 651fa5a34d..07d797f29e 100644 --- a/ghcide/src/Development/IDE/GHC/Error.hs +++ b/ghcide/src/Development/IDE/GHC/Error.hs @@ -45,6 +45,7 @@ import qualified Development.IDE.GHC.Compat.Util as Compat import Development.IDE.GHC.Orphans () import Development.IDE.Types.Diagnostics as D import Development.IDE.Types.Location +import Development.IDE.Types.Path import GHC import Language.LSP.Protocol.Types (isSubrangeOf) import Language.LSP.VFS (CodePointPosition (CodePointPosition), @@ -52,18 +53,21 @@ import Language.LSP.VFS (CodePointPosition (CodePoint diagFromText :: T.Text -> D.DiagnosticSeverity -> SrcSpan -> T.Text -> FileDiagnostic -diagFromText diagSource sev loc msg = (toNormalizedFilePath' $ fromMaybe noFilePath $ srcSpanToFilename loc,ShowDiag,) - Diagnostic - { _range = fromMaybe noRange $ srcSpanToRange loc - , _severity = Just sev - , _source = Just diagSource -- not shown in the IDE, but useful for ghcide developers - , _message = msg - , _code = Nothing - , _relatedInformation = Nothing - , _tags = Nothing - , _codeDescription = Nothing - , _data_ = Nothing - } +diagFromText diagSource sev loc msg = (filePath, ShowDiag,) + Diagnostic + { _range = fromMaybe noRange $ srcSpanToRange loc + , _severity = Just sev + , _source = Just diagSource -- not shown in the IDE, but useful for ghcide developers + , _message = msg + , _code = Nothing + , _relatedInformation = Nothing + , _tags = Nothing + , _codeDescription = Nothing + , _data_ = Nothing + } + where + normPath = toNormalizedFilePath' $ fromMaybe noFilePath $ srcSpanToFilename loc + filePath = mkAbsPath normPath -- | Produce a GHC-style error from a source span and a message. diagFromErrMsg :: T.Text -> DynFlags -> MsgEnvelope DecoratedSDoc -> [FileDiagnostic] diff --git a/ghcide/src/Development/IDE/Import/DependencyInformation.hs b/ghcide/src/Development/IDE/Import/DependencyInformation.hs index 5372a1364a..187a9a43e3 100644 --- a/ghcide/src/Development/IDE/Import/DependencyInformation.hs +++ b/ghcide/src/Development/IDE/Import/DependencyInformation.hs @@ -52,6 +52,7 @@ import Development.IDE.GHC.Orphans () import Development.IDE.Import.FindImports (ArtifactsLocation (..)) import Development.IDE.Types.Diagnostics import Development.IDE.Types.Location +import Development.IDE.Types.Path import GHC.Generics (Generic) import Prelude hiding (mod) @@ -77,7 +78,7 @@ type FilePathIdSet = IntSet data PathIdMap = PathIdMap { idToPathMap :: !(FilePathIdMap ArtifactsLocation) - , pathToIdMap :: !(HashMap NormalizedFilePath FilePathId) + , pathToIdMap :: !(HashMap (Path Abs NormalizedFilePath) FilePathId) , nextFreshId :: !Int } deriving (Show, Generic) @@ -105,13 +106,13 @@ getPathId path m@PathIdMap{..} = insertImport :: FilePathId -> Either ModuleParseError ModuleImports -> RawDependencyInformation -> RawDependencyInformation insertImport (FilePathId k) v rawDepInfo = rawDepInfo { rawImports = IntMap.insert k v (rawImports rawDepInfo) } -pathToId :: PathIdMap -> NormalizedFilePath -> Maybe FilePathId +pathToId :: PathIdMap -> Path Abs NormalizedFilePath -> Maybe FilePathId pathToId PathIdMap{pathToIdMap} path = pathToIdMap HMS.!? path -lookupPathToId :: PathIdMap -> NormalizedFilePath -> Maybe FilePathId +lookupPathToId :: PathIdMap -> Path Abs NormalizedFilePath -> Maybe FilePathId lookupPathToId PathIdMap{pathToIdMap} path = HMS.lookup path pathToIdMap -idToPath :: PathIdMap -> FilePathId -> NormalizedFilePath +idToPath :: PathIdMap -> FilePathId -> Path Abs NormalizedFilePath idToPath pathIdMap filePathId = artifactFilePath $ idToModLocation pathIdMap filePathId idToModLocation :: PathIdMap -> FilePathId -> ArtifactsLocation @@ -166,7 +167,7 @@ instance NFData a => NFData (ShowableModuleEnv a) where instance Show ShowableModule where show = moduleNameString . moduleName . showableModule -reachableModules :: DependencyInformation -> [NormalizedFilePath] +reachableModules :: DependencyInformation -> [Path Abs NormalizedFilePath] reachableModules DependencyInformation{..} = map (idToPath depPathIdMap . FilePathId) $ IntMap.keys depErrorNodes <> IntMap.keys depModuleDeps @@ -322,7 +323,7 @@ partitionSCC (AcyclicSCC x:rest) = first (x:) $ partitionSCC rest partitionSCC [] = ([], []) -- | Transitive reverse dependencies of a file -transitiveReverseDependencies :: NormalizedFilePath -> DependencyInformation -> Maybe [NormalizedFilePath] +transitiveReverseDependencies :: Path Abs NormalizedFilePath -> DependencyInformation -> Maybe [Path Abs NormalizedFilePath] transitiveReverseDependencies file DependencyInformation{..} = do FilePathId cur_id <- lookupPathToId depPathIdMap file return $ map (idToPath depPathIdMap . FilePathId) (IntSet.toList (go cur_id IntSet.empty)) @@ -335,13 +336,13 @@ transitiveReverseDependencies file DependencyInformation{..} = do in IntSet.foldr go res new -- | Immediate reverse dependencies of a file -immediateReverseDependencies :: NormalizedFilePath -> DependencyInformation -> Maybe [NormalizedFilePath] +immediateReverseDependencies :: Path Abs NormalizedFilePath -> DependencyInformation -> Maybe [Path Abs NormalizedFilePath] immediateReverseDependencies file DependencyInformation{..} = do FilePathId cur_id <- lookupPathToId depPathIdMap file return $ map (idToPath depPathIdMap . FilePathId) (maybe mempty IntSet.toList (IntMap.lookup cur_id depReverseModuleDeps)) -- | returns all transitive dependencies in topological order. -transitiveDeps :: DependencyInformation -> NormalizedFilePath -> Maybe TransitiveDependencies +transitiveDeps :: DependencyInformation -> Path Abs NormalizedFilePath -> Maybe TransitiveDependencies transitiveDeps DependencyInformation{..} file = do !fileId <- pathToId depPathIdMap file reachableVs <- @@ -366,12 +367,12 @@ transitiveDeps DependencyInformation{..} file = do vs = topSort g -lookupModuleFile :: Module -> DependencyInformation -> Maybe NormalizedFilePath +lookupModuleFile :: Module -> DependencyInformation -> Maybe (Path Abs NormalizedFilePath) lookupModuleFile mod DependencyInformation{..} = idToPath depPathIdMap <$> lookupModuleEnv (showableModuleEnv depModuleFiles) mod newtype TransitiveDependencies = TransitiveDependencies - { transitiveModuleDeps :: [NormalizedFilePath] + { transitiveModuleDeps :: [Path Abs NormalizedFilePath] -- ^ Transitive module dependencies in topological order. -- The module itself is not included. } deriving (Eq, Show, Generic) @@ -379,7 +380,7 @@ newtype TransitiveDependencies = TransitiveDependencies instance NFData TransitiveDependencies data NamedModuleDep = NamedModuleDep { - nmdFilePath :: !NormalizedFilePath, + nmdFilePath :: !(Path Abs NormalizedFilePath), nmdModuleName :: !ModuleName, nmdModLocation :: !(Maybe ModLocation) } diff --git a/ghcide/src/Development/IDE/Import/FindImports.hs b/ghcide/src/Development/IDE/Import/FindImports.hs index e17c490c5a..c8fb01071e 100644 --- a/ghcide/src/Development/IDE/Import/FindImports.hs +++ b/ghcide/src/Development/IDE/Import/FindImports.hs @@ -24,18 +24,18 @@ import Development.IDE.GHC.Error as ErrUtils import Development.IDE.GHC.Orphans () import Development.IDE.Types.Diagnostics import Development.IDE.Types.Location +import Development.IDE.Types.Path import GHC.Types.PkgQual import GHC.Unit.State import System.FilePath - data Import = FileImport !ArtifactsLocation | PackageImport deriving (Show) data ArtifactsLocation = ArtifactsLocation - { artifactFilePath :: !NormalizedFilePath + { artifactFilePath :: !(Path Abs NormalizedFilePath) , artifactModLocation :: !(Maybe ModLocation) , artifactIsSource :: !Bool -- ^ True if a module is a source input , artifactModule :: !(Maybe Module) @@ -51,32 +51,32 @@ instance NFData Import where rnf (FileImport x) = rnf x rnf PackageImport = () -modSummaryToArtifactsLocation :: NormalizedFilePath -> Maybe ModSummary -> ArtifactsLocation +modSummaryToArtifactsLocation :: Path Abs NormalizedFilePath -> Maybe ModSummary -> ArtifactsLocation 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 + Nothing -> "-boot" `isSuffixOf` fromAbsPath nfp Just modSum -> isSource (ms_hsc_src modSum) mbMod = ms_mod <$> ms data LocateResult = LocateNotFound | LocateFoundReexport UnitId - | LocateFoundFile UnitId NormalizedFilePath + | LocateFoundFile UnitId (Path Abs NormalizedFilePath) -- | locate a module in the file system. Where we go from *daml to Haskell locateModuleFile :: MonadIO m => [(UnitId, [FilePath], S.Set ModuleName)] -> [String] - -> (ModuleName -> NormalizedFilePath -> m (Maybe NormalizedFilePath)) + -> (ModuleName -> Path Abs NormalizedFilePath -> m (Maybe (Path Abs NormalizedFilePath))) -> Bool -> ModuleName -> m LocateResult locateModuleFile import_dirss exts targetFor isSource modName = do let candidates import_dirs = - [ toNormalizedFilePath' (prefix moduleNameSlashes modName <.> maybeBoot ext) + [ mkAbsFromFp (prefix moduleNameSlashes modName <.> maybeBoot ext) | prefix <- import_dirs , ext <- exts] mf <- firstJustM go (concat [map (uid,) (candidates dirs) | (uid, dirs, _) <- import_dirss]) case mf of @@ -105,7 +105,7 @@ locateModule => HscEnv -> [(UnitId, DynFlags)] -- ^ Import directories -> [String] -- ^ File extensions - -> (ModuleName -> NormalizedFilePath -> m (Maybe NormalizedFilePath)) -- ^ does file exist predicate + -> (ModuleName -> Path Abs NormalizedFilePath -> m (Maybe (Path Abs NormalizedFilePath))) -- ^ does file exist predicate -> Located ModuleName -- ^ Module name -> PkgQual -- ^ Package name -> Bool -- ^ Is boot module @@ -164,7 +164,7 @@ locateModule env comp_info exts targetFor modName mbPkgName isSource = do import_paths toModLocation uid file = liftIO $ do - loc <- mkHomeModLocation dflags (unLoc modName) (fromNormalizedFilePath file) + loc <- mkHomeModLocation dflags (unLoc modName) (fromAbsPath file) let genMod = mkModule (RealUnit $ Definite uid) (unLoc modName) -- TODO support backpack holes return $ Right $ FileImport $ ArtifactsLocation file (Just loc) (not isSource) (Just genMod) diff --git a/ghcide/src/Development/IDE/LSP/HoverDefinition.hs b/ghcide/src/Development/IDE/LSP/HoverDefinition.hs index aea3449bf3..fb1f8bf8ac 100644 --- a/ghcide/src/Development/IDE/LSP/HoverDefinition.hs +++ b/ghcide/src/Development/IDE/LSP/HoverDefinition.hs @@ -17,23 +17,23 @@ module Development.IDE.LSP.HoverDefinition import Control.Monad.Except (ExceptT) import Control.Monad.IO.Class import Data.Maybe (fromMaybe) +import qualified Data.Text as T import Development.IDE.Core.Actions import qualified Development.IDE.Core.Rules as Shake import Development.IDE.Core.Shake (IdeAction, IdeState (..), runIdeAction) import Development.IDE.Types.Location +import Development.IDE.Types.Path import Ide.Logger import Ide.Plugin.Error import Ide.Types import Language.LSP.Protocol.Message import Language.LSP.Protocol.Types -import qualified Data.Text as T - data Log = LogWorkspaceSymbolRequest !T.Text - | LogRequest !T.Text !Position !NormalizedFilePath + | LogRequest !T.Text !Position !(Path Abs NormalizedFilePath) deriving (Show) instance Pretty Log where @@ -41,7 +41,7 @@ instance Pretty Log where LogWorkspaceSymbolRequest query -> "Workspace symbols request:" <+> pretty query LogRequest label pos nfp -> pretty label <+> "request at position" <+> pretty (showPosition pos) <+> - "in file:" <+> pretty (fromNormalizedFilePath nfp) + "in file:" <+> pretty (fromAbsPath nfp) gotoDefinition :: Recorder (WithPriority Log) -> IdeState -> TextDocumentPositionParams -> ExceptT PluginError (HandlerM c) (MessageResult Method_TextDocumentDefinition) hover :: Recorder (WithPriority Log) -> IdeState -> TextDocumentPositionParams -> ExceptT PluginError (HandlerM c) (Hover |? Null) @@ -55,8 +55,8 @@ documentHighlight = request "DocumentHighlight" highlightAtPoint (InR Null) InL references :: Recorder (WithPriority Log) -> PluginMethodHandler IdeState Method_TextDocumentReferences references recorder ide _ (ReferenceParams (TextDocumentIdentifier uri) pos _ _ _) = do nfp <- getNormalizedFilePathE uri - liftIO $ logWith recorder Debug $ LogRequest "References" pos nfp - InL <$> (liftIO $ Shake.runAction "references" ide $ refsAtPoint nfp pos) + liftIO $ logWith recorder Debug $ LogRequest "References" pos (mkAbsPath nfp) + InL <$> (liftIO $ Shake.runAction "references" ide $ refsAtPoint (mkAbsPath nfp) pos) wsSymbols :: Recorder (WithPriority Log) -> PluginMethodHandler IdeState Method_WorkspaceSymbol wsSymbols recorder ide _ (WorkspaceSymbolParams _ _ query) = liftIO $ do @@ -70,7 +70,7 @@ foundHover (mbRange, contents) = -- | Respond to and log a hover or go-to-definition request request :: T.Text - -> (NormalizedFilePath -> Position -> IdeAction (Maybe a)) + -> (Path Abs NormalizedFilePath -> Position -> IdeAction (Maybe a)) -> b -> (a -> b) -> Recorder (WithPriority Log) @@ -83,8 +83,8 @@ request label getResults notFound found recorder ide (TextDocumentPositionParams Nothing -> pure Nothing pure $ maybe notFound found mbResult -logAndRunRequest :: Recorder (WithPriority Log) -> T.Text -> (NormalizedFilePath -> Position -> IdeAction b) -> IdeState -> Position -> String -> IO b +logAndRunRequest :: Recorder (WithPriority Log) -> T.Text -> (Path Abs NormalizedFilePath -> Position -> IdeAction b) -> IdeState -> Position -> String -> IO b logAndRunRequest recorder label getResults ide pos path = do - let filePath = toNormalizedFilePath' path + let filePath = mkAbsFromFp path logWith recorder Debug $ LogRequest label pos filePath runIdeAction (T.unpack label) (shakeExtras ide) (getResults filePath pos) diff --git a/ghcide/src/Development/IDE/LSP/Notifications.hs b/ghcide/src/Development/IDE/LSP/Notifications.hs index 4f5475442c..e996496c12 100644 --- a/ghcide/src/Development/IDE/LSP/Notifications.hs +++ b/ghcide/src/Development/IDE/LSP/Notifications.hs @@ -33,7 +33,7 @@ import Development.IDE.Core.OfInterest hiding (Log, LogShake) import Development.IDE.Core.Service hiding (Log, LogShake) import Development.IDE.Core.Shake hiding (Log) import qualified Development.IDE.Core.Shake as Shake -import Development.IDE.Types.Location +import Development.IDE.Types.Path import Ide.Logger import Ide.Types import Numeric.Natural @@ -60,8 +60,8 @@ instance Pretty Log where LogWatchedFileEvents msg -> "Watched file events:" <+> pretty msg LogWarnNoWatchedFilesSupport -> "Client does not support watched files. Falling back to OS polling" -whenUriFile :: Uri -> (NormalizedFilePath -> IO ()) -> IO () -whenUriFile uri act = whenJust (LSP.uriToFilePath uri) $ act . toNormalizedFilePath' +whenUriFile :: Uri -> (Path Abs NormalizedFilePath -> IO ()) -> IO () +whenUriFile uri act = whenJust (LSP.uriToFilePath uri) $ act . mkAbsFromFp descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState descriptor recorder plId = (defaultPluginDescriptor plId desc) { pluginNotificationHandlers = mconcat @@ -109,7 +109,7 @@ descriptor recorder plId = (defaultPluginDescriptor plId desc) { pluginNotificat let fileEvents' = [ (nfp, event) | (FileEvent uri event) <- fileEvents , Just fp <- [uriToFilePath uri] - , let nfp = toNormalizedFilePath fp + , let nfp = mkAbsFromFp fp , not $ HM.member nfp filesOfInterest ] unless (null fileEvents') $ do diff --git a/ghcide/src/Development/IDE/LSP/Outline.hs b/ghcide/src/Development/IDE/LSP/Outline.hs index af2a0f1c97..55e303a9c7 100644 --- a/ghcide/src/Development/IDE/LSP/Outline.hs +++ b/ghcide/src/Development/IDE/LSP/Outline.hs @@ -21,6 +21,7 @@ import Development.IDE.GHC.Error (rangeToRealSrcSpan, realSrcSpanToRange) import Development.IDE.GHC.Util (printOutputable) import Development.IDE.Types.Location +import Development.IDE.Types.Path import Ide.Types import Language.LSP.Protocol.Message import Language.LSP.Protocol.Types (DocumentSymbol (..), @@ -35,7 +36,7 @@ moduleOutline :: PluginMethodHandler IdeState Method_TextDocumentDocumentSymbol moduleOutline ideState _ DocumentSymbolParams{ _textDocument = TextDocumentIdentifier uri } = liftIO $ case uriToFilePath uri of - Just (toNormalizedFilePath' -> fp) -> do + Just (mkAbsFromFp -> fp) -> do mb_decls <- fmap fst <$> runAction "Outline" ideState (useWithStale GetParsedModule fp) pure $ case mb_decls of Nothing -> InL [] diff --git a/ghcide/src/Development/IDE/Main.hs b/ghcide/src/Development/IDE/Main.hs index d4c80e23a6..843b685920 100644 --- a/ghcide/src/Development/IDE/Main.hs +++ b/ghcide/src/Development/IDE/Main.hs @@ -86,6 +86,7 @@ import Development.IDE.Types.Options (IdeGhcSession, defaultIdeOptions, optModifyDynFlags, optTesting) +import Development.IDE.Types.Path import Development.IDE.Types.Shake (WithHieDb, toNoFileKey) import GHC.Conc (getNumProcessors) @@ -419,10 +420,10 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re registerIdeConfiguration (shakeExtras ide) $ IdeConfiguration mempty (hashed Nothing) putStrLn "\nStep 4/4: Type checking the 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) + setFilesOfInterest ide $ HashMap.fromList $ map ((,OnDisk) . mkAbsFromFp) absoluteFiles + results <- runAction "User TypeCheck" ide $ uses TypeCheck (map mkAbsFromFp absoluteFiles) + _results <- runAction "GetHie" ide $ uses GetHieAst (map mkAbsFromFp absoluteFiles) + _results <- runAction "GenerateCore" ide $ uses GenerateCore (map mkAbsFromFp absoluteFiles) let (worked, failed) = partition fst $ zip (map isJust results) absoluteFiles when (failed /= []) $ putStr $ unlines $ "Files that failed:" : map ((++) " * " . snd) failed diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index 337f159424..0a2b5bb601 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -53,9 +53,9 @@ import Text.Fuzzy.Parallel (Scored (..)) import Development.IDE.Core.Rules (usePropertyAction) -import qualified Ide.Plugin.Config as Config - +import Development.IDE.Types.Path import qualified GHC.LanguageExtensions as LangExt +import qualified Ide.Plugin.Config as Config data Log = LogShake Shake.Log deriving Show @@ -81,7 +81,7 @@ descriptor recorder plId = (defaultPluginDescriptor plId desc) produceCompletions :: Recorder (WithPriority Log) -> Rules () produceCompletions recorder = do define (cmapWithPrio LogShake recorder) $ \LocalCompletions file -> do - let uri = fromNormalizedUri $ normalizedFilePathToUri file + let uri = fromNormalizedUri $ absToUri file mbPm <- useWithStale GetParsedModule file case mbPm of Just (pm, _) -> do @@ -103,7 +103,7 @@ produceCompletions recorder = do case (global, inScope) of ((_, Just globalEnv), (_, Just inScopeEnv)) -> do visibleMods <- liftIO $ fmap (fromMaybe []) $ envVisibleModuleNames sess - let uri = fromNormalizedUri $ normalizedFilePathToUri file + let uri = fromNormalizedUri $ absToUri file let cdata = cacheDataProducer uri visibleMods (ms_mod msrModSummary) globalEnv inScopeEnv msrImports return ([], Just cdata) (_diag, _) -> @@ -131,10 +131,10 @@ resolveCompletion ide _pid comp@CompletionItem{_detail,_documentation,_data_} ur file <- getNormalizedFilePathE uri (sess,_) <- withExceptT (const PluginStaleResolve) $ runIdeActionE "CompletionResolve.GhcSessionDeps" (shakeExtras ide) - $ useWithStaleFastE GhcSessionDeps file + $ useWithStaleFastE GhcSessionDeps (mkAbsPath file) let nc = ideNc $ shakeExtras ide name <- liftIO $ lookupNameCache nc mod occ - mdkm <- liftIO $ runIdeAction "CompletionResolve.GetDocMap" (shakeExtras ide) $ useWithStaleFast GetDocMap file + mdkm <- liftIO $ runIdeAction "CompletionResolve.GetDocMap" (shakeExtras ide) $ useWithStaleFast GetDocMap (mkAbsPath file) let (dm,km) = case mdkm of Just (DKMap docMap tyThingMap, _) -> (docMap,tyThingMap) Nothing -> (mempty, mempty) @@ -168,7 +168,7 @@ getCompletionsLSP ide plId contents <- pluginGetVirtualFile $ toNormalizedUri uri fmap Right $ case (contents, uriToFilePath' uri) of (Just cnts, Just path) -> do - let npath = toNormalizedFilePath' path + let npath = mkAbsFromFp path (ideOpts, compls, moduleExports, astres) <- liftIO $ runIdeAction "Completion" (shakeExtras ide) $ do opts <- liftIO $ getIdeOptionsIO $ shakeExtras ide localCompls <- useWithStaleFast LocalCompletions npath diff --git a/ghcide/src/Development/IDE/Plugin/Test.hs b/ghcide/src/Development/IDE/Plugin/Test.hs index e24bcfeee9..cc974383e1 100644 --- a/ghcide/src/Development/IDE/Plugin/Test.hs +++ b/ghcide/src/Development/IDE/Plugin/Test.hs @@ -44,6 +44,7 @@ import qualified Development.IDE.Graph.Internal.Types as Graph import Development.IDE.Types.Action import Development.IDE.Types.HscEnvEq (HscEnvEq (hscEnv)) import Development.IDE.Types.Location (fromUri) +import Development.IDE.Types.Path import GHC.Generics (Generic) import Ide.Plugin.Error import Ide.Types @@ -97,7 +98,7 @@ testRequestHandler _ (BlockSeconds secs) = do liftIO $ sleep secs return (Right A.Null) testRequestHandler s (GetInterfaceFilesDir file) = liftIO $ do - let nfp = fromUri $ toNormalizedUri file + let nfp = mkAbsPath $ fromUri $ toNormalizedUri file sess <- runAction "Test - GhcSession" s $ use_ GhcSession nfp let hiPath = hiDir $ hsc_dflags $ hscEnv sess return $ Right (toJSON hiPath) @@ -110,7 +111,7 @@ testRequestHandler s WaitForShakeQueue = liftIO $ do when (n>0) retry return $ Right A.Null testRequestHandler s (WaitForIdeRule k file) = liftIO $ do - let nfp = fromUri $ toNormalizedUri file + let nfp = mkAbsPath $ fromUri $ toNormalizedUri file success <- runAction ("WaitForIdeRule " <> k <> " " <> show file) s $ parseAction (fromString k) nfp let res = WaitForIdeRuleResult <$> success return $ bimap PluginInvalidParams toJSON res @@ -134,7 +135,7 @@ testRequestHandler s GetStoredKeys = do return $ Right $ toJSON $ map show keys testRequestHandler s GetFilesOfInterest = do ff <- liftIO $ getFilesOfInterest s - return $ Right $ toJSON $ map fromNormalizedFilePath $ HM.keys ff + return $ Right $ toJSON $ map fromAbsPath $ HM.keys ff testRequestHandler s GetRebuildsCount = do count <- liftIO $ runAction "get build count" s getRebuildCount return $ Right $ toJSON count @@ -147,7 +148,7 @@ getDatabaseKeys field db = do step <- shakeGetBuildStep db return [ k | (k, res) <- keys, field res == Step step] -parseAction :: CI String -> NormalizedFilePath -> Action (Either Text Bool) +parseAction :: CI String -> Path Abs NormalizedFilePath -> Action (Either Text Bool) parseAction "typecheck" fp = Right . isJust <$> use TypeCheck fp parseAction "getLocatedImports" fp = Right . isJust <$> use GetLocatedImports fp parseAction "getmodsummary" fp = Right . isJust <$> use GetModSummary fp diff --git a/ghcide/src/Development/IDE/Plugin/TypeLenses.hs b/ghcide/src/Development/IDE/Plugin/TypeLenses.hs index 51d25e995b..6cbd8a1e6b 100644 --- a/ghcide/src/Development/IDE/Plugin/TypeLenses.hs +++ b/ghcide/src/Development/IDE/Plugin/TypeLenses.hs @@ -47,6 +47,7 @@ import Development.IDE.GHC.Util (printName) import Development.IDE.Graph.Classes import Development.IDE.Types.Location (Position (Position, _line), Range (Range, _end, _start)) +import Development.IDE.Types.Path import GHC.Generics (Generic) import Ide.Logger (Pretty (pretty), Recorder, WithPriority, @@ -127,7 +128,7 @@ codeLensProvider ideState pId CodeLensParams{_textDocument = TextDocumentIdentif -- dummy type to make sure HLS resolves our lens [ CodeLens _range Nothing (Just $ toJSON TypeLensesResolve) | (dFile, _, diag@Diagnostic{_range}) <- diags - , dFile == nfp + , dFile == mkAbsPath nfp , isGlobalDiagnostic diag] -- The second option is to generate lenses from the GlobalBindingTypeSig -- rule. This is the only type that needs to have the range adjusted @@ -148,7 +149,7 @@ codeLensProvider ideState pId CodeLensParams{_textDocument = TextDocumentIdentif -- GlobalBindingTypeSigs rule. (GlobalBindingTypeSigsResult gblSigs, gblSigsMp) <- runActionE "codeLens.GetGlobalBindingTypeSigs" ideState - $ useWithStaleE GetGlobalBindingTypeSigs nfp + $ useWithStaleE GetGlobalBindingTypeSigs (mkAbsPath nfp) -- Depending on whether we only want exported or not we filter our list -- of signatures to get what we want let relevantGlobalSigs = @@ -169,7 +170,7 @@ codeLensResolveProvider ideState pId lens@CodeLens{_range} uri TypeLensesResolve nfp <- getNormalizedFilePathE uri (gblSigs@(GlobalBindingTypeSigsResult _), pm) <- runActionE "codeLens.GetGlobalBindingTypeSigs" ideState - $ useWithStaleE GetGlobalBindingTypeSigs nfp + $ useWithStaleE GetGlobalBindingTypeSigs (mkAbsPath nfp) -- regardless of how the original lens was generated, we want to get the range -- that the global bindings rule would expect here, hence the need to reverse -- position map the range, regardless of whether it was position mapped in the diff --git a/ghcide/src/Development/IDE/Spans/AtPoint.hs b/ghcide/src/Development/IDE/Spans/AtPoint.hs index 434c684b96..e2d0db766b 100644 --- a/ghcide/src/Development/IDE/Spans/AtPoint.hs +++ b/ghcide/src/Development/IDE/Spans/AtPoint.hs @@ -56,6 +56,7 @@ import Data.List (isSuffixOf) import Data.List.Extra (dropEnd1, nubOrd) import Data.Version (showVersion) +import Development.IDE.Types.Path import Development.IDE.Types.Shake (WithHieDb) import HieDb hiding (pointCommand, withHieDb) @@ -66,7 +67,7 @@ import System.Directory (doesFileExist) type LookupModule m = FilePath -> ModuleName -> Unit -> Bool -> MaybeT m Uri -- | HieFileResult for files of interest, along with the position mappings -newtype FOIReferences = FOIReferences (HM.HashMap NormalizedFilePath (HieAstResult, PositionMapping)) +newtype FOIReferences = FOIReferences (HM.HashMap (Path Abs NormalizedFilePath) (HieAstResult, PositionMapping)) computeTypeReferences :: Foldable f => f (HieAST Type) -> M.Map Name [Span] computeTypeReferences = foldr (\ast m -> M.unionWith (++) (go ast) m) M.empty @@ -83,7 +84,7 @@ computeTypeReferences = foldr (\ast m -> M.unionWith (++) (go ast) m) M.empty -- | Given a file and position, return the names at a point, the references for -- those names in the FOIs, and a list of file paths we already searched through foiReferencesAtPoint - :: NormalizedFilePath + :: Path Abs NormalizedFilePath -> Position -> FOIReferences -> ([Name],[Location],[FilePath]) @@ -99,7 +100,7 @@ foiReferencesAtPoint file pos (FOIReferences asts) = (mapMaybe (\n -> M.lookup (Right n) rf) names) typerefs = concatMap (mapMaybe (toCurrentLocation goMapping . realSrcSpanToLocation)) (mapMaybe (`M.lookup` tr) names) - in (names, adjustedLocs,map fromNormalizedFilePath $ HM.keys asts) + in (names, adjustedLocs,map fromAbsPath $ HM.keys asts) getNamesAtPoint :: HieASTs a -> Position -> PositionMapping -> [Name] getNamesAtPoint hf pos mapping = @@ -114,7 +115,7 @@ toCurrentLocation mapping (Location uri range) = referencesAtPoint :: MonadIO m => WithHieDb - -> NormalizedFilePath -- ^ The file the cursor is in + -> Path Abs NormalizedFilePath -- ^ The file the cursor is in -> Position -- ^ position in the file -> FOIReferences -- ^ references data for FOIs -> m [Location] @@ -196,7 +197,7 @@ gotoDefinition => WithHieDb -> LookupModule m -> IdeOptions - -> M.Map ModuleName NormalizedFilePath + -> M.Map ModuleName (Path Abs NormalizedFilePath) -> HieASTs a -> Position -> MaybeT m [Location] @@ -358,7 +359,7 @@ locationsAtPoint => WithHieDb -> LookupModule m -> IdeOptions - -> M.Map ModuleName NormalizedFilePath + -> M.Map ModuleName (Path Abs NormalizedFilePath) -> Position -> HieASTs a -> m [Location] @@ -366,7 +367,7 @@ locationsAtPoint withHieDb lookupModule _ideOptions imports pos ast = let ns = concat $ pointCommand ast pos (M.keys . getNodeIds) zeroPos = Position 0 0 zeroRange = Range zeroPos zeroPos - modToLocation m = fmap (\fs -> pure $ Location (fromNormalizedUri $ filePathToUri' fs) zeroRange) $ M.lookup m imports + modToLocation m = fmap (\fs -> pure $ Location (fromNormalizedUri $ absToUri fs) zeroRange) $ M.lookup m imports in fmap (nubOrd . concat) $ mapMaybeM (either (pure . modToLocation) $ nameToLocation withHieDb lookupModule) ns -- | Given a 'Name' attempt to find the location where it is defined. diff --git a/ghcide/src/Development/IDE/Spans/Pragmas.hs b/ghcide/src/Development/IDE/Spans/Pragmas.hs index a2b4981a38..2a7110226b 100644 --- a/ghcide/src/Development/IDE/Spans/Pragmas.hs +++ b/ghcide/src/Development/IDE/Spans/Pragmas.hs @@ -9,23 +9,27 @@ module Development.IDE.Spans.Pragmas , insertNewPragma , getFirstPragma ) where -import Control.Lens ((&), (.~)) -import Data.Bits (Bits (setBit)) -import qualified Data.List as List -import qualified Data.Maybe as Maybe -import Data.Text (Text, pack) -import qualified Data.Text as Text -import Development.IDE (srcSpanToRange, IdeState, NormalizedFilePath, GhcSession (..), getFileContents, hscEnv, runAction) +import Control.Lens ((&), (.~)) +import Control.Monad.IO.Class (MonadIO (..)) +import Control.Monad.Trans.Except (ExceptT) +import Data.Bits (Bits (setBit)) +import qualified Data.List as List +import qualified Data.Maybe as Maybe +import Data.Text (Text, pack) +import qualified Data.Text as T +import qualified Data.Text as Text +import Development.IDE (GhcSession (..), IdeState, + NormalizedFilePath, + getFileContents, hscEnv, + runAction, srcSpanToRange) +import Development.IDE.Core.PluginUtils 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.Lens as L +import Development.IDE.Types.Path +import Ide.Plugin.Error (PluginError) +import Ide.Types (PluginId (..)) +import qualified Language.LSP.Protocol.Lens as L +import qualified Language.LSP.Protocol.Types as LSP getNextPragmaInfo :: DynFlags -> Maybe Text -> NextPragmaInfo getNextPragmaInfo dynFlags mbSourceText = @@ -44,7 +48,7 @@ getNextPragmaInfo dynFlags mbSourceText = -- https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6156 showExtension :: Extension -> Text showExtension NamedFieldPuns = "NamedFieldPuns" -showExtension ext = pack (show ext) +showExtension ext = pack (show ext) insertNewPragma :: NextPragmaInfo -> Extension -> LSP.TextEdit insertNewPragma (NextPragmaInfo _ (Just (LineSplitTextEdits ins _))) newPragma = ins & L.newText .~ "{-# LANGUAGE " <> showExtension newPragma <> " #-}\n" :: LSP.TextEdit @@ -53,7 +57,7 @@ insertNewPragma (NextPragmaInfo nextPragmaLine _) newPragma = LSP.TextEdit prag pragmaInsertPosition = LSP.Position (fromIntegral nextPragmaLine) 0 pragmaInsertRange = LSP.Range pragmaInsertPosition pragmaInsertPosition -getFirstPragma :: MonadIO m => PluginId -> IdeState -> NormalizedFilePath -> ExceptT PluginError m NextPragmaInfo +getFirstPragma :: MonadIO m => PluginId -> IdeState -> Path Abs NormalizedFilePath -> ExceptT PluginError m NextPragmaInfo getFirstPragma (PluginId pId) state nfp = do (hscEnv -> hsc_dflags -> sessionDynFlags, _) <- runActionE (T.unpack pId <> ".GhcSession") state $ useWithStaleE GhcSession nfp (_, fileContents) <- liftIO $ runAction (T.unpack pId <> ".GetFileContents") state $ getFileContents nfp diff --git a/ghcide/src/Development/IDE/Types/Diagnostics.hs b/ghcide/src/Development/IDE/Types/Diagnostics.hs index 8189ff89c1..46f071e9c5 100644 --- a/ghcide/src/Development/IDE/Types/Diagnostics.hs +++ b/ghcide/src/Development/IDE/Types/Diagnostics.hs @@ -20,6 +20,7 @@ import Data.ByteString (ByteString) import Data.Maybe as Maybe import qualified Data.Text as T import Development.IDE.Types.Location +import Development.IDE.Types.Path import Language.LSP.Diagnostics import Language.LSP.Protocol.Types as LSP (Diagnostic (..), DiagnosticSeverity (..)) @@ -44,7 +45,7 @@ type IdeResult v = ([FileDiagnostic], Maybe v) -- | an IdeResult with a fingerprint type IdeResultNoDiagnosticsEarlyCutoff v = (Maybe ByteString, Maybe v) -ideErrorText :: NormalizedFilePath -> T.Text -> FileDiagnostic +ideErrorText :: Path Abs NormalizedFilePath -> T.Text -> FileDiagnostic ideErrorText = ideErrorWithSource (Just "compiler") (Just DiagnosticSeverity_Error) ideErrorWithSource @@ -86,7 +87,7 @@ instance NFData ShowDiagnostic where -- along with the related source location so that we can display the error -- on either the console or in the IDE at the right source location. -- -type FileDiagnostic = (NormalizedFilePath, ShowDiagnostic, Diagnostic) +type FileDiagnostic = (Path Abs NormalizedFilePath, ShowDiagnostic, Diagnostic) prettyRange :: Range -> Doc Terminal.AnsiStyle prettyRange Range{..} = f _start <> "-" <> f _end @@ -108,7 +109,7 @@ prettyDiagnostics = vcat . map prettyDiagnostic prettyDiagnostic :: FileDiagnostic -> Doc Terminal.AnsiStyle prettyDiagnostic (fp, sh, LSP.Diagnostic{..}) = vcat - [ slabel_ "File: " $ pretty (fromNormalizedFilePath fp) + [ slabel_ "File: " $ prettyAbsPath fp , slabel_ "Hidden: " $ if sh == ShowDiag then "no" else "yes" , slabel_ "Range: " $ prettyRange _range , slabel_ "Source: " $ pretty _source diff --git a/ghcide/src/Development/IDE/Types/KnownTargets.hs b/ghcide/src/Development/IDE/Types/KnownTargets.hs index 6ae6d52ba3..cb6b9a843e 100644 --- a/ghcide/src/Development/IDE/Types/KnownTargets.hs +++ b/ghcide/src/Development/IDE/Types/KnownTargets.hs @@ -16,11 +16,12 @@ import qualified Data.HashSet as HSet import Development.IDE.GHC.Compat (ModuleName) import Development.IDE.GHC.Orphans () import Development.IDE.Types.Location +import Development.IDE.Types.Path import GHC.Generics -- | A mapping of module name to known files data KnownTargets = KnownTargets - { targetMap :: !(HashMap Target (HashSet NormalizedFilePath)) + { targetMap :: !(HashMap Target (HashSet (Path Abs NormalizedFilePath))) -- | 'normalisingMap' is a cached copy of `HMap.mapKey const targetMap` -- -- At startup 'GetLocatedImports' is called on all known files. Say you have 10000 @@ -48,7 +49,7 @@ unionKnownTargets :: KnownTargets -> KnownTargets -> KnownTargets unionKnownTargets (KnownTargets tm nm) (KnownTargets tm' nm') = KnownTargets (HMap.unionWith (<>) tm tm') (HMap.union nm nm') -mkKnownTargets :: [(Target, HashSet NormalizedFilePath)] -> KnownTargets +mkKnownTargets :: [(Target, HashSet (Path Abs NormalizedFilePath))] -> KnownTargets mkKnownTargets vs = KnownTargets (HMap.fromList vs) (HMap.fromList [(k,k) | (k,_) <- vs ]) instance NFData KnownTargets where @@ -63,9 +64,9 @@ instance Hashable KnownTargets where emptyKnownTargets :: KnownTargets emptyKnownTargets = KnownTargets HMap.empty HMap.empty -data Target = TargetModule ModuleName | TargetFile NormalizedFilePath +data Target = TargetModule ModuleName | TargetFile (Path Abs NormalizedFilePath) deriving ( Eq, Ord, Generic, Show ) deriving anyclass (Hashable, NFData) -toKnownFiles :: KnownTargets -> HashSet NormalizedFilePath +toKnownFiles :: KnownTargets -> HashSet (Path Abs NormalizedFilePath) toKnownFiles = HSet.unions . HMap.elems . targetMap diff --git a/ghcide/src/Development/IDE/Types/Path.hs b/ghcide/src/Development/IDE/Types/Path.hs new file mode 100644 index 0000000000..f20a3763b4 --- /dev/null +++ b/ghcide/src/Development/IDE/Types/Path.hs @@ -0,0 +1,45 @@ +module Development.IDE.Types.Path + (Abs, Rel, mkAbsPath, Path, fromAbsPath, mkAbsFromFp, prettyAbsPath, absToUri, emptyAbsPath, removeSuffix, addSuffix) +where + +import Control.DeepSeq +import Control.Exception +import Data.Hashable +import Data.List.Extra as L +import Development.IDE.Types.Location +import qualified Language.LSP.Protocol.Types as LSP + +import Prettyprinter.Internal +import Prettyprinter.Render.Terminal as Terminal +import System.FilePath + +data Abs +data Rel + +newtype Path a b = Path { getRawPath :: b } deriving (Eq, Show, Ord, Hashable, NFData) + +prettyAbsPath :: Path Abs NormalizedFilePath -> Doc Terminal.AnsiStyle +prettyAbsPath (Path x) = pretty (show x) + +-- | TODO: guarantee that path is absolute +mkAbsPath :: NormalizedFilePath -> Path Abs NormalizedFilePath +mkAbsPath path = Path path + +mkAbsFromFp :: FilePath -> Path Abs NormalizedFilePath +mkAbsFromFp path = assert (isAbsolute path) (mkAbsPath $ toNormalizedFilePath' path) + +fromAbsPath :: Path Abs NormalizedFilePath -> FilePath +fromAbsPath = fromNormalizedFilePath . getRawPath + +absToUri :: Path Abs NormalizedFilePath -> LSP.NormalizedUri +absToUri = LSP.normalizedFilePathToUri . getRawPath + +emptyAbsPath :: Path Abs NormalizedFilePath +emptyAbsPath = mkAbsPath LSP.emptyNormalizedFilePath + +-- | remove last suffix of length s from supplied path +removeSuffix :: Path Abs NormalizedFilePath -> Int -> Path Abs NormalizedFilePath +removeSuffix (Path f) s = mkAbsFromFp $ L.dropEnd s $ fromNormalizedFilePath f + +addSuffix :: Path Abs NormalizedFilePath -> String -> Path Abs NormalizedFilePath +addSuffix (Path f) s = mkAbsFromFp $ fromNormalizedFilePath f ++ s diff --git a/ghcide/src/Development/IDE/Types/Shake.hs b/ghcide/src/Development/IDE/Types/Shake.hs index 2083625c43..cdeaca2dda 100644 --- a/ghcide/src/Development/IDE/Types/Shake.hs +++ b/ghcide/src/Development/IDE/Types/Shake.hs @@ -29,6 +29,7 @@ import Development.IDE.Graph (Key, RuleResult, newKey, import qualified Development.IDE.Graph as Shake import Development.IDE.Types.Diagnostics import Development.IDE.Types.Location +import Development.IDE.Types.Path import GHC.Generics import HieDb.Types (HieDb) import qualified StmContainers.Map as STM @@ -76,16 +77,16 @@ isBadDependency x | Just (_ :: BadDependency) <- fromException x = True | otherwise = False -toKey :: Shake.ShakeValue k => k -> NormalizedFilePath -> Key +toKey :: Shake.ShakeValue k => k -> Path Abs NormalizedFilePath -> Key toKey = (newKey.) . curry Q -fromKey :: Typeable k => Key -> Maybe (k, NormalizedFilePath) +fromKey :: Typeable k => Key -> Maybe (k, Path Abs NormalizedFilePath) fromKey (Key k) | Just (Q (k', f)) <- cast k = Just (k', f) | otherwise = Nothing -- | fromKeyType (Q (k,f)) = (typeOf k, f) -fromKeyType :: Key -> Maybe (SomeTypeRep, NormalizedFilePath) +fromKeyType :: Key -> Maybe (SomeTypeRep, Path Abs NormalizedFilePath) fromKeyType (Key k) = case typeOf k of App (Con tc) a | tc == typeRepTyCon (typeRep @Q) -> case unsafeCoerce k of @@ -93,13 +94,13 @@ fromKeyType (Key k) = case typeOf k of _ -> Nothing toNoFileKey :: (Show k, Typeable k, Eq k, Hashable k) => k -> Key -toNoFileKey k = newKey $ Q (k, emptyFilePath) +toNoFileKey k = newKey $ Q (k, mkAbsPath emptyFilePath) -newtype Q k = Q (k, NormalizedFilePath) +newtype Q k = Q (k, Path Abs NormalizedFilePath) deriving newtype (Eq, Hashable, NFData) instance Show k => Show (Q k) where - show (Q (k, file)) = show k ++ "; " ++ fromNormalizedFilePath file + show (Q (k, file)) = show k ++ "; " ++ fromAbsPath file -- | Invariant: the 'v' must be in normal form (fully evaluated). -- Otherwise we keep repeatedly 'rnf'ing values taken from the Shake database diff --git a/ghcide/test/exe/Progress.hs b/ghcide/test/exe/Progress.hs index 08ad03c78b..33dae48f45 100644 --- a/ghcide/test/exe/Progress.hs +++ b/ghcide/test/exe/Progress.hs @@ -6,6 +6,7 @@ import Data.Foldable (for_) import qualified Data.HashMap.Strict as Map import Development.IDE (NormalizedFilePath) import Development.IDE.Core.ProgressReporting +import Development.IDE.Types.Path import qualified "list-t" ListT import qualified StmContainers.Map as STM import Test.Tasty @@ -18,7 +19,7 @@ tests = testGroup "Progress" data InProgressModel = InProgressModel { done, todo :: Int, - current :: Map.HashMap NormalizedFilePath Int + current :: Map.HashMap (Path Abs NormalizedFilePath) Int } reportProgressTests :: TestTree @@ -30,10 +31,10 @@ reportProgressTests = testGroup "recordProgress" ] where p0 = pure $ InProgressModel 0 0 mempty - addNew = recordProgressModel "A" succ p0 - increase = recordProgressModel "A" succ addNew - decrease = recordProgressModel "A" succ increase - done = recordProgressModel "A" pred decrease + addNew = recordProgressModel (mkAbsPath "A") succ p0 + increase = recordProgressModel (mkAbsPath "A") succ addNew + decrease = recordProgressModel (mkAbsPath "A") succ increase + done = recordProgressModel (mkAbsPath "A") pred decrease recordProgressModel key change state = model state $ \st -> recordProgress st key change model stateModelIO k = do diff --git a/ghcide/test/exe/UnitTests.hs b/ghcide/test/exe/UnitTests.hs index 68e6f3e1f0..32b6fc69d8 100644 --- a/ghcide/test/exe/UnitTests.hs +++ b/ghcide/test/exe/UnitTests.hs @@ -13,6 +13,7 @@ import Development.IDE.Core.FileStore (getModTime) import qualified Development.IDE.Plugin.HLS.GhcIde as Ghcide import qualified Development.IDE.Types.Diagnostics as Diagnostics import Development.IDE.Types.Location +import Development.IDE.Types.Path import qualified FuzzySearch import Ide.Logger (Recorder, WithPriority) import Ide.PluginUtils (pluginDescToIdePlugins) @@ -51,7 +52,7 @@ tests = do let uri = Uri "file://" uriToFilePath' uri @?= Just "" , testCase "showDiagnostics prints ranges 1-based (like vscode)" $ do - let diag = ("", Diagnostics.ShowDiag, Diagnostic + let diag = (mkAbsPath "", Diagnostics.ShowDiag, Diagnostic { _codeDescription = Nothing , _data_ = Nothing , _range = Range diff --git a/hls-plugin-api/src/Ide/Plugin/Error.hs b/hls-plugin-api/src/Ide/Plugin/Error.hs index b323079aff..7f8a3a102b 100644 --- a/hls-plugin-api/src/Ide/Plugin/Error.hs +++ b/hls-plugin-api/src/Ide/Plugin/Error.hs @@ -18,6 +18,7 @@ import Ide.Logger import Ide.Plugin.HandleRequestTypes (RejectionReason) import Language.LSP.Protocol.Types + -- ---------------------------------------------------------------------------- -- Plugin Error wrapping -- ---------------------------------------------------------------------------- @@ -141,7 +142,7 @@ handleMaybe msg = maybe (throwE msg) return handleMaybeM :: Monad m => e -> m (Maybe b) -> ExceptT e m b handleMaybeM msg act = maybeM (throwE msg) return $ lift act -getNormalizedFilePathE :: Monad m => Uri -> ExceptT PluginError m NormalizedFilePath +getNormalizedFilePathE :: Monad m => Uri -> ExceptT PluginError m (NormalizedFilePath) getNormalizedFilePathE uri = handleMaybe (PluginInvalidParams (T.pack $ "uriToNormalizedFile failed. Uri:" <> show uri)) $ uriToNormalizedFilePath $ toNormalizedUri uri