ghci-quickfix 0.1.0.0 → 0.1.1.0
raw patch · 4 files changed
+44/−29 lines, 4 filesdep ~basedep ~ghcPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, ghc
API changes (from Hackage documentation)
Files
- CHANGELOG.md +6/−0
- ghci-quickfix.cabal +5/−5
- src/GhciQuickfix.hs +33/−21
- src/GhciQuickfix/GhcFacade.hs +0/−3
CHANGELOG.md view
@@ -1,5 +1,11 @@ # Revision history for ghci-quickfix +## 0.1.1.0 -- 2026-05-13++* Support GHC 9.14+* Drop support for 9.6+* Sort errors in quickfix file by order of emission+ ## 0.1.0.0 -- 2026-01-09 * First version. Released on an unsuspecting world.
ghci-quickfix.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: ghci-quickfix-version: 0.1.0.0+version: 0.1.1.0 synopsis: GHC plugin that writes errors to a file for use with quickfix description: GHC plugin that writes errors to a file for use with vim/nvim's quickfix system license: BSD-3-Clause@@ -12,7 +12,7 @@ build-type: Simple extra-doc-files: CHANGELOG.md README.md-tested-with: GHC == 9.12.1, GHC == 9.10.1, GHC == 9.8.1, GHC == 9.6.1+tested-with: GHC == 9.14.1, GHC == 9.12.1, GHC == 9.10.1, GHC == 9.8.1 -- extra-source-files: source-repository head@@ -27,8 +27,8 @@ exposed-modules: GhciQuickfix other-modules: GhciQuickfix.GhcFacade -- other-extensions:- build-depends: base < 4.23,- ghc >= 9.6 && < 9.13,+ build-depends: base < 4.24,+ ghc >= 9.8 && < 9.15, text, async >= 2.2 && < 3, stm-containers >= 1.2 && < 1.3,@@ -47,7 +47,7 @@ type: exitcode-stdio-1.0 hs-source-dirs: test main-is: Main.hs- ghc-options: -threaded -with-rtsopts=-N+ -- ghc-options: -threaded -rtsopts -with-rtsopts=-N build-depends: base, ghci-quickfix,
src/GhciQuickfix.hs view
@@ -18,9 +18,10 @@ import Data.Either (partitionEithers) import Data.Foldable import Data.IORef-import Data.List (stripPrefix)+import qualified Data.List as List import Data.Maybe import Data.Monoid (First(..))+import qualified Data.Ord as Ord import qualified Data.Text as T import qualified Data.Text.Encoding as TE import qualified Data.Text.IO as TIO@@ -32,7 +33,11 @@ import qualified GhciQuickfix.GhcFacade as Ghc -type ErrMap = SM.Map FilePath [T.Text]+-- | STM Map is used to avoid contention between modules compiled in parrallel,+-- although it isn't clear if it actually helps since contention will still+-- happen with the STVars used in the same transaction.+-- The Int paired with the errors is used for ordering.+type ErrMap = SM.Map FilePath (Int, [T.Text]) plugin :: Ghc.Plugin plugin = Ghc.defaultPlugin@@ -54,7 +59,11 @@ writeTVar updated False DF.foldM (F.generalize F.list) (SM.unfoldlM errMap) prunedMsgs <- pruneDeletedFiles msgs errMap- TIO.writeFile (fromMaybe "errors.err" mErrFilePath) $ T.unlines prunedMsgs+ TIO.writeFile (fromMaybe "errors.err" mErrFilePath)+ . T.unlines . foldMap snd+ -- Sort in reverse so that the first error in the list is the last one to+ -- be emitted by the compiler+ $ List.sortOn Ord.Down prunedMsgs threadDelay 200_000 -- 200ms parseFilePathModifier :: [Ghc.CommandLineOption] -> IO (Either String [T.Text -> T.Text])@@ -73,13 +82,13 @@ mPat <- Env.lookupEnv "GHCI_QUICKFIX_PATH_REPLACE" pure $ parseReplacement "GHCI_QUICKFIX_PATH_REPLACE environment variable" <$> mPat getModifier opt = do- pat <- stripPrefix "--quickfix-path-replace=" opt+ pat <- List.stripPrefix "--quickfix-path-replace=" opt Just $ parseReplacement "--quickfix-path-replace argument" pat parseQuickfixFilePath :: [Ghc.CommandLineOption] -> IO (Maybe FilePath) parseQuickfixFilePath opts = do envPath <- Env.lookupEnv "GHCI_QUICKFIX_FILE"- pure $ getFirst $ foldMap (First . stripPrefix "--quickfix-file=") opts <> First envPath+ pure $ getFirst $ foldMap (First . List.stripPrefix "--quickfix-file=") opts <> First envPath parseIncludeParserErrors :: [Ghc.CommandLineOption] -> IO Bool parseIncludeParserErrors opts = do@@ -102,14 +111,15 @@ Right filePathMods -> do errMap <- SM.newIO errsUpdated <- newTVarIO False+ counter <- newTVarIO 0 quickfixFilePath <- parseQuickfixFilePath opts void . Async.async $ writeQuickfixLoop quickfixFilePath errMap errsUpdated includeParserErrors <- parseIncludeParserErrors opts- pure hscEnv { Ghc.hsc_hooks = modifyHooks includeParserErrors filePathMods (Ghc.hsc_hooks hscEnv) errMap errsUpdated }+ pure hscEnv { Ghc.hsc_hooks = modifyHooks includeParserErrors filePathMods (Ghc.hsc_hooks hscEnv) errMap errsUpdated counter } else pure hscEnv where- modifyHooks includeParserErrors filePathMods hooks (errMap :: ErrMap) (errsUpdated :: TVar Bool) =+ modifyHooks includeParserErrors filePathMods hooks (errMap :: ErrMap) (errsUpdated :: TVar Bool) (counter :: TVar Int) = let runPhaseOrExistingHook :: Ghc.TPhase res -> IO res runPhaseOrExistingHook = maybe Ghc.runPhase (\(Ghc.PhaseHook h) -> h) $ Ghc.runPhaseHook hooks@@ -122,7 +132,7 @@ dsWarnVar <- newIORef mempty try (runPhaseOrExistingHook $ addDsLogHook (logHookHack dsWarnVar hscEnv) phase) >>= \case Left err@(Ghc.SourceError msgs) -> do- handleMessages includeParserErrors filePathMods errMap errsUpdated msgs+ handleMessages includeParserErrors filePathMods errMap errsUpdated counter msgs throw err Right res -> do dsWarns <- readIORef dsWarnVar@@ -137,7 +147,7 @@ Just _ -> do SM.delete (Ghc.ms_hspp_file modSummary) errMap writeTVar errsUpdated True- else handleMessages includeParserErrors filePathMods errMap errsUpdated $+ else handleMessages includeParserErrors filePathMods errMap errsUpdated counter $ if length tcWarnings == length dsWarns then tcWarnings -- has preferred formatting else dsWarns@@ -192,8 +202,15 @@ <> severity <> ": " <> msg -- | Update state given all diagnostics for a module-handleMessages :: Bool -> [T.Text -> T.Text] -> ErrMap -> TVar Bool -> Ghc.Messages Ghc.GhcMessage -> IO ()-handleMessages includeParserErrors filePathMods errMap errsUpdated messages = do+handleMessages+ :: Bool+ -> [T.Text -> T.Text]+ -> ErrMap+ -> TVar Bool+ -> TVar Int+ -> Ghc.Messages Ghc.GhcMessage+ -> IO ()+handleMessages includeParserErrors filePathMods errMap errsUpdated counter messages = do let envelopes = Ghc.getMessages messages isParseError = \case Ghc.GhcPsMessage{} -> True@@ -207,11 +224,12 @@ (First . fmap Ghc.unpackFS . Ghc.srcSpanFileName_maybe . Ghc.errMsgSpan) $ Ghc.getMessages messages for_ mFile $ \file -> atomically $ do- SM.insert errs file errMap+ n <- stateTVar counter (\x -> let !nx = x + 1 in (x, nx))+ SM.insert (n, errs) file errMap writeTVar errsUpdated True -- | Remove errors for files that no longer exist-pruneDeletedFiles :: [(FilePath, [T.Text])] -> ErrMap -> IO [T.Text]+pruneDeletedFiles :: [(FilePath, (Int, [T.Text]))] -> ErrMap -> IO [(Int, [T.Text])] pruneDeletedFiles errs errMap = do let files = fst <$> errs deletedFiles <- fmap catMaybes $@@ -220,7 +238,7 @@ True -> pure Nothing False -> pure (Just file) atomically $ traverse_ (`SM.delete` errMap) deletedFiles- pure . foldMap snd $ filter (not . (`elem` deletedFiles) . fst) errs+ pure . fmap snd $ filter (not . (`elem` deletedFiles) . fst) errs -- | Currently no good way to get warnings from desugarer, so a log action hook -- is used to get the raw SDoc. Note: unfortunately this will also capture@@ -236,13 +254,7 @@ , Ghc.diagHints = [] } diagOpts = Ghc.initDiagOpts $ Ghc.hsc_dflags hscEnv- mkUnknownDiag =-#if MIN_VERSION_ghc(9,8,0)- Ghc.UnknownDiagnostic id-#else- Ghc.UnknownDiagnostic-#endif- ghcMessage = Ghc.GhcDsMessage . Ghc.DsUnknownMessage $ mkUnknownDiag diag+ ghcMessage = Ghc.GhcDsMessage . Ghc.DsUnknownMessage $ Ghc.mkUnknownDiagnostic diag warn = Ghc.mkMsgEnvelope diagOpts srcSpan Ghc.neverQualify ghcMessage modifyIORef dsWarnVar (Ghc.addMessage warn) _ -> pure ()
src/GhciQuickfix/GhcFacade.hs view
@@ -22,6 +22,3 @@ import GHC.Utils.Logger as Ghc import GHC.Driver.Config.Diagnostic as Ghc import GHC.HsToCore.Errors.Types as Ghc-#if !MIN_VERSION_ghc(9,8,0)-import GHC.Tc.Errors.Ppr as Ghc-#endif