From cdc8f78a9852ed35cbfccc191a0b87f59dc9e271 Mon Sep 17 00:00:00 2001 From: Stefan Matting Date: Sun, 26 Jun 2022 13:39:07 +0200 Subject: [PATCH 001/213] Turn HLS-wrapper into an LSP Server (#2960) * Make wrapper a LSP on failure * Fix incorrect imports * revert import block for smaller diff * add missing imports * Fix: callProcess on win32 machines not called * import container only on win32 * add missing liftIO Co-authored-by: Pepe Iborra --- exe/Wrapper.hs | 215 ++++++++++++--- .../src/Development/IDE/LSP/LanguageServer.hs | 255 ++++++++++-------- ghcide/src/Development/IDE/LSP/Server.hs | 8 +- ghcide/src/Development/IDE/Main.hs | 100 +++---- haskell-language-server.cabal | 7 + 5 files changed, 385 insertions(+), 200 deletions(-) diff --git a/exe/Wrapper.hs b/exe/Wrapper.hs index b721a4f3f5..23cc153215 100644 --- a/exe/Wrapper.hs +++ b/exe/Wrapper.hs @@ -1,6 +1,12 @@ -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE CPP #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE ExplicitNamespaces #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} -- | This module is based on the hie-wrapper.sh script in -- https://github.com/alanz/vscode-hie-server module Main where @@ -28,6 +34,28 @@ import qualified Data.Map.Strict as Map #else import System.Process #endif +import qualified Data.Text.IO as T +import Control.Monad.Trans.Except (ExceptT, runExceptT, throwE) +import qualified Data.Text as T +import Language.LSP.Server (LspM) +import Control.Monad.IO.Class (MonadIO (liftIO)) +import Control.Monad.IO.Unlift (MonadUnliftIO) +import qualified Language.LSP.Server as LSP +import qualified Development.IDE.Main as Main +import Ide.Plugin.Config (Config) +import Language.LSP.Types (RequestMessage, ResponseError, MessageActionItem (MessageActionItem), Method(Initialize), MessageType (MtError), SMethod (SWindowShowMessageRequest, SExit), ShowMessageRequestParams (ShowMessageRequestParams)) +import Development.IDE.Types.Logger ( makeDefaultStderrRecorder, + cmapWithPrio, + Pretty(pretty), + Logger(Logger), + Priority(Error, Debug, Info, Warning), + Recorder(logger_), + WithPriority(WithPriority) ) +import Data.Maybe +import GHC.Stack.Types (emptyCallStack) +import Control.Concurrent (tryPutMVar) +import Development.IDE.LSP.LanguageServer (runLanguageServer) +import HIE.Bios.Internal.Log -- --------------------------------------------------------------------- @@ -57,9 +85,15 @@ main = do cradle <- findProjectCradle' False (CradleSuccess libdir) <- HieBios.getRuntimeGhcLibDir cradle putStr libdir - _ -> launchHaskellLanguageServer args + _ -> launchHaskellLanguageServer args >>= \case + Right () -> pure () + Left err -> do + T.hPutStrLn stderr (prettyError err NoShorten) + case args of + Ghcide _ -> launchErrorLSP (prettyError err Shorten) + _ -> pure () -launchHaskellLanguageServer :: Arguments -> IO () +launchHaskellLanguageServer :: Arguments -> IO (Either WrapperSetupError ()) launchHaskellLanguageServer parsedArgs = do case parsedArgs of Ghcide GhcideArguments{..} -> whenJust argsCwd setCurrentDirectory @@ -75,7 +109,10 @@ launchHaskellLanguageServer parsedArgs = do case parsedArgs of Ghcide GhcideArguments{..} -> - when argsProjectGhcVersion $ getRuntimeGhcVersion' cradle >>= putStrLn >> exitSuccess + when argsProjectGhcVersion $ do + runExceptT (getRuntimeGhcVersion' cradle) >>= \case + Right ghcVersion -> putStrLn ghcVersion >> exitSuccess + Left err -> T.putStrLn (prettyError err NoShorten) >> exitFailure _ -> pure () progName <- getProgName @@ -94,64 +131,74 @@ launchHaskellLanguageServer parsedArgs = do hPutStrLn stderr "" -- Get the ghc version -- this might fail! hPutStrLn stderr "Consulting the cradle to get project GHC version..." - ghcVersion <- getRuntimeGhcVersion' cradle - hPutStrLn stderr $ "Project GHC version: " ++ ghcVersion - let - hlsBin = "haskell-language-server-" ++ ghcVersion - candidates' = [hlsBin, "haskell-language-server"] - candidates = map (++ exeExtension) candidates' + runExceptT $ do + ghcVersion <- getRuntimeGhcVersion' cradle + liftIO $ hPutStrLn stderr $ "Project GHC version: " ++ ghcVersion - hPutStrLn stderr $ "haskell-language-server exe candidates: " ++ show candidates + let + hlsBin = "haskell-language-server-" ++ ghcVersion + candidates' = [hlsBin, "haskell-language-server"] + candidates = map (++ exeExtension) candidates' - mexes <- traverse findExecutable candidates + liftIO $ hPutStrLn stderr $ "haskell-language-server exe candidates: " ++ show candidates + + mexes <- liftIO $ traverse findExecutable candidates + + case asum mexes of + Nothing -> throwE (NoLanguageServer ghcVersion candidates) + Just e -> do + liftIO $ hPutStrLn stderr $ "Launching haskell-language-server exe at:" ++ e - case asum mexes of - Nothing -> die $ "Cannot find any haskell-language-server exe, looked for: " ++ intercalate ", " candidates - Just e -> do - hPutStrLn stderr $ "Launching haskell-language-server exe at:" ++ e #ifdef mingw32_HOST_OS - callProcess e args + liftIO $ callProcess e args #else - let Cradle { cradleOptsProg = CradleAction { runGhcCmd } } = cradle - -- we need to be compatible with NoImplicitPrelude - ghcBinary <- (fmap trim <$> runGhcCmd ["-v0", "-package-env=-", "-ignore-dot-ghci", "-e", "Control.Monad.join (Control.Monad.fmap System.IO.putStr System.Environment.getExecutablePath)"]) - >>= cradleResult "Failed to get project GHC executable path" - libdir <- HieBios.getRuntimeGhcLibDir cradle - >>= cradleResult "Failed to get project GHC libdir path" - env <- Map.fromList <$> getEnvironment - let newEnv = Map.insert "GHC_BIN" ghcBinary $ Map.insert "GHC_LIBDIR" libdir env - executeFile e True args (Just (Map.toList newEnv)) + + let Cradle { cradleOptsProg = CradleAction { runGhcCmd } } = cradle + + let cradleName = actionName (cradleOptsProg cradle) + -- we need to be compatible with NoImplicitPrelude + ghcBinary <- liftIO (fmap trim <$> runGhcCmd ["-v0", "-package-env=-", "-ignore-dot-ghci", "-e", "Control.Monad.join (Control.Monad.fmap System.IO.putStr System.Environment.getExecutablePath)"]) + >>= cradleResult cradleName + + libdir <- liftIO (HieBios.getRuntimeGhcLibDir cradle) + >>= cradleResult cradleName + + env <- Map.fromList <$> liftIO getEnvironment + let newEnv = Map.insert "GHC_BIN" ghcBinary $ Map.insert "GHC_LIBDIR" libdir env + liftIO $ executeFile e True args (Just (Map.toList newEnv)) #endif -cradleResult :: String -> CradleLoadResult a -> IO a -cradleResult _ (CradleSuccess a) = pure a -cradleResult str (CradleFail e) = die $ str ++ ": " ++ show e -cradleResult str CradleNone = die $ str ++ ": no cradle" + +cradleResult :: ActionName Void -> CradleLoadResult a -> ExceptT WrapperSetupError IO a +cradleResult _ (CradleSuccess ver) = pure ver +cradleResult cradleName (CradleFail error) = throwE $ FailedToObtainGhcVersion cradleName error +cradleResult cradleName CradleNone = throwE $ NoneCradleGhcVersion cradleName -- | Version of 'getRuntimeGhcVersion' that dies if we can't get it, and also -- checks to see if the tool is missing if it is one of -getRuntimeGhcVersion' :: Show a => Cradle a -> IO String +getRuntimeGhcVersion' :: Cradle Void -> ExceptT WrapperSetupError IO String getRuntimeGhcVersion' cradle = do + let cradleName = actionName (cradleOptsProg cradle) -- See if the tool is installed - case actionName (cradleOptsProg cradle) of + case cradleName of Stack -> checkToolExists "stack" Cabal -> checkToolExists "cabal" Default -> checkToolExists "ghc" Direct -> checkToolExists "ghc" _ -> pure () - HieBios.getRuntimeGhcVersion cradle >>= cradleResult "Failed to get project GHC version" + ghcVersionRes <- liftIO $ HieBios.getRuntimeGhcVersion cradle + cradleResult cradleName ghcVersionRes + where checkToolExists exe = do - exists <- findExecutable exe + exists <- liftIO $ findExecutable exe case exists of Just _ -> pure () - Nothing -> - die $ "Cradle requires " ++ exe ++ " but couldn't find it" ++ "\n" - ++ show cradle + Nothing -> throwE $ ToolRequirementMissing exe (actionName (cradleOptsProg cradle)) findProjectCradle :: IO (Cradle Void) findProjectCradle = findProjectCradle' True @@ -175,3 +222,93 @@ trim :: String -> String trim s = case lines s of [] -> s ls -> dropWhileEnd isSpace $ last ls + +data WrapperSetupError + = FailedToObtainGhcVersion (ActionName Void) CradleError + | NoneCradleGhcVersion (ActionName Void) + | NoLanguageServer String [FilePath] + | ToolRequirementMissing String (ActionName Void) + deriving (Show) + +data Shorten = Shorten | NoShorten + +-- | Pretty error message displayable to the future. +-- Extra argument 'Shorten' can be used to shorten error message. +-- Reduces usefulness, but allows us to show the error message via LSP +-- as LSP doesn't allow any newlines and makes it really hard to read +-- the message otherwise. +prettyError :: WrapperSetupError -> Shorten -> T.Text +prettyError (FailedToObtainGhcVersion name crdlError) shorten = + "Failed to find the GHC version of this " <> T.pack (show name) <> " project." <> + case shorten of + Shorten -> + "\n" <> T.pack (fromMaybe "" . listToMaybe $ cradleErrorStderr crdlError) + NoShorten -> + "\n" <> T.pack (intercalate "\n" (cradleErrorStderr crdlError)) +prettyError (NoneCradleGhcVersion name) _ = + "Failed to get the GHC version of this " <> T.pack (show name) <> + " project because a none cradle is configured" +prettyError (NoLanguageServer ghcVersion candidates) _ = + "Failed to find a HLS version for GHC " <> T.pack ghcVersion <> + "\nExecutable names we failed to find: " <> T.pack (intercalate "," candidates) +prettyError (ToolRequirementMissing toolExe name) _ = + "Failed to find executable \"" <> T.pack toolExe <> "\" in $PATH for this " <> T.pack (show name) <> " project." + +newtype ErrorLSPM c a = ErrorLSPM { unErrorLSPM :: (LspM c) a } + deriving (Functor, Applicative, Monad, MonadIO, MonadUnliftIO, LSP.MonadLsp c) + +-- | Launches a LSP that displays an error and presents the user with a request +-- to shut down the LSP. +launchErrorLSP :: T.Text -> IO () +launchErrorLSP errorMsg = do + recorder <- makeDefaultStderrRecorder Nothing Info + + let logger = Logger $ \p m -> logger_ recorder (WithPriority p emptyCallStack (pretty m)) + + let defaultArguments = Main.defaultArguments (cmapWithPrio pretty recorder) logger + + inH <- Main.argsHandleIn defaultArguments + + outH <- Main.argsHandleOut defaultArguments + + let onConfigurationChange cfg _ = Right cfg + + let setup clientMsgVar = do + -- Forcefully exit + let exit = void $ tryPutMVar clientMsgVar () + + let doInitialize :: LSP.LanguageContextEnv Config -> RequestMessage Initialize -> IO (Either ResponseError (LSP.LanguageContextEnv Config, ())) + doInitialize env _ = do + + let restartTitle = "Try to restart" + void $ LSP.runLspT env $ LSP.sendRequest SWindowShowMessageRequest (ShowMessageRequestParams MtError errorMsg (Just [MessageActionItem restartTitle])) $ \case + Right (Just (MessageActionItem title)) + | title == restartTitle -> liftIO exit + _ -> pure () + + pure (Right (env, ())) + + let asyncHandlers = mconcat + [ exitHandler exit ] + + let interpretHandler (env, _st) = LSP.Iso (LSP.runLspT env . unErrorLSPM) liftIO + pure (doInitialize, asyncHandlers, interpretHandler) + + runLanguageServer + (Main.argsLspOptions defaultArguments) + inH + outH + (Main.argsDefaultHlsConfig defaultArguments) + onConfigurationChange + setup + +exitHandler :: IO () -> LSP.Handlers (ErrorLSPM c) +exitHandler exit = LSP.notificationHandler SExit $ const $ liftIO exit + +hlsWrapperLogger :: Logger +hlsWrapperLogger = Logger $ \pri txt -> + case pri of + Debug -> debugm (T.unpack txt) + Info -> logm (T.unpack txt) + Warning -> warningm (T.unpack txt) + Error -> errorm (T.unpack txt) diff --git a/ghcide/src/Development/IDE/LSP/LanguageServer.hs b/ghcide/src/Development/IDE/LSP/LanguageServer.hs index 97eb1675af..798ea40a68 100644 --- a/ghcide/src/Development/IDE/LSP/LanguageServer.hs +++ b/ghcide/src/Development/IDE/LSP/LanguageServer.hs @@ -5,11 +5,13 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-} +{-# LANGUAGE StarIsType #-} -- WARNING: A copy of DA.Daml.LanguageServer, try to keep them in sync -- This version removes the daml: handling module Development.IDE.LSP.LanguageServer ( runLanguageServer + , setupLSP , Log(..) ) where @@ -38,9 +40,12 @@ import Development.IDE.Core.Tracing import Development.IDE.Types.Logger import Control.Monad.IO.Unlift (MonadUnliftIO) +import Data.Kind (Type) import qualified Development.IDE.Session as Session import qualified Development.IDE.Types.Logger as Logger import Development.IDE.Types.Shake (WithHieDb) +import Language.LSP.Server (LanguageContextEnv, + type (<~>)) import System.IO.Unsafe (unsafeInterleaveIO) data Log @@ -74,71 +79,30 @@ instance Pretty Log where newtype WithHieDbShield = WithHieDbShield WithHieDb runLanguageServer - :: forall config. (Show config) - => Recorder (WithPriority Log) - -> LSP.Options + :: forall config a m. (Show config) + => LSP.Options -> Handle -- input -> Handle -- output - -> (FilePath -> IO FilePath) -- ^ Map root paths to the location of the hiedb for the project -> config -> (config -> Value -> Either T.Text config) - -> LSP.Handlers (ServerM config) - -> (LSP.LanguageContextEnv config -> Maybe FilePath -> WithHieDb -> IndexQueue -> IO IdeState) + -> (MVar () + -> IO (LSP.LanguageContextEnv config -> RequestMessage Initialize -> IO (Either ResponseError (LSP.LanguageContextEnv config, a)), + LSP.Handlers (m config), + (LanguageContextEnv config, a) -> m config <~> IO)) -> IO () -runLanguageServer recorder options inH outH getHieDbLoc defaultConfig onConfigurationChange userHandlers getIdeState = do - +runLanguageServer options inH outH defaultConfig onConfigurationChange setup = do -- This MVar becomes full when the server thread exits or we receive exit message from client. -- LSP server will be canceled when it's full. clientMsgVar <- newEmptyMVar - -- Forcefully exit - let exit = void $ tryPutMVar clientMsgVar () - - -- An MVar to control the lifetime of the reactor loop. - -- The loop will be stopped and resources freed when it's full - reactorLifetime <- newEmptyMVar - let stopReactorLoop = void $ tryPutMVar reactorLifetime () - - -- The set of requests ids that we have received but not finished processing - pendingRequests <- newTVarIO Set.empty - -- The set of requests that have been cancelled and are also in pendingRequests - cancelledRequests <- newTVarIO Set.empty - - let cancelRequest reqId = atomically $ do - queued <- readTVar pendingRequests - -- We want to avoid that the list of cancelled requests - -- keeps growing if we receive cancellations for requests - -- that do not exist or have already been processed. - when (reqId `elem` queued) $ - modifyTVar cancelledRequests (Set.insert reqId) - let clearReqId reqId = atomically $ do - modifyTVar pendingRequests (Set.delete reqId) - modifyTVar cancelledRequests (Set.delete reqId) - -- We implement request cancellation by racing waitForCancel against - -- the actual request handler. - let waitForCancel reqId = atomically $ do - cancelled <- readTVar cancelledRequests - unless (reqId `Set.member` cancelled) retry - - -- Send everything over a channel, since you need to wait until after initialise before - -- LspFuncs is available - clientMsgChan :: Chan ReactorMessage <- newChan - - let asyncHandlers = mconcat - [ userHandlers - , cancelHandler cancelRequest - , exitHandler exit - , shutdownHandler stopReactorLoop - ] - -- Cancel requests are special since they need to be handled - -- out of order to be useful. Existing handlers are run afterwards. + (doInitialize, staticHandlers, interpretHandler) <- setup clientMsgVar let serverDefinition = LSP.ServerDefinition { LSP.onConfigurationChange = onConfigurationChange , LSP.defaultConfig = defaultConfig - , LSP.doInitialize = handleInit reactorLifetime exit clearReqId waitForCancel clientMsgChan - , LSP.staticHandlers = asyncHandlers - , LSP.interpretHandler = \(env, st) -> LSP.Iso (LSP.runLspT env . flip runReaderT (clientMsgChan,st)) liftIO + , LSP.doInitialize = doInitialize + , LSP.staticHandlers = staticHandlers + , LSP.interpretHandler = interpretHandler , LSP.options = modifyOptions options } @@ -148,67 +112,134 @@ runLanguageServer recorder options inH outH getHieDbLoc defaultConfig onConfigur outH serverDefinition +setupLSP :: + forall config err. + Recorder (WithPriority Log) + -> (FilePath -> IO FilePath) -- ^ Map root paths to the location of the hiedb for the project + -> LSP.Handlers (ServerM config) + -> (LSP.LanguageContextEnv config -> Maybe FilePath -> WithHieDb -> IndexQueue -> IO IdeState) + -> MVar () + -> IO (LSP.LanguageContextEnv config -> RequestMessage Initialize -> IO (Either err (LSP.LanguageContextEnv config, IdeState)), + LSP.Handlers (ServerM config), + (LanguageContextEnv config, IdeState) -> ServerM config <~> IO) +setupLSP recorder getHieDbLoc userHandlers getIdeState clientMsgVar = do + -- Send everything over a channel, since you need to wait until after initialise before + -- LspFuncs is available + clientMsgChan :: Chan ReactorMessage <- newChan + + -- An MVar to control the lifetime of the reactor loop. + -- The loop will be stopped and resources freed when it's full + reactorLifetime <- newEmptyMVar + let stopReactorLoop = void $ tryPutMVar reactorLifetime () + + -- Forcefully exit + let exit = void $ tryPutMVar clientMsgVar () + + -- The set of requests ids that we have received but not finished processing + pendingRequests <- newTVarIO Set.empty + -- The set of requests that have been cancelled and are also in pendingRequests + cancelledRequests <- newTVarIO Set.empty + + let cancelRequest reqId = atomically $ do + queued <- readTVar pendingRequests + -- We want to avoid that the list of cancelled requests + -- keeps growing if we receive cancellations for requests + -- that do not exist or have already been processed. + when (reqId `elem` queued) $ + modifyTVar cancelledRequests (Set.insert reqId) + let clearReqId reqId = atomically $ do + modifyTVar pendingRequests (Set.delete reqId) + modifyTVar cancelledRequests (Set.delete reqId) + -- We implement request cancellation by racing waitForCancel against + -- the actual request handler. + let waitForCancel reqId = atomically $ do + cancelled <- readTVar cancelledRequests + unless (reqId `Set.member` cancelled) retry + + let asyncHandlers = mconcat + [ userHandlers + , cancelHandler cancelRequest + , exitHandler exit + , shutdownHandler stopReactorLoop + ] + -- Cancel requests are special since they need to be handled + -- out of order to be useful. Existing handlers are run afterwards. + + let doInitialize = handleInit recorder getHieDbLoc getIdeState reactorLifetime exit clearReqId waitForCancel clientMsgChan + + let interpretHandler (env, st) = LSP.Iso (LSP.runLspT env . flip (runReaderT . unServerM) (clientMsgChan,st)) liftIO + + pure (doInitialize, asyncHandlers, interpretHandler) + + +handleInit + :: Recorder (WithPriority Log) + -> (FilePath -> IO FilePath) + -> (LSP.LanguageContextEnv config -> Maybe FilePath -> WithHieDb -> IndexQueue -> IO IdeState) + -> MVar () + -> IO () + -> (SomeLspId -> IO ()) + -> (SomeLspId -> IO ()) + -> Chan ReactorMessage + -> LSP.LanguageContextEnv config -> RequestMessage Initialize -> IO (Either err (LSP.LanguageContextEnv config, IdeState)) +handleInit recorder getHieDbLoc getIdeState lifetime exitClientMsg clearReqId waitForCancel clientMsgChan env (RequestMessage _ _ m params) = otTracedHandler "Initialize" (show m) $ \sp -> do + traceWithSpan sp params + let root = LSP.resRootPath env + dir <- maybe getCurrentDirectory return root + dbLoc <- getHieDbLoc dir + + -- The database needs to be open for the duration of the reactor thread, but we need to pass in a reference + -- to 'getIdeState', so we use this dirty trick + dbMVar <- newEmptyMVar + ~(WithHieDbShield withHieDb,hieChan) <- unsafeInterleaveIO $ takeMVar dbMVar + + ide <- getIdeState env root withHieDb hieChan + + let initConfig = parseConfiguration params + + log Info $ LogRegisteringIdeConfig initConfig + registerIdeConfiguration (shakeExtras ide) initConfig + + let handleServerException (Left e) = do + log Error $ LogReactorThreadException e + exitClientMsg + handleServerException (Right _) = pure () + + exceptionInHandler e = do + log Error $ LogReactorMessageActionException e + + checkCancelled _id act k = + flip finally (clearReqId _id) $ + catch (do + -- We could optimize this by first checking if the id + -- is in the cancelled set. However, this is unlikely to be a + -- bottleneck and the additional check might hide + -- issues with async exceptions that need to be fixed. + cancelOrRes <- race (waitForCancel _id) act + case cancelOrRes of + Left () -> do + log Debug $ LogCancelledRequest _id + k $ ResponseError RequestCancelled "" Nothing + Right res -> pure res + ) $ \(e :: SomeException) -> do + exceptionInHandler e + k $ ResponseError InternalError (T.pack $ show e) Nothing + _ <- flip forkFinally handleServerException $ do + untilMVar lifetime $ runWithDb (cmapWithPrio LogSession recorder) dbLoc $ \withHieDb hieChan -> do + putMVar dbMVar (WithHieDbShield withHieDb,hieChan) + forever $ do + msg <- readChan clientMsgChan + -- We dispatch notifications synchronously and requests asynchronously + -- This is to ensure that all file edits and config changes are applied before a request is handled + case msg of + ReactorNotification act -> handle exceptionInHandler act + ReactorRequest _id act k -> void $ async $ checkCancelled _id act k + log Info LogReactorThreadStopped + pure $ Right (env,ide) + where - log :: Logger.Priority -> Log -> IO () - log = logWith recorder - - handleInit - :: MVar () -> IO () -> (SomeLspId -> IO ()) -> (SomeLspId -> IO ()) -> Chan ReactorMessage - -> LSP.LanguageContextEnv config -> RequestMessage Initialize -> IO (Either err (LSP.LanguageContextEnv config, IdeState)) - handleInit lifetime exitClientMsg clearReqId waitForCancel clientMsgChan env (RequestMessage _ _ m params) = otTracedHandler "Initialize" (show m) $ \sp -> do - traceWithSpan sp params - let root = LSP.resRootPath env - dir <- maybe getCurrentDirectory return root - dbLoc <- getHieDbLoc dir - - -- The database needs to be open for the duration of the reactor thread, but we need to pass in a reference - -- to 'getIdeState', so we use this dirty trick - dbMVar <- newEmptyMVar - ~(WithHieDbShield withHieDb,hieChan) <- unsafeInterleaveIO $ takeMVar dbMVar - - ide <- getIdeState env root withHieDb hieChan - - let initConfig = parseConfiguration params - - log Info $ LogRegisteringIdeConfig initConfig - registerIdeConfiguration (shakeExtras ide) initConfig - - let handleServerException (Left e) = do - log Error $ LogReactorThreadException e - exitClientMsg - handleServerException (Right _) = pure () - - exceptionInHandler e = do - log Error $ LogReactorMessageActionException e - - checkCancelled _id act k = - flip finally (clearReqId _id) $ - catch (do - -- We could optimize this by first checking if the id - -- is in the cancelled set. However, this is unlikely to be a - -- bottleneck and the additional check might hide - -- issues with async exceptions that need to be fixed. - cancelOrRes <- race (waitForCancel _id) act - case cancelOrRes of - Left () -> do - log Debug $ LogCancelledRequest _id - k $ ResponseError RequestCancelled "" Nothing - Right res -> pure res - ) $ \(e :: SomeException) -> do - exceptionInHandler e - k $ ResponseError InternalError (T.pack $ show e) Nothing - _ <- flip forkFinally handleServerException $ do - untilMVar lifetime $ runWithDb (cmapWithPrio LogSession recorder) dbLoc $ \withHieDb hieChan -> do - putMVar dbMVar (WithHieDbShield withHieDb,hieChan) - forever $ do - msg <- readChan clientMsgChan - -- We dispatch notifications synchronously and requests asynchronously - -- This is to ensure that all file edits and config changes are applied before a request is handled - case msg of - ReactorNotification act -> handle exceptionInHandler act - ReactorRequest _id act k -> void $ async $ checkCancelled _id act k - log Info LogReactorThreadStopped - pure $ Right (env,ide) + log :: Logger.Priority -> Log -> IO () + log = logWith recorder -- | Runs the action until it ends or until the given MVar is put. diff --git a/ghcide/src/Development/IDE/LSP/Server.hs b/ghcide/src/Development/IDE/LSP/Server.hs index 19e438e0da..b47bc46f90 100644 --- a/ghcide/src/Development/IDE/LSP/Server.hs +++ b/ghcide/src/Development/IDE/LSP/Server.hs @@ -10,11 +10,12 @@ module Development.IDE.LSP.Server ( ReactorMessage(..) , ReactorChan - , ServerM + , ServerM(..) , requestHandler , notificationHandler ) where +import Control.Monad.IO.Unlift (MonadUnliftIO) import Control.Monad.Reader import Development.IDE.Core.Shake import Development.IDE.Core.Tracing @@ -30,7 +31,8 @@ data ReactorMessage | ReactorRequest SomeLspId (IO ()) (ResponseError -> IO ()) type ReactorChan = Chan ReactorMessage -type ServerM c = ReaderT (ReactorChan, IdeState) (LspM c) +newtype ServerM c a = ServerM { unServerM :: ReaderT (ReactorChan, IdeState) (LspM c) a } + deriving (Functor, Applicative, Monad, MonadReader (ReactorChan, IdeState), MonadIO, MonadUnliftIO, LSP.MonadLsp c) requestHandler :: forall (m :: Method FromClient Request) c. (HasTracing (MessageParams m)) => @@ -40,7 +42,7 @@ requestHandler requestHandler m k = LSP.requestHandler m $ \RequestMessage{_method,_id,_params} resp -> do st@(chan,ide) <- ask env <- LSP.getLspEnv - let resp' = flip runReaderT st . resp + let resp' = flip (runReaderT . unServerM) st . resp trace x = otTracedHandler "Request" (show _method) $ \sp -> do traceWithSpan sp _params x diff --git a/ghcide/src/Development/IDE/Main.hs b/ghcide/src/Development/IDE/Main.hs index aa62c60ecf..585a2badb7 100644 --- a/ghcide/src/Development/IDE/Main.hs +++ b/ghcide/src/Development/IDE/Main.hs @@ -1,5 +1,6 @@ {-# LANGUAGE PackageImports #-} {-# OPTIONS_GHC -Wno-orphans #-} +{-# LANGUAGE RankNTypes #-} module Development.IDE.Main (Arguments(..) ,defaultArguments @@ -57,13 +58,15 @@ import Development.IDE.Core.Service (initialise, runAction) import qualified Development.IDE.Core.Service as Service import Development.IDE.Core.Shake (IdeState (shakeExtras), + IndexQueue, ShakeExtras (state), shakeSessionInit, uses) import qualified Development.IDE.Core.Shake as Shake import Development.IDE.Core.Tracing (measureMemory) import Development.IDE.Graph (action) -import Development.IDE.LSP.LanguageServer (runLanguageServer) +import Development.IDE.LSP.LanguageServer (runLanguageServer, + setupLSP) import qualified Development.IDE.LSP.LanguageServer as LanguageServer import Development.IDE.Main.HeapStats (withHeapStats) import qualified Development.IDE.Main.HeapStats as HeapStats @@ -98,7 +101,8 @@ import Development.IDE.Types.Options (IdeGhcSession, defaultIdeOptions, optModifyDynFlags, optTesting) -import Development.IDE.Types.Shake (fromKeyType) +import Development.IDE.Types.Shake (WithHieDb, + fromKeyType) import GHC.Conc (getNumProcessors) import GHC.IO.Encoding (setLocaleEncoding) import GHC.IO.Handle (hDuplicate) @@ -300,7 +304,6 @@ testing recorder logger = , argsIdeOptions = ideOptions } - defaultMain :: Recorder (WithPriority Log) -> Arguments -> IO () defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats recorder) fun where @@ -335,49 +338,54 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re t <- offsetTime log Info LogLspStart - runLanguageServer (cmapWithPrio LogLanguageServer recorder) options inH outH argsGetHieDbLoc argsDefaultHlsConfig argsOnConfigChange (pluginHandlers plugins) $ \env rootPath withHieDb hieChan -> do - traverse_ IO.setCurrentDirectory rootPath - t <- t - log Info $ LogLspStartDuration t - - dir <- maybe IO.getCurrentDirectory return rootPath - - -- We want to set the global DynFlags right now, so that we can use - -- `unsafeGlobalDynFlags` even before the project is configured - _mlibdir <- - setInitialDynFlags (cmapWithPrio LogSession recorder) dir argsSessionLoadingOptions - -- TODO: should probably catch/log/rethrow at top level instead - `catchAny` (\e -> log Error (LogSetInitialDynFlagsException e) >> pure Nothing) - - sessionLoader <- loadSessionWithOptions (cmapWithPrio LogSession recorder) argsSessionLoadingOptions dir - config <- LSP.runLspT env LSP.getConfig - let def_options = argsIdeOptions config sessionLoader - - -- disable runSubset if the client doesn't support watched files - runSubset <- (optRunSubset def_options &&) <$> LSP.runLspT env isWatchSupported - log Debug $ LogShouldRunSubset runSubset - - let options = def_options - { optReportProgress = clientSupportsProgress caps - , optModifyDynFlags = optModifyDynFlags def_options <> pluginModifyDynflags plugins - , optRunSubset = runSubset - } - caps = LSP.resClientCapabilities env - -- FIXME: Remove this after GHC 9.2 gets fully supported - when (ghcVersion == GHC92) $ - log Warning LogOnlyPartialGhc92Support - monitoring <- argsMonitoring - initialise - (cmapWithPrio LogService recorder) - argsDefaultHlsConfig - rules - (Just env) - logger - debouncer - options - withHieDb - hieChan - monitoring + let getIdeState :: LSP.LanguageContextEnv Config -> Maybe FilePath -> WithHieDb -> IndexQueue -> IO IdeState + getIdeState env rootPath withHieDb hieChan = do + traverse_ IO.setCurrentDirectory rootPath + t <- t + log Info $ LogLspStartDuration t + + dir <- maybe IO.getCurrentDirectory return rootPath + + -- We want to set the global DynFlags right now, so that we can use + -- `unsafeGlobalDynFlags` even before the project is configured + _mlibdir <- + setInitialDynFlags (cmapWithPrio LogSession recorder) dir argsSessionLoadingOptions + -- TODO: should probably catch/log/rethrow at top level instead + `catchAny` (\e -> log Error (LogSetInitialDynFlagsException e) >> pure Nothing) + + sessionLoader <- loadSessionWithOptions (cmapWithPrio LogSession recorder) argsSessionLoadingOptions dir + config <- LSP.runLspT env LSP.getConfig + let def_options = argsIdeOptions config sessionLoader + + -- disable runSubset if the client doesn't support watched files + runSubset <- (optRunSubset def_options &&) <$> LSP.runLspT env isWatchSupported + log Debug $ LogShouldRunSubset runSubset + + let options = def_options + { optReportProgress = clientSupportsProgress caps + , optModifyDynFlags = optModifyDynFlags def_options <> pluginModifyDynflags plugins + , optRunSubset = runSubset + } + caps = LSP.resClientCapabilities env + -- FIXME: Remove this after GHC 9.2 gets fully supported + when (ghcVersion == GHC92) $ + log Warning LogOnlyPartialGhc92Support + monitoring <- argsMonitoring + initialise + (cmapWithPrio LogService recorder) + argsDefaultHlsConfig + rules + (Just env) + logger + debouncer + options + withHieDb + hieChan + monitoring + + let setup = setupLSP (cmapWithPrio LogLanguageServer recorder) argsGetHieDbLoc (pluginHandlers plugins) getIdeState + + runLanguageServer options inH outH argsDefaultHlsConfig argsOnConfigChange setup dumpSTMStats Check argFiles -> do dir <- maybe IO.getCurrentDirectory return argsProjectRoot diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index cf63af660d..970bb6b8fb 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -460,10 +460,17 @@ executable haskell-language-server-wrapper , ghcide , gitrev , haskell-language-server + , hslogger , hie-bios + , hls-plugin-api + , lsp + , lsp-types + , mtl , optparse-applicative , optparse-simple , process + , transformers + , unliftio-core if !os(windows) build-depends: unix From 0b8c793dfdf0d6adb1d4704343fa512c7646a15a Mon Sep 17 00:00:00 2001 From: Kobayashi Date: Mon, 27 Jun 2022 10:01:54 +0800 Subject: [PATCH 002/213] fix new import position (#2981) * #2414 fix new import position * fix auto import for ghc version < 9.2 * re-fix it for ghc-9.2 * handle comments * add more comments * reword comments of findPositionNoImports Co-authored-by: Pepe Iborra Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../src/Development/IDE/Plugin/CodeAction.hs | 165 ++++++++++++++---- .../src/Development/IDE/Plugin/Completions.hs | 5 +- ...owerInFileWithCommentsBeforeIt.expected.hs | 20 +++ ...hereDeclLowerInFileWithCommentsBeforeIt.hs | 19 ++ ghcide/test/exe/Main.hs | 50 +++--- 5 files changed, 193 insertions(+), 66 deletions(-) create mode 100644 ghcide/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.expected.hs create mode 100644 ghcide/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.hs diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 267c51c398..a3eb4f4774 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -19,11 +19,10 @@ module Development.IDE.Plugin.CodeAction import Control.Applicative ((<|>)) import Control.Arrow (second, - (>>>), - (&&&)) + (&&&), + (>>>)) import Control.Concurrent.STM.Stats (atomically) -import Control.Monad (guard, join, - msum) +import Control.Monad (guard, join) import Control.Monad.IO.Class import Data.Char import qualified Data.DList as DL @@ -34,7 +33,7 @@ import qualified Data.HashSet as Set import Data.List.Extra import Data.List.NonEmpty (NonEmpty ((:|))) import qualified Data.List.NonEmpty as NE -import qualified Data.Map as M +import qualified Data.Map.Strict as M import Data.Maybe import Data.Ord (comparing) import qualified Data.Rope.UTF16 as Rope @@ -47,7 +46,6 @@ import Development.IDE.Core.Service import Development.IDE.GHC.Compat import Development.IDE.GHC.Compat.Util import Development.IDE.GHC.Error -import Development.IDE.GHC.ExactPrint import Development.IDE.GHC.Util (printOutputable, printRdrName, traceAst) @@ -80,6 +78,25 @@ import Language.LSP.Types (CodeAction ( import Language.LSP.VFS import Text.Regex.TDFA (mrAfter, (=~), (=~~)) +#if MIN_VERSION_ghc(9,2,0) +import GHC (AddEpAnn (AddEpAnn), + Anchor (anchor_op), + AnchorOperation (..), + AnnsModule (am_main), + DeltaPos (..), + EpAnn (..), + EpaLocation (..), + LEpaComment, + LocatedA) + +import Control.Monad (msum) +#else +import Language.Haskell.GHC.ExactPrint.Types (Annotation (annsDP), + DeltaPos, + KeywordId (G), + deltaRow, + mkAnnKey) +#endif ------------------------------------------------------------------------------------------------- @@ -227,10 +244,8 @@ findInstanceHead df instanceHead decls = #if MIN_VERSION_ghc(9,2,0) findDeclContainingLoc :: Foldable t => Position -> t (GenLocated (SrcSpanAnn' a) e) -> Maybe (GenLocated (SrcSpanAnn' a) e) -#elif MIN_VERSION_ghc(8,10,0) -findDeclContainingLoc :: Foldable t => Position -> t (GenLocated SrcSpan e) -> Maybe (GenLocated SrcSpan e) #else --- TODO populate this type signature for GHC versions <8.10 +findDeclContainingLoc :: Foldable t => Position -> t (GenLocated SrcSpan e) -> Maybe (GenLocated SrcSpan e) #endif findDeclContainingLoc loc = find (\(L l _) -> loc `isInsideSrcSpan` locA l) @@ -243,8 +258,8 @@ findDeclContainingLoc loc = find (\(L l _) -> loc `isInsideSrcSpan` locA l) -- imported from ‘Data.ByteString’ at B.hs:6:1-22 -- imported from ‘Data.ByteString.Lazy’ at B.hs:8:1-27 -- imported from ‘Data.Text’ at B.hs:7:1-16 -suggestHideShadow :: ParsedSource -> T.Text -> Maybe TcModuleResult -> Maybe HieAstResult -> Diagnostic -> [(T.Text, [Either TextEdit Rewrite])] -suggestHideShadow ps@(L _ HsModule {hsmodImports}) fileContents mTcM mHar Diagnostic {_message, _range} +suggestHideShadow :: Annotated ParsedSource -> T.Text -> Maybe TcModuleResult -> Maybe HieAstResult -> Diagnostic -> [(T.Text, [Either TextEdit Rewrite])] +suggestHideShadow ps fileContents mTcM mHar Diagnostic {_message, _range} | Just [identifier, modName, s] <- matchRegexUnifySpaces _message @@ -261,6 +276,8 @@ suggestHideShadow ps@(L _ HsModule {hsmodImports}) fileContents mTcM mHar Diagno result <> [hideAll] | otherwise = [] where + L _ HsModule {hsmodImports} = astA ps + suggests identifier modName s | Just tcM <- mTcM, Just har <- mHar, @@ -940,11 +957,11 @@ isPreludeImplicit = xopt Lang.ImplicitPrelude suggestImportDisambiguation :: DynFlags -> Maybe T.Text -> - ParsedSource -> + Annotated ParsedSource -> T.Text -> Diagnostic -> [(T.Text, [Either TextEdit Rewrite])] -suggestImportDisambiguation df (Just txt) ps@(L _ HsModule {hsmodImports}) fileContents diag@Diagnostic {..} +suggestImportDisambiguation df (Just txt) ps fileContents diag@Diagnostic {..} | Just [ambiguous] <- matchRegexUnifySpaces _message @@ -956,6 +973,8 @@ suggestImportDisambiguation df (Just txt) ps@(L _ HsModule {hsmodImports}) fileC suggestions ambiguous modules (isJust local) | otherwise = [] where + L _ HsModule {hsmodImports} = astA ps + locDic = fmap (NE.fromList . DL.toList) $ Map.fromListWith (<>) $ @@ -1048,13 +1067,13 @@ targetModuleName (ExistingImp _) = error "Cannot happen!" disambiguateSymbol :: - ParsedSource -> + Annotated ParsedSource -> T.Text -> Diagnostic -> T.Text -> HidingMode -> [Either TextEdit Rewrite] -disambiguateSymbol pm fileContents Diagnostic {..} (T.unpack -> symbol) = \case +disambiguateSymbol ps fileContents Diagnostic {..} (T.unpack -> symbol) = \case (HideOthers hiddens0) -> [ Right $ hideSymbol symbol idecl | ExistingImp idecls <- hiddens0 @@ -1062,7 +1081,7 @@ disambiguateSymbol pm fileContents Diagnostic {..} (T.unpack -> symbol) = \case ] ++ mconcat [ if null imps - then maybeToList $ Left . snd <$> newImportToEdit (hideImplicitPreludeSymbol $ T.pack symbol) pm fileContents + then maybeToList $ Left . snd <$> newImportToEdit (hideImplicitPreludeSymbol $ T.pack symbol) ps fileContents else Right . hideSymbol symbol <$> imps | ImplicitPrelude imps <- hiddens0 ] @@ -1292,7 +1311,7 @@ removeRedundantConstraints df (L _ HsModule {hsmodDecls}) Diagnostic{..} ------------------------------------------------------------------------------------------------- -suggestNewOrExtendImportForClassMethod :: ExportsMap -> ParsedSource -> T.Text -> Diagnostic -> [(T.Text, CodeActionKind, [Either TextEdit Rewrite])] +suggestNewOrExtendImportForClassMethod :: ExportsMap -> Annotated ParsedSource -> T.Text -> Diagnostic -> [(T.Text, CodeActionKind, [Either TextEdit Rewrite])] suggestNewOrExtendImportForClassMethod packageExportsMap ps fileContents Diagnostic {_message} | Just [methodName, className] <- matchRegexUnifySpaces @@ -1306,7 +1325,7 @@ suggestNewOrExtendImportForClassMethod packageExportsMap ps fileContents Diagnos where suggest identInfo@IdentInfo {moduleNameText} | importStyle <- NE.toList $ importStyles identInfo, - mImportDecl <- findImportDeclByModuleName (hsmodImports $ unLoc ps) (T.unpack moduleNameText) = + mImportDecl <- findImportDeclByModuleName (hsmodImports . unLoc . astA $ ps) (T.unpack moduleNameText) = case mImportDecl of -- extend Just decl -> @@ -1328,8 +1347,8 @@ suggestNewOrExtendImportForClassMethod packageExportsMap ps fileContents Diagnos <> [(quickFixImportKind "new.all", newImportAll moduleNameText)] | otherwise -> [] -suggestNewImport :: ExportsMap -> ParsedSource -> T.Text -> Diagnostic -> [(T.Text, CodeActionKind, TextEdit)] -suggestNewImport packageExportsMap ps@(L _ HsModule {..}) fileContents Diagnostic{_message} +suggestNewImport :: ExportsMap -> Annotated ParsedSource -> T.Text -> Diagnostic -> [(T.Text, CodeActionKind, TextEdit)] +suggestNewImport packageExportsMap ps fileContents Diagnostic{_message} | msg <- unifySpaces _message , Just thingMissing <- extractNotInScopeName msg , qual <- extractQualifiedModuleName msg @@ -1344,6 +1363,8 @@ suggestNewImport packageExportsMap ps@(L _ HsModule {..}) fileContents Diagnosti = sortOn fst3 [(imp, kind, TextEdit range (imp <> "\n" <> T.replicate indent " ")) | (kind, unNewImport -> imp) <- constructNewImportSuggestions packageExportsMap (qual <|> qual', thingMissing) extendImportSuggestions ] + where + L _ HsModule {..} = astA ps suggestNewImport _ _ _ _ = [] constructNewImportSuggestions @@ -1371,7 +1392,7 @@ constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules = newtype NewImport = NewImport {unNewImport :: T.Text} deriving (Show, Eq, Ord) -newImportToEdit :: NewImport -> ParsedSource -> T.Text -> Maybe (T.Text, TextEdit) +newImportToEdit :: NewImport -> Annotated ParsedSource -> T.Text -> Maybe (T.Text, TextEdit) newImportToEdit (unNewImport -> imp) ps fileContents | Just (range, indent) <- newImportInsertRange ps fileContents = Just (imp, TextEdit range (imp <> "\n" <> T.replicate indent " ")) @@ -1385,35 +1406,105 @@ newImportToEdit (unNewImport -> imp) ps fileContents -- * If the file has neither existing imports nor a module declaration, -- the import will be inserted at line zero if there are no pragmas, -- * otherwise inserted one line after the last file-header pragma -newImportInsertRange :: ParsedSource -> T.Text -> Maybe (Range, Int) -newImportInsertRange (L _ HsModule {..}) fileContents +newImportInsertRange :: Annotated ParsedSource -> T.Text -> Maybe (Range, Int) +newImportInsertRange ps fileContents | Just ((l, c), col) <- case hsmodImports of - [] -> findPositionNoImports (fmap reLoc hsmodName) (fmap reLoc hsmodExports) fileContents - _ -> findPositionFromImportsOrModuleDecl (map reLoc hsmodImports) last True + -- When there is no existing imports, we only cares about the line number, setting column and indent to zero. + [] -> (\line -> ((line, 0), 0)) <$> findPositionNoImports ps fileContents + _ -> findPositionFromImports (map reLoc hsmodImports) last , let insertPos = Position (fromIntegral l) (fromIntegral c) = Just (Range insertPos insertPos, col) | otherwise = Nothing + where + L _ HsModule {..} = astA ps + +-- | Find the position for a new import when there isn't an existing one. +-- * If there is a module declaration, a new import should be inserted under the module declaration (including exports list) +-- * Otherwise, a new import should be inserted after any file-header pragma. +findPositionNoImports :: Annotated ParsedSource -> T.Text -> Maybe Int +findPositionNoImports ps fileContents = + maybe (Just (findNextPragmaPosition fileContents)) (findPositionAfterModuleName ps) hsmodName + where + L _ HsModule {..} = astA ps --- | Insert the import under the Module declaration exports if they exist, otherwise just under the module declaration. --- If no module declaration exists, then no exports will exist either, in that case --- insert the import after any file-header pragmas or at position zero if there are no pragmas -findPositionNoImports :: Maybe (Located ModuleName) -> Maybe (Located [LIE name]) -> T.Text -> Maybe ((Int, Int), Int) -findPositionNoImports Nothing _ fileContents = findNextPragmaPosition fileContents -findPositionNoImports _ (Just hsmodExports) _ = findPositionFromImportsOrModuleDecl hsmodExports id False -findPositionNoImports (Just hsmodName) _ _ = findPositionFromImportsOrModuleDecl hsmodName id False +-- | find line number right after module ... where +findPositionAfterModuleName :: Annotated ParsedSource +#if MIN_VERSION_ghc(9,2,0) + -> LocatedA ModuleName +#else + -> Located ModuleName +#endif + -> Maybe Int +findPositionAfterModuleName ps hsmodName' = do + -- Note that 'where' keyword and comments are not part of the AST. They belongs to + -- the exact-print information. To locate it, we need to find the previous AST node, + -- calculate the gap between it and 'where', then add them up to produce the absolute + -- position of 'where'. + + lineOffset <- whereKeywordLineOffset -- Calculate the gap before 'where' keyword. + case prevSrcSpan of + UnhelpfulSpan _ -> Nothing + (RealSrcSpan prevSrcSpan' _) -> + -- add them up produce the absolute location of 'where' keyword + Just $ srcLocLine (realSrcSpanEnd prevSrcSpan') + lineOffset + where + L _ HsModule {..} = astA ps + + -- The last AST node before 'where' keyword. Might be module name or export list. + prevSrcSpan = maybe (getLoc hsmodName') getLoc hsmodExports + + -- The relative position of 'where' keyword (in lines, relative to the previous AST node). + -- The exact-print API changed a lot in ghc-9.2, so we need to handle it seperately for different compiler versions. + whereKeywordLineOffset :: Maybe Int +#if MIN_VERSION_ghc(9,2,0) + whereKeywordLineOffset = case hsmodAnn of + EpAnn _ annsModule _ -> do + -- Find the first 'where' + whereLocation <- fmap NE.head . NE.nonEmpty . mapMaybe filterWhere . am_main $ annsModule + epaLocationToLine whereLocation + EpAnnNotUsed -> Nothing + filterWhere (AddEpAnn AnnWhere loc) = Just loc + filterWhere _ = Nothing + + epaLocationToLine :: EpaLocation -> Maybe Int + epaLocationToLine (EpaSpan sp) = Just . srcLocLine . realSrcSpanEnd $ sp + epaLocationToLine (EpaDelta (SameLine _) priorComments) = Just $ sumCommentsOffset priorComments + -- 'priorComments' contains the comments right before the current EpaLocation + -- Summing line offset of priorComments is necessary, as 'line' is the gap between the last comment and + -- the current AST node + epaLocationToLine (EpaDelta (DifferentLine line _) priorComments) = Just (line + sumCommentsOffset priorComments) + + sumCommentsOffset :: [LEpaComment] -> Int + sumCommentsOffset = sum . fmap (\(L anchor _) -> anchorOpLine (anchor_op anchor)) + + anchorOpLine :: AnchorOperation -> Int + anchorOpLine UnchangedAnchor = 0 + anchorOpLine (MovedAnchor (SameLine _)) = 0 + anchorOpLine (MovedAnchor (DifferentLine line _)) = line +#else + whereKeywordLineOffset = do + ann <- annsA ps M.!? mkAnnKey (astA ps) + deltaPos <- fmap NE.head . NE.nonEmpty .mapMaybe filterWhere $ annsDP ann + pure $ deltaRow deltaPos + + -- Before ghc 9.2, DeltaPos doesn't take comment into acccount, so we don't need to sum line offset of comments. + filterWhere :: (KeywordId, DeltaPos) -> Maybe DeltaPos + filterWhere (keywordId, deltaPos) = + if keywordId == G AnnWhere then Just deltaPos else Nothing +#endif -findPositionFromImportsOrModuleDecl :: HasSrcSpan a => t -> (t -> a) -> Bool -> Maybe ((Int, Int), Int) -findPositionFromImportsOrModuleDecl hsField f hasImports = case getLoc (f hsField) of +findPositionFromImports :: HasSrcSpan a => t -> (t -> a) -> Maybe ((Int, Int), Int) +findPositionFromImports hsField f = case getLoc (f hsField) of RealSrcSpan s _ -> let col = calcCol s in Just ((srcLocLine (realSrcSpanEnd s), col), col) _ -> Nothing - where calcCol s = if hasImports then srcLocCol (realSrcSpanStart s) - 1 else 0 + where calcCol s = srcLocCol (realSrcSpanStart s) - 1 -- | Find the position one after the last file-header pragma -- Defaults to zero if there are no pragmas in file -findNextPragmaPosition :: T.Text -> Maybe ((Int, Int), Int) -findNextPragmaPosition contents = Just ((lineNumber, 0), 0) +findNextPragmaPosition :: T.Text -> Int +findNextPragmaPosition contents = lineNumber where lineNumber = afterLangPragma . afterOptsGhc $ afterShebang afterLangPragma = afterPragma "LANGUAGE" contents' diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index 51eee11e27..c0a76bc360 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -250,10 +250,7 @@ extendImportHandler' ideState ExtendImport {..} it = case thingParent of Nothing -> newThing Just p -> p <> "(" <> newThing <> ")" - t <- liftMaybe $ snd <$> newImportToEdit - n - (astA ps) - (fromMaybe "" contents) + t <- liftMaybe $ snd <$> newImportToEdit n ps (fromMaybe "" contents) return (nfp, WorkspaceEdit {_changes=Just (fromList [(doc,List [t])]), _documentChanges=Nothing, _changeAnnotations=Nothing}) | otherwise = mzero diff --git a/ghcide/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.expected.hs b/ghcide/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.expected.hs new file mode 100644 index 0000000000..9ea40643c9 --- /dev/null +++ b/ghcide/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.expected.hs @@ -0,0 +1,20 @@ +module Asdf + (f + , where') +-- hello +-- world + + where +import Data.Int + + + +f :: Int64 -> Int64 +f = id' + where id' = id + +g :: Int -> Int +g = id + +where' :: Int -> Int +where' = id diff --git a/ghcide/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.hs b/ghcide/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.hs new file mode 100644 index 0000000000..d79ea57f21 --- /dev/null +++ b/ghcide/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.hs @@ -0,0 +1,19 @@ +module Asdf + (f + , where') +-- hello +-- world + + where + + + +f :: Int64 -> Int64 +f = id' + where id' = id + +g :: Int -> Int +g = id + +where' :: Int -> Int +where' = id diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index b44e865083..af8ae70de2 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -85,9 +85,9 @@ import System.Environment.Blank (getEnv, setEnv, unsetEnv) import System.Exit (ExitCode (ExitSuccess)) import System.FilePath -import System.IO.Extra hiding (withTempDir) -import qualified System.IO.Extra import System.Info.Extra (isMac, isWindows) +import qualified System.IO.Extra +import System.IO.Extra hiding (withTempDir) import System.Mem (performGC) import System.Process.Extra (CreateProcess (cwd), createPipe, proc, @@ -95,7 +95,7 @@ import System.Process.Extra (CreateProcess (cwd), import Test.QuickCheck -- import Test.QuickCheck.Instances () import Control.Concurrent.Async -import Control.Lens (to, (^.), (.~)) +import Control.Lens (to, (.~), (^.)) import Control.Monad.Extra (whenJust) import Data.Function ((&)) import Data.IORef @@ -123,8 +123,9 @@ import qualified HieDbRetry import Ide.PluginUtils (pluginDescToIdePlugins) import Ide.Types import qualified Language.LSP.Types as LSP +import Language.LSP.Types.Lens (didChangeWatchedFiles, + workspace) import qualified Language.LSP.Types.Lens as L -import Language.LSP.Types.Lens (workspace, didChangeWatchedFiles) import qualified Progress import System.Time.Extra import Test.Tasty @@ -901,22 +902,21 @@ watchedFilesTests = testGroup "watched files" insertImportTests :: TestTree insertImportTests = testGroup "insert import" - [ expectFailBecause - ("'findPositionFromImportsOrModuleDecl' function adds import directly under line with module declaration, " - ++ "not accounting for case when 'where' keyword is placed on lower line") - (checkImport - "module where keyword lower in file no exports" - "WhereKeywordLowerInFileNoExports.hs" - "WhereKeywordLowerInFileNoExports.expected.hs" - "import Data.Int") - , expectFailBecause - ("'findPositionFromImportsOrModuleDecl' function adds import directly under line with module exports list, " - ++ "not accounting for case when 'where' keyword is placed on lower line") - (checkImport - "module where keyword lower in file with exports" - "WhereDeclLowerInFile.hs" - "WhereDeclLowerInFile.expected.hs" - "import Data.Int") + [ checkImport + "module where keyword lower in file no exports" + "WhereKeywordLowerInFileNoExports.hs" + "WhereKeywordLowerInFileNoExports.expected.hs" + "import Data.Int" + , checkImport + "module where keyword lower in file with exports" + "WhereDeclLowerInFile.hs" + "WhereDeclLowerInFile.expected.hs" + "import Data.Int" + , checkImport + "module where keyword lower in file with comments before it" + "WhereDeclLowerInFileWithCommentsBeforeIt.hs" + "WhereDeclLowerInFileWithCommentsBeforeIt.expected.hs" + "import Data.Int" , expectFailBecause "'findNextPragmaPosition' function doesn't account for case when shebang is not placed at top of file" (checkImport @@ -5467,7 +5467,7 @@ completionDocTests = -- We ignore doc uris since it points to the local path which determined by specific machines case mn of Nothing -> txt - Just n -> T.take n txt + Just n -> T.take n txt | CompletionItem {_documentation = Just (CompletionDocMarkup (MarkupContent MkMarkdown txt)), ..} <- compls , _label == label ] @@ -5767,13 +5767,13 @@ knownBrokenFor = knownIssueFor Broken knownIssueFor :: IssueSolution -> BrokenTarget -> String -> TestTree -> TestTree knownIssueFor solution = go . \case BrokenSpecific bos vers -> isTargetOS bos && isTargetGhc vers - BrokenForOS bos -> isTargetOS bos - BrokenForGHC vers -> isTargetGhc vers + BrokenForOS bos -> isTargetOS bos + BrokenForGHC vers -> isTargetGhc vers where isTargetOS = \case Windows -> isWindows - MacOS -> isMac - Linux -> not isWindows && not isMac + MacOS -> isMac + Linux -> not isWindows && not isMac isTargetGhc = elem ghcVersion From 771d8f44e694c70d0ee487f2bd6d095fd5e1b99f Mon Sep 17 00:00:00 2001 From: Kobayashi Date: Tue, 28 Jun 2022 16:54:32 +0800 Subject: [PATCH 003/213] unify pre-commit hook & update Gitpod config (#2991) * update Gitpod config * update nix shellHook & docs * install pre-commit hook * add kokobd as code owner to .gitpod.* * add gen-hie to Gitpod * add tools for doc * remove .pre-commit-config.yaml from .gitignore * set vscode formatter to stylish-haskell in Gitpod --- .gitignore | 3 -- .gitpod.Dockerfile | 18 ++++++++ .gitpod.yml | 76 ++++++++++++++++++------------- CODEOWNERS | 1 + docs/contributing/contributing.md | 46 ++----------------- flake.lock | 49 -------------------- flake.nix | 50 ++++---------------- 7 files changed, 76 insertions(+), 167 deletions(-) create mode 100644 .gitpod.Dockerfile diff --git a/.gitignore b/.gitignore index 61cfe5877b..ed983e69c8 100644 --- a/.gitignore +++ b/.gitignore @@ -30,9 +30,6 @@ test/testdata/**/hie.yaml # shake build folder (used in benchmark suite) .shake/ -# pre-commit-hook.nix -.pre-commit-config.yaml - # direnv /.direnv/ /.envrc diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile new file mode 100644 index 0000000000..5a244cf56f --- /dev/null +++ b/.gitpod.Dockerfile @@ -0,0 +1,18 @@ +FROM gitpod/workspace-full + +RUN sudo install-packages build-essential curl libffi-dev libffi7 libgmp-dev libgmp10 \ + libncurses-dev libncurses5 libtinfo5 && \ + BOOTSTRAP_HASKELL_NONINTERACTIVE=1 \ + BOOTSTRAP_HASKELL_MINIMAL=1 \ + curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh && \ + echo 'source $HOME/.ghcup/env' >> $HOME/.bashrc && \ + echo 'export PATH=$HOME/.cabal/bin:$HOME/.local/bin:$PATH' >> $HOME/.bashrc && \ + . /home/gitpod/.ghcup/env && \ + ghcup install ghc --set && \ + ghcup install hls --set && \ + ghcup install cabal --set && \ + ghcup install stack --set && \ + cabal update && \ + cabal install stylish-haskell hoogle implicit-hie && \ + pip install pre-commit && \ + npm install -g http-server diff --git a/.gitpod.yml b/.gitpod.yml index 3adf74afc2..e492004825 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,42 +1,54 @@ +image: + file: .gitpod.Dockerfile # List the start up tasks. Learn more https://www.gitpod.io/docs/config-start-tasks/ tasks: - - before: | - # Only the /workspace folder is persistent - export XDG_DATA_HOME=/workspace/.local/share - export XDG_CONFIG_HOME=/workspace/.local/config - export XDG_STATE_HOME=/workspace/.local/state - export XDG_CACHE_HOME=/workspace/.cache - export CABAL_DIR=/workspace/.cabal - export STACK_ROOT=/workspace/.stack + - name: Setup + before: | + # Make sure some folders not in /workspace persist between worksapce restarts. + # You may add additional directories to this list. + declare -a CACHE_DIRS=( + $HOME/.local + $HOME/.cabal + $HOME/.stack + $HOME/.ghcup + /nix + ) + for DIR in "${CACHE_DIRS[@]}"; do + mkdir -p $(dirname /workspace/cache$DIR) + mkdir -p $DIR # in case $DIR doesn't already exist + # On a fresh start with no prebuilds, we move existing directory + # to /workspace. 'sudo mv' fails with 'no permission', I don't know why + if [ ! -d /workspace/cache$DIR ]; then + sudo cp -rp $DIR /workspace/cache$DIR + sudo rm -rf $DIR/* + fi + mkdir -p /workspace/cache$DIR # make sure it exists even if cp fails + # Now /workspace/cache$DIR exists. + # Use bind mount to make $DIR backed by /workspace/cache$DIR + sudo mount --bind /workspace/cache$DIR $DIR + done - # install ghcup, ghc and cabal - export GHCUP_INSTALL_BASE_PREFIX=/workspace - export BOOTSTRAP_HASKELL_NONINTERACTIVE=1 - export BOOTSTRAP_HASKELL_MINIMAL=1 - curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh - /workspace/.ghcup/bin/ghcup install ghc --set - /workspace/.ghcup/bin/ghcup install cabal - - # Add ghcup binaries to the PATH since VSCode does not see 'source .ghcup/env' - pushd /usr/local/bin - sudo ln -s /workspace/.ghcup/bin/* /usr/local/bin - popd - - # Fix the Cabal dir since VSCode does not see CABAL_DIR - cabal update - echo "Symlinking /workspace/.cabal to ~/.cabal" - ln -s /workspace/.cabal ~ + # Install pre-commit hook + pre-commit install # Configure VSCode to use the locally built version of HLS mkdir -p .vscode - echo '{ "haskell.serverExecutablePath": "/workspace/.cabal/bin/haskell-language-server" }' > .vscode/settings.json + if [ ! -f .vscode/settings.json ]; then + # Only write to .vscode/settings.json if it doesn't exist. + echo '{' > .vscode/settings.json + echo ' "haskell.serverExecutablePath": "/home/gitpod/.cabal/bin/haskell-language-server",' >> .vscode/settings.json + echo ' "haskell.formattingProvider": "stylish-haskell"' >> .vscode/settings.json + echo '}' >> .vscode/settings.json + fi - init: | + pushd docs + pip install -r requirements.txt + popd + init: | + cabal update cabal configure --enable-executable-dynamic - cabal build --enable-tests - cabal install exe:haskell-language-server - command: | - cabal build --enable-tests + cabal build --enable-tests all + cabal install exe:haskell-language-server # List the ports to expose. Learn more https://www.gitpod.io/docs/config-ports/ ports: [] @@ -62,4 +74,4 @@ vscode: extensions: - "haskell.haskell" - "justusadam.language-haskell" - - "usernamehw.errorlens" \ No newline at end of file + - "EditorConfig.EditorConfig" diff --git a/CODEOWNERS b/CODEOWNERS index 62cd8878b9..b54ff268c3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -44,3 +44,4 @@ # Build *.nix @berberman @michaelpj @guibou *.project @jneira +.gitpod.* @kokobd diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index 1907d40856..a3fd5660b3 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -170,49 +170,13 @@ Please, try to follow those basic settings to keep the codebase as uniform as po ### Formatter pre-commit hook -We are using [pre-commit-hook.nix](https://github.com/cachix/pre-commit-hooks.nix) to configure git pre-commit hook for formatting. Although it is possible to run formatting manually, we recommend you to use it to set pre-commit hook as our CI checks pre-commit hook is applied or not. +We are using [pre-commit](https://pre-commit.com/) to configure git pre-commit hook for formatting. Although it is possible to run formatting manually, we recommend you to use it to set pre-commit hook as our CI checks pre-commit hook is applied or not. -You can configure the pre-commit-hook by running +If you are using Nix or Gitpod, pre-commit hook is automatically installed. Otherwise, follow instructions on +[https://pre-commit.com/](https://pre-commit.com/) to install the `pre-commit` tool, then run the following command: -``` bash -nix-shell -``` - -If you don't want to use [nix](https://nixos.org/guides/install-nix.html), you can instead use [pre-commit](https://pre-commit.com) with the following config. - -```json -{ - "repos": [ - { - "hooks": [ - { - "entry": "stylish-haskell --inplace", - "exclude": "(^Setup.hs$|test/testdata/.*$|test/data/.*$|test/manual/lhs/.*$|^hie-compat/.*$|^plugins/hls-tactics-plugin/.*$|^ghcide/src/Development/IDE/GHC/Compat.hs$|^ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs$|^ghcide/src/Development/IDE/GHC/Compat/Core.hs$|^ghcide/src/Development/IDE/Spans/Pragmas.hs$|^ghcide/src/Development/IDE/LSP/Outline.hs$|^plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs$|^ghcide/test/exe/Main.hs$|ghcide/src/Development/IDE/Core/Rules.hs|^hls-test-utils/src/Test/Hls/Util.hs$)", - "files": "\\.l?hs$", - "id": "stylish-haskell", - "language": "system", - "name": "stylish-haskell", - "pass_filenames": true, - "types": [ - "file" - ] - } - ], - "repo": "local" - }, - { - "repo": "https://github.com/pre-commit/pre-commit-hooks", - "rev": "v4.1.0", - "hooks": [ - { - "id": "mixed-line-ending", - "args": ["--fix", "lf"], - "exclude": "test/testdata/.*CRLF*.hs$" - } - ] - } - ] -} +```sh +pre-commit install ``` #### Why some components are excluded from automatic formatting? diff --git a/flake.lock b/flake.lock index 2e99cc6c13..a78b703cd4 100644 --- a/flake.lock +++ b/flake.lock @@ -82,21 +82,6 @@ "type": "github" } }, - "flake-utils_3": { - "locked": { - "lastModified": 1644229661, - "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "fourmolu": { "flake": false, "locked": { @@ -293,20 +278,6 @@ "type": "github" } }, - "nixpkgs_3": { - "locked": { - "lastModified": 1645655918, - "narHash": "sha256-ZfbEFRW7o237+A1P7eTKhXje435FCAoe0blj2n20Was=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "77a7a4197740213879b9a1d2e1788c6c8ade4274", - "type": "github" - }, - "original": { - "id": "nixpkgs", - "type": "indirect" - } - }, "poetry2nix": { "inputs": { "flake-utils": "flake-utils_2", @@ -327,25 +298,6 @@ "type": "github" } }, - "pre-commit-hooks": { - "inputs": { - "flake-utils": "flake-utils_3", - "nixpkgs": "nixpkgs_3" - }, - "locked": { - "lastModified": 1652714503, - "narHash": "sha256-qQKVEfDe5FqvGgkZtg5Pc491foeiDPIOeycHMqnPDps=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "521a524771a8e93caddaa0ac1d67d03766a8b0b3", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, "ptr-poker": { "flake": false, "locked": { @@ -392,7 +344,6 @@ "myst-parser": "myst-parser", "nixpkgs": "nixpkgs", "poetry2nix": "poetry2nix", - "pre-commit-hooks": "pre-commit-hooks", "ptr-poker": "ptr-poker", "retrie": "retrie", "sphinx_rtd_theme": "sphinx_rtd_theme", diff --git a/flake.nix b/flake.nix index 51d0d5e43a..c7db14aac6 100644 --- a/flake.nix +++ b/flake.nix @@ -14,9 +14,6 @@ flake = false; }; flake-utils.url = "github:numtide/flake-utils"; - pre-commit-hooks = { - url = "github:cachix/pre-commit-hooks.nix"; - }; gitignore = { url = "github:hercules-ci/gitignore.nix"; flake = false; @@ -92,7 +89,7 @@ flake = false; }; myst-parser = { - url = "github:smunix/MyST-Parser?ref=fix.hls-docutils"; + url = "github:smunix/MyST-Parser?ref=fix.hls-docutils"; flake = false; }; # For https://github.com/readthedocs/sphinx_rtd_theme/pull/1185, otherwise lists are broken locally @@ -103,7 +100,7 @@ poetry2nix.url = "github:nix-community/poetry2nix/master"; }; outputs = - inputs@{ self, nixpkgs, flake-compat, flake-utils, pre-commit-hooks, gitignore, ... }: + inputs@{ self, nixpkgs, flake-compat, flake-utils, gitignore, ... }: { overlays.default = final: prev: with prev; @@ -123,7 +120,7 @@ in hsuper.mkDerivation (args // { jailbreak = if broken then true else jailbreak; doCheck = if broken then false else check; - # Library profiling is disabled as it causes long compilation time + # Library profiling is disabled as it causes long compilation time # on our CI jobs. Nix users are free tor revert this anytime. enableLibraryProfiling = false; doHaddock = false; @@ -215,42 +212,11 @@ config = { allowBroken = true; }; }; - # Pre-commit hooks to run stylish-haskell - pre-commit-check = hpkgs: pre-commit-hooks.lib.${system}.run { - src = ./.; - hooks = { - stylish-haskell.enable = true; - # use stylish-haskell with our target ghc - stylish-haskell.entry = pkgs.lib.mkForce "${hpkgs.stylish-haskell}/bin/stylish-haskell --inplace"; - stylish-haskell.excludes = [ - # Ignored files - "^Setup.hs$" - "test/testdata/.*$" - "test/data/.*$" - "test/manual/lhs/.*$" - "^hie-compat/.*$" - "^plugins/hls-tactics-plugin/.*$" - - # Temporarily ignored files - # Stylish-haskell (and other formatters) does not work well with some CPP usages in these files - "^ghcide/src/Development/IDE/GHC/Compat.hs$" - "^ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs$" - "^ghcide/src/Development/IDE/GHC/Compat/Core.hs$" - "^ghcide/src/Development/IDE/Spans/Pragmas.hs$" - "^ghcide/src/Development/IDE/LSP/Outline.hs$" - "^plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs$" - "^ghcide/test/exe/Main.hs$" - "ghcide/src/Development/IDE/Core/Rules.hs" - "^hls-test-utils/src/Test/Hls/Util.hs$" - ]; - }; - }; - ghc902Config = (import ./configuration-ghc-90.nix) { inherit pkgs inputs; }; ghc922Config = (import ./configuration-ghc-92.nix) { inherit pkgs inputs; }; # GHC versions - # While HLS still works fine with 8.10 GHCs, we only support the versions that are cached + # While HLS still works fine with 8.10 GHCs, we only support the versions that are cached # by upstream nixpkgs, which now only includes GHC version 9+ supportedGHCs = let ghcVersion = "ghc" + (pkgs.lib.replaceStrings ["."] [""] pkgs.haskellPackages.ghc.version); @@ -268,14 +234,14 @@ myst-parser = pkgs.poetry2nix.mkPoetryEnv { projectDir = inputs.myst-parser; python = pkgs.python39; - overrides = [ + overrides = [ pkgs.poetry2nix.defaultPoetryOverrides ]; }; sphinx_rtd_theme = pkgs.poetry2nix.mkPoetryEnv { projectDir = inputs.sphinx_rtd_theme; python = pkgs.python39; - overrides = [ + overrides = [ pkgs.poetry2nix.defaultPoetryOverrides (self: super: { # The RTD theme doesn't work with newer docutils @@ -334,8 +300,8 @@ export DYLD_LIBRARY_PATH=${gmp}/lib:${zlib}/lib:${ncurses}/lib:${capstone}/lib export PATH=$PATH:$HOME/.local/bin - # Enable the shell hooks - ${self.checks.${system}.pre-commit-check.shellHook} + # Install pre-commit hook + pre-commit install # If the cabal project file is not the default one. # Print a warning and generate an alias. From c3c73cf30bf8b59182e6df674e3f804b55b062ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20G=C3=A1sp=C3=A1r?= <58485900+gasparattila@users.noreply.github.com> Date: Tue, 28 Jun 2022 15:44:42 +0200 Subject: [PATCH 004/213] Add associated type families to local completions (#2987) Co-authored-by: Lei Zhu --- ghcide/src/Development/IDE/Plugin/Completions/Logic.hs | 6 ++++-- test/functional/Completion.hs | 10 ++++++++++ test/testdata/completion/AssociatedTypeFamily.hs | 8 ++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 test/testdata/completion/AssociatedTypeFamily.hs diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index 1a2cb5304b..2269cb3914 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -462,11 +462,13 @@ localCompletionsForParsedModule uri pm@ParsedModule{pm_parsed_source = L _ HsMod ValD _ PatBind{pat_lhs} -> [mkComp id CiVariable Nothing | VarPat _ id <- listify (\(_ :: Pat GhcPs) -> True) pat_lhs] - TyClD _ ClassDecl{tcdLName, tcdSigs} -> + TyClD _ ClassDecl{tcdLName, tcdSigs, tcdATs} -> mkComp tcdLName CiInterface (Just $ showForSnippet tcdLName) : [ mkComp id CiFunction (Just $ showForSnippet typ) | L _ (ClassOpSig _ _ ids typ) <- tcdSigs - , id <- ids] + , id <- ids] ++ + [ mkComp fdLName CiStruct (Just $ showForSnippet fdLName) + | L _ (FamilyDecl{fdLName}) <- tcdATs] TyClD _ x -> let generalCompls = [mkComp id cl (Just $ showForSnippet $ tyClDeclLName x) | id <- listify (\(_ :: LIdP GhcPs) -> True) x diff --git a/test/functional/Completion.hs b/test/functional/Completion.hs index 76a661bd8f..820f25ce95 100644 --- a/test/functional/Completion.hs +++ b/test/functional/Completion.hs @@ -147,6 +147,16 @@ tests = testGroup "completions" [ item ^. label @?= "liftA" item ^. kind @?= Just CiFunction item ^. detail @?= Just "Control.Applicative" + + , testCase "completes locally defined associated type family" $ runSession hlsCommand fullCaps "test/testdata/completion" $ do + doc <- openDoc "AssociatedTypeFamily.hs" "haskell" + + compls <- getCompletions doc (Position 5 20) + item <- getCompletionByLabel "Fam" compls + liftIO $ do + item ^. label @?= "Fam" + item ^. kind @?= Just CiStruct + , contextTests , snippetTests ] diff --git a/test/testdata/completion/AssociatedTypeFamily.hs b/test/testdata/completion/AssociatedTypeFamily.hs new file mode 100644 index 0000000000..f50c1e20cf --- /dev/null +++ b/test/testdata/completion/AssociatedTypeFamily.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE TypeFamilies #-} +module AssociatedTypeFamily () where + +class C a where + type Fam a + +x :: C a => a -> Fam a +x = undefined From 510bd51e46fea8fb51ddfaa60bba505f0663497d Mon Sep 17 00:00:00 2001 From: Michael Peyton Jones Date: Wed, 29 Jun 2022 09:00:49 +0100 Subject: [PATCH 005/213] Remove some partial functions from Shake.hs (#2986) Also removes a partial pattern match from Action.hs. --- .hlint.yaml | 2 -- ghcide/src/Development/IDE/Core/Shake.hs | 29 ++++++++++--------- hls-graph/hls-graph.cabal | 1 + .../Development/IDE/Graph/Internal/Action.hs | 10 ++++--- .../IDE/Graph/Internal/Database.hs | 20 +++++++++---- hls-graph/test/ActionSpec.hs | 4 +-- 6 files changed, 38 insertions(+), 28 deletions(-) diff --git a/.hlint.yaml b/.hlint.yaml index e8cf2d9751..f50ea75cc3 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -88,7 +88,6 @@ within: - Main - Experiments - - Development.IDE.Core.Shake - Development.IDE.Plugin.CodeAction - Development.IDE.Plugin.Completions - Development.IDE.Plugin.CodeAction.ExactPrint @@ -137,7 +136,6 @@ - Wingman.CaseSplit - Wingman.Simplify - - name: Data.Text.head within: - Development.IDE.Plugin.CodeAction diff --git a/ghcide/src/Development/IDE/Core/Shake.hs b/ghcide/src/Development/IDE/Core/Shake.hs index 740a54cb95..cd37c1b26f 100644 --- a/ghcide/src/Development/IDE/Core/Shake.hs +++ b/ghcide/src/Development/IDE/Core/Shake.hs @@ -99,6 +99,7 @@ import Data.EnumMap.Strict (EnumMap) import qualified Data.EnumMap.Strict as EM import Data.Foldable (for_, toList) import Data.Functor ((<&>)) +import Data.Functor.Identity import Data.Hashable import qualified Data.HashMap.Strict as HMap import Data.HashSet (HashSet) @@ -920,21 +921,21 @@ defineNoDiagnostics recorder op = defineEarlyCutoff recorder $ RuleNoDiagnostics -- | Request a Rule result if available use :: IdeRule k v => k -> NormalizedFilePath -> Action (Maybe v) -use key file = head <$> uses key [file] +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)) -useWithStale key file = head <$> usesWithStale key [file] +useWithStale key file = runIdentity <$> usesWithStale key (Identity file) -- | 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 (v, PositionMapping) -useWithStale_ key file = head <$> usesWithStale_ key [file] +useWithStale_ key file = runIdentity <$> usesWithStale_ key (Identity file) -- | Plural version of 'useWithStale_' -usesWithStale_ :: IdeRule k v => k -> [NormalizedFilePath] -> Action [(v, PositionMapping)] +usesWithStale_ :: (Traversable f, IdeRule k v) => k -> f NormalizedFilePath -> Action (f (v, PositionMapping)) usesWithStale_ key files = do res <- usesWithStale key files case sequence res of @@ -999,12 +1000,12 @@ useNoFile :: IdeRule k v => k -> Action (Maybe v) useNoFile key = use key emptyFilePath use_ :: IdeRule k v => k -> NormalizedFilePath -> Action v -use_ key file = head <$> uses_ key [file] +use_ key file = runIdentity <$> uses_ key (Identity file) useNoFile_ :: IdeRule k v => k -> Action v useNoFile_ key = use_ key emptyFilePath -uses_ :: IdeRule k v => k -> [NormalizedFilePath] -> Action [v] +uses_ :: (Traversable f, IdeRule k v) => k -> f NormalizedFilePath -> Action (f v) uses_ key files = do res <- uses key files case sequence res of @@ -1012,24 +1013,24 @@ uses_ key files = do Just v -> return v -- | Plural version of 'use' -uses :: IdeRule k v - => k -> [NormalizedFilePath] -> Action [Maybe v] -uses key files = map (\(A value) -> currentValue value) <$> apply (map (Q . (key,)) files) +uses :: (Traversable f, IdeRule k v) + => k -> f 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 :: IdeRule k v - => k -> [NormalizedFilePath] -> Action [Maybe (v, PositionMapping)] +usesWithStale :: (Traversable f, IdeRule k v) + => k -> f NormalizedFilePath -> Action (f (Maybe (v, PositionMapping))) usesWithStale key files = do - _ <- apply (map (Q . (key,)) files) + _ <- apply (fmap (Q . (key,)) files) -- We don't look at the result of the 'apply' since 'lastValue' will -- return the most recent successfully computed value regardless of -- whether the rule succeeded or not. - mapM (lastValue key) files + traverse (lastValue key) files useWithoutDependency :: IdeRule k v => k -> NormalizedFilePath -> Action (Maybe v) useWithoutDependency key file = - (\[A value] -> currentValue value) <$> applyWithoutDependency [Q (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)) diff --git a/hls-graph/hls-graph.cabal b/hls-graph/hls-graph.cabal index bef6f58d32..2edcbe3aa5 100644 --- a/hls-graph/hls-graph.cabal +++ b/hls-graph/hls-graph.cabal @@ -134,4 +134,5 @@ test-suite tests , tasty-hunit , tasty-rerun , text + , unordered-containers build-tool-depends: hspec-discover:hspec-discover -any diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Action.hs b/hls-graph/src/Development/IDE/Graph/Internal/Action.hs index b9e9a1b08f..854ba903c5 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Action.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Action.hs @@ -25,6 +25,8 @@ import Control.Monad.IO.Class import Control.Monad.Trans.Class import Control.Monad.Trans.Reader import Data.IORef +import Data.Functor.Identity +import Data.Foldable (toList) import Development.IDE.Graph.Classes import Development.IDE.Graph.Internal.Database import Development.IDE.Graph.Internal.Rules (RuleResult) @@ -111,19 +113,19 @@ actionFinally a b = do Action $ lift $ finally (runReaderT (fromAction a) v) b apply1 :: (RuleResult key ~ value, ShakeValue key, Typeable value) => key -> Action value -apply1 k = head <$> apply [k] +apply1 k = runIdentity <$> apply (Identity k) -apply :: (RuleResult key ~ value, ShakeValue key, Typeable value) => [key] -> Action [value] +apply :: (Traversable f, RuleResult key ~ value, ShakeValue key, Typeable value) => f key -> Action (f value) apply ks = do db <- Action $ asks actionDatabase stack <- Action $ asks actionStack (is, vs) <- liftIO $ build db stack ks ref <- Action $ asks actionDeps - liftIO $ modifyIORef ref (ResultDeps is <>) + liftIO $ modifyIORef ref (ResultDeps (toList is) <>) pure vs -- | Evaluate a list of keys without recording any dependencies. -applyWithoutDependency :: (RuleResult key ~ value, ShakeValue key, Typeable value) => [key] -> Action [value] +applyWithoutDependency :: (Traversable f, RuleResult key ~ value, ShakeValue key, Typeable value) => f key -> Action (f value) applyWithoutDependency ks = do db <- Action $ asks actionDatabase stack <- Action $ asks actionStack diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Database.hs b/hls-graph/src/Development/IDE/Graph/Internal/Database.hs index 0ed2ccbb64..308bbde232 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Database.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Database.hs @@ -12,6 +12,8 @@ module Development.IDE.Graph.Internal.Database (newDatabase, incDatabase, build, getDirtySet, getKeysAndVisitAge) where +import Prelude hiding (unzip) + import Control.Concurrent.Async import Control.Concurrent.Extra import Control.Concurrent.STM.Stats (STM, atomically, @@ -30,6 +32,7 @@ import Data.Foldable (for_, traverse_) import Data.HashSet (HashSet) import qualified Data.HashSet as HSet import Data.IORef.Extra +import Data.List.NonEmpty (unzip) import Data.Maybe import Data.Traversable (for) import Data.Tuple.Extra @@ -43,6 +46,7 @@ import qualified StmContainers.Map as SMap import System.Time.Extra (duration, sleep) import System.IO.Unsafe + newDatabase :: Dynamic -> TheRules -> IO Database newDatabase databaseExtra databaseRules = do databaseStep <- newTVarIO $ Step 0 @@ -78,13 +82,17 @@ updateDirty = Focus.adjust $ \(KeyDetails status rdeps) -> in KeyDetails status' rdeps -- | Unwrap and build a list of keys in parallel build - :: forall key value . (RuleResult key ~ value, Typeable key, Show key, Hashable key, Eq key, Typeable value) - => Database -> Stack -> [key] -> IO ([Key], [value]) + :: forall f key value . (Traversable f, RuleResult key ~ value, Typeable key, Show key, Hashable key, Eq key, Typeable value) + => Database -> Stack -> f key -> IO (f Key, f value) -- build _ st k | traceShow ("build", st, k) False = undefined build db stack keys = do - (ids, vs) <- runAIO $ fmap unzip $ either return liftIO =<< - builder db stack (map Key keys) - pure (ids, map (asV . resultValue) vs) + built <- runAIO $ do + built <- builder db stack (fmap Key keys) + case built of + Left clean -> return clean + Right dirty -> liftIO dirty + let (ids, vs) = unzip built + pure (ids, fmap (asV . resultValue) vs) where asV :: Value -> value asV (Value x) = unwrapDynamic x @@ -93,7 +101,7 @@ build db stack keys = do -- If none of the keys are dirty, we can return the results immediately. -- Otherwise, a blocking computation is returned *which must be evaluated asynchronously* to avoid deadlock. builder - :: Database -> Stack -> [Key] -> AIO (Either [(Key, Result)] (IO [(Key, Result)])) + :: Traversable f => Database -> Stack -> f Key -> AIO (Either (f (Key, Result)) (IO (f (Key, Result)))) -- builder _ st kk | traceShow ("builder", st,kk) False = undefined builder db@Database{..} stack keys = withRunInIO $ \(RunInIO run) -> do -- Things that I need to force before my results are ready diff --git a/hls-graph/test/ActionSpec.hs b/hls-graph/test/ActionSpec.hs index 952b6df241..972786dcc2 100644 --- a/hls-graph/test/ActionSpec.hs +++ b/hls-graph/test/ActionSpec.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeApplications #-} @@ -12,6 +11,7 @@ import Development.IDE.Graph.Database (shakeNewDatabase, shakeRunDatabase) import Development.IDE.Graph.Internal.Action (apply1) import Development.IDE.Graph.Internal.Types import Development.IDE.Graph.Rule +import qualified Data.HashSet as HashSet import Example import qualified StmContainers.Map as STM import Test.Hspec @@ -54,7 +54,7 @@ spec = do apply1 theKey res `shouldBe` [True] Just KeyDetails {..} <- atomically $ STM.lookup (Key (Rule @())) databaseValues - keyReverseDeps `shouldBe` [Key theKey] + keyReverseDeps `shouldBe` HashSet.fromList [Key theKey] it "rethrows exceptions" $ do db <- shakeNewDatabase shakeOptions $ do addRule $ \(Rule :: Rule ()) old mode -> error "boom" From 73652d7515879c20577e0494bdc615105905f6fe Mon Sep 17 00:00:00 2001 From: Nick Suchecki <40047416+drsooch@users.noreply.github.com> Date: Fri, 1 Jul 2022 18:52:51 -0400 Subject: [PATCH 006/213] Log response errors returned from Plugins (#2988) * Log ResponseErrors when returned from Plugins * Log from Plugins * Create 'logAndReturnError' that will log any failures in plugins * Missed opportunity to use logAndReturnError * Revert throwPluginError to throwE This reverts a change made previously to try to make pluginErrors have a common error format. This will be updated in the near future. * Warning -> Error * Fix Functional Test for Plugin Response Error * Add orphan instances for * Revert back to Warning * Update log format in test suite --- ghcide/src/Development/IDE/Plugin/HLS.hs | 85 +++++++++++------- ghcide/src/Development/IDE/Types/Logger.hs | 89 +++++++++++-------- hls-plugin-api/src/Ide/PluginUtils.hs | 6 +- .../src/Ide/Plugin/AlternateNumberFormat.hs | 1 - .../src/Ide/Plugin/CallHierarchy/Internal.hs | 4 +- test/functional/Format.hs | 2 +- 6 files changed, 109 insertions(+), 78 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/HLS.hs b/ghcide/src/Development/IDE/Plugin/HLS.hs index 1959dd8dcd..5a2f0a38a3 100644 --- a/ghcide/src/Development/IDE/Plugin/HLS.hs +++ b/ghcide/src/Development/IDE/Plugin/HLS.hs @@ -10,6 +10,7 @@ module Development.IDE.Plugin.HLS ) where import Control.Exception (SomeException) +import Control.Lens ((^.)) import Control.Monad import qualified Data.Aeson as J import Data.Bifunctor @@ -21,6 +22,7 @@ import qualified Data.List as List import Data.List.NonEmpty (NonEmpty, nonEmpty, toList) import qualified Data.Map as Map import Data.String +import Data.Text (Text) import qualified Data.Text as T import Development.IDE.Core.Shake hiding (Log) import Development.IDE.Core.Tracing @@ -33,9 +35,10 @@ import Ide.Plugin.Config import Ide.PluginUtils (getClientConfig) import Ide.Types as HLS import qualified Language.LSP.Server as LSP -import Language.LSP.VFS import Language.LSP.Types import qualified Language.LSP.Types as J +import qualified Language.LSP.Types.Lens as LSP +import Language.LSP.VFS import Text.Regex.TDFA.Text () import UnliftIO (MonadUnliftIO) import UnliftIO.Async (forConcurrently) @@ -44,20 +47,48 @@ import UnliftIO.Exception (catchAny) -- --------------------------------------------------------------------- -- -data Log - = LogNoEnabledPlugins - deriving Show +data Log = LogPluginError ResponseError + deriving Show instance Pretty Log where pretty = \case - LogNoEnabledPlugins -> - "extensibleNotificationPlugins no enabled plugins" + LogPluginError err -> prettyResponseError err + +-- various error message specific builders +prettyResponseError :: ResponseError -> Doc a +prettyResponseError err = errorCode <> ":" <+> errorBody + where + errorCode = pretty $ show $ err ^. LSP.code + errorBody = pretty $ err ^. LSP.message + +pluginNotEnabled :: SMethod m -> [(PluginId, b, a)] -> Text +pluginNotEnabled method availPlugins = "No plugin enabled for " <> T.pack (show method) <> ", available:\n" <> T.pack (unlines $ map (\(plid,_,_) -> show plid) availPlugins) + +pluginDoesntExist :: PluginId -> Text +pluginDoesntExist (PluginId pid) = "Plugin " <> pid <> " doesn't exist" + +commandDoesntExist :: CommandId -> PluginId -> [PluginCommand ideState] -> Text +commandDoesntExist (CommandId com) (PluginId pid) legalCmds = "Command " <> com <> " isn't defined for plugin " <> pid <> ". Legal commands are:\n" <> T.pack (unlines $ map (show . commandId) legalCmds) + +failedToParseArgs :: CommandId -- ^ command that failed to parse + -> PluginId -- ^ Plugin that created the command + -> String -- ^ The JSON Error message + -> J.Value -- ^ The Argument Values + -> Text +failedToParseArgs (CommandId com) (PluginId pid) err arg = "Error while parsing args for " <> com <> " in plugin " <> pid <> ": " <> T.pack err <> "\narg = " <> T.pack (show arg) + +-- | Build a ResponseError and log it before returning to the caller +logAndReturnError :: Recorder (WithPriority Log) -> ErrorCode -> Text -> LSP.LspT Config IO (Either ResponseError a) +logAndReturnError recorder errCode msg = do + let err = ResponseError errCode msg Nothing + logWith recorder Warning $ LogPluginError err + pure $ Left err -- | Map a set of plugins to the underlying ghcide engine. asGhcIdePlugin :: Recorder (WithPriority Log) -> IdePlugins IdeState -> Plugin Config asGhcIdePlugin recorder (IdePlugins ls) = mkPlugin rulesPlugins HLS.pluginRules <> - mkPlugin executeCommandPlugins HLS.pluginCommands <> + mkPlugin (executeCommandPlugins recorder) HLS.pluginCommands <> mkPlugin (extensiblePlugins recorder) id <> mkPlugin (extensibleNotificationPlugins recorder) id <> mkPlugin dynFlagsPlugins HLS.pluginModifyDynflags @@ -91,11 +122,11 @@ dynFlagsPlugins rs = mempty -- --------------------------------------------------------------------- -executeCommandPlugins :: [(PluginId, [PluginCommand IdeState])] -> Plugin Config -executeCommandPlugins ecs = mempty { P.pluginHandlers = executeCommandHandlers ecs } +executeCommandPlugins :: Recorder (WithPriority Log) -> [(PluginId, [PluginCommand IdeState])] -> Plugin Config +executeCommandPlugins recorder ecs = mempty { P.pluginHandlers = executeCommandHandlers recorder ecs } -executeCommandHandlers :: [(PluginId, [PluginCommand IdeState])] -> LSP.Handlers (ServerM Config) -executeCommandHandlers ecs = requestHandler SWorkspaceExecuteCommand execCmd +executeCommandHandlers :: Recorder (WithPriority Log) -> [(PluginId, [PluginCommand IdeState])] -> LSP.Handlers (ServerM Config) +executeCommandHandlers recorder ecs = requestHandler SWorkspaceExecuteCommand execCmd where pluginMap = Map.fromList ecs @@ -134,21 +165,15 @@ executeCommandHandlers ecs = requestHandler SWorkspaceExecuteCommand execCmd Just (plugin, cmd) -> runPluginCommand ide plugin cmd cmdParams -- Couldn't parse the command identifier - _ -> return $ Left $ ResponseError InvalidParams "Invalid command identifier" Nothing + _ -> logAndReturnError recorder InvalidParams "Invalid command Identifier" - runPluginCommand ide p@(PluginId p') com@(CommandId com') arg = + runPluginCommand ide p com arg = case Map.lookup p pluginMap of - Nothing -> return - (Left $ ResponseError InvalidRequest ("Plugin " <> p' <> " doesn't exist") Nothing) + Nothing -> logAndReturnError recorder InvalidRequest (pluginDoesntExist p) Just xs -> case List.find ((com ==) . commandId) xs of - Nothing -> return $ Left $ - ResponseError InvalidRequest ("Command " <> com' <> " isn't defined for plugin " <> p' - <> ". Legal commands are: " <> T.pack(show $ map commandId xs)) Nothing + Nothing -> logAndReturnError recorder InvalidRequest (commandDoesntExist com p xs) Just (PluginCommand _ _ f) -> case J.fromJSON arg of - J.Error err -> return $ Left $ - ResponseError InvalidParams ("error while parsing args for " <> com' <> " in plugin " <> p' - <> ": " <> T.pack err - <> "\narg = " <> T.pack (show arg)) Nothing + J.Error err -> logAndReturnError recorder InvalidParams (failedToParseArgs com p err arg) J.Success a -> f ide a -- --------------------------------------------------------------------- @@ -169,19 +194,15 @@ extensiblePlugins recorder xs = mempty { P.pluginHandlers = handlers } config <- Ide.PluginUtils.getClientConfig -- Only run plugins that are allowed to run on this request let fs = filter (\(_, desc, _) -> pluginEnabled m params desc config) fs' + -- Clients generally don't display ResponseErrors so instead we log any that we come across case nonEmpty fs of - Nothing -> do - logWith recorder Info LogNoEnabledPlugins - pure $ Left $ ResponseError InvalidRequest - ( "No plugin enabled for " <> T.pack (show m) - <> ", available: " <> T.pack (show $ map (\(plid,_,_) -> plid) fs) - ) - Nothing + Nothing -> logAndReturnError recorder InvalidRequest (pluginNotEnabled m fs') Just fs -> do let msg e pid = "Exception in plugin " <> T.pack (show pid) <> "while processing " <> T.pack (show m) <> ": " <> T.pack (show e) handlers = fmap (\(plid,_,handler) -> (plid,handler)) fs es <- runConcurrently msg (show m) handlers ide params let (errs,succs) = partitionEithers $ toList es + unless (null errs) $ forM_ errs $ \err -> logWith recorder Error $ LogPluginError err case nonEmpty succs of Nothing -> pure $ Left $ combineErrors errs Just xs -> do @@ -206,9 +227,7 @@ extensibleNotificationPlugins recorder xs = mempty { P.pluginHandlers = handlers -- Only run plugins that are allowed to run on this request let fs = filter (\(_, desc, _) -> pluginEnabled m params desc config) fs' case nonEmpty fs of - Nothing -> do - logWith recorder Info LogNoEnabledPlugins - pure () + Nothing -> void $ logAndReturnError recorder InvalidRequest (pluginNotEnabled m fs') Just fs -> do -- We run the notifications in order, so the core ghcide provider -- (which restarts the shake process) hopefully comes last @@ -227,7 +246,7 @@ runConcurrently -> m (NonEmpty (Either ResponseError d)) runConcurrently msg method fs a b = fmap join $ forConcurrently fs $ \(pid,f) -> otTracedProvider pid (fromString method) $ do f a b - `catchAny` (\e -> pure $ pure $ Left $ ResponseError InternalError (msg e pid) Nothing) + `catchAny` (\e -> pure $ pure $ Left $ ResponseError InternalError (msg e pid) Nothing) combineErrors :: [ResponseError] -> ResponseError combineErrors [x] = x diff --git a/ghcide/src/Development/IDE/Types/Logger.hs b/ghcide/src/Development/IDE/Types/Logger.hs index a858a5f520..6673707204 100644 --- a/ghcide/src/Development/IDE/Types/Logger.hs +++ b/ghcide/src/Development/IDE/Types/Logger.hs @@ -29,48 +29,63 @@ module Development.IDE.Types.Logger , renderStrict ) where -import Control.Concurrent (myThreadId) -import Control.Concurrent.Extra (Lock, newLock, withLock) -import Control.Concurrent.STM (atomically, - newTVarIO, writeTVar, readTVarIO, newTBQueueIO, flushTBQueue, writeTBQueue, isFullTBQueue) -import Control.Exception (IOException) -import Control.Monad (forM_, when, (>=>), unless) -import Control.Monad.IO.Class (MonadIO (liftIO)) -import Data.Foldable (for_) -import Data.Functor.Contravariant (Contravariant (contramap)) -import Data.Maybe (fromMaybe) -import Data.Text (Text) -import qualified Data.Text as T -import qualified Data.Text as Text -import qualified Data.Text.IO as Text -import Data.Time (defaultTimeLocale, formatTime, - getCurrentTime) -import GHC.Stack (CallStack, HasCallStack, - SrcLoc (SrcLoc, srcLocModule, srcLocStartCol, srcLocStartLine), - callStack, getCallStack, - withFrozenCallStack) +import Control.Concurrent (myThreadId) +import Control.Concurrent.Extra (Lock, newLock, withLock) +import Control.Concurrent.STM (atomically, + flushTBQueue, + isFullTBQueue, + newTBQueueIO, newTVarIO, + readTVarIO, + writeTBQueue, writeTVar) +import Control.Exception (IOException) +import Control.Monad (forM_, unless, when, + (>=>)) +import Control.Monad.IO.Class (MonadIO (liftIO)) +import Data.Foldable (for_) +import Data.Functor.Contravariant (Contravariant (contramap)) +import Data.Maybe (fromMaybe) +import Data.Text (Text) +import qualified Data.Text as T +import qualified Data.Text as Text +import qualified Data.Text.IO as Text +import Data.Time (defaultTimeLocale, + formatTime, + getCurrentTime) +import GHC.Stack (CallStack, HasCallStack, + SrcLoc (SrcLoc, srcLocModule, srcLocStartCol, srcLocStartLine), + callStack, getCallStack, + withFrozenCallStack) import Language.LSP.Server -import qualified Language.LSP.Server as LSP -import Language.LSP.Types (LogMessageParams (..), - MessageType (..), - SMethod (SWindowLogMessage, SWindowShowMessage), - ShowMessageParams (..)) +import qualified Language.LSP.Server as LSP +import Language.LSP.Types (LogMessageParams (..), + MessageType (..), + ResponseError, + SMethod (SWindowLogMessage, SWindowShowMessage), + ShowMessageParams (..)) #if MIN_VERSION_prettyprinter(1,7,0) -import Prettyprinter as PrettyPrinterModule -import Prettyprinter.Render.Text (renderStrict) +import Prettyprinter as PrettyPrinterModule +import Prettyprinter.Render.Text (renderStrict) #else -import Data.Text.Prettyprint.Doc as PrettyPrinterModule +import Data.Text.Prettyprint.Doc as PrettyPrinterModule import Data.Text.Prettyprint.Doc.Render.Text (renderStrict) #endif -import System.IO (Handle, IOMode (AppendMode), - hClose, hFlush, hSetEncoding, - openFile, stderr, utf8) -import qualified System.Log.Formatter as HSL -import qualified System.Log.Handler as HSL -import qualified System.Log.Handler.Simple as HSL -import qualified System.Log.Logger as HsLogger -import UnliftIO (MonadUnliftIO, displayException, - finally, try) +import Control.Lens ((^.)) +import Ide.Types (CommandId (CommandId), + PluginId (PluginId)) +import Language.LSP.Types.Lens (HasCode (code), + HasMessage (message)) +import System.IO (Handle, + IOMode (AppendMode), + hClose, hFlush, + hSetEncoding, openFile, + stderr, utf8) +import qualified System.Log.Formatter as HSL +import qualified System.Log.Handler as HSL +import qualified System.Log.Handler.Simple as HSL +import qualified System.Log.Logger as HsLogger +import UnliftIO (MonadUnliftIO, + displayException, + finally, try) data Priority -- Don't change the ordering of this type or you will mess up the Ord diff --git a/hls-plugin-api/src/Ide/PluginUtils.hs b/hls-plugin-api/src/Ide/PluginUtils.hs index 19303516ac..c5bb881b58 100644 --- a/hls-plugin-api/src/Ide/PluginUtils.hs +++ b/hls-plugin-api/src/Ide/PluginUtils.hs @@ -253,10 +253,8 @@ getNormalizedFilePath (PluginId plId) uri = handleMaybe errMsg errMsg = T.unpack $ "Error(" <> plId <> "): converting " <> getUri uri <> " to NormalizedFilePath" -- --------------------------------------------------------------------- -throwPluginError :: Monad m => PluginId -> String -> String -> ExceptT String m b -throwPluginError (PluginId who) what where' = throwE msg - where - msg = (T.unpack who) <> " failed with " <> what <> " at " <> where' +throwPluginError :: Monad m => String -> ExceptT String m b +throwPluginError = throwE handleMaybe :: Monad m => e -> Maybe b -> ExceptT e m b handleMaybe msg = maybe (throwE msg) return diff --git a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs index e240ee297d..530ced8f7a 100644 --- a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs +++ b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs @@ -97,7 +97,6 @@ codeActionHandler state plId (CodeActionParams _ _ docId currRange _) = pluginRe literalPairs = map (\lit -> (lit, alternateFormat lit)) litsInRange -- make a code action for every literal and its' alternates (then flatten the result) actions = concatMap (\(lit, alts) -> map (mkCodeAction nfp lit enabledExtensions pragma) alts) literalPairs - pure $ List actions where inCurrentRange :: Literal -> Bool diff --git a/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Internal.hs b/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Internal.hs index 0a4b1de41e..ed6ad5e534 100644 --- a/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Internal.hs +++ b/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Internal.hs @@ -203,7 +203,7 @@ incomingCalls state pluginId param = pluginResponse $ do mergeIncomingCalls case calls of Just x -> pure $ Just $ List x - Nothing -> throwPluginError callHierarchyId "Internal Error" "incomingCalls" + Nothing -> throwPluginError "incomingCalls - Internal Error" where mkCallHierarchyIncomingCall :: Vertex -> Action (Maybe CallHierarchyIncomingCall) mkCallHierarchyIncomingCall = mkCallHierarchyCall CallHierarchyIncomingCall @@ -224,7 +224,7 @@ outgoingCalls state pluginId param = pluginResponse $ do mergeOutgoingCalls case calls of Just x -> pure $ Just $ List x - Nothing -> throwPluginError callHierarchyId "Internal Error" "outgoingCalls" + Nothing -> throwPluginError "outgoingCalls - Internal Error" where mkCallHierarchyOutgoingCall :: Vertex -> Action (Maybe CallHierarchyOutgoingCall) mkCallHierarchyOutgoingCall = mkCallHierarchyCall CallHierarchyOutgoingCall diff --git a/test/functional/Format.hs b/test/functional/Format.hs index 43e9366843..af90fc7a9c 100644 --- a/test/functional/Format.hs +++ b/test/functional/Format.hs @@ -47,7 +47,7 @@ providerTests = testGroup "formatting provider" [ testCase "respects none" $ runSessionWithConfig (formatConfig "none") hlsCommand fullCaps "test/testdata/format" $ do doc <- openDoc "Format.hs" "haskell" resp <- request STextDocumentFormatting $ DocumentFormattingParams Nothing doc (FormattingOptions 2 True Nothing Nothing Nothing) - liftIO $ resp ^. LSP.result @?= Left (ResponseError InvalidRequest "No plugin enabled for STextDocumentFormatting, available: []" Nothing) + liftIO $ resp ^. LSP.result @?= Left (ResponseError InvalidRequest "No plugin enabled for STextDocumentFormatting, available:\nPluginId \"floskell\"\nPluginId \"fourmolu\"\nPluginId \"ormolu\"\nPluginId \"stylish-haskell\"\nPluginId \"brittany\"\n" Nothing) , requiresOrmoluPlugin . requiresFloskellPlugin $ testCase "can change on the fly" $ runSession hlsCommand fullCaps "test/testdata/format" $ do formattedOrmolu <- liftIO $ T.readFile "test/testdata/format/Format.ormolu.formatted.hs" From d4eea6627cd679be97b795187b7f5115186775b7 Mon Sep 17 00:00:00 2001 From: Nick Suchecki <40047416+drsooch@users.noreply.github.com> Date: Sun, 3 Jul 2022 07:08:14 -0400 Subject: [PATCH 007/213] Missed this during PR fixes (#3009) --- ghcide/src/Development/IDE/Plugin/HLS.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Plugin/HLS.hs b/ghcide/src/Development/IDE/Plugin/HLS.hs index 5a2f0a38a3..bd192f18e8 100644 --- a/ghcide/src/Development/IDE/Plugin/HLS.hs +++ b/ghcide/src/Development/IDE/Plugin/HLS.hs @@ -202,7 +202,7 @@ extensiblePlugins recorder xs = mempty { P.pluginHandlers = handlers } handlers = fmap (\(plid,_,handler) -> (plid,handler)) fs es <- runConcurrently msg (show m) handlers ide params let (errs,succs) = partitionEithers $ toList es - unless (null errs) $ forM_ errs $ \err -> logWith recorder Error $ LogPluginError err + unless (null errs) $ forM_ errs $ \err -> logWith recorder Warning $ LogPluginError err case nonEmpty succs of Nothing -> pure $ Left $ combineErrors errs Just xs -> do From ffef347fa77238870480cd95d74111f16f590122 Mon Sep 17 00:00:00 2001 From: Michael Peyton Jones Date: Mon, 4 Jul 2022 09:42:54 +0100 Subject: [PATCH 008/213] Disable flaky test on Windows (#3008) I don't think anyone is going to fix this soon, let's just disable it so we can move on. Fixes #2997. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- ghcide/test/exe/Main.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index af8ae70de2..2b0dfd0ddb 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -4439,7 +4439,11 @@ findDefinitionAndHoverTests = let , test no yes holeL65 hleInfo2 "hole with variable" , test no yes cccL17 docLink "Haddock html links" , testM yes yes imported importedSig "Imported symbol" - , testM yes yes reexported reexportedSig "Imported symbol (reexported)" + , if | isWindows -> + -- Flaky on Windows: https://github.com/haskell/haskell-language-server/issues/2997 + testM no yes reexported reexportedSig "Imported symbol (reexported)" + | otherwise -> + testM yes yes reexported reexportedSig "Imported symbol (reexported)" , if | ghcVersion == GHC90 && isWindows -> test no broken thLocL57 thLoc "TH Splice Hover" | ghcVersion == GHC92 && (isWindows || isMac) -> From 344323be54c95bc7f3dc24908300132bba20a4e8 Mon Sep 17 00:00:00 2001 From: Kobayashi Date: Tue, 5 Jul 2022 11:49:32 +0800 Subject: [PATCH 009/213] re-enable haddock (#3015) * re-enable haddock * fix it for old GHC Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- cabal.project | 3 +- .../hls-hlint-plugin/hls-hlint-plugin.cabal | 6 ---- .../hls-hlint-plugin/src/Ide/Plugin/Hlint.hs | 31 ++++++++++--------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/cabal.project b/cabal.project index eb5147ba70..19313edee0 100644 --- a/cabal.project +++ b/cabal.project @@ -38,8 +38,7 @@ optional-packages: vendored/*/*.cabal tests: true package * - -- ghc 8.10 cannot build ghc-lib 9.2 with --haddock - -- ghc-options: -haddock + ghc-options: -haddock test-show-details: direct write-ghc-environment-files: never diff --git a/plugins/hls-hlint-plugin/hls-hlint-plugin.cabal b/plugins/hls-hlint-plugin/hls-hlint-plugin.cabal index d17d788be9..82f60839e6 100644 --- a/plugins/hls-hlint-plugin/hls-hlint-plugin.cabal +++ b/plugins/hls-hlint-plugin/hls-hlint-plugin.cabal @@ -25,11 +25,6 @@ flag pedantic default: False manual: True -flag ghc-lib - default: True - manual: True - description: Use ghc-lib types (requires hlint to be built with ghc-lib) - library exposed-modules: Ide.Plugin.Hlint hs-source-dirs: src @@ -61,7 +56,6 @@ library , transformers , unordered-containers , apply-refact >=0.9.0.0 - , ghc-lib , ghc-lib-parser , ghc-lib-parser-ex diff --git a/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs b/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs index 0919badef1..1b58e21919 100644 --- a/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs +++ b/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs @@ -4,24 +4,24 @@ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PackageImports #-} {-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StrictData #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE MultiWayIf #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE StrictData #-} {-# OPTIONS_GHC -Wno-orphans #-} #ifdef HLINT_ON_GHC_LIB -#define MIN_GHC_API_VERSION(x,y,z) MIN_VERSION_ghc_lib(x,y,z) +#define MIN_GHC_API_VERSION(x,y,z) MIN_VERSION_ghc_lib_parser(x,y,z) #else #define MIN_GHC_API_VERSION(x,y,z) MIN_VERSION_ghc(x,y,z) #endif @@ -44,8 +44,8 @@ import Data.Aeson.Types (FromJSON (. Value (..)) import qualified Data.ByteString as BS import Data.Default -import qualified Data.HashMap.Strict as Map import Data.Hashable +import qualified Data.HashMap.Strict as Map import Data.Maybe import qualified Data.Text as T import qualified Data.Text.Encoding as T @@ -67,13 +67,15 @@ import Development.IDE.GHC.Compat (DynFlags, topDir, wopt) import qualified Development.IDE.GHC.Compat.Util as EnumSet -import "ghc-lib" GHC hiding - (DynFlags (..), - RealSrcSpan, - ms_hspp_opts) -import qualified "ghc-lib" GHC + #if MIN_GHC_API_VERSION(9,0,0) -import "ghc-lib-parser" GHC.Types.SrcLoc (BufSpan) +import "ghc-lib-parser" GHC.Types.SrcLoc hiding + (RealSrcSpan) +import qualified "ghc-lib-parser" GHC.Types.SrcLoc as GHC +#else +import "ghc-lib-parser" SrcLoc hiding + (RealSrcSpan) +import qualified "ghc-lib-parser" SrcLoc as GHC #endif import "ghc-lib-parser" GHC.LanguageExtensions (Extension) import Language.Haskell.GhclibParserEx.GHC.Driver.Session as GhclibParserEx (readExtension) @@ -89,7 +91,8 @@ import System.IO (IOMode (Wri import System.IO.Temp #else import Development.IDE.GHC.Compat hiding - (setEnv, (<+>)) + (setEnv, + (<+>)) import GHC.Generics (Associativity (LeftAssociative, NotAssociative, RightAssociative)) #if MIN_GHC_API_VERSION(9,2,0) import Language.Haskell.GHC.ExactPrint.ExactPrint (deltaOptions) From 6dcc0eeadedffdc2b2fb65ae41f6956bb5762a39 Mon Sep 17 00:00:00 2001 From: 0rphee <79347623+0rphee@users.noreply.github.com> Date: Tue, 5 Jul 2022 10:39:59 -0500 Subject: [PATCH 010/213] add Helix to configuration.md (#3014) --- docs/configuration.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/configuration.md b/docs/configuration.md index e91bf55532..b1ca00d8f4 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -414,3 +414,8 @@ roots = ["Setup.hs", "stack.yaml", "*.cabal"] command = "haskell-language-server-wrapper" args = ["--lsp"] ``` + +### [Helix](https://github.com/helix-editor/helix) + +Once `haskell-language-server-wrapper` is installed in your system, it will be used automatically by the editor. +For more details please refer to the [helix guide on installing language servers](https://github.com/helix-editor/helix/wiki/How-to-install-the-default-language-servers) From 1bc1def498c69681cef934c9b1d9cbb460591788 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 6 Jul 2022 13:44:05 +0200 Subject: [PATCH 011/213] Hlint: CodeAction with isPreferred (#3018) * Hlint: CodeAction with isPreferred > A refactoring should be marked preferred if it is the most reasonable choice of actions to take. For hlint it is the likeliest choice to apply the fix. Ignore the rule for the complete module is preferred less. * Hlint: Comment for isPreferred Including the rationale for why we're making this decision as a comment in the code. --- plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs b/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs index 1b58e21919..9f75dff9f3 100644 --- a/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs +++ b/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs @@ -446,23 +446,25 @@ diagnosticToCodeActions dynFlags fileContents pluginId documentId diagnostic Nothing Nothing = catMaybes + -- Applying the hint is marked preferred because it addresses the underlying error. + -- Disabling the rule isn't, because less often used and configuration can be adapted. [ if | isHintApplicable , let applyHintTitle = "Apply hint \"" <> hint <> "\"" applyHintArguments = [toJSON (AOP (documentId ^. LSP.uri) start hint)] applyHintCommand = mkLspCommand pluginId "applyOne" applyHintTitle (Just applyHintArguments) -> - Just (mkCodeAction applyHintTitle diagnostic Nothing (Just applyHintCommand)) + Just (mkCodeAction applyHintTitle diagnostic Nothing (Just applyHintCommand) True) | otherwise -> Nothing - , Just (mkCodeAction suppressHintTitle diagnostic (Just suppressHintWorkspaceEdit) Nothing) + , Just (mkCodeAction suppressHintTitle diagnostic (Just suppressHintWorkspaceEdit) Nothing False) ] | otherwise = [] -mkCodeAction :: T.Text -> LSP.Diagnostic -> Maybe LSP.WorkspaceEdit -> Maybe LSP.Command -> LSP.CodeAction -mkCodeAction title diagnostic workspaceEdit command = +mkCodeAction :: T.Text -> LSP.Diagnostic -> Maybe LSP.WorkspaceEdit -> Maybe LSP.Command -> Bool -> LSP.CodeAction +mkCodeAction title diagnostic workspaceEdit command isPreferred = LSP.CodeAction { _title = title , _kind = Just LSP.CodeActionQuickFix , _diagnostics = Just (LSP.List [diagnostic]) - , _isPreferred = Nothing + , _isPreferred = Just isPreferred , _disabled = Nothing , _edit = workspaceEdit , _command = command From a32db0b296d16611def15fb1d7d377fa0505beea Mon Sep 17 00:00:00 2001 From: Michael Peyton Jones Date: Wed, 6 Jul 2022 14:36:22 +0100 Subject: [PATCH 012/213] Hlint more partial functions, and Debug.Trace (#3000) * Add partial functions under alternative import names * Ban tracing functions also Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .hlint.yaml | 90 ++++++++++++++++++- .../test/Main.hs | 1 - 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/.hlint.yaml b/.hlint.yaml index f50ea75cc3..f8068e8495 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -84,6 +84,32 @@ - Compat.HieBin # Partial functions + + # For some reason we need to check fucntions which + # are typically exported multiple ways under both names, + # see https://github.com/ndmitchell/hlint/issues/1389 + - name: Prelude.head + within: + - Main + - Development.Benchmark.Rules + - Development.IDE.Plugin.CodeAction + - Development.IDE.Plugin.Completions + - Development.IDE.Plugin.CodeAction.ExactPrint + - Development.IDE.Spans.Documentation + - Ide.Plugin.CallHierarchy.Internal + - Ide.Plugin.Eval.Code + - Ide.Plugin.Eval.Util + - Ide.Plugin.Floskell + - Ide.Plugin.ModuleName + - Ide.Plugin.Rename + - Ide.Plugin.Class.ExactPrint + - TExpectedActual + - TRigidType + - RightToLeftFixities + - Typeclass + - Wingman.Judgements + - Wingman.Machinery + - name: Data.List.head within: - Main @@ -94,11 +120,18 @@ - Development.IDE.Session - Development.IDE.Spans.Documentation - Ide.Plugin.CallHierarchy.Internal - - TExpectedActual - - TRigidType - - TRigidType - Ide.Plugin.Class - Wingman.Tactics + - TExpectedActual + - TRigidType + + - name: Prelude.tail + within: + - Main + - Development.Benchmark.Rules + - Development.IDE.Plugin.CodeAction + - Development.IDE.Plugin.CodeAction.ExactPrint + - UnificationSpec - name: Data.List.tail within: @@ -111,11 +144,31 @@ - IDE.Plugin.Eval.Util - UnificationSpec + - name: Prelude.last + within: + - Main + - Development.IDE.Plugin.CodeAction + - Development.IDE.Plugin.CodeAction.ExactPrint + - Development.IDE.Spans.Common + - Development.IDE.Graph.Internal.Types + - Ide.PluginUtils + - Ide.Plugin.Eval.Parse.Comments + - Ide.Plugin.Eval.CodeLens + - name: Data.List.last within: - GenChangelogs - Main + - name: Prelude.init + within: + - Main + - Development.IDE.Spans.Common + - Ide.PluginUtils + - Wingman.Metaprogramming.Parser + - Development.Benchmark.Rules + - ErrorGivenPartialSignature + - name: Data.List.init within: [] @@ -125,6 +178,16 @@ - name: Data.List.foldr1' within: [] + - name: "Prelude.!!" + within: + - Main + - Development.IDE.Plugin.CodeAction + - Development.IDE.Plugin.Completions.Logic + - Development.IDE.Spans.Documentation + - TErrorGivenPartialSignature + - Wingman.CaseSplit + - Wingman.Simplify + - name: "Data.List.!!" within: - Main @@ -178,6 +241,27 @@ - name: "GHC.Arr.!" within: [] + # Tracing functions + # We ban an explicit list rather than the + # Debug.Trace, because that module also + # includes the eventlog tracing functions, + # which are legitimate to use. + - name: + - Debug.Trace.trace + - Debug.Trace.traceId + - Debug.Trace.traceShow + - Debug.Trace.traceShowId + - Debug.Trace.traceStack + - Debug.Trace.traceIO + - Debug.Trace.traceM + - Debug.Trace.traceShowM + - Debug.Trace.putTraceMsg + within: + - Development.IDE.Core.Compile + - Development.IDE.Graph.Internal.Database + - Development.IDE.GHC.Util + - Wingman.Debug + # We really do not want novel usages of restricted functions, and mere # Warning is not enough to prevent those consistently; you need a build failure. - error: {name: Avoid restricted function} diff --git a/plugins/hls-alternate-number-format-plugin/test/Main.hs b/plugins/hls-alternate-number-format-plugin/test/Main.hs index a5fcc73f73..f3ea16bb15 100644 --- a/plugins/hls-alternate-number-format-plugin/test/Main.hs +++ b/plugins/hls-alternate-number-format-plugin/test/Main.hs @@ -7,7 +7,6 @@ import Data.Either (rights) import Data.List (find) import Data.Text (Text) import qualified Data.Text as T -import Debug.Trace import qualified Ide.Plugin.AlternateNumberFormat as AlternateNumberFormat import qualified Ide.Plugin.Conversion as Conversion import Language.LSP.Types (toEither) From 293704f612c4942e2cab422c531ade7d7d7f6670 Mon Sep 17 00:00:00 2001 From: Michael Peyton Jones Date: Thu, 7 Jul 2022 00:04:46 +0100 Subject: [PATCH 013/213] Improve troubleshooting and installation docs a bit (#3004) * Add material about using versions of GHC that aren't supported yet * Add a note about clearing out the build cache * Add note about cabal update to ghcup instructions * Minor improvements to installing doc * Tweak ghcup docs more * Minor fixes Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- docs/installation.md | 77 +++++++++++++++++----------------- docs/supported-versions.md | 84 +++++++++++++++++++++++++------------- docs/troubleshooting.md | 13 ++++-- 3 files changed, 104 insertions(+), 70 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index 12abc3aa23..e231fb9ee6 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,17 +1,10 @@ # Installation -A typical haskell-language-server installation consists of: - -- One binary file for each supported ghc version: `haskell-language-server-${ghcVersion}` -- Another binary named `haskell-language-version-wrapper` which analyzes the project or file in the current working dir - and calls the appropiate `haskell-language-server-${ghcVersion}` variant. - - It accepts all executable arguments from the plain `haskell-language-server` - ## Prerequisites -- For standalone `.hs`/`.lhs` files, [ghc](https://www.haskell.org/ghc/) must be installed and on the PATH. The easiest way to install it is with [ghcup](https://www.haskell.org/ghcup/) or [chocolatey](https://community.chocolatey.org/packages/ghc) on Windows. -- For Cabal based projects, both ghc and [cabal-install](https://www.haskell.org/cabal/) must be installed and on the PATH. It can also be installed with [ghcup](https://www.haskell.org/ghcup/) or [chocolatey](https://community.chocolatey.org/packages/cabal) on Windows. -- For Stack based projects, [stack](http://haskellstack.org) must be installed and on the PATH. +- For standalone `.hs`/`.lhs` files, [ghc](https://www.haskell.org/ghc/) must be installed and on the `PATH`. The easiest way to install it is with [ghcup](https://www.haskell.org/ghcup/) or [chocolatey](https://community.chocolatey.org/packages/ghc) on Windows. +- For Cabal based projects, both ghc and [cabal-install](https://www.haskell.org/cabal/) must be installed and on the `PATH`. It can also be installed with [ghcup](https://www.haskell.org/ghcup/) or [chocolatey](https://community.chocolatey.org/packages/cabal) on Windows. +- For Stack based projects, [stack](http://haskellstack.org) must be installed and on the `PATH`. ## ghcup @@ -21,45 +14,52 @@ If you are using [`ghcup`](https://www.haskell.org/ghcup/) to manage your instal ghcup install hls ``` -You can check if HLS is available for your platorm via ghcup here: . +You can check if HLS is available for your platform via `ghcup` here: . You can also install HLS from source without checking out the code manually: ```bash -ghcup compile hls -v 1.6.1.0 --ghc 8.10.7 +# `ghcup compile` uses cabal under the hood to build, so you may +# want to run `cabal update` beforehand +cabal update +ghcup compile hls -v $HLS_VERSION --ghc $GHC_VERSION ``` -Install HLS for multiple GHC versions: -``` -ghcup compile hls -v 1.6.1.0 --ghc 8.10.7 --ghc 8.8.4 +Make sure to check `ghcup compile hls --help` for more complete help. + +Examples: + +Install HLS 1.7.0.0 for GHC 8.10.7, or for multiple GHC versions: +```bash +ghcup compile hls -v 1.7.0.0 --ghc 8.10.7 +ghcup compile hls -v 1.7.0.0 --ghc 8.10.7 --ghc 9.2.3 ``` -Use a different `cabal.project` for a GHC version: +Install HLS from master, or a specific commit: ``` -ghcup compile hls -v 1.6.1.0 --ghc 9.2.1 --cabal-project cabal.project +ghcup compile hls -g master --ghc 9.2.3 +ghcup compile hls -g 510bd51e46fea8fb51ddfaa60bba505f0663497d --ghc 9.2.3 ``` -Check `ghcup compile hls --help` for a full list of compilation options. - ## Installation from source -Direct installation from Source, while possible via `cabal install haskell-language-server` +Direct installation from source, while possible via `cabal install haskell-language-server` and `stack install --stack-yaml stack-.yaml`, is not recommended for most people. -Said command builds the `haskell-language-server` binary and installs it in the default Cabal binaries folder, +Said command builds the `haskell-language-server` binary and installs it in the default `cabal` binaries folder, but the binary will only work with projects that use the same GHC version that built it. ### Common pre-requirements -- `stack` or `cabal` must be in your PATH - - You need stack version >= 2.1.1 or cabal >= 2.4.0.0 -- `git` must be in your PATH +- `stack` or `cabal` must be in your `PATH` + - You need `stack` version >= 2.1.1 or `cabal` >= 2.4.0.0 +- `git` must be in your `PATH` - The directory where `stack`or `cabal` put the binaries must be in you PATH: - - For stack you can get it with `stack path --local-bin` - - For cabal it is by default `$HOME/.cabal/bin` in linux and `%APPDATA%\cabal\bin` in windows. + - For `stack` you can get it with `stack path --local-bin` + - For `cabal` it is by default `$HOME/.cabal/bin` in Linux and `%APPDATA%\cabal\bin` in windows. Tip: you can quickly check if some command is in your path by running the command. If you receive some meaningful output instead of "command not found"-like message -then it means you have the command in PATH. +then it means you have the command in `PATH`. ### Linux-specific pre-requirements @@ -87,15 +87,6 @@ sudo apt install libicu-dev libncurses-dev libgmp-dev zlib1g-dev sudo dnf install libicu-devel ncurses-devel zlib-devel ``` -**Gentoo** - -Haskell Language Server is available via the Haskell overlay. Follow the instructions [here](https://github.com/gentoo-haskell/gentoo-haskell) to install the overlay, then run: - -```bash -emerge -av dev-util/haskell-language-server -``` -Depending on your system setup, you may need to enable the unstable flag for this package before install, and possible also for the dependencies. If you enabled the ~testing versions as explained in the gentoo-haskell overlay instructions, then this won't be necessary. - ### Windows-specific pre-requirements In order to avoid problems with long paths on Windows you can do either one of the following: @@ -120,7 +111,7 @@ If you are using [`chocolatey`](https://chocolatey.org/) to manage your installa ```bash choco install haskell-language-server -```` +``` ## Visual Studio Code @@ -131,7 +122,7 @@ If you need to find the binaries, please consult the [documentation](https://git ## Pre-built binaries There are pre-built binaries available from the [releases page](https://github.com/haskell/haskell-language-server/releases) for Linux, Windows and macOS. -To install, download the `haskell-language-server-wrapper` executable for your platform as well as any `haskell-language-server` executables for the GHC versions you plan on working with, and either put them on your PATH or point your client to them. +To install, download the `haskell-language-server-wrapper` executable for your platform as well as any `haskell-language-server` executables for the GHC versions you plan on working with, and either put them on your `PATH` or point your client to them. ## Arch Linux @@ -143,6 +134,7 @@ See [ArchWiki](https://wiki.archlinux.org/index.php/Haskell) for the details of ## Fedora + Binary packages for Fedora are available from [this Copr repo](https://copr.fedorainfracloud.org/coprs/petersen/haskell-language-server), built against the official Fedora ghc package. @@ -157,6 +149,15 @@ pkg install hs-haskell-language-server to install it. At the moment, HLS installed this way only supports the same GHC version as the ports one. +## Gentoo + +Haskell Language Server is available via the Haskell overlay. Follow the instructions [here](https://github.com/gentoo-haskell/gentoo-haskell) to install the overlay, then run: + +```bash +emerge -av dev-util/haskell-language-server +``` +Depending on your system setup, you may need to enable the unstable flag for this package before install, and possible also for the dependencies. If you enabled the ~testing versions as explained in the gentoo-haskell overlay instructions, then this won't be necessary. + ## Installation from Hackage Direct installation from Hackage, while possible via `cabal install haskell-language-server`, is not recommended for most people. diff --git a/docs/supported-versions.md b/docs/supported-versions.md index 2140906abf..3948daeab1 100644 --- a/docs/supported-versions.md +++ b/docs/supported-versions.md @@ -4,40 +4,52 @@ The current support for different GHC versions is given in the following table. -| GHC version | Last supporting HLS version | Deprecation status | -| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | -| 9.2.3 | [current](https://github.com/haskell/haskell-language-server/releases/latest) ([partial](https://github.com/haskell/haskell-language-server/issues/2982)) -| 9.2.2 | [current](https://github.com/haskell/haskell-language-server/releases/latest) ([partial](https://github.com/haskell/haskell-language-server/issues/2982)) -| 9.2.1 | [1.7.0.0](https://github.com/haskell/haskell-language-server/releases/tag/1.7.0.0) | deprecated | -| 9.0.2 | [current](https://github.com/haskell/haskell-language-server/releases/latest) | | -| 9.0.1 | [1.6.1.0](https://github.com/haskell/haskell-language-server/releases/tag/1.6.1.0) | deprecated | -| 8.10.7 | [current](https://github.com/haskell/haskell-language-server/releases/latest) | | -| 8.10.6 | [1.6.1.0](https://github.com/haskell/haskell-language-server/releases/tag/1.6.1.0) | deprecated | -| 8.10.5 | [1.5.1](https://github.com/haskell/haskell-language-server/releases/tag/1.5.1) | deprecated | -| 8.10.4 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | -| 8.10.3 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | -| 8.10.2 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | -| 8.10.1 | [0.9.0](https://github.com/haskell/haskell-language-server/releases/tag/0.9.0) | deprecated | -| 8.8.4 | [current](https://github.com/haskell/haskell-language-server/releases/latest) | will be deprecated after LTS and HLS full support for ghc-9.2 | -| 8.8.3 | [1.5.1](https://github.com/haskell/haskell-language-server/releases/1.5.1) | deprecated | -| 8.8.2 | [1.2.0](https://github.com/haskell/haskell-language-server/releases/tag/1.2.0) | deprecated | -| 8.6.5 | [current](https://github.com/haskell/haskell-language-server/releases/latest) | will be deprecated after LTS and HLS full suppot for ghc-9.2 | -| 8.6.4 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | - -GHC versions not in the list have never been supported by HLS, or are not planned. LTS stands for [Stackage](https://www.stackage.org/) Long Term Support. +Last supporting HLS version: +- "next": this GHC version is supported in master, and will be in the next released version of HLS. +- "latest": this GHC version is one of the actively supported versions (see below) and is supported in the latest released version of HLS. +- specific version number: this GHC version is no longer one of the actively supported versions, and the last version of HLS which supports it is listed. + +Support status (see the support policy below for more details): +- "supported": this version of GHC is currently actively supported +- "deprecated": this version of GHC was supported in the past, but is now deprecated +- "will be deprecated ...": this version of GHC has special deprecation conditions that deviate from the support policy +- "partial": not all features and plugins work, see the plugin support table and any linked issues for more details + +| GHC version | Last supporting HLS version | Support status | +| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | +| 9.2.3 | next | supported, ([partial](https://github.com/haskell/haskell-language-server/issues/2982)) | +| 9.2.2 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported, ([partial](https://github.com/haskell/haskell-language-server/issues/2982)) | +| 9.2.1 | [1.7.0.0](https://github.com/haskell/haskell-language-server/releases/tag/1.7.0.0) | deprecated | +| 9.0.2 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported | +| 9.0.1 | [1.6.1.0](https://github.com/haskell/haskell-language-server/releases/tag/1.6.1.0) | deprecated | +| 8.10.7 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported | +| 8.10.6 | [1.6.1.0](https://github.com/haskell/haskell-language-server/releases/tag/1.6.1.0) | deprecated | +| 8.10.5 | [1.5.1](https://github.com/haskell/haskell-language-server/releases/tag/1.5.1) | deprecated | +| 8.10.4 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | +| 8.10.3 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | +| 8.10.2 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | +| 8.10.1 | [0.9.0](https://github.com/haskell/haskell-language-server/releases/tag/0.9.0) | deprecated | +| 8.8.4 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | will be deprecated after LTS and HLS full support for ghc-9.2 | +| 8.8.3 | [1.5.1](https://github.com/haskell/haskell-language-server/releases/1.5.1) | deprecated | +| 8.8.2 | [1.2.0](https://github.com/haskell/haskell-language-server/releases/tag/1.2.0) | deprecated | +| 8.6.5 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | will be deprecated after LTS and HLS full suppot for ghc-9.2 | +| 8.6.4 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | + + +GHC versions not in the list have never been supported by HLS. LTS stands for [Stackage](https://www.stackage.org/) Long Term Support. The policy for when we deprecate support for versions of GHC is given below. The table reflects that, but we may decide to deviate from it for good reasons. Additionally, some plugins do not have support for some GHC versions, as shown in the following table. As such, the functionality provided by those plugins is not available in HLS when using a GHC version which they do not support. -Sometimes a plugin will be supported in the prebuilt binaries but not in a HLS binary installed from Hackage. +Sometimes a plugin will be supported in the pre-built binaries but not in a HLS binary installed from Hackage. ### Plugins support by GHC version | Plugin | Unsupported GHC versions | |-------------------------------------|--------------------------| | `hls-alternate-number-plugin` | | -| `hls-brittany-plugin` | 9.2 | +| `hls-brittany-plugin` | 9.2 | | `hls-call-hierarchy-plugin` | | | `hls-class-plugin` | | | `hls-eval-plugin` | | @@ -66,11 +78,27 @@ In the future, we may extend the existing discovery mechanisms (`haskell-languag Users of a deprecated minor version (where the major version is still supported) can try building the latest HLS from source, which will likely still work, since the GHC API tends to remain compatible across minor versions. +### Using GHC versions not yet supported in a HLS release + +Some users may wish to use a version of GHC that is not yet supported by a released version of HLS. +In particular, this means that pre-built binaries will not be available for that GHC version. + +The easiest thing to do in this case is to build HLS from source yourself. +This can be done easily with `ghcup`, see the examples for `ghcup compile` on the [installation page](./installation.md). + +Generally, if a version of GHC is supported by HLS on master _or_ is a new minor version of a GHC version that is supported by HLS on master, then compiling from source is likely to work. +Major versions of GHC which are not supported by HLS on master are extremely unlikely to work. + ## GHC version deprecation policy ### Major versions -A major GHC version is a "legacy" version if it is 3 or more major versions behind the GHC version used in the newest Stackage LTS. +A major GHC version is a "legacy" version if it is 3 or more major versions behind the latest GHC version that is + +1. Fully supported by HLS +2. Used in the a Stackage LTS + +For example, if 9.2 is the latest major version fully supported by HLS and used in a Stackage LTS, then the 8.8 major version and older will be legacy. HLS will support all non-legacy major versions of GHC. @@ -86,15 +114,15 @@ We will warn users about the upcoming deprecation of a GHC version in the notes ### Why deprecate older versions of GHC? -`haskell-language-server`(HLS) is highly tied to the ghc api. This imposes a high maintenance cost: +`haskell-language-server`(HLS) is highly tied to the GHC API. This imposes a high maintenance cost: - The codebase is littered with conditional logic, -- We own auxiliary packages to support older versions of ghc. +- We own auxiliary packages to support older versions of GHC. - CI has to cover all the supported versions. -So we need to limit the ghc support to save maintainers and contributors time and reduce CI resources. +So we need to limit the GHC support to save maintainers and contributors time and reduce CI resources. -At same time we aim to support the right balance of ghc versions to minimize impact to final users. +At same time we aim to support the right balance of GHC versions to minimize impact to final users. ### What factors do we take into account when deprecating a version? diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 153f34275a..d92da65f3f 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -117,6 +117,13 @@ One strategy for diagnosing this is simply disable all plugins, check if the iss There is a configuration JSON snippet which disables all plugins [here](https://github.com/haskell/haskell-language-server/issues/2151#issuecomment-911397030). +### Clearing HLS's build cache + +HLS builds the dependencies of your project in a separate directory to avoid clashing with your normal build tools. +Sometimes clearing this out can help if you have persistent build problems. +The cache directory is at `$HOME/.cache/hie-bios`. +You may be able to identify a specific subdirectory that relates to your project, but it should always be safe to delete the whole thing, at worst it will cause HLS to redo build work next time it opens a project. + ## Diagnosing problems with the client The most important thing to do is to consult the client's documentation. @@ -139,12 +146,10 @@ Normally, we ship binaries for multiple versions and `haskell-language-server-wr If you see an error about HLS being compiled with the wrong version of GHC, then you either need to install the correct one (if you installed it yourself), or there is something going wrong with the wrapper selecting the right HLS binary to launch. -### Unsupported GHC version +### Unsupported GHC version or missing binaries HLS does not support every GHC version - there are a lot of them! -Please see the [supported versions page](./supported-versions.md) for more information. - -In particular, support for GHC 9.0 and 9.2 is only partial; some features are unavailable with GHC >= 9.0.1. +Please see the [supported versions page](./supported-versions.md) for more information, including what to do if you need binaries for a version that is not yet supported by a HLS release. ### Missing server or build tools From ffefe761c8210c6b4a0c5092935f34767a3cd827 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CB=8Cbod=CA=B2=C9=AA=CB=88=C9=A1r=CA=B2im?= Date: Thu, 7 Jul 2022 00:17:38 +0100 Subject: [PATCH 014/213] hls-graph: add lower bound for async (#3021) `Control.Concurrent.Async` is not available before `async-2.0` Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- hls-graph/hls-graph.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hls-graph/hls-graph.cabal b/hls-graph/hls-graph.cabal index 2edcbe3aa5..1cb975dc51 100644 --- a/hls-graph/hls-graph.cabal +++ b/hls-graph/hls-graph.cabal @@ -61,7 +61,7 @@ library hs-source-dirs: src build-depends: , aeson - , async + , async >= 2.0 , base >=4.12 && <5 , bytestring , containers From 0f6cd41d51e1dd81ddeb117ab949ceb1f38e68cf Mon Sep 17 00:00:00 2001 From: Oliver Madine <30090176+OliverMadine@users.noreply.github.com> Date: Thu, 7 Jul 2022 12:34:18 +0100 Subject: [PATCH 015/213] Renaming of indirect references (RecordFieldPuns) (#3013) * test: add tests for record puns * feat: rename indirect references refactor: remove "safe" from function names * test: ignore record field tests for ghc92 (#2915) * test: ignore record field tests for ghc90 (#2915) * fix: update record field test ignore message * expand comment about indirect reference renaming * fix: find all punned references * test: ignore record field pun test for ghc > 9 * docs: mention test in indirect pun explaination * link issue for ignored record field rename tests Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../src/Ide/Plugin/Rename.hs | 80 +++++++++++-------- plugins/hls-rename-plugin/test/Main.hs | 13 ++- .../test/testdata/FieldPuns.expected.hs | 8 ++ .../test/testdata/FieldPuns.hs | 8 ++ .../test/testdata/IndirectPuns.expected.hs | 8 ++ .../test/testdata/IndirectPuns.hs | 8 ++ .../hls-rename-plugin/test/testdata/hie.yaml | 2 + 7 files changed, 92 insertions(+), 35 deletions(-) create mode 100644 plugins/hls-rename-plugin/test/testdata/FieldPuns.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/FieldPuns.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/IndirectPuns.expected.hs create mode 100644 plugins/hls-rename-plugin/test/testdata/IndirectPuns.hs diff --git a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs index 6d051d96de..6d7dbea5d5 100644 --- a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs +++ b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs @@ -24,7 +24,7 @@ import Data.Generics import Data.Hashable import Data.HashSet (HashSet) import qualified Data.HashSet as HS -import Data.List.Extra +import Data.List.Extra hiding (length) import qualified Data.Map as M import Data.Maybe import Data.Mod.Word @@ -42,7 +42,6 @@ import Development.IDE.GHC.ExactPrint import Development.IDE.Spans.AtPoint import Development.IDE.Types.Location import HieDb.Query -import Ide.Plugin.Config import Ide.Plugin.Properties import Ide.PluginUtils import Ide.Types @@ -65,16 +64,28 @@ descriptor pluginId = (defaultPluginDescriptor pluginId) renameProvider :: PluginMethodHandler IdeState TextDocumentRename renameProvider state pluginId (RenameParams (TextDocumentIdentifier uri) pos _prog newNameText) = pluginResponse $ do - nfp <- safeUriToNfp uri - oldName <- getNameAtPos state nfp pos - refLocs <- refsAtName state nfp oldName + nfp <- handleUriToNfp uri + directOldNames <- getNamesAtPos state nfp pos + directRefs <- concat <$> mapM (refsAtName state nfp) directOldNames + + {- References in HieDB are not necessarily transitive. With `NamedFieldPuns`, we can have + indirect references through punned names. To find the transitive closure, we do a pass of + the direct references to find the references for any punned names. + See the `IndirectPuns` test for an example. -} + indirectOldNames <- concat . filter ((>1) . Prelude.length) <$> + mapM (uncurry (getNamesAtPos state) . locToFilePos) directRefs + let oldNames = indirectOldNames ++ directOldNames + refs <- HS.fromList . concat <$> mapM (refsAtName state nfp) oldNames + + -- Validate rename crossModuleEnabled <- lift $ usePropertyLsp #crossModule pluginId properties - unless crossModuleEnabled $ failWhenImportOrExport state nfp refLocs oldName - when (isBuiltInSyntax oldName) $ - throwE ("Invalid rename of built-in syntax: \"" ++ showName oldName ++ "\"") + unless crossModuleEnabled $ failWhenImportOrExport state nfp refs oldNames + when (any isBuiltInSyntax oldNames) $ throwE "Invalid rename of built-in syntax" + + -- Perform rename let newName = mkTcOcc $ T.unpack newNameText - filesRefs = collectWith locToUri refLocs - getFileEdit = flip $ getSrcEdit state . renameRefs newName + filesRefs = collectWith locToUri refs + getFileEdit = flip $ getSrcEdit state . replaceRefs newName fileEdits <- mapM (uncurry getFileEdit) filesRefs pure $ foldl' (<>) mempty fileEdits @@ -84,16 +95,16 @@ failWhenImportOrExport :: IdeState -> NormalizedFilePath -> HashSet Location -> - Name -> + [Name] -> ExceptT String m () -failWhenImportOrExport state nfp refLocs name = do +failWhenImportOrExport state nfp refLocs names = do pm <- handleMaybeM ("No parsed module for: " ++ show nfp) $ liftIO $ runAction "Rename.GetParsedModule" state (use GetParsedModule nfp) let hsMod = unLoc $ pm_parsed_source pm case (unLoc <$> hsmodName hsMod, hsmodExports hsMod) of - (mbModName, _) | not $ nameIsLocalOrFrom (replaceModName name mbModName) name + (mbModName, _) | not $ any (\n -> nameIsLocalOrFrom (replaceModName n mbModName) n) names -> throwE "Renaming of an imported name is unsupported" (_, Just (L _ exports)) | any ((`HS.member` refLocs) . unsafeSrcSpanToLoc . getLoc) exports -> throwE "Renaming of an exported name is unsupported" @@ -112,7 +123,7 @@ getSrcEdit :: ExceptT String m WorkspaceEdit getSrcEdit state updatePs uri = do ccs <- lift getClientCapabilities - nfp <- safeUriToNfp uri + nfp <- handleUriToNfp uri annAst <- handleMaybeM ("No parsed source for: " ++ show nfp) $ liftIO $ runAction "Rename.GetAnnotatedParsedSource" state @@ -128,13 +139,13 @@ getSrcEdit state updatePs uri = do pure $ diffText ccs (uri, src) res IncludeDeletions -- | Replace names at every given `Location` (in a given `ParsedSource`) with a given new name. -renameRefs :: +replaceRefs :: OccName -> HashSet Location -> ParsedSource -> ParsedSource #if MIN_VERSION_ghc(9,2,1) -renameRefs newName refs = everywhere $ +replaceRefs newName refs = everywhere $ -- there has to be a better way... mkT (replaceLoc @AnnListItem) `extT` -- replaceLoc @AnnList `extT` -- not needed @@ -149,14 +160,13 @@ renameRefs newName refs = everywhere $ | isRef (locA srcSpan) = L srcSpan $ replace oldRdrName replaceLoc lOldRdrName = lOldRdrName #else -renameRefs newName refs = everywhere $ mkT replaceLoc +replaceRefs newName refs = everywhere $ mkT replaceLoc where replaceLoc :: Located RdrName -> Located RdrName replaceLoc (L srcSpan oldRdrName) | isRef srcSpan = L srcSpan $ replace oldRdrName replaceLoc lOldRdrName = lOldRdrName #endif - replace :: RdrName -> RdrName replace (Qual modName _) = Qual modName newName replace _ = Unqual newName @@ -173,10 +183,10 @@ refsAtName :: IdeState -> NormalizedFilePath -> Name -> - ExceptT String m (HashSet Location) + ExceptT String m [Location] refsAtName state nfp name = do ShakeExtras{withHieDb} <- liftIO $ runAction "Rename.HieDb" state getShakeExtras - ast <- safeGetHieAst state nfp + ast <- handleGetHieAst state nfp dbRefs <- case nameModule_maybe name of Nothing -> pure [] Just mod -> liftIO $ mapMaybe rowToLoc <$> withHieDb (\hieDb -> @@ -188,32 +198,32 @@ refsAtName state nfp name = do (Just $ moduleUnit mod) [fromNormalizedFilePath nfp] ) - pure $ HS.fromList $ getNameLocs name ast ++ dbRefs + pure $ nameLocs name ast ++ dbRefs -getNameLocs :: Name -> (HieAstResult, PositionMapping) -> [Location] -getNameLocs name (HAR _ _ rm _ _, pm) = +nameLocs :: Name -> (HieAstResult, PositionMapping) -> [Location] +nameLocs name (HAR _ _ rm _ _, pm) = mapMaybe (toCurrentLocation pm . realSrcSpanToLocation . fst) (concat $ M.lookup (Right name) rm) --------------------------------------------------------------------------------------------------- -- Util -getNameAtPos :: IdeState -> NormalizedFilePath -> Position -> ExceptT String (LspT Config IO) Name -getNameAtPos state nfp pos = do - (HAR{hieAst}, pm) <- safeGetHieAst state nfp - handleMaybe ("No name at " ++ showPos pos) $ listToMaybe $ getNamesAtPoint hieAst pos pm +getNamesAtPos :: MonadIO m => IdeState -> NormalizedFilePath -> Position -> ExceptT String m [Name] +getNamesAtPos state nfp pos = do + (HAR{hieAst}, pm) <- handleGetHieAst state nfp + pure $ getNamesAtPoint hieAst pos pm -safeGetHieAst :: +handleGetHieAst :: MonadIO m => IdeState -> NormalizedFilePath -> ExceptT String m (HieAstResult, PositionMapping) -safeGetHieAst state nfp = handleMaybeM +handleGetHieAst state nfp = handleMaybeM ("No AST for file: " ++ show nfp) (liftIO $ runAction "Rename.GetHieAst" state $ useWithStale GetHieAst nfp) -safeUriToNfp :: (Monad m) => Uri -> ExceptT String m NormalizedFilePath -safeUriToNfp uri = handleMaybe +handleUriToNfp :: (Monad m) => Uri -> ExceptT String m NormalizedFilePath +handleUriToNfp uri = handleMaybe ("No filepath for uri: " ++ show uri) (toNormalizedFilePath <$> uriToFilePath uri) @@ -230,15 +240,17 @@ nfpToUri = filePathToUri . fromNormalizedFilePath showName :: Name -> String showName = occNameString . getOccName -showPos :: Position -> String -showPos Position{_line, _character} = "line: " ++ show _line ++ " - character: " ++ show _character - unsafeSrcSpanToLoc :: SrcSpan -> Location unsafeSrcSpanToLoc srcSpan = case srcSpanToLocation srcSpan of Nothing -> error "Invalid conversion from UnhelpfulSpan to Location" Just location -> location +locToFilePos :: Location -> (NormalizedFilePath, Position) +locToFilePos (Location uri (Range pos _)) = (nfp, pos) + where + Just nfp = (uriToNormalizedFilePath . toNormalizedUri) uri + replaceModName :: Name -> Maybe ModuleName -> Module replaceModName name mbModName = mkModule (moduleUnit $ nameModule name) (fromMaybe (mkModuleName "Main") mbModName) diff --git a/plugins/hls-rename-plugin/test/Main.hs b/plugins/hls-rename-plugin/test/Main.hs index 66bcea6222..21151dec1a 100644 --- a/plugins/hls-rename-plugin/test/Main.hs +++ b/plugins/hls-rename-plugin/test/Main.hs @@ -15,12 +15,19 @@ main = defaultTestRunner tests renamePlugin :: PluginDescriptor IdeState renamePlugin = Rename.descriptor "rename" +-- See https://github.com/wz1000/HieDb/issues/45 +recordConstructorIssue :: String +recordConstructorIssue = "HIE references for record fields incorrect with GHC versions >= 9" + tests :: TestTree tests = testGroup "Rename" [ goldenWithRename "Data constructor" "DataConstructor" $ \doc -> rename doc (Position 0 15) "Op" , goldenWithRename "Exported function" "ExportedFunction" $ \doc -> rename doc (Position 2 1) "quux" + , ignoreForGhcVersions [GHC90, GHC92] recordConstructorIssue $ + goldenWithRename "Field Puns" "FieldPuns" $ \doc -> + rename doc (Position 7 13) "bleh" , goldenWithRename "Function argument" "FunctionArgument" $ \doc -> rename doc (Position 3 4) "y" , goldenWithRename "Function name" "FunctionName" $ \doc -> @@ -33,6 +40,9 @@ tests = testGroup "Rename" rename doc (Position 3 8) "baz" , goldenWithRename "Import hiding" "ImportHiding" $ \doc -> rename doc (Position 0 22) "hiddenFoo" + , ignoreForGhcVersions [GHC90, GHC92] recordConstructorIssue $ + goldenWithRename "Indirect Puns" "IndirectPuns" $ \doc -> + rename doc (Position 4 23) "blah" , goldenWithRename "Let expression" "LetExpression" $ \doc -> rename doc (Position 5 11) "foobar" , goldenWithRename "Qualified as" "QualifiedAs" $ \doc -> @@ -43,7 +53,8 @@ tests = testGroup "Rename" rename doc (Position 3 12) "baz" , goldenWithRename "Realigns do block indentation" "RealignDo" $ \doc -> rename doc (Position 0 2) "fooBarQuux" - , goldenWithRename "Record field" "RecordField" $ \doc -> + , ignoreForGhcVersions [GHC90, GHC92] recordConstructorIssue $ + goldenWithRename "Record field" "RecordField" $ \doc -> rename doc (Position 6 9) "number" , goldenWithRename "Shadowed name" "ShadowedName" $ \doc -> rename doc (Position 1 1) "baz" diff --git a/plugins/hls-rename-plugin/test/testdata/FieldPuns.expected.hs b/plugins/hls-rename-plugin/test/testdata/FieldPuns.expected.hs new file mode 100644 index 0000000000..f6618927b0 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/FieldPuns.expected.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE NamedFieldPuns #-} + +module FieldPun () where + +newtype Foo = Foo { bleh :: Int } + +unFoo :: Foo -> Int +unFoo Foo{bleh} = bleh diff --git a/plugins/hls-rename-plugin/test/testdata/FieldPuns.hs b/plugins/hls-rename-plugin/test/testdata/FieldPuns.hs new file mode 100644 index 0000000000..2cd53d0026 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/FieldPuns.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE NamedFieldPuns #-} + +module FieldPun () where + +newtype Foo = Foo { field :: Int } + +unFoo :: Foo -> Int +unFoo Foo{field} = field diff --git a/plugins/hls-rename-plugin/test/testdata/IndirectPuns.expected.hs b/plugins/hls-rename-plugin/test/testdata/IndirectPuns.expected.hs new file mode 100644 index 0000000000..cf181c7215 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/IndirectPuns.expected.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE NamedFieldPuns #-} + +module IndirectPuns () where + +newtype Foo = Foo { blah :: Int } + +unFoo :: Foo -> Int +unFoo Foo{blah} = blah diff --git a/plugins/hls-rename-plugin/test/testdata/IndirectPuns.hs b/plugins/hls-rename-plugin/test/testdata/IndirectPuns.hs new file mode 100644 index 0000000000..c823126a76 --- /dev/null +++ b/plugins/hls-rename-plugin/test/testdata/IndirectPuns.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE NamedFieldPuns #-} + +module IndirectPuns () where + +newtype Foo = Foo { field :: Int } + +unFoo :: Foo -> Int +unFoo Foo{field} = field diff --git a/plugins/hls-rename-plugin/test/testdata/hie.yaml b/plugins/hls-rename-plugin/test/testdata/hie.yaml index 4c184b3c33..892a7d675f 100644 --- a/plugins/hls-rename-plugin/test/testdata/hie.yaml +++ b/plugins/hls-rename-plugin/test/testdata/hie.yaml @@ -3,6 +3,7 @@ cradle: arguments: - "DataConstructor" - "ExportedFunction" + - "FieldPuns" - "Foo" - "FunctionArgument" - "FunctionName" @@ -10,6 +11,7 @@ cradle: - "HiddenFunction" - "ImportHiding" - "ImportedFunction" + - "IndirectPuns" - "LetExpression" - "QualifiedAs" - "QualifiedFunction" From 5dc134ef77f647a6dbac0b4714f3103cde83be03 Mon Sep 17 00:00:00 2001 From: Kobayashi Date: Fri, 8 Jul 2022 16:30:30 +0800 Subject: [PATCH 016/213] remove all usages of pre-commit-check in nix (#3024) --- flake.nix | 3 --- 1 file changed, 3 deletions(-) diff --git a/flake.nix b/flake.nix index c7db14aac6..e8a18c1ad5 100644 --- a/flake.nix +++ b/flake.nix @@ -374,7 +374,6 @@ devShells = simpleDevShells // nixDevShells // { default = simpleDevShells.haskell-language-server-dev; - inherit (self.checks.${system}.pre-commit-check) shellHook; }; packages = allPackages // { @@ -397,8 +396,6 @@ docs = docs; }; - checks = { pre-commit-check = pre-commit-check ghcDefault; }; - # The attributes for the default shell and package changed in recent versions of Nix, # these are here for backwards compatibility with the old versions. devShell = devShells.default; From 12095b2e530f23015d07fd1ae36c4152da68b9ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CB=8Cbod=CA=B2=C9=AA=CB=88=C9=A1r=CA=B2im?= Date: Fri, 8 Jul 2022 11:07:56 +0100 Subject: [PATCH 017/213] hls-plugin-api: add lower bounds (#3022) Before `opentelemetry-0.4` `SpanInFlight` is not exported. Before `dependent-sum-0.7` `GCompare` is not coming from `some`, but is defined elsewhere, which leads to incompatibility with `lsp`. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Michael Peyton Jones --- hls-plugin-api/hls-plugin-api.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hls-plugin-api/hls-plugin-api.cabal b/hls-plugin-api/hls-plugin-api.cabal index 301b0cd233..0d79ec3bac 100644 --- a/hls-plugin-api/hls-plugin-api.cabal +++ b/hls-plugin-api/hls-plugin-api.cabal @@ -39,7 +39,7 @@ library , containers , data-default , dependent-map - , dependent-sum + , dependent-sum >=0.7 , Diff ^>=0.4.0 , dlist , extra @@ -50,7 +50,7 @@ library , lens , lens-aeson , lsp >=1.4.0.0 && < 1.6 - , opentelemetry + , opentelemetry >=0.4 , optparse-applicative , process , regex-tdfa >=1.3.1.0 From 37e6e85f22db97c374c7d7d67ef0f6be50f1616f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CB=8Cbod=CA=B2=C9=AA=CB=88=C9=A1r=CA=B2im?= Date: Sat, 9 Jul 2022 11:12:25 +0100 Subject: [PATCH 018/213] ghcide: lower bounds (#3025) * ghcide: lower bounds Before `ghc-exactprint-1.4` some `instance Default` are missing. `UnliftIO.Directory` appears only in `unliftio-0.2.6`. `tasty-hunit < 0.10` does not re-export `HasCallStack`. * Correct bounds for ghc-exactprint --- ghcide/ghcide.cabal | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index b8b7b006da..e91c296f36 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -59,7 +59,7 @@ library filepath, fingertree, focus, - ghc-exactprint, + ghc-exactprint < 1 || >= 1.4, ghc-trace-events, Glob, haddock-library >= 1.8 && < 1.11, @@ -102,7 +102,7 @@ library vector, opentelemetry >=0.6.1, heapsize ==0.3.*, - unliftio, + unliftio >= 0.2.6, unliftio-core, ghc-boot-th, ghc-boot, @@ -432,7 +432,7 @@ test-suite ghcide-tests hls-graph, tasty, tasty-expected-failure, - tasty-hunit, + tasty-hunit >= 0.10, tasty-quickcheck, tasty-rerun, text, @@ -497,7 +497,7 @@ executable ghcide-bench safe-exceptions, hls-graph, shake, - tasty-hunit, + tasty-hunit >= 0.10, text hs-source-dirs: bench/lib bench/exe test/src ghc-options: -threaded -Wall -Wno-name-shadowing -rtsopts From 531646b047878204e96669b5f5462f409592c611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CB=8Cbod=CA=B2=C9=AA=CB=88=C9=A1r=CA=B2im?= Date: Sat, 9 Jul 2022 15:33:09 +0100 Subject: [PATCH 019/213] hls-fourmolu-plugin: add lower bound for process-extras (#3028) --- plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal b/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal index 75a8ab15ad..96eaf7dc64 100644 --- a/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal +++ b/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal @@ -36,7 +36,7 @@ library , hls-plugin-api ^>=1.4 , lens , lsp - , process-extras + , process-extras >= 0.7.1 , text default-language: Haskell2010 From 4b0b99efc1d7a2abd8139e288702372463b333ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CB=8Cbod=CA=B2=C9=AA=CB=88=C9=A1r=CA=B2im?= Date: Sat, 9 Jul 2022 17:44:08 +0100 Subject: [PATCH 020/213] haskell-language-server: add lower bound for githash (#3030) --- haskell-language-server.cabal | 2 +- stack-lts16.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 970bb6b8fb..77d935f8d0 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -72,7 +72,7 @@ library , data-default , ghc , ghcide ^>=1.7 - , githash + , githash >=0.1.6.1 , lsp , hie-bios , hiedb diff --git a/stack-lts16.yaml b/stack-lts16.yaml index 6d4189fb68..69a4f58272 100644 --- a/stack-lts16.yaml +++ b/stack-lts16.yaml @@ -90,6 +90,7 @@ extra-deps: - stm-hamt-1.2.0.6@sha256:fba86ccb4b45c5706c19b0e1315ba63dcac3b5d71de945ec001ba921fae80061,3972 - primitive-extras-0.10.1 - primitive-unlifted-0.1.3.1 + - githash-0.1.6.2 configure-options: ghcide: From b689abb69e15fd0380932e754477900ea84e49cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CB=8Cbod=CA=B2=C9=AA=CB=88=C9=A1r=CA=B2im?= Date: Sun, 10 Jul 2022 02:02:11 +0100 Subject: [PATCH 021/213] hls-eval-plugin: add lower bound for parser-combinators (#3029) --- plugins/hls-eval-plugin/hls-eval-plugin.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hls-eval-plugin/hls-eval-plugin.cabal b/plugins/hls-eval-plugin/hls-eval-plugin.cabal index 490a2486b8..676e0bf732 100644 --- a/plugins/hls-eval-plugin/hls-eval-plugin.cabal +++ b/plugins/hls-eval-plugin/hls-eval-plugin.cabal @@ -75,7 +75,7 @@ library , lsp-types , megaparsec >=9.0 , mtl - , parser-combinators + , parser-combinators >=1.2 , pretty-simple , QuickCheck , safe-exceptions From b747aa0bd2f9cec933d813eb843514f5a7a92338 Mon Sep 17 00:00:00 2001 From: Dmitry Pogodin Date: Mon, 11 Jul 2022 01:23:00 +0100 Subject: [PATCH 022/213] Fix Stack build with Nix on macOS (#3031) --- flake.nix | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index e8a18c1ad5..bcd4e28dba 100644 --- a/flake.nix +++ b/flake.nix @@ -291,8 +291,11 @@ # ormolu # stylish-haskell pre-commit - ]; - + ] ++ lib.optionals stdenv.isDarwin + (with darwin.apple_sdk.frameworks; [ + Cocoa + CoreServices + ]); shellHook = '' # @guibou: I'm not sure theses lines are needed From 445192e21daa4f4973e60a13e748ffa910819e79 Mon Sep 17 00:00:00 2001 From: Kobayashi Date: Mon, 11 Jul 2022 12:24:35 +0800 Subject: [PATCH 023/213] refactor selection range plugin (#3003) * update Gitpod config * update nix shellHook & docs * install pre-commit hook * add kokobd as code owner to .gitpod.* * add gen-hie to Gitpod * add tools for doc * remove .pre-commit-config.yaml from .gitignore * set vscode formatter to stylish-haskell in Gitpod * refactor selection range plugin * refine selection range * add CodeKind to CodeRange * rename hls-selection-range-plugin to hls-code-range-plugin * update docs about selection range * cleanup RuleTypes.hs * add the missing bang pattern * fix subRange * add some unit tests to CodeRange.Rules * add tests for removeInterleaving * add even more tests * fix extra sources Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .github/workflows/hackage.yml | 2 +- .github/workflows/test.yml | 4 +- CODEOWNERS | 2 +- cabal.project | 2 +- docs/features.md | 6 +- docs/supported-versions.md | 2 +- exe/Plugins.hs | 8 +- ghcide/src/Development/IDE/Core/RuleTypes.hs | 13 +- ghcide/src/Development/IDE/GHC/Compat.hs | 178 +++++++++------- haskell-language-server.cabal | 14 +- hls-plugin-api/src/Ide/PluginUtils.hs | 6 +- hls-plugin-api/test/Ide/PluginUtilsTest.hs | 2 + .../LICENSE | 0 .../hls-code-range-plugin.cabal} | 36 ++-- .../src/Ide/Plugin/CodeRange.hs | 136 ++++++++++++ .../Ide/Plugin/CodeRange}/ASTPreProcess.hs | 104 ++++++---- .../src/Ide/Plugin/CodeRange/Rules.hs | 195 ++++++++++++++++++ .../test/Ide/Plugin/CodeRange/RulesTest.hs | 80 +++++++ .../test/Ide/Plugin/CodeRangeTest.hs | 54 +++++ plugins/hls-code-range-plugin/test/Main.hs | 66 ++++++ .../selection-range}/Function.golden.txt | 0 .../testdata/selection-range}/Function.hs | 0 .../selection-range}/Import.golden.txt | 0 .../test/testdata/selection-range}/Import.hs | 0 .../test/testdata/selection-range}/hie.yaml | 0 .../src/Ide/Plugin/SelectionRange.hs | 145 ------------- .../hls-selection-range-plugin/test/Main.hs | 52 ----- stack-lts16.yaml | 2 +- stack-lts19.yaml | 2 +- stack.yaml | 2 +- 30 files changed, 762 insertions(+), 351 deletions(-) rename plugins/{hls-selection-range-plugin => hls-code-range-plugin}/LICENSE (100%) rename plugins/{hls-selection-range-plugin/hls-selection-range-plugin.cabal => hls-code-range-plugin/hls-code-range-plugin.cabal} (68%) create mode 100644 plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs rename plugins/{hls-selection-range-plugin/src/Ide/Plugin/SelectionRange => hls-code-range-plugin/src/Ide/Plugin/CodeRange}/ASTPreProcess.hs (61%) create mode 100644 plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs create mode 100644 plugins/hls-code-range-plugin/test/Ide/Plugin/CodeRange/RulesTest.hs create mode 100644 plugins/hls-code-range-plugin/test/Ide/Plugin/CodeRangeTest.hs create mode 100644 plugins/hls-code-range-plugin/test/Main.hs rename plugins/{hls-selection-range-plugin/test/testdata => hls-code-range-plugin/test/testdata/selection-range}/Function.golden.txt (100%) rename plugins/{hls-selection-range-plugin/test/testdata => hls-code-range-plugin/test/testdata/selection-range}/Function.hs (100%) rename plugins/{hls-selection-range-plugin/test/testdata => hls-code-range-plugin/test/testdata/selection-range}/Import.golden.txt (100%) rename plugins/{hls-selection-range-plugin/test/testdata => hls-code-range-plugin/test/testdata/selection-range}/Import.hs (100%) rename plugins/{hls-selection-range-plugin/test/testdata => hls-code-range-plugin/test/testdata/selection-range}/hie.yaml (100%) delete mode 100644 plugins/hls-selection-range-plugin/src/Ide/Plugin/SelectionRange.hs delete mode 100644 plugins/hls-selection-range-plugin/test/Main.hs diff --git a/.github/workflows/hackage.yml b/.github/workflows/hackage.yml index f90518f5d1..09b1f57de7 100644 --- a/.github/workflows/hackage.yml +++ b/.github/workflows/hackage.yml @@ -36,7 +36,7 @@ jobs: "hls-refine-imports-plugin", "hls-rename-plugin", "hls-retrie-plugin", "hls-splice-plugin", "hls-tactics-plugin", "hls-call-hierarchy-plugin", "hls-alternate-number-format-plugin", - "hls-qualify-imported-names-plugin", "hls-selection-range-plugin", + "hls-qualify-imported-names-plugin", "hls-code-range-plugin", "haskell-language-server"] ghc: [ "9.0.2" , "8.10.7" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 951f7c7a11..c57c4de3cd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -236,8 +236,8 @@ jobs: run: cabal test hls-qualify-imported-names-plugin --test-options="$TEST_OPTS" || cabal test hls-qualify-imported-names-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-qualify-imported-names-plugin --test-options="$TEST_OPTS" - if: matrix.test - name: Test hls-selection-range-plugin test suite - run: cabal test hls-selection-range-plugin --test-options="$TEST_OPTS" || cabal test hls-selection-range-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-selection-range-plugin --test-options="$TEST_OPTS" + name: Test hls-code-range-plugin test suite + run: cabal test hls-code-range-plugin --test-options="$TEST_OPTS" || cabal test hls-code-range-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-code-range-plugin --test-options="$TEST_OPTS" - if: matrix.test name: Test hls-change-type-signature test suite diff --git a/CODEOWNERS b/CODEOWNERS index b54ff268c3..70dc00b939 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -25,7 +25,7 @@ /plugins/hls-refine-imports-plugin /plugins/hls-rename-plugin @OliverMadine /plugins/hls-retrie-plugin @pepeiborra -/plugins/hls-selection-range-plugin @kokobd +/plugins/hls-code-range-plugin @kokobd /plugins/hls-splice-plugin @konn /plugins/hls-stylish-haskell-plugin @Ailrun /plugins/hls-tactics-plugin @isovector diff --git a/cabal.project b/cabal.project index 19313edee0..62dc214f28 100644 --- a/cabal.project +++ b/cabal.project @@ -26,7 +26,7 @@ packages: ./plugins/hls-call-hierarchy-plugin ./plugins/hls-alternate-number-format-plugin ./plugins/hls-qualify-imported-names-plugin - ./plugins/hls-selection-range-plugin + ./plugins/hls-code-range-plugin ./plugins/hls-change-type-signature-plugin ./plugins/hls-gadt-plugin diff --git a/docs/features.md b/docs/features.md index 0bf1d16487..58fa945299 100644 --- a/docs/features.md +++ b/docs/features.md @@ -317,13 +317,13 @@ Shows module name matching file path, and applies it with a click. ## Selection range -Provided by: `hls-selection-range-plugin` +Provided by: `hls-code-range-plugin` Provides haskell specific -[shrink/expand selection](https://code.visualstudio.com/docs/editor/codebasics#shrinkexpand-selection) +[shrink/expand selection](https://code.visualstudio.com/docs/editor/codebasics#_shrinkexpand-selection) support. -![Selection range demo](https://user-images.githubusercontent.com/16440269/150301502-4c002605-9f8d-43f5-86d3-28846942c4ff.mov) +![Selection range demo](https://user-images.githubusercontent.com/16440269/177240833-7dc8fe39-b446-477e-b5b1-7fc303608d4f.gif) ## Rename diff --git a/docs/supported-versions.md b/docs/supported-versions.md index 3948daeab1..9e010373bf 100644 --- a/docs/supported-versions.md +++ b/docs/supported-versions.md @@ -68,7 +68,7 @@ Sometimes a plugin will be supported in the pre-built binaries but not in a HLS | `hls-splice-plugin` | 9.2 | | `hls-stylish-haskell-plugin` | | | `hls-tactics-plugin` | 9.2 | -| `hls-selection-range-plugin` | | +| `hls-code-range-plugin` | | | `hls-gadt-plugin` | | ### Using deprecated GHC versions diff --git a/exe/Plugins.hs b/exe/Plugins.hs index 07c15eb7f2..6fbf6cd1dd 100644 --- a/exe/Plugins.hs +++ b/exe/Plugins.hs @@ -76,8 +76,8 @@ import qualified Ide.Plugin.Splice as Splice import qualified Ide.Plugin.AlternateNumberFormat as AlternateNumberFormat #endif -#if selectionRange -import Ide.Plugin.SelectionRange as SelectionRange +#if codeRange +import qualified Ide.Plugin.CodeRange as CodeRange #endif #if changeTypeSignature @@ -190,8 +190,8 @@ idePlugins recorder includeExamples = pluginDescToIdePlugins allPlugins #if alternateNumberFormat AlternateNumberFormat.descriptor pluginRecorder : #endif -#if selectionRange - SelectionRange.descriptor "selectionRange" : +#if codeRange + CodeRange.descriptor pluginRecorder "codeRange" : #endif #if changeTypeSignature ChangeTypeSignature.descriptor : diff --git a/ghcide/src/Development/IDE/Core/RuleTypes.hs b/ghcide/src/Development/IDE/Core/RuleTypes.hs index 41ccfc4819..2a10be92eb 100644 --- a/ghcide/src/Development/IDE/Core/RuleTypes.hs +++ b/ghcide/src/Development/IDE/Core/RuleTypes.hs @@ -35,7 +35,6 @@ import Development.IDE.Types.HscEnvEq (HscEnvEq) import Development.IDE.Types.KnownTargets import GHC.Generics (Generic) -import qualified Data.Binary as B import Data.ByteString (ByteString) import Data.Text (Text) import Development.IDE.Import.FindImports (ArtifactsLocation) @@ -173,17 +172,17 @@ tmrModSummary :: TcModuleResult -> ModSummary tmrModSummary = pm_mod_summary . tmrParsed data HiFileResult = HiFileResult - { hirModSummary :: !ModSummary + { hirModSummary :: !ModSummary -- Bang patterns here are important to stop the result retaining -- a reference to a typechecked module - , hirModIface :: !ModIface - , hirModDetails :: ModDetails + , hirModIface :: !ModIface + , hirModDetails :: ModDetails -- ^ Populated lazily - , hirIfaceFp :: !ByteString + , hirIfaceFp :: !ByteString -- ^ Fingerprint for the ModIface , hirRuntimeModules :: !(ModuleEnv ByteString) -- ^ same as tmrRuntimeModules - , hirCoreFp :: !(Maybe (CoreFile, ByteString)) + , hirCoreFp :: !(Maybe (CoreFile, ByteString)) -- ^ If we wrote a core file for this module, then its contents (lazily deserialised) -- along with its hash } @@ -445,7 +444,7 @@ newtype GhcSessionDeps = GhcSessionDeps_ instance Show GhcSessionDeps where show (GhcSessionDeps_ False) = "GhcSessionDeps" - show (GhcSessionDeps_ True) = "GhcSessionDepsFull" + show (GhcSessionDeps_ True) = "GhcSessionDepsFull" pattern GhcSessionDeps :: GhcSessionDeps pattern GhcSessionDeps = GhcSessionDeps_ False diff --git a/ghcide/src/Development/IDE/GHC/Compat.hs b/ghcide/src/Development/IDE/GHC/Compat.hs index 1c2876f736..8bb3c5fd1c 100644 --- a/ghcide/src/Development/IDE/GHC/Compat.hs +++ b/ghcide/src/Development/IDE/GHC/Compat.hs @@ -32,10 +32,14 @@ module Development.IDE.GHC.Compat( myCoreToStgExpr, #endif + FastStringCompat, nodeInfo', getNodeIds, - nodeInfoFromSource, + sourceNodeInfo, + generatedNodeInfo, + simpleNodeInfoCompat, isAnnotationInNodeInfo, + nodeAnnotations, mkAstNode, combineRealSrcSpans, @@ -94,7 +98,6 @@ module Development.IDE.GHC.Compat( module UniqSet, module UniqDFM, getDependentMods, - diffBinds, flattenBinds, mkRnEnv2, emptyInScopeSet, @@ -113,6 +116,7 @@ module Development.IDE.GHC.Compat( #endif ) where +import Data.Bifunctor import Development.IDE.GHC.Compat.Core import Development.IDE.GHC.Compat.Env import Development.IDE.GHC.Compat.ExactPrint @@ -125,58 +129,74 @@ import Development.IDE.GHC.Compat.Units import Development.IDE.GHC.Compat.Util import GHC hiding (HasSrcSpan, ModLocation, - RealSrcSpan, getLoc, - lookupName, exprType) + RealSrcSpan, exprType, + getLoc, lookupName) + +import Data.Coerce (coerce) +import Data.String (IsString (fromString)) + + #if MIN_VERSION_ghc(9,0,0) -import GHC.Driver.Hooks (hscCompileCoreExprHook) -import GHC.Core (CoreExpr, CoreProgram, Unfolding(..), noUnfolding, flattenBinds) -import qualified GHC.Core.Opt.Pipeline as GHC -import GHC.Core.Tidy (tidyExpr) -import GHC.Types.Var.Env (emptyTidyEnv, mkRnEnv2, emptyInScopeSet) -import qualified GHC.CoreToStg.Prep as GHC -import GHC.CoreToStg.Prep (corePrepPgm) -import GHC.Core.Lint (lintInteractiveExpr) +import GHC.Core.Lint (lintInteractiveExpr) +import qualified GHC.Core.Opt.Pipeline as GHC +import GHC.Core.Tidy (tidyExpr) +import GHC.CoreToStg.Prep (corePrepPgm) +import qualified GHC.CoreToStg.Prep as GHC +import GHC.Driver.Hooks (hscCompileCoreExprHook) #if MIN_VERSION_ghc(9,2,0) -import GHC.Unit.Home.ModInfo (lookupHpt, HomePackageTable) -import GHC.Runtime.Context (icInteractiveModule) -import GHC.Unit.Module.Deps (Dependencies(dep_mods)) -import GHC.Linker.Types (isObjectLinkable) -import GHC.Linker.Loader (loadExpr) +import GHC.Linker.Loader (loadExpr) +import GHC.Linker.Types (isObjectLinkable) +import GHC.Runtime.Context (icInteractiveModule) +import GHC.Unit.Home.ModInfo (HomePackageTable, + lookupHpt) +import GHC.Unit.Module.Deps (Dependencies (dep_mods)) #else -import GHC.CoreToByteCode (coreExprToBCOs) -import GHC.Driver.Types (Dependencies(dep_mods), icInteractiveModule, lookupHpt, HomePackageTable) -import GHC.Runtime.Linker (linkExpr) -#endif -import GHC.ByteCode.Asm (bcoFreeNames) -import GHC.Types.Annotations (Annotation(..), AnnTarget(ModuleTarget), extendAnnEnvList) -import GHC.Types.Unique.DSet as UniqDSet -import GHC.Types.Unique.Set as UniqSet -import GHC.Types.Unique.DFM as UniqDFM +import GHC.CoreToByteCode (coreExprToBCOs) +import GHC.Driver.Types (Dependencies (dep_mods), + HomePackageTable, + icInteractiveModule, + lookupHpt) +import GHC.Runtime.Linker (linkExpr) +#endif +import GHC.ByteCode.Asm (bcoFreeNames) +import GHC.Types.Annotations (AnnTarget (ModuleTarget), + Annotation (..), + extendAnnEnvList) +import GHC.Types.Unique.DFM as UniqDFM +import GHC.Types.Unique.DSet as UniqDSet +import GHC.Types.Unique.Set as UniqSet #else -import Hooks (hscCompileCoreExprHook) -import CoreSyn (CoreExpr, flattenBinds, Unfolding(..), noUnfolding) -import qualified SimplCore as GHC -import CoreTidy (tidyExpr) -import VarEnv (emptyTidyEnv, mkRnEnv2, emptyInScopeSet) -import CorePrep (corePrepExpr, corePrepPgm) -import CoreLint (lintInteractiveExpr) -import ByteCodeGen (coreExprToBCOs) -import HscTypes (icInteractiveModule, HomePackageTable, lookupHpt, Dependencies(dep_mods)) -import Linker (linkExpr) -import ByteCodeAsm (bcoFreeNames) -import Annotations (Annotation(..), AnnTarget(ModuleTarget), extendAnnEnvList) -import UniqDSet -import UniqSet -import UniqDFM +import Annotations (AnnTarget (ModuleTarget), + Annotation (..), + extendAnnEnvList) +import ByteCodeAsm (bcoFreeNames) +import ByteCodeGen (coreExprToBCOs) +import CoreLint (lintInteractiveExpr) +import CorePrep (corePrepExpr, + corePrepPgm) +import CoreSyn (CoreExpr, + Unfolding (..), + flattenBinds, + noUnfolding) +import CoreTidy (tidyExpr) +import Hooks (hscCompileCoreExprHook) +import Linker (linkExpr) +import qualified SimplCore as GHC +import UniqDFM +import UniqDSet +import UniqSet +import VarEnv (emptyInScopeSet, + emptyTidyEnv, mkRnEnv2) #endif #if MIN_VERSION_ghc(9,0,0) +import GHC.Core import GHC.Data.StringBuffer import GHC.Driver.Session hiding (ExposePackage) import qualified GHC.Types.SrcLoc as SrcLoc +import GHC.Types.Var.Env import GHC.Utils.Error #if MIN_VERSION_ghc(9,2,0) -import Data.Bifunctor import GHC.Driver.Env as Env import GHC.Unit.Module.ModIface import GHC.Unit.Module.ModSummary @@ -209,41 +229,32 @@ import System.IO import Compat.HieAst (enrichHie) import Compat.HieBin -import Compat.HieTypes +import Compat.HieTypes hiding (nodeAnnotations) +import qualified Compat.HieTypes as GHC (nodeAnnotations) import Compat.HieUtils import qualified Data.ByteString as BS import Data.IORef import Data.List (foldl') import qualified Data.Map as Map -import qualified Data.Set as Set - -#if MIN_VERSION_ghc(9,0,0) import qualified Data.Set as S -#endif #if !MIN_VERSION_ghc(8,10,0) import Bag (unitBag) #endif #if MIN_VERSION_ghc(9,2,0) -import GHC.Types.CostCentre -import GHC.Stg.Syntax -import GHC.Types.IPE -import GHC.Stg.Syntax -import GHC.Types.IPE -import GHC.Types.CostCentre -import GHC.Core -import GHC.Builtin.Uniques -import GHC.Runtime.Interpreter -import GHC.StgToByteCode -import GHC.Stg.Pipeline -import GHC.ByteCode.Types -import GHC.Linker.Loader (loadDecls) -import GHC.Data.Maybe -import GHC.CoreToStg -import GHC.Core.Utils -import GHC.Types.Var.Env +import GHC.Builtin.Uniques +import GHC.ByteCode.Types +import GHC.CoreToStg +import GHC.Data.Maybe +import GHC.Linker.Loader (loadDecls) +import GHC.Runtime.Interpreter +import GHC.Stg.Pipeline +import GHC.Stg.Syntax +import GHC.StgToByteCode +import GHC.Types.CostCentre +import GHC.Types.IPE #endif type ModIfaceAnnotation = Annotation @@ -506,11 +517,18 @@ nodeInfo' = nodeInfo -- unhelpfulSpanFS = id #endif -nodeInfoFromSource :: HieAST a -> Maybe (NodeInfo a) +sourceNodeInfo :: HieAST a -> Maybe (NodeInfo a) +#if MIN_VERSION_ghc(9,0,0) +sourceNodeInfo = Map.lookup SourceInfo . getSourcedNodeInfo . sourcedNodeInfo +#else +sourceNodeInfo = Just . nodeInfo +#endif + +generatedNodeInfo :: HieAST a -> Maybe (NodeInfo a) #if MIN_VERSION_ghc(9,0,0) -nodeInfoFromSource = Map.lookup SourceInfo . getSourcedNodeInfo . sourcedNodeInfo +generatedNodeInfo = Map.lookup GeneratedInfo . getSourcedNodeInfo . sourcedNodeInfo #else -nodeInfoFromSource = Just . nodeInfo +generatedNodeInfo = sourceNodeInfo -- before ghc 9.0, we don't distinguish the source #endif data GhcVersion @@ -553,11 +571,31 @@ runPp = const SysTools.runPp #endif -isAnnotationInNodeInfo :: (FastString, FastString) -> NodeInfo a -> Bool +simpleNodeInfoCompat :: FastStringCompat -> FastStringCompat -> NodeInfo a +simpleNodeInfoCompat ctor typ = simpleNodeInfo (coerce ctor) (coerce typ) + +isAnnotationInNodeInfo :: (FastStringCompat, FastStringCompat) -> NodeInfo a -> Bool +isAnnotationInNodeInfo p = S.member p . nodeAnnotations + +nodeAnnotations :: NodeInfo a -> S.Set (FastStringCompat, FastStringCompat) +#if MIN_VERSION_ghc(9,2,0) +nodeAnnotations = S.map (\(NodeAnnotation ctor typ) -> (coerce ctor, coerce typ)) . GHC.nodeAnnotations +#else +nodeAnnotations = S.map (bimap coerce coerce) . GHC.nodeAnnotations +#endif + +#if MIN_VERSION_ghc(9,2,0) +newtype FastStringCompat = FastStringCompat LexicalFastString +#else +newtype FastStringCompat = FastStringCompat FastString +#endif + deriving (Show, Eq, Ord) + +instance IsString FastStringCompat where #if MIN_VERSION_ghc(9,2,0) -isAnnotationInNodeInfo (ctor, typ) = Set.member (NodeAnnotation ctor typ) . nodeAnnotations + fromString = FastStringCompat . LexicalFastString . fromString #else -isAnnotationInNodeInfo p = Set.member p . nodeAnnotations + fromString = FastStringCompat . fromString #endif mkAstNode :: NodeInfo a -> Span -> [HieAST a] -> HieAST a diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 77d935f8d0..d63b82495c 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -176,8 +176,8 @@ flag qualifyImportedNames default: True manual: True -flag selectionRange - description: Enable selectionRange plugin +flag codeRange + description: Enable Code Range plugin default: True manual: True @@ -304,10 +304,10 @@ common qualifyImportedNames build-depends: hls-qualify-imported-names-plugin ^>=1.0 cpp-options: -DqualifyImportedNames -common selectionRange - if flag(selectionRange) - build-depends: hls-selection-range-plugin ^>= 1.0 - cpp-options: -DselectionRange +common codeRange + if flag(codeRange) + build-depends: hls-code-range-plugin ^>= 1.0 + cpp-options: -DcodeRange common changeTypeSignature if flag(changeTypeSignature) @@ -369,7 +369,7 @@ executable haskell-language-server , splice , alternateNumberFormat , qualifyImportedNames - , selectionRange + , codeRange , gadt , floskell , fourmolu diff --git a/hls-plugin-api/src/Ide/PluginUtils.hs b/hls-plugin-api/src/Ide/PluginUtils.hs index c5bb881b58..1f10b1cc70 100644 --- a/hls-plugin-api/src/Ide/PluginUtils.hs +++ b/hls-plugin-api/src/Ide/PluginUtils.hs @@ -218,12 +218,10 @@ fullRange s = Range startPos endPos lastLine = fromIntegral $ length $ T.lines s subRange :: Range -> Range -> Bool -subRange smallRange range = - positionInRange (_start smallRange) range - && positionInRange (_end smallRange) range +subRange smallRange range = _start smallRange >= _start range && _end smallRange <= _end range positionInRange :: Position -> Range -> Bool -positionInRange p (Range sp ep) = sp <= p && p <= ep +positionInRange p (Range sp ep) = sp <= p && p < ep -- Range's end position is exclusive, see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#range -- --------------------------------------------------------------------- diff --git a/hls-plugin-api/test/Ide/PluginUtilsTest.hs b/hls-plugin-api/test/Ide/PluginUtilsTest.hs index 19a832f165..c6bedfdf28 100644 --- a/hls-plugin-api/test/Ide/PluginUtilsTest.hs +++ b/hls-plugin-api/test/Ide/PluginUtilsTest.hs @@ -20,6 +20,8 @@ positionInRangeTest = testGroup "positionInRange" positionInRange (Position 1 0) (Range (Position 1 1) (Position 1 6)) @?= False , testCase "single line, in range" $ positionInRange (Position 1 5) (Range (Position 1 1) (Position 1 6)) @?= True + , testCase "single line, at the end" $ + positionInRange (Position 1 5) (Range (Position 1 1) (Position 1 5)) @?= False , testCase "multiline, in range" $ positionInRange (Position 3 5) (Range (Position 1 1) (Position 5 6)) @?= True , testCase "multiline, out of range" $ diff --git a/plugins/hls-selection-range-plugin/LICENSE b/plugins/hls-code-range-plugin/LICENSE similarity index 100% rename from plugins/hls-selection-range-plugin/LICENSE rename to plugins/hls-code-range-plugin/LICENSE diff --git a/plugins/hls-selection-range-plugin/hls-selection-range-plugin.cabal b/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal similarity index 68% rename from plugins/hls-selection-range-plugin/hls-selection-range-plugin.cabal rename to plugins/hls-code-range-plugin/hls-code-range-plugin.cabal index 1038b6760a..e51ad55268 100644 --- a/plugins/hls-selection-range-plugin/hls-selection-range-plugin.cabal +++ b/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal @@ -1,5 +1,5 @@ cabal-version: 2.4 -name: hls-selection-range-plugin +name: hls-code-range-plugin version: 1.0.0.0 synopsis: HLS Plugin to support smart selection range @@ -16,15 +16,16 @@ category: Development build-type: Simple extra-source-files: LICENSE - test/testdata/*.hs - test/testdata/*.yaml - test/testdata/*.txt + test/testdata/selection-range/*.hs + test/testdata/selection-range/*.yaml + test/testdata/selection-range/*.txt library exposed-modules: - Ide.Plugin.SelectionRange + Ide.Plugin.CodeRange + Ide.Plugin.CodeRange.Rules other-modules: - Ide.Plugin.SelectionRange.ASTPreProcess + Ide.Plugin.CodeRange.ASTPreProcess ghc-options: -Wall hs-source-dirs: src default-language: Haskell2010 @@ -32,29 +33,40 @@ library , aeson , base >=4.12 && <5 , containers + , deepseq + , extra , ghcide ^>=1.6 || ^>=1.7 + , hashable , hls-plugin-api ^>=1.3 || ^>=1.4 + , lens , lsp - , transformers , mtl - , text - , extra , semigroupoids + , text + , transformers + , vector test-suite tests type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test main-is: Main.hs + other-modules: + Ide.Plugin.CodeRangeTest + Ide.Plugin.CodeRange.RulesTest ghc-options: -threaded -rtsopts -with-rtsopts=-N build-depends: , base + , bytestring , containers , filepath - , hls-selection-range-plugin + , ghcide ^>=1.6 || ^>=1.7 + , hls-code-range-plugin , hls-test-utils ^>=1.2 || ^>=1.3 + , lens , lsp , lsp-test + , tasty-hunit , text - , bytestring - , lens + , transformers + , vector diff --git a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs new file mode 100644 index 0000000000..0a48a3467b --- /dev/null +++ b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs @@ -0,0 +1,136 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} + +module Ide.Plugin.CodeRange ( + descriptor + , Log + + -- * Internal + , findPosition + ) where + +import Control.Monad.Except (ExceptT (ExceptT), + runExceptT) +import Control.Monad.IO.Class (liftIO) +import Control.Monad.Trans.Maybe (MaybeT (MaybeT), + maybeToExceptT) +import Data.Either.Extra (maybeToEither) +import Data.Maybe (fromMaybe) +import Data.Vector (Vector) +import qualified Data.Vector as V +import Development.IDE (IdeAction, + IdeState (shakeExtras), + Range (Range), Recorder, + WithPriority, + cmapWithPrio, + runIdeAction, + toNormalizedFilePath', + uriToFilePath') +import Development.IDE.Core.Actions (useE) +import Development.IDE.Core.PositionMapping (PositionMapping, + fromCurrentPosition, + toCurrentRange) +import Development.IDE.Types.Logger (Pretty (..)) +import Ide.Plugin.CodeRange.Rules (CodeRange (..), + GetCodeRange (..), + codeRangeRule) +import qualified Ide.Plugin.CodeRange.Rules as Rules (Log) +import Ide.PluginUtils (pluginResponse, + positionInRange) +import Ide.Types (PluginDescriptor (pluginHandlers, pluginRules), + PluginId, + defaultPluginDescriptor, + mkPluginHandler) +import Language.LSP.Server (LspM) +import Language.LSP.Types (List (List), + NormalizedFilePath, + Position (..), + Range (_start), + ResponseError, + SMethod (STextDocumentSelectionRange), + SelectionRange (..), + SelectionRangeParams (..), + TextDocumentIdentifier (TextDocumentIdentifier), + Uri) +import Prelude hiding (log, span) + +descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState +descriptor recorder plId = (defaultPluginDescriptor plId) + { pluginHandlers = mkPluginHandler STextDocumentSelectionRange selectionRangeHandler + -- TODO @sloorush add folding range + -- <> mkPluginHandler STextDocumentFoldingRange foldingRangeHandler + , pluginRules = codeRangeRule (cmapWithPrio LogRules recorder) + } + +data Log = LogRules Rules.Log + +instance Pretty Log where + pretty log = case log of + LogRules codeRangeLog -> pretty codeRangeLog + +selectionRangeHandler :: IdeState -> PluginId -> SelectionRangeParams -> LspM c (Either ResponseError (List SelectionRange)) +selectionRangeHandler ide _ SelectionRangeParams{..} = do + pluginResponse $ do + filePath <- ExceptT . pure . maybeToEither "fail to convert uri to file path" $ + toNormalizedFilePath' <$> uriToFilePath' uri + selectionRanges <- ExceptT . liftIO . runIdeAction "SelectionRange" (shakeExtras ide) . runExceptT $ + getSelectionRanges filePath positions + pure . List $ selectionRanges + where + uri :: Uri + TextDocumentIdentifier uri = _textDocument + + positions :: [Position] + List positions = _positions + +getSelectionRanges :: NormalizedFilePath -> [Position] -> ExceptT String IdeAction [SelectionRange] +getSelectionRanges file positions = do + (codeRange, positionMapping) <- maybeToExceptT "fail to get code range" $ useE GetCodeRange file + -- 'positionMapping' should be appied to the input before using them + positions' <- maybeToExceptT "fail to apply position mapping to input positions" . MaybeT . pure $ + traverse (fromCurrentPosition positionMapping) positions + + let selectionRanges = flip fmap positions' $ \pos -> + -- We need a default selection range if the lookup fails, so that other positions can still have valid results. + let defaultSelectionRange = SelectionRange (Range pos pos) Nothing + in fromMaybe defaultSelectionRange . findPosition pos $ codeRange + + -- 'positionMapping' should be applied to the output ranges before returning them + maybeToExceptT "fail to apply position mapping to output positions" . MaybeT . pure $ + traverse (toCurrentSelectionRange positionMapping) selectionRanges + +-- | Find 'Position' in 'CodeRange'. This can fail, if the given position is not covered by the 'CodeRange'. +findPosition :: Position -> CodeRange -> Maybe SelectionRange +findPosition pos root = go Nothing root + where + -- Helper function for recursion. The range list is built top-down + go :: Maybe SelectionRange -> CodeRange -> Maybe SelectionRange + go acc node = + if positionInRange pos range + then maybe acc' (go acc') (binarySearchPos children) + -- If all children doesn't contain pos, acc' will be returned. + -- acc' will be Nothing only if we are in the root level. + else Nothing + where + range = _codeRange_range node + children = _codeRange_children node + acc' = Just $ maybe (SelectionRange range Nothing) (SelectionRange range . Just) acc + + binarySearchPos :: Vector CodeRange -> Maybe CodeRange + binarySearchPos v + | V.null v = Nothing + | V.length v == 1, + Just r <- V.headM v = if positionInRange pos (_codeRange_range r) then Just r else Nothing + | otherwise = do + let (left, right) = V.splitAt (V.length v `div` 2) v + startOfRight <- _start . _codeRange_range <$> V.headM right + if pos < startOfRight then binarySearchPos left else binarySearchPos right + +-- | Likes 'toCurrentPosition', but works on 'SelectionRange' +toCurrentSelectionRange :: PositionMapping -> SelectionRange -> Maybe SelectionRange +toCurrentSelectionRange positionMapping SelectionRange{..} = do + newRange <- toCurrentRange positionMapping _range + pure $ SelectionRange { + _range = newRange, + _parent = _parent >>= toCurrentSelectionRange positionMapping + } diff --git a/plugins/hls-selection-range-plugin/src/Ide/Plugin/SelectionRange/ASTPreProcess.hs b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/ASTPreProcess.hs similarity index 61% rename from plugins/hls-selection-range-plugin/src/Ide/Plugin/SelectionRange/ASTPreProcess.hs rename to plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/ASTPreProcess.hs index 9fd6ab24c2..d44ed3debd 100644 --- a/plugins/hls-selection-range-plugin/src/Ide/Plugin/SelectionRange/ASTPreProcess.hs +++ b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/ASTPreProcess.hs @@ -1,37 +1,30 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} -module Ide.Plugin.SelectionRange.ASTPreProcess +module Ide.Plugin.CodeRange.ASTPreProcess ( preProcessAST , PreProcessEnv(..) + , isCustomNode + , CustomNodeType(..) ) where -import Control.Monad.Reader (Reader, asks) -import Data.Foldable (find, foldl') -import Data.Functor.Identity (Identity (Identity, runIdentity)) -import Data.List (groupBy) -import Data.List.NonEmpty (NonEmpty) -import qualified Data.List.NonEmpty as NonEmpty -import qualified Data.Map.Strict as Map -import Data.Maybe (mapMaybe) -import Data.Semigroup.Foldable (foldlM1) -import qualified Data.Set as Set -import Development.IDE.GHC.Compat (ContextInfo (MatchBind, TyDecl, ValBind), - HieAST (..), Identifier, - IdentifierDetails (identInfo), - NodeInfo (NodeInfo, nodeIdentifiers), - RealSrcSpan, RefMap, Span, - combineRealSrcSpans, - flattenAst, - isAnnotationInNodeInfo, - mkAstNode, nodeInfoFromSource, - realSrcSpanEnd, - realSrcSpanStart) -import Development.IDE.GHC.Compat.Util (FastString) -import Prelude hiding (span) +import Control.Monad.Reader (Reader, asks) +import Data.Foldable +import Data.Functor.Identity (Identity (Identity, runIdentity)) +import Data.List (groupBy) +import Data.List.NonEmpty (NonEmpty) +import qualified Data.List.NonEmpty as NonEmpty +import Data.Map.Strict (Map) +import qualified Data.Map.Strict as Map +import Data.Maybe (fromMaybe, mapMaybe) +import Data.Semigroup (First (First, getFirst)) +import Data.Semigroup.Foldable (foldlM1) +import qualified Data.Set as Set +import Development.IDE.GHC.Compat hiding (nodeInfo) +import Prelude hiding (span) {-| -Extra arguments for 'preaProcessAST', meant to be used in a 'Reader' context. We use 'Reader' to combine +Extra arguments for 'preProcessAST'. It's expected to be used in a 'Reader' context -} newtype PreProcessEnv a = PreProcessEnv { preProcessEnvRefMap :: RefMap a @@ -52,6 +45,47 @@ If it goes more complex, it may be more appropriate to split different manipulat preProcessAST :: HieAST a -> Reader (PreProcessEnv a) (HieAST a) preProcessAST node = mergeImports node >>= mergeSignatureWithDefinition +{-| +Create a custom node in 'HieAST'. By "custom", we mean this node doesn't actually exist in the original 'HieAST' +provided by GHC, but created to suite the needs of hls-code-range-plugin. +-} +createCustomNode :: CustomNodeType -> NonEmpty (HieAST a) -> HieAST a +createCustomNode customNodeType children = mkAstNode customNodeInfo span' (NonEmpty.toList children) + where + span' :: RealSrcSpan + span' = runIdentity . foldlM1 (\x y -> Identity (combineRealSrcSpans x y)) . fmap nodeSpan $ children + + customNodeInfo = simpleNodeInfoCompat "HlsCustom" (customNodeTypeToFastString customNodeType) + +isCustomNode :: HieAST a -> Maybe CustomNodeType +isCustomNode node = do + nodeInfo <- generatedNodeInfo node + getFirst <$> foldMap go (nodeAnnotations nodeInfo) + where + go :: (FastStringCompat, FastStringCompat) -> Maybe (First CustomNodeType) + go (k, v) + | k == "HlsCustom", Just v' <- revCustomNodeTypeMapping Map.!? v = Just (First v') + | otherwise = Nothing + +data CustomNodeType = + -- | a group of imports + CustomNodeImportsGroup + -- | adjacent type signature and value definition are paired under a custom parent node + | CustomNodeAdjacentSignatureDefinition + deriving (Show, Eq, Ord) + +customNodeTypeMapping :: Map CustomNodeType FastStringCompat +customNodeTypeMapping = Map.fromList + [ (CustomNodeImportsGroup, "Imports") + , (CustomNodeAdjacentSignatureDefinition, "AdjacentSignatureDefinition") + ] + +revCustomNodeTypeMapping :: Map FastStringCompat CustomNodeType +revCustomNodeTypeMapping = Map.fromList . fmap (\(k, v) -> (v, k)) . Map.toList $ customNodeTypeMapping + +customNodeTypeToFastString :: CustomNodeType -> FastStringCompat +customNodeTypeToFastString k = fromMaybe "" (customNodeTypeMapping Map.!? k) + {-| Combines adjacent import declarations under a new parent node, so that the user will have an extra step selecting the whole import area while expanding/shrinking the selection range. @@ -67,17 +101,11 @@ mergeImports node = pure $ node { nodeChildren = children } merge :: [HieAST a] -> Maybe (HieAST a) merge [] = Nothing merge [x] = Just x - merge (x:xs) = Just $ createVirtualNode (x NonEmpty.:| xs) + merge (x:xs) = Just $ createCustomNode CustomNodeImportsGroup (x NonEmpty.:| xs) nodeIsImport :: HieAST a -> Bool nodeIsImport = isAnnotationInAstNode ("ImportDecl", "ImportDecl") -createVirtualNode :: NonEmpty (HieAST a) -> HieAST a -createVirtualNode children = mkAstNode (NodeInfo mempty mempty mempty) span' (NonEmpty.toList children) - where - span' :: RealSrcSpan - span' = runIdentity . foldlM1 (\x y -> Identity (combineRealSrcSpans x y)) . fmap nodeSpan $ children - {-| Combine type signature with variable definition under a new parent node, if the signature is placed right before the definition. This allows the user to have a step selecting both type signature and its accompanying definition. @@ -110,7 +138,7 @@ mergeAdjacentSigDef refMap (n1, n2) = do -- Does that identifier appear in the second AST node as a definition? If so, we combines the two nodes. refs <- Map.lookup typeSigId refMap if any (isIdentADef (nodeSpan n2)) refs - then pure . createVirtualNode $ n1 NonEmpty.:| [n2] + then pure . createCustomNode CustomNodeAdjacentSignatureDefinition $ n1 NonEmpty.:| [n2] else Nothing where checkAnnotation :: Maybe () @@ -136,7 +164,7 @@ identifierForTypeSig node = nodes = flattenAst node extractIdentifier :: HieAST a -> Maybe Identifier - extractIdentifier node' = nodeInfoFromSource node' >>= + extractIdentifier node' = sourceNodeInfo node' >>= (fmap fst . find (\(_, detail) -> TyDecl `Set.member` identInfo detail) . Map.toList . nodeIdentifiers) @@ -147,13 +175,13 @@ isIdentADef outerSpan (span, detail) = && isDef where isDef :: Bool - isDef = any isContextInfoDef . Set.toList . identInfo $ detail + isDef = any isContextInfoDef . toList . identInfo $ detail - -- Does the 'ContextInfo' represents a variable/function definition? + -- Determines if the 'ContextInfo' represents a variable/function definition isContextInfoDef :: ContextInfo -> Bool isContextInfoDef ValBind{} = True isContextInfoDef MatchBind = True isContextInfoDef _ = False -isAnnotationInAstNode :: (FastString, FastString) -> HieAST a -> Bool -isAnnotationInAstNode p = maybe False (isAnnotationInNodeInfo p) . nodeInfoFromSource +isAnnotationInAstNode :: (FastStringCompat, FastStringCompat) -> HieAST a -> Bool +isAnnotationInAstNode p = maybe False (isAnnotationInNodeInfo p) . sourceNodeInfo diff --git a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs new file mode 100644 index 0000000000..8a573d9ebb --- /dev/null +++ b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs @@ -0,0 +1,195 @@ +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE InstanceSigs #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeFamilies #-} + +module Ide.Plugin.CodeRange.Rules + ( CodeRange (..) + , codeRange_range + , codeRange_children + , codeRange_kind + , CodeRangeKind(..) + , GetCodeRange(..) + , codeRangeRule + , Log(..) + + -- * Internal + , removeInterleaving + , simplify + ) where + +import Control.DeepSeq (NFData) +import qualified Control.Lens as Lens +import Control.Monad (foldM) +import Control.Monad.Except (ExceptT (..), runExceptT) +import Control.Monad.Reader (runReader) +import Control.Monad.Trans.Class (lift) +import Control.Monad.Trans.Maybe (MaybeT (MaybeT), + maybeToExceptT) +import Control.Monad.Trans.Writer.CPS +import Data.Coerce (coerce) +import Data.Data (Typeable) +import Data.Foldable (traverse_) +import Data.Function (on, (&)) +import Data.Hashable +import Data.List (sort) +import qualified Data.Map.Strict as Map +import Data.Vector (Vector) +import qualified Data.Vector as V +import Development.IDE +import Development.IDE.Core.Rules (toIdeResult) +import qualified Development.IDE.Core.Shake as Shake +import Development.IDE.GHC.Compat (Annotated, HieAST (..), + HieASTs (getAsts), + ParsedSource, RefMap) +import Development.IDE.GHC.Compat.Util +import Development.IDE.GHC.ExactPrint (GetAnnotatedParsedSource (GetAnnotatedParsedSource)) +import GHC.Generics (Generic) +import Ide.Plugin.CodeRange.ASTPreProcess (CustomNodeType (..), + PreProcessEnv (..), + isCustomNode, + preProcessAST) +import Language.LSP.Types.Lens (HasEnd (end), + HasStart (start)) +import Prelude hiding (log) + +data Log = LogShake Shake.Log + | LogNoAST + | LogFoundInterleaving CodeRange CodeRange + deriving Show + +instance Pretty Log where + pretty log = case log of + LogShake shakeLog -> pretty shakeLog + LogNoAST -> "no HieAst exist for file" + LogFoundInterleaving r1 r2 -> + let prettyRange = pretty . show . _codeRange_range + in "CodeRange interleave: " <> prettyRange r1 <> " & " <> prettyRange r2 + +-- | A tree representing code ranges in a file. This can be useful for features like selection range and folding range +data CodeRange = CodeRange { + -- | Range for current level + _codeRange_range :: !Range, + -- | A vector of children, sorted by their ranges in ascending order. + -- Children are guaranteed not to interleave, but some gaps may exist among them. + _codeRange_children :: !(Vector CodeRange), + -- The kind of current code range + _codeRange_kind :: !CodeRangeKind + } + deriving (Show, Generic, NFData) + +-- | 'CodeKind' represents the kind of a code range +data CodeRangeKind = + -- | ordinary code + CodeKindRegion + -- | the group of imports + | CodeKindImports + -- | a comment + | CodeKindComment + deriving (Show, Generic, NFData) + +Lens.makeLenses ''CodeRange + +instance Eq CodeRange where + (==) = (==) `on` _codeRange_range + +instance Ord CodeRange where + compare :: CodeRange -> CodeRange -> Ordering + compare = compare `on` _codeRange_range + +-- | Construct a 'CodeRange'. A valid CodeRange will be returned in any case. If anything go wrong, +-- a list of warnings will be returned as 'Log' +buildCodeRange :: HieAST a -> RefMap a -> Annotated ParsedSource -> Writer [Log] CodeRange +buildCodeRange ast refMap _ = do + -- We work on 'HieAST', then convert it to 'CodeRange', so that applications such as selection range and folding + -- range don't need to care about 'HieAST' + -- TODO @sloorush actually use 'Annotated ParsedSource' to handle structures not in 'HieAST' properly (for example comments) + let ast' = runReader (preProcessAST ast) (PreProcessEnv refMap) + codeRange <- astToCodeRange ast' + pure $ simplify codeRange + +astToCodeRange :: HieAST a -> Writer [Log] CodeRange +astToCodeRange (Node _ sp []) = pure $ CodeRange (realSrcSpanToRange sp) mempty CodeKindRegion +astToCodeRange node@(Node _ sp children) = do + children' <- removeInterleaving . sort =<< traverse astToCodeRange children + let codeKind = if Just CustomNodeImportsGroup == isCustomNode node then CodeKindImports else CodeKindRegion + pure $ CodeRange (realSrcSpanToRange sp) (V.fromList children') codeKind + +-- | Remove interleaving of the list of 'CodeRange's. +removeInterleaving :: [CodeRange] -> Writer [Log] [CodeRange] +removeInterleaving = fmap reverse . foldM go [] + where + -- we want to traverse from left to right (to make the logs easier to read) + go :: [CodeRange] -> CodeRange -> Writer [Log] [CodeRange] + go [] x = pure [x] + go (x1:acc) x2 = do + -- Given that the CodeRange is already sorted on it's Range, and the Ord instance of Range + -- compares it's start position first, the start position must be already in an ascending order. + -- Then, if the end position of a node is larger than it's next neighbour's start position, an interleaving + -- must exist. + -- (Note: LSP Range's end position is exclusive) + x1' <- if x1 Lens.^. codeRange_range . end > x2 Lens.^. codeRange_range . start + then do + -- set x1.end to x2.start + let x1' :: CodeRange = x1 & codeRange_range . end Lens..~ (x2 Lens.^. codeRange_range . start) + tell [LogFoundInterleaving x1 x2] + pure x1' + else pure x1 + pure $ x2:x1':acc + +-- | Remove redundant nodes in 'CodeRange' tree +simplify :: CodeRange -> CodeRange +simplify r = + case onlyChild of + -- If a node has the exact same range as it's parent, and it has no sibling, then it can be removed. + Just onlyChild' -> + if _codeRange_range onlyChild' == curRange + then simplify (r { _codeRange_children = _codeRange_children onlyChild' }) + else withChildrenSimplified + Nothing -> withChildrenSimplified + where + curRange = _codeRange_range r + + onlyChild :: Maybe CodeRange = + let children = _codeRange_children r + in if V.length children == 1 then V.headM children else Nothing + + withChildrenSimplified = r { _codeRange_children = simplify <$> _codeRange_children r } + +data GetCodeRange = GetCodeRange + deriving (Eq, Show, Typeable, Generic) + +instance Hashable GetCodeRange +instance NFData GetCodeRange + +type instance RuleResult GetCodeRange = CodeRange + +codeRangeRule :: Recorder (WithPriority Log) -> Rules () +codeRangeRule recorder = + define (cmapWithPrio LogShake recorder) $ \GetCodeRange file -> handleError recorder $ do + -- We need both 'HieAST' (for basic AST) and api annotations (for comments and some keywords). + -- See https://gitlab.haskell.org/ghc/ghc/-/wikis/api-annotations + HAR{hieAst, refMap} <- lift $ use_ GetHieAst file + ast <- maybeToExceptT LogNoAST . MaybeT . pure $ + getAsts hieAst Map.!? (coerce . mkFastString . fromNormalizedFilePath) file + annPS <- lift $ use_ GetAnnotatedParsedSource file + + let (codeRange, warnings) = runWriter (buildCodeRange ast refMap annPS) + traverse_ (logWith recorder Warning) warnings + + pure codeRange + +-- | Handle error in 'Action'. Returns an 'IdeResult' with no value and no diagnostics on error. (but writes log) +handleError :: Recorder (WithPriority msg) -> ExceptT msg Action a -> Action (IdeResult a) +handleError recorder action' = do + valueEither <- runExceptT action' + case valueEither of + Left msg -> do + logWith recorder Error msg + pure $ toIdeResult (Left []) + Right value -> pure $ toIdeResult (Right value) diff --git a/plugins/hls-code-range-plugin/test/Ide/Plugin/CodeRange/RulesTest.hs b/plugins/hls-code-range-plugin/test/Ide/Plugin/CodeRange/RulesTest.hs new file mode 100644 index 0000000000..473d5b7f77 --- /dev/null +++ b/plugins/hls-code-range-plugin/test/Ide/Plugin/CodeRange/RulesTest.hs @@ -0,0 +1,80 @@ +{-# LANGUAGE OverloadedLists #-} + +module Ide.Plugin.CodeRange.RulesTest (testTree) where + +import Control.Monad.Trans.Writer.CPS +import Data.Bifunctor (Bifunctor (first, second)) +import qualified Data.Vector as V +import Ide.Plugin.CodeRange.Rules +import Test.Hls +import Test.Tasty.HUnit + +testTree :: TestTree +testTree = + testGroup "CodeRange.Rules" [ + testGroup "removeInterleaving" $ + let check :: [CodeRange] -> ([CodeRange], [Log]) -> Assertion + check input want = + second (fmap LogEq) (runWriter (removeInterleaving input)) @?= second (fmap LogEq) want + mkNode :: UInt -> UInt -> CodeRange + mkNode startCol endCol = + CodeRange (Range (Position 1 startCol) (Position 1 endCol)) [] CodeKindRegion + in [ + testCase "empty list" $ check [] ([], []), + testCase "one" $ check [mkNode 1 5] ([mkNode 1 5], []), + testCase "two, without intersection" $ check [mkNode 1 5, mkNode 5 6] ([mkNode 1 5, mkNode 5 6], []), + testCase "two, with intersection" $ let (x, y) = (mkNode 1 5, mkNode 2 4) + in check [x, y] ([mkNode 1 2, mkNode 2 4], [LogFoundInterleaving x y]), + testCase "three, with intersection" $ let (x, y, z) = (mkNode 1 10, mkNode 2 6, mkNode 4 12) + in check [x, y, z] ([mkNode 1 2, mkNode 2 4, mkNode 4 12], + [LogFoundInterleaving x y, LogFoundInterleaving y z]) + ], + testGroup "simplify" $ + let mkNode :: UInt -> UInt -> V.Vector CodeRange -> CodeRange + mkNode startCol endCol children = + CodeRange (Range (Position 1 startCol) (Position 1 endCol)) children CodeKindRegion + in [ + testCase "one level should not change" $ + let codeRange = mkNode 1 5 [] + in codeRange @=? simplify codeRange, + testCase "dedup 3 nested layers" $ + let input = + mkNode 1 10 [ + mkNode 1 5 [], + mkNode 5 10 [ + mkNode 5 10 [ + mkNode 5 10 [ + mkNode 6 10 [] + ] + ] + ] + ] + want = + mkNode 1 10 [ + mkNode 1 5 [], + mkNode 5 10 [ + mkNode 6 10 [] + ] + ] + in want @=? simplify input, + testCase "should not dedup node that has multiple children" $ + let input = + mkNode 1 10 [ + mkNode 1 10 [], + mkNode 2 10 [] + ] + in simplify input @?= input, + testCase "dedup simple two layers" $ + let input = mkNode 1 10 [ mkNode 1 10 []] + in simplify input @?= mkNode 1 10 [] + ] + ] + +newtype LogEq = LogEq Log + deriving Show + +instance Eq LogEq where + LogEq (LogShake _) == LogEq (LogShake _) = True + LogEq LogNoAST == LogEq LogNoAST = True + LogEq (LogFoundInterleaving left right) == LogEq (LogFoundInterleaving left' right') = + left == left' && right == right' diff --git a/plugins/hls-code-range-plugin/test/Ide/Plugin/CodeRangeTest.hs b/plugins/hls-code-range-plugin/test/Ide/Plugin/CodeRangeTest.hs new file mode 100644 index 0000000000..73bebf3a2a --- /dev/null +++ b/plugins/hls-code-range-plugin/test/Ide/Plugin/CodeRangeTest.hs @@ -0,0 +1,54 @@ +{-# LANGUAGE OverloadedLists #-} + +module Ide.Plugin.CodeRangeTest (testTree) where + +import qualified Data.Vector as V +import Ide.Plugin.CodeRange +import Ide.Plugin.CodeRange.Rules +import Test.Hls +import Test.Tasty.HUnit + +testTree :: TestTree +testTree = + testGroup "CodeRange" [ + testGroup "findPosition" $ + let check :: Position -> CodeRange -> Maybe SelectionRange -> Assertion + check position codeRange = (findPosition position codeRange @?=) + + mkCodeRange :: Position -> Position -> V.Vector CodeRange -> CodeRange + mkCodeRange start end children = CodeRange (Range start end) children CodeKindRegion + in [ + testCase "not in range" $ check + (Position 10 1) + (mkCodeRange (Position 1 1) (Position 5 10) []) + Nothing, + testCase "in top level range" $ check + (Position 3 8) + (mkCodeRange (Position 1 1) (Position 5 10) []) + (Just $ SelectionRange (Range (Position 1 1) (Position 5 10)) Nothing), + testCase "in the gap between children, in parent" $ check + (Position 3 6) + (mkCodeRange (Position 1 1) (Position 5 10) [ + mkCodeRange (Position 1 1) (Position 3 6) [], + mkCodeRange (Position 3 7) (Position 5 10) [] + ]) + (Just $ SelectionRange (Range (Position 1 1) (Position 5 10)) Nothing), + testCase "before all children, in parent" $ check + (Position 1 1) + (mkCodeRange (Position 1 1) (Position 5 10) [ + mkCodeRange (Position 1 2) (Position 3 6) [], + mkCodeRange (Position 3 7) (Position 5 10) [] + ]) + (Just $ SelectionRange (Range (Position 1 1) (Position 5 10)) Nothing), + testCase "in children, in parent" $ check + (Position 2 1) + (mkCodeRange (Position 1 1) (Position 5 10) [ + mkCodeRange (Position 1 2) (Position 3 6) [], + mkCodeRange (Position 3 7) (Position 5 10) [] + ]) + (Just $ SelectionRange (Range (Position 1 2) (Position 3 6)) $ Just + ( SelectionRange (Range (Position 1 1) (Position 5 10)) Nothing + ) + ) + ] + ] diff --git a/plugins/hls-code-range-plugin/test/Main.hs b/plugins/hls-code-range-plugin/test/Main.hs new file mode 100644 index 0000000000..bffc3f716e --- /dev/null +++ b/plugins/hls-code-range-plugin/test/Main.hs @@ -0,0 +1,66 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Main (main) where + +import Control.Lens hiding (List, (<.>)) +import Data.ByteString.Lazy (ByteString) +import qualified Data.ByteString.Lazy.Char8 as LBSChar8 +import Data.String (fromString) +import Development.IDE.Types.Logger (Priority (Debug), + Recorder (Recorder), + WithPriority (WithPriority), + makeDefaultStderrRecorder, + pretty) +import Ide.Plugin.CodeRange (Log, descriptor) +import qualified Ide.Plugin.CodeRange.RulesTest +import qualified Ide.Plugin.CodeRangeTest +import Language.LSP.Types.Lens +import System.FilePath ((<.>), ()) +import Test.Hls + +plugin :: Recorder (WithPriority Log) -> PluginDescriptor IdeState +plugin recorder = descriptor recorder "codeRange" + +main :: IO () +main = do + recorder <- contramap (fmap pretty) <$> makeDefaultStderrRecorder Nothing Debug + defaultTestRunner $ + testGroup "Code Range" [ + testGroup "Integration Tests" [ + makeSelectionRangeGoldenTest recorder "Import" [(4, 36), (1, 8)], + makeSelectionRangeGoldenTest recorder "Function" [(5, 19), (5, 12), (4, 4), (3, 5)] + ], + testGroup "Unit Tests" [ + Ide.Plugin.CodeRangeTest.testTree, + Ide.Plugin.CodeRange.RulesTest.testTree + ] + ] + +makeSelectionRangeGoldenTest :: Recorder (WithPriority Log) -> TestName -> [(UInt, UInt)] -> TestTree +makeSelectionRangeGoldenTest recorder testName positions = goldenGitDiff testName (testDataDir testName <.> "golden" <.> "txt") $ do + res <- runSessionWithServer (plugin recorder) testDataDir $ do + doc <- openDoc (testName <.> "hs") "haskell" + resp <- request STextDocumentSelectionRange $ SelectionRangeParams Nothing Nothing doc + (List $ fmap (uncurry Position . (\(x, y) -> (x-1, y-1))) positions) + let res = resp ^. result + pure $ fmap showSelectionRangesForTest res + case res of + Left err -> assertFailure (show err) + Right golden -> pure golden + where + testDataDir :: FilePath + testDataDir = "test" "testdata" "selection-range" + + showSelectionRangesForTest :: List SelectionRange -> ByteString + showSelectionRangesForTest (List selectionRanges) = LBSChar8.intercalate "\n" $ fmap showSelectionRangeForTest selectionRanges + + showSelectionRangeForTest :: SelectionRange -> ByteString + showSelectionRangeForTest selectionRange = go True (Just selectionRange) + where + go :: Bool -> Maybe SelectionRange -> ByteString + go _ Nothing = "" + go isFirst (Just (SelectionRange (Range sp ep) parent)) = + (if isFirst then "" else " => ") <> showPosition sp <> " " <> showPosition ep <> go False parent + showPosition :: Position -> ByteString + showPosition (Position line col) = "(" <> showLBS (line + 1) <> "," <> showLBS (col + 1) <> ")" + showLBS = fromString . show diff --git a/plugins/hls-selection-range-plugin/test/testdata/Function.golden.txt b/plugins/hls-code-range-plugin/test/testdata/selection-range/Function.golden.txt similarity index 100% rename from plugins/hls-selection-range-plugin/test/testdata/Function.golden.txt rename to plugins/hls-code-range-plugin/test/testdata/selection-range/Function.golden.txt diff --git a/plugins/hls-selection-range-plugin/test/testdata/Function.hs b/plugins/hls-code-range-plugin/test/testdata/selection-range/Function.hs similarity index 100% rename from plugins/hls-selection-range-plugin/test/testdata/Function.hs rename to plugins/hls-code-range-plugin/test/testdata/selection-range/Function.hs diff --git a/plugins/hls-selection-range-plugin/test/testdata/Import.golden.txt b/plugins/hls-code-range-plugin/test/testdata/selection-range/Import.golden.txt similarity index 100% rename from plugins/hls-selection-range-plugin/test/testdata/Import.golden.txt rename to plugins/hls-code-range-plugin/test/testdata/selection-range/Import.golden.txt diff --git a/plugins/hls-selection-range-plugin/test/testdata/Import.hs b/plugins/hls-code-range-plugin/test/testdata/selection-range/Import.hs similarity index 100% rename from plugins/hls-selection-range-plugin/test/testdata/Import.hs rename to plugins/hls-code-range-plugin/test/testdata/selection-range/Import.hs diff --git a/plugins/hls-selection-range-plugin/test/testdata/hie.yaml b/plugins/hls-code-range-plugin/test/testdata/selection-range/hie.yaml similarity index 100% rename from plugins/hls-selection-range-plugin/test/testdata/hie.yaml rename to plugins/hls-code-range-plugin/test/testdata/selection-range/hie.yaml diff --git a/plugins/hls-selection-range-plugin/src/Ide/Plugin/SelectionRange.hs b/plugins/hls-selection-range-plugin/src/Ide/Plugin/SelectionRange.hs deleted file mode 100644 index 35e6009be7..0000000000 --- a/plugins/hls-selection-range-plugin/src/Ide/Plugin/SelectionRange.hs +++ /dev/null @@ -1,145 +0,0 @@ -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} - -module Ide.Plugin.SelectionRange (descriptor) where - -import Control.Monad.Except (ExceptT (ExceptT), - runExceptT) -import Control.Monad.IO.Class (liftIO) -import Control.Monad.Reader (runReader) -import Control.Monad.Trans.Maybe (MaybeT (MaybeT), - maybeToExceptT) -import Data.Coerce (coerce) -import Data.Containers.ListUtils (nubOrd) -import Data.Either.Extra (maybeToEither) -import Data.Foldable (find) -import qualified Data.Map.Strict as Map -import Data.Maybe (fromMaybe, mapMaybe) -import qualified Data.Text as T -import Development.IDE (GetHieAst (GetHieAst), - HieAstResult (HAR, hieAst, refMap), - IdeAction, - IdeState (shakeExtras), - Range (Range), - fromNormalizedFilePath, - ideLogger, logDebug, - realSrcSpanToRange, - runIdeAction, - toNormalizedFilePath', - uriToFilePath') -import Development.IDE.Core.Actions (useE) -import Development.IDE.Core.PositionMapping (PositionMapping, - fromCurrentPosition, - toCurrentRange) -import Development.IDE.GHC.Compat (HieAST (Node), Span, - getAsts) -import Development.IDE.GHC.Compat.Util -import Ide.Plugin.SelectionRange.ASTPreProcess (PreProcessEnv (PreProcessEnv), - preProcessAST) -import Ide.PluginUtils (pluginResponse) -import Ide.Types (PluginDescriptor (pluginHandlers), - PluginId, - defaultPluginDescriptor, - mkPluginHandler) -import Language.LSP.Server (LspM) -import Language.LSP.Types (List (List), - NormalizedFilePath, - Position, - ResponseError, - SMethod (STextDocumentSelectionRange), - SelectionRange (..), - SelectionRangeParams (..), - TextDocumentIdentifier (TextDocumentIdentifier), - Uri) -import Prelude hiding (span) - -descriptor :: PluginId -> PluginDescriptor IdeState -descriptor plId = (defaultPluginDescriptor plId) - { pluginHandlers = mkPluginHandler STextDocumentSelectionRange selectionRangeHandler - } - -selectionRangeHandler :: IdeState -> PluginId -> SelectionRangeParams -> LspM c (Either ResponseError (List SelectionRange)) -selectionRangeHandler ide _ SelectionRangeParams{..} = do - liftIO $ logDebug logger $ "requesting selection range for file: " <> T.pack (show uri) - pluginResponse $ do - filePath <- ExceptT . pure . maybeToEither "fail to convert uri to file path" $ - toNormalizedFilePath' <$> uriToFilePath' uri - selectionRanges <- ExceptT . liftIO . runIdeAction "SelectionRange" (shakeExtras ide) . runExceptT $ - getSelectionRanges filePath positions - pure . List $ selectionRanges - where - uri :: Uri - TextDocumentIdentifier uri = _textDocument - - positions :: [Position] - List positions = _positions - - logger = ideLogger ide - -getSelectionRanges :: NormalizedFilePath -> [Position] -> ExceptT String IdeAction [SelectionRange] -getSelectionRanges file positions = do - (HAR{hieAst, refMap}, positionMapping) <- maybeToExceptT "fail to get hie ast" $ useE GetHieAst file - -- 'positionMapping' should be applied to the input positions before using them - positions' <- maybeToExceptT "fail to apply position mapping to input positions" . MaybeT . pure $ - traverse (fromCurrentPosition positionMapping) positions - - ast <- maybeToExceptT "fail to get ast for current file" . MaybeT . pure $ - -- in GHC 9, the 'FastString' in 'HieASTs' is replaced by a newtype wrapper around 'LexicalFastString' - -- so we use 'coerce' to make it work in both GHC 8 and 9 - getAsts hieAst Map.!? (coerce . mkFastString . fromNormalizedFilePath) file - - let ast' = runReader (preProcessAST ast) (PreProcessEnv refMap) - let selectionRanges = findSelectionRangesByPositions (astPathsLeafToRoot ast') positions' - - -- 'positionMapping' should be applied to the output ranges before returning them - maybeToExceptT "fail to apply position mapping to output positions" . MaybeT . pure $ - traverse (toCurrentSelectionRange positionMapping) selectionRanges - --- | Likes 'toCurrentPosition', but works on 'SelectionRange' -toCurrentSelectionRange :: PositionMapping -> SelectionRange -> Maybe SelectionRange -toCurrentSelectionRange positionMapping SelectionRange{..} = do - newRange <- toCurrentRange positionMapping _range - pure $ SelectionRange { - _range = newRange, - _parent = _parent >>= toCurrentSelectionRange positionMapping - } - --- | Build all paths from ast leaf to root -astPathsLeafToRoot :: HieAST a -> [SelectionRange] -astPathsLeafToRoot = mapMaybe (spansToSelectionRange . nubOrd) . go [[]] - where - go :: [[Span]] -> HieAST a -> [[Span]] - go acc (Node _ span []) = fmap (span:) acc - go acc (Node _ span children) = concatMap (go (fmap (span:) acc)) children - -spansToSelectionRange :: [Span] -> Maybe SelectionRange -spansToSelectionRange [] = Nothing -spansToSelectionRange (span:spans) = Just $ - SelectionRange {_range = realSrcSpanToRange span, _parent = spansToSelectionRange spans} - -{-| -For each position, find the selection range that contains it, without taking each selection range's -parent into account. These selection ranges are un-divisible, representing the leaf nodes in original AST, so they -won't overlap. --} -findSelectionRangesByPositions :: [SelectionRange] -- ^ all possible selection ranges - -> [Position] -- ^ requested positions - -> [SelectionRange] -findSelectionRangesByPositions selectionRanges = fmap findByPosition - {- - Performance Tips: - Doing a linear search from the first selection range for each position is not optimal. - If it becomes too slow for a large file and many positions, you may optimize the implementation. - Assume the number of selection range is n, then the following techniques may be applied: - 1. For each position, we may treat HieAST as a position indexed tree to search it in O(log(n)). - 2. For all positions, a searched position will narrow the search range for other positions. - -} - where - findByPosition :: Position -> SelectionRange - findByPosition p = fromMaybe SelectionRange{_range = Range p p, _parent = Nothing} $ - find (isPositionInSelectionRange p) selectionRanges - - isPositionInSelectionRange :: Position -> SelectionRange -> Bool - isPositionInSelectionRange p SelectionRange{_range} = - let Range sp ep = _range in sp <= p && p <= ep diff --git a/plugins/hls-selection-range-plugin/test/Main.hs b/plugins/hls-selection-range-plugin/test/Main.hs deleted file mode 100644 index ac0335a0f6..0000000000 --- a/plugins/hls-selection-range-plugin/test/Main.hs +++ /dev/null @@ -1,52 +0,0 @@ -{-# LANGUAGE OverloadedStrings #-} - -module Main (main) where - -import Control.Lens hiding (List, (<.>)) -import Data.ByteString.Lazy (ByteString) -import qualified Data.ByteString.Lazy.Char8 as LBSChar8 -import Data.String (fromString) -import Ide.Plugin.SelectionRange (descriptor) -import Language.LSP.Types.Lens -import System.FilePath ((<.>), ()) -import Test.Hls - -plugin :: PluginDescriptor IdeState -plugin = descriptor "selectionRange" - -main :: IO () -main = defaultTestRunner $ - testGroup "Selection Range" - [ goldenTest "Import" [(4, 36), (1, 8)] - , goldenTest "Function" [(5, 19), (5, 12), (4, 4), (3, 5)] - ] - --- | build a golden test for -goldenTest :: TestName -> [(UInt, UInt)] -> TestTree -goldenTest testName positions = goldenGitDiff testName (testDataDir testName <.> "golden" <.> "txt") $ do - res <- runSessionWithServer plugin testDataDir $ do - doc <- openDoc (testName <.> "hs") "haskell" - resp <- request STextDocumentSelectionRange $ SelectionRangeParams Nothing Nothing doc - (List $ fmap (uncurry Position . (\(x, y) -> (x-1, y-1))) positions) - let res = resp ^. result - pure $ fmap showSelectionRangesForTest res - case res of - Left err -> assertFailure (show err) - Right golden -> pure golden - -testDataDir :: FilePath -testDataDir = "test" "testdata" - -showSelectionRangesForTest :: List SelectionRange -> ByteString -showSelectionRangesForTest (List selectionRanges) = LBSChar8.intercalate "\n" $ fmap showSelectionRangeForTest selectionRanges - -showSelectionRangeForTest :: SelectionRange -> ByteString -showSelectionRangeForTest selectionRange = go True (Just selectionRange) - where - go :: Bool -> Maybe SelectionRange -> ByteString - go _ Nothing = "" - go isFirst (Just (SelectionRange (Range sp ep) parent)) = - (if isFirst then "" else " => ") <> showPosition sp <> " " <> showPosition ep <> go False parent - showPosition :: Position -> ByteString - showPosition (Position line col) = "(" <> showLBS (line + 1) <> "," <> showLBS (col + 1) <> ")" - showLBS = fromString . show diff --git a/stack-lts16.yaml b/stack-lts16.yaml index 69a4f58272..6a057acf0d 100644 --- a/stack-lts16.yaml +++ b/stack-lts16.yaml @@ -28,7 +28,7 @@ packages: - ./plugins/hls-module-name-plugin - ./plugins/hls-ormolu-plugin - ./plugins/hls-alternate-number-format-plugin - - ./plugins/hls-selection-range-plugin + - ./plugins/hls-code-range-plugin - ./plugins/hls-change-type-signature-plugin - ./plugins/hls-gadt-plugin diff --git a/stack-lts19.yaml b/stack-lts19.yaml index 91a56f4e9d..48f834ddcb 100644 --- a/stack-lts19.yaml +++ b/stack-lts19.yaml @@ -28,7 +28,7 @@ packages: - ./plugins/hls-module-name-plugin - ./plugins/hls-ormolu-plugin - ./plugins/hls-alternate-number-format-plugin - - ./plugins/hls-selection-range-plugin + - ./plugins/hls-code-range-plugin - ./plugins/hls-change-type-signature-plugin - ./plugins/hls-gadt-plugin diff --git a/stack.yaml b/stack.yaml index 78398e6882..438328b03a 100644 --- a/stack.yaml +++ b/stack.yaml @@ -28,7 +28,7 @@ packages: - ./plugins/hls-module-name-plugin - ./plugins/hls-ormolu-plugin - ./plugins/hls-alternate-number-format-plugin -- ./plugins/hls-selection-range-plugin +- ./plugins/hls-code-range-plugin - ./plugins/hls-change-type-signature-plugin - ./plugins/hls-gadt-plugin From fa868b5f34240894893af6f6cda41e3172a78980 Mon Sep 17 00:00:00 2001 From: Michael Peyton Jones Date: Wed, 13 Jul 2022 00:08:43 +0100 Subject: [PATCH 024/213] Simplify hlint config (#3038) Apparently I was just doing it wrong and naming multiple functions this way does work properly! --- .hlint.yaml | 57 +++++++++-------------------------------------------- 1 file changed, 9 insertions(+), 48 deletions(-) diff --git a/.hlint.yaml b/.hlint.yaml index f8068e8495..bb41a16e64 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -85,17 +85,19 @@ # Partial functions - # For some reason we need to check fucntions which + # We need to check fucntions which # are typically exported multiple ways under both names, # see https://github.com/ndmitchell/hlint/issues/1389 - - name: Prelude.head + - name: [Prelude.head, Data.List.head] within: - Main + - Experiments - Development.Benchmark.Rules - Development.IDE.Plugin.CodeAction - Development.IDE.Plugin.Completions - Development.IDE.Plugin.CodeAction.ExactPrint - Development.IDE.Spans.Documentation + - Development.IDE.Session - Ide.Plugin.CallHierarchy.Internal - Ide.Plugin.Eval.Code - Ide.Plugin.Eval.Util @@ -109,42 +111,18 @@ - Typeclass - Wingman.Judgements - Wingman.Machinery - - - name: Data.List.head - within: - - Main - - Experiments - - Development.IDE.Plugin.CodeAction - - Development.IDE.Plugin.Completions - - Development.IDE.Plugin.CodeAction.ExactPrint - - Development.IDE.Session - - Development.IDE.Spans.Documentation - - Ide.Plugin.CallHierarchy.Internal - - Ide.Plugin.Class - Wingman.Tactics - - TExpectedActual - - TRigidType - - name: Prelude.tail + - name: [Prelude.tail, Data.List.tail] within: - Main - Development.Benchmark.Rules - Development.IDE.Plugin.CodeAction - Development.IDE.Plugin.CodeAction.ExactPrint - - UnificationSpec - - - name: Data.List.tail - within: - - Main - - Development.Benchmark.Rules - - Development.IDE.Plugin.CodeAction - - Development.IDE.Plugin.CodeAction.ExactPrint - Development.IDE.Session - - IDE.Plugin.Eval.Code - - IDE.Plugin.Eval.Util - UnificationSpec - - name: Prelude.last + - name: [Prelude.last, Data.List.last] within: - Main - Development.IDE.Plugin.CodeAction @@ -155,12 +133,7 @@ - Ide.Plugin.Eval.Parse.Comments - Ide.Plugin.Eval.CodeLens - - name: Data.List.last - within: - - GenChangelogs - - Main - - - name: Prelude.init + - name: [Prelude.init, Data.List.init] within: - Main - Development.IDE.Spans.Common @@ -169,33 +142,21 @@ - Development.Benchmark.Rules - ErrorGivenPartialSignature - - name: Data.List.init - within: [] - - name: Data.List.foldl1' within: [] - name: Data.List.foldr1' within: [] - - name: "Prelude.!!" + - name: ["Prelude.!!", "Data.List.!!"] within: - Main - - Development.IDE.Plugin.CodeAction - - Development.IDE.Plugin.Completions.Logic - - Development.IDE.Spans.Documentation - - TErrorGivenPartialSignature - - Wingman.CaseSplit - - Wingman.Simplify - - - name: "Data.List.!!" - within: - - Main - Experiments - FunctionalCodeAction - Development.IDE.Plugin.CodeAction - Development.IDE.Plugin.Completions.Logic - Development.IDE.Spans.Documentation + - TErrorGivenPartialSignature - Wingman.CaseSplit - Wingman.Simplify From 2f886bfdca36b957aa0b99f3cd7d5dd1b1493b1d Mon Sep 17 00:00:00 2001 From: Kobayashi Date: Sat, 16 Jul 2022 18:16:55 +0800 Subject: [PATCH 025/213] handle trailing comma in import list properly (#3035) * handle trailing comma in import list properly * no longer backup .ghcup in gitpod * fix for ghc < 9 * fix it without using CPP * explain gitpod change * read trailing comma before adding one * refine imports * refine gitpod * gitpod store ghcide and hie-bios cache These cache directories are small, but not preserving them requires HLS to compile all modules in local project on workspace restarts. * fix code styling --- .gitpod.Dockerfile | 16 ++- .gitpod.yml | 7 +- .../IDE/Plugin/CodeAction/ExactPrint.hs | 116 +++++++++++------- ghcide/test/exe/Main.hs | 22 ++++ 4 files changed, 110 insertions(+), 51 deletions(-) diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index 5a244cf56f..5631a302f1 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -2,17 +2,23 @@ FROM gitpod/workspace-full RUN sudo install-packages build-essential curl libffi-dev libffi7 libgmp-dev libgmp10 \ libncurses-dev libncurses5 libtinfo5 && \ - BOOTSTRAP_HASKELL_NONINTERACTIVE=1 \ - BOOTSTRAP_HASKELL_MINIMAL=1 \ - curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh && \ + curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | BOOTSTRAP_HASKELL_NONINTERACTIVE=1 BOOTSTRAP_HASKELL_MINIMAL=1 sh && \ echo 'source $HOME/.ghcup/env' >> $HOME/.bashrc && \ echo 'export PATH=$HOME/.cabal/bin:$HOME/.local/bin:$PATH' >> $HOME/.bashrc && \ . /home/gitpod/.ghcup/env && \ - ghcup install ghc --set && \ + # Install all verions of GHC that HLS supports. Putting GHC into Docker image makes workspace start much faster. + ghcup install ghc 8.6.5 && \ + ghcup install ghc 8.8.4 && \ + ghcup install ghc 8.10.7 && \ + ghcup install ghc 9.0.2 && \ + ghcup install ghc 9.2.2 && \ + ghcup install ghc 9.2.3 --set && \ ghcup install hls --set && \ ghcup install cabal --set && \ ghcup install stack --set && \ cabal update && \ - cabal install stylish-haskell hoogle implicit-hie && \ + cabal install --disable-executable-dynamic --install-method copy --constraint "stylish-haskell +ghc-lib" \ + stylish-haskell implicit-hie hoogle && \ + rm -rf $HOME/.cabal/store && \ pip install pre-commit && \ npm install -g http-server diff --git a/.gitpod.yml b/.gitpod.yml index e492004825..ae2cf47a3c 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -10,7 +10,8 @@ tasks: $HOME/.local $HOME/.cabal $HOME/.stack - $HOME/.ghcup + $HOME/.cache/ghcide + $HOME/.cache/hie-bios /nix ) for DIR in "${CACHE_DIRS[@]}"; do @@ -41,9 +42,7 @@ tasks: echo '}' >> .vscode/settings.json fi - pushd docs - pip install -r requirements.txt - popd + pip install -r docs/requirements.txt init: | cabal update cabal configure --enable-executable-dynamic diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index ef2e13704b..4b516a16ab 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -4,6 +4,7 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE ScopedTypeVariables #-} module Development.IDE.Plugin.CodeAction.ExactPrint ( Rewrite (..), @@ -23,41 +24,47 @@ module Development.IDE.Plugin.CodeAction.ExactPrint ( wildCardSymbol ) where -import Control.Applicative import Control.Monad -import Control.Monad.Extra (whenJust) import Control.Monad.Trans -import Data.Char (isAlphaNum) -import Data.Data (Data) -import Data.Functor -import Data.Generics (listify) -import qualified Data.Map.Strict as Map -import Data.Maybe (fromJust, isNothing, - mapMaybe) -import qualified Data.Text as T -import Development.IDE.GHC.Compat hiding (Annotation) +import Data.Char (isAlphaNum) +import Data.Data (Data) +import Data.Generics (listify) +import qualified Data.Text as T +import Development.IDE.GHC.Compat hiding (Annotation) import Development.IDE.GHC.Error import Development.IDE.GHC.ExactPrint +import Development.IDE.GHC.Util import Development.IDE.Spans.Common -import GHC.Exts (IsList (fromList)) +import GHC.Exts (IsList (fromList)) +import GHC.Stack (HasCallStack) import Language.Haskell.GHC.ExactPrint -#if !MIN_VERSION_ghc(9,2,0) +import Language.LSP.Types + +-- GHC version specific imports. For any supported GHC version, make sure there is no warning in imports. +#if MIN_VERSION_ghc(9,2,0) +import Control.Lens (_head, _last, over) +import Data.Bifunctor (first) +import Data.Default (Default (..)) +import Data.Maybe (fromJust, fromMaybe, mapMaybe) +import GHC (AddEpAnn (..), AnnContext (..), AnnList (..), + AnnParen (..), DeltaPos (SameLine), EpAnn (..), + EpaLocation (EpaDelta), + IsUnicodeSyntax (NormalSyntax), + NameAdornment (NameParens), + TrailingAnn (AddCommaAnn), addAnns, ann, + emptyComments, reAnnL) +#else +import Control.Applicative (Alternative ((<|>))) +import Control.Monad.Extra (whenJust) +import Data.Foldable (find) +import Data.Functor (($>)) +import qualified Data.Map.Strict as Map +import Data.Maybe (fromJust, isJust, + isNothing, mapMaybe) import qualified Development.IDE.GHC.Compat.Util as Util import Language.Haskell.GHC.ExactPrint.Types (DeltaPos (DP), KeywordId (G), mkAnnKey) -#else -import Data.Default -import GHC (AddEpAnn (..), AnnContext (..), AnnParen (..), - DeltaPos (SameLine), EpAnn (..), EpaLocation (EpaDelta), - IsUnicodeSyntax (NormalSyntax), - NameAdornment (NameParens), NameAnn (..), addAnns, ann, emptyComments, - reAnnL, AnnList (..), TrailingAnn (AddCommaAnn), addTrailingAnnToA) #endif -import Language.LSP.Types -import Development.IDE.GHC.Util -import Data.Bifunctor (first) -import Control.Lens (_head, _last, over) -import GHC.Stack (HasCallStack) ------------------------------------------------------------------------------ @@ -367,17 +374,28 @@ extendImportTopLevel thing (L l it@ImportDecl{..}) then lift (Left $ thing <> " already imported") else do #if !MIN_VERSION_ghc(9,2,0) - when hasSibling $ - addTrailingCommaT (last lies) + anns <- getAnnsT + maybe (pure ()) addTrailingCommaT (lastMaybe lies) addSimpleAnnT x (DP (0, if hasSibling then 1 else 0)) [] addSimpleAnnT rdr dp00 [(G AnnVal, dp00)] + + -- When the last item already has a trailing comma, we append a trailing comma to the new item. + let isAnnComma (G AnnComma, _) = True + isAnnComma _ = False + shouldAddTrailingComma = maybe False nodeHasComma (lastMaybe lies) + && not (nodeHasComma (L l' lies)) + + nodeHasComma :: Data a => Located a -> Bool + nodeHasComma x = isJust $ Map.lookup (mkAnnKey x) anns >>= find isAnnComma . annsDP + when shouldAddTrailingComma (addTrailingCommaT x) + -- Parens are attachted to `lies`, so if `lies` was empty previously, -- we need change the ann key from `[]` to `:` to keep parens and other anns. unless hasSibling $ transferAnn (L l' lies) (L l' [x]) id return $ L l it{ideclHiding = Just (hide, L l' $ lies ++ [x])} #else - lies' <- addCommaInImportList lies x + let lies' = addCommaInImportList lies x return $ L l it{ideclHiding = Just (hide, L l' lies')} #endif extendImportTopLevel _ _ = lift $ Left "Unable to extend the import list" @@ -514,30 +532,44 @@ extendImportViaParent df parent child (L l it@ImportDecl{..}) listAnn = epAnn srcParent [AddEpAnn AnnOpenP (epl 1), AddEpAnn AnnCloseP (epl 0)] x :: LIE GhcPs = reLocA $ L l'' $ IEThingWith listAnn parentLIE NoIEWildcard [childLIE] - lies' <- addCommaInImportList (reverse pre) x + lies' = addCommaInImportList (reverse pre) x #endif return $ L l it{ideclHiding = Just (hide, L l' lies')} extendImportViaParent _ _ _ _ = lift $ Left "Unable to extend the import list via parent" #if MIN_VERSION_ghc(9,2,0) -- Add an item in an import list, taking care of adding comma if needed. -addCommaInImportList :: Monad m => +addCommaInImportList :: -- | Initial list [LocatedAn AnnListItem a] -- | Additionnal item -> LocatedAn AnnListItem a - -> m [LocatedAn AnnListItem a] -addCommaInImportList lies x = do - let hasSibling = not (null lies) - -- Add the space before the comma - x <- pure $ setEntryDP x (SameLine $ if hasSibling then 1 else 0) - - -- Add the comma (if needed) - let - fixLast = if hasSibling then first addComma else id - lies' = over _last fixLast lies ++ [x] - - pure lies' + -> [LocatedAn AnnListItem a] +addCommaInImportList lies x = + fixLast lies ++ [newItem] + where + isTrailingAnnComma :: TrailingAnn -> Bool + isTrailingAnnComma (AddCommaAnn _) = True + isTrailingAnnComma _ = False + + -- check if there is an existing trailing comma + existingTrailingComma = fromMaybe False $ do + L lastItemSrcAnn _ <- lastMaybe lies + lastItemAnn <- case ann lastItemSrcAnn of + EpAnn _ lastItemAnn _ -> pure lastItemAnn + _ -> Nothing + pure $ any isTrailingAnnComma (lann_trailing lastItemAnn) + + hasSibling = not . null $ lies + + -- Setup the new item. It should have a preceding whitespace if it has siblings, and a trailing comma if the + -- preceding item already has one. + newItem = first (if existingTrailingComma then addComma else id) $ + setEntryDP x (SameLine $ if hasSibling then 1 else 0) + + -- Add the comma (if needed) + fixLast :: [LocatedAn AnnListItem a] -> [LocatedAn AnnListItem a] + fixLast = over _last (first (if existingTrailingComma then id else addComma)) #endif unIEWrappedName :: IEWrappedName (IdP GhcPs) -> String diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 2b0dfd0ddb..b2da4beb1b 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -1882,6 +1882,28 @@ extendImportTests = testGroup "extend import actions" , " )" , "main = print (stuffA, stuffB)" ]) + , testSession "extend multi line import with trailing comma" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "stuffA :: Double" + , "stuffA = 0.00750" + , "stuffB :: Integer" + , "stuffB = 123" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA (stuffB," + , " )" + , "main = print (stuffA, stuffB)" + ]) + (Range (Position 3 17) (Position 3 18)) + ["Add stuffA to the import list of ModuleA"] + (T.unlines + [ "module ModuleB where" + , "import ModuleA (stuffB, stuffA," + , " )" + , "main = print (stuffA, stuffB)" + ]) , testSession "extend single line import with method within class" $ template [("ModuleA.hs", T.unlines [ "module ModuleA where" From 6b2239f4086d58eb3afecb2d398746d8ebee3e93 Mon Sep 17 00:00:00 2001 From: Michael Peyton Jones Date: Mon, 18 Jul 2022 12:09:40 +0100 Subject: [PATCH 026/213] Update issue templates (#3044) - Add a support issue template - Ask questions about versions of GHC and HLS - Add some heuristic details fields to help people pick an issue type --- .github/ISSUE_TEMPLATE/bug_report.md | 29 +++++++++----- ...ture_request.md => enhancement_request.md} | 12 +++--- .github/ISSUE_TEMPLATE/support.md | 39 +++++++++++++++++++ 3 files changed, 64 insertions(+), 16 deletions(-) rename .github/ISSUE_TEMPLATE/{feature_request.md => enhancement_request.md} (63%) create mode 100644 .github/ISSUE_TEMPLATE/support.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index c986cee430..7dc09e9c88 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,6 +1,6 @@ --- name: Bug report -about: Create a report to help us improve +about: I've spotted something specific thats' going wrong title: '' labels: 'status: needs triage, type: bug' assignees: '' @@ -8,31 +8,40 @@ assignees: '' --- ### Your environment -Which OS do you use: + + +Which OS do you use? -Which LSP client (editor/plugin) do you use: +Which version of GHC do you use and how did you install it? + +How is your project built (alternative: link to the project)? + +Which LSP client (editor/plugin) do you use? -Describe your project (alternative: link to the project): - +Which version of HLS do you use and how did you install it? + +Have you configured HLS in any way (especially: a `hie.yaml` file)? ### Steps to reproduce + ### Expected behaviour + ### Actual behaviour + -### Include debug information +### Debug information diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/enhancement_request.md similarity index 63% rename from .github/ISSUE_TEMPLATE/feature_request.md rename to .github/ISSUE_TEMPLATE/enhancement_request.md index 475681f707..c22b63bc52 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/enhancement_request.md @@ -1,24 +1,24 @@ --- -name: Feature request -about: Suggest an idea for this project +name: Enhancement request +about: I have an idea for how to make things better title: '' labels: 'status: needs triage, type: enhancement' assignees: '' --- -**Is your feature request related to a problem? Please describe.** +## Is your enhancement request related to a problem? Please describe. -**Describe the solution you'd like** +## Describe the solution you'd like -**Describe alternatives you've considered** +## Describe alternatives you've considered -**Additional context** +## Additional context diff --git a/.github/ISSUE_TEMPLATE/support.md b/.github/ISSUE_TEMPLATE/support.md new file mode 100644 index 0000000000..768e42ba90 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/support.md @@ -0,0 +1,39 @@ +--- +name: Support request +about: Help, something isn't working and I'm stuck! +title: '' +labels: 'status: needs triage, type: support' +assignees: '' + +--- + + + +## Your environment + + + +Which OS do you use? + +Which version of GHC do you use and how did you install it? + +How is your project built (alternative: link to the project)? + +Which LSP client (editor/plugin) do you use? + +Which version of HLS do you use and how did you install it? + +Have you configured HLS in any way (especially: a `hie.yaml` file)? + +## What's wrong? + + + +### Debug information + + From 2e2b3f125ff3654c5740b413226e37f599cb83c9 Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Mon, 18 Jul 2022 18:26:17 +0200 Subject: [PATCH 027/213] build(nix): bumped gitignore dependency (#3048) to avoid the issue specified at https://github.com/NixOS/nixpkgs/issues/179172 --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index a78b703cd4..28ca48f622 100644 --- a/flake.lock +++ b/flake.lock @@ -133,11 +133,11 @@ "gitignore": { "flake": false, "locked": { - "lastModified": 1646480205, - "narHash": "sha256-kekOlTlu45vuK2L9nq8iVN17V3sB0WWPqTTW3a2SQG0=", + "lastModified": 1657706534, + "narHash": "sha256-5jIzNHKtDu06mA325K/5CshUVb5r7sSmnRiula6Gr7o=", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "bff2832ec341cf30acb3a4d3e2e7f1f7b590116a", + "rev": "f840a659d57e53fa751a9248b17149fd0cf2a221", "type": "github" }, "original": { From b7c4274ddf1091409bc9b352399a1f602de5f6fa Mon Sep 17 00:00:00 2001 From: Colten Webb <8738145+coltenwebb@users.noreply.github.com> Date: Tue, 19 Jul 2022 11:59:42 -0400 Subject: [PATCH 028/213] Record Dot Hover Types (#3016) * patch hieast * add comments * add hlint ignore * update readme * add tests --- ghcide/test/data/hover/RecordDotSyntax.hs | 21 + ghcide/test/data/hover/hie.yaml | 2 +- ghcide/test/exe/Main.hs | 27 +- hie-compat/README.md | 4 + hie-compat/hie-compat.cabal | 4 +- .../Compat/HieAst.hs | 0 hie-compat/src-ghc92/Compat/HieAst.hs | 2142 +++++++++++++++++ 7 files changed, 2188 insertions(+), 12 deletions(-) create mode 100644 ghcide/test/data/hover/RecordDotSyntax.hs rename hie-compat/{src-reexport-ghc9 => src-ghc90}/Compat/HieAst.hs (100%) create mode 100644 hie-compat/src-ghc92/Compat/HieAst.hs diff --git a/ghcide/test/data/hover/RecordDotSyntax.hs b/ghcide/test/data/hover/RecordDotSyntax.hs new file mode 100644 index 0000000000..2f43b99977 --- /dev/null +++ b/ghcide/test/data/hover/RecordDotSyntax.hs @@ -0,0 +1,21 @@ +{-# LANGUAGE CPP #-} +#if __GLASGOW_HASKELL__ >= 902 +{-# LANGUAGE OverloadedRecordDot, DuplicateRecordFields, NoFieldSelectors #-} + +module RecordDotSyntax ( module RecordDotSyntax) where + +import qualified Data.Maybe as M + +data MyRecord = MyRecord + { a :: String + , b :: Integer + , c :: MyChild + } deriving (Eq, Show) + +newtype MyChild = MyChild + { z :: String + } deriving (Eq, Show) + +x = MyRecord { a = "Hello", b = 12, c = MyChild { z = "there" } } +y = x.a ++ show x.b ++ x.c.z +#endif diff --git a/ghcide/test/data/hover/hie.yaml b/ghcide/test/data/hover/hie.yaml index f076eb000e..e2b3e97c5d 100644 --- a/ghcide/test/data/hover/hie.yaml +++ b/ghcide/test/data/hover/hie.yaml @@ -1 +1 @@ -cradle: {direct: {arguments: ["Foo", "Bar", "GotoHover"]}} +cradle: {direct: {arguments: ["Foo", "Bar", "GotoHover", "RecordDotSyntax"]}} diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index b2da4beb1b..710fdd576e 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -4283,8 +4283,8 @@ canonicalizeLocation (Location uri range) = Location <$> canonicalizeUri uri <*> findDefinitionAndHoverTests :: TestTree findDefinitionAndHoverTests = let - tst :: (TextDocumentIdentifier -> Position -> Session a, a -> Session [Expect] -> Session ()) -> Position -> Session [Expect] -> String -> TestTree - tst (get, check) pos targetRange title = testSessionWithExtraFiles "hover" title $ \dir -> do + tst :: (TextDocumentIdentifier -> Position -> Session a, a -> Session [Expect] -> Session ()) -> Position -> String -> Session [Expect] -> String -> TestTree + tst (get, check) pos sfp targetRange title = testSessionWithExtraFiles "hover" title $ \dir -> do -- Dirty the cache to check that definitions work even in the presence of iface files liftIO $ runInDir dir $ do @@ -4294,7 +4294,7 @@ findDefinitionAndHoverTests = let _ <- getHover fooDoc $ Position 4 3 closeDoc fooDoc - doc <- openTestDataDoc (dir sourceFilePath) + doc <- openTestDataDoc (dir sfp) waitForProgressDone found <- get doc pos check found targetRange @@ -4352,16 +4352,25 @@ findDefinitionAndHoverTests = let [ ( "GotoHover.hs", [(DsError, (62, 7), "Found hole: _")]) , ( "GotoHover.hs", [(DsError, (65, 8), "Found hole: _")]) ] - , testGroup "type-definition" typeDefinitionTests ] - - typeDefinitionTests = [ tst (getTypeDefinitions, checkDefs) aaaL14 (pure tcData) "Saturated data con" - , tst (getTypeDefinitions, checkDefs) aL20 (pure [ExpectNoDefinitions]) "Polymorphic variable"] + , testGroup "type-definition" typeDefinitionTests + , testGroup "hover-record-dot-syntax" recordDotSyntaxTests ] + + typeDefinitionTests = [ tst (getTypeDefinitions, checkDefs) aaaL14 sourceFilePath (pure tcData) "Saturated data con" + , tst (getTypeDefinitions, checkDefs) aL20 sourceFilePath (pure [ExpectNoDefinitions]) "Polymorphic variable"] + + recordDotSyntaxTests + | ghcVersion == GHC92 = + [ tst (getHover, checkHover) (Position 19 24) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["x :: MyRecord"]]) "hover over parent" + , tst (getHover, checkHover) (Position 19 25) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["_ :: MyChild"]]) "hover over dot shows child" + , tst (getHover, checkHover) (Position 19 26) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["_ :: MyChild"]]) "hover over child" + ] + | otherwise = [] test runDef runHover look expect = testM runDef runHover look (return expect) testM runDef runHover look expect title = - ( runDef $ tst def look expect title - , runHover $ tst hover look expect title ) where + ( runDef $ tst def look sourceFilePath expect title + , runHover $ tst hover look sourceFilePath expect title ) where def = (getDefinitions, checkDefs) hover = (getHover , checkHover) diff --git a/hie-compat/README.md b/hie-compat/README.md index 08fddefac4..6b5e101def 100644 --- a/hie-compat/README.md +++ b/hie-compat/README.md @@ -4,6 +4,8 @@ Mainly a backport of [HIE Files](https://gitlab.haskell.org/ghc/ghc/-/wikis/hie-files) for ghc 8.6, along with a few other backports of fixes useful for `ghcide` +Also includes backport of record-dot-syntax support to 9.2.x + Fully compatible with `.hie` files natively produced by versions of GHC that support them. @@ -11,6 +13,8 @@ them. Backports included: +https://gitlab.haskell.org/ghc/ghc/-/merge_requests/8589 + https://gitlab.haskell.org/ghc/ghc/-/merge_requests/4037 https://gitlab.haskell.org/ghc/ghc/-/merge_requests/4068 diff --git a/hie-compat/hie-compat.cabal b/hie-compat/hie-compat.cabal index 0683c7a628..3313b49b62 100644 --- a/hie-compat/hie-compat.cabal +++ b/hie-compat/hie-compat.cabal @@ -47,6 +47,6 @@ library if (impl(ghc > 8.9) && impl(ghc < 8.11)) hs-source-dirs: src-ghc810 src-reexport if (impl(ghc >= 9.0) && impl(ghc < 9.1) || flag(ghc-lib)) - hs-source-dirs: src-reexport-ghc9 + hs-source-dirs: src-ghc90 src-reexport-ghc9 if (impl(ghc >= 9.2) && impl(ghc < 9.3)) - hs-source-dirs: src-reexport-ghc9 + hs-source-dirs: src-ghc92 src-reexport-ghc9 diff --git a/hie-compat/src-reexport-ghc9/Compat/HieAst.hs b/hie-compat/src-ghc90/Compat/HieAst.hs similarity index 100% rename from hie-compat/src-reexport-ghc9/Compat/HieAst.hs rename to hie-compat/src-ghc90/Compat/HieAst.hs diff --git a/hie-compat/src-ghc92/Compat/HieAst.hs b/hie-compat/src-ghc92/Compat/HieAst.hs new file mode 100644 index 0000000000..6d887c46a0 --- /dev/null +++ b/hie-compat/src-ghc92/Compat/HieAst.hs @@ -0,0 +1,2142 @@ +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE UndecidableSuperClasses #-} +{- HLINT ignore -} +{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} + +{- +Forked from GHC v9.2.3 to include record-dot-syntax type information in .hie files. + +Changes are marked with "CHANGED:" + +Main functions for .hie file generation +-} + +-- CHANGED: removed this include and updated the module declaration +-- #include "HsVersions.h" +-- +-- module GHC.Iface.Ext.Ast ( mkHieFile, mkHieFileWithSource, getCompressedAsts, enrichHie) where + +module Compat.HieAst ( enrichHie ) where + +import GHC.Utils.Outputable(ppr) + +import GHC.Prelude + +import GHC.Types.Avail ( Avails ) +import GHC.Data.Bag ( Bag, bagToList ) +import GHC.Types.Basic +import GHC.Data.BooleanFormula +import GHC.Core.Class ( className, classSCSelIds ) +import GHC.Core.Utils ( exprType ) +import GHC.Core.ConLike ( conLikeName, ConLike(RealDataCon) ) +import GHC.Core.TyCon ( TyCon, tyConClass_maybe ) +import GHC.Core.FVs +import GHC.Core.DataCon ( dataConNonlinearType ) +import GHC.Types.FieldLabel +import GHC.Hs +import GHC.Driver.Env +import GHC.Utils.Monad ( concatMapM, liftIO ) +import GHC.Types.Id ( isDataConId_maybe ) +import GHC.Types.Name ( Name, nameSrcSpan, nameUnique ) +import GHC.Types.Name.Env ( NameEnv, emptyNameEnv, extendNameEnv, lookupNameEnv ) +import GHC.Types.SrcLoc +import GHC.Tc.Utils.Zonk ( hsLitType, hsPatType ) +import GHC.Core.Type ( mkVisFunTys, Type ) +import GHC.Core.Predicate +import GHC.Core.InstEnv +import GHC.Builtin.Types ( mkListTy, mkSumTy ) +import GHC.Tc.Types +import GHC.Tc.Types.Evidence +import GHC.Types.Var ( Id, Var, EvId, varName, varType, varUnique ) +import GHC.Types.Var.Env +import GHC.Builtin.Uniques +import GHC.Iface.Make ( mkIfaceExports ) +import GHC.Utils.Panic +import GHC.Utils.Misc +import GHC.Data.Maybe +import GHC.Data.FastString + +import GHC.Iface.Ext.Types +import GHC.Iface.Ext.Utils + +import GHC.Unit.Module ( ModuleName, ml_hs_file ) +import GHC.Unit.Module.ModSummary + +import qualified Data.Array as A +import qualified Data.ByteString as BS +import qualified Data.Map as M +import qualified Data.Set as S +import Data.Data ( Data, Typeable ) +import Data.Void ( Void, absurd ) +import Control.Monad ( forM_ ) +import Control.Monad.Trans.State.Strict +import Control.Monad.Trans.Reader +import Control.Monad.Trans.Class ( lift ) +import GHC.HsToCore.Types +import GHC.HsToCore.Expr +import GHC.HsToCore.Monad + +{- Note [Updating HieAst for changes in the GHC AST] + +When updating the code in this file for changes in the GHC AST, you +need to pay attention to the following things: + +1) Symbols (Names/Vars/Modules) in the following categories: + + a) Symbols that appear in the source file that directly correspond to + something the user typed + b) Symbols that don't appear in the source, but should be in some sense + "visible" to a user, particularly via IDE tooling or the like. This + includes things like the names introduced by RecordWildcards (We record + all the names introduced by a (..) in HIE files), and will include implicit + parameters and evidence variables after one of my pending MRs lands. + +2) Subtrees that may contain such symbols, or correspond to a SrcSpan in + the file. This includes all `Located` things + +For 1), you need to call `toHie` for one of the following instances + +instance ToHie (Context (Located Name)) where ... +instance ToHie (Context (Located Var)) where ... +instance ToHie (IEContext (Located ModuleName)) where ... + +`Context` is a data type that looks like: + +data Context a = C ContextInfo a -- Used for names and bindings + +`ContextInfo` is defined in `GHC.Iface.Ext.Types`, and looks like + +data ContextInfo + = Use -- ^ regular variable + | MatchBind + | IEThing IEType -- ^ import/export + | TyDecl + -- | Value binding + | ValBind + BindType -- ^ whether or not the binding is in an instance + Scope -- ^ scope over which the value is bound + (Maybe Span) -- ^ span of entire binding + ... + +It is used to annotate symbols in the .hie files with some extra information on +the context in which they occur and should be fairly self explanatory. You need +to select one that looks appropriate for the symbol usage. In very rare cases, +you might need to extend this sum type if none of the cases seem appropriate. + +So, given a `Located Name` that is just being "used", and not defined at a +particular location, you would do the following: + + toHie $ C Use located_name + +If you select one that corresponds to a binding site, you will need to +provide a `Scope` and a `Span` for your binding. Both of these are basically +`SrcSpans`. + +The `SrcSpan` in the `Scope` is supposed to span over the part of the source +where the symbol can be legally allowed to occur. For more details on how to +calculate this, see Note [Capturing Scopes and other non local information] +in GHC.Iface.Ext.Ast. + +The binding `Span` is supposed to be the span of the entire binding for +the name. + +For a function definition `foo`: + +foo x = x + y + where y = x^2 + +The binding `Span` is the span of the entire function definition from `foo x` +to `x^2`. For a class definition, this is the span of the entire class, and +so on. If this isn't well defined for your bit of syntax (like a variable +bound by a lambda), then you can just supply a `Nothing` + +There is a test that checks that all symbols in the resulting HIE file +occur inside their stated `Scope`. This can be turned on by passing the +-fvalidate-ide-info flag to ghc along with -fwrite-ide-info to generate the +.hie file. + +You may also want to provide a test in testsuite/test/hiefile that includes +a file containing your new construction, and tests that the calculated scope +is valid (by using -fvalidate-ide-info) + +For subtrees in the AST that may contain symbols, the procedure is fairly +straightforward. If you are extending the GHC AST, you will need to provide a +`ToHie` instance for any new types you may have introduced in the AST. + +Here is an extract from the `ToHie` instance for (LHsExpr (GhcPass p)): + + toHie e@(L mspan oexpr) = concatM $ getTypeNode e : case oexpr of + HsVar _ (L _ var) -> + [ toHie $ C Use (L mspan var) + -- Patch up var location since typechecker removes it + ] + HsConLikeOut _ con -> + [ toHie $ C Use $ L mspan $ conLikeName con + ] + ... + HsApp _ a b -> + [ toHie a + , toHie b + ] + +If your subtree is `Located` or has a `SrcSpan` available, the output list +should contain a HieAst `Node` corresponding to the subtree. You can use +either `makeNode` or `getTypeNode` for this purpose, depending on whether it +makes sense to assign a `Type` to the subtree. After this, you just need +to concatenate the result of calling `toHie` on all subexpressions and +appropriately annotated symbols contained in the subtree. + +The code above from the ToHie instance of `LhsExpr (GhcPass p)` is supposed +to work for both the renamed and typechecked source. `getTypeNode` is from +the `HasType` class defined in this file, and it has different instances +for `GhcTc` and `GhcRn` that allow it to access the type of the expression +when given a typechecked AST: + +class Data a => HasType a where + getTypeNode :: a -> HieM [HieAST Type] +instance HasType (LHsExpr GhcTc) where + getTypeNode e@(L spn e') = ... -- Actually get the type for this expression +instance HasType (LHsExpr GhcRn) where + getTypeNode (L spn e) = makeNode e spn -- Fallback to a regular `makeNode` without recording the type + +If your subtree doesn't have a span available, you can omit the `makeNode` +call and just recurse directly in to the subexpressions. + +-} + +-- These synonyms match those defined in compiler/GHC.hs +type RenamedSource = ( HsGroup GhcRn, [LImportDecl GhcRn] + , Maybe [(LIE GhcRn, Avails)] + , Maybe LHsDocString ) +type TypecheckedSource = LHsBinds GhcTc + + +{- Note [Name Remapping] +The Typechecker introduces new names for mono names in AbsBinds. +We don't care about the distinction between mono and poly bindings, +so we replace all occurrences of the mono name with the poly name. +-} +type VarMap a = DVarEnv (Var,a) +data HieState = HieState + { name_remapping :: NameEnv Id + , unlocated_ev_binds :: VarMap (S.Set ContextInfo) + -- These contain evidence bindings that we don't have a location for + -- These are placed at the top level Node in the HieAST after everything + -- else has been generated + -- This includes things like top level evidence bindings. + } + +addUnlocatedEvBind :: Var -> ContextInfo -> HieM () +addUnlocatedEvBind var ci = do + let go (a,b) (_,c) = (a,S.union b c) + lift $ modify' $ \s -> + s { unlocated_ev_binds = + extendDVarEnv_C go (unlocated_ev_binds s) + var (var,S.singleton ci) + } + +getUnlocatedEvBinds :: FastString -> HieM (NodeIdentifiers Type,[HieAST Type]) +getUnlocatedEvBinds file = do + binds <- lift $ gets unlocated_ev_binds + org <- ask + let elts = dVarEnvElts binds + + mkNodeInfo (n,ci) = (Right (varName n), IdentifierDetails (Just $ varType n) ci) + + go e@(v,_) (xs,ys) = case nameSrcSpan $ varName v of + RealSrcSpan spn _ + | srcSpanFile spn == file -> + let node = Node (mkSourcedNodeInfo org ni) spn [] + ni = NodeInfo mempty [] $ M.fromList [mkNodeInfo e] + in (xs,node:ys) + _ -> (mkNodeInfo e : xs,ys) + + (nis,asts) = foldr go ([],[]) elts + + pure $ (M.fromList nis, asts) + +initState :: HieState +initState = HieState emptyNameEnv emptyDVarEnv + +class ModifyState a where -- See Note [Name Remapping] + addSubstitution :: a -> a -> HieState -> HieState + +instance ModifyState Name where + addSubstitution _ _ hs = hs + +instance ModifyState Id where + addSubstitution mono poly hs = + hs{name_remapping = extendNameEnv (name_remapping hs) (varName mono) poly} + +modifyState :: ModifyState (IdP p) => [ABExport p] -> HieState -> HieState +modifyState = foldr go id + where + go ABE{abe_poly=poly,abe_mono=mono} f + = addSubstitution mono poly . f + go _ f = f + +type HieM = ReaderT NodeOrigin (StateT HieState DsM) + +-- | Construct an 'HieFile' from the outputs of the typechecker. +mkHieFile :: ModSummary + -> TcGblEnv + -> RenamedSource -> Hsc HieFile +mkHieFile ms ts rs = do + let src_file = expectJust "mkHieFile" (ml_hs_file $ ms_location ms) + src <- liftIO $ BS.readFile src_file + mkHieFileWithSource src_file src ms ts rs + +-- | Construct an 'HieFile' from the outputs of the typechecker but don't +-- read the source file again from disk. +mkHieFileWithSource :: FilePath + -> BS.ByteString + -> ModSummary + -> TcGblEnv + -> RenamedSource -> Hsc HieFile +mkHieFileWithSource src_file src ms ts rs = do + let tc_binds = tcg_binds ts + top_ev_binds = tcg_ev_binds ts + insts = tcg_insts ts + tcs = tcg_tcs ts + hsc_env <- Hsc $ \e w -> return (e, w) + (_msgs, res) <- liftIO $ initDs hsc_env ts $ getCompressedAsts tc_binds rs top_ev_binds insts tcs + let (asts',arr) = expectJust "mkHieFileWithSource" res + return $ HieFile + { hie_hs_file = src_file + , hie_module = ms_mod ms + , hie_types = arr + , hie_asts = asts' + -- mkIfaceExports sorts the AvailInfos for stability + , hie_exports = mkIfaceExports (tcg_exports ts) + , hie_hs_src = src + } + +getCompressedAsts :: TypecheckedSource -> RenamedSource -> Bag EvBind -> [ClsInst] -> [TyCon] + -> DsM (HieASTs TypeIndex, A.Array TypeIndex HieTypeFlat) +getCompressedAsts ts rs top_ev_binds insts tcs = do + asts <- enrichHie ts rs top_ev_binds insts tcs + return $ compressTypes asts + +enrichHie :: TypecheckedSource -> RenamedSource -> Bag EvBind -> [ClsInst] -> [TyCon] + -> DsM (HieASTs Type) +enrichHie ts (hsGrp, imports, exports, _) ev_bs insts tcs = + flip evalStateT initState $ flip runReaderT SourceInfo $ do + tasts <- toHie $ fmap (BC RegularBind ModuleScope) ts + rasts <- processGrp hsGrp + imps <- toHie $ filter (not . ideclImplicit . unLoc) imports + exps <- toHie $ fmap (map $ IEC Export . fst) exports + -- Add Instance bindings + forM_ insts $ \i -> + addUnlocatedEvBind (is_dfun i) (EvidenceVarBind (EvInstBind False (is_cls_nm i)) ModuleScope Nothing) + -- Add class parent bindings + forM_ tcs $ \tc -> + case tyConClass_maybe tc of + Nothing -> pure () + Just c -> forM_ (classSCSelIds c) $ \v -> + addUnlocatedEvBind v (EvidenceVarBind (EvInstBind True (className c)) ModuleScope Nothing) + let spanFile file children = case children of + [] -> realSrcLocSpan (mkRealSrcLoc file 1 1) + _ -> mkRealSrcSpan (realSrcSpanStart $ nodeSpan $ head children) + (realSrcSpanEnd $ nodeSpan $ last children) + + flat_asts = concat + [ tasts + , rasts + , imps + , exps + ] + + modulify (HiePath file) xs' = do + + top_ev_asts :: [HieAST Type] <- do + let + l :: SrcSpanAnnA + l = noAnnSrcSpan (RealSrcSpan (realSrcLocSpan $ mkRealSrcLoc file 1 1) Nothing) + toHie $ EvBindContext ModuleScope Nothing + $ L l (EvBinds ev_bs) + + (uloc_evs,more_ev_asts) <- getUnlocatedEvBinds file + + let xs = mergeSortAsts $ xs' ++ top_ev_asts ++ more_ev_asts + span = spanFile file xs + + moduleInfo = SourcedNodeInfo + $ M.singleton SourceInfo + $ (simpleNodeInfo "Module" "Module") + {nodeIdentifiers = uloc_evs} + + moduleNode = Node moduleInfo span [] + + case mergeSortAsts $ moduleNode : xs of + [x] -> return x + xs -> panicDoc "enrichHie: mergeSortAsts retur:ed more than one result" (ppr $ map nodeSpan xs) + + asts' <- sequence + $ M.mapWithKey modulify + $ M.fromListWith (++) + $ map (\x -> (HiePath (srcSpanFile (nodeSpan x)),[x])) flat_asts + + let asts = HieASTs $ resolveTyVarScopes asts' + return asts + where + processGrp grp = concatM + [ toHie $ fmap (RS ModuleScope ) hs_valds grp + , toHie $ hs_splcds grp + , toHie $ hs_tyclds grp + , toHie $ hs_derivds grp + , toHie $ hs_fixds grp + , toHie $ hs_defds grp + , toHie $ hs_fords grp + , toHie $ hs_warnds grp + , toHie $ hs_annds grp + , toHie $ hs_ruleds grp + ] + +getRealSpanA :: SrcSpanAnn' ann -> Maybe Span +getRealSpanA la = getRealSpan (locA la) + +getRealSpan :: SrcSpan -> Maybe Span +getRealSpan (RealSrcSpan sp _) = Just sp +getRealSpan _ = Nothing + +grhss_span :: (Anno (GRHS (GhcPass p) (LocatedA (body (GhcPass p)))) ~ SrcSpan + , Data (HsLocalBinds (GhcPass p))) + => GRHSs (GhcPass p) (LocatedA (body (GhcPass p))) -> SrcSpan +grhss_span (GRHSs _ xs bs) = foldl' combineSrcSpans (spanHsLocaLBinds bs) (map getLoc xs) + +bindingsOnly :: [Context Name] -> HieM [HieAST a] +bindingsOnly [] = pure [] +bindingsOnly (C c n : xs) = do + org <- ask + rest <- bindingsOnly xs + pure $ case nameSrcSpan n of + RealSrcSpan span _ -> Node (mkSourcedNodeInfo org nodeinfo) span [] : rest + where nodeinfo = NodeInfo S.empty [] (M.singleton (Right n) info) + info = mempty{identInfo = S.singleton c} + _ -> rest + +concatM :: Monad m => [m [a]] -> m [a] +concatM xs = concat <$> sequence xs + +{- Note [Capturing Scopes and other non local information] +toHie is a local transformation, but scopes of bindings cannot be known locally, +hence we have to push the relevant info down into the binding nodes. +We use the following types (*Context and *Scoped) to wrap things and +carry the required info +(Maybe Span) always carries the span of the entire binding, including rhs +-} +data Context a = C ContextInfo a -- Used for names and bindings + +data RContext a = RC RecFieldContext a +data RFContext a = RFC RecFieldContext (Maybe Span) a +-- ^ context for record fields + +data IEContext a = IEC IEType a +-- ^ context for imports/exports + +data BindContext a = BC BindType Scope a +-- ^ context for imports/exports + +data PatSynFieldContext a = PSC (Maybe Span) a +-- ^ context for pattern synonym fields. + +data SigContext a = SC SigInfo a +-- ^ context for type signatures + +data SigInfo = SI SigType (Maybe Span) + +data SigType = BindSig | ClassSig | InstSig + +data EvBindContext a = EvBindContext Scope (Maybe Span) a + +data RScoped a = RS Scope a +-- ^ Scope spans over everything to the right of a, (mostly) not +-- including a itself +-- (Includes a in a few special cases like recursive do bindings) or +-- let/where bindings + +-- | Pattern scope +data PScoped a = PS (Maybe Span) + Scope -- ^ use site of the pattern + Scope -- ^ pattern to the right of a, not including a + a + deriving (Typeable, Data) -- Pattern Scope + +{- Note [TyVar Scopes] +Due to -XScopedTypeVariables, type variables can be in scope quite far from +their original binding. We resolve the scope of these type variables +in a separate pass +-} +data TScoped a = TS TyVarScope a -- TyVarScope + +data TVScoped a = TVS TyVarScope Scope a -- TyVarScope +-- ^ First scope remains constant +-- Second scope is used to build up the scope of a tyvar over +-- things to its right, ala RScoped + +-- | Each element scopes over the elements to the right +listScopes :: Scope -> [LocatedA a] -> [RScoped (LocatedA a)] +listScopes _ [] = [] +listScopes rhsScope [pat] = [RS rhsScope pat] +listScopes rhsScope (pat : pats) = RS sc pat : pats' + where + pats'@((RS scope p):_) = listScopes rhsScope pats + sc = combineScopes scope $ mkScope $ getLocA p + +-- | 'listScopes' specialised to 'PScoped' things +patScopes + :: Maybe Span + -> Scope + -> Scope + -> [LPat (GhcPass p)] + -> [PScoped (LPat (GhcPass p))] +patScopes rsp useScope patScope xs = + map (\(RS sc a) -> PS rsp useScope sc a) $ + listScopes patScope xs + +-- | 'listScopes' specialised to 'HsPatSigType' +tScopes + :: Scope + -> Scope + -> [HsPatSigType (GhcPass a)] + -> [TScoped (HsPatSigType (GhcPass a))] +tScopes scope rhsScope xs = + map (\(RS sc a) -> TS (ResolvedScopes [scope, sc]) (unLoc a)) $ + listScopes rhsScope (map (\hsps -> L (getLoc $ hsps_body hsps) hsps) xs) + -- We make the HsPatSigType into a Located one by using the location of the underlying LHsType. + -- We then strip off the redundant location information afterward, and take the union of the given scope and those to the right when forming the TS. + +-- | 'listScopes' specialised to 'TVScoped' things +tvScopes + :: TyVarScope + -> Scope + -> [LHsTyVarBndr flag (GhcPass a)] + -> [TVScoped (LHsTyVarBndr flag (GhcPass a))] +tvScopes tvScope rhsScope xs = + map (\(RS sc a)-> TVS tvScope sc a) $ listScopes rhsScope xs + +{- Note [Scoping Rules for SigPat] +Explicitly quantified variables in pattern type signatures are not +brought into scope in the rhs, but implicitly quantified variables +are (HsWC and HsIB). +This is unlike other signatures, where explicitly quantified variables +are brought into the RHS Scope +For example +foo :: forall a. ...; +foo = ... -- a is in scope here + +bar (x :: forall a. a -> a) = ... -- a is not in scope here +-- ^ a is in scope here (pattern body) + +bax (x :: a) = ... -- a is in scope here + +This case in handled in the instance for HsPatSigType +-} + +class HasLoc a where + -- ^ conveniently calculate locations for things without locations attached + loc :: a -> SrcSpan + +instance HasLoc thing => HasLoc (PScoped thing) where + loc (PS _ _ _ a) = loc a + +instance HasLoc (Located a) where + loc (L l _) = l + +instance HasLoc (LocatedA a) where + loc (L la _) = locA la + +instance HasLoc (LocatedN a) where + loc (L la _) = locA la + +instance HasLoc a => HasLoc [a] where + loc [] = noSrcSpan + loc xs = foldl1' combineSrcSpans $ map loc xs + +instance (HasLoc a, HiePass p) => HasLoc (FamEqn (GhcPass p) a) where + loc (FamEqn _ a outer_bndrs b _ c) = case outer_bndrs of + HsOuterImplicit{} -> + foldl1' combineSrcSpans [loc a, loc b, loc c] + HsOuterExplicit{hso_bndrs = tvs} -> + foldl1' combineSrcSpans [loc a, loc tvs, loc b, loc c] + +instance (HasLoc tm, HasLoc ty) => HasLoc (HsArg tm ty) where + loc (HsValArg tm) = loc tm + loc (HsTypeArg _ ty) = loc ty + loc (HsArgPar sp) = sp + +instance HasLoc (HsDataDefn GhcRn) where + loc def@(HsDataDefn{}) = loc $ dd_cons def + -- Only used for data family instances, so we only need rhs + -- Most probably the rest will be unhelpful anyway + +-- | The main worker class +-- See Note [Updating HieAst for changes in the GHC AST] for more information +-- on how to add/modify instances for this. +class ToHie a where + toHie :: a -> HieM [HieAST Type] + +-- | Used to collect type info +class HasType a where + getTypeNode :: a -> HieM [HieAST Type] + +instance ToHie Void where + toHie v = absurd v + +instance (ToHie a) => ToHie [a] where + toHie = concatMapM toHie + +instance (ToHie a) => ToHie (Bag a) where + toHie = toHie . bagToList + +instance (ToHie a) => ToHie (Maybe a) where + toHie = maybe (pure []) toHie + +instance ToHie (IEContext (LocatedA ModuleName)) where + toHie (IEC c (L (SrcSpanAnn _ (RealSrcSpan span _)) mname)) = do + org <- ask + pure $ [Node (mkSourcedNodeInfo org $ NodeInfo S.empty [] idents) span []] + where details = mempty{identInfo = S.singleton (IEThing c)} + idents = M.singleton (Left mname) details + toHie _ = pure [] + +instance ToHie (Context (Located a)) => ToHie (Context (LocatedN a)) where + toHie (C c (L l a)) = toHie (C c (L (locA l) a)) + +instance ToHie (Context (Located a)) => ToHie (Context (LocatedA a)) where + toHie (C c (L l a)) = toHie (C c (L (locA l) a)) + +instance ToHie (Context (Located Var)) where + toHie c = case c of + C context (L (RealSrcSpan span _) name') + | varUnique name' == mkBuiltinUnique 1 -> pure [] + -- `mkOneRecordSelector` makes a field var using this unique, which we ignore + | otherwise -> do + m <- lift $ gets name_remapping + org <- ask + let name = case lookupNameEnv m (varName name') of + Just var -> var + Nothing-> name' + ty = case isDataConId_maybe name' of + Nothing -> varType name' + Just dc -> dataConNonlinearType dc + pure + [Node + (mkSourcedNodeInfo org $ NodeInfo S.empty [] $ + M.singleton (Right $ varName name) + (IdentifierDetails (Just ty) + (S.singleton context))) + span + []] + C (EvidenceVarBind i _ sp) (L _ name) -> do + addUnlocatedEvBind name (EvidenceVarBind i ModuleScope sp) + pure [] + _ -> pure [] + +instance ToHie (Context (Located Name)) where + toHie c = case c of + C context (L (RealSrcSpan span _) name') + | nameUnique name' == mkBuiltinUnique 1 -> pure [] + -- `mkOneRecordSelector` makes a field var using this unique, which we ignore + | otherwise -> do + m <- lift $ gets name_remapping + org <- ask + let name = case lookupNameEnv m name' of + Just var -> varName var + Nothing -> name' + pure + [Node + (mkSourcedNodeInfo org $ NodeInfo S.empty [] $ + M.singleton (Right name) + (IdentifierDetails Nothing + (S.singleton context))) + span + []] + _ -> pure [] + +evVarsOfTermList :: EvTerm -> [EvId] +evVarsOfTermList (EvExpr e) = exprSomeFreeVarsList isEvVar e +evVarsOfTermList (EvTypeable _ ev) = + case ev of + EvTypeableTyCon _ e -> concatMap evVarsOfTermList e + EvTypeableTyApp e1 e2 -> concatMap evVarsOfTermList [e1,e2] + EvTypeableTrFun e1 e2 e3 -> concatMap evVarsOfTermList [e1,e2,e3] + EvTypeableTyLit e -> evVarsOfTermList e +evVarsOfTermList (EvFun{}) = [] + +instance ToHie (EvBindContext (LocatedA TcEvBinds)) where + toHie (EvBindContext sc sp (L span (EvBinds bs))) + = concatMapM go $ bagToList bs + where + go evbind = do + let evDeps = evVarsOfTermList $ eb_rhs evbind + depNames = EvBindDeps $ map varName evDeps + concatM $ + [ toHie (C (EvidenceVarBind (EvLetBind depNames) (combineScopes sc (mkScopeA span)) sp) + (L span $ eb_lhs evbind)) + , toHie $ map (C EvidenceVarUse . L span) $ evDeps + ] + toHie _ = pure [] + +instance ToHie (LocatedA HsWrapper) where + toHie (L osp wrap) + = case wrap of + (WpLet bs) -> toHie $ EvBindContext (mkScopeA osp) (getRealSpanA osp) (L osp bs) + (WpCompose a b) -> concatM $ + [toHie (L osp a), toHie (L osp b)] + (WpFun a b _ _) -> concatM $ + [toHie (L osp a), toHie (L osp b)] + (WpEvLam a) -> + toHie $ C (EvidenceVarBind EvWrapperBind (mkScopeA osp) (getRealSpanA osp)) + $ L osp a + (WpEvApp a) -> + concatMapM (toHie . C EvidenceVarUse . L osp) $ evVarsOfTermList a + _ -> pure [] + +instance HiePass p => HasType (LocatedA (HsBind (GhcPass p))) where + getTypeNode (L spn bind) = + case hiePass @p of + HieRn -> makeNode bind (locA spn) + HieTc -> case bind of + FunBind{fun_id = name} -> makeTypeNode bind (locA spn) (varType $ unLoc name) + _ -> makeNode bind (locA spn) + +instance HiePass p => HasType (LocatedA (Pat (GhcPass p))) where + getTypeNode (L spn pat) = + case hiePass @p of + HieRn -> makeNodeA pat spn + HieTc -> makeTypeNodeA pat spn (hsPatType pat) + +-- | This instance tries to construct 'HieAST' nodes which include the type of +-- the expression. It is not yet possible to do this efficiently for all +-- expression forms, so we skip filling in the type for those inputs. +-- +-- 'HsApp', for example, doesn't have any type information available directly on +-- the node. Our next recourse would be to desugar it into a 'CoreExpr' then +-- query the type of that. Yet both the desugaring call and the type query both +-- involve recursive calls to the function and argument! This is particularly +-- problematic when you realize that the HIE traversal will eventually visit +-- those nodes too and ask for their types again. +-- +-- Since the above is quite costly, we just skip cases where computing the +-- expression's type is going to be expensive. +-- +-- See #16233 +instance HiePass p => HasType (LocatedA (HsExpr (GhcPass p))) where + getTypeNode e@(L spn e') = + case hiePass @p of + HieRn -> makeNodeA e' spn + HieTc -> + -- Some expression forms have their type immediately available + let tyOpt = case e' of + HsUnboundVar (HER _ ty _) _ -> Just ty + HsLit _ l -> Just (hsLitType l) + HsOverLit _ o -> Just (overLitType o) + + HsConLikeOut _ (RealDataCon con) -> Just (dataConNonlinearType con) + + HsLam _ (MG { mg_ext = groupTy }) -> Just (matchGroupType groupTy) + HsLamCase _ (MG { mg_ext = groupTy }) -> Just (matchGroupType groupTy) + HsCase _ _ (MG { mg_ext = groupTy }) -> Just (mg_res_ty groupTy) + + ExplicitList ty _ -> Just (mkListTy ty) + ExplicitSum ty _ _ _ -> Just (mkSumTy ty) + HsDo ty _ _ -> Just ty + HsMultiIf ty _ -> Just ty + + _ -> Nothing + + in + case tyOpt of + Just t -> makeTypeNodeA e' spn t + Nothing + | skipDesugaring e' -> fallback + | otherwise -> do + (e, no_errs) <- lift $ lift $ discardWarningsDs $ askNoErrsDs $ dsLExpr e + if no_errs + then makeTypeNodeA e' spn . exprType $ e + else fallback + where + fallback = makeNodeA e' spn + + matchGroupType :: MatchGroupTc -> Type + matchGroupType (MatchGroupTc args res) = mkVisFunTys args res + + -- | Skip desugaring of these expressions for performance reasons. + -- + -- See impact on Haddock output (esp. missing type annotations or links) + -- before marking more things here as 'False'. See impact on Haddock + -- performance before marking more things as 'True'. + skipDesugaring :: HsExpr GhcTc -> Bool + skipDesugaring e = case e of + HsVar{} -> False + HsConLikeOut{} -> False + HsRecFld{} -> False + HsOverLabel{} -> False + HsIPVar{} -> False + XExpr (WrapExpr {}) -> False + -- CHANGED: the line below makes record-dot-syntax types work + XExpr (ExpansionExpr {}) -> False + _ -> True + +data HiePassEv p where + HieRn :: HiePassEv 'Renamed + HieTc :: HiePassEv 'Typechecked + +class ( IsPass p + , HiePass (NoGhcTcPass p) + , ModifyState (IdGhcP p) + , Data (GRHS (GhcPass p) (LocatedA (HsExpr (GhcPass p)))) + , Data (Match (GhcPass p) (LocatedA (HsExpr (GhcPass p)))) + , Data (Match (GhcPass p) (LocatedA (HsCmd (GhcPass p)))) + , Data (Stmt (GhcPass p) (LocatedA (HsExpr (GhcPass p)))) + , Data (Stmt (GhcPass p) (LocatedA (HsCmd (GhcPass p)))) + , Data (HsExpr (GhcPass p)) + , Data (HsCmd (GhcPass p)) + , Data (AmbiguousFieldOcc (GhcPass p)) + , Data (HsCmdTop (GhcPass p)) + , Data (GRHS (GhcPass p) (LocatedA (HsCmd (GhcPass p)))) + , Data (HsSplice (GhcPass p)) + , Data (HsLocalBinds (GhcPass p)) + , Data (FieldOcc (GhcPass p)) + , Data (HsTupArg (GhcPass p)) + , Data (IPBind (GhcPass p)) + , ToHie (Context (Located (IdGhcP p))) + , ToHie (RFContext (Located (AmbiguousFieldOcc (GhcPass p)))) + , ToHie (RFContext (Located (FieldOcc (GhcPass p)))) + , ToHie (TScoped (LHsWcType (GhcPass (NoGhcTcPass p)))) + , ToHie (TScoped (LHsSigWcType (GhcPass (NoGhcTcPass p)))) + , Anno (IdGhcP p) ~ SrcSpanAnnN + ) + => HiePass p where + hiePass :: HiePassEv p + +instance HiePass 'Renamed where + hiePass = HieRn +instance HiePass 'Typechecked where + hiePass = HieTc + +instance ToHie (Context (Located NoExtField)) where + toHie _ = pure [] + +type AnnoBody p body + = ( Anno (Match (GhcPass p) (LocatedA (body (GhcPass p)))) + ~ SrcSpanAnnA + , Anno [LocatedA (Match (GhcPass p) (LocatedA (body (GhcPass p))))] + ~ SrcSpanAnnL + , Anno (GRHS (GhcPass p) (LocatedA (body (GhcPass p)))) + ~ SrcSpan + , Anno (StmtLR (GhcPass p) (GhcPass p) (LocatedA (body (GhcPass p)))) ~ SrcSpanAnnA + + , Data (body (GhcPass p)) + , Data (Match (GhcPass p) (LocatedA (body (GhcPass p)))) + , Data (GRHS (GhcPass p) (LocatedA (body (GhcPass p)))) + , Data (Stmt (GhcPass p) (LocatedA (body (GhcPass p)))) + + , IsPass p + ) + +instance HiePass p => ToHie (BindContext (LocatedA (HsBind (GhcPass p)))) where + toHie (BC context scope b@(L span bind)) = + concatM $ getTypeNode b : case bind of + FunBind{fun_id = name, fun_matches = matches, fun_ext = wrap} -> + [ toHie $ C (ValBind context scope $ getRealSpanA span) name + , toHie matches + , case hiePass @p of + HieTc -> toHie $ L span wrap + _ -> pure [] + ] + PatBind{pat_lhs = lhs, pat_rhs = rhs} -> + [ toHie $ PS (getRealSpan (locA span)) scope NoScope lhs + , toHie rhs + ] + VarBind{var_rhs = expr} -> + [ toHie expr + ] + AbsBinds{ abs_exports = xs, abs_binds = binds + , abs_ev_binds = ev_binds + , abs_ev_vars = ev_vars } -> + [ lift (modify (modifyState xs)) >> -- Note [Name Remapping] + (toHie $ fmap (BC context scope) binds) + , toHie $ map (L span . abe_wrap) xs + , toHie $ + map (EvBindContext (mkScopeA span) (getRealSpanA span) + . L span) ev_binds + , toHie $ + map (C (EvidenceVarBind EvSigBind + (mkScopeA span) + (getRealSpanA span)) + . L span) ev_vars + ] + PatSynBind _ psb -> + [ toHie $ L (locA span) psb -- PatSynBinds only occur at the top level + ] + +instance ( HiePass p + , AnnoBody p body + , ToHie (LocatedA (body (GhcPass p))) + ) => ToHie (MatchGroup (GhcPass p) (LocatedA (body (GhcPass p)))) where + toHie mg = case mg of + MG{ mg_alts = (L span alts) , mg_origin = origin} -> + local (setOrigin origin) $ concatM + [ locOnly (locA span) + , toHie alts + ] + +setOrigin :: Origin -> NodeOrigin -> NodeOrigin +setOrigin FromSource _ = SourceInfo +setOrigin Generated _ = GeneratedInfo + +instance HiePass p => ToHie (Located (PatSynBind (GhcPass p) (GhcPass p))) where + toHie (L sp psb) = concatM $ case psb of + PSB{psb_id=var, psb_args=dets, psb_def=pat, psb_dir=dir} -> + [ toHie $ C (Decl PatSynDec $ getRealSpan sp) var + , toHie $ toBind dets + , toHie $ PS Nothing lhsScope patScope pat + , toHie dir + ] + where + lhsScope = combineScopes varScope detScope + varScope = mkLScopeN var + patScope = mkScopeA $ getLoc pat + detScope = case dets of + (PrefixCon _ args) -> foldr combineScopes NoScope $ map mkLScopeN args + (InfixCon a b) -> combineScopes (mkLScopeN a) (mkLScopeN b) + (RecCon r) -> foldr go NoScope r + go (RecordPatSynField a b) c = combineScopes c + $ combineScopes (mkLScopeN (rdrNameFieldOcc a)) (mkLScopeN b) + detSpan = case detScope of + LocalScope a -> Just a + _ -> Nothing + -- CHANGED: removed ASSERT + -- toBind (PrefixCon ts args) = ASSERT(null ts) PrefixCon ts $ map (C Use) args + toBind (PrefixCon ts args) = PrefixCon ts $ map (C Use) args + toBind (InfixCon a b) = InfixCon (C Use a) (C Use b) + toBind (RecCon r) = RecCon $ map (PSC detSpan) r + +instance HiePass p => ToHie (HsPatSynDir (GhcPass p)) where + toHie dir = case dir of + ExplicitBidirectional mg -> toHie mg + _ -> pure [] + +instance ( HiePass p + , Data (body (GhcPass p)) + , AnnoBody p body + , ToHie (LocatedA (body (GhcPass p))) + ) => ToHie (LocatedA (Match (GhcPass p) (LocatedA (body (GhcPass p))))) where + toHie (L span m ) = concatM $ node : case m of + Match{m_ctxt=mctx, m_pats = pats, m_grhss = grhss } -> + [ toHie mctx + , let rhsScope = mkScope $ grhss_span grhss + in toHie $ patScopes Nothing rhsScope NoScope pats + , toHie grhss + ] + where + node = case hiePass @p of + HieTc -> makeNodeA m span + HieRn -> makeNodeA m span + +instance HiePass p => ToHie (HsMatchContext (GhcPass p)) where + toHie (FunRhs{mc_fun=name}) = toHie $ C MatchBind name + toHie (StmtCtxt a) = toHie a + toHie _ = pure [] + +instance HiePass p => ToHie (HsStmtContext (GhcPass p)) where + toHie (PatGuard a) = toHie a + toHie (ParStmtCtxt a) = toHie a + toHie (TransStmtCtxt a) = toHie a + toHie _ = pure [] + +instance HiePass p => ToHie (PScoped (LocatedA (Pat (GhcPass p)))) where + toHie (PS rsp scope pscope lpat@(L ospan opat)) = + concatM $ getTypeNode lpat : case opat of + WildPat _ -> + [] + VarPat _ lname -> + [ toHie $ C (PatternBind scope pscope rsp) lname + ] + LazyPat _ p -> + [ toHie $ PS rsp scope pscope p + ] + AsPat _ lname pat -> + [ toHie $ C (PatternBind scope + (combineScopes (mkLScopeA pat) pscope) + rsp) + lname + , toHie $ PS rsp scope pscope pat + ] + ParPat _ pat -> + [ toHie $ PS rsp scope pscope pat + ] + BangPat _ pat -> + [ toHie $ PS rsp scope pscope pat + ] + ListPat _ pats -> + [ toHie $ patScopes rsp scope pscope pats + ] + TuplePat _ pats _ -> + [ toHie $ patScopes rsp scope pscope pats + ] + SumPat _ pat _ _ -> + [ toHie $ PS rsp scope pscope pat + ] + ConPat {pat_con = con, pat_args = dets, pat_con_ext = ext} -> + case hiePass @p of + HieTc -> + [ toHie $ C Use $ fmap conLikeName con + , toHie $ contextify dets + , let ev_binds = cpt_binds ext + ev_vars = cpt_dicts ext + wrap = cpt_wrap ext + evscope = mkScopeA ospan `combineScopes` scope `combineScopes` pscope + in concatM [ toHie $ EvBindContext scope rsp $ L ospan ev_binds + , toHie $ L ospan wrap + , toHie $ map (C (EvidenceVarBind EvPatternBind evscope rsp) + . L ospan) ev_vars + ] + ] + HieRn -> + [ toHie $ C Use con + , toHie $ contextify dets + ] + ViewPat _ expr pat -> + [ toHie expr + , toHie $ PS rsp scope pscope pat + ] + SplicePat _ sp -> + [ toHie $ L ospan sp + ] + LitPat _ _ -> + [] + NPat _ _ _ _ -> + [] + NPlusKPat _ n _ _ _ _ -> + [ toHie $ C (PatternBind scope pscope rsp) n + ] + SigPat _ pat sig -> + [ toHie $ PS rsp scope pscope pat + , case hiePass @p of + HieTc -> + let cscope = mkLScopeA pat in + toHie $ TS (ResolvedScopes [cscope, scope, pscope]) + sig + HieRn -> pure [] + ] + XPat e -> + case hiePass @p of + HieTc -> + let CoPat wrap pat _ = e + in [ toHie $ L ospan wrap + , toHie $ PS rsp scope pscope $ (L ospan pat) + ] +-- CHANGED: removed preprocessor stuff +-- #if __GLASGOW_HASKELL__ < 811 +-- HieRn -> [] +-- #endif + where + contextify :: a ~ LPat (GhcPass p) => HsConDetails (HsPatSigType (NoGhcTc (GhcPass p))) a (HsRecFields (GhcPass p) a) + -> HsConDetails (TScoped (HsPatSigType (NoGhcTc (GhcPass p)))) (PScoped a) (RContext (HsRecFields (GhcPass p) (PScoped a))) + contextify (PrefixCon tyargs args) = PrefixCon (tScopes scope argscope tyargs) (patScopes rsp scope pscope args) + where argscope = foldr combineScopes NoScope $ map mkLScopeA args + contextify (InfixCon a b) = InfixCon a' b' + where [a', b'] = patScopes rsp scope pscope [a,b] + contextify (RecCon r) = RecCon $ RC RecFieldMatch $ contextify_rec r + contextify_rec (HsRecFields fds a) = HsRecFields (map go scoped_fds) a + where + go :: RScoped (LocatedA (HsRecField' id a1)) + -> LocatedA (HsRecField' id (PScoped a1)) -- AZ + go (RS fscope (L spn (HsRecField x lbl pat pun))) = + L spn $ HsRecField x lbl (PS rsp scope fscope pat) pun + scoped_fds = listScopes pscope fds + +instance ToHie (TScoped (HsPatSigType GhcRn)) where + toHie (TS sc (HsPS (HsPSRn wcs tvs) body@(L span _))) = concatM $ + [ bindingsOnly $ map (C $ TyVarBind (mkScopeA span) sc) (wcs++tvs) + , toHie body + ] + -- See Note [Scoping Rules for SigPat] + +instance ( ToHie (LocatedA (body (GhcPass p))) + , HiePass p + , AnnoBody p body + ) => ToHie (GRHSs (GhcPass p) (LocatedA (body (GhcPass p)))) where + toHie grhs = concatM $ case grhs of + GRHSs _ grhss binds -> + [ toHie grhss + , toHie $ RS (mkScope $ grhss_span grhs) binds + ] + +instance ( ToHie (LocatedA (body (GhcPass p))) + , HiePass p + , AnnoBody p body + ) => ToHie (Located (GRHS (GhcPass p) (LocatedA (body (GhcPass p))))) where + toHie (L span g) = concatM $ node : case g of + GRHS _ guards body -> + [ toHie $ listScopes (mkLScopeA body) guards + , toHie body + ] + where + node = case hiePass @p of + HieRn -> makeNode g span + HieTc -> makeNode g span + +instance HiePass p => ToHie (LocatedA (HsExpr (GhcPass p))) where + toHie e@(L mspan oexpr) = concatM $ getTypeNode e : case oexpr of + HsVar _ (L _ var) -> + [ toHie $ C Use (L mspan var) + -- Patch up var location since typechecker removes it + ] + HsUnboundVar _ _ -> [] -- there is an unbound name here, but that causes trouble + HsConLikeOut _ con -> + [ toHie $ C Use $ L mspan $ conLikeName con + ] + HsRecFld _ fld -> + [ toHie $ RFC RecFieldOcc Nothing (L (locA mspan) fld) + ] + HsOverLabel {} -> [] + HsIPVar _ _ -> [] + HsOverLit _ _ -> [] + HsLit _ _ -> [] + HsLam _ mg -> + [ toHie mg + ] + HsLamCase _ mg -> + [ toHie mg + ] + HsApp _ a b -> + [ toHie a + , toHie b + ] + HsAppType _ expr sig -> + [ toHie expr + , toHie $ TS (ResolvedScopes []) sig + ] + OpApp _ a b c -> + [ toHie a + , toHie b + , toHie c + ] + NegApp _ a _ -> + [ toHie a + ] + HsPar _ a -> + [ toHie a + ] + SectionL _ a b -> + [ toHie a + , toHie b + ] + SectionR _ a b -> + [ toHie a + , toHie b + ] + ExplicitTuple _ args _ -> + [ toHie args + ] + ExplicitSum _ _ _ expr -> + [ toHie expr + ] + HsCase _ expr matches -> + [ toHie expr + , toHie matches + ] + HsIf _ a b c -> + [ toHie a + , toHie b + , toHie c + ] + HsMultiIf _ grhss -> + [ toHie grhss + ] + HsLet _ binds expr -> + [ toHie $ RS (mkLScopeA expr) binds + , toHie expr + ] + HsDo _ _ (L ispan stmts) -> + [ locOnly (locA ispan) + , toHie $ listScopes NoScope stmts + ] + ExplicitList _ exprs -> + [ toHie exprs + ] + RecordCon { rcon_con = con, rcon_flds = binds} -> + [ toHie $ C Use $ con_name + , toHie $ RC RecFieldAssign $ binds + ] + where + con_name :: LocatedN Name + con_name = case hiePass @p of -- Like ConPat + HieRn -> con + HieTc -> fmap conLikeName con + RecordUpd {rupd_expr = expr, rupd_flds = Left upds}-> + [ toHie expr + , toHie $ map (RC RecFieldAssign) upds + ] + RecordUpd {rupd_expr = expr, rupd_flds = Right _}-> + [ toHie expr + ] + ExprWithTySig _ expr sig -> + [ toHie expr + , toHie $ TS (ResolvedScopes [mkLScopeA expr]) sig + ] + ArithSeq _ _ info -> + [ toHie info + ] + HsPragE _ _ expr -> + [ toHie expr + ] + HsProc _ pat cmdtop -> + [ toHie $ PS Nothing (mkLScope cmdtop) NoScope pat + , toHie cmdtop + ] + HsStatic _ expr -> + [ toHie expr + ] + HsTick _ _ expr -> + [ toHie expr + ] + HsBinTick _ _ _ expr -> + [ toHie expr + ] + HsBracket _ b -> + [ toHie b + ] + HsRnBracketOut _ b p -> + [ toHie b + , toHie p + ] + HsTcBracketOut _ _wrap b p -> + [ toHie b + , toHie p + ] + HsSpliceE _ x -> + [ toHie $ L mspan x + ] + HsGetField {} -> [] + HsProjection {} -> [] + XExpr x + | GhcTc <- ghcPass @p + , WrapExpr (HsWrap w a) <- x + -> [ toHie $ L mspan a + , toHie (L mspan w) + ] + | GhcTc <- ghcPass @p + , ExpansionExpr (HsExpanded _ b) <- x + -> [ toHie (L mspan b) + ] + | otherwise -> [] + +-- NOTE: no longer have the location +instance HiePass p => ToHie (HsTupArg (GhcPass p)) where + toHie arg = concatM $ case arg of + Present _ expr -> + [ toHie expr + ] + Missing _ -> [] + +instance ( ToHie (LocatedA (body (GhcPass p))) + , AnnoBody p body + , HiePass p + ) => ToHie (RScoped (LocatedA (Stmt (GhcPass p) (LocatedA (body (GhcPass p)))))) where + toHie (RS scope (L span stmt)) = concatM $ node : case stmt of + LastStmt _ body _ _ -> + [ toHie body + ] + BindStmt _ pat body -> + [ toHie $ PS (getRealSpan $ getLocA body) scope NoScope pat + , toHie body + ] + ApplicativeStmt _ stmts _ -> + [ concatMapM (toHie . RS scope . snd) stmts + ] + BodyStmt _ body _ _ -> + [ toHie body + ] + LetStmt _ binds -> + [ toHie $ RS scope binds + ] + ParStmt _ parstmts _ _ -> + [ concatMapM (\(ParStmtBlock _ stmts _ _) -> + toHie $ listScopes NoScope stmts) + parstmts + ] + TransStmt {trS_stmts = stmts, trS_using = using, trS_by = by} -> + [ toHie $ listScopes scope stmts + , toHie using + , toHie by + ] + RecStmt {recS_stmts = L _ stmts} -> + [ toHie $ map (RS $ combineScopes scope (mkScope (locA span))) stmts + ] + where + node = case hiePass @p of + HieTc -> makeNodeA stmt span + HieRn -> makeNodeA stmt span + +instance HiePass p => ToHie (RScoped (HsLocalBinds (GhcPass p))) where + toHie (RS scope binds) = concatM $ makeNode binds (spanHsLocaLBinds binds) : case binds of + EmptyLocalBinds _ -> [] + HsIPBinds _ ipbinds -> case ipbinds of + IPBinds evbinds xs -> let sc = combineScopes scope $ scopeHsLocaLBinds binds + sp :: SrcSpanAnnA + sp = noAnnSrcSpan $ spanHsLocaLBinds binds in + [ + case hiePass @p of + HieTc -> toHie $ EvBindContext sc (getRealSpan $ locA sp) $ L sp evbinds + HieRn -> pure [] + , toHie $ map (RS sc) xs + ] + HsValBinds _ valBinds -> + [ + toHie $ RS (combineScopes scope (scopeHsLocaLBinds binds)) + valBinds + ] + + +scopeHsLocaLBinds :: HsLocalBinds (GhcPass p) -> Scope +scopeHsLocaLBinds (HsValBinds _ (ValBinds _ bs sigs)) + = foldr combineScopes NoScope (bsScope ++ sigsScope) + where + bsScope :: [Scope] + bsScope = map (mkScopeA . getLoc) $ bagToList bs + sigsScope :: [Scope] + sigsScope = map (mkScope . getLocA) sigs +scopeHsLocaLBinds (HsValBinds _ (XValBindsLR (NValBinds bs sigs))) + = foldr combineScopes NoScope (bsScope ++ sigsScope) + where + bsScope :: [Scope] + bsScope = map (mkScopeA . getLoc) $ concatMap (bagToList . snd) bs + sigsScope :: [Scope] + sigsScope = map (mkScope . getLocA) sigs + +scopeHsLocaLBinds (HsIPBinds _ (IPBinds _ bs)) + = foldr combineScopes NoScope (map (mkScopeA . getLoc) bs) +scopeHsLocaLBinds (EmptyLocalBinds _) = NoScope + + +instance HiePass p => ToHie (RScoped (LocatedA (IPBind (GhcPass p)))) where + toHie (RS scope (L sp bind)) = concatM $ makeNodeA bind sp : case bind of + IPBind _ (Left _) expr -> [toHie expr] + IPBind _ (Right v) expr -> + [ toHie $ C (EvidenceVarBind EvImplicitBind scope (getRealSpanA sp)) + $ L sp v + , toHie expr + ] + +instance HiePass p => ToHie (RScoped (HsValBindsLR (GhcPass p) (GhcPass p))) where + toHie (RS sc v) = concatM $ case v of + ValBinds _ binds sigs -> + [ toHie $ fmap (BC RegularBind sc) binds + , toHie $ fmap (SC (SI BindSig Nothing)) sigs + ] + XValBindsLR x -> [ toHie $ RS sc x ] + +instance HiePass p => ToHie (RScoped (NHsValBindsLR (GhcPass p))) where + toHie (RS sc (NValBinds binds sigs)) = concatM $ + [ toHie (concatMap (map (BC RegularBind sc) . bagToList . snd) binds) + , toHie $ fmap (SC (SI BindSig Nothing)) sigs + ] + +instance ( ToHie arg , HasLoc arg , Data arg + , HiePass p ) => ToHie (RContext (HsRecFields (GhcPass p) arg)) where + toHie (RC c (HsRecFields fields _)) = toHie $ map (RC c) fields + +instance ( ToHie (RFContext (Located label)) + , ToHie arg, HasLoc arg, Data arg + , Data label + ) => ToHie (RContext (LocatedA (HsRecField' label arg))) where + toHie (RC c (L span recfld)) = concatM $ makeNode recfld (locA span) : case recfld of + HsRecField _ label expr _ -> + [ toHie $ RFC c (getRealSpan $ loc expr) label + , toHie expr + ] + +instance ToHie (RFContext (Located (FieldOcc GhcRn))) where + toHie (RFC c rhs (L nspan f)) = concatM $ case f of + FieldOcc name _ -> + [ toHie $ C (RecField c rhs) (L nspan name) + ] + +instance ToHie (RFContext (Located (FieldOcc GhcTc))) where + toHie (RFC c rhs (L nspan f)) = concatM $ case f of + FieldOcc var _ -> + [ toHie $ C (RecField c rhs) (L nspan var) + ] + +instance ToHie (RFContext (Located (AmbiguousFieldOcc GhcRn))) where + toHie (RFC c rhs (L nspan afo)) = concatM $ case afo of + Unambiguous name _ -> + [ toHie $ C (RecField c rhs) $ L nspan name + ] + Ambiguous _name _ -> + [ ] + +instance ToHie (RFContext (Located (AmbiguousFieldOcc GhcTc))) where + toHie (RFC c rhs (L nspan afo)) = concatM $ case afo of + Unambiguous var _ -> + [ toHie $ C (RecField c rhs) (L nspan var) + ] + Ambiguous var _ -> + [ toHie $ C (RecField c rhs) (L nspan var) + ] + +instance HiePass p => ToHie (RScoped (ApplicativeArg (GhcPass p))) where + toHie (RS sc (ApplicativeArgOne _ pat expr _)) = concatM + [ toHie $ PS Nothing sc NoScope pat + , toHie expr + ] + toHie (RS sc (ApplicativeArgMany _ stmts _ pat _)) = concatM + [ toHie $ listScopes NoScope stmts + , toHie $ PS Nothing sc NoScope pat + ] + +instance (ToHie tyarg, ToHie arg, ToHie rec) => ToHie (HsConDetails tyarg arg rec) where + toHie (PrefixCon tyargs args) = concatM [ toHie tyargs, toHie args ] + toHie (RecCon rec) = toHie rec + toHie (InfixCon a b) = concatM [ toHie a, toHie b] + +instance ToHie (HsConDeclGADTDetails GhcRn) where + toHie (PrefixConGADT args) = toHie args + toHie (RecConGADT rec) = toHie rec + +instance HiePass p => ToHie (Located (HsCmdTop (GhcPass p))) where + toHie (L span top) = concatM $ makeNode top span : case top of + HsCmdTop _ cmd -> + [ toHie cmd + ] + +instance HiePass p => ToHie (LocatedA (HsCmd (GhcPass p))) where + toHie (L span cmd) = concatM $ makeNodeA cmd span : case cmd of + HsCmdArrApp _ a b _ _ -> + [ toHie a + , toHie b + ] + HsCmdArrForm _ a _ _ cmdtops -> + [ toHie a + , toHie cmdtops + ] + HsCmdApp _ a b -> + [ toHie a + , toHie b + ] + HsCmdLam _ mg -> + [ toHie mg + ] + HsCmdPar _ a -> + [ toHie a + ] + HsCmdCase _ expr alts -> + [ toHie expr + , toHie alts + ] + HsCmdLamCase _ alts -> + [ toHie alts + ] + HsCmdIf _ _ a b c -> + [ toHie a + , toHie b + , toHie c + ] + HsCmdLet _ binds cmd' -> + [ toHie $ RS (mkLScopeA cmd') binds + , toHie cmd' + ] + HsCmdDo _ (L ispan stmts) -> + [ locOnly (locA ispan) + , toHie $ listScopes NoScope stmts + ] + XCmd _ -> [] + +instance ToHie (TyClGroup GhcRn) where + toHie TyClGroup{ group_tyclds = classes + , group_roles = roles + , group_kisigs = sigs + , group_instds = instances } = + concatM + [ toHie classes + , toHie sigs + , toHie roles + , toHie instances + ] + +instance ToHie (LocatedA (TyClDecl GhcRn)) where + toHie (L span decl) = concatM $ makeNodeA decl span : case decl of + FamDecl {tcdFam = fdecl} -> + [ toHie ((L span fdecl) :: LFamilyDecl GhcRn) + ] + SynDecl {tcdLName = name, tcdTyVars = vars, tcdRhs = typ} -> + [ toHie $ C (Decl SynDec $ getRealSpanA span) name + , toHie $ TS (ResolvedScopes [mkScope $ getLocA typ]) vars + , toHie typ + ] + DataDecl {tcdLName = name, tcdTyVars = vars, tcdDataDefn = defn} -> + [ toHie $ C (Decl DataDec $ getRealSpanA span) name + , toHie $ TS (ResolvedScopes [quant_scope, rhs_scope]) vars + , toHie defn + ] + where + quant_scope = mkLScopeA $ fromMaybe (noLocA []) $ dd_ctxt defn + rhs_scope = sig_sc `combineScopes` con_sc `combineScopes` deriv_sc + sig_sc = maybe NoScope mkLScopeA $ dd_kindSig defn + con_sc = foldr combineScopes NoScope $ map mkLScopeA $ dd_cons defn + deriv_sc = foldr combineScopes NoScope $ map mkLScope $ dd_derivs defn + ClassDecl { tcdCtxt = context + , tcdLName = name + , tcdTyVars = vars + , tcdFDs = deps + , tcdSigs = sigs + , tcdMeths = meths + , tcdATs = typs + , tcdATDefs = deftyps + } -> + [ toHie $ C (Decl ClassDec $ getRealSpanA span) name + , toHie context + , toHie $ TS (ResolvedScopes [context_scope, rhs_scope]) vars + , toHie deps + , toHie $ map (SC $ SI ClassSig $ getRealSpanA span) sigs + , toHie $ fmap (BC InstanceBind ModuleScope) meths + , toHie typs + , concatMapM (locOnly . getLocA) deftyps + , toHie deftyps + ] + where + context_scope = mkLScopeA $ fromMaybe (noLocA []) context + rhs_scope = foldl1' combineScopes $ map mkScope + [ loc deps, loc sigs, loc (bagToList meths), loc typs, loc deftyps] + +instance ToHie (LocatedA (FamilyDecl GhcRn)) where + toHie (L span decl) = concatM $ makeNodeA decl span : case decl of + FamilyDecl _ info _ name vars _ sig inj -> + [ toHie $ C (Decl FamDec $ getRealSpanA span) name + , toHie $ TS (ResolvedScopes [rhsSpan]) vars + , toHie info + , toHie $ RS injSpan sig + , toHie inj + ] + where + rhsSpan = sigSpan `combineScopes` injSpan + sigSpan = mkScope $ getLoc sig + injSpan = maybe NoScope (mkScope . getLoc) inj + +instance ToHie (FamilyInfo GhcRn) where + toHie (ClosedTypeFamily (Just eqns)) = concatM $ + [ concatMapM (locOnly . getLocA) eqns + , toHie $ map go eqns + ] + where + go (L l ib) = TS (ResolvedScopes [mkScopeA l]) ib + toHie _ = pure [] + +instance ToHie (RScoped (Located (FamilyResultSig GhcRn))) where + toHie (RS sc (L span sig)) = concatM $ makeNode sig span : case sig of + NoSig _ -> + [] + KindSig _ k -> + [ toHie k + ] + TyVarSig _ bndr -> + [ toHie $ TVS (ResolvedScopes [sc]) NoScope bndr + ] + +instance ToHie (LocatedA (FunDep GhcRn)) where + toHie (L span fd@(FunDep _ lhs rhs)) = concatM $ + [ makeNode fd (locA span) + , toHie $ map (C Use) lhs + , toHie $ map (C Use) rhs + ] + + +instance ToHie (TScoped (FamEqn GhcRn (HsDataDefn GhcRn))) where + toHie (TS _ f) = toHie f + +instance ToHie (TScoped (FamEqn GhcRn (LocatedA (HsType GhcRn)))) where + toHie (TS _ f) = toHie f + +instance (ToHie rhs, HasLoc rhs) + => ToHie (FamEqn GhcRn rhs) where + toHie fe@(FamEqn _ var outer_bndrs pats _ rhs) = concatM $ + [ toHie $ C (Decl InstDec $ getRealSpan $ loc fe) var + , toHie $ TVS (ResolvedScopes []) scope outer_bndrs + , toHie pats + , toHie rhs + ] + where scope = combineScopes patsScope rhsScope + patsScope = mkScope (loc pats) + rhsScope = mkScope (loc rhs) + +instance ToHie (Located (InjectivityAnn GhcRn)) where + toHie (L span ann) = concatM $ makeNode ann span : case ann of + InjectivityAnn _ lhs rhs -> + [ toHie $ C Use lhs + , toHie $ map (C Use) rhs + ] + +instance ToHie (HsDataDefn GhcRn) where + toHie (HsDataDefn _ _ ctx _ mkind cons derivs) = concatM + [ toHie ctx + , toHie mkind + , toHie cons + , toHie derivs + ] + +instance ToHie (Located [Located (HsDerivingClause GhcRn)]) where + toHie (L span clauses) = concatM + [ locOnly span + , toHie clauses + ] + +instance ToHie (Located (HsDerivingClause GhcRn)) where + toHie (L span cl) = concatM $ makeNode cl span : case cl of + HsDerivingClause _ strat dct -> + [ toHie strat + , toHie dct + ] + +instance ToHie (LocatedC (DerivClauseTys GhcRn)) where + toHie (L span dct) = concatM $ makeNodeA dct span : case dct of + DctSingle _ ty -> [ toHie $ TS (ResolvedScopes []) ty ] + DctMulti _ tys -> [ toHie $ map (TS (ResolvedScopes [])) tys ] + +instance ToHie (Located (DerivStrategy GhcRn)) where + toHie (L span strat) = concatM $ makeNode strat span : case strat of + StockStrategy _ -> [] + AnyclassStrategy _ -> [] + NewtypeStrategy _ -> [] + ViaStrategy s -> [ toHie (TS (ResolvedScopes []) s) ] + +instance ToHie (LocatedP OverlapMode) where + toHie (L span _) = locOnly (locA span) + +instance ToHie a => ToHie (HsScaled GhcRn a) where + toHie (HsScaled w t) = concatM [toHie (arrowToHsType w), toHie t] + +instance ToHie (LocatedA (ConDecl GhcRn)) where + toHie (L span decl) = concatM $ makeNode decl (locA span) : case decl of + ConDeclGADT { con_names = names, con_bndrs = L outer_bndrs_loc outer_bndrs + , con_mb_cxt = ctx, con_g_args = args, con_res_ty = typ } -> + [ toHie $ map (C (Decl ConDec $ getRealSpanA span)) names + , case outer_bndrs of + HsOuterImplicit{hso_ximplicit = imp_vars} -> + bindingsOnly $ map (C $ TyVarBind (mkScopeA outer_bndrs_loc) resScope) + imp_vars + HsOuterExplicit{hso_bndrs = exp_bndrs} -> + toHie $ tvScopes resScope NoScope exp_bndrs + , toHie ctx + , toHie args + , toHie typ + ] + where + rhsScope = combineScopes argsScope tyScope + ctxScope = maybe NoScope mkLScopeA ctx + argsScope = case args of + PrefixConGADT xs -> scaled_args_scope xs + RecConGADT x -> mkLScopeA x + tyScope = mkLScopeA typ + resScope = ResolvedScopes [ctxScope, rhsScope] + ConDeclH98 { con_name = name, con_ex_tvs = qvars + , con_mb_cxt = ctx, con_args = dets } -> + [ toHie $ C (Decl ConDec $ getRealSpan (locA span)) name + , toHie $ tvScopes (ResolvedScopes []) rhsScope qvars + , toHie ctx + , toHie dets + ] + where + rhsScope = combineScopes ctxScope argsScope + ctxScope = maybe NoScope mkLScopeA ctx + argsScope = case dets of + PrefixCon _ xs -> scaled_args_scope xs + InfixCon a b -> scaled_args_scope [a, b] + RecCon x -> mkLScopeA x + where scaled_args_scope :: [HsScaled GhcRn (LHsType GhcRn)] -> Scope + scaled_args_scope = foldr combineScopes NoScope . map (mkLScopeA . hsScaledThing) + +instance ToHie (LocatedL [LocatedA (ConDeclField GhcRn)]) where + toHie (L span decls) = concatM $ + [ locOnly (locA span) + , toHie decls + ] + +instance ToHie (TScoped (HsWildCardBndrs GhcRn (LocatedA (HsSigType GhcRn)))) where + toHie (TS sc (HsWC names a)) = concatM $ + [ bindingsOnly $ map (C $ TyVarBind (mkScope span) sc) names + , toHie $ TS sc a + ] + where span = loc a + +instance ToHie (TScoped (HsWildCardBndrs GhcRn (LocatedA (HsType GhcRn)))) where + toHie (TS sc (HsWC names a)) = concatM $ + [ bindingsOnly $ map (C $ TyVarBind (mkScope span) sc) names + , toHie a + ] + where span = loc a + +instance ToHie (LocatedA (StandaloneKindSig GhcRn)) where + toHie (L sp sig) = concatM [makeNodeA sig sp, toHie sig] + +instance ToHie (StandaloneKindSig GhcRn) where + toHie sig = concatM $ case sig of + StandaloneKindSig _ name typ -> + [ toHie $ C TyDecl name + , toHie $ TS (ResolvedScopes []) typ + ] + +instance HiePass p => ToHie (SigContext (LocatedA (Sig (GhcPass p)))) where + toHie (SC (SI styp msp) (L sp sig)) = + case hiePass @p of + HieTc -> pure [] + HieRn -> concatM $ makeNodeA sig sp : case sig of + TypeSig _ names typ -> + [ toHie $ map (C TyDecl) names + , toHie $ TS (UnresolvedScope (map unLoc names) Nothing) typ + ] + PatSynSig _ names typ -> + [ toHie $ map (C TyDecl) names + , toHie $ TS (UnresolvedScope (map unLoc names) Nothing) typ + ] + ClassOpSig _ _ names typ -> + [ case styp of + ClassSig -> toHie $ map (C $ ClassTyDecl $ getRealSpanA sp) names + _ -> toHie $ map (C $ TyDecl) names + , toHie $ TS (UnresolvedScope (map unLoc names) msp) typ + ] + IdSig _ _ -> [] + FixSig _ fsig -> + [ toHie $ L sp fsig + ] + InlineSig _ name _ -> + [ toHie $ (C Use) name + ] + SpecSig _ name typs _ -> + [ toHie $ (C Use) name + , toHie $ map (TS (ResolvedScopes [])) typs + ] + SpecInstSig _ _ typ -> + [ toHie $ TS (ResolvedScopes []) typ + ] + MinimalSig _ _ form -> + [ toHie form + ] + SCCFunSig _ _ name mtxt -> + [ toHie $ (C Use) name + , maybe (pure []) (locOnly . getLoc) mtxt + ] + CompleteMatchSig _ _ (L ispan names) typ -> + [ locOnly ispan + , toHie $ map (C Use) names + , toHie $ fmap (C Use) typ + ] + +instance ToHie (TScoped (LocatedA (HsSigType GhcRn))) where + toHie (TS tsc (L span t@HsSig{sig_bndrs=bndrs,sig_body=body})) = concatM $ makeNodeA t span : + [ toHie (TVS tsc (mkScopeA span) bndrs) + , toHie body + ] + +-- Check this +instance Data flag => ToHie (TVScoped (HsOuterTyVarBndrs flag GhcRn)) where + toHie (TVS tsc sc bndrs) = case bndrs of + HsOuterImplicit xs -> bindingsOnly $ map (C $ TyVarBind sc tsc) xs + HsOuterExplicit _ xs -> toHie $ tvScopes tsc sc xs + +instance ToHie (LocatedA (HsType GhcRn)) where + toHie (L span t) = concatM $ makeNode t (locA span) : case t of + HsForAllTy _ tele body -> + let scope = mkScope $ getLocA body in + [ case tele of + HsForAllVis { hsf_vis_bndrs = bndrs } -> + toHie $ tvScopes (ResolvedScopes []) scope bndrs + HsForAllInvis { hsf_invis_bndrs = bndrs } -> + toHie $ tvScopes (ResolvedScopes []) scope bndrs + , toHie body + ] + HsQualTy _ ctx body -> + [ toHie ctx + , toHie body + ] + HsTyVar _ _ var -> + [ toHie $ C Use var + ] + HsAppTy _ a b -> + [ toHie a + , toHie b + ] + HsAppKindTy _ ty ki -> + [ toHie ty + , toHie ki + ] + HsFunTy _ w a b -> + [ toHie (arrowToHsType w) + , toHie a + , toHie b + ] + HsListTy _ a -> + [ toHie a + ] + HsTupleTy _ _ tys -> + [ toHie tys + ] + HsSumTy _ tys -> + [ toHie tys + ] + HsOpTy _ a op b -> + [ toHie a + , toHie $ C Use op + , toHie b + ] + HsParTy _ a -> + [ toHie a + ] + HsIParamTy _ ip ty -> + [ toHie ip + , toHie ty + ] + HsKindSig _ a b -> + [ toHie a + , toHie b + ] + HsSpliceTy _ a -> + [ toHie $ L span a + ] + HsDocTy _ a _ -> + [ toHie a + ] + HsBangTy _ _ ty -> + [ toHie ty + ] + HsRecTy _ fields -> + [ toHie fields + ] + HsExplicitListTy _ _ tys -> + [ toHie tys + ] + HsExplicitTupleTy _ tys -> + [ toHie tys + ] + HsTyLit _ _ -> [] + HsWildCardTy _ -> [] + HsStarTy _ _ -> [] + XHsType _ -> [] + +instance (ToHie tm, ToHie ty) => ToHie (HsArg tm ty) where + toHie (HsValArg tm) = toHie tm + toHie (HsTypeArg _ ty) = toHie ty + toHie (HsArgPar sp) = locOnly sp + +instance Data flag => ToHie (TVScoped (LocatedA (HsTyVarBndr flag GhcRn))) where + toHie (TVS tsc sc (L span bndr)) = concatM $ makeNodeA bndr span : case bndr of + UserTyVar _ _ var -> + [ toHie $ C (TyVarBind sc tsc) var + ] + KindedTyVar _ _ var kind -> + [ toHie $ C (TyVarBind sc tsc) var + , toHie kind + ] + +instance ToHie (TScoped (LHsQTyVars GhcRn)) where + toHie (TS sc (HsQTvs implicits vars)) = concatM $ + [ bindingsOnly bindings + , toHie $ tvScopes sc NoScope vars + ] + where + varLoc = loc vars + bindings = map (C $ TyVarBind (mkScope varLoc) sc) implicits + +instance ToHie (LocatedC [LocatedA (HsType GhcRn)]) where + toHie (L span tys) = concatM $ + [ locOnly (locA span) + , toHie tys + ] + +instance ToHie (LocatedA (ConDeclField GhcRn)) where + toHie (L span field) = concatM $ makeNode field (locA span) : case field of + ConDeclField _ fields typ _ -> + [ toHie $ map (RFC RecFieldDecl (getRealSpan $ loc typ)) fields + , toHie typ + ] + +instance ToHie (LHsExpr a) => ToHie (ArithSeqInfo a) where + toHie (From expr) = toHie expr + toHie (FromThen a b) = concatM $ + [ toHie a + , toHie b + ] + toHie (FromTo a b) = concatM $ + [ toHie a + , toHie b + ] + toHie (FromThenTo a b c) = concatM $ + [ toHie a + , toHie b + , toHie c + ] + +instance ToHie (LocatedA (SpliceDecl GhcRn)) where + toHie (L span decl) = concatM $ makeNodeA decl span : case decl of + SpliceDecl _ splice _ -> + [ toHie splice + ] + +instance ToHie (HsBracket a) where + toHie _ = pure [] + +instance ToHie PendingRnSplice where + toHie _ = pure [] + +instance ToHie PendingTcSplice where + toHie _ = pure [] + +instance ToHie (LBooleanFormula (LocatedN Name)) where + toHie (L span form) = concatM $ makeNode form (locA span) : case form of + Var a -> + [ toHie $ C Use a + ] + And forms -> + [ toHie forms + ] + Or forms -> + [ toHie forms + ] + Parens f -> + [ toHie f + ] + +instance ToHie (Located HsIPName) where + toHie (L span e) = makeNode e span + +instance HiePass p => ToHie (LocatedA (HsSplice (GhcPass p))) where + toHie (L span sp) = concatM $ makeNodeA sp span : case sp of + HsTypedSplice _ _ _ expr -> + [ toHie expr + ] + HsUntypedSplice _ _ _ expr -> + [ toHie expr + ] + HsQuasiQuote _ _ _ ispan _ -> + [ locOnly ispan + ] + HsSpliced _ _ _ -> + [] + XSplice x -> case ghcPass @p of +-- CHANGED: removed preprocessor stuff +-- #if __GLASGOW_HASKELL__ < 811 +-- GhcPs -> noExtCon x +-- GhcRn -> noExtCon x +-- #endif + GhcTc -> case x of + HsSplicedT _ -> [] + +instance ToHie (LocatedA (RoleAnnotDecl GhcRn)) where + toHie (L span annot) = concatM $ makeNodeA annot span : case annot of + RoleAnnotDecl _ var roles -> + [ toHie $ C Use var + , concatMapM (locOnly . getLoc) roles + ] + +instance ToHie (LocatedA (InstDecl GhcRn)) where + toHie (L span decl) = concatM $ makeNodeA decl span : case decl of + ClsInstD _ d -> + [ toHie $ L span d + ] + DataFamInstD _ d -> + [ toHie $ L span d + ] + TyFamInstD _ d -> + [ toHie $ L span d + ] + +instance ToHie (LocatedA (ClsInstDecl GhcRn)) where + toHie (L span decl) = concatM + [ toHie $ TS (ResolvedScopes [mkScopeA span]) $ cid_poly_ty decl + , toHie $ fmap (BC InstanceBind ModuleScope) $ cid_binds decl + , toHie $ map (SC $ SI InstSig $ getRealSpanA span) $ cid_sigs decl + , concatMapM (locOnly . getLocA) $ cid_tyfam_insts decl + , toHie $ cid_tyfam_insts decl + , concatMapM (locOnly . getLocA) $ cid_datafam_insts decl + , toHie $ cid_datafam_insts decl + , toHie $ cid_overlap_mode decl + ] + +instance ToHie (LocatedA (DataFamInstDecl GhcRn)) where + toHie (L sp (DataFamInstDecl d)) = toHie $ TS (ResolvedScopes [mkScopeA sp]) d + +instance ToHie (LocatedA (TyFamInstDecl GhcRn)) where + toHie (L sp (TyFamInstDecl _ d)) = toHie $ TS (ResolvedScopes [mkScopeA sp]) d + +instance HiePass p => ToHie (Context (FieldOcc (GhcPass p))) where + toHie (C c (FieldOcc n (L l _))) = case hiePass @p of + HieTc -> toHie (C c (L l n)) + HieRn -> toHie (C c (L l n)) + +instance HiePass p => ToHie (PatSynFieldContext (RecordPatSynField (GhcPass p))) where + toHie (PSC sp (RecordPatSynField a b)) = concatM $ + [ toHie $ C (RecField RecFieldDecl sp) a + , toHie $ C Use b + ] + +instance ToHie (LocatedA (DerivDecl GhcRn)) where + toHie (L span decl) = concatM $ makeNodeA decl span : case decl of + DerivDecl _ typ strat overlap -> + [ toHie $ TS (ResolvedScopes []) typ + , toHie strat + , toHie overlap + ] + +instance ToHie (LocatedA (FixitySig GhcRn)) where + toHie (L span sig) = concatM $ makeNodeA sig span : case sig of + FixitySig _ vars _ -> + [ toHie $ map (C Use) vars + ] + +instance ToHie (LocatedA (DefaultDecl GhcRn)) where + toHie (L span decl) = concatM $ makeNodeA decl span : case decl of + DefaultDecl _ typs -> + [ toHie typs + ] + +instance ToHie (LocatedA (ForeignDecl GhcRn)) where + toHie (L span decl) = concatM $ makeNodeA decl span : case decl of + ForeignImport {fd_name = name, fd_sig_ty = sig, fd_fi = fi} -> + [ toHie $ C (ValBind RegularBind ModuleScope $ getRealSpanA span) name + , toHie $ TS (ResolvedScopes []) sig + , toHie fi + ] + ForeignExport {fd_name = name, fd_sig_ty = sig, fd_fe = fe} -> + [ toHie $ C Use name + , toHie $ TS (ResolvedScopes []) sig + , toHie fe + ] + +instance ToHie ForeignImport where + toHie (CImport (L a _) (L b _) _ _ (L c _)) = concatM $ + [ locOnly a + , locOnly b + , locOnly c + ] + +instance ToHie ForeignExport where + toHie (CExport (L a _) (L b _)) = concatM $ + [ locOnly a + , locOnly b + ] + +instance ToHie (LocatedA (WarnDecls GhcRn)) where + toHie (L span decl) = concatM $ makeNodeA decl span : case decl of + Warnings _ _ warnings -> + [ toHie warnings + ] + +instance ToHie (LocatedA (WarnDecl GhcRn)) where + toHie (L span decl) = concatM $ makeNode decl (locA span) : case decl of + Warning _ vars _ -> + [ toHie $ map (C Use) vars + ] + +instance ToHie (LocatedA (AnnDecl GhcRn)) where + toHie (L span decl) = concatM $ makeNodeA decl span : case decl of + HsAnnotation _ _ prov expr -> + [ toHie prov + , toHie expr + ] + +instance ToHie (AnnProvenance GhcRn) where + toHie (ValueAnnProvenance a) = toHie $ C Use a + toHie (TypeAnnProvenance a) = toHie $ C Use a + toHie ModuleAnnProvenance = pure [] + +instance ToHie (LocatedA (RuleDecls GhcRn)) where + toHie (L span decl) = concatM $ makeNodeA decl span : case decl of + HsRules _ _ rules -> + [ toHie rules + ] + +instance ToHie (LocatedA (RuleDecl GhcRn)) where + toHie (L span r@(HsRule _ rname _ tybndrs bndrs exprA exprB)) = concatM + [ makeNodeA r span + , locOnly $ getLoc rname + , toHie $ fmap (tvScopes (ResolvedScopes []) scope) tybndrs + , toHie $ map (RS $ mkScope (locA span)) bndrs + , toHie exprA + , toHie exprB + ] + where scope = bndrs_sc `combineScopes` exprA_sc `combineScopes` exprB_sc + bndrs_sc = maybe NoScope mkLScope (listToMaybe bndrs) + exprA_sc = mkLScopeA exprA + exprB_sc = mkLScopeA exprB + +instance ToHie (RScoped (Located (RuleBndr GhcRn))) where + toHie (RS sc (L span bndr)) = concatM $ makeNode bndr span : case bndr of + RuleBndr _ var -> + [ toHie $ C (ValBind RegularBind sc Nothing) var + ] + RuleBndrSig _ var typ -> + [ toHie $ C (ValBind RegularBind sc Nothing) var + , toHie $ TS (ResolvedScopes [sc]) typ + ] + +instance ToHie (LocatedA (ImportDecl GhcRn)) where + toHie (L span decl) = concatM $ makeNode decl (locA span) : case decl of + ImportDecl { ideclName = name, ideclAs = as, ideclHiding = hidden } -> + [ toHie $ IEC Import name + , toHie $ fmap (IEC ImportAs) as + , maybe (pure []) goIE hidden + ] + where + goIE (hiding, (L sp liens)) = concatM $ + [ locOnly (locA sp) + , toHie $ map (IEC c) liens + ] + where + c = if hiding then ImportHiding else Import + +instance ToHie (IEContext (LocatedA (IE GhcRn))) where + toHie (IEC c (L span ie)) = concatM $ makeNode ie (locA span) : case ie of + IEVar _ n -> + [ toHie $ IEC c n + ] + IEThingAbs _ n -> + [ toHie $ IEC c n + ] + IEThingAll _ n -> + [ toHie $ IEC c n + ] + IEThingWith flds n _ ns -> + [ toHie $ IEC c n + , toHie $ map (IEC c) ns + , toHie $ map (IEC c) flds + ] + IEModuleContents _ n -> + [ toHie $ IEC c n + ] + IEGroup _ _ _ -> [] + IEDoc _ _ -> [] + IEDocNamed _ _ -> [] + +instance ToHie (IEContext (LIEWrappedName Name)) where + toHie (IEC c (L span iewn)) = concatM $ makeNodeA iewn span : case iewn of + IEName n -> + [ toHie $ C (IEThing c) n + ] + IEPattern _ p -> + [ toHie $ C (IEThing c) p + ] + IEType _ n -> + [ toHie $ C (IEThing c) n + ] + +instance ToHie (IEContext (Located FieldLabel)) where + toHie (IEC c (L span lbl)) = concatM + [ makeNode lbl span + , toHie $ C (IEThing c) $ L span (flSelector lbl) + ] From 41b1085df5887e6ed1fa3be4da2df30fb407a5d3 Mon Sep 17 00:00:00 2001 From: Colten Webb <8738145+coltenwebb@users.noreply.github.com> Date: Wed, 20 Jul 2022 07:03:05 -0400 Subject: [PATCH 029/213] fix test (#3051) --- ghcide/test/exe/Main.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 710fdd576e..24669ad7a5 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -4359,7 +4359,7 @@ findDefinitionAndHoverTests = let , tst (getTypeDefinitions, checkDefs) aL20 sourceFilePath (pure [ExpectNoDefinitions]) "Polymorphic variable"] recordDotSyntaxTests - | ghcVersion == GHC92 = + | ghcVersion >= GHC92 = [ tst (getHover, checkHover) (Position 19 24) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["x :: MyRecord"]]) "hover over parent" , tst (getHover, checkHover) (Position 19 25) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["_ :: MyChild"]]) "hover over dot shows child" , tst (getHover, checkHover) (Position 19 26) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["_ :: MyChild"]]) "hover over child" From 100b53a4744c21501c925d8a9261c38b840f69be Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Wed, 20 Jul 2022 18:59:54 +0200 Subject: [PATCH 030/213] build(nix): ghc922 -> ghc923 (#3049) * build(nix): ghc922 -> ghc923 bumped nixpkgs and add support for ghc923 (replacing ghc922) * fixes * fixing for ghc90? Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Michael Peyton Jones --- cabal.project | 1 + configuration-ghc-90.nix | 7 +++---- configuration-ghc-92.nix | 2 ++ flake.lock | 42 ++++++++++++++++++++-------------------- flake.nix | 16 +++++++-------- 5 files changed, 35 insertions(+), 33 deletions(-) diff --git a/cabal.project b/cabal.project index 62dc214f28..f87c79e86a 100644 --- a/cabal.project +++ b/cabal.project @@ -67,4 +67,5 @@ allow-newer: ---------- hiedb:base, + ekg-core, ekg-wai:time diff --git a/configuration-ghc-90.nix b/configuration-ghc-90.nix index 2a19f397aa..7c16281847 100644 --- a/configuration-ghc-90.nix +++ b/configuration-ghc-90.nix @@ -19,13 +19,12 @@ let ptr-poker = hself.callCabal2nix "ptr-poker" inputs.ptr-poker { }; ghc-lib = hself.ghc-lib_9_2_2_20220307; - ghc-lib-parser = hself.ghc-lib-parser_9_2_2_20220307; - ghc-lib-parser-ex = hself.ghc-lib-parser-ex_9_2_0_3; + ghc-lib-parser = hself.ghc-lib-parser_9_2_3_20220709; + ghc-lib-parser-ex = hself.ghc-lib-parser-ex_9_2_0_4; Cabal = hself.Cabal_3_6_3_0; - ormolu = hself.ormolu_0_4_0_0; + ormolu = hself.ormolu_0_5_0_0; fourmolu = hself.fourmolu_0_6_0_0; - # Hlint is still broken hlint = doJailbreak (hself.callCabal2nix "hlint" inputs.hlint-34 { }); diff --git a/configuration-ghc-92.nix b/configuration-ghc-92.nix index 1577a7a2cf..3790e182a7 100644 --- a/configuration-ghc-92.nix +++ b/configuration-ghc-92.nix @@ -29,6 +29,8 @@ let # Hlint is still broken hlint = doJailbreak (hself.callCabal2nix "hlint" inputs.hlint { }); + stylish-haskell = appendConfigureFlag hsuper.stylish-haskell "-fghc-lib"; + # Re-generate HLS drv excluding some plugins haskell-language-server = hself.callCabal2nixWithOptions "haskell-language-server" ./. diff --git a/flake.lock b/flake.lock index 28ca48f622..de1c8b39ca 100644 --- a/flake.lock +++ b/flake.lock @@ -54,11 +54,11 @@ }, "flake-utils": { "locked": { - "lastModified": 1653893745, - "narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=", + "lastModified": 1656928814, + "narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=", "owner": "numtide", "repo": "flake-utils", - "rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1", + "rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249", "type": "github" }, "original": { @@ -69,11 +69,11 @@ }, "flake-utils_2": { "locked": { - "lastModified": 1653893745, - "narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=", + "lastModified": 1656928814, + "narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=", "owner": "numtide", "repo": "flake-utils", - "rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1", + "rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249", "type": "github" }, "original": { @@ -249,11 +249,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1650049622, - "narHash": "sha256-fh/xYFctwzuowIWsVdtXUVtil4pxk54GabGY/sNKU30=", - "owner": "nixos", + "lastModified": 1657849727, + "narHash": "sha256-68J4eSwzr98r7VCzgrX/WWaQzkY7gdKqH2uSyQheYj0=", + "owner": "NixOS", "repo": "nixpkgs", - "rev": "92d1f76186bb974dd90971d7619f8bf0bff23a73", + "rev": "0b683abff06fe55755ea992ba47f2e787081a30f", "type": "github" }, "original": { @@ -265,11 +265,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1654085299, - "narHash": "sha256-sR1pefD3cvNvYIcGTUXvUMe9UesEkSKtjGN1H/TBXsQ=", + "lastModified": 1657888067, + "narHash": "sha256-GnwJoFBTPfW3+mz7QEeJEEQ9OMHZOiIJ/qDhZxrlKh8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3b4bdfbe6f19ca497d999ad08aeb668839858a18", + "rev": "65fae659e31098ca4ac825a6fef26d890aaf3f4e", "type": "github" }, "original": { @@ -284,11 +284,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1653561148, - "narHash": "sha256-JzAttqACdvMOTwkzkJ0jFF8MWIo8Uau4w/XUMyqpnd8=", + "lastModified": 1657626303, + "narHash": "sha256-O/JJ0hSBCmlx0oP8QGAlRrWn0BvlC5cj7/EZ0CCWHTU=", "owner": "nix-community", "repo": "poetry2nix", - "rev": "3b01c3e3dc57d511848d8433153ab67db79640e1", + "rev": "920ba682377d5c0d87945c5eb6141ab8447ca509", "type": "github" }, "original": { @@ -347,7 +347,7 @@ "ptr-poker": "ptr-poker", "retrie": "retrie", "sphinx_rtd_theme": "sphinx_rtd_theme", - "stylish-haskell-01220": "stylish-haskell-01220" + "stylish-haskell": "stylish-haskell" } }, "sphinx_rtd_theme": { @@ -367,16 +367,16 @@ "type": "github" } }, - "stylish-haskell-01220": { + "stylish-haskell": { "flake": false, "locked": { - "narHash": "sha256-uQIvhz/xRbKLHe9et+tHUVE9To5vt1Pz3+vvDEqJaLI=", + "narHash": "sha256-oZSR5UQ+ieMFRq7MvL0sJqOblMk74awvcepuT+JHYtA=", "type": "tarball", - "url": "https://hackage.haskell.org/package/stylish-haskell-0.12.2.0/stylish-haskell-0.12.2.0.tar.gz" + "url": "https://hackage.haskell.org/package/stylish-haskell-0.14.2.0/stylish-haskell-0.14.2.0.tar.gz" }, "original": { "type": "tarball", - "url": "https://hackage.haskell.org/package/stylish-haskell-0.12.2.0/stylish-haskell-0.12.2.0.tar.gz" + "url": "https://hackage.haskell.org/package/stylish-haskell-0.14.2.0/stylish-haskell-0.14.2.0.tar.gz" } } }, diff --git a/flake.nix b/flake.nix index bcd4e28dba..c14a40bb49 100644 --- a/flake.nix +++ b/flake.nix @@ -76,8 +76,8 @@ url = "https://hackage.haskell.org/package/ptr-poker-0.1.2.8/ptr-poker-0.1.2.8.tar.gz"; flake = false; }; - stylish-haskell-01220 = { - url = "https://hackage.haskell.org/package/stylish-haskell-0.12.2.0/stylish-haskell-0.12.2.0.tar.gz"; + stylish-haskell = { + url = "https://hackage.haskell.org/package/stylish-haskell-0.14.2.0/stylish-haskell-0.14.2.0.tar.gz"; flake = false; }; implicit-hie-cradle = { @@ -213,7 +213,7 @@ }; ghc902Config = (import ./configuration-ghc-90.nix) { inherit pkgs inputs; }; - ghc922Config = (import ./configuration-ghc-92.nix) { inherit pkgs inputs; }; + ghc923Config = (import ./configuration-ghc-92.nix) { inherit pkgs inputs; }; # GHC versions # While HLS still works fine with 8.10 GHCs, we only support the versions that are cached @@ -222,12 +222,12 @@ ghcVersion = "ghc" + (pkgs.lib.replaceStrings ["."] [""] pkgs.haskellPackages.ghc.version); cases = { ghc902 = ghc902Config.tweakHpkgs (pkgs.hlsHpkgs "ghc902"); - ghc922 = ghc922Config.tweakHpkgs (pkgs.hlsHpkgs "ghc922"); + ghc923 = ghc923Config.tweakHpkgs (pkgs.hlsHpkgs "ghc923"); }; in { default = cases."${ghcVersion}"; } // cases; ghc902 = supportedGHCs.ghc902; - ghc922 = supportedGHCs.ghc922; + ghc923 = supportedGHCs.ghc923; ghcDefault = supportedGHCs.default; # For markdown support @@ -359,20 +359,20 @@ simpleDevShells = { haskell-language-server-dev = mkDevShell ghcDefault "cabal.project"; haskell-language-server-902-dev = mkDevShell ghc902 "cabal.project"; - haskell-language-server-922-dev = mkDevShell ghc922 "cabal.project"; + haskell-language-server-923-dev = mkDevShell ghc923 "cabal.project"; }; # Developement shell, haskell packages are also provided by nix nixDevShells = { haskell-language-server-dev-nix = mkDevShellWithNixDeps ghcDefault "cabal.project"; haskell-language-server-902-dev-nix = mkDevShellWithNixDeps ghc902 "cabal.project"; - haskell-language-server-922-dev-nix = mkDevShellWithNixDeps ghc922 "cabal.project"; + haskell-language-server-923-dev-nix = mkDevShellWithNixDeps ghc923 "cabal.project"; }; allPackages = { haskell-language-server = mkExe ghcDefault; haskell-language-server-902 = mkExe ghc902; - haskell-language-server-922 = mkExe ghc922; + haskell-language-server-923 = mkExe ghc923; }; devShells = simpleDevShells // nixDevShells // { From c045857ecbb00c0c0757e3e3ef5fefcd3a4dac99 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Tue, 28 Jun 2022 14:01:30 +0530 Subject: [PATCH 031/213] Don't use typecheck rule for non FOIs in refine imports plugin Also add an assertion to check that we never use non-FOI rules Fixes #2962 --- ghcide/src/Development/IDE/Core/Rules.hs | 15 +++++++++++++++ .../src/Ide/Plugin/RefineImports.hs | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ghcide/src/Development/IDE/Core/Rules.hs b/ghcide/src/Development/IDE/Core/Rules.hs index b02a58dd9f..818aaea3a3 100644 --- a/ghcide/src/Development/IDE/Core/Rules.hs +++ b/ghcide/src/Development/IDE/Core/Rules.hs @@ -165,6 +165,7 @@ data Log | LogLoadingHieFileFail !FilePath !SomeException | LogLoadingHieFileSuccess !FilePath | LogExactPrint ExactPrint.Log + | LogTypecheckedFOI !NormalizedFilePath deriving Show instance Pretty Log where @@ -182,6 +183,14 @@ instance Pretty Log where LogLoadingHieFileSuccess path -> "SUCCEEDED LOADING HIE FILE FOR" <+> pretty path LogExactPrint log -> pretty log + LogTypecheckedFOI path -> vcat + [ "WARNING: Typechecked a file which is not currently open in the editor:" <+> pretty (fromNormalizedFilePath 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" + <+> "the HLS version being used, the plugins enabled, and if possible the codebase and file which" + <+> "triggered this warning." + ] templateHaskellInstructions :: T.Text templateHaskellInstructions = "https://haskell-language-server.readthedocs.io/en/latest/troubleshooting.html#static-binaries" @@ -650,6 +659,12 @@ typeCheckRule :: Recorder (WithPriority Log) -> Rules () typeCheckRule recorder = define (cmapWithPrio LogShake recorder) $ \TypeCheck file -> do pm <- use_ GetParsedModule file hsc <- hscEnv <$> use_ GhcSessionDeps file + foi <- use_ IsFileOfInterest file + -- We should only call the typecheck rule for files of interest. + -- Keeping typechecked modules in memory for other files is + -- very expensive. + when (foi == NotFOI) $ + logWith recorder Logger.Warning $ LogTypecheckedFOI file typeCheckRuleDefinition hsc pm knownFilesRule :: Recorder (WithPriority Log) -> Rules () diff --git a/plugins/hls-refine-imports-plugin/src/Ide/Plugin/RefineImports.hs b/plugins/hls-refine-imports-plugin/src/Ide/Plugin/RefineImports.hs index 582fba0a72..d4a8076484 100644 --- a/plugins/hls-refine-imports-plugin/src/Ide/Plugin/RefineImports.hs +++ b/plugins/hls-refine-imports-plugin/src/Ide/Plugin/RefineImports.hs @@ -187,8 +187,8 @@ refineImportsRule recorder = define (cmapWithPrio LogShake recorder) $ \RefineIm -- second layer is from the imports of first layer to their imports ImportMap importIm <- use_ GetImportMap path forM importIm $ \imp_path -> do - imp_tmr <- use_ TypeCheck imp_path - return $ tcg_exports $ tmrTypechecked imp_tmr + imp_hir <- use_ GetModIface imp_path + return $ mi_exports $ hirModIface imp_hir -- Use the GHC api to extract the "minimal" imports -- We shouldn't blindly refine imports From 83ca1256feaa3d08b7047dd24c4fd8ca72c86f16 Mon Sep 17 00:00:00 2001 From: tensorknower69 Date: Fri, 22 Jul 2022 16:06:15 +0800 Subject: [PATCH 032/213] fix a typo in src/Ide/Plugin/Class/CodeLens.hs (#3053) --- plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeLens.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeLens.hs b/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeLens.hs index 32e9e349e8..5ea74fe780 100644 --- a/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeLens.hs +++ b/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeLens.hs @@ -27,7 +27,7 @@ codeLens state plId CodeLensParams{..} = do enabled <- enableTypeLens <$> getCompletionsConfig plId if not enabled then pure $ pure $ List [] else pluginResponse $ do nfp <- getNormalizedFilePath plId uri - tmr <- handleMaybeM "Unable to typecheak" + tmr <- handleMaybeM "Unable to typecheck" $ liftIO $ runAction "classplugin.TypeCheck" state $ use TypeCheck nfp From e6dfa4e9c437a02452fc52130ba1e4d007e4fd64 Mon Sep 17 00:00:00 2001 From: Michael Peyton Jones Date: Fri, 22 Jul 2022 23:47:14 +0100 Subject: [PATCH 033/213] Remove redundant WARNING prefix (#3055) --- ghcide/src/Development/IDE/Core/Rules.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Core/Rules.hs b/ghcide/src/Development/IDE/Core/Rules.hs index 818aaea3a3..b82d928fa0 100644 --- a/ghcide/src/Development/IDE/Core/Rules.hs +++ b/ghcide/src/Development/IDE/Core/Rules.hs @@ -184,7 +184,7 @@ instance Pretty Log where "SUCCEEDED LOADING HIE FILE FOR" <+> pretty path LogExactPrint log -> pretty log LogTypecheckedFOI path -> vcat - [ "WARNING: 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 (fromNormalizedFilePath 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" From 8ccb3f3e5db6589dc32ce7028cb1e3b33b967a6f Mon Sep 17 00:00:00 2001 From: arsenkhy <77200251+arsenkhy@users.noreply.github.com> Date: Tue, 26 Jul 2022 03:47:34 +0600 Subject: [PATCH 034/213] Fix grammar and spelling errors in configuration.md (#3056) * Fix grammar and spelling errors in configuration.md - the way in which -> how - which any -> that any - perfomance -> performance - diganostics -> diagnostics * Apply suggestion - how -> the way in which --- docs/configuration.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index b1ca00d8f4..73ab3f39fd 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -18,7 +18,7 @@ The LSP protocol is designed to support many useful server configuration options These are sent to the server by the client, and can be controlled without reference to a specific language. For example, there are protocol methods for highlighting matching identifiers throughout a document. -This is a capability which any server can implement, so the client can decide generically whether to ask the server to do it or not. +This is a capability that any server can implement, so the client can decide generically whether to ask the server to do it or not. So your editor can provide a setting to turn this on or off globally, for any language server you might use. Settings like this are typically provided by the generic LSP client support for your editor, for example in Emacs by [lsp-mode](https://github.com/emacs-lsp/lsp-mode). @@ -42,7 +42,7 @@ Here is a list of the additional settings currently supported by `haskell-langua - Formatting provider (`haskell.formattingProvider`, default `ormolu`): what formatter to use; one of `floskell`, `ormolu`, `fourmolu`, `stylish-haskell`, or `brittany` (if compiled with the brittany plugin). - Max completions (`haskell.maxCompletions`, default 40): maximum number of completions sent to the LSP client. -- Check project (`haskell.checkProject`, default true): whether to typecheck the entire project on load. As it is activated by default could drive to bad perfomance in large projects. +- Check project (`haskell.checkProject`, default true): whether to typecheck the entire project on load. As it is activated by default could drive to bad performance in large projects. - Check parents (`haskell.checkParents`, default `CheckOnSaveAndClose`): when to typecheck reverse dependencies of a file; one of `NeverCheck`, `CheckOnClose`, `CheckOnSaveAndClose`, or `AlwaysCheck`. #### Generic plugin configuration @@ -72,7 +72,7 @@ Plugins have a generic config to control their behaviour. The schema of such con - `haskell.plugin.ghcide-completions.config.snippetsOn`, default true: Inserts snippets when using code completions. - `haskell.plugin.ghcide-completions.config.autoExtendOn`, default true: Extends the import list automatically when completing a out-of-scope identifier. - `ghcide-type-lenses`: - - `haskell.plugin.ghcide-type-lenses.config.mode`, default `always`: Control how type lenses are shown. One of `always`, `exported`, `diganostics`. + - `haskell.plugin.ghcide-type-lenses.config.mode`, default `always`: Control how type lenses are shown. One of `always`, `exported`, `diagnostics`. - `hlint`: - `haskell.plugin.hlint.config.flags`, default empty: List of flags used by hlint. This reference of configuration can be outdated at any time but we can query the `haskell-server-executable` about what configuration is effectively used: From 078fc14dda2534e5c107af5d821d2ac6f83e4c7c Mon Sep 17 00:00:00 2001 From: Gregory Gerasev Date: Thu, 28 Jul 2022 02:11:31 +0300 Subject: [PATCH 035/213] Stan integration #258 (#2908) --- .github/workflows/hackage.yml | 2 +- .github/workflows/test.yml | 4 + CODEOWNERS | 1 + cabal.project | 1 + docs/features.md | 6 + docs/supported-versions.md | 1 + exe/Plugins.hs | 8 + ghcide/src/Development/IDE/Core/RuleTypes.hs | 2 +- ghcide/src/Development/IDE/Core/Rules.hs | 15 ++ haskell-language-server.cabal | 11 + plugins/hls-stan-plugin/LICENSE | 201 ++++++++++++++++++ plugins/hls-stan-plugin/hls-stan-plugin.cabal | 79 +++++++ .../hls-stan-plugin/src/Ide/Plugin/Stan.hs | 120 +++++++++++ plugins/hls-stan-plugin/test/Main.hs | 46 ++++ .../test/testdata/.hie/Main.hie | Bin 0 -> 1056 bytes .../hls-stan-plugin/test/testdata/hie.yaml | 4 + plugins/hls-stan-plugin/test/testdata/test.hs | 4 + stack-lts16.yaml | 8 + 18 files changed, 511 insertions(+), 2 deletions(-) create mode 100644 plugins/hls-stan-plugin/LICENSE create mode 100644 plugins/hls-stan-plugin/hls-stan-plugin.cabal create mode 100644 plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs create mode 100644 plugins/hls-stan-plugin/test/Main.hs create mode 100644 plugins/hls-stan-plugin/test/testdata/.hie/Main.hie create mode 100644 plugins/hls-stan-plugin/test/testdata/hie.yaml create mode 100644 plugins/hls-stan-plugin/test/testdata/test.hs diff --git a/.github/workflows/hackage.yml b/.github/workflows/hackage.yml index 09b1f57de7..ac939f9896 100644 --- a/.github/workflows/hackage.yml +++ b/.github/workflows/hackage.yml @@ -31,7 +31,7 @@ jobs: "hls-brittany-plugin", "hls-floskell-plugin", "hls-fourmolu-plugin", "hls-ormolu-plugin", "hls-stylish-haskell-plugin", "hls-class-plugin", "hls-eval-plugin", "hls-explicit-imports-plugin", - "hls-haddock-comments-plugin", "hls-hlint-plugin", + "hls-haddock-comments-plugin", "hls-hlint-plugin", "hls-stan-plugin", "hls-module-name-plugin", "hls-pragmas-plugin", "hls-refine-imports-plugin", "hls-rename-plugin", "hls-retrie-plugin", "hls-splice-plugin", "hls-tactics-plugin", diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c57c4de3cd..c504aa87bd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -223,6 +223,10 @@ jobs: name: Test hls-hlint-plugin test suite run: cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-hlint-plugin --test-options="$TEST_OPTS" + - if: matrix.test && matrix.ghc != '9.0.1' && matrix.ghc != '9.0.2' && matrix.ghc != '9.2.2' + name: Test hls-stan-plugin test suite + run: cabal test hls-stan-plugin --test-options="$TEST_OPTS" || cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-stan-plugin --test-options="$TEST_OPTS" + - if: matrix.test name: Test hls-module-name-plugin test suite run: cabal test hls-module-name-plugin --test-options="$TEST_OPTS" || cabal test hls-module-name-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-module-name-plugin --test-options="$TEST_OPTS" diff --git a/CODEOWNERS b/CODEOWNERS index 70dc00b939..27cc7e20d4 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -29,6 +29,7 @@ /plugins/hls-splice-plugin @konn /plugins/hls-stylish-haskell-plugin @Ailrun /plugins/hls-tactics-plugin @isovector +/plugins/hls-stan-plugin @uhbif19 # Benchmarking /shake-bench @pepeiborra diff --git a/cabal.project b/cabal.project index f87c79e86a..bb14019d99 100644 --- a/cabal.project +++ b/cabal.project @@ -28,6 +28,7 @@ packages: ./plugins/hls-qualify-imported-names-plugin ./plugins/hls-code-range-plugin ./plugins/hls-change-type-signature-plugin + ./plugins/hls-stan-plugin ./plugins/hls-gadt-plugin -- Standard location for temporary packages needed for particular environments diff --git a/docs/features.md b/docs/features.md index 58fa945299..4b8b10725f 100644 --- a/docs/features.md +++ b/docs/features.md @@ -38,6 +38,12 @@ Provided by: `hls-hlint-plugin` Provides hlint hints as diagnostics. +### Stan hints + +Provided by: `hls-stan-plugin` + +Provides Stan hints as diagnostics. + ## Hovers Provided by: `ghcide` diff --git a/docs/supported-versions.md b/docs/supported-versions.md index 9e010373bf..2adc4c43ce 100644 --- a/docs/supported-versions.md +++ b/docs/supported-versions.md @@ -58,6 +58,7 @@ Sometimes a plugin will be supported in the pre-built binaries but not in a HLS | `hls-fourmolu-plugin` | | | `hls-haddock-comments-plugin` | 9.2 | | `hls-hlint-plugin` | | +| `hls-stan-plugin` | 8.6, 9.0, 9.2 | | `hls-module-name-plugin` | | | `hls-ormolu-plugin` | | | `hls-pragmas-plugin` | | diff --git a/exe/Plugins.hs b/exe/Plugins.hs index 6fbf6cd1dd..f0cd3c208b 100644 --- a/exe/Plugins.hs +++ b/exe/Plugins.hs @@ -60,6 +60,11 @@ import qualified Ide.Plugin.Tactic as Tactic import qualified Ide.Plugin.Hlint as Hlint #endif +#if stan +import qualified Ide.Plugin.Stan as Stan +#endif + + #if moduleName import qualified Ide.Plugin.ModuleName as ModuleName #endif @@ -184,6 +189,9 @@ idePlugins recorder includeExamples = pluginDescToIdePlugins allPlugins #if hlint Hlint.descriptor pluginRecorder "hlint" : #endif +#if stan + Stan.descriptor pluginRecorder "stan" : +#endif #if splice Splice.descriptor "splice" : #endif diff --git a/ghcide/src/Development/IDE/Core/RuleTypes.hs b/ghcide/src/Development/IDE/Core/RuleTypes.hs index 2a10be92eb..1aa88ed800 100644 --- a/ghcide/src/Development/IDE/Core/RuleTypes.hs +++ b/ghcide/src/Development/IDE/Core/RuleTypes.hs @@ -207,7 +207,7 @@ instance Show HiFileResult where -- | Save the uncompressed AST here, we compress it just before writing to disk data HieAstResult - = forall a. HAR + = forall a . (Typeable a) => HAR { hieModule :: Module , hieAst :: !(HieASTs a) , refMap :: RefMap a diff --git a/ghcide/src/Development/IDE/Core/Rules.hs b/ghcide/src/Development/IDE/Core/Rules.hs index b82d928fa0..786a4ae156 100644 --- a/ghcide/src/Development/IDE/Core/Rules.hs +++ b/ghcide/src/Development/IDE/Core/Rules.hs @@ -28,6 +28,7 @@ module Development.IDE.Core.Rules( getParsedModuleWithComments, getClientConfigAction, usePropertyAction, + getHieFile, -- * Rules CompiledLinkables(..), getParsedModuleRule, @@ -55,6 +56,7 @@ module Development.IDE.Core.Rules( getParsedModuleDefinition, typeCheckRuleDefinition, getRebuildCount, + getSourceFileSource, GhcSessionDepsConfig(..), Log(..), DisplayTHWarning(..), @@ -96,6 +98,7 @@ import qualified Data.Text as T import qualified Data.Text.Encoding as T import Data.Time (UTCTime (..)) import Data.Tuple.Extra +import Data.Typeable (cast) import Development.IDE.Core.Compile import Development.IDE.Core.FileExists hiding (LogShake, Log) import Development.IDE.Core.FileStore (getFileContents, @@ -1232,3 +1235,15 @@ mainRule recorder RulesConfig{..} = do persistentDocMapRule persistentImportMapRule getLinkableRule recorder + +-- | Get HieFile for haskell file on NormalizedFilePath +getHieFile :: NormalizedFilePath -> Action (Maybe HieFile) +getHieFile nfp = runMaybeT $ do + HAR {hieAst} <- MaybeT $ use GetHieAst nfp + tmr <- MaybeT $ use TypeCheck nfp + ghc <- MaybeT $ use GhcSession nfp + msr <- MaybeT $ use GetModSummaryWithoutTimestamps nfp + source <- lift $ getSourceFileSource nfp + let exports = tcg_exports $ tmrTypechecked tmr + typedAst <- MaybeT $ pure $ cast hieAst + liftIO $ runHsc (hscEnv ghc) $ mkHieFile' (msrModSummary msr) exports typedAst source diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index d63b82495c..28193e011d 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -151,6 +151,11 @@ flag hlint default: True manual: True +flag stan + description: Enable stan plugin + default: True + manual: True + flag moduleName description: Enable moduleName plugin default: True @@ -279,6 +284,11 @@ common hlint build-depends: hls-hlint-plugin ^>= 1.0 cpp-options: -Dhlint +common stan + if flag(stan) && (impl(ghc >= 8.8) && impl(ghc < 9.0)) + build-depends: hls-stan-plugin ^>= 1.0 + cpp-options: -Dstan + common moduleName if flag(moduleName) build-depends: hls-module-name-plugin ^>= 1.0 @@ -364,6 +374,7 @@ executable haskell-language-server , retrie , tactic , hlint + , stan , moduleName , pragmas , splice diff --git a/plugins/hls-stan-plugin/LICENSE b/plugins/hls-stan-plugin/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/plugins/hls-stan-plugin/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/hls-stan-plugin/hls-stan-plugin.cabal b/plugins/hls-stan-plugin/hls-stan-plugin.cabal new file mode 100644 index 0000000000..0f63a01a77 --- /dev/null +++ b/plugins/hls-stan-plugin/hls-stan-plugin.cabal @@ -0,0 +1,79 @@ +cabal-version: 2.4 +name: hls-stan-plugin +version: 1.0.0.0 +synopsis: Stan integration plugin with Haskell Language Server +description: + Please see the README on GitHub at + +license: Apache-2.0 +license-file: LICENSE +author: The Haskell IDE Team +maintainer: uhbif19@gmail.com +copyright: The Haskell IDE Team +category: Development +build-type: Simple +extra-source-files: + LICENSE + test/testdata/*.hs + +flag pedantic + description: Enable -Werror + default: False + manual: True + + +library + if impl(ghc < 8.8) || impl(ghc >= 9.0) + buildable: False + else + buildable: True + exposed-modules: Ide.Plugin.Stan + hs-source-dirs: src + build-depends: + base + , containers + , deepseq + , hashable + , hls-plugin-api + , ghc + , ghcide + , lsp-types + , text + , transformers + , unordered-containers + , stan + + default-language: Haskell2010 + default-extensions: + LambdaCase + NamedFieldPuns + DeriveGeneric + TypeFamilies + StandaloneDeriving + DuplicateRecordFields + OverloadedStrings + +test-suite test + if impl(ghc < 8.8) || impl(ghc >= 9.0) + buildable: False + else + buildable: True + type: exitcode-stdio-1.0 + default-language: Haskell2010 + hs-source-dirs: test + main-is: Main.hs + ghc-options: -threaded -rtsopts -with-rtsopts=-N + build-depends: + aeson + , base + , containers + , filepath + , hls-stan-plugin + , hls-plugin-api + , hls-test-utils ^>=1.3 + , lens + , lsp-types + , text + default-extensions: + NamedFieldPuns + OverloadedStrings \ No newline at end of file diff --git a/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs b/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs new file mode 100644 index 0000000000..0d3607a6b3 --- /dev/null +++ b/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs @@ -0,0 +1,120 @@ +module Ide.Plugin.Stan (descriptor) where + +import Control.DeepSeq (NFData) +import Control.Monad (void) +import Control.Monad.IO.Class (liftIO) +import Control.Monad.Trans.Class (lift) +import Control.Monad.Trans.Maybe (MaybeT (MaybeT), runMaybeT) +import Data.Foldable (toList) +import qualified Data.HashMap.Strict as HM +import Data.Hashable (Hashable) +import qualified Data.Map as Map +import Data.Maybe (fromJust, mapMaybe) +import qualified Data.Text as T +import Development.IDE (Action, FileDiagnostic, + GetHieAst (..), + GetModSummaryWithoutTimestamps (..), + GhcSession (..), IdeState, + NormalizedFilePath, + Pretty (..), Recorder, + RuleResult, Rules, + ShowDiagnostic (..), + TypeCheck (..), WithPriority, + action, cmapWithPrio, define, + getFilesOfInterestUntracked, + hscEnv, msrModSummary, + tmrTypechecked, use, uses) +import Development.IDE.Core.RuleTypes (HieAstResult (..)) +import Development.IDE.Core.Rules (getSourceFileSource, + getHieFile) +import qualified Development.IDE.Core.Shake as Shake +import Development.IDE.GHC.Compat (HieASTs (HieASTs), + RealSrcSpan (..), mkHieFile', + mkRealSrcLoc, mkRealSrcSpan, + runHsc, srcSpanEndCol, + srcSpanEndLine, + srcSpanStartCol, + srcSpanStartLine, tcg_exports) +import Development.IDE.GHC.Error (realSrcSpanToRange) +import GHC.Generics (Generic) +import HieTypes (HieASTs, HieFile) +import Ide.Types (PluginDescriptor (..), + PluginId, + defaultPluginDescriptor) +import qualified Language.LSP.Types as LSP +import Stan.Analysis (Analysis (..), runAnalysis) +import Stan.Category (prettyShowCategory) +import Stan.Core.Id (Id (..)) +import Stan.Inspection (Inspection (..)) +import Stan.Inspection.All (inspectionsIds, inspectionsMap) +import Stan.Observation (Observation (..)) +import Stan.Severity (prettyShowSeverity) + +descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState +descriptor recorder plId = (defaultPluginDescriptor plId) {pluginRules = rules recorder} + +newtype Log = LogShake Shake.Log deriving (Show) + +instance Pretty Log where + pretty = \case + LogShake log -> pretty log + +data GetStanDiagnostics = GetStanDiagnostics + deriving (Eq, Show, Generic) + +instance Hashable GetStanDiagnostics + +instance NFData GetStanDiagnostics + +type instance RuleResult GetStanDiagnostics = () + +rules :: Recorder (WithPriority Log) -> Rules () +rules recorder = do + define (cmapWithPrio LogShake recorder) $ + \GetStanDiagnostics file -> do + maybeHie <- getHieFile file + case maybeHie of + Nothing -> return ([], Nothing) + Just hie -> do + let enabledInspections = HM.fromList [(LSP.fromNormalizedFilePath file, inspectionsIds)] + -- This should use Cabal config for extensions and Stan config for inspection preferences is the future + let analysis = runAnalysis Map.empty enabledInspections [] [hie] + return (analysisToDiagnostics file analysis, Just ()) + + action $ do + files <- getFilesOfInterestUntracked + void $ uses GetStanDiagnostics $ HM.keys files + where + analysisToDiagnostics :: NormalizedFilePath -> Analysis -> [FileDiagnostic] + analysisToDiagnostics file = mapMaybe (observationToDianostic file) . toList . analysisObservations + observationToDianostic :: NormalizedFilePath -> Observation -> Maybe FileDiagnostic + observationToDianostic file (Observation {observationSrcSpan, observationInspectionId}) = + do + inspection <- HM.lookup observationInspectionId inspectionsMap + let + -- Looking similar to Stan CLI output + -- We do not use `prettyShowInspection` cuz Id is redundant here + message :: T.Text + message = + T.unlines $ + [ " ✲ Name: " <> inspectionName inspection, + " ✲ Description: " <> inspectionDescription inspection, + " ✲ Severity: " <> (prettyShowSeverity $ + inspectionSeverity inspection), + " ✲ Category: " <> T.intercalate " " + (map prettyShowCategory $ toList $ inspectionCategory inspection), + "Possible solutions:" + ] + ++ map (" - " <>) (inspectionSolution inspection) + return ( file, + ShowDiag, + LSP.Diagnostic + { _range = realSrcSpanToRange $ observationSrcSpan, + _severity = Just LSP.DsHint, + _code = Just (LSP.InR $ unId (inspectionId inspection)), + _source = Just "stan", + _message = message, + _relatedInformation = Nothing, + _tags = Nothing + } + ) diff --git a/plugins/hls-stan-plugin/test/Main.hs b/plugins/hls-stan-plugin/test/Main.hs new file mode 100644 index 0000000000..3f6c5e9bad --- /dev/null +++ b/plugins/hls-stan-plugin/test/Main.hs @@ -0,0 +1,46 @@ +module Main + ( main, + ) +where + +import Control.Lens ((^.)) +import Control.Monad (void) +import Data.List (find) +import Data.Text (Text) +import qualified Data.Text as T +import qualified Data.Text.IO as T +import qualified Ide.Plugin.Stan as Stan +import qualified Language.LSP.Types.Lens as L +import System.FilePath +import Test.Hls + +main :: IO () +main = defaultTestRunner tests + +tests :: TestTree +tests = + testGroup + "stan suggestions" + [ testCase "provides diagnostics" $ + runStanSession "" $ do + doc <- openDoc "test.hs" "haskell" + diags@(reduceDiag : _) <- waitForDiagnosticsFromSource doc "stan" + liftIO $ do + length diags @?= 1 + reduceDiag ^. L.range @?= Range (Position 0 0) (Position 3 19) + reduceDiag ^. L.severity @?= Just DsHint + let expectedPrefix = " ✲ Name: " + assertBool "" $ T.isPrefixOf expectedPrefix (reduceDiag ^. L.message) + reduceDiag ^. L.source @?= Just "stan" + return () + ] + +testDir :: FilePath +testDir = "test/testdata" + +stanPlugin :: PluginDescriptor IdeState +stanPlugin = Stan.descriptor mempty "stan" + +runStanSession :: FilePath -> Session a -> IO a +runStanSession subdir = + failIfSessionTimeout . runSessionWithServer stanPlugin (testDir subdir) diff --git a/plugins/hls-stan-plugin/test/testdata/.hie/Main.hie b/plugins/hls-stan-plugin/test/testdata/.hie/Main.hie new file mode 100644 index 0000000000000000000000000000000000000000..0c7367ab46d26d5db6079a385348b51447d72c49 GIT binary patch literal 1056 zcmZ8g+iuf95Z#$w-yJ&_xAelr@QR4qMxuQvmD;M*NUe%mRG!_$B^Ie|c^yb3K826q z3-}4%`2ZgI0%n~{QKMvbX3p%{xg~mcvbVjpYwvZpx4OGFW4y~4emAN_R^6dw4A{md zqeG}KB9bnc5KKc36e$P-gDhPsmav4d9L;vQWTh2s%0Lgh{@wn zJMS)-Dnn%t~yv71|~ua#CS+hjd+U&2fJNUwC{lw{IbY&jpgXvXuSLsTeU1}G_VNvLjJed@w z7D);x<6{{YRwOUJB;{FJK@3HEKg+{W-jAy^&%!jj%@3dyH?d6o)9AQ+RmJ_!-4D|u uk!}>r<9IZxUDk3uFoCK0Y1n^Nl y = 2 + | otherwise = 3 diff --git a/stack-lts16.yaml b/stack-lts16.yaml index 6a057acf0d..826f8730e4 100644 --- a/stack-lts16.yaml +++ b/stack-lts16.yaml @@ -15,6 +15,7 @@ packages: - ./plugins/hls-explicit-imports-plugin - ./plugins/hls-refine-imports-plugin - ./plugins/hls-hlint-plugin + - ./plugins/hls-stan-plugin - ./plugins/hls-rename-plugin - ./plugins/hls-retrie-plugin - ./plugins/hls-splice-plugin @@ -91,6 +92,13 @@ extra-deps: - primitive-extras-0.10.1 - primitive-unlifted-0.1.3.1 - githash-0.1.6.2 + - stan-0.0.1.0 + - dir-traverse-0.2.3.0@sha256:adcc128f201ff95131b15ffe41365dc99c50dc3fa3a910f021521dc734013bfa,2137 + - extensions-0.0.0.1@sha256:16517ab9df3dd6c7a20da746c8ed02cfd59c8cb40ae5719aef8b5dd4edceadc0,3993 + - microaeson-0.1.0.1@sha256:88ba1cc52181b57abc453e222bbb76ca6ab252e38c6507d15a596d6a582fdf69,3968 + - trial-0.0.0.0@sha256:834d3be439dc9b52a759a45a4d3944e5e55c3d50fd5874003147cc1f6231d4aa,4301 + - trial-optparse-applicative-0.0.0.0@sha256:ba05edfc327a281766df5e0f44d91229e6a98afaf59abe1894b293453f076192,2449 + - trial-tomland-0.0.0.0@sha256:743a9baaa36891ed3a44618fdfd5bc4ed9afc39cf9b9fa23ea1b96f3787f5ec0,2526 configure-options: ghcide: From 347a7187f20242540926b9927c59a45b18798c67 Mon Sep 17 00:00:00 2001 From: Dmitry Pogodin Date: Thu, 28 Jul 2022 17:48:13 +0100 Subject: [PATCH 036/213] Run pre-commit hooks (#3059) --- exe/Main.hs | 16 +- exe/Wrapper.hs | 57 +++--- ghcide/bench/lib/Experiments.hs | 29 ++- ghcide/exe/Main.hs | 78 +++---- .../session-loader/Development/IDE/Session.hs | 6 +- .../Development/IDE/Session/VersionCheck.hs | 2 +- ghcide/src/Development/IDE.hs | 6 +- ghcide/src/Development/IDE/Core/Compile.hs | 39 ++-- ghcide/src/Development/IDE/Core/FileStore.hs | 2 +- ghcide/src/Development/IDE/Core/FileUtils.hs | 7 +- .../Development/IDE/Core/IdeConfiguration.hs | 2 +- .../src/Development/IDE/Core/Preprocessor.hs | 2 +- .../Development/IDE/GHC/Compat/Outputable.hs | 9 +- ghcide/src/Development/IDE/GHC/CoreFile.hs | 70 +++---- ghcide/src/Development/IDE/GHC/Dump.hs | 2 +- ghcide/src/Development/IDE/GHC/ExactPrint.hs | 2 +- ghcide/src/Development/IDE/GHC/Orphans.hs | 8 +- ghcide/src/Development/IDE/Monitoring/EKG.hs | 4 +- ghcide/src/Development/IDE/Plugin/Test.hs | 2 +- .../src/Development/IDE/Plugin/TypeLenses.hs | 2 +- ghcide/src/Development/IDE/Types/Action.hs | 2 +- ghcide/src/Development/IDE/Types/Exports.hs | 4 +- .../src/Development/IDE/Types/KnownTargets.hs | 2 +- .../src/Development/IDE/Types/Monitoring.hs | 4 +- ghcide/src/Development/IDE/Types/Shake.hs | 2 +- ghcide/src/Text/Fuzzy/Parallel.hs | 4 +- ghcide/test/exe/FuzzySearch.hs | 36 ++-- ghcide/test/src/Development/IDE/Test.hs | 2 +- .../Development/IDE/Graph/Internal/Action.hs | 4 +- .../IDE/Graph/Internal/Database.hs | 10 +- .../Development/IDE/Graph/Internal/Types.hs | 8 +- hls-graph/test/ActionSpec.hs | 29 +-- hls-graph/test/DatabaseSpec.hs | 29 +-- hls-graph/test/Example.hs | 20 +- hls-graph/test/Main.hs | 6 +- hls-graph/test/RulesSpec.hs | 2 +- hls-plugin-api/src/Ide/Types.hs | 4 +- plugins/default/src/Ide/Plugin/Example.hs | 2 +- plugins/default/src/Ide/Plugin/Example2.hs | 2 +- .../default/src/Ide/Plugin/ExampleCabal.hs | 26 +-- .../src/Ide/Plugin/Literals.hs | 2 +- .../src/Ide/Plugin/Eval/CodeLens.hs | 190 ++++++++++-------- .../src/Ide/Plugin/Eval/Config.hs | 4 +- .../src/Ide/Plugin/Eval/Parse/Comments.hs | 2 +- plugins/hls-eval-plugin/test/Main.hs | 2 +- .../src/Ide/Plugin/ExplicitImports.hs | 2 +- .../src/Ide/Plugin/ModuleName.hs | 3 +- .../src/Development/Benchmark/Rules.hs | 11 +- 48 files changed, 405 insertions(+), 354 deletions(-) diff --git a/exe/Main.hs b/exe/Main.hs index 6257b72a3c..098a427ef9 100644 --- a/exe/Main.hs +++ b/exe/Main.hs @@ -7,20 +7,26 @@ module Main(main) where import Control.Monad.IO.Class (liftIO) import Data.Function ((&)) -import Data.Text (Text) -import qualified Development.IDE.Types.Logger as Logger -import Development.IDE.Types.Logger (Priority (Debug, Info, Error), +import Data.Text (Text) +import Development.IDE.Types.Logger (Doc, + Priority (Debug, Error, Info), WithPriority (WithPriority, priority), cfilter, cmapWithPrio, + defaultLayoutOptions, + layoutPretty, makeDefaultStderrRecorder, - withDefaultRecorder, renderStrict, layoutPretty, defaultLayoutOptions, Doc) + renderStrict, + withDefaultRecorder) +import qualified Development.IDE.Types.Logger as Logger import Ide.Arguments (Arguments (..), GhcideArguments (..), getArguments) import Ide.Main (defaultMain) import qualified Ide.Main as IdeMain import Ide.PluginUtils (pluginDescToIdePlugins) -import Ide.Types (PluginDescriptor (pluginNotificationHandlers), defaultPluginDescriptor, mkPluginNotificationHandler) +import Ide.Types (PluginDescriptor (pluginNotificationHandlers), + defaultPluginDescriptor, + mkPluginNotificationHandler) import Language.LSP.Server as LSP import Language.LSP.Types as LSP import qualified Plugins diff --git a/exe/Wrapper.hs b/exe/Wrapper.hs index 23cc153215..83f10cedb2 100644 --- a/exe/Wrapper.hs +++ b/exe/Wrapper.hs @@ -12,13 +12,13 @@ module Main where import Control.Monad.Extra -import Data.Char (isSpace) +import Data.Char (isSpace) import Data.Default import Data.Foldable import Data.List import Data.Void -import qualified Development.IDE.Session as Session -import qualified HIE.Bios.Environment as HieBios +import qualified Development.IDE.Session as Session +import qualified HIE.Bios.Environment as HieBios import HIE.Bios.Types import Ide.Arguments import Ide.Version @@ -26,36 +26,43 @@ import System.Directory import System.Environment import System.Exit import System.FilePath -import System.IO import System.Info +import System.IO #ifndef mingw32_HOST_OS -import System.Posix.Process (executeFile) -import qualified Data.Map.Strict as Map +import qualified Data.Map.Strict as Map +import System.Posix.Process (executeFile) #else import System.Process #endif -import qualified Data.Text.IO as T -import Control.Monad.Trans.Except (ExceptT, runExceptT, throwE) -import qualified Data.Text as T -import Language.LSP.Server (LspM) -import Control.Monad.IO.Class (MonadIO (liftIO)) -import Control.Monad.IO.Unlift (MonadUnliftIO) -import qualified Language.LSP.Server as LSP -import qualified Development.IDE.Main as Main -import Ide.Plugin.Config (Config) -import Language.LSP.Types (RequestMessage, ResponseError, MessageActionItem (MessageActionItem), Method(Initialize), MessageType (MtError), SMethod (SWindowShowMessageRequest, SExit), ShowMessageRequestParams (ShowMessageRequestParams)) -import Development.IDE.Types.Logger ( makeDefaultStderrRecorder, - cmapWithPrio, - Pretty(pretty), - Logger(Logger), - Priority(Error, Debug, Info, Warning), - Recorder(logger_), - WithPriority(WithPriority) ) +import Control.Concurrent (tryPutMVar) +import Control.Monad.IO.Class (MonadIO (liftIO)) +import Control.Monad.IO.Unlift (MonadUnliftIO) +import Control.Monad.Trans.Except (ExceptT, runExceptT, + throwE) import Data.Maybe -import GHC.Stack.Types (emptyCallStack) -import Control.Concurrent (tryPutMVar) +import qualified Data.Text as T +import qualified Data.Text.IO as T import Development.IDE.LSP.LanguageServer (runLanguageServer) +import qualified Development.IDE.Main as Main +import Development.IDE.Types.Logger (Logger (Logger), + Pretty (pretty), + Priority (Debug, Error, Info, Warning), + Recorder (logger_), + WithPriority (WithPriority), + cmapWithPrio, + makeDefaultStderrRecorder) +import GHC.Stack.Types (emptyCallStack) import HIE.Bios.Internal.Log +import Ide.Plugin.Config (Config) +import Language.LSP.Server (LspM) +import qualified Language.LSP.Server as LSP +import Language.LSP.Types (MessageActionItem (MessageActionItem), + MessageType (MtError), + Method (Initialize), + RequestMessage, + ResponseError, + SMethod (SExit, SWindowShowMessageRequest), + ShowMessageRequestParams (ShowMessageRequestParams)) -- --------------------------------------------------------------------- diff --git a/ghcide/bench/lib/Experiments.hs b/ghcide/bench/lib/Experiments.hs index 159f0addb9..df58577e6c 100644 --- a/ghcide/bench/lib/Experiments.hs +++ b/ghcide/bench/lib/Experiments.hs @@ -37,9 +37,8 @@ import Development.IDE.Test (getBuildEdgesCount, getBuildKeysBuilt, getBuildKeysChanged, getBuildKeysVisited, - getStoredKeys, getRebuildsCount, - ) + getStoredKeys) import Development.IDE.Test.Diagnostic import Development.Shake (CmdOption (Cwd, FileStdout), cmd_) @@ -428,20 +427,20 @@ runBenchmarksFun dir allBenchmarks = do } data BenchRun = BenchRun - { startup :: !Seconds, - runSetup :: !Seconds, - runExperiment :: !Seconds, - userWaits :: !Seconds, - delayedWork :: !Seconds, - firstResponse :: !Seconds, + { startup :: !Seconds, + runSetup :: !Seconds, + runExperiment :: !Seconds, + userWaits :: !Seconds, + delayedWork :: !Seconds, + firstResponse :: !Seconds, firstResponseDelayed :: !Seconds, - rulesBuilt :: !Int, - rulesChanged :: !Int, - rulesVisited :: !Int, - rulesTotal :: !Int, - edgesTotal :: !Int, - rebuildsTotal :: !Int, - success :: !Bool + rulesBuilt :: !Int, + rulesChanged :: !Int, + rulesVisited :: !Int, + rulesTotal :: !Int, + edgesTotal :: !Int, + rebuildsTotal :: !Int, + success :: !Bool } badRun :: BenchRun diff --git a/ghcide/exe/Main.hs b/ghcide/exe/Main.hs index 538367c3bd..ec5fe8518a 100644 --- a/ghcide/exe/Main.hs +++ b/ghcide/exe/Main.hs @@ -5,44 +5,50 @@ module Main(main) where -import Arguments (Arguments (..), - getArguments) -import Control.Monad.Extra (unless) -import Control.Monad.IO.Class (liftIO) -import Data.Default (def) -import Data.Function ((&)) -import Data.Version (showVersion) -import Development.GitRev (gitHash) -import Development.IDE (action) -import Development.IDE.Core.OfInterest (kick) -import Development.IDE.Core.Rules (mainRule) -import qualified Development.IDE.Core.Rules as Rules -import Development.IDE.Core.Tracing (withTelemetryLogger) -import qualified Development.IDE.Main as IDEMain +import Arguments (Arguments (..), + getArguments) +import Control.Monad.Extra (unless) +import Control.Monad.IO.Class (liftIO) +import Data.Default (def) +import Data.Function ((&)) +import Data.Version (showVersion) +import Development.GitRev (gitHash) +import Development.IDE (action) +import Development.IDE.Core.OfInterest (kick) +import Development.IDE.Core.Rules (mainRule) +import qualified Development.IDE.Core.Rules as Rules +import Development.IDE.Core.Tracing (withTelemetryLogger) +import qualified Development.IDE.Main as IDEMain +import qualified Development.IDE.Monitoring.EKG as EKG import qualified Development.IDE.Monitoring.OpenTelemetry as OpenTelemetry -import qualified Development.IDE.Monitoring.EKG as EKG -import qualified Development.IDE.Plugin.HLS.GhcIde as GhcIde -import Development.IDE.Types.Logger (Logger (Logger), - LoggingColumn (DataColumn, PriorityColumn), - Pretty (pretty), - Priority (Debug, Info, Error), - WithPriority (WithPriority, priority), - cfilter, cmapWithPrio, - makeDefaultStderrRecorder, layoutPretty, renderStrict, defaultLayoutOptions) -import qualified Development.IDE.Types.Logger as Logger +import qualified Development.IDE.Plugin.HLS.GhcIde as GhcIde +import Development.IDE.Types.Logger (Logger (Logger), + LoggingColumn (DataColumn, PriorityColumn), + Pretty (pretty), + Priority (Debug, Error, Info), + WithPriority (WithPriority, priority), + cfilter, + cmapWithPrio, + defaultLayoutOptions, + layoutPretty, + makeDefaultStderrRecorder, + renderStrict) +import qualified Development.IDE.Types.Logger as Logger import Development.IDE.Types.Options -import GHC.Stack (emptyCallStack) -import Language.LSP.Server as LSP -import Language.LSP.Types as LSP -import Ide.Plugin.Config (Config (checkParents, checkProject)) -import Ide.PluginUtils (pluginDescToIdePlugins) -import Ide.Types (PluginDescriptor (pluginNotificationHandlers), defaultPluginDescriptor, mkPluginNotificationHandler) -import Paths_ghcide (version) -import qualified System.Directory.Extra as IO -import System.Environment (getExecutablePath) -import System.Exit (exitSuccess) -import System.IO (hPutStrLn, stderr) -import System.Info (compilerVersion) +import GHC.Stack (emptyCallStack) +import Ide.Plugin.Config (Config (checkParents, checkProject)) +import Ide.PluginUtils (pluginDescToIdePlugins) +import Ide.Types (PluginDescriptor (pluginNotificationHandlers), + defaultPluginDescriptor, + mkPluginNotificationHandler) +import Language.LSP.Server as LSP +import Language.LSP.Types as LSP +import Paths_ghcide (version) +import qualified System.Directory.Extra as IO +import System.Environment (getExecutablePath) +import System.Exit (exitSuccess) +import System.Info (compilerVersion) +import System.IO (hPutStrLn, stderr) data Log = LogIDEMain IDEMain.Log diff --git a/ghcide/session-loader/Development/IDE/Session.hs b/ghcide/session-loader/Development/IDE/Session.hs index 44ba1c0728..4a3e932025 100644 --- a/ghcide/session-loader/Development/IDE/Session.hs +++ b/ghcide/session-loader/Development/IDE/Session.hs @@ -36,8 +36,8 @@ import qualified Data.ByteString.Char8 as B import Data.Default import Data.Either.Extra import Data.Function -import qualified Data.HashMap.Strict as HM import Data.Hashable +import qualified Data.HashMap.Strict as HM import Data.IORef import Data.List import qualified Data.Map.Strict as Map @@ -87,6 +87,7 @@ import Data.Void import Control.Concurrent.STM.Stats (atomically, modifyTVar', readTVar, writeTVar) import Control.Concurrent.STM.TQueue +import Control.Monad.IO.Unlift (MonadUnliftIO) import Data.Foldable (for_) import Data.HashMap.Strict (HashMap) import Data.HashSet (HashSet) @@ -97,9 +98,8 @@ import Development.IDE.Types.Shake (WithHieDb) import HieDb.Create import HieDb.Types import HieDb.Utils -import System.Random (RandomGen) import qualified System.Random as Random -import Control.Monad.IO.Unlift (MonadUnliftIO) +import System.Random (RandomGen) data Log = LogSettingInitialDynFlags diff --git a/ghcide/session-loader/Development/IDE/Session/VersionCheck.hs b/ghcide/session-loader/Development/IDE/Session/VersionCheck.hs index a5fbda891c..80399846c3 100644 --- a/ghcide/session-loader/Development/IDE/Session/VersionCheck.hs +++ b/ghcide/session-loader/Development/IDE/Session/VersionCheck.hs @@ -5,7 +5,7 @@ -- See https://github.com/haskell/ghcide/pull/697 module Development.IDE.Session.VersionCheck (ghcVersionChecker) where -import GHC.Check +import GHC.Check -- Only use this for checking against the compile time GHC libDir! -- Use getRuntimeGhcLibDir from hie-bios instead for everything else -- otherwise binaries will not be distributable since paths will be baked into them diff --git a/ghcide/src/Development/IDE.hs b/ghcide/src/Development/IDE.hs index f4e998944a..6cc2989fd1 100644 --- a/ghcide/src/Development/IDE.hs +++ b/ghcide/src/Development/IDE.hs @@ -16,15 +16,16 @@ import Development.IDE.Core.FileStore as X (getFileContents) import Development.IDE.Core.IdeConfiguration as X (IdeConfiguration (..), isWorkspaceFile) import Development.IDE.Core.OfInterest as X (getFilesOfInterestUntracked) -import Development.IDE.Core.RuleTypes as X import Development.IDE.Core.Rules as X (getClientConfigAction, getParsedModule) +import Development.IDE.Core.RuleTypes as X import Development.IDE.Core.Service as X (runAction) import Development.IDE.Core.Shake as X (FastResult (..), IdeAction (..), IdeRule, IdeState, RuleBody (..), ShakeExtras, + VFSModified (..), actionLogger, define, defineEarlyCutoff, @@ -40,8 +41,7 @@ import Development.IDE.Core.Shake as X (FastResult (..), useWithStaleFast, useWithStaleFast', useWithStale_, - use_, uses, uses_, - VFSModified(..)) + use_, uses, uses_) import Development.IDE.GHC.Compat as X (GhcVersion (..), ghcVersion) import Development.IDE.GHC.Error as X diff --git a/ghcide/src/Development/IDE/Core/Compile.hs b/ghcide/src/Development/IDE/Core/Compile.hs index 7496b3d5fa..2430ec719a 100644 --- a/ghcide/src/Development/IDE/Core/Compile.hs +++ b/ghcide/src/Development/IDE/Core/Compile.hs @@ -38,14 +38,15 @@ module Development.IDE.Core.Compile import Control.Concurrent.Extra import Control.Concurrent.STM.Stats hiding (orElse) -import Control.DeepSeq (force, liftRnf, rnf, rwhnf, NFData(..)) +import Control.DeepSeq (NFData (..), force, liftRnf, + rnf, rwhnf) import Control.Exception (evaluate) import Control.Exception.Safe import Control.Lens hiding (List, (<.>)) import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Trans.Except -import qualified Control.Monad.Trans.State.Strict as S +import qualified Control.Monad.Trans.State.Strict as S import Data.Aeson (toJSON) import Data.Bifunctor (first, second) import Data.Binary @@ -53,12 +54,12 @@ import qualified Data.ByteString as BS import Data.Coerce import qualified Data.DList as DL import Data.Functor -import Data.Generics.Schemes -import Data.Generics.Aliases +import Data.Generics.Aliases +import Data.Generics.Schemes import qualified Data.HashMap.Strict as HashMap -import Data.IORef import Data.IntMap (IntMap) import qualified Data.IntMap.Strict as IntMap +import Data.IORef import Data.List.Extra import Data.Map (Map) import qualified Data.Map.Strict as Map @@ -79,6 +80,7 @@ import Development.IDE.GHC.Compat hiding (loadInterface, import qualified Development.IDE.GHC.Compat as Compat import qualified Development.IDE.GHC.Compat as GHC import qualified Development.IDE.GHC.Compat.Util as Util +import Development.IDE.GHC.CoreFile import Development.IDE.GHC.Error import Development.IDE.GHC.Orphans () import Development.IDE.GHC.Util @@ -86,12 +88,10 @@ import Development.IDE.GHC.Warnings import Development.IDE.Types.Diagnostics import Development.IDE.Types.Location import Development.IDE.Types.Options -import Development.IDE.GHC.CoreFile import GHC (ForeignHValue, GetDocsFailure (..), - parsedSource, - GhcException(..) - ) + GhcException (..), + parsedSource) import qualified GHC.LanguageExtensions as LangExt import GHC.Serialized import HieDb @@ -111,25 +111,26 @@ import ErrUtils import GHC.Tc.Gen.Splice #if MIN_VERSION_ghc(9,2,1) -import GHC.Types.HpcInfo import GHC.Types.ForeignStubs +import GHC.Types.HpcInfo import GHC.Types.TypeEnv #else import GHC.Driver.Types #endif #else -import TcSplice import HscTypes +import TcSplice #endif #if MIN_VERSION_ghc(9,2,0) import GHC (Anchor (anchor), EpaComment (EpaComment), EpaCommentTok (EpaBlockComment, EpaLineComment), - epAnnComments, + ModuleGraph, epAnnComments, + mgLookupModule, + mgModSummaries, priorComments) -import GHC (ModuleGraph, mgLookupModule, mgModSummaries) import qualified GHC as G import GHC.Hs (LEpaComment) import qualified GHC.Types.Error as Error @@ -163,7 +164,7 @@ computePackageDeps env pkg = do data TypecheckHelpers = TypecheckHelpers { getLinkablesToKeep :: !(IO (ModuleEnv UTCTime)) - , getLinkables :: !([NormalizedFilePath] -> IO [LinkableResult]) + , getLinkables :: !([NormalizedFilePath] -> IO [LinkableResult]) } typecheckModule :: IdeDefer @@ -486,7 +487,7 @@ mkHiFileResultCompile se session' tcm simplified_guts = catchErrs $ do in setIdOccInfo v' noOccInfo else v isOtherUnfolding (OtherCon _) = True - isOtherUnfolding _ = False + isOtherUnfolding _ = False when (not $ null diffs) $ @@ -1229,7 +1230,7 @@ data RecompilationInfo m data IdeLinkable = GhcLinkable !Linkable | CoreLinkable !UTCTime !CoreFile instance NFData IdeLinkable where - rnf (GhcLinkable lb) = rnf lb + rnf (GhcLinkable lb) = rnf lb rnf (CoreLinkable time _) = rnf time ml_core_file :: ModLocation -> FilePath @@ -1258,7 +1259,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 (toNormalizedFilePath' 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 @@ -1290,7 +1291,7 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do maybe_recomp <- checkLinkableDependencies get_linkable_hashes (hsc_mod_graph sessionWithMsDynFlags) (hirRuntimeModules old_hir) case maybe_recomp of Just msg -> do_regenerate msg - Nothing -> return ([], Just old_hir) + Nothing -> return ([], Just old_hir) -- Otherwise use the value from disk, provided the core file is up to date if required _ -> do details <- liftIO $ mkDetailsFromIface sessionWithMsDynFlags iface @@ -1393,7 +1394,7 @@ coreFileToLinkable linkableType session ms iface details core_file t = do tyCons = typeEnvTyCons (md_types details) let cgi_guts = CgGuts this_mod tyCons (implicit_binds ++ core_binds) NoStubs [] [] (emptyHpcInfo False) Nothing [] (warns, lb) <- case linkableType of - BCOLinkable -> generateByteCode (CoreFileTime t) session ms cgi_guts + BCOLinkable -> generateByteCode (CoreFileTime t) session ms cgi_guts ObjectLinkable -> generateObjectCode session ms cgi_guts pure (warns, HomeModInfo iface details . Just <$> lb) diff --git a/ghcide/src/Development/IDE/Core/FileStore.hs b/ghcide/src/Development/IDE/Core/FileStore.hs index 849167988d..697df7c3fd 100644 --- a/ghcide/src/Development/IDE/Core/FileStore.hs +++ b/ghcide/src/Development/IDE/Core/FileStore.hs @@ -32,9 +32,9 @@ import qualified Data.Rope.UTF16 as Rope import qualified Data.Text as T import Data.Time import Data.Time.Clock.POSIX +import Development.IDE.Core.FileUtils import Development.IDE.Core.RuleTypes import Development.IDE.Core.Shake hiding (Log) -import Development.IDE.Core.FileUtils import Development.IDE.GHC.Orphans () import Development.IDE.Graph import Development.IDE.Import.DependencyInformation diff --git a/ghcide/src/Development/IDE/Core/FileUtils.hs b/ghcide/src/Development/IDE/Core/FileUtils.hs index 1cbfa0ee0b..4725ed83bd 100644 --- a/ghcide/src/Development/IDE/Core/FileUtils.hs +++ b/ghcide/src/Development/IDE/Core/FileUtils.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE CPP #-} +{-# LANGUAGE CPP #-} module Development.IDE.Core.FileUtils( getModTime, @@ -7,10 +7,9 @@ module Development.IDE.Core.FileUtils( import Data.Time.Clock.POSIX #ifdef mingw32_HOST_OS -import qualified System.Directory as Dir +import qualified System.Directory as Dir #else -import System.Posix.Files (getFileStatus, - modificationTimeHiRes) +import System.Posix.Files (getFileStatus, modificationTimeHiRes) #endif -- Dir.getModificationTime is surprisingly slow since it performs diff --git a/ghcide/src/Development/IDE/Core/IdeConfiguration.hs b/ghcide/src/Development/IDE/Core/IdeConfiguration.hs index b91301215f..c3b5323548 100644 --- a/ghcide/src/Development/IDE/Core/IdeConfiguration.hs +++ b/ghcide/src/Development/IDE/Core/IdeConfiguration.hs @@ -16,8 +16,8 @@ import Control.Concurrent.Strict import Control.Monad import Control.Monad.IO.Class import Data.Aeson.Types (Value) -import Data.HashSet (HashSet, singleton) import Data.Hashable (Hashed, hashed, unhashed) +import Data.HashSet (HashSet, singleton) import Data.Text (Text, isPrefixOf) import Development.IDE.Core.Shake import Development.IDE.Graph diff --git a/ghcide/src/Development/IDE/Core/Preprocessor.hs b/ghcide/src/Development/IDE/Core/Preprocessor.hs index 29fe43296e..678471c9c1 100644 --- a/ghcide/src/Development/IDE/Core/Preprocessor.hs +++ b/ghcide/src/Development/IDE/Core/Preprocessor.hs @@ -5,9 +5,9 @@ module Development.IDE.Core.Preprocessor ( preprocessor ) where -import Development.IDE.GHC.CPP import Development.IDE.GHC.Compat import qualified Development.IDE.GHC.Compat.Util as Util +import Development.IDE.GHC.CPP import Development.IDE.GHC.Orphans () import Control.DeepSeq (NFData (rnf)) diff --git a/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs b/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs index 8abff2c6ea..88bd76934e 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs @@ -44,7 +44,8 @@ import GHC.Types.SourceError import GHC.Types.SrcLoc import GHC.Unit.State import GHC.Utils.Error hiding (mkWarnMsg) -import GHC.Utils.Outputable as Out hiding (defaultUserStyle) +import GHC.Utils.Outputable as Out hiding + (defaultUserStyle) import qualified GHC.Utils.Outputable as Out import GHC.Utils.Panic #elif MIN_VERSION_ghc(9,0,0) @@ -54,7 +55,8 @@ import GHC.Types.Name.Reader (GlobalRdrEnv) import GHC.Types.SrcLoc import GHC.Utils.Error as Err hiding (mkWarnMsg) import qualified GHC.Utils.Error as Err -import GHC.Utils.Outputable as Out hiding (defaultUserStyle) +import GHC.Utils.Outputable as Out hiding + (defaultUserStyle) import qualified GHC.Utils.Outputable as Out #else import Development.IDE.GHC.Compat.Core (GlobalRdrEnv) @@ -62,7 +64,8 @@ import DynFlags import ErrUtils hiding (mkWarnMsg) import qualified ErrUtils as Err import HscTypes -import Outputable as Out hiding (defaultUserStyle) +import Outputable as Out hiding + (defaultUserStyle) import qualified Outputable as Out import SrcLoc #endif diff --git a/ghcide/src/Development/IDE/GHC/CoreFile.hs b/ghcide/src/Development/IDE/GHC/CoreFile.hs index 5f0cbd981c..dde331c6ce 100644 --- a/ghcide/src/Development/IDE/GHC/CoreFile.hs +++ b/ghcide/src/Development/IDE/GHC/CoreFile.hs @@ -1,6 +1,6 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE RecordWildCards #-} -- | CoreFiles let us serialize Core to a file in order to later recover it -- without reparsing or retypechecking @@ -12,45 +12,45 @@ module Development.IDE.GHC.CoreFile , writeBinCoreFile , getImplicitBinds) where -import GHC.Fingerprint -import Data.IORef -import Data.Foldable -import Data.List (isPrefixOf) -import Control.Monad.IO.Class -import Control.Monad -import Data.Maybe +import Control.Monad +import Control.Monad.IO.Class +import Data.Foldable +import Data.IORef +import Data.List (isPrefixOf) +import Data.Maybe +import GHC.Fingerprint -import Development.IDE.GHC.Compat +import Development.IDE.GHC.Compat #if MIN_VERSION_ghc(9,0,0) -import GHC.Utils.Binary -import GHC.Core -import GHC.CoreToIface -import GHC.IfaceToCore -import GHC.Iface.Env -import GHC.Iface.Binary -import GHC.Types.Id.Make -import GHC.Iface.Recomp.Binary ( fingerprintBinMem ) +import GHC.Core +import GHC.CoreToIface +import GHC.Iface.Binary +import GHC.Iface.Env +import GHC.Iface.Recomp.Binary (fingerprintBinMem) +import GHC.IfaceToCore +import GHC.Types.Id.Make +import GHC.Utils.Binary #if MIN_VERSION_ghc(9,2,0) -import GHC.Types.TypeEnv +import GHC.Types.TypeEnv #else -import GHC.Driver.Types +import GHC.Driver.Types #endif #elif MIN_VERSION_ghc(8,6,0) -import Binary -import CoreSyn -import ToIface -import TcIface -import IfaceEnv -import BinIface -import HscTypes -import IdInfo -import Var -import Unique -import MkId -import BinFingerprint ( fingerprintBinMem ) +import Binary +import BinFingerprint (fingerprintBinMem) +import BinIface +import CoreSyn +import HscTypes +import IdInfo +import IfaceEnv +import MkId +import TcIface +import ToIface +import Unique +import Var #endif import qualified Development.IDE.GHC.Compat.Util as Util @@ -61,7 +61,7 @@ initBinMemSize = 1024 * 1024 data CoreFile = CoreFile - { cf_bindings :: [TopIfaceBinding IfaceId] + { cf_bindings :: [TopIfaceBinding IfaceId] -- ^ The actual core file bindings, deserialized lazily , cf_iface_hash :: !Fingerprint } @@ -143,7 +143,7 @@ isNotImplictBind bind = any (not . isImplicitId) $ bindBindings bind bindBindings :: CoreBind -> [Var] bindBindings (NonRec b _) = [b] -bindBindings (Rec bnds) = map fst bnds +bindBindings (Rec bnds) = map fst bnds getImplicitBinds :: TyCon -> [CoreBind] getImplicitBinds tc = cls_binds ++ getTyConImplicitBinds tc @@ -219,7 +219,7 @@ tcIfaceId = fmap getIfaceId . tcIfaceDecl False <=< unmangle_decl_name | otherwise = pure ifid -- invariant: 'IfaceId' is always a 'IfaceId' constructor getIfaceId (AnId id) = id - getIfaceId _ = error "tcIfaceId: got non Id" + getIfaceId _ = error "tcIfaceId: got non Id" tc_iface_bindings :: TopIfaceBinding Id -> IfL CoreBind tc_iface_bindings (TopIfaceNonRec v e) = do diff --git a/ghcide/src/Development/IDE/GHC/Dump.hs b/ghcide/src/Development/IDE/GHC/Dump.hs index 40bd97b54c..a81d6e1215 100644 --- a/ghcide/src/Development/IDE/GHC/Dump.hs +++ b/ghcide/src/Development/IDE/GHC/Dump.hs @@ -10,8 +10,8 @@ import HsDumpAst #if MIN_VERSION_ghc(9,2,1) import qualified Data.ByteString as B import Development.IDE.GHC.Compat.Util -import GHC.Hs import Generics.SYB (ext1Q, ext2Q, extQ) +import GHC.Hs #endif #if MIN_VERSION_ghc(9,0,1) import GHC.Plugins diff --git a/ghcide/src/Development/IDE/GHC/ExactPrint.hs b/ghcide/src/Development/IDE/GHC/ExactPrint.hs index a2257250b4..f7a67d75bc 100644 --- a/ghcide/src/Development/IDE/GHC/ExactPrint.hs +++ b/ghcide/src/Development/IDE/GHC/ExactPrint.hs @@ -79,9 +79,9 @@ import Development.IDE.Types.Logger (Pretty (pretty), Recorder, WithPriority, cmapWithPrio) -import qualified GHC.Generics as GHC import Generics.SYB import Generics.SYB.GHC +import qualified GHC.Generics as GHC import Ide.PluginUtils import Language.Haskell.GHC.ExactPrint.Parsers import Language.LSP.Types diff --git a/ghcide/src/Development/IDE/GHC/Orphans.hs b/ghcide/src/Development/IDE/GHC/Orphans.hs index 3dfaaedf80..9ec6bfb5b8 100644 --- a/ghcide/src/Development/IDE/GHC/Orphans.hs +++ b/ghcide/src/Development/IDE/GHC/Orphans.hs @@ -41,9 +41,9 @@ import Data.Hashable import Data.String (IsString (fromString)) import Data.Text (unpack) #if MIN_VERSION_ghc(9,0,0) -import GHC.ByteCode.Types +import GHC.ByteCode.Types #else -import ByteCodeTypes +import ByteCodeTypes #endif -- Orphan instances for types from the GHC API. @@ -57,8 +57,8 @@ instance NFData SafeHaskellMode where rnf = rwhnf instance Show Linkable where show = unpack . printOutputable instance NFData Linkable where rnf (LM a b c) = rnf a `seq` rnf b `seq` rnf c instance NFData Unlinked where - rnf (DotO f) = rnf f - rnf (DotA f) = rnf f + rnf (DotO f) = rnf f + rnf (DotA f) = rnf f rnf (DotDLL f) = rnf f rnf (BCOs a b) = seqCompiledByteCode a `seq` liftRnf rwhnf b instance Show PackageFlag where show = unpack . printOutputable diff --git a/ghcide/src/Development/IDE/Monitoring/EKG.hs b/ghcide/src/Development/IDE/Monitoring/EKG.hs index 84bc85935a..2999285442 100644 --- a/ghcide/src/Development/IDE/Monitoring/EKG.hs +++ b/ghcide/src/Development/IDE/Monitoring/EKG.hs @@ -1,16 +1,16 @@ {-# LANGUAGE CPP #-} module Development.IDE.Monitoring.EKG(monitoring) where -import Development.IDE.Types.Monitoring (Monitoring (..)) import Development.IDE.Types.Logger (Logger) +import Development.IDE.Types.Monitoring (Monitoring (..)) #ifdef MONITORING_EKG import Control.Concurrent (killThread) import Control.Concurrent.Async (async, waitCatch) import Control.Monad (forM_) import Data.Text (pack) import Development.IDE.Types.Logger (logInfo) -import qualified System.Remote.Monitoring.Wai as Monitoring import qualified System.Metrics as Monitoring +import qualified System.Remote.Monitoring.Wai as Monitoring -- | Monitoring using EKG monitoring :: Logger -> Int -> IO Monitoring diff --git a/ghcide/src/Development/IDE/Plugin/Test.hs b/ghcide/src/Development/IDE/Plugin/Test.hs index 86df7807c2..9932e1dc3a 100644 --- a/ghcide/src/Development/IDE/Plugin/Test.hs +++ b/ghcide/src/Development/IDE/Plugin/Test.hs @@ -25,10 +25,10 @@ import Data.Maybe (isJust) import Data.String import Data.Text (Text, pack) import Development.IDE.Core.OfInterest (getFilesOfInterest) +import Development.IDE.Core.Rules import Development.IDE.Core.RuleTypes import Development.IDE.Core.Service import Development.IDE.Core.Shake -import Development.IDE.Core.Rules import Development.IDE.GHC.Compat import Development.IDE.Graph (Action) import qualified Development.IDE.Graph as Graph diff --git a/ghcide/src/Development/IDE/Plugin/TypeLenses.hs b/ghcide/src/Development/IDE/Plugin/TypeLenses.hs index ecfdd35449..847e44827c 100644 --- a/ghcide/src/Development/IDE/Plugin/TypeLenses.hs +++ b/ghcide/src/Development/IDE/Plugin/TypeLenses.hs @@ -29,9 +29,9 @@ import Development.IDE (GhcSession (..), RuleResult, Rules, define, srcSpanToRange) import Development.IDE.Core.Compile (TcModuleResult (..)) +import Development.IDE.Core.Rules (IdeState, runAction) import Development.IDE.Core.RuleTypes (GetBindings (GetBindings), TypeCheck (TypeCheck)) -import Development.IDE.Core.Rules (IdeState, runAction) import Development.IDE.Core.Service (getDiagnostics) import Development.IDE.Core.Shake (getHiddenDiagnostics, use) import qualified Development.IDE.Core.Shake as Shake diff --git a/ghcide/src/Development/IDE/Types/Action.hs b/ghcide/src/Development/IDE/Types/Action.hs index b0fef98274..4507f39825 100644 --- a/ghcide/src/Development/IDE/Types/Action.hs +++ b/ghcide/src/Development/IDE/Types/Action.hs @@ -11,9 +11,9 @@ module Development.IDE.Types.Action where import Control.Concurrent.STM +import Data.Hashable (Hashable (..)) import Data.HashSet (HashSet) import qualified Data.HashSet as Set -import Data.Hashable (Hashable (..)) import Data.Unique (Unique) import Development.IDE.Graph (Action) import Development.IDE.Types.Logger diff --git a/ghcide/src/Development/IDE/Types/Exports.hs b/ghcide/src/Development/IDE/Types/Exports.hs index faef7d9001..8d75c12f1d 100644 --- a/ghcide/src/Development/IDE/Types/Exports.hs +++ b/ghcide/src/Development/IDE/Types/Exports.hs @@ -17,12 +17,12 @@ module Development.IDE.Types.Exports import Control.DeepSeq (NFData (..)) import Control.Monad import Data.Bifunctor (Bifunctor (second)) +import Data.Hashable (Hashable) import Data.HashMap.Strict (HashMap, elems) import qualified Data.HashMap.Strict as Map import Data.HashSet (HashSet) import qualified Data.HashSet as Set -import Data.Hashable (Hashable) -import Data.List (isSuffixOf, foldl') +import Data.List (foldl', isSuffixOf) import Data.Text (Text, pack) import Development.IDE.GHC.Compat import Development.IDE.GHC.Orphans () diff --git a/ghcide/src/Development/IDE/Types/KnownTargets.hs b/ghcide/src/Development/IDE/Types/KnownTargets.hs index 7ec2e493a5..7f49ced08d 100644 --- a/ghcide/src/Development/IDE/Types/KnownTargets.hs +++ b/ghcide/src/Development/IDE/Types/KnownTargets.hs @@ -3,11 +3,11 @@ module Development.IDE.Types.KnownTargets (KnownTargets, Target(..), toKnownFiles) where import Control.DeepSeq +import Data.Hashable import Data.HashMap.Strict import qualified Data.HashMap.Strict as HMap import Data.HashSet import qualified Data.HashSet as HSet -import Data.Hashable import Development.IDE.GHC.Compat (ModuleName) import Development.IDE.GHC.Orphans () import Development.IDE.Types.Location diff --git a/ghcide/src/Development/IDE/Types/Monitoring.hs b/ghcide/src/Development/IDE/Types/Monitoring.hs index 256381e60a..41e92ed7dd 100644 --- a/ghcide/src/Development/IDE/Types/Monitoring.hs +++ b/ghcide/src/Development/IDE/Types/Monitoring.hs @@ -3,7 +3,7 @@ module Development.IDE.Types.Monitoring ) where import Data.Int -import Data.Text (Text) +import Data.Text (Text) -- | An abstraction for runtime monitoring inspired by the 'ekg' package data Monitoring = Monitoring { @@ -11,7 +11,7 @@ data Monitoring = Monitoring { registerGauge :: Text -> IO Int64 -> IO (), -- | Register a non-negative, monotonically increasing, integer-valued metric. registerCounter :: Text -> IO Int64 -> IO (), - start :: IO (IO ()) -- ^ Start the monitoring system, returning an action which will stop the system. + start :: IO (IO ()) -- ^ Start the monitoring system, returning an action which will stop the system. } instance Semigroup Monitoring where diff --git a/ghcide/src/Development/IDE/Types/Shake.hs b/ghcide/src/Development/IDE/Types/Shake.hs index 905a386cda..43298d8a7e 100644 --- a/ghcide/src/Development/IDE/Types/Shake.hs +++ b/ghcide/src/Development/IDE/Types/Shake.hs @@ -25,6 +25,7 @@ import Data.Hashable import Data.Typeable (cast) import Data.Vector (Vector) import Development.IDE.Core.PositionMapping +import Development.IDE.Core.RuleTypes (FileVersion) import Development.IDE.Graph (Key (..), RuleResult) import qualified Development.IDE.Graph as Shake import Development.IDE.Types.Diagnostics @@ -37,7 +38,6 @@ import Type.Reflection (SomeTypeRep (SomeTypeRep) typeOf, typeRep, typeRepTyCon) import Unsafe.Coerce (unsafeCoerce) -import Development.IDE.Core.RuleTypes (FileVersion) -- | Intended to represent HieDb calls wrapped with (currently) retry -- functionality diff --git a/ghcide/src/Text/Fuzzy/Parallel.hs b/ghcide/src/Text/Fuzzy/Parallel.hs index 1fc6a4e679..6d822e7538 100644 --- a/ghcide/src/Text/Fuzzy/Parallel.hs +++ b/ghcide/src/Text/Fuzzy/Parallel.hs @@ -7,12 +7,12 @@ module Text.Fuzzy.Parallel Scored(..) ) where -import Control.Parallel.Strategies (rseq, using, parList, evalList) +import Control.Parallel.Strategies (evalList, parList, rseq, using) import Data.Bits ((.|.)) import Data.Maybe (fromMaybe, mapMaybe) import qualified Data.Text as T -import qualified Data.Text.Internal as T import qualified Data.Text.Array as TA +import qualified Data.Text.Internal as T import Prelude hiding (filter) data Scored a = Scored {score :: !Int, original:: !a} diff --git a/ghcide/test/exe/FuzzySearch.hs b/ghcide/test/exe/FuzzySearch.hs index f9794270ae..8d2f196b52 100644 --- a/ghcide/test/exe/FuzzySearch.hs +++ b/ghcide/test/exe/FuzzySearch.hs @@ -1,23 +1,23 @@ module FuzzySearch (tests) where -import Control.Monad (guard) -import Data.Char (toLower) -import Data.Maybe (catMaybes) -import qualified Data.Monoid.Textual as T -import Data.Text (Text, inits, pack) -import qualified Data.Text as Text -import System.Directory (doesFileExist) -import System.IO.Unsafe (unsafePerformIO) -import System.Info.Extra (isWindows) -import Test.QuickCheck -import Test.Tasty -import Test.Tasty.ExpectedFailure -import Test.Tasty.HUnit -import Test.Tasty.QuickCheck (testProperty) -import Text.Fuzzy (Fuzzy (..)) -import qualified Text.Fuzzy as Fuzzy -import Text.Fuzzy.Parallel -import Prelude hiding (filter) +import Control.Monad (guard) +import Data.Char (toLower) +import Data.Maybe (catMaybes) +import qualified Data.Monoid.Textual as T +import Data.Text (Text, inits, pack) +import qualified Data.Text as Text +import Prelude hiding (filter) +import System.Directory (doesFileExist) +import System.Info.Extra (isWindows) +import System.IO.Unsafe (unsafePerformIO) +import Test.QuickCheck +import Test.Tasty +import Test.Tasty.ExpectedFailure +import Test.Tasty.HUnit +import Test.Tasty.QuickCheck (testProperty) +import qualified Text.Fuzzy as Fuzzy +import Text.Fuzzy (Fuzzy (..)) +import Text.Fuzzy.Parallel tests :: TestTree tests = diff --git a/ghcide/test/src/Development/IDE/Test.hs b/ghcide/test/src/Development/IDE/Test.hs index 87ee301ec9..b4385043be 100644 --- a/ghcide/test/src/Development/IDE/Test.hs +++ b/ghcide/test/src/Development/IDE/Test.hs @@ -63,9 +63,9 @@ import Language.LSP.Types hiding SemanticTokensEdit (_start)) import Language.LSP.Types.Lens as Lsp import System.Directory (canonicalizePath) +import System.FilePath (equalFilePath) import System.Time.Extra import Test.Tasty.HUnit -import System.FilePath (equalFilePath) requireDiagnosticM :: (Foldable f, Show (f Diagnostic), HasCallStack) diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Action.hs b/hls-graph/src/Development/IDE/Graph/Internal/Action.hs index 854ba903c5..708a414ae5 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Action.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Action.hs @@ -24,9 +24,9 @@ import Control.Exception import Control.Monad.IO.Class import Control.Monad.Trans.Class import Control.Monad.Trans.Reader -import Data.IORef +import Data.Foldable (toList) import Data.Functor.Identity -import Data.Foldable (toList) +import Data.IORef import Development.IDE.Graph.Classes import Development.IDE.Graph.Internal.Database import Development.IDE.Graph.Internal.Rules (RuleResult) diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Database.hs b/hls-graph/src/Development/IDE/Graph/Internal/Database.hs index 308bbde232..f936687ebb 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Database.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Database.hs @@ -7,8 +7,8 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TupleSections #-} module Development.IDE.Graph.Internal.Database (newDatabase, incDatabase, build, getDirtySet, getKeysAndVisitAge) where @@ -36,15 +36,15 @@ import Data.List.NonEmpty (unzip) import Data.Maybe import Data.Traversable (for) import Data.Tuple.Extra -import Debug.Trace (traceM) +import Debug.Trace (traceM) import Development.IDE.Graph.Classes import Development.IDE.Graph.Internal.Rules import Development.IDE.Graph.Internal.Types import qualified Focus import qualified ListT import qualified StmContainers.Map as SMap -import System.Time.Extra (duration, sleep) import System.IO.Unsafe +import System.Time.Extra (duration, sleep) newDatabase :: Dynamic -> TheRules -> IO Database @@ -89,7 +89,7 @@ build db stack keys = do built <- runAIO $ do built <- builder db stack (fmap Key keys) case built of - Left clean -> return clean + Left clean -> return clean Right dirty -> liftIO dirty let (ids, vs) = unzip built pure (ids, fmap (asV . resultValue) vs) @@ -325,7 +325,7 @@ data Wait | Spawn {justWait :: !(IO ())} fmapWait :: (IO () -> IO ()) -> Wait -> Wait -fmapWait f (Wait io) = Wait (f io) +fmapWait f (Wait io) = Wait (f io) fmapWait f (Spawn io) = Spawn (f io) waitOrSpawn :: Wait -> IO (Either (IO ()) (Async ())) diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Types.hs b/hls-graph/src/Development/IDE/Graph/Internal/Types.hs index 449aba1112..56d2d48ac5 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Types.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Types.hs @@ -27,18 +27,18 @@ import qualified Data.ByteString as BS import Data.Dynamic import qualified Data.HashMap.Strict as Map import Data.HashSet (HashSet, member) +import qualified Data.HashSet as Set import Data.IORef +import Data.List (intercalate) import Data.Maybe import Data.Typeable import Development.IDE.Graph.Classes import GHC.Generics (Generic) import qualified ListT -import StmContainers.Map (Map) import qualified StmContainers.Map as SMap +import StmContainers.Map (Map) import System.Time.Extra (Seconds) -import qualified Data.HashSet as Set -import Data.List (intercalate) -import UnliftIO (MonadUnliftIO) +import UnliftIO (MonadUnliftIO) unwrapDynamic :: forall a . Typeable a => Dynamic -> a diff --git a/hls-graph/test/ActionSpec.hs b/hls-graph/test/ActionSpec.hs index 972786dcc2..d79e6edb40 100644 --- a/hls-graph/test/ActionSpec.hs +++ b/hls-graph/test/ActionSpec.hs @@ -1,21 +1,22 @@ -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} module ActionSpec where -import Control.Concurrent.STM -import Development.IDE.Graph (shakeOptions) -import Development.IDE.Graph.Database (shakeNewDatabase, shakeRunDatabase) -import Development.IDE.Graph.Internal.Action (apply1) -import Development.IDE.Graph.Internal.Types -import Development.IDE.Graph.Rule -import qualified Data.HashSet as HashSet -import Example -import qualified StmContainers.Map as STM -import Test.Hspec -import System.Time.Extra (timeout) +import Control.Concurrent.STM +import qualified Data.HashSet as HashSet +import Development.IDE.Graph (shakeOptions) +import Development.IDE.Graph.Database (shakeNewDatabase, + shakeRunDatabase) +import Development.IDE.Graph.Internal.Action (apply1) +import Development.IDE.Graph.Internal.Types +import Development.IDE.Graph.Rule +import Example +import qualified StmContainers.Map as STM +import System.Time.Extra (timeout) +import Test.Hspec spec :: Spec spec = do diff --git a/hls-graph/test/DatabaseSpec.hs b/hls-graph/test/DatabaseSpec.hs index 7ab812e612..0189a92b9a 100644 --- a/hls-graph/test/DatabaseSpec.hs +++ b/hls-graph/test/DatabaseSpec.hs @@ -1,20 +1,21 @@ -{-# LANGUAGE OverloadedLists #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE OverloadedLists #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} module DatabaseSpec where -import Control.Concurrent.STM -import Development.IDE.Graph (shakeOptions) -import Development.IDE.Graph.Database (shakeNewDatabase, shakeRunDatabase) -import Development.IDE.Graph.Internal.Action (apply1) -import Development.IDE.Graph.Internal.Types -import Development.IDE.Graph.Rule -import Example -import qualified StmContainers.Map as STM -import Test.Hspec -import System.Time.Extra (timeout) +import Control.Concurrent.STM +import Development.IDE.Graph (shakeOptions) +import Development.IDE.Graph.Database (shakeNewDatabase, + shakeRunDatabase) +import Development.IDE.Graph.Internal.Action (apply1) +import Development.IDE.Graph.Internal.Types +import Development.IDE.Graph.Rule +import Example +import qualified StmContainers.Map as STM +import System.Time.Extra (timeout) +import Test.Hspec spec :: Spec spec = do diff --git a/hls-graph/test/Example.hs b/hls-graph/test/Example.hs index 3903cbe32c..18807bd1c1 100644 --- a/hls-graph/test/Example.hs +++ b/hls-graph/test/Example.hs @@ -1,16 +1,16 @@ -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} module Example where -import Development.IDE.Graph -import Development.IDE.Graph.Rule -import Development.IDE.Graph.Classes -import GHC.Generics -import Type.Reflection (typeRep) +import Development.IDE.Graph +import Development.IDE.Graph.Classes +import Development.IDE.Graph.Rule +import GHC.Generics +import Type.Reflection (typeRep) data Rule a = Rule deriving (Eq, Generic, Hashable, NFData) diff --git a/hls-graph/test/Main.hs b/hls-graph/test/Main.hs index 452f6208ae..553982775f 100644 --- a/hls-graph/test/Main.hs +++ b/hls-graph/test/Main.hs @@ -1,7 +1,7 @@ import qualified Spec -import Test.Tasty -import Test.Tasty.Hspec -import Test.Tasty.Ingredients.Rerun (defaultMainWithRerun) +import Test.Tasty +import Test.Tasty.Hspec +import Test.Tasty.Ingredients.Rerun (defaultMainWithRerun) main :: IO () main = testSpecs Spec.spec >>= defaultMainWithRerun . testGroup "tactics" diff --git a/hls-graph/test/RulesSpec.hs b/hls-graph/test/RulesSpec.hs index cdea145aa5..bb9253fb26 100644 --- a/hls-graph/test/RulesSpec.hs +++ b/hls-graph/test/RulesSpec.hs @@ -1,6 +1,6 @@ module RulesSpec where -import Test.Hspec +import Test.Hspec spec :: Spec spec = do diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index 7d60fd281d..a9f2bb3050 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -9,6 +9,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PolyKinds #-} @@ -17,7 +18,6 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ViewPatterns #-} -{-# LANGUAGE MultiParamTypeClasses #-} module Ide.Types where @@ -31,10 +31,10 @@ import System.Posix.Signals #endif import Control.Lens ((^.)) import Data.Aeson hiding (defaultOptions) -import qualified Data.DList as DList import qualified Data.Default import Data.Dependent.Map (DMap) import qualified Data.Dependent.Map as DMap +import qualified Data.DList as DList import Data.GADT.Compare import Data.List.NonEmpty (NonEmpty (..), toList) import qualified Data.Map as Map diff --git a/plugins/default/src/Ide/Plugin/Example.hs b/plugins/default/src/Ide/Plugin/Example.hs index 0416cbe8d1..33bf8720fa 100644 --- a/plugins/default/src/Ide/Plugin/Example.hs +++ b/plugins/default/src/Ide/Plugin/Example.hs @@ -22,8 +22,8 @@ import Control.Monad.IO.Class import Control.Monad.Trans.Maybe import Data.Aeson import Data.Functor -import qualified Data.HashMap.Strict as Map import Data.Hashable +import qualified Data.HashMap.Strict as Map import qualified Data.Text as T import Data.Typeable import Development.IDE as D diff --git a/plugins/default/src/Ide/Plugin/Example2.hs b/plugins/default/src/Ide/Plugin/Example2.hs index 6595ce58a6..8ba3a69b68 100644 --- a/plugins/default/src/Ide/Plugin/Example2.hs +++ b/plugins/default/src/Ide/Plugin/Example2.hs @@ -22,8 +22,8 @@ import Control.Monad.IO.Class import Control.Monad.Trans.Maybe import Data.Aeson import Data.Functor -import qualified Data.HashMap.Strict as Map import Data.Hashable +import qualified Data.HashMap.Strict as Map import qualified Data.Text as T import Data.Typeable import Development.IDE as D diff --git a/plugins/default/src/Ide/Plugin/ExampleCabal.hs b/plugins/default/src/Ide/Plugin/ExampleCabal.hs index 74f7982393..39a64f220a 100644 --- a/plugins/default/src/Ide/Plugin/ExampleCabal.hs +++ b/plugins/default/src/Ide/Plugin/ExampleCabal.hs @@ -1,21 +1,21 @@ -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TupleSections #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE ViewPatterns #-} module Ide.Plugin.ExampleCabal where import Control.Monad.IO.Class import Data.Aeson -import qualified Data.HashMap.Strict as Map -import qualified Data.Text as T -import Development.IDE as D hiding (pluginHandlers) +import qualified Data.HashMap.Strict as Map +import qualified Data.Text as T +import Development.IDE as D hiding (pluginHandlers) import GHC.Generics import Ide.PluginUtils import Ide.Types diff --git a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Literals.hs b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Literals.hs index 41fe0967e8..980d25685a 100644 --- a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Literals.hs +++ b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Literals.hs @@ -16,9 +16,9 @@ import Data.Text (Text) import qualified Data.Text as T import Development.IDE.GHC.Compat hiding (getSrcSpan) import Development.IDE.Graph.Classes (NFData (rnf)) -import qualified GHC.Generics as GHC import Generics.SYB (Data, Typeable, everything, extQ) +import qualified GHC.Generics as GHC -- data type to capture what type of literal we are dealing with -- provides location and possibly source text (for OverLits) as well as it's value diff --git a/plugins/hls-eval-plugin/src/Ide/Plugin/Eval/CodeLens.hs b/plugins/hls-eval-plugin/src/Ide/Plugin/Eval/CodeLens.hs index 4f265bbce2..2ed90bab48 100644 --- a/plugins/hls-eval-plugin/src/Ide/Plugin/Eval/CodeLens.hs +++ b/plugins/hls-eval-plugin/src/Ide/Plugin/Eval/CodeLens.hs @@ -23,101 +23,127 @@ module Ide.Plugin.Eval.CodeLens ( evalCommand, ) where -import Control.Applicative (Alternative ((<|>))) -import Control.Arrow (second, (>>>)) -import Control.Exception (try) -import qualified Control.Exception as E -import Control.Lens (_1, _3, ix, (%~), (<&>), (^.)) -import Control.Monad (guard, join, void, when) -import Control.Monad.IO.Class (MonadIO (liftIO)) -import Control.Monad.Trans (lift) -import Control.Monad.Trans.Except (ExceptT (..)) -import Data.Aeson (toJSON) -import Data.Char (isSpace) +import Control.Applicative (Alternative ((<|>))) +import Control.Arrow (second, (>>>)) +import Control.Exception (try) +import qualified Control.Exception as E +import Control.Lens (_1, _3, ix, (%~), + (<&>), (^.)) +import Control.Monad (guard, join, + void, when) +import Control.Monad.IO.Class (MonadIO (liftIO)) +import Control.Monad.Trans (lift) +import Control.Monad.Trans.Except (ExceptT (..)) +import Data.Aeson (toJSON) +import Data.Char (isSpace) import Data.Default -import qualified Data.HashMap.Strict as HashMap -import Data.List (dropWhileEnd, find, - intercalate, intersperse) -import Data.Maybe (catMaybes, fromMaybe) -import Data.String (IsString) -import Data.Text (Text) -import qualified Data.Text as T -import Data.Time (getCurrentTime) -import Data.Typeable (Typeable) -import Development.IDE (GetModSummary (..), - GetDependencyInformation (..), - GetLinkable (..), - GhcSessionIO (..), IdeState, - ModSummaryResult (..), - NeedsCompilation (NeedsCompilation), - VFSModified (..), evalGhcEnv, - hscEnvWithImportPaths, - printOutputable, runAction, - linkableHomeMod, - textToStringBuffer, - toNormalizedFilePath', - uriToFilePath', useNoFile_, - useWithStale_, use_, uses_) -import Development.IDE.Core.Rules (GhcSessionDepsConfig (..), - ghcSessionDepsDefinition) -import Development.IDE.Import.DependencyInformation ( reachableModules ) -import Development.IDE.GHC.Compat hiding (typeKind, unitState) -import qualified Development.IDE.GHC.Compat as Compat -import qualified Development.IDE.GHC.Compat as SrcLoc -import Development.IDE.GHC.Compat.Util (GhcException, - OverridingBool (..)) +import qualified Data.HashMap.Strict as HashMap +import Data.List (dropWhileEnd, + find, + intercalate, + intersperse) +import Data.Maybe (catMaybes, + fromMaybe) +import Data.String (IsString) +import Data.Text (Text) +import qualified Data.Text as T +import Data.Time (getCurrentTime) +import Data.Typeable (Typeable) +import Development.IDE (GetDependencyInformation (..), + GetLinkable (..), + GetModSummary (..), + GhcSessionIO (..), + IdeState, + ModSummaryResult (..), + NeedsCompilation (NeedsCompilation), + VFSModified (..), + evalGhcEnv, + hscEnvWithImportPaths, + linkableHomeMod, + printOutputable, + runAction, + textToStringBuffer, + toNormalizedFilePath', + uriToFilePath', + useNoFile_, + useWithStale_, + use_, uses_) +import Development.IDE.Core.Rules (GhcSessionDepsConfig (..), + ghcSessionDepsDefinition) +import Development.IDE.GHC.Compat hiding (typeKind, + unitState) +import qualified Development.IDE.GHC.Compat as Compat +import qualified Development.IDE.GHC.Compat as SrcLoc +import Development.IDE.GHC.Compat.Util (GhcException, + OverridingBool (..)) +import Development.IDE.Import.DependencyInformation (reachableModules) import Development.IDE.Types.Options -import GHC (ClsInst, - ExecOptions (execLineNumber, execSourceFile), - FamInst, GhcMonad, - LoadHowMuch (LoadAllTargets), - NamedThing (getName), - defaultFixity, execOptions, - exprType, getInfo, - getInteractiveDynFlags, - isImport, isStmt, load, - parseName, pprFamInst, - pprInstance, setTargets, - typeKind) +import GHC (ClsInst, + ExecOptions (execLineNumber, execSourceFile), + FamInst, + GhcMonad, + LoadHowMuch (LoadAllTargets), + NamedThing (getName), + defaultFixity, + execOptions, + exprType, + getInfo, + getInteractiveDynFlags, + isImport, isStmt, + load, parseName, + pprFamInst, + pprInstance, + setTargets, + typeKind) #if MIN_VERSION_ghc(9,2,0) -import GHC (Fixity) +import GHC (Fixity) #endif -import qualified GHC.LanguageExtensions.Type as LangExt (Extension (..)) +import qualified GHC.LanguageExtensions.Type as LangExt (Extension (..)) -import Development.IDE.Core.FileStore (setSomethingModified) -import Development.IDE.Types.Shake (toKey) -import Ide.Plugin.Config (Config) +import Development.IDE.Core.FileStore (setSomethingModified) +import Development.IDE.Types.Shake (toKey) +import Ide.Plugin.Config (Config) #if MIN_VERSION_ghc(9,2,0) -import GHC.Types.SrcLoc (UnhelpfulSpanReason (UnhelpfulInteractive)) +import GHC.Types.SrcLoc (UnhelpfulSpanReason (UnhelpfulInteractive)) #endif -import Ide.Plugin.Eval.Code (Statement, asStatements, - evalSetup, myExecStmt, - propSetup, resultRange, - testCheck, testRanges) -import Ide.Plugin.Eval.Config (EvalConfig (..), - getEvalConfig) -import Ide.Plugin.Eval.GHC (addImport, addPackages, - hasPackage, showDynFlags) -import Ide.Plugin.Eval.Parse.Comments (commentsToSections) -import Ide.Plugin.Eval.Parse.Option (parseSetFlags) -import Ide.Plugin.Eval.Rules (queueForEvaluation) +import Ide.Plugin.Eval.Code (Statement, + asStatements, + evalSetup, + myExecStmt, + propSetup, + resultRange, + testCheck, + testRanges) +import Ide.Plugin.Eval.Config (EvalConfig (..), + getEvalConfig) +import Ide.Plugin.Eval.GHC (addImport, + addPackages, + hasPackage, + showDynFlags) +import Ide.Plugin.Eval.Parse.Comments (commentsToSections) +import Ide.Plugin.Eval.Parse.Option (parseSetFlags) +import Ide.Plugin.Eval.Rules (queueForEvaluation) import Ide.Plugin.Eval.Types -import Ide.Plugin.Eval.Util (gStrictTry, isLiterate, - logWith, response', timed) -import Ide.PluginUtils (handleMaybe, handleMaybeM, - pluginResponse) +import Ide.Plugin.Eval.Util (gStrictTry, + isLiterate, + logWith, + response', timed) +import Ide.PluginUtils (handleMaybe, + handleMaybeM, + pluginResponse) import Ide.Types import Language.LSP.Server -import Language.LSP.Types hiding - (SemanticTokenAbsolute (length, line), - SemanticTokenRelative (length)) -import Language.LSP.Types.Lens (end, line) -import Language.LSP.VFS (virtualFileText) +import Language.LSP.Types hiding + (SemanticTokenAbsolute (length, line), + SemanticTokenRelative (length)) +import Language.LSP.Types.Lens (end, line) +import Language.LSP.VFS (virtualFileText) #if MIN_VERSION_ghc(9,2,0) #elif MIN_VERSION_ghc(9,0,0) -import GHC.Driver.Session (unitDatabases, unitState) -import GHC.Types.SrcLoc (UnhelpfulSpanReason (UnhelpfulInteractive)) +import GHC.Driver.Session (unitDatabases, + unitState) +import GHC.Types.SrcLoc (UnhelpfulSpanReason (UnhelpfulInteractive)) #else import DynFlags #endif diff --git a/plugins/hls-eval-plugin/src/Ide/Plugin/Eval/Config.hs b/plugins/hls-eval-plugin/src/Ide/Plugin/Eval/Config.hs index ca7b0cca9b..8ff948ba77 100644 --- a/plugins/hls-eval-plugin/src/Ide/Plugin/Eval/Config.hs +++ b/plugins/hls-eval-plugin/src/Ide/Plugin/Eval/Config.hs @@ -15,8 +15,8 @@ import Language.LSP.Server (MonadLsp) -- | The Eval plugin configuration. (see 'properties') data EvalConfig = EvalConfig - { eval_cfg_diff :: Bool - , eval_cfg_exception :: Bool + { eval_cfg_diff :: Bool + , eval_cfg_exception :: Bool } deriving (Eq, Ord, Show) diff --git a/plugins/hls-eval-plugin/src/Ide/Plugin/Eval/Parse/Comments.hs b/plugins/hls-eval-plugin/src/Ide/Plugin/Eval/Parse/Comments.hs index c937d631f3..f3479fa42c 100644 --- a/plugins/hls-eval-plugin/src/Ide/Plugin/Eval/Parse/Comments.hs +++ b/plugins/hls-eval-plugin/src/Ide/Plugin/Eval/Parse/Comments.hs @@ -41,8 +41,8 @@ import Ide.Plugin.Eval.Types import Language.LSP.Types (UInt) import Language.LSP.Types.Lens (character, end, line, start) -import Text.Megaparsec import qualified Text.Megaparsec as P +import Text.Megaparsec import Text.Megaparsec.Char (alphaNumChar, char, eol, hspace, letterChar) diff --git a/plugins/hls-eval-plugin/test/Main.hs b/plugins/hls-eval-plugin/test/Main.hs index a505a2486d..a16e405706 100644 --- a/plugins/hls-eval-plugin/test/Main.hs +++ b/plugins/hls-eval-plugin/test/Main.hs @@ -12,7 +12,7 @@ import Control.Lens (_Just, folded, preview, toListOf, view, (^..)) import Data.Aeson (Value (Object), fromJSON, object, toJSON, (.=)) -import Data.Aeson.Types (Result (Success), Pair) +import Data.Aeson.Types (Pair, Result (Success)) import Data.List (isInfixOf) import Data.List.Extra (nubOrdOn) import qualified Data.Map as Map diff --git a/plugins/hls-explicit-imports-plugin/src/Ide/Plugin/ExplicitImports.hs b/plugins/hls-explicit-imports-plugin/src/Ide/Plugin/ExplicitImports.hs index 09743f7e0c..9c96678d5a 100644 --- a/plugins/hls-explicit-imports-plugin/src/Ide/Plugin/ExplicitImports.hs +++ b/plugins/hls-explicit-imports-plugin/src/Ide/Plugin/ExplicitImports.hs @@ -28,8 +28,8 @@ import Data.IORef (readIORef) import qualified Data.Map.Strict as Map import Data.Maybe (catMaybes, fromMaybe, isJust) -import qualified Data.Text as T import Data.String (fromString) +import qualified Data.Text as T import Development.IDE hiding (pluginHandlers, pluginRules) import Development.IDE.Core.PositionMapping diff --git a/plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs b/plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs index 9e91b6348d..55685778f4 100644 --- a/plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs +++ b/plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs @@ -35,8 +35,9 @@ import Development.IDE (GetParsedModule (GetParsedModule), uriToFilePath', use, use_) import Development.IDE.GHC.Compat (GenLocated (L), getSessionDynFlags, hsmodName, importPaths, locA, + moduleNameString, pattern RealSrcSpan, - pm_parsed_source, unLoc, moduleNameString) + pm_parsed_source, unLoc) import Ide.Types import Language.LSP.Server import Language.LSP.Types hiding diff --git a/shake-bench/src/Development/Benchmark/Rules.hs b/shake-bench/src/Development/Benchmark/Rules.hs index d6fc885d1c..a68507e604 100644 --- a/shake-bench/src/Development/Benchmark/Rules.hs +++ b/shake-bench/src/Development/Benchmark/Rules.hs @@ -67,14 +67,15 @@ module Development.Benchmark.Rules ) where import Control.Applicative -import Control.Lens ((^.), view, preview) +import Control.Lens (preview, view, (^.)) import Control.Monad import qualified Control.Monad.State as S import Data.Aeson (FromJSON (..), ToJSON (..), Value (..), object, (.!=), (.:?), (.=)) -import Data.Aeson.Lens (_Object, AsJSON (_JSON), _String) +import Data.Aeson.Lens (AsJSON (_JSON), + _Object, _String) import Data.Char (isDigit) import Data.List (find, isInfixOf, stripPrefix, @@ -562,9 +563,9 @@ instance Read Frame where -- | A file path containing the output of -S for a given run data RunLog = RunLog - { runVersion :: !String, - runFrames :: ![Frame], - runSuccess :: !Bool, + { runVersion :: !String, + runFrames :: ![Frame], + runSuccess :: !Bool, runFirstReponse :: !(Maybe Seconds) } From 488d287455813561aab8d9454d33e6bc39a238ea Mon Sep 17 00:00:00 2001 From: Dmitry Pogodin Date: Fri, 29 Jul 2022 07:03:09 +0100 Subject: [PATCH 037/213] Add Github precommit workflow (#3060) * Add Github precommit workflow * Reformat remaining files * Don't shorten project name * Fix pre-commit file exclusion pattern --- .github/workflows/pre-commit.yml | 43 +++++++++++++++++++ .pre-commit-config.yaml | 2 +- .../hls-stan-plugin/src/Ide/Plugin/Stan.hs | 6 +-- 3 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/pre-commit.yml diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000000..fe363ef650 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,43 @@ +name: pre-commit + +on: + pull_request: + push: + branches: [master] + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: ./.github/actions/setup-build + with: + ghc: "9.2.3" + os: ${{ runner.os }} + shorten-hls: false + + - name: "Install stylish-haskell" + run: | + cabal install stylish-haskell + echo "${HOME}/.cabal/bin" >> $GITHUB_PATH + + - name: "Check stylish-haskell is available" + run: | + echo $(which stylish-haskell) + + - name: Compiled deps cache + id: stylish-haskell-compiled-cache + uses: actions/cache@v2 + env: + cache-name: stylish-haskell-compiled-cache + with: + path: ${{ env.CABAL_PKGS_DIR }} + key: ${{ inputs.cache-prefix }}${{ env.cache-name }}-${{ inputs.os }}-${{ inputs.ghc }}-${{ env.INDEX_STATE }}-${{ hashFiles('cabal.project.freeze') }} + restore-keys: | + ${{ inputs.cache-prefix }}${{ env.cache-name }}-${{ inputs.os }}-${{ inputs.ghc }}-${{ env.INDEX_STATE }}- + ${{ inputs.cache-prefix }}${{ env.cache-name }}-${{ inputs.os }}-${{ inputs.ghc }}- + ${{ inputs.cache-prefix }}${{ env.cache-name }}-${{ inputs.os }}- + + - uses: actions/setup-python@v3 + - uses: pre-commit/action@v3.0.0 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b2bac28fcd..ba541e83e3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ { "id": "mixed-line-ending", "args": ["--fix", "lf"], - "exclude": "test/testdata/.*CRLF*.hs$" + "exclude": "test/testdata/.*CRLF.*?\\.hs$" } ] } diff --git a/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs b/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs index 0d3607a6b3..7eb46e05b0 100644 --- a/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs +++ b/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs @@ -6,8 +6,8 @@ import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Maybe (MaybeT (MaybeT), runMaybeT) import Data.Foldable (toList) -import qualified Data.HashMap.Strict as HM import Data.Hashable (Hashable) +import qualified Data.HashMap.Strict as HM import qualified Data.Map as Map import Data.Maybe (fromJust, mapMaybe) import qualified Data.Text as T @@ -24,9 +24,9 @@ import Development.IDE (Action, FileDiagnostic, getFilesOfInterestUntracked, hscEnv, msrModSummary, tmrTypechecked, use, uses) +import Development.IDE.Core.Rules (getHieFile, + getSourceFileSource) import Development.IDE.Core.RuleTypes (HieAstResult (..)) -import Development.IDE.Core.Rules (getSourceFileSource, - getHieFile) import qualified Development.IDE.Core.Shake as Shake import Development.IDE.GHC.Compat (HieASTs (HieASTs), RealSrcSpan (..), mkHieFile', From dc682ba118783be5e0770fd76ac93a0ee2012bd2 Mon Sep 17 00:00:00 2001 From: Kobayashi Date: Fri, 29 Jul 2022 18:28:31 +0800 Subject: [PATCH 038/213] add a prefix to plugin CPP (#3065) --- exe/Plugins.hs | 97 +++++++++++++++++------------------ haskell-language-server.cabal | 56 ++++++++++---------- 2 files changed, 76 insertions(+), 77 deletions(-) diff --git a/exe/Plugins.hs b/exe/Plugins.hs index f0cd3c208b..561c8541a8 100644 --- a/exe/Plugins.hs +++ b/exe/Plugins.hs @@ -16,101 +16,100 @@ import qualified Ide.Plugin.Example2 as Example2 import qualified Ide.Plugin.ExampleCabal as ExampleCabal -- haskell-language-server optional plugins -#if qualifyImportedNames +#if hls_qualifyImportedNames import qualified Ide.Plugin.QualifyImportedNames as QualifyImportedNames #endif -#if callHierarchy +#if hls_callHierarchy import qualified Ide.Plugin.CallHierarchy as CallHierarchy #endif -#if class +#if hls_class import qualified Ide.Plugin.Class as Class #endif -#if haddockComments +#if hls_haddockComments import qualified Ide.Plugin.HaddockComments as HaddockComments #endif -#if eval +#if hls_eval import qualified Ide.Plugin.Eval as Eval #endif -#if importLens +#if hls_importLens import qualified Ide.Plugin.ExplicitImports as ExplicitImports #endif -#if refineImports +#if hls_refineImports import qualified Ide.Plugin.RefineImports as RefineImports #endif -#if rename +#if hls_rename import qualified Ide.Plugin.Rename as Rename #endif -#if retrie +#if hls_retrie import qualified Ide.Plugin.Retrie as Retrie #endif -#if tactic +#if hls_tactic import qualified Ide.Plugin.Tactic as Tactic #endif -#if hlint +#if hls_hlint import qualified Ide.Plugin.Hlint as Hlint #endif -#if stan +#if hls_stan import qualified Ide.Plugin.Stan as Stan #endif - -#if moduleName +#if hls_moduleName import qualified Ide.Plugin.ModuleName as ModuleName #endif -#if pragmas +#if hls_pragmas import qualified Ide.Plugin.Pragmas as Pragmas #endif -#if splice +#if hls_splice import qualified Ide.Plugin.Splice as Splice #endif -#if alternateNumberFormat +#if hls_alternateNumberFormat import qualified Ide.Plugin.AlternateNumberFormat as AlternateNumberFormat #endif -#if codeRange +#if hls_codeRange import qualified Ide.Plugin.CodeRange as CodeRange #endif -#if changeTypeSignature +#if hls_changeTypeSignature import Ide.Plugin.ChangeTypeSignature as ChangeTypeSignature #endif -#if gadt +#if hls_gadt import Ide.Plugin.GADT as GADT #endif -- formatters -#if floskell +#if hls_floskell import qualified Ide.Plugin.Floskell as Floskell #endif -#if fourmolu +#if hls_fourmolu import qualified Ide.Plugin.Fourmolu as Fourmolu #endif -#if ormolu +#if hls_ormolu import qualified Ide.Plugin.Ormolu as Ormolu #endif -#if stylishHaskell +#if hls_stylishHaskell import qualified Ide.Plugin.StylishHaskell as StylishHaskell #endif -#if brittany +#if hls_brittany import qualified Ide.Plugin.Brittany as Brittany #endif @@ -135,76 +134,76 @@ idePlugins recorder includeExamples = pluginDescToIdePlugins allPlugins then basePlugins ++ examplePlugins else basePlugins basePlugins = -#if pragmas +#if hls_pragmas Pragmas.descriptor "pragmas" : #endif -#if floskell +#if hls_floskell Floskell.descriptor "floskell" : #endif -#if fourmolu +#if hls_fourmolu Fourmolu.descriptor pluginRecorder "fourmolu" : #endif -#if tactic +#if hls_tactic Tactic.descriptor pluginRecorder "tactics" : #endif -#if ormolu +#if hls_ormolu Ormolu.descriptor "ormolu" : #endif -#if stylishHaskell +#if hls_stylishHaskell StylishHaskell.descriptor "stylish-haskell" : #endif -#if rename +#if hls_rename Rename.descriptor "rename" : #endif -#if retrie +#if hls_retrie Retrie.descriptor "retrie" : #endif -#if brittany +#if hls_brittany Brittany.descriptor "brittany" : #endif -#if callHierarchy +#if hls_callHierarchy CallHierarchy.descriptor : #endif -#if class +#if hls_class Class.descriptor pluginRecorder "class" : #endif -#if haddockComments +#if hls_haddockComments HaddockComments.descriptor "haddockComments" : #endif -#if eval +#if hls_eval Eval.descriptor pluginRecorder "eval" : #endif -#if importLens +#if hls_importLens ExplicitImports.descriptor pluginRecorder "importLens" : #endif -#if qualifyImportedNames +#if hls_qualifyImportedNames QualifyImportedNames.descriptor "qualifyImportedNames" : #endif -#if refineImports +#if hls_refineImports RefineImports.descriptor pluginRecorder "refineImports" : #endif -#if moduleName +#if hls_moduleName ModuleName.descriptor "moduleName" : #endif -#if hlint +#if hls_hlint Hlint.descriptor pluginRecorder "hlint" : #endif -#if stan +#if hls_stan Stan.descriptor pluginRecorder "stan" : #endif -#if splice +#if hls_splice Splice.descriptor "splice" : #endif -#if alternateNumberFormat +#if hls_alternateNumberFormat AlternateNumberFormat.descriptor pluginRecorder : #endif -#if codeRange +#if hls_codeRange CodeRange.descriptor pluginRecorder "codeRange" : #endif -#if changeTypeSignature +#if hls_changeTypeSignature ChangeTypeSignature.descriptor : #endif -#if gadt +#if hls_gadt GADT.descriptor "gadt" : #endif -- The ghcide descriptors should come last so that the notification handlers diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 28193e011d..4618e156e4 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -237,124 +237,124 @@ common example-plugins common class if flag(class) build-depends: hls-class-plugin ^>= 1.0 - cpp-options: -Dclass + cpp-options: -Dhls_class common callHierarchy if flag(callHierarchy) build-depends: hls-call-hierarchy-plugin ^>= 1.0 - cpp-options: -DcallHierarchy + cpp-options: -Dhls_callHierarchy common haddockComments if flag(haddockComments) && (impl(ghc < 9.2.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-haddock-comments-plugin ^>= 1.0 - cpp-options: -DhaddockComments + cpp-options: -Dhls_haddockComments common eval if flag(eval) build-depends: hls-eval-plugin ^>= 1.2 - cpp-options: -Deval + cpp-options: -Dhls_eval common importLens if flag(importLens) build-depends: hls-explicit-imports-plugin ^>= 1.1 - cpp-options: -DimportLens + cpp-options: -Dhls_importLens common refineImports if flag(refineImports) build-depends: hls-refine-imports-plugin ^>=1.0 - cpp-options: -DrefineImports + cpp-options: -Dhls_refineImports common rename if flag(rename) build-depends: hls-rename-plugin ^>= 1.0 - cpp-options: -Drename + cpp-options: -Dhls_rename common retrie if flag(retrie) && (impl(ghc < 9.2.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-retrie-plugin ^>= 1.0 - cpp-options: -Dretrie + cpp-options: -Dhls_retrie common tactic if flag(tactic) && (impl(ghc < 9.2.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-tactics-plugin >=1.2.0.0 && <1.7 - cpp-options: -Dtactic + cpp-options: -Dhls_tactic common hlint if flag(hlint) build-depends: hls-hlint-plugin ^>= 1.0 - cpp-options: -Dhlint + cpp-options: -Dhls_hlint common stan if flag(stan) && (impl(ghc >= 8.8) && impl(ghc < 9.0)) build-depends: hls-stan-plugin ^>= 1.0 - cpp-options: -Dstan + cpp-options: -Dhls_stan common moduleName if flag(moduleName) build-depends: hls-module-name-plugin ^>= 1.0 - cpp-options: -DmoduleName + cpp-options: -Dhls_moduleName common pragmas if flag(pragmas) build-depends: hls-pragmas-plugin ^>= 1.0 - cpp-options: -Dpragmas + cpp-options: -Dhls_pragmas common splice if flag(splice) && (impl(ghc < 9.2.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-splice-plugin ^>=1.0.0.1 - cpp-options: -Dsplice + cpp-options: -Dhls_splice common alternateNumberFormat if flag(alternateNumberFormat) build-depends: hls-alternate-number-format-plugin ^>= 1.1 - cpp-options: -DalternateNumberFormat + cpp-options: -Dhls_alternateNumberFormat common qualifyImportedNames if flag(qualifyImportedNames) build-depends: hls-qualify-imported-names-plugin ^>=1.0 - cpp-options: -DqualifyImportedNames + cpp-options: -Dhls_qualifyImportedNames common codeRange if flag(codeRange) build-depends: hls-code-range-plugin ^>= 1.0 - cpp-options: -DcodeRange + cpp-options: -Dhls_codeRange common changeTypeSignature if flag(changeTypeSignature) build-depends: hls-change-type-signature-plugin ^>= 1.0 - cpp-options: -DchangeTypeSignature + cpp-options: -Dhls_changeTypeSignature common gadt if flag(gadt) build-depends: hls-gadt-plugin ^>= 1.0 - cpp-options: -Dgadt + cpp-options: -Dhls_gadt -- formatters common floskell if flag(floskell) build-depends: hls-floskell-plugin ^>= 1.0 - cpp-options: -Dfloskell + cpp-options: -Dhls_floskell common fourmolu if flag(fourmolu) build-depends: hls-fourmolu-plugin ^>= 1.0 - cpp-options: -Dfourmolu + cpp-options: -Dhls_fourmolu common ormolu if flag(ormolu) build-depends: hls-ormolu-plugin ^>= 1.0 - cpp-options: -Dormolu + cpp-options: -Dhls_ormolu common stylishHaskell if flag(stylishHaskell) build-depends: hls-stylish-haskell-plugin ^>= 1.0 - cpp-options: -DstylishHaskell + cpp-options: -Dhls_stylishHaskell common brittany if flag(brittany) && (impl(ghc < 9.0.2) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-brittany-plugin ^>= 1.0 - cpp-options: -Dbrittany + cpp-options: -Dhls_brittany executable haskell-language-server import: common-deps @@ -543,14 +543,14 @@ test-suite func-test -- Duplicating inclusion plugin conditions until tests are moved to their own packages if flag(eval) - cpp-options: -Deval + cpp-options: -Dhls_eval -- formatters if flag(floskell) && (impl(ghc < 9.2.1) || flag(ignore-plugins-ghc-bounds)) - cpp-options: -Dfloskell + cpp-options: -Dhls_floskell if flag(fourmolu) - cpp-options: -Dfourmolu + cpp-options: -Dhls_fourmolu if flag(ormolu) - cpp-options: -Dormolu + cpp-options: -Dhls_ormolu test-suite wrapper-test import: common-deps From a89ba412293327ec838c00d899ea9c6013c46aa3 Mon Sep 17 00:00:00 2001 From: Julian Ospald Date: Sat, 30 Jul 2022 17:45:05 +0800 Subject: [PATCH 039/213] Simplify instructions about 'ghcup compile hls' (#3068) --- docs/installation.md | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index e231fb9ee6..cc107a2315 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -19,27 +19,10 @@ You can check if HLS is available for your platform via `ghcup` here: ## Installation from source From f3372714cd08afe4697a46cb08b76a71f5e9e679 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 02:22:38 +0000 Subject: [PATCH 040/213] Bump actions/cache from 2 to 3 Bumps [actions/cache](https://github.com/actions/cache) from 2 to 3. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/pre-commit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index fe363ef650..b978463ec9 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -28,7 +28,7 @@ jobs: - name: Compiled deps cache id: stylish-haskell-compiled-cache - uses: actions/cache@v2 + uses: actions/cache@v3 env: cache-name: stylish-haskell-compiled-cache with: From 5efd0946ada3054299482adb6e223d86edbf6a51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 06:43:32 +0000 Subject: [PATCH 041/213] Bump actions/setup-python from 3 to 4 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 3 to 4. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/pre-commit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index b978463ec9..c536f8c48a 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -39,5 +39,5 @@ jobs: ${{ inputs.cache-prefix }}${{ env.cache-name }}-${{ inputs.os }}-${{ inputs.ghc }}- ${{ inputs.cache-prefix }}${{ env.cache-name }}-${{ inputs.os }}- - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 - uses: pre-commit/action@v3.0.0 From f3225adea546c0528ece30a8e06c20c84278e813 Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Tue, 2 Aug 2022 16:44:57 +0200 Subject: [PATCH 042/213] Run the benchmark suite on GHC 9.0.2 and 9.2.3 (#3069) * Run the benchmark suite in GHC 9.0.2 and 9.2.3 * Benchmark Cabal 3.6.3.0 Cabal 3.0.0.0 doesn't build with GHC 9.2.x --- .github/workflows/bench.yml | 4 ++-- ghcide/bench/config.yaml | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 52959d946c..81b31923da 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -46,7 +46,7 @@ jobs: strategy: fail-fast: false matrix: - ghc: ['8.10.7'] + ghc: ['8.10.7', '9.2.3'] os: [ubuntu-latest] # This code is fitted to the strategy: assumes Linux is used ... etc, @@ -103,7 +103,7 @@ jobs: strategy: fail-fast: false matrix: - ghc: ['8.10.7'] + ghc: ['8.10.7', '9.2.3'] os: [ubuntu-latest] cabal: ['3.6'] example: ['cabal', 'lsp-types'] diff --git a/ghcide/bench/config.yaml b/ghcide/bench/config.yaml index 5071a37141..bcec13dfbe 100644 --- a/ghcide/bench/config.yaml +++ b/ghcide/bench/config.yaml @@ -14,34 +14,34 @@ examples: # Medium-sized project without TH - name: cabal package: Cabal - version: 3.0.0.0 + version: 3.6.3.0 modules: - - Distribution/Simple.hs - - Distribution/Types/Module.hs + - src/Distribution/Simple.hs + - src/Distribution/Types/Module.hs extra-args: [] # extra ghcide command line args - name: cabal-1module package: Cabal - version: 3.0.0.0 + version: 3.6.3.0 modules: - - Distribution/Simple.hs + - src/Distribution/Simple.hs - name: cabal-conservative package: Cabal - version: 3.0.0.0 + version: 3.6.3.0 modules: - - Distribution/Simple.hs - - Distribution/Types/Module.hs + - src/Distribution/Simple.hs + - src/Distribution/Types/Module.hs extra-args: # extra ghcide command line args - --conservative-change-tracking # Small-sized project with TH - name: lsp-types package: lsp-types - version: 1.0.0.1 + version: 1.5.0.0 modules: - src/Language/LSP/VFS.hs - src/Language/LSP/Types/Lens.hs - name: lsp-types-conservative package: lsp-types - version: 1.0.0.1 + version: 1.5.0.0 modules: - src/Language/LSP/VFS.hs - src/Language/LSP/Types/Lens.hs From d965e02ac602f12ff592e7671724fb6a31d1671b Mon Sep 17 00:00:00 2001 From: Brandon Chinn Date: Fri, 5 Aug 2022 18:58:54 -0700 Subject: [PATCH 043/213] Break out fmap --- plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs b/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs index d1d8565dad..f22b64d020 100644 --- a/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs +++ b/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs @@ -16,7 +16,7 @@ import Control.Exception (IOException, try) import Control.Lens ((^.)) import Control.Monad import Control.Monad.IO.Class -import Data.Bifunctor (first) +import Data.Bifunctor (bimap, first) import Data.Maybe import Data.Text (Text) import qualified Data.Text as T @@ -100,8 +100,8 @@ provider recorder plId ideState typ contents fp fo = withIndefiniteProgress titl pure . Left . responseError $ "Fourmolu failed with exit code " <> T.pack (show n) else do let format fourmoluConfig = - first (mkError . show) - <$> try @OrmoluException (makeDiffTextEdit contents <$> ormolu config fp' (T.unpack contents)) + bimap (mkError . show) (makeDiffTextEdit contents) + <$> try @OrmoluException (ormolu config fp' (T.unpack contents)) where printerOpts = #if MIN_VERSION_fourmolu(0,7,0) From 425763a589cbafcdd8bb1b09597845cf12fe33ab Mon Sep 17 00:00:00 2001 From: Brandon Chinn Date: Fri, 5 Aug 2022 19:19:44 -0700 Subject: [PATCH 044/213] Consolidate shims --- .../hls-fourmolu-plugin.cabal | 4 +- .../src/Ide/Plugin/Fourmolu.hs | 39 ++--------- .../src/Ide/Plugin/Fourmolu/Shim.hs | 68 +++++++++++++++++++ 3 files changed, 77 insertions(+), 34 deletions(-) create mode 100644 plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu/Shim.hs diff --git a/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal b/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal index 96eaf7dc64..43c6a83377 100644 --- a/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal +++ b/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal @@ -23,7 +23,9 @@ source-repository head location: git://github.com/haskell/haskell-language-server.git library - exposed-modules: Ide.Plugin.Fourmolu + exposed-modules: + Ide.Plugin.Fourmolu + , Ide.Plugin.Fourmolu.Shim hs-source-dirs: src ghc-options: -Wall build-depends: diff --git a/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs b/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs index f22b64d020..f477b97d29 100644 --- a/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs +++ b/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DisambiguateRecordFields #-} {-# LANGUAGE LambdaCase #-} @@ -25,6 +24,7 @@ import Development.IDE.GHC.Compat as Compat hiding (Cpp, Warning, hang, vcat) import qualified Development.IDE.GHC.Compat.Util as S import GHC.LanguageExtensions.Type (Extension (Cpp)) +import Ide.Plugin.Fourmolu.Shim import Ide.Plugin.Properties import Ide.PluginUtils (makeDiffTextEdit, usePropertyLsp) @@ -33,7 +33,6 @@ import Language.LSP.Server hiding (defaultConfig) import Language.LSP.Types hiding (line) import Language.LSP.Types.Lens (HasTabSize (tabSize)) import Ormolu -import Ormolu.Config import System.Exit import System.FilePath import System.Process.Run (cwd, proc) @@ -103,14 +102,9 @@ provider recorder plId ideState typ contents fp fo = withIndefiniteProgress titl bimap (mkError . show) (makeDiffTextEdit contents) <$> try @OrmoluException (ormolu config fp' (T.unpack contents)) where - printerOpts = -#if MIN_VERSION_fourmolu(0,7,0) - cfgFilePrinterOpts fourmoluConfig -#else - fourmoluConfig - -#endif + printerOpts = cfgFilePrinterOpts fourmoluConfig config = + addFixityOverrides (cfgFileFixities fourmoluConfig) $ defaultConfig { cfgDynOptions = map DynOption fileOpts , cfgRegion = region @@ -119,29 +113,14 @@ provider recorder plId ideState typ contents fp fo = withIndefiniteProgress titl fillMissingPrinterOpts (printerOpts <> lspPrinterOpts) defaultPrinterOpts -#if MIN_VERSION_fourmolu(0,7,0) - , cfgFixityOverrides = - cfgFileFixities fourmoluConfig -#endif } in liftIO (loadConfigFile fp') >>= \case ConfigLoaded file opts -> liftIO $ do logWith recorder Info $ ConfigPath file - format opts + format (toConfig opts) ConfigNotFound searchDirs -> liftIO $ do logWith recorder Info $ NoConfigPath searchDirs - format emptyOptions - where - emptyOptions = -#if MIN_VERSION_fourmolu(0,7,0) - FourmoluConfig - { cfgFilePrinterOpts = mempty - , cfgFileFixities = mempty - } -#else - mempty -#endif - + format emptyConfig ConfigParseError f err -> do sendNotification SWindowShowMessage $ ShowMessageParams @@ -150,13 +129,7 @@ provider recorder plId ideState typ contents fp fo = withIndefiniteProgress titl } return . Left $ responseError errorMessage where - errorMessage = "Failed to load " <> T.pack f <> ": " <> T.pack (convertErr err) - convertErr = -#if MIN_VERSION_fourmolu(0,7,0) - show -#else - snd -#endif + errorMessage = "Failed to load " <> T.pack f <> ": " <> T.pack (showParseError err) where fp' = fromNormalizedFilePath fp title = "Formatting " <> T.pack (takeFileName fp') diff --git a/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu/Shim.hs b/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu/Shim.hs new file mode 100644 index 0000000000..7859666aaf --- /dev/null +++ b/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu/Shim.hs @@ -0,0 +1,68 @@ +{-# LANGUAGE CPP #-} + +module Ide.Plugin.Fourmolu.Shim ( + -- * FourmoluConfig + FourmoluConfig (..), + toConfig, + emptyConfig, + + -- * FixityMap + addFixityOverrides, + + -- * ConfigParseError + showParseError, +) where + +import Ormolu.Config + +#if MIN_VERSION_fourmolu(0,7,0) +import Ormolu.Fixity +#endif + +{-- Backport FourmoluConfig --} + +#if MIN_VERSION_fourmolu(0,7,0) +toConfig :: FourmoluConfig -> FourmoluConfig +toConfig = id +#else +data FourmoluConfig = FourmoluConfig + { cfgFilePrinterOpts :: PrinterOptsPartial + , cfgFileFixities :: FixityMap + } + +toConfig :: PrinterOptsPartial -> FourmoluConfig +toConfig opts = + FourmoluConfig + { cfgFilePrinterOpts = opts + , cfgFileFixities = mempty + } +#endif + +emptyConfig :: FourmoluConfig +emptyConfig = + FourmoluConfig + { cfgFilePrinterOpts = mempty + , cfgFileFixities = mempty + } + +{-- Backport FixityMap --} + +#if MIN_VERSION_fourmolu(0,7,0) +addFixityOverrides :: FixityMap -> Config region -> Config region +addFixityOverrides fixities cfg = cfg{cfgFixityOverrides = fixities} +#else +type FixityMap = () + +addFixityOverrides :: FixityMap -> Config region -> Config region +addFixityOverrides _ = id +#endif + +{-- Backport ConfigParseError --} + +#if MIN_VERSION_fourmolu(0,7,0) +showParseError :: Show parseException => parseException -> String +showParseError = show +#else +showParseError :: (pos, String) -> String +showParseError = snd +#endif From 59415bb8f7618e071394816206531b7af7a417ba Mon Sep 17 00:00:00 2001 From: Brandon Chinn Date: Fri, 5 Aug 2022 20:09:19 -0700 Subject: [PATCH 045/213] Add support for fourmolu-0.8.0.0 --- plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal b/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal index 43c6a83377..bb4e0b687a 100644 --- a/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal +++ b/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal @@ -31,7 +31,7 @@ library build-depends: , base >=4.12 && <5 , filepath - , fourmolu ^>=0.3 || ^>=0.4 || ^>= 0.6 || ^>= 0.7 + , fourmolu ^>=0.3 || ^>=0.4 || ^>= 0.6 || ^>= 0.7 || ^>= 0.8 , ghc , ghc-boot-th , ghcide ^>=1.7 From abd7a1955c09129cbc2a394ad3b9f057aed2937e Mon Sep 17 00:00:00 2001 From: Brandon Chinn Date: Fri, 5 Aug 2022 20:15:34 -0700 Subject: [PATCH 046/213] Make even more forwards-compatible --- .../src/Ide/Plugin/Fourmolu.hs | 2 +- .../src/Ide/Plugin/Fourmolu/Shim.hs | 30 +++++++++---------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs b/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs index f477b97d29..d62210d71e 100644 --- a/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs +++ b/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs @@ -117,7 +117,7 @@ provider recorder plId ideState typ contents fp fo = withIndefiniteProgress titl in liftIO (loadConfigFile fp') >>= \case ConfigLoaded file opts -> liftIO $ do logWith recorder Info $ ConfigPath file - format (toConfig opts) + format opts ConfigNotFound searchDirs -> liftIO $ do logWith recorder Info $ NoConfigPath searchDirs format emptyConfig diff --git a/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu/Shim.hs b/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu/Shim.hs index 7859666aaf..976e522d93 100644 --- a/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu/Shim.hs +++ b/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu/Shim.hs @@ -2,8 +2,8 @@ module Ide.Plugin.Fourmolu.Shim ( -- * FourmoluConfig - FourmoluConfig (..), - toConfig, + cfgFilePrinterOpts, + cfgFileFixities, emptyConfig, -- * FixityMap @@ -21,29 +21,27 @@ import Ormolu.Fixity {-- Backport FourmoluConfig --} -#if MIN_VERSION_fourmolu(0,7,0) -toConfig :: FourmoluConfig -> FourmoluConfig -toConfig = id -#else -data FourmoluConfig = FourmoluConfig - { cfgFilePrinterOpts :: PrinterOptsPartial - , cfgFileFixities :: FixityMap - } +#if !MIN_VERSION_fourmolu(0,7,0) +type FourmoluConfig = PrinterOptsPartial -toConfig :: PrinterOptsPartial -> FourmoluConfig -toConfig opts = - FourmoluConfig - { cfgFilePrinterOpts = opts - , cfgFileFixities = mempty - } +cfgFilePrinterOpts :: FourmoluConfig -> PrinterOptsPartial +cfgFilePrinterOpts = id + +cfgFileFixities :: FourmoluConfig -> FixityMap +cfgFileFixities _ = mempty #endif +#if MIN_VERSION_fourmolu(0,7,0) emptyConfig :: FourmoluConfig emptyConfig = FourmoluConfig { cfgFilePrinterOpts = mempty , cfgFileFixities = mempty } +#else +emptyConfig :: FourmoluConfig +emptyConfig = mempty +#endif {-- Backport FixityMap --} From 524ef152342f37ac524e4da1e419d164316c7c8f Mon Sep 17 00:00:00 2001 From: cydparser Date: Mon, 8 Aug 2022 07:26:15 -0700 Subject: [PATCH 047/213] Bump Nix flake GHC 9.2.3 to 9.2.4 (#3081) --- configuration-ghc-90.nix | 2 +- flake.lock | 6 +++--- flake.nix | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/configuration-ghc-90.nix b/configuration-ghc-90.nix index 7c16281847..a31673afed 100644 --- a/configuration-ghc-90.nix +++ b/configuration-ghc-90.nix @@ -19,7 +19,7 @@ let ptr-poker = hself.callCabal2nix "ptr-poker" inputs.ptr-poker { }; ghc-lib = hself.ghc-lib_9_2_2_20220307; - ghc-lib-parser = hself.ghc-lib-parser_9_2_3_20220709; + ghc-lib-parser = hself.ghc-lib-parser_9_2_4_20220729; ghc-lib-parser-ex = hself.ghc-lib-parser-ex_9_2_0_4; Cabal = hself.Cabal_3_6_3_0; diff --git a/flake.lock b/flake.lock index de1c8b39ca..3823849eba 100644 --- a/flake.lock +++ b/flake.lock @@ -249,11 +249,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1657849727, - "narHash": "sha256-68J4eSwzr98r7VCzgrX/WWaQzkY7gdKqH2uSyQheYj0=", + "lastModified": 1659782844, + "narHash": "sha256-tM/qhHFE61puBxh9ebP3BIG1fkRAT4rHqD3jCM0HXGY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0b683abff06fe55755ea992ba47f2e787081a30f", + "rev": "c85e56bb060291eac3fb3c75d4e0e64f6836fcfe", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index c14a40bb49..df92770614 100644 --- a/flake.nix +++ b/flake.nix @@ -213,7 +213,7 @@ }; ghc902Config = (import ./configuration-ghc-90.nix) { inherit pkgs inputs; }; - ghc923Config = (import ./configuration-ghc-92.nix) { inherit pkgs inputs; }; + ghc924Config = (import ./configuration-ghc-92.nix) { inherit pkgs inputs; }; # GHC versions # While HLS still works fine with 8.10 GHCs, we only support the versions that are cached @@ -222,12 +222,12 @@ ghcVersion = "ghc" + (pkgs.lib.replaceStrings ["."] [""] pkgs.haskellPackages.ghc.version); cases = { ghc902 = ghc902Config.tweakHpkgs (pkgs.hlsHpkgs "ghc902"); - ghc923 = ghc923Config.tweakHpkgs (pkgs.hlsHpkgs "ghc923"); + ghc924 = ghc924Config.tweakHpkgs (pkgs.hlsHpkgs "ghc924"); }; in { default = cases."${ghcVersion}"; } // cases; ghc902 = supportedGHCs.ghc902; - ghc923 = supportedGHCs.ghc923; + ghc924 = supportedGHCs.ghc924; ghcDefault = supportedGHCs.default; # For markdown support @@ -359,20 +359,20 @@ simpleDevShells = { haskell-language-server-dev = mkDevShell ghcDefault "cabal.project"; haskell-language-server-902-dev = mkDevShell ghc902 "cabal.project"; - haskell-language-server-923-dev = mkDevShell ghc923 "cabal.project"; + haskell-language-server-924-dev = mkDevShell ghc924 "cabal.project"; }; # Developement shell, haskell packages are also provided by nix nixDevShells = { haskell-language-server-dev-nix = mkDevShellWithNixDeps ghcDefault "cabal.project"; haskell-language-server-902-dev-nix = mkDevShellWithNixDeps ghc902 "cabal.project"; - haskell-language-server-923-dev-nix = mkDevShellWithNixDeps ghc923 "cabal.project"; + haskell-language-server-924-dev-nix = mkDevShellWithNixDeps ghc924 "cabal.project"; }; allPackages = { haskell-language-server = mkExe ghcDefault; haskell-language-server-902 = mkExe ghc902; - haskell-language-server-923 = mkExe ghc923; + haskell-language-server-924 = mkExe ghc924; }; devShells = simpleDevShells // nixDevShells // { From df77e7dd2de87702f5a02b7d94b2bb6e3bd7caf9 Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Thu, 11 Aug 2022 16:54:02 +0800 Subject: [PATCH 048/213] Support ghc-9.2.4 (#3085) * Support ghc-9.2.4 * Restore caching workflow for ghc-9.2.3 --- .circleci/config.yml | 6 ------ .github/actions/setup-build/action.yml | 21 --------------------- .github/workflows/bench.yml | 4 ++-- .github/workflows/caching.yml | 7 ++----- .github/workflows/flags.yml | 4 ++-- .github/workflows/pre-commit.yml | 1 - .github/workflows/test.yml | 23 ++++++++++------------- .gitlab-ci.yml | 14 ++------------ .gitpod.Dockerfile | 4 ++-- bindist/ghcs | 2 +- bindist/ghcs-Msys | 2 +- cabal.project | 3 +-- docs/supported-versions.md | 7 ++++--- ghcide/ghcide.cabal | 2 +- haskell-language-server.cabal | 2 +- stack-lts19.yaml | 6 +++--- stack.yaml | 5 +---- 17 files changed, 33 insertions(+), 80 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bb48f40d3b..91f6e2e89c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -77,22 +77,16 @@ version: 2 jobs: stackage-lts16: environment: - # https://github.com/digital-asset/ghc-lib/issues/352 - - CPATH: "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/ffi:$CPATH" - STACK_FILE: "stack-lts16.yaml" <<: *defaults stackage-lts19: environment: - # https://github.com/digital-asset/ghc-lib/issues/352 - - CPATH: "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/ffi:$CPATH" - STACK_FILE: "stack-lts19.yaml" <<: *defaults stackage-nightly: environment: - # https://github.com/digital-asset/ghc-lib/issues/352 - - CPATH: "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/ffi:$CPATH" - STACK_FILE: "stack.yaml" <<: *defaults diff --git a/.github/actions/setup-build/action.yml b/.github/actions/setup-build/action.yml index e69eaa00f9..15b309926e 100644 --- a/.github/actions/setup-build/action.yml +++ b/.github/actions/setup-build/action.yml @@ -42,27 +42,6 @@ runs: echo "CABAL_PKGS_DIR=~/.cabal/packages" >> $GITHUB_ENV shell: bash - # This copy an alternative cabal-ghc${GHCVER}.project (for example cabal-ghc92.project) - # as main cabal-project, for not fully supported ghc versions - # Needs to be before the caching step so that the cache can detect changes to the modified cabal.project file - - name: Use possible modified `cabal.project` - env: - GHCVER: ${{ inputs.ghc }} - run: | - # File has some protections preventing regular `rm`. - # (most probably sticky bit is set on $HOME) - # `&&` insures `rm -f` return is positive. - # Many platforms also have `alias cp='cp -i'`. - GHCVER2=${GHCVER//./} - ALT_PROJECT_FILE_MINOR=cabal-ghc${GHCVER2}.project - ALT_PROJECT_FILE_MAJOR=cabal-ghc${GHCVER2:0:2}.project - if [[ -f "$ALT_PROJECT_FILE_MINOR" ]]; then - rm -f -v cabal.project && cp -v "$ALT_PROJECT_FILE_MINOR" cabal.project - elif [[ -f "$ALT_PROJECT_FILE_MAJOR" ]]; then - rm -f -v cabal.project && cp -v "$ALT_PROJECT_FILE_MAJOR" cabal.project - fi - shell: bash - - if: inputs.os == 'Windows' && inputs.ghc == '8.8.4' name: (Windows,GHC 8.8) Modify `cabal.project` to workaround segfaults run: | diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 81b31923da..1691ca2152 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -46,7 +46,7 @@ jobs: strategy: fail-fast: false matrix: - ghc: ['8.10.7', '9.2.3'] + ghc: ['8.10.7', '9.2.4'] os: [ubuntu-latest] # This code is fitted to the strategy: assumes Linux is used ... etc, @@ -103,7 +103,7 @@ jobs: strategy: fail-fast: false matrix: - ghc: ['8.10.7', '9.2.3'] + ghc: ['8.10.7', '9.2.4'] os: [ubuntu-latest] cabal: ['3.6'] example: ['cabal', 'lsp-types'] diff --git a/.github/workflows/caching.yml b/.github/workflows/caching.yml index 8594df4105..ada3e69ba6 100644 --- a/.github/workflows/caching.yml +++ b/.github/workflows/caching.yml @@ -80,8 +80,8 @@ jobs: strategy: fail-fast: false matrix: - ghc: [ "9.2.3" - , "9.2.2" + ghc: [ "9.2.4" + , "9.2.3" , "9.0.2" , "8.10.7" , "8.8.4" @@ -115,9 +115,6 @@ jobs: run: | # repeating builds to workaround segfaults in windows and ghc-8.8.4 cabal $cabalBuild || cabal $cabalBuild || cabal $cabalBuild - env: - # needed for GHC 9.2.2 https://github.com/digital-asset/ghc-lib/issues/352 - CPATH: "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/ffi:$CPATH" # We build ghcide with benchs and test enabled to include its dependencies in the cache # (including shake-bench) diff --git a/.github/workflows/flags.yml b/.github/workflows/flags.yml index ff12cfdfb9..849d68bc1b 100644 --- a/.github/workflows/flags.yml +++ b/.github/workflows/flags.yml @@ -44,7 +44,7 @@ jobs: strategy: fail-fast: true matrix: - ghc: [ "9.2.3" + ghc: [ "9.2.4" , "9.0.2" , "8.10.7" , "8.8.4" @@ -68,7 +68,7 @@ jobs: run: cabal v2-build ghcide --flags="ghc-patched-unboxed-bytecode test-exe executable bench-exe ekg" # we have to clean up warnings for 9.0 and 9.2 before enable -WAll - - if: matrix.ghc != '9.0.2' && matrix.ghc != '9.2.3' + - if: matrix.ghc != '9.0.2' && matrix.ghc != '9.2.4' name: Build with pedantic (-WError) run: cabal v2-build --flags="pedantic" diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index c536f8c48a..af9f4981af 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -13,7 +13,6 @@ jobs: - uses: ./.github/actions/setup-build with: - ghc: "9.2.3" os: ${{ runner.os }} shorten-hls: false diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c504aa87bd..c463b6432f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -57,8 +57,8 @@ jobs: strategy: fail-fast: true matrix: - ghc: [ "9.2.3" - , "9.2.2" + ghc: [ "9.2.4" + , "9.2.3" , "9.0.2" , "8.10.7" , "8.8.4" @@ -70,7 +70,7 @@ jobs: include: # only test supported ghc major versions - os: ubuntu-latest - ghc: '9.2.3' + ghc: '9.2.4' test: true - os: ubuntu-latest ghc: '9.0.2' @@ -85,7 +85,7 @@ jobs: ghc: '8.6.5' test: true - os: windows-latest - ghc: '9.2.3' + ghc: '9.2.4' test: true - os: windows-latest ghc: '9.0.2' @@ -100,7 +100,7 @@ jobs: - os: windows-latest ghc: '8.8.4' - os: windows-latest - ghc: '9.2.2' + ghc: '9.2.3' steps: - uses: actions/checkout@v3 @@ -112,9 +112,6 @@ jobs: # repeating builds to workaround segfaults in windows and ghc-8.8.4 - name: Build - # needed for GHC 9.2.2 https://github.com/digital-asset/ghc-lib/issues/352 - env: - CPATH: "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/ffi:$CPATH" run: cabal build || cabal build || cabal build - name: Set test options @@ -159,7 +156,7 @@ jobs: HLS_WRAPPER_TEST_EXE: hls-wrapper run: cabal test wrapper-test --test-options="$TEST_OPTS --rerun-log-file .tasty-rerun-log-wrapper" || cabal test wrapper-test --test-options="$TEST_OPTS --rerun-log-file .tasty-rerun-log-wrapper" || cabal test wrapper-test --test-options="$TEST_OPTS --rerun-log-file .tasty-rerun-log-wrapper" - - if: matrix.test && matrix.ghc != '9.2.3' + - if: matrix.test && matrix.ghc != '9.2.4' name: Test hls-brittany-plugin run: cabal test hls-brittany-plugin --test-options="$TEST_OPTS" || cabal test hls-brittany-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-brittany-plugin --test-options="$TEST_OPTS" @@ -179,11 +176,11 @@ jobs: name: Test hls-eval-plugin run: cabal test hls-eval-plugin --test-options="$TEST_OPTS" || cabal test hls-eval-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-eval-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.2.3' + - if: matrix.test && matrix.ghc != '9.2.4' name: Test hls-haddock-comments-plugin run: cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS" || cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.2.3' + - if: matrix.test && matrix.ghc != '9.2.4' name: Test hls-splice-plugin run: cabal test hls-splice-plugin --test-options="$TEST_OPTS" || cabal test hls-splice-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-splice-plugin --test-options="$TEST_OPTS" @@ -199,7 +196,7 @@ jobs: name: Test hls-fourmolu-plugin run: cabal test hls-fourmolu-plugin --test-options="$TEST_OPTS" || cabal test hls-fourmolu-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-fourmolu-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.2.3' + - if: matrix.test && matrix.ghc != '9.2.4' name: Test hls-tactics-plugin test suite run: cabal test hls-tactics-plugin --test-options="$TEST_OPTS" || cabal test hls-tactics-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-tactics-plugin --test-options="$TEST_OPTS" @@ -223,7 +220,7 @@ jobs: name: Test hls-hlint-plugin test suite run: cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-hlint-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.0.1' && matrix.ghc != '9.0.2' && matrix.ghc != '9.2.2' + - if: matrix.test && matrix.ghc != '9.0.1' && matrix.ghc != '9.0.2' && matrix.ghc != '9.2.4' name: Test hls-stan-plugin test suite run: cabal test hls-stan-plugin --test-options="$TEST_OPTS" || cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-stan-plugin --test-options="$TEST_OPTS" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 25144b304d..d0bed4d60d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,10 +17,10 @@ variables: CABAL_PROJECT: cabal.project - GHC_VERSION: 9.0.2 CABAL_PROJECT: cabal.project - - GHC_VERSION: 9.2.2 - CABAL_PROJECT: cabal.project - GHC_VERSION: 9.2.3 CABAL_PROJECT: cabal.project + - GHC_VERSION: 9.2.4 + CABAL_PROJECT: cabal.project workflow: rules: @@ -419,10 +419,8 @@ build-x86_64-darwin: ADD_CABAL_ARGS: "" before_script: - /bin/bash ./.gitlab/brew.sh autoconf automake coreutils make tree - # CPATH https://github.com/digital-asset/ghc-lib/issues/352 script: | export PATH="$CI_PROJECT_DIR/.brew/bin:$CI_PROJECT_DIR/.brew/sbin:$PATH" - export CPATH="/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/ffi:$CPATH" /bin/bash ./.gitlab/ci.sh after_script: - rm -Rf /private/tmp/.brew_tmp @@ -470,8 +468,6 @@ build-aarch64-darwin: before_script: - export HOMEBREW_CHANGE_ARCH_TO_ARM=1 - arch -arm64 /bin/bash ./.gitlab/brew.sh llvm autoconf automake coreutils make tree - # C_INCLUDE_PATH: https://gitlab.haskell.org/ghc/ghc/-/issues/20592 - # CPATH https://github.com/digital-asset/ghc-lib/issues/352 script: | export PATH="$CI_PROJECT_DIR/.brew/opt/llvm/bin:$CI_PROJECT_DIR/.brew/bin:$CI_PROJECT_DIR/.brew/sbin:$PATH" export CC=$CI_PROJECT_DIR/.brew/opt/llvm/bin/clang @@ -479,8 +475,6 @@ build-aarch64-darwin: export LD=ld export AR=$CI_PROJECT_DIR/.brew/opt/llvm/bin/llvm-ar export RANLIB=$CI_PROJECT_DIR/.brew/opt/llvm/bin/llvm-ranlib - export C_INCLUDE_PATH="`xcrun --show-sdk-path`/usr/include/ffi" - export CPATH="/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/ffi:$CPATH" arch -arm64 /bin/bash ./.gitlab/ci.sh after_script: - rm -Rf /private/tmp/.brew_tmp @@ -508,12 +502,8 @@ test-aarch64-darwin: before_script: - export HOMEBREW_CHANGE_ARCH_TO_ARM=1 - arch -arm64 /bin/bash ./.gitlab/brew.sh make tree - # C_INCLUDE_PATH: https://gitlab.haskell.org/ghc/ghc/-/issues/20592 - # CPATH https://github.com/digital-asset/ghc-lib/issues/352 script: | export PATH="$CI_PROJECT_DIR/.brew/opt/llvm/bin:$CI_PROJECT_DIR/.brew/bin:$CI_PROJECT_DIR/.brew/sbin:$PATH" - export C_INCLUDE_PATH="`xcrun --show-sdk-path`/usr/include/ffi" - export CPATH="/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/ffi:$CPATH" arch -arm64 /bin/bash ./.gitlab/test.sh after_script: - rm -Rf /private/tmp/.brew_tmp diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index 5631a302f1..6deba14260 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -11,8 +11,8 @@ RUN sudo install-packages build-essential curl libffi-dev libffi7 libgmp-dev lib ghcup install ghc 8.8.4 && \ ghcup install ghc 8.10.7 && \ ghcup install ghc 9.0.2 && \ - ghcup install ghc 9.2.2 && \ - ghcup install ghc 9.2.3 --set && \ + ghcup install ghc 9.2.3 && \ + ghcup install ghc 9.2.4 --set && \ ghcup install hls --set && \ ghcup install cabal --set && \ ghcup install stack --set && \ diff --git a/bindist/ghcs b/bindist/ghcs index 64b8db624b..b5009e2c78 100644 --- a/bindist/ghcs +++ b/bindist/ghcs @@ -2,5 +2,5 @@ 8.8.4,cabal.project 8.10.7,cabal.project 9.0.2,cabal.project -9.2.2,cabal.project 9.2.3,cabal.project +9.2.4,cabal.project diff --git a/bindist/ghcs-Msys b/bindist/ghcs-Msys index 3eaee6fcff..b4ed5601d5 100644 --- a/bindist/ghcs-Msys +++ b/bindist/ghcs-Msys @@ -1,4 +1,4 @@ 8.10.7,cabal.project 9.0.2,cabal.project -9.2.2,cabal.project 9.2.3,cabal.project +9.2.4,cabal.project diff --git a/cabal.project b/cabal.project index bb14019d99..b05eac94f0 100644 --- a/cabal.project +++ b/cabal.project @@ -44,7 +44,7 @@ package * write-ghc-environment-files: never -index-state: 2022-06-12T00:00:00Z +index-state: 2022-08-09T13:13:41Z constraints: hyphenation +embed, @@ -68,5 +68,4 @@ allow-newer: ---------- hiedb:base, - ekg-core, ekg-wai:time diff --git a/docs/supported-versions.md b/docs/supported-versions.md index 2adc4c43ce..687ecccc94 100644 --- a/docs/supported-versions.md +++ b/docs/supported-versions.md @@ -10,15 +10,16 @@ Last supporting HLS version: - specific version number: this GHC version is no longer one of the actively supported versions, and the last version of HLS which supports it is listed. Support status (see the support policy below for more details): -- "supported": this version of GHC is currently actively supported +- "supported": this version of GHC is currently actively supported - "deprecated": this version of GHC was supported in the past, but is now deprecated - "will be deprecated ...": this version of GHC has special deprecation conditions that deviate from the support policy - "partial": not all features and plugins work, see the plugin support table and any linked issues for more details | GHC version | Last supporting HLS version | Support status | | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -| 9.2.3 | next | supported, ([partial](https://github.com/haskell/haskell-language-server/issues/2982)) | -| 9.2.2 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported, ([partial](https://github.com/haskell/haskell-language-server/issues/2982)) | +| 9.2.4 | next | supported([partial](https://github.com/haskell/haskell-language-server/issues/2982)) | +| 9.2.3 | next | supported([partial](https://github.com/haskell/haskell-language-server/issues/2982)) | +| 9.2.2 | [1.7.0.0](https://github.com/haskell/haskell-language-server/releases/tag/1.7.0.0) | supported, ([partial](https://github.com/haskell/haskell-language-server/issues/2982)) | | 9.2.1 | [1.7.0.0](https://github.com/haskell/haskell-language-server/releases/tag/1.7.0.0) | deprecated | | 9.0.2 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported | | 9.0.1 | [1.6.1.0](https://github.com/haskell/haskell-language-server/releases/tag/1.6.1.0) | deprecated | diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index e91c296f36..99abf39cc6 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -13,7 +13,7 @@ description: A library for building Haskell IDE's on top of the GHC API. homepage: https://github.com/haskell/haskell-language-server/tree/master/ghcide#readme bug-reports: https://github.com/haskell/haskell-language-server/issues -tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.2 || == 9.2.3 +tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 extra-source-files: README.md CHANGELOG.md test/data/**/*.project test/data/**/*.cabal diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 4618e156e4..9dd079bc9a 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -14,7 +14,7 @@ copyright: The Haskell IDE Team license: Apache-2.0 license-file: LICENSE build-type: Simple -tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.2 || == 9.2.3 +tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 extra-source-files: README.md ChangeLog.md diff --git a/stack-lts19.yaml b/stack-lts19.yaml index 48f834ddcb..6beff15f8b 100644 --- a/stack-lts19.yaml +++ b/stack-lts19.yaml @@ -1,4 +1,4 @@ -resolver: lts-19.5 +resolver: lts-19.18 packages: - . @@ -39,8 +39,8 @@ extra-deps: - Cabal-3.6.0.0 - floskell-0.10.6@sha256:e77d194189e8540abe2ace2c7cb8efafc747ca35881a2fefcbd2d40a1292e036,3819 - fourmolu-0.6.0.0 -- ghc-lib-9.2.3.20220527 -- ghc-lib-parser-9.2.3.20220527 +- ghc-lib-9.2.4.20220729 +- ghc-lib-parser-9.2.4.20220729 - ghc-lib-parser-ex-9.2.0.4 - heapsize-0.3.0.1@sha256:0b69aa97a46d819b700ac7b145f3b5493c3565cf2c5b8298682238d405d0326e,1417 - hiedb-0.4.1.0@sha256:fb20c657d9ecc91701b00dffcf4bbd77cb83720a1f9d867badd77ea227973135,2875 diff --git a/stack.yaml b/stack.yaml index 438328b03a..31af6039e1 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,4 +1,4 @@ -resolver: nightly-2022-06-10 +resolver: nightly-2022-08-04 packages: - . @@ -33,11 +33,9 @@ packages: - ./plugins/hls-gadt-plugin extra-deps: -- direct-sqlite-2.3.26@sha256:04e835402f1508abca383182023e4e2b9b86297b8533afbd4e57d1a5652e0c23,3718 - floskell-0.10.6@sha256:e77d194189e8540abe2ace2c7cb8efafc747ca35881a2fefcbd2d40a1292e036,3819 - heapsize-0.3.0.1@sha256:0b69aa97a46d819b700ac7b145f3b5493c3565cf2c5b8298682238d405d0326e,1417 - hiedb-0.4.1.0@sha256:fb20c657d9ecc91701b00dffcf4bbd77cb83720a1f9d867badd77ea227973135,2875 -- hlint-3.4 - implicit-hie-0.1.2.7@sha256:82bbbb1a8c05f99c8af3c16ac53e80c8648d8bf047b25ed5ce45a135bd736907,3122 - implicit-hie-cradle-0.5.0.0@sha256:4276f60f3a59bc22df03fd918f73bca9f777de9568f85e3a8be8bd7566234a59,2368 - lsp-1.4.0.0@sha256:d992cb88d6212f113baf372404c141a6bea14c436baa64ea6e4f01b6188c575b,5088 @@ -46,7 +44,6 @@ extra-deps: - monad-dijkstra-0.1.1.3@sha256:d2fc098d7c122555e726830a12ae0423ac187f89de9228f32e56e2f6fc2238e1,1900 - retrie-1.2.0.1 - rope-utf16-splay-0.3.2.0 -- sqlite-simple-0.4.18.0@sha256:3ceea56375c0a3590c814e411a4eb86943f8d31b93b110ca159c90689b6b39e5,3002 # currently needed for ghcide>extra, etc. allow-newer: true From 0e74593e8df9665067174ca27543006c9a6ad230 Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Thu, 11 Aug 2022 23:48:18 +0800 Subject: [PATCH 049/213] hls-explicit-fixity-plugin (#2941) * init hls-explicit-fixity-plugin * Update dependencies * Refactor: Prevent block on startup * Run pre-commit * Compatibility: Add emptyMessages * Remove unused ModIface * Comment why to make this plugin a lower priority * Provide hover content while testing fail * Avoid lambda * 4 space indent * Pass Text instead of Name Co-authored-by: Pepe Iborra --- .github/workflows/test.yml | 4 + cabal.project | 1 + docs/features.md | 6 + exe/Plugins.hs | 11 + ghcide/src/Development/IDE/GHC/Compat/Core.hs | 14 +- haskell-language-server.cabal | 11 + plugins/hls-explicit-fixity-plugin/LICENSE | 201 ++++++++++++++++++ plugins/hls-explicit-fixity-plugin/README.md | 13 ++ .../hls-explicit-fixity-plugin/fixity1.png | Bin 0 -> 16425 bytes .../hls-explicit-fixity-plugin/fixity2.png | Bin 0 -> 18908 bytes .../hls-explicit-fixity-plugin.cabal | 52 +++++ .../src/Ide/Plugin/ExplicitFixity.hs | 165 ++++++++++++++ .../hls-explicit-fixity-plugin/test/Main.hs | 70 ++++++ .../test/testdata/Hover.hs | 40 ++++ .../test/testdata/HoverImport.hs | 5 + .../test/testdata/hie.yaml | 3 + stack-lts16.yaml | 1 + stack-lts19.yaml | 1 + stack.yaml | 1 + 19 files changed, 595 insertions(+), 4 deletions(-) create mode 100644 plugins/hls-explicit-fixity-plugin/LICENSE create mode 100644 plugins/hls-explicit-fixity-plugin/README.md create mode 100644 plugins/hls-explicit-fixity-plugin/fixity1.png create mode 100644 plugins/hls-explicit-fixity-plugin/fixity2.png create mode 100644 plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal create mode 100644 plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs create mode 100644 plugins/hls-explicit-fixity-plugin/test/Main.hs create mode 100644 plugins/hls-explicit-fixity-plugin/test/testdata/Hover.hs create mode 100644 plugins/hls-explicit-fixity-plugin/test/testdata/HoverImport.hs create mode 100644 plugins/hls-explicit-fixity-plugin/test/testdata/hie.yaml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c463b6432f..5b963a662d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -248,6 +248,10 @@ jobs: name: Test hls-gadt-plugin test suit run: cabal test hls-gadt-plugin --test-options="$TEST_OPTS" || cabal test hls-gadt-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-gadt-plugin --test-options="$TEST_OPTS" + - if: matrix.test + name: Test hls-explicit-fixity-plugin test suite + run: cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" || cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" + test_post_job: if: always() runs-on: ubuntu-latest diff --git a/cabal.project b/cabal.project index b05eac94f0..9a49ac4fa5 100644 --- a/cabal.project +++ b/cabal.project @@ -30,6 +30,7 @@ packages: ./plugins/hls-change-type-signature-plugin ./plugins/hls-stan-plugin ./plugins/hls-gadt-plugin + ./plugins/hls-explicit-fixity-plugin -- Standard location for temporary packages needed for particular environments -- For example it is used in the project gitlab mirror to help in the MAcOS M1 build script diff --git a/docs/features.md b/docs/features.md index 4b8b10725f..793b66a61e 100644 --- a/docs/features.md +++ b/docs/features.md @@ -50,6 +50,12 @@ Provided by: `ghcide` Type information and documentation on hover, [including from local definitions](./configuration.md#how-to-show-local-documentation-on-hover). +### Show fixity + +Provided by: `hls-explicit-fixity-plugin` + +Provides fixity information. + ## Jump to definition Provided by: `ghcide` diff --git a/exe/Plugins.hs b/exe/Plugins.hs index 561c8541a8..86dbff0a16 100644 --- a/exe/Plugins.hs +++ b/exe/Plugins.hs @@ -91,6 +91,11 @@ import Ide.Plugin.ChangeTypeSignature as ChangeTypeSignature #if hls_gadt import Ide.Plugin.GADT as GADT #endif + +#if explicitFixity +import Ide.Plugin.ExplicitFixity as ExplicitFixity +#endif + -- formatters #if hls_floskell @@ -209,8 +214,14 @@ idePlugins recorder includeExamples = pluginDescToIdePlugins allPlugins -- The ghcide descriptors should come last so that the notification handlers -- (which restart the Shake build) run after everything else GhcIde.descriptors pluginRecorder +#if explicitFixity + -- Make this plugin has a lower priority than ghcide's plugin to ensure + -- type info display first. + ++ [ExplicitFixity.descriptor pluginRecorder] +#endif examplePlugins = [Example.descriptor pluginRecorder "eg" ,Example2.descriptor pluginRecorder "eg2" ,ExampleCabal.descriptor pluginRecorder "ec" ] + diff --git a/ghcide/src/Development/IDE/GHC/Compat/Core.hs b/ghcide/src/Development/IDE/GHC/Compat/Core.hs index 625010fd7c..222be572e6 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Core.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Core.hs @@ -102,6 +102,10 @@ module Development.IDE.GHC.Compat.Core ( #endif -- * Fixity LexicalFixity(..), + Fixity (..), + mi_fix, + defaultFixity, + lookupFixityRn, -- * ModSummary ModSummary(..), -- * HomeModInfo @@ -378,6 +382,7 @@ module Development.IDE.GHC.Compat.Core ( module GHC.Types.Name.Cache, module GHC.Types.Name.Env, module GHC.Types.Name.Reader, + module GHC.Utils.Error, #if MIN_VERSION_ghc(9,2,0) module GHC.Types.Avail, module GHC.Types.SourceFile, @@ -388,7 +393,6 @@ module Development.IDE.GHC.Compat.Core ( module GHC.Types.Unique.Supply, module GHC.Types.Var, module GHC.Unit.Module, - module GHC.Utils.Error, #else module BasicTypes, module Class, @@ -552,6 +556,7 @@ import GHC.Runtime.Context (InteractiveImport (..)) import GHC.Parser.Lexer import qualified GHC.Runtime.Linker as Linker #endif +import GHC.Rename.Fixity (lookupFixityRn) import GHC.Rename.Names import GHC.Rename.Splice import qualified GHC.Runtime.Interpreter as GHCi @@ -568,7 +573,7 @@ import GHC.Tc.Utils.TcType as TcType import qualified GHC.Types.Avail as Avail #if MIN_VERSION_ghc(9,2,0) import GHC.Types.Avail (greNamePrintableName) -import GHC.Types.Fixity (LexicalFixity (..)) +import GHC.Types.Fixity (LexicalFixity (..), Fixity (..), defaultFixity) #endif #if MIN_VERSION_ghc(9,2,0) import GHC.Types.Meta @@ -613,11 +618,11 @@ import GHC.Unit.Module.Imported import GHC.Unit.Module.ModDetails import GHC.Unit.Module.ModGuts import GHC.Unit.Module.ModIface (IfaceExport, ModIface (..), - ModIface_ (..)) + ModIface_ (..), mi_fix) import GHC.Unit.Module.ModSummary (ModSummary (..)) #endif import GHC.Unit.State (ModuleOrigin (..)) -import GHC.Utils.Error (Severity (..)) +import GHC.Utils.Error (Severity (..), emptyMessages) import GHC.Utils.Panic hiding (try) import qualified GHC.Utils.Panic.Plain as Plain #else @@ -688,6 +693,7 @@ import qualified Panic as Plain #endif import Parser import PatSyn +import RnFixity #if MIN_VERSION_ghc(8,8,0) import Plugins #endif diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 9dd079bc9a..d786e71530 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -196,6 +196,11 @@ flag gadt default: True manual: True +flag explicitFixity + description: Enable explicitFixity plugin + default: True + manual: True + -- formatters flag floskell @@ -329,6 +334,11 @@ common gadt build-depends: hls-gadt-plugin ^>= 1.0 cpp-options: -Dhls_gadt +common explicitFixity + if flag(explicitFixity) + build-depends: hls-explicit-fixity-plugin ^>= 1.0 + cpp-options: -DexplicitFixity + -- formatters common floskell @@ -382,6 +392,7 @@ executable haskell-language-server , qualifyImportedNames , codeRange , gadt + , explicitFixity , floskell , fourmolu , ormolu diff --git a/plugins/hls-explicit-fixity-plugin/LICENSE b/plugins/hls-explicit-fixity-plugin/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/plugins/hls-explicit-fixity-plugin/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/hls-explicit-fixity-plugin/README.md b/plugins/hls-explicit-fixity-plugin/README.md new file mode 100644 index 0000000000..409ff7f3dc --- /dev/null +++ b/plugins/hls-explicit-fixity-plugin/README.md @@ -0,0 +1,13 @@ +# Explicit Fixity Plugin + +The hls-explicit-fixity-plugin will show available fixity explicitly while hovering. + +## Demo + +![fixity1](./fixity1.png) + +![fixity2](./fixity2.png) + +## Change log +### 1.0.0.0 +- Released! diff --git a/plugins/hls-explicit-fixity-plugin/fixity1.png b/plugins/hls-explicit-fixity-plugin/fixity1.png new file mode 100644 index 0000000000000000000000000000000000000000..0f88fd53d59cc1a99f1233840d409318128cd597 GIT binary patch literal 16425 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 literal 0 HcmV?d00001 diff --git a/plugins/hls-explicit-fixity-plugin/fixity2.png b/plugins/hls-explicit-fixity-plugin/fixity2.png new file mode 100644 index 0000000000000000000000000000000000000000..7798192a3964ab8efb44b05f0cce69ebf915db8a GIT binary patch literal 18908 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 + +license: Apache-2.0 +license-file: LICENSE +author: Lei Zhu +maintainer: julytreee@gmail.com +category: Development +build-type: Simple +extra-source-files: + LICENSE + test/testdata/*.hs + +library + exposed-modules: Ide.Plugin.ExplicitFixity + + hs-source-dirs: src + build-depends: + base >=4.12 && <5 + , containers + , deepseq + , extra + , ghc + , ghcide ^>=1.7 + , hashable + , hls-plugin-api ^>=1.4 + , lsp >=1.2.0.1 + , text + + ghc-options: + -Wall + -Wno-name-shadowing + -Wno-unticked-promoted-constructors + default-language: Haskell2010 + default-extensions: DataKinds + +test-suite tests + type: exitcode-stdio-1.0 + default-language: Haskell2010 + hs-source-dirs: test + main-is: Main.hs + ghc-options: -threaded -rtsopts -with-rtsopts=-N + build-depends: + , base + , filepath + , hls-explicit-fixity-plugin + , hls-test-utils ^>=1.3 + , text diff --git a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs new file mode 100644 index 0000000000..fb4e1c1d06 --- /dev/null +++ b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs @@ -0,0 +1,165 @@ +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeFamilies #-} +{-# OPTIONS_GHC -Wno-deprecations #-} +{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} +{-# HLINT ignore "Use nubOrdOn" #-} + +module Ide.Plugin.ExplicitFixity(descriptor) where + +import Control.DeepSeq +import Control.Monad (forM) +import Control.Monad.IO.Class (MonadIO, liftIO) +import Data.Coerce (coerce) +import Data.Either.Extra +import Data.Hashable +import Data.List.Extra (nubOn) +import qualified Data.Map as M +import Data.Maybe +import Data.Monoid +import qualified Data.Text as T +import Development.IDE hiding (pluginHandlers, + pluginRules) +import Development.IDE.Core.PositionMapping (idDelta) +import Development.IDE.Core.Shake (addPersistentRule) +import qualified Development.IDE.Core.Shake as Shake +import Development.IDE.GHC.Compat +import Development.IDE.GHC.Compat.Util (FastString) +import qualified Development.IDE.GHC.Compat.Util as Util +import GHC.Generics (Generic) +import Ide.PluginUtils (getNormalizedFilePath, + handleMaybeM, + pluginResponse) +import Ide.Types hiding (pluginId) +import Language.LSP.Types + +pluginId :: PluginId +pluginId = "explicitFixity" + +descriptor :: Recorder (WithPriority Log) -> PluginDescriptor IdeState +descriptor recorder = (defaultPluginDescriptor pluginId) + { pluginRules = fixityRule recorder + , pluginHandlers = mkPluginHandler STextDocumentHover hover + } + +hover :: PluginMethodHandler IdeState TextDocumentHover +hover state plId (HoverParams (TextDocumentIdentifier uri) pos _) = pluginResponse $ do + nfp <- getNormalizedFilePath plId uri + fixityTrees <- handleMaybeM "ExplicitFixity: Unable to get fixity" + $ liftIO + $ runAction "ExplicitFixity.GetFixity" state + $ use GetFixity nfp + -- We don't have much fixities on one position, so `nubOn` is acceptable. + pure $ toHover $ nubOn snd $ findInTree fixityTrees pos fNodeFixty + where + toHover :: [(T.Text, Fixity)] -> Maybe Hover + toHover [] = Nothing + toHover fixities = + let -- Splicing fixity info + contents = T.intercalate "\n\n" $ fixityText <$> fixities + -- Append to the previous hover content + contents' = "\n" <> sectionSeparator <> contents + in Just $ Hover (HoverContents $ unmarkedUpContent contents') Nothing + + fixityText :: (T.Text, Fixity) -> T.Text + fixityText (name, Fixity _ precedence direction) = + printOutputable direction <> " " <> printOutputable precedence <> " `" <> name <> "`" + +-- | Transferred from ghc `selectSmallestContaining` +selectSmallestContainingForFixityTree :: Span -> FixityTree -> Maybe FixityTree +selectSmallestContainingForFixityTree sp node + | sp `containsSpan` fNodeSpan node = Just node + | fNodeSpan node `containsSpan` sp = getFirst $ mconcat + [ foldMap (First . selectSmallestContainingForFixityTree sp) $ fNodeChildren node + , First (Just node) + ] + | otherwise = Nothing + +-- | Transferred from ghcide `pointCommand` +findInTree :: FixityTrees -> Position -> (FixityTree -> [a]) -> [a] +findInTree tree pos k = + concat $ M.elems $ flip M.mapWithKey tree $ \fs ast -> + maybe [] k (selectSmallestContainingForFixityTree (sp fs) ast) + where + sloc fs = mkRealSrcLoc fs (fromIntegral $ line+1) (fromIntegral $ cha+1) + sp fs = mkRealSrcSpan (sloc fs) (sloc fs) + line = _line pos + cha = _character pos + +data FixityTree = FNode + { fNodeSpan :: Span + , fNodeChildren :: [FixityTree] + , fNodeFixty :: [(T.Text, Fixity)] + } deriving (Generic) + +instance NFData FixityTree where + rnf = rwhnf + +instance Show FixityTree where + show _ = "" + +type FixityTrees = M.Map FastString FixityTree + +newtype Log = LogShake Shake.Log + +instance Pretty Log where + pretty = \case + LogShake log -> pretty log + +data GetFixity = GetFixity deriving (Show, Eq, Generic) + +instance Hashable GetFixity +instance NFData GetFixity + +type instance RuleResult GetFixity = FixityTrees + +fakeFixityTrees :: FixityTrees +fakeFixityTrees = M.empty + +-- | Convert a HieASTs to FixityTrees with fixity info gathered +hieAstsToFixitTrees :: MonadIO m => HscEnv -> TcGblEnv -> HieASTs a -> m FixityTrees +hieAstsToFixitTrees hscEnv tcGblEnv ast = + -- coerce to avoid compatibility issues. + M.mapKeysWith const coerce <$> + sequence (M.map (hieAstToFixtyTree hscEnv tcGblEnv) (getAsts ast)) + +-- | Convert a HieAST to FixityTree with fixity info gathered +hieAstToFixtyTree :: MonadIO m => HscEnv -> TcGblEnv -> HieAST a -> m FixityTree +hieAstToFixtyTree hscEnv tcGblEnv ast = case ast of + (Node _ span []) -> FNode span [] <$> getFixities + (Node _ span children) -> do + fixities <- getFixities + childrenFixities <- mapM (hieAstToFixtyTree hscEnv tcGblEnv) children + pure $ FNode span childrenFixities fixities + where + -- Names at the current ast node + names :: [Name] + names = mapMaybe eitherToMaybe $ M.keys $ getNodeIds ast + + getFixities :: MonadIO m => m [(T.Text, Fixity)] + getFixities = liftIO + $ fmap (filter ((/= defaultFixity) . snd) . mapMaybe pickFixity) + $ forM names $ \name -> + (,) (printOutputable name) + . snd + <$> Util.handleGhcException + (const $ pure (emptyMessages, Nothing)) + (initTcWithGbl hscEnv tcGblEnv (realSrcLocSpan $ mkRealSrcLoc "" 1 1) (lookupFixityRn name)) + + pickFixity :: (T.Text, Maybe Fixity) -> Maybe (T.Text, Fixity) + pickFixity (_, Nothing) = Nothing + pickFixity (name, Just f) = Just (name, f) + +fixityRule :: Recorder (WithPriority Log) -> Rules () +fixityRule recorder = do + define (cmapWithPrio LogShake recorder) $ \GetFixity nfp -> do + HAR{hieAst} <- use_ GetHieAst nfp + env <- hscEnv <$> use_ GhcSession nfp + tcGblEnv <- tmrTypechecked <$> use_ TypeCheck nfp + trees <- hieAstsToFixitTrees env tcGblEnv hieAst + pure ([], Just trees) + + -- Ensure that this plugin doesn't block on startup + addPersistentRule GetFixity $ \_ -> pure $ Just (fakeFixityTrees, idDelta, Nothing) diff --git a/plugins/hls-explicit-fixity-plugin/test/Main.hs b/plugins/hls-explicit-fixity-plugin/test/Main.hs new file mode 100644 index 0000000000..52367e215c --- /dev/null +++ b/plugins/hls-explicit-fixity-plugin/test/Main.hs @@ -0,0 +1,70 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Main where + +import qualified Data.Text as T +import Ide.Plugin.ExplicitFixity (descriptor) +import System.FilePath +import Test.Hls + +plugin :: PluginDescriptor IdeState +plugin = descriptor mempty + +main :: IO () +main = defaultTestRunner tests + +tests :: TestTree +tests = testGroup "Explicit fixity" + [ hoverTest "(++)" (Position 5 7) "infixr 5 `++`" + , hoverTest "($)" (Position 6 7) "infixr 0 `$`" + , hoverTest "(.)" (Position 7 7) "infixr 9 `.`" + , hoverTest "(+)" (Position 8 7) "infixl 6 `+`" + , hoverTest "(-)" (Position 9 8) "infixl 6 `-`" + , hoverTest "(<>)" (Position 10 7) "infixr 6 `<>`" + , hoverTest "(>>=)" (Position 11 7) "infixl 1 `>>=`" + , hoverTest "(>=>)" (Position 12 7) "infixr 1 `>=>`" + , hoverTest "elem" (Position 13 7) "infix 4 `elem`" + , hoverTest "on" (Position 14 7) "infixl 0 `on`" + , hoverTest "(||)" (Position 15 8) "infixr 2 `||`" + , hoverTest "mod" (Position 16 8) "infixl 7 `mod`" + , hoverTest "(**)" (Position 17 8) "infixr 8 `**`" + , hoverTest "(^)" (Position 18 8) "infixr 8 `^`" + , hoverTest "(<$)" (Position 19 8) "infixl 4 `<$`" + , hoverTest "seq" (Position 20 9) "infixr 0 `seq`" + , hoverTest "(<|>)" (Position 21 8) "infixl 3 `<|>`" + , hoverTest "fixity define" (Position 23 11) "infixr 7 `>>:`" + , hoverTest "record" (Position 28 10) "infix 9 `>>::`" + , hoverTest "wildcards" (Position 30 5) "infixr 7 `>>:` \n \ninfix 9 `>>::`" + , hoverTest "function" (Position 32 11) "infixl 1 `f`" + , hoverTest "signature" (Position 35 2) "infixr 9 `>>>:`" + , hoverTest "operator" (Position 36 2) "infixr 9 `>>>:`" + , hoverTest "escape" (Position 39 2) "infixl 3 `~\\:`" + -- Ensure that there is no one extra new line in import statement + , expectFail $ hoverTest "import" (Position 2 18) "Control.Monad***" + -- Known issue, See https://github.com/haskell/haskell-language-server/pull/2973/files#r916535742 + , expectFail $ hoverTestImport "import" (Position 4 7) "infixr 9 `>>>:`" + ] + +hoverTest :: TestName -> Position -> T.Text -> TestTree +hoverTest = hoverTest' "Hover.hs" +hoverTestImport :: TestName -> Position -> T.Text -> TestTree +hoverTestImport = hoverTest' "HoverImport.hs" + +hoverTest' :: String -> TestName -> Position -> T.Text -> TestTree +hoverTest' docName title pos expected = testCase title $ runSessionWithServer plugin testDataDir $ do + doc <- openDoc docName "haskell" + waitForKickDone + h <- getHover doc pos + let expected' = "\n" <> sectionSeparator <> expected + case h of + Nothing -> liftIO $ assertFailure "No hover" + Just (Hover contents _) -> case contents of + HoverContentsMS _ -> liftIO $ assertFailure "Unexpected content type" + HoverContents (MarkupContent mk txt) -> do + liftIO + $ assertBool ("Failed to find `" <> T.unpack expected <> "` in hover message: " <> T.unpack txt) + $ expected `T.isInfixOf` txt + closeDoc doc + +testDataDir :: FilePath +testDataDir = "test" "testdata" diff --git a/plugins/hls-explicit-fixity-plugin/test/testdata/Hover.hs b/plugins/hls-explicit-fixity-plugin/test/testdata/Hover.hs new file mode 100644 index 0000000000..f5fd50a501 --- /dev/null +++ b/plugins/hls-explicit-fixity-plugin/test/testdata/Hover.hs @@ -0,0 +1,40 @@ +{-# LANGUAGE RecordWildCards #-} +module Hover where +import Control.Monad +import Data.Function (on) +import Control.Applicative ((<|>)) +f1 = (++) +f2 = ($) +f3 = (.) +f4 = (+) +f5 = 1 - 2 +f6 = (<>) +f7 = (>>=) +f8 = (>=>) +f9 = elem +f10 = on +f11 = (||) +f12 = mod +f13 = (**) +f14 = (^) +f15 = (<$) +f16 = seq +f17 = (<|>) + +infixr 7 >>: +infix 9 >>:: +data F = G + { (>>:) :: Int -> Int -> Int + , c :: Int + , (>>::) :: Char + } +f G{..} = undefined + +infixl 1 `f` + +infixr 9 >>>: +(>>>:) :: Int -> Int +(>>>:) x = 3 + +infixl 3 ~\: +(~\:) x y = 3 diff --git a/plugins/hls-explicit-fixity-plugin/test/testdata/HoverImport.hs b/plugins/hls-explicit-fixity-plugin/test/testdata/HoverImport.hs new file mode 100644 index 0000000000..e3474eb0c3 --- /dev/null +++ b/plugins/hls-explicit-fixity-plugin/test/testdata/HoverImport.hs @@ -0,0 +1,5 @@ +module HoverImport where + +import Hover + +g = (>>>:) diff --git a/plugins/hls-explicit-fixity-plugin/test/testdata/hie.yaml b/plugins/hls-explicit-fixity-plugin/test/testdata/hie.yaml new file mode 100644 index 0000000000..824558147d --- /dev/null +++ b/plugins/hls-explicit-fixity-plugin/test/testdata/hie.yaml @@ -0,0 +1,3 @@ +cradle: + direct: + arguments: [] diff --git a/stack-lts16.yaml b/stack-lts16.yaml index 826f8730e4..32a1e3f5ba 100644 --- a/stack-lts16.yaml +++ b/stack-lts16.yaml @@ -32,6 +32,7 @@ packages: - ./plugins/hls-code-range-plugin - ./plugins/hls-change-type-signature-plugin - ./plugins/hls-gadt-plugin + - ./plugins/hls-explicit-fixity-plugin ghc-options: "$everything": -haddock diff --git a/stack-lts19.yaml b/stack-lts19.yaml index 6beff15f8b..53c00671e8 100644 --- a/stack-lts19.yaml +++ b/stack-lts19.yaml @@ -31,6 +31,7 @@ packages: - ./plugins/hls-code-range-plugin - ./plugins/hls-change-type-signature-plugin - ./plugins/hls-gadt-plugin + - ./plugins/hls-explicit-fixity-plugin ghc-options: "$everything": -haddock diff --git a/stack.yaml b/stack.yaml index 31af6039e1..72e06a135d 100644 --- a/stack.yaml +++ b/stack.yaml @@ -31,6 +31,7 @@ packages: - ./plugins/hls-code-range-plugin - ./plugins/hls-change-type-signature-plugin - ./plugins/hls-gadt-plugin +- ./plugins/hls-explicit-fixity-plugin extra-deps: - floskell-0.10.6@sha256:e77d194189e8540abe2ace2c7cb8efafc747ca35881a2fefcbd2d40a1292e036,3819 From 3b2f9f6d7cfc4d89ea2b95527f89bb65806bfffc Mon Sep 17 00:00:00 2001 From: Andy Date: Fri, 12 Aug 2022 01:55:24 +0200 Subject: [PATCH 050/213] Stan: Avoid terminal colors in messages (#3090) --- plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs b/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs index 7eb46e05b0..8ea671e9ee 100644 --- a/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs +++ b/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs @@ -43,12 +43,11 @@ import Ide.Types (PluginDescriptor (..), defaultPluginDescriptor) import qualified Language.LSP.Types as LSP import Stan.Analysis (Analysis (..), runAnalysis) -import Stan.Category (prettyShowCategory) +import Stan.Category (Category (..)) import Stan.Core.Id (Id (..)) import Stan.Inspection (Inspection (..)) import Stan.Inspection.All (inspectionsIds, inspectionsMap) import Stan.Observation (Observation (..)) -import Stan.Severity (prettyShowSeverity) descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState descriptor recorder plId = (defaultPluginDescriptor plId) {pluginRules = rules recorder} @@ -94,15 +93,16 @@ rules recorder = do let -- Looking similar to Stan CLI output -- We do not use `prettyShowInspection` cuz Id is redundant here + -- `prettyShowSeverity` and `prettyShowCategory` would contain color + -- codes and are replaced, too message :: T.Text message = T.unlines $ [ " ✲ Name: " <> inspectionName inspection, " ✲ Description: " <> inspectionDescription inspection, - " ✲ Severity: " <> (prettyShowSeverity $ - inspectionSeverity inspection), + " ✲ Severity: " <> (T.pack $ show $ inspectionSeverity inspection), " ✲ Category: " <> T.intercalate " " - (map prettyShowCategory $ toList $ inspectionCategory inspection), + (map (("#" <>) . unCategory) $ toList $ inspectionCategory inspection), "Possible solutions:" ] ++ map (" - " <>) (inspectionSolution inspection) From 255498147abb1b18a9cae3c61c5558d7fc84ab31 Mon Sep 17 00:00:00 2001 From: Kobayashi Date: Fri, 12 Aug 2022 17:09:07 +0800 Subject: [PATCH 051/213] upgrade lsp to 1.5 (#3072) * upgrade lsp to 1.5 * fix stack.yaml * try fix tests * disable verbose logging in ghcide * fix more tests in ghcide * fix floskell test * disable debug log in func-test * disable debug log in lsp itself * Revert "disable debug log in func-test" This reverts commit 1fd6658c770b3b254a7927ccf403c55512637dd6. * remove unused import * fix hls test utils * upgrade lsp in nix * fix func-tests * Revert "fix func-tests" This reverts commit 2ecd76d0bafc25df12ac818f1c72b0f56a79c9df. * fix waitForDiagnosticsFromSourceWithTimeout * use Null as dummy message in waitForDiagnosticsFromSourceWithTimeout * simplify a test case * add comment about lsp bad logs --- exe/Wrapper.hs | 2 +- flake.lock | 18 ++-- flake.nix | 6 +- ghcide/ghcide.cabal | 9 +- ghcide/src/Development/IDE/Core/FileStore.hs | 4 +- ghcide/src/Development/IDE/Core/Rules.hs | 10 +- ghcide/src/Development/IDE/Core/Shake.hs | 5 +- .../src/Development/IDE/LSP/LanguageServer.hs | 25 +++-- ghcide/src/Development/IDE/Main.hs | 2 +- .../src/Development/IDE/Plugin/CodeAction.hs | 7 +- ghcide/src/Development/IDE/Plugin/Test.hs | 1 - ghcide/src/Development/IDE/Types/Logger.hs | 22 ++-- ghcide/test/exe/Main.hs | 56 +++++----- hls-plugin-api/hls-plugin-api.cabal | 2 +- hls-test-utils/hls-test-utils.cabal | 4 +- hls-test-utils/src/Test/Hls.hs | 101 +++++++++--------- hls-test-utils/src/Test/Hls/Util.hs | 48 +++------ .../src/Ide/Plugin/Floskell.hs | 2 +- .../src/Ide/Plugin/Rename.hs | 4 - stack-lts16.yaml | 8 +- stack-lts19.yaml | 4 + stack.yaml | 8 +- 22 files changed, 180 insertions(+), 168 deletions(-) diff --git a/exe/Wrapper.hs b/exe/Wrapper.hs index 83f10cedb2..0122cf319f 100644 --- a/exe/Wrapper.hs +++ b/exe/Wrapper.hs @@ -301,7 +301,7 @@ launchErrorLSP errorMsg = do let interpretHandler (env, _st) = LSP.Iso (LSP.runLspT env . unErrorLSPM) liftIO pure (doInitialize, asyncHandlers, interpretHandler) - runLanguageServer + runLanguageServer (cmapWithPrio pretty recorder) (Main.argsLspOptions defaultArguments) inH outH diff --git a/flake.lock b/flake.lock index 3823849eba..e01237f392 100644 --- a/flake.lock +++ b/flake.lock @@ -197,37 +197,37 @@ "lsp": { "flake": false, "locked": { - "narHash": "sha256-OcyNHNRh9j5nbJ8SjaNAWIEKuixAJlA7+vTimFY0c2c=", + "narHash": "sha256-+rkFYvSAI1hyFxPkgWZReyM2P6irVDpGVUGK8mcfEJE=", "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-1.4.0.0/lsp-1.4.0.0.tar.gz" + "url": "https://hackage.haskell.org/package/lsp-1.5.0.0/lsp-1.5.0.0.tar.gz" }, "original": { "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-1.4.0.0/lsp-1.4.0.0.tar.gz" + "url": "https://hackage.haskell.org/package/lsp-1.5.0.0/lsp-1.5.0.0.tar.gz" } }, "lsp-test": { "flake": false, "locked": { - "narHash": "sha256-IOmbQH6tKdu9kAyirvLx6xFS2N+/tbs6vZn0mNGm3No=", + "narHash": "sha256-TXRy/VT94Cn0BPtiL65c7UqahyJZgUtBQQgESZacrdY=", "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-test-0.14.0.2/lsp-test-0.14.0.2.tar.gz" + "url": "https://hackage.haskell.org/package/lsp-test-0.14.0.3/lsp-test-0.14.0.3.tar.gz" }, "original": { "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-test-0.14.0.2/lsp-test-0.14.0.2.tar.gz" + "url": "https://hackage.haskell.org/package/lsp-test-0.14.0.3/lsp-test-0.14.0.3.tar.gz" } }, "lsp-types": { "flake": false, "locked": { - "narHash": "sha256-HGg4upgirM6/px+vflY5S0Y79gAIDpl32Ad9mbbzTdU=", + "narHash": "sha256-q4XTvIvsLvISjgedpRktJbWsWHSRIQbOx2Z/2u+3s50=", "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-types-1.4.0.1/lsp-types-1.4.0.1.tar.gz" + "url": "https://hackage.haskell.org/package/lsp-types-1.5.0.0/lsp-types-1.5.0.0.tar.gz" }, "original": { "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-types-1.4.0.1/lsp-types-1.4.0.1.tar.gz" + "url": "https://hackage.haskell.org/package/lsp-types-1.5.0.0/lsp-types-1.5.0.0.tar.gz" } }, "myst-parser": { diff --git a/flake.nix b/flake.nix index df92770614..738bcde4da 100644 --- a/flake.nix +++ b/flake.nix @@ -21,15 +21,15 @@ # List of hackage dependencies lsp = { - url = "https://hackage.haskell.org/package/lsp-1.4.0.0/lsp-1.4.0.0.tar.gz"; + url = "https://hackage.haskell.org/package/lsp-1.5.0.0/lsp-1.5.0.0.tar.gz"; flake = false; }; lsp-types = { - url = "https://hackage.haskell.org/package/lsp-types-1.4.0.1/lsp-types-1.4.0.1.tar.gz"; + url = "https://hackage.haskell.org/package/lsp-types-1.5.0.0/lsp-types-1.5.0.0.tar.gz"; flake = false; }; lsp-test = { - url = "https://hackage.haskell.org/package/lsp-test-0.14.0.2/lsp-test-0.14.0.2.tar.gz"; + url = "https://hackage.haskell.org/package/lsp-test-0.14.0.3/lsp-test-0.14.0.3.tar.gz"; flake = false; }; ghc-exactprint-150 = { diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index 99abf39cc6..7d037dcc38 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -46,6 +46,7 @@ library binary, bytestring, case-insensitive, + co-log-core, containers, data-default, deepseq, @@ -69,8 +70,8 @@ library lens, list-t, hiedb == 0.4.1.*, - lsp-types ^>= 1.4.0.1, - lsp ^>= 1.4.0.0 , + lsp-types ^>= 1.5.0.0, + lsp ^>= 1.5.0.0 , monoid-subclasses, mtl, network-uri, @@ -81,7 +82,7 @@ library random, regex-tdfa >= 1.3.1.0, retrie, - rope-utf16-splay, + text-rope, safe, safe-exceptions, hls-graph ^>= 1.7, @@ -421,7 +422,6 @@ test-suite ghcide-tests QuickCheck, quickcheck-instances, random, - rope-utf16-splay, regex-tdfa ^>= 1.3.1, safe, safe-exceptions, @@ -436,6 +436,7 @@ test-suite ghcide-tests tasty-quickcheck, tasty-rerun, text, + text-rope, unordered-containers, vector, if (impl(ghc >= 8.6) && impl(ghc < 9.2)) diff --git a/ghcide/src/Development/IDE/Core/FileStore.hs b/ghcide/src/Development/IDE/Core/FileStore.hs index 697df7c3fd..93a9c0a90f 100644 --- a/ghcide/src/Development/IDE/Core/FileStore.hs +++ b/ghcide/src/Development/IDE/Core/FileStore.hs @@ -28,8 +28,8 @@ import Control.Exception import Control.Monad.Extra import Control.Monad.IO.Class import qualified Data.ByteString as BS -import qualified Data.Rope.UTF16 as Rope import qualified Data.Text as T +import qualified Data.Text.Utf16.Rope as Rope import Data.Time import Data.Time.Clock.POSIX import Development.IDE.Core.FileUtils @@ -188,7 +188,7 @@ getFileContentsImpl file = do time <- use_ GetModificationTime file res <- do mbVirtual <- getVirtualFile file - pure $ Rope.toText . _text <$> mbVirtual + pure $ Rope.toText . _file_text <$> mbVirtual pure ([], Just (time, res)) -- | Returns the modification time and the contents. diff --git a/ghcide/src/Development/IDE/Core/Rules.hs b/ghcide/src/Development/IDE/Core/Rules.hs index 786a4ae156..e3f1c4aaa3 100644 --- a/ghcide/src/Development/IDE/Core/Rules.hs +++ b/ghcide/src/Development/IDE/Core/Rules.hs @@ -92,7 +92,7 @@ import qualified Data.IntMap.Strict as IntMap import Data.List import qualified Data.Map as M import Data.Maybe -import qualified Data.Rope.UTF16 as Rope +import qualified Data.Text.Utf16.Rope as Rope import qualified Data.Set as Set import qualified Data.Text as T import qualified Data.Text.Encoding as T @@ -574,10 +574,10 @@ persistentHieFileRule :: Recorder (WithPriority Log) -> Rules () persistentHieFileRule recorder = addPersistentRule GetHieAst $ \file -> runMaybeT $ do res <- readHieFileForSrcFromDisk recorder file vfsRef <- asks vfsVar - vfsData <- liftIO $ vfsMap <$> readTVarIO vfsRef + vfsData <- liftIO $ _vfsMap <$> readTVarIO vfsRef (currentSource, ver) <- liftIO $ case M.lookup (filePathToUri' file) vfsData of Nothing -> (,Nothing) . T.decodeUtf8 <$> BS.readFile (fromNormalizedFilePath file) - Just vf -> pure (Rope.toText $ _text vf, Just $ _lsp_version vf) + Just vf -> pure (Rope.toText $ _file_text vf, Just $ _lsp_version 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) @@ -1108,8 +1108,8 @@ getLinkableType f = use_ NeedsCompilation f -- needsCompilationRule :: Rules () needsCompilationRule :: NormalizedFilePath -> Action (IdeResultNoDiagnosticsEarlyCutoff (Maybe LinkableType)) -needsCompilationRule file - | "boot" `isSuffixOf` (fromNormalizedFilePath file) = +needsCompilationRule file + | "boot" `isSuffixOf` (fromNormalizedFilePath file) = pure (Just $ encodeLinkableType Nothing, Just Nothing) needsCompilationRule file = do graph <- useNoFile GetModuleGraph diff --git a/ghcide/src/Development/IDE/Core/Shake.hs b/ghcide/src/Development/IDE/Core/Shake.hs index cd37c1b26f..d341838e0a 100644 --- a/ghcide/src/Development/IDE/Core/Shake.hs +++ b/ghcide/src/Development/IDE/Core/Shake.hs @@ -164,7 +164,7 @@ import qualified Language.LSP.Server as LSP import Language.LSP.Types import qualified Language.LSP.Types as LSP import Language.LSP.Types.Capabilities -import Language.LSP.VFS +import Language.LSP.VFS hiding (start) import qualified "list-t" ListT import OpenTelemetry.Eventlog import qualified StmContainers.Map as STM @@ -323,7 +323,7 @@ class Typeable a => IsIdeGlobal a where -- | Read a virtual file from the current snapshot getVirtualFile :: NormalizedFilePath -> Action (Maybe VirtualFile) getVirtualFile nf = do - vfs <- fmap vfsMap . liftIO . readTVarIO . vfsVar =<< getShakeExtras + vfs <- fmap _vfsMap . liftIO . readTVarIO . vfsVar =<< getShakeExtras pure $! Map.lookup (filePathToUri' nf) vfs -- Don't leak a reference to the entire map -- Take a snapshot of the current LSP VFS @@ -706,6 +706,7 @@ shakeRestart recorder IdeState{..} vfs reason acts = backlog <- readTVarIO $ dirtyKeys shakeExtras queue <- atomicallyNamed "actionQueue - peek" $ peekInProgress $ actionQueue shakeExtras + -- this log is required by tests log Debug $ LogBuildSessionRestart reason queue backlog stopTime res ) -- It is crucial to be masked here, otherwise we can get killed diff --git a/ghcide/src/Development/IDE/LSP/LanguageServer.hs b/ghcide/src/Development/IDE/LSP/LanguageServer.hs index 798ea40a68..4d717dd999 100644 --- a/ghcide/src/Development/IDE/LSP/LanguageServer.hs +++ b/ghcide/src/Development/IDE/LSP/LanguageServer.hs @@ -34,17 +34,17 @@ import UnliftIO.Concurrent import UnliftIO.Directory import UnliftIO.Exception +import qualified Colog.Core as Colog +import Control.Monad.IO.Unlift (MonadUnliftIO) import Development.IDE.Core.IdeConfiguration -import Development.IDE.Core.Shake hiding (Log) +import Development.IDE.Core.Shake hiding (Log, Priority) import Development.IDE.Core.Tracing -import Development.IDE.Types.Logger - -import Control.Monad.IO.Unlift (MonadUnliftIO) -import Data.Kind (Type) import qualified Development.IDE.Session as Session +import Development.IDE.Types.Logger import qualified Development.IDE.Types.Logger as Logger import Development.IDE.Types.Shake (WithHieDb) import Language.LSP.Server (LanguageContextEnv, + LspServerLog, type (<~>)) import System.IO.Unsafe (unsafeInterleaveIO) @@ -55,6 +55,7 @@ data Log | LogReactorThreadStopped | LogCancelledRequest !SomeLspId | LogSession Session.Log + | LogLspServer LspServerLog deriving Show instance Pretty Log where @@ -74,13 +75,15 @@ instance Pretty Log where LogCancelledRequest requestId -> "Cancelled request" <+> viaShow requestId LogSession log -> pretty log + LogLspServer log -> pretty log -- used to smuggle RankNType WithHieDb through dbMVar newtype WithHieDbShield = WithHieDbShield WithHieDb runLanguageServer :: forall config a m. (Show config) - => LSP.Options + => Recorder (WithPriority Log) + -> LSP.Options -> Handle -- input -> Handle -- output -> config @@ -90,7 +93,7 @@ runLanguageServer LSP.Handlers (m config), (LanguageContextEnv config, a) -> m config <~> IO)) -> IO () -runLanguageServer options inH outH defaultConfig onConfigurationChange setup = do +runLanguageServer recorder options inH outH defaultConfig onConfigurationChange setup = do -- This MVar becomes full when the server thread exits or we receive exit message from client. -- LSP server will be canceled when it's full. clientMsgVar <- newEmptyMVar @@ -106,8 +109,16 @@ runLanguageServer options inH outH defaultConfig onConfigurationChange setup = d , LSP.options = modifyOptions options } + let lspCologAction :: MonadIO m2 => Colog.LogAction m2 (Colog.WithSeverity LspServerLog) + lspCologAction = toCologActionWithPrio $ cfilter + -- filter out bad logs in lsp, see: https://github.com/haskell/lsp/issues/447 + (\msg -> priority msg >= Info) + (cmapWithPrio LogLspServer recorder) + void $ untilMVar clientMsgVar $ void $ LSP.runServerWithHandles + lspCologAction + lspCologAction inH outH serverDefinition diff --git a/ghcide/src/Development/IDE/Main.hs b/ghcide/src/Development/IDE/Main.hs index 585a2badb7..7ce2ac47d5 100644 --- a/ghcide/src/Development/IDE/Main.hs +++ b/ghcide/src/Development/IDE/Main.hs @@ -385,7 +385,7 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re let setup = setupLSP (cmapWithPrio LogLanguageServer recorder) argsGetHieDbLoc (pluginHandlers plugins) getIdeState - runLanguageServer options inH outH argsDefaultHlsConfig argsOnConfigChange setup + runLanguageServer (cmapWithPrio LogLanguageServer recorder) options inH outH argsDefaultHlsConfig argsOnConfigChange setup dumpSTMStats Check argFiles -> do dir <- maybe IO.getCurrentDirectory return argsProjectRoot diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index a3eb4f4774..35f89ac108 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -36,9 +36,9 @@ import qualified Data.List.NonEmpty as NE import qualified Data.Map.Strict as M import Data.Maybe import Data.Ord (comparing) -import qualified Data.Rope.UTF16 as Rope import qualified Data.Set as S import qualified Data.Text as T +import qualified Data.Text.Utf16.Rope as Rope import Data.Tuple.Extra (fst3) import Development.IDE.Core.Rules import Development.IDE.Core.RuleTypes @@ -75,7 +75,8 @@ import Language.LSP.Types (CodeAction ( WorkspaceEdit (WorkspaceEdit, _changeAnnotations, _changes, _documentChanges), type (|?) (InR), uriToFilePath) -import Language.LSP.VFS +import Language.LSP.VFS (VirtualFile, + _file_text) import Text.Regex.TDFA (mrAfter, (=~), (=~~)) #if MIN_VERSION_ghc(9,2,0) @@ -109,7 +110,7 @@ codeAction codeAction state _ (CodeActionParams _ _ (TextDocumentIdentifier uri) _range CodeActionContext{_diagnostics=List xs}) = do contents <- LSP.getVirtualFile $ toNormalizedUri uri liftIO $ do - let text = Rope.toText . (_text :: VirtualFile -> Rope.Rope) <$> contents + let text = Rope.toText . (_file_text :: VirtualFile -> Rope.Rope) <$> contents mbFile = toNormalizedFilePath' <$> uriToFilePath uri diag <- atomically $ fmap (\(_, _, d) -> d) . filter (\(p, _, _) -> mbFile == Just p) <$> getDiagnostics state (join -> parsedModule) <- runAction "GhcideCodeActions.getParsedModule" state $ getParsedModule `traverse` mbFile diff --git a/ghcide/src/Development/IDE/Plugin/Test.hs b/ghcide/src/Development/IDE/Plugin/Test.hs index 9932e1dc3a..c6163ab105 100644 --- a/ghcide/src/Development/IDE/Plugin/Test.hs +++ b/ghcide/src/Development/IDE/Plugin/Test.hs @@ -27,7 +27,6 @@ import Data.Text (Text, pack) import Development.IDE.Core.OfInterest (getFilesOfInterest) import Development.IDE.Core.Rules import Development.IDE.Core.RuleTypes -import Development.IDE.Core.Service import Development.IDE.Core.Shake import Development.IDE.GHC.Compat import Development.IDE.Graph (Action) diff --git a/ghcide/src/Development/IDE/Types/Logger.hs b/ghcide/src/Development/IDE/Types/Logger.hs index 6673707204..f5e68d7032 100644 --- a/ghcide/src/Development/IDE/Types/Logger.hs +++ b/ghcide/src/Development/IDE/Types/Logger.hs @@ -27,6 +27,7 @@ module Development.IDE.Types.Logger , lspClientLogRecorder , module PrettyPrinterModule , renderStrict + , toCologActionWithPrio ) where import Control.Concurrent (myThreadId) @@ -59,7 +60,6 @@ import Language.LSP.Server import qualified Language.LSP.Server as LSP import Language.LSP.Types (LogMessageParams (..), MessageType (..), - ResponseError, SMethod (SWindowLogMessage, SWindowShowMessage), ShowMessageParams (..)) #if MIN_VERSION_prettyprinter(1,7,0) @@ -69,11 +69,10 @@ import Prettyprinter.Render.Text (renderStrict) import Data.Text.Prettyprint.Doc as PrettyPrinterModule import Data.Text.Prettyprint.Doc.Render.Text (renderStrict) #endif -import Control.Lens ((^.)) -import Ide.Types (CommandId (CommandId), - PluginId (PluginId)) -import Language.LSP.Types.Lens (HasCode (code), - HasMessage (message)) +import Colog.Core (LogAction (..), + Severity, + WithSeverity (..)) +import qualified Colog.Core as Colog import System.IO (Handle, IOMode (AppendMode), hClose, hFlush, @@ -381,3 +380,14 @@ priorityToLsp = Info -> MtInfo Warning -> MtWarning Error -> MtError + +toCologActionWithPrio :: (MonadIO m, HasCallStack) => Recorder (WithPriority msg) -> LogAction m (WithSeverity msg) +toCologActionWithPrio (Recorder _logger) = LogAction $ \WithSeverity{..} -> do + let priority = severityToPriority getSeverity + _logger $ WithPriority priority callStack getMsg + where + severityToPriority :: Severity -> Priority + severityToPriority Colog.Debug = Debug + severityToPriority Colog.Info = Info + severityToPriority Colog.Warning = Warning + severityToPriority Colog.Error = Error diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 24669ad7a5..c4a4b082a7 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -28,10 +28,10 @@ import Data.Default import Data.Foldable import Data.List.Extra import Data.Maybe -import Data.Rope.UTF16 (Rope) -import qualified Data.Rope.UTF16 as Rope import qualified Data.Set as Set import qualified Data.Text as T +import Data.Text.Utf16.Rope (Rope) +import qualified Data.Text.Utf16.Rope as Rope import Development.IDE.Core.PositionMapping (PositionResult (..), fromCurrent, positionResultToMaybe, @@ -78,7 +78,7 @@ import qualified Language.LSP.Types.Lens as Lens (label) import qualified Language.LSP.Types.Lens as Lsp (diagnostics, message, params) -import Language.LSP.VFS (applyChange) +import Language.LSP.VFS (VfsLog, applyChange) import Network.URI import System.Directory import System.Environment.Blank (getEnv, setEnv, @@ -98,6 +98,7 @@ import Control.Concurrent.Async import Control.Lens (to, (.~), (^.)) import Control.Monad.Extra (whenJust) import Data.Function ((&)) +import Data.Functor.Identity (runIdentity) import Data.IORef import Data.IORef.Extra (atomicModifyIORef_) import Data.String (IsString (fromString)) @@ -116,7 +117,8 @@ import Development.IDE.Types.Logger (Logger (Logger), WithPriority (WithPriority, priority), cfilter, cmapWithPrio, - makeDefaultStderrRecorder) + makeDefaultStderrRecorder, + toCologActionWithPrio) import qualified FuzzySearch import GHC.Stack (emptyCallStack) import qualified HieDbRetry @@ -128,6 +130,8 @@ import Language.LSP.Types.Lens (didChangeWatchedFiles import qualified Language.LSP.Types.Lens as L import qualified Progress import System.Time.Extra +import qualified Test.QuickCheck.Monadic as MonadicQuickCheck +import Test.QuickCheck.Monadic (forAllM, monadicIO) import Test.Tasty import Test.Tasty.ExpectedFailure import Test.Tasty.HUnit @@ -139,11 +143,13 @@ import Text.Regex.TDFA ((=~)) data Log = LogGhcIde Ghcide.Log | LogIDEMain IDE.Log + | LogVfs VfsLog instance Pretty Log where pretty = \case LogGhcIde log -> pretty log LogIDEMain log -> pretty log + LogVfs log -> pretty log -- | Wait for the next progress begin step waitForProgressBegin :: Session () @@ -210,7 +216,7 @@ main = do , safeTests , unitTests recorder logger , haddockTests - , positionMappingTests + , positionMappingTests recorder , watchedFilesTests , cradleTests , dependentFileTest @@ -250,7 +256,7 @@ initializeResponseTests = withResource acquire release tests where , chk " find references" _referencesProvider (Just $ InL True) , chk " doc highlight" _documentHighlightProvider (Just $ InL True) , chk " doc symbol" _documentSymbolProvider (Just $ InL True) - , chk " workspace symbol" _workspaceSymbolProvider (Just True) + , chk " workspace symbol" _workspaceSymbolProvider (Just $ InL True) , chk " code action" _codeActionProvider (Just $ InL True) , chk " code lens" _codeLensProvider (Just $ CodeLensOptions (Just False) (Just False)) , chk "NO doc formatting" _documentFormattingProvider (Just $ InL False) @@ -4357,12 +4363,12 @@ findDefinitionAndHoverTests = let typeDefinitionTests = [ tst (getTypeDefinitions, checkDefs) aaaL14 sourceFilePath (pure tcData) "Saturated data con" , tst (getTypeDefinitions, checkDefs) aL20 sourceFilePath (pure [ExpectNoDefinitions]) "Polymorphic variable"] - - recordDotSyntaxTests + + recordDotSyntaxTests | ghcVersion >= GHC92 = - [ tst (getHover, checkHover) (Position 19 24) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["x :: MyRecord"]]) "hover over parent" - , tst (getHover, checkHover) (Position 19 25) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["_ :: MyChild"]]) "hover over dot shows child" - , tst (getHover, checkHover) (Position 19 26) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["_ :: MyChild"]]) "hover over child" + [ tst (getHover, checkHover) (Position 19 24) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["x :: MyRecord"]]) "hover over parent" + , tst (getHover, checkHover) (Position 19 25) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["_ :: MyChild"]]) "hover over dot shows child" + , tst (getHover, checkHover) (Position 19 26) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["_ :: MyChild"]]) "hover over child" ] | otherwise = [] @@ -6374,7 +6380,8 @@ clientSettingsTest = testGroup "client settings handling" void $ skipManyTill anyMessage $ message SClientRegisterCapability void $ createDoc "A.hs" "haskell" "module A where" waitForProgressDone - sendNotification SWorkspaceDidChangeConfiguration (DidChangeConfigurationParams (toJSON ("" :: String))) + sendNotification SWorkspaceDidChangeConfiguration + (DidChangeConfigurationParams (toJSON (mempty :: A.Object))) skipManyTill anyMessage restartingBuildSession ] @@ -6869,10 +6876,8 @@ testIde recorder arguments session = do flip finally (setCurrentDirectory cwd) $ withAsync server $ \_ -> runSessionWithHandles hInWrite hOutRead config lspTestCaps projDir session - - -positionMappingTests :: TestTree -positionMappingTests = +positionMappingTests :: Recorder (WithPriority Log) -> TestTree +positionMappingTests recorder = testGroup "position mapping" [ testGroup "toCurrent" [ testCase "before" $ @@ -6990,7 +6995,8 @@ positionMappingTests = rope <- genRope range <- genRange rope PrintableText replacement <- arbitrary - let newRope = applyChange rope (TextDocumentContentChangeEvent (Just range) Nothing replacement) + let newRope = runIdentity $ applyChange mempty rope + (TextDocumentContentChangeEvent (Just range) Nothing replacement) newPos <- genPosition newRope pure (range, replacement, newPos) forAll @@ -7013,19 +7019,19 @@ genRope = Rope.fromText . getPrintableText <$> arbitrary genPosition :: Rope -> Gen Position genPosition r = do - let rows = Rope.rows r + let rows :: Int = fromIntegral $ Rope.lengthInLines r row <- choose (0, max 0 $ rows - 1) `suchThat` inBounds @UInt - let columns = Rope.columns (nthLine row r) + let columns = T.length (nthLine (fromIntegral row) r) column <- choose (0, max 0 $ columns - 1) `suchThat` inBounds @UInt pure $ Position (fromIntegral row) (fromIntegral column) genRange :: Rope -> Gen Range genRange r = do - let rows = Rope.rows r + let rows :: Int = fromIntegral $ Rope.lengthInLines r startPos@(Position startLine startColumn) <- genPosition r let maxLineDiff = max 0 $ rows - 1 - fromIntegral startLine endLine <- choose (fromIntegral startLine, fromIntegral startLine + maxLineDiff) `suchThat` inBounds @UInt - let columns = Rope.columns (nthLine (fromIntegral endLine) r) + let columns = T.length (nthLine (fromIntegral endLine) r) endColumn <- if fromIntegral startLine == endLine then choose (fromIntegral startColumn, columns) @@ -7037,12 +7043,10 @@ inBounds :: forall b a . (Integral a, Integral b, Bounded b) => a -> Bool inBounds a = let i = toInteger a in i <= toInteger (maxBound @b) && i >= toInteger (minBound @b) -- | Get the ith line of a rope, starting from 0. Trailing newline not included. -nthLine :: Int -> Rope -> Rope +nthLine :: Int -> Rope -> T.Text nthLine i r - | i < 0 = error $ "Negative line number: " <> show i - | i == 0 && Rope.rows r == 0 = r - | i >= Rope.rows r = error $ "Row number out of bounds: " <> show i <> "/" <> show (Rope.rows r) - | otherwise = Rope.takeWhile (/= '\n') $ fst $ Rope.splitAtLine 1 $ snd $ Rope.splitAtLine (i - 1) r + | Rope.null r = "" + | otherwise = Rope.lines r !! i getWatchedFilesSubscriptionsUntil :: forall m. SServerMethod m -> Session [DidChangeWatchedFilesRegistrationOptions] getWatchedFilesSubscriptionsUntil m = do diff --git a/hls-plugin-api/hls-plugin-api.cabal b/hls-plugin-api/hls-plugin-api.cabal index 0d79ec3bac..67e57b578b 100644 --- a/hls-plugin-api/hls-plugin-api.cabal +++ b/hls-plugin-api/hls-plugin-api.cabal @@ -49,7 +49,7 @@ library , hls-graph ^>= 1.7 , lens , lens-aeson - , lsp >=1.4.0.0 && < 1.6 + , lsp ^>=1.5.0.0 , opentelemetry >=0.4 , optparse-applicative , process diff --git a/hls-test-utils/hls-test-utils.cabal b/hls-test-utils/hls-test-utils.cabal index 4a90797316..3335f5d8d3 100644 --- a/hls-test-utils/hls-test-utils.cabal +++ b/hls-test-utils/hls-test-utils.cabal @@ -45,9 +45,9 @@ library , hls-graph , hls-plugin-api ^>=1.3 || ^>=1.4 , lens - , lsp ^>=1.4 + , lsp ^>=1.5.0.0 , lsp-test ^>=0.14 - , lsp-types ^>=1.4.0.1 + , lsp-types ^>=1.5.0.0 , tasty , tasty-expected-failure , tasty-golden diff --git a/hls-test-utils/src/Test/Hls.hs b/hls-test-utils/src/Test/Hls.hs index c0af35b29a..768c67d384 100644 --- a/hls-test-utils/src/Test/Hls.hs +++ b/hls-test-utils/src/Test/Hls.hs @@ -189,57 +189,56 @@ runSessionWithServer' :: Session a -> IO a runSessionWithServer' plugins conf sconf caps root s = withLock lock $ keepCurrentDirectory $ do - (inR, inW) <- createPipe - (outR, outW) <- createPipe - - docWithPriorityRecorder <- makeDefaultStderrRecorder Nothing Debug - - logStdErr <- fromMaybe "0" <$> lookupEnv "LSP_TEST_LOG_STDERR" - - let - docWithFilteredPriorityRecorder@Recorder{ logger_ } = - if logStdErr == "0" then mempty - else cfilter (\WithPriority{ priority } -> priority >= Debug) docWithPriorityRecorder - - -- exists until old logging style is phased out - logger = Logger $ \p m -> logger_ (WithPriority p emptyCallStack (pretty m)) - - recorder = cmapWithPrio pretty docWithFilteredPriorityRecorder - - arguments@Arguments{ argsHlsPlugins, argsIdeOptions, argsLogger } = defaultArguments (cmapWithPrio LogIDEMain recorder) logger - - hlsPlugins = - plugins - ++ [Test.blockCommandDescriptor "block-command", Test.plugin] - ++ idePluginsToPluginDesc argsHlsPlugins - ideOptions = \config ghcSession -> - let defIdeOptions = argsIdeOptions config ghcSession - in defIdeOptions - { optTesting = IdeTesting True - , optCheckProject = pure False - } - - server <- - async $ - Ghcide.defaultMain - (cmapWithPrio LogIDEMain recorder) - arguments - { argsHandleIn = pure inR - , argsHandleOut = pure outW - , argsDefaultHlsConfig = conf - , argsLogger = argsLogger - , argsIdeOptions = ideOptions - , argsHlsPlugins = pluginDescToIdePlugins hlsPlugins } - - x <- runSessionWithHandles inW outR sconf caps root s - hClose inW - timeout 3 (wait server) >>= \case - Just () -> pure () - Nothing -> do - putStrLn "Server does not exit in 3s, canceling the async task..." - (t, _) <- duration $ cancel server - putStrLn $ "Finishing canceling (took " <> showDuration t <> "s)" - pure x + (inR, inW) <- createPipe + (outR, outW) <- createPipe + + docWithPriorityRecorder <- makeDefaultStderrRecorder Nothing Debug + + logStdErr <- fromMaybe "0" <$> lookupEnv "LSP_TEST_LOG_STDERR" + + let + docWithFilteredPriorityRecorder@Recorder{ logger_ } = + if logStdErr == "0" then mempty + else cfilter (\WithPriority{ priority } -> priority >= Debug) docWithPriorityRecorder + + -- exists until old logging style is phased out + logger = Logger $ \p m -> logger_ (WithPriority p emptyCallStack (pretty m)) + + recorder = cmapWithPrio pretty docWithFilteredPriorityRecorder + + arguments@Arguments{ argsHlsPlugins, argsIdeOptions, argsLogger } = defaultArguments (cmapWithPrio LogIDEMain recorder) logger + + hlsPlugins = + plugins + ++ [Test.blockCommandDescriptor "block-command", Test.plugin] + ++ idePluginsToPluginDesc argsHlsPlugins + ideOptions config ghcSession = + let defIdeOptions = argsIdeOptions config ghcSession + in defIdeOptions + { optTesting = IdeTesting True + , optCheckProject = pure False + } + + server <- async $ + Ghcide.defaultMain (cmapWithPrio LogIDEMain recorder) + arguments + { argsHandleIn = pure inR + , argsHandleOut = pure outW + , argsDefaultHlsConfig = conf + , argsLogger = argsLogger + , argsIdeOptions = ideOptions + , argsHlsPlugins = pluginDescToIdePlugins hlsPlugins + } + + x <- runSessionWithHandles inW outR sconf caps root s + hClose inW + timeout 3 (wait server) >>= \case + Just () -> pure () + Nothing -> do + putStrLn "Server does not exit in 3s, canceling the async task..." + (t, _) <- duration $ cancel server + putStrLn $ "Finishing canceling (took " <> showDuration t <> "s)" + pure x -- | Wait for the next progress end step waitForProgressDone :: Session () diff --git a/hls-test-utils/src/Test/Hls/Util.hs b/hls-test-utils/src/Test/Hls/Util.hs index 519e15caf7..322a2bf867 100644 --- a/hls-test-utils/src/Test/Hls/Util.hs +++ b/hls-test-utils/src/Test/Hls/Util.hs @@ -3,7 +3,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeOperators #-} module Test.Hls.Util @@ -46,7 +45,7 @@ where import Control.Applicative.Combinators (skipManyTill, (<|>)) import Control.Exception (catch, throwIO) -import Control.Lens ((^.)) +import Control.Lens ((&), (?~), (^.)) import Control.Monad import Control.Monad.IO.Class import qualified Data.Aeson as A @@ -59,6 +58,7 @@ import Development.IDE (GhcVersion (..), ghcVersion) import qualified Language.LSP.Test as Test import Language.LSP.Types hiding (Reason (..)) import qualified Language.LSP.Types.Capabilities as C +import Language.LSP.Types.Lens (textDocument) import qualified Language.LSP.Types.Lens as L import System.Directory import System.Environment @@ -74,13 +74,13 @@ import Test.Tasty.HUnit (Assertion, assertFailure, (@?=)) noLiteralCaps :: C.ClientCapabilities -noLiteralCaps = def { C._textDocument = Just textDocumentCaps } +noLiteralCaps = def & textDocument ?~ textDocumentCaps where textDocumentCaps = def { C._codeAction = Just codeActionCaps } codeActionCaps = CodeActionClientCapabilities (Just True) Nothing Nothing Nothing Nothing Nothing Nothing codeActionSupportCaps :: C.ClientCapabilities -codeActionSupportCaps = def { C._textDocument = Just textDocumentCaps } +codeActionSupportCaps = def & textDocument ?~ textDocumentCaps where textDocumentCaps = def { C._codeAction = Just codeActionCaps } codeActionCaps = CodeActionClientCapabilities (Just True) (Just literalSupport) (Just True) Nothing Nothing Nothing Nothing @@ -297,16 +297,7 @@ waitForDiagnosticsFrom doc = do else return diags waitForDiagnosticsFromSource :: TextDocumentIdentifier -> String -> Test.Session [Diagnostic] -waitForDiagnosticsFromSource doc src = do - diagsNot <- skipManyTill Test.anyMessage (Test.message STextDocumentPublishDiagnostics) - let (List diags) = diagsNot ^. L.params . L.diagnostics - let res = filter matches diags - if doc ^. L.uri /= diagsNot ^. L.params . L.uri || null res - then waitForDiagnosticsFromSource doc src - else return res - where - matches :: Diagnostic -> Bool - matches d = d ^. L.source == Just (T.pack src) +waitForDiagnosticsFromSource = waitForDiagnosticsFromSourceWithTimeout 5 -- | wait for @timeout@ seconds and report an assertion failure -- if any diagnostic messages arrive in that period @@ -322,38 +313,31 @@ expectNoMoreDiagnostics timeout doc src = do -- If timeout is 0 it will wait until the session timeout waitForDiagnosticsFromSourceWithTimeout :: Seconds -> TextDocumentIdentifier -> String -> Test.Session [Diagnostic] waitForDiagnosticsFromSourceWithTimeout timeout document source = do - when (timeout > 0) $ do + when (timeout > 0) $ -- Give any further diagnostic messages time to arrive. liftIO $ sleep timeout -- Send a dummy message to provoke a response from the server. -- This guarantees that we have at least one message to -- process, so message won't block or timeout. - void $ Test.sendNotification (SCustomMethod "non-existent-method") A.Null - handleMessages + testId <- Test.sendRequest (SCustomMethod "test") A.Null + handleMessages testId where matches :: Diagnostic -> Bool matches d = d ^. L.source == Just (T.pack source) - handleMessages = handleDiagnostic <|> handleCustomMethodResponse <|> ignoreOthers - handleDiagnostic = do + handleMessages testId = handleDiagnostic testId <|> handleCustomMethodResponse testId <|> ignoreOthers testId + handleDiagnostic testId = do diagsNot <- Test.message STextDocumentPublishDiagnostics let fileUri = diagsNot ^. L.params . L.uri (List diags) = diagsNot ^. L.params . L.diagnostics res = filter matches diags if fileUri == document ^. L.uri && not (null res) - then return diags else handleMessages - handleCustomMethodResponse = - -- the CustomClientMethod triggers a RspCustomServer - -- handle that and then exit - void (Test.satisfyMaybe responseForNonExistentMethod) >> return [] - - responseForNonExistentMethod :: FromServerMessage -> Maybe FromServerMessage - responseForNonExistentMethod notif - | FromServerMess SWindowLogMessage logMsg <- notif, - "non-existent-method" `T.isInfixOf` (logMsg ^. L.params . L.message) = Just notif - | otherwise = Nothing - - ignoreOthers = void Test.anyMessage >> handleMessages + then return res else handleMessages testId + handleCustomMethodResponse testId = do + _ <- Test.responseForId (SCustomMethod "test") testId + pure [] + + ignoreOthers testId = void Test.anyMessage >> handleMessages testId failIfSessionTimeout :: IO a -> IO a failIfSessionTimeout action = action `catch` errorHandler diff --git a/plugins/hls-floskell-plugin/src/Ide/Plugin/Floskell.hs b/plugins/hls-floskell-plugin/src/Ide/Plugin/Floskell.hs index 70fe309b66..e59e0e9e92 100644 --- a/plugins/hls-floskell-plugin/src/Ide/Plugin/Floskell.hs +++ b/plugins/hls-floskell-plugin/src/Ide/Plugin/Floskell.hs @@ -33,7 +33,7 @@ provider _ideState typ contents fp _ = liftIO $ do config <- findConfigOrDefault file let (range, selectedContents) = case typ of FormatText -> (fullRange contents, contents) - FormatRange r -> (r, extractRange r contents) + FormatRange r -> (normalize r, extractRange r contents) result = reformat config (Just file) . TL.encodeUtf8 $ TL.fromStrict selectedContents case result of Left err -> pure $ Left $ responseError $ T.pack $ "floskellCmd: " ++ err diff --git a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs index 6d7dbea5d5..b83423254a 100644 --- a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs +++ b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs @@ -48,10 +48,6 @@ import Ide.Types import Language.LSP.Server import Language.LSP.Types -instance Hashable Location -instance Hashable Range -instance Hashable Position -instance Hashable UInt instance Hashable (Mod a) where hash n = hash (unMod n) descriptor :: PluginId -> PluginDescriptor IdeState diff --git a/stack-lts16.yaml b/stack-lts16.yaml index 32a1e3f5ba..5460e8940f 100644 --- a/stack-lts16.yaml +++ b/stack-lts16.yaml @@ -85,9 +85,9 @@ extra-deps: - constraints-extras-0.3.0.2@sha256:013b8d0392582c6ca068e226718a4fe8be8e22321cc0634f6115505bf377ad26,1853 - some-1.0.1@sha256:26e5bab7276f48b25ea8660d3fd1166c0f20fd497dac879a40f408e23211f93e,2055 - unliftio-core-0.2.0.1@sha256:9b3e44ea9aacacbfc35b3b54015af450091916ac3618a41868ebf6546977659a,1082 - - lsp-1.4.0.0 - - lsp-types-1.4.0.1 - - lsp-test-0.14.0.2 + - lsp-1.5.0.0 + - lsp-types-1.5.0.0 + - lsp-test-0.14.0.3 - stm-containers-1.1.0.4 - stm-hamt-1.2.0.6@sha256:fba86ccb4b45c5706c19b0e1315ba63dcac3b5d71de945ec001ba921fae80061,3972 - primitive-extras-0.10.1 @@ -100,6 +100,8 @@ extra-deps: - trial-0.0.0.0@sha256:834d3be439dc9b52a759a45a4d3944e5e55c3d50fd5874003147cc1f6231d4aa,4301 - trial-optparse-applicative-0.0.0.0@sha256:ba05edfc327a281766df5e0f44d91229e6a98afaf59abe1894b293453f076192,2449 - trial-tomland-0.0.0.0@sha256:743a9baaa36891ed3a44618fdfd5bc4ed9afc39cf9b9fa23ea1b96f3787f5ec0,2526 + - text-rope-0.2 + - co-log-core-0.3.1.0 configure-options: ghcide: diff --git a/stack-lts19.yaml b/stack-lts19.yaml index 53c00671e8..219dc7e820 100644 --- a/stack-lts19.yaml +++ b/stack-lts19.yaml @@ -53,6 +53,10 @@ extra-deps: - refinery-0.4.0.0@sha256:fe3a43add8ff1db5cfffee7e7694c86128b1dfe62c541f26e25a8eadf9585610,1663 - retrie-1.1.0.0 - stylish-haskell-0.14.2.0@sha256:fffe1c13ad4c2678cf28a7470cac5d3bf20c71c36f09969e3e5f186787cceb7c,4321 +- lsp-1.5.0.0 +- lsp-types-1.5.0.0 +- lsp-test-0.14.0.3 +- co-log-core-0.3.1.0 configure-options: ghcide: diff --git a/stack.yaml b/stack.yaml index 72e06a135d..6ea299746c 100644 --- a/stack.yaml +++ b/stack.yaml @@ -39,12 +39,12 @@ extra-deps: - hiedb-0.4.1.0@sha256:fb20c657d9ecc91701b00dffcf4bbd77cb83720a1f9d867badd77ea227973135,2875 - implicit-hie-0.1.2.7@sha256:82bbbb1a8c05f99c8af3c16ac53e80c8648d8bf047b25ed5ce45a135bd736907,3122 - implicit-hie-cradle-0.5.0.0@sha256:4276f60f3a59bc22df03fd918f73bca9f777de9568f85e3a8be8bd7566234a59,2368 -- lsp-1.4.0.0@sha256:d992cb88d6212f113baf372404c141a6bea14c436baa64ea6e4f01b6188c575b,5088 -- lsp-test-0.14.0.2@sha256:d62d2af45508f04c5fcad23e469c45b37ca19760cee15b025a0eb499cbd28050,4663 -- lsp-types-1.4.0.1@sha256:b902952df7becc1827947ee3ff1cd8c746aa8f9f80db330db20e2fdf1b6089e8,4504 +- lsp-1.5.0.0 +- lsp-test-0.14.0.3 +- lsp-types-1.5.0.0 - monad-dijkstra-0.1.1.3@sha256:d2fc098d7c122555e726830a12ae0423ac187f89de9228f32e56e2f6fc2238e1,1900 - retrie-1.2.0.1 -- rope-utf16-splay-0.3.2.0 +- co-log-core-0.3.1.0 # currently needed for ghcide>extra, etc. allow-newer: true From e20b026c507d73d298b4c123cd4ee0c78bf70e69 Mon Sep 17 00:00:00 2001 From: Andy Date: Fri, 12 Aug 2022 17:42:27 +0200 Subject: [PATCH 052/213] Probe-tools: Print stack ghc version (#3093) * Probe-tools: Print stack ghc version The ghc version on the $PATH is often not relevant for stack projects. The ghc version stack is using is printed in addition. * Probetools: cradle ghc version added to wrapper * Revert stack ghc changes to Ide.Main * Update exe/Wrapper.hs Co-authored-by: fendor * Probe tools: Print version with padded spaces Addressing Co-authored-by: fendor --- exe/Wrapper.hs | 5 +++++ src/Ide/Version.hs | 20 ++++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/exe/Wrapper.hs b/exe/Wrapper.hs index 0122cf319f..e4f50f2ad4 100644 --- a/exe/Wrapper.hs +++ b/exe/Wrapper.hs @@ -14,6 +14,7 @@ module Main where import Control.Monad.Extra import Data.Char (isSpace) import Data.Default +import Data.Either.Extra (eitherToMaybe) import Data.Foldable import Data.List import Data.Void @@ -79,6 +80,10 @@ main = do putStrLn hlsVer putStrLn "Tool versions found on the $PATH" putStrLn $ showProgramVersionOfInterest programsOfInterest + putStrLn "Tool versions in your project" + cradle <- findProjectCradle' False + ghcVersion <- runExceptT $ getRuntimeGhcVersion' cradle + putStrLn $ showProgramVersion "ghc" $ mkVersion =<< eitherToMaybe ghcVersion VersionMode PrintVersion -> putStrLn hlsVer diff --git a/src/Ide/Version.hs b/src/Ide/Version.hs index e963767087..1c67c0da46 100644 --- a/src/Ide/Version.hs +++ b/src/Ide/Version.hs @@ -46,13 +46,17 @@ data ProgramsOfInterest = ProgramsOfInterest showProgramVersionOfInterest :: ProgramsOfInterest -> String showProgramVersionOfInterest ProgramsOfInterest {..} = unlines - [ "cabal:\t\t" ++ showVersionWithDefault cabalVersion - , "stack:\t\t" ++ showVersionWithDefault stackVersion - , "ghc:\t\t" ++ showVersionWithDefault ghcVersion + [ showProgramVersion "cabal" cabalVersion + , showProgramVersion "stack" stackVersion + , showProgramVersion "ghc" ghcVersion ] + +showProgramVersion :: String -> Maybe Version -> String +showProgramVersion name version = + pad 16 (name ++ ":") ++ showVersionWithDefault version where - showVersionWithDefault :: Maybe Version -> String showVersionWithDefault = maybe "Not found" showVersion + pad n s = s ++ replicate (n - length s) ' ' findProgramVersions :: IO ProgramsOfInterest findProgramVersions = ProgramsOfInterest @@ -69,8 +73,11 @@ findVersionOf tool = Nothing -> pure Nothing Just path -> readProcessWithExitCode path ["--numeric-version"] "" >>= \case - (ExitSuccess, sout, _) -> pure $ consumeParser myVersionParser sout + (ExitSuccess, sout, _) -> pure $ mkVersion sout _ -> pure Nothing + +mkVersion :: String -> Maybe Version +mkVersion = consumeParser myVersionParser where myVersionParser = do skipSpaces @@ -79,4 +86,5 @@ findVersionOf tool = pure version consumeParser :: ReadP a -> String -> Maybe a - consumeParser p input = listToMaybe $ map fst . filter (null . snd) $ readP_to_S p input + consumeParser p input = + listToMaybe $ map fst . filter (null . snd) $ readP_to_S p input From 548ca17cc73546407ac6e2cd26068a12f2d56af7 Mon Sep 17 00:00:00 2001 From: Brandon Chinn Date: Sun, 14 Aug 2022 02:43:46 -0700 Subject: [PATCH 053/213] Support fourmolu-0.8.1.0 (#3103) --- plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu/Shim.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu/Shim.hs b/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu/Shim.hs index 976e522d93..fcb6099ad8 100644 --- a/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu/Shim.hs +++ b/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu/Shim.hs @@ -31,7 +31,9 @@ cfgFileFixities :: FourmoluConfig -> FixityMap cfgFileFixities _ = mempty #endif -#if MIN_VERSION_fourmolu(0,7,0) +#if MIN_VERSION_fourmolu(0,8,1) +-- emptyConfig now provided +#elif MIN_VERSION_fourmolu(0,7,0) emptyConfig :: FourmoluConfig emptyConfig = FourmoluConfig From 49373fd01465d56146fa5f1ceded3907ad520ca0 Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Sun, 14 Aug 2022 22:44:59 +0800 Subject: [PATCH 054/213] Fix #3047 (#3092) * Make path canonicalized * Update extra-source-files * Replace with normalise * Change to a more detailed log * Comment patch detail * 2 spaces indent --- exe/Plugins.hs | 2 +- .../hls-module-name-plugin.cabal | 2 + .../src/Ide/Plugin/ModuleName.hs | 140 +++++++++++------- plugins/hls-module-name-plugin/test/Main.hs | 15 +- .../test/testdata/cabal.project | 1 + .../testdata/canonicalize/Lib/A.expected.hs | 1 + .../test/testdata/canonicalize/Lib/A.hs | 0 .../testdata/canonicalize/canonicalize.cabal | 7 + .../test/testdata/hie.yaml | 23 ++- 9 files changed, 124 insertions(+), 67 deletions(-) create mode 100644 plugins/hls-module-name-plugin/test/testdata/cabal.project create mode 100644 plugins/hls-module-name-plugin/test/testdata/canonicalize/Lib/A.expected.hs create mode 100644 plugins/hls-module-name-plugin/test/testdata/canonicalize/Lib/A.hs create mode 100644 plugins/hls-module-name-plugin/test/testdata/canonicalize/canonicalize.cabal diff --git a/exe/Plugins.hs b/exe/Plugins.hs index 86dbff0a16..8dafaa3495 100644 --- a/exe/Plugins.hs +++ b/exe/Plugins.hs @@ -188,7 +188,7 @@ idePlugins recorder includeExamples = pluginDescToIdePlugins allPlugins RefineImports.descriptor pluginRecorder "refineImports" : #endif #if hls_moduleName - ModuleName.descriptor "moduleName" : + ModuleName.descriptor pluginRecorder "moduleName" : #endif #if hls_hlint Hlint.descriptor pluginRecorder "hlint" : diff --git a/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal b/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal index 29fbe521a0..1290ab75bf 100644 --- a/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal +++ b/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal @@ -16,6 +16,8 @@ extra-source-files: LICENSE test/testdata/**/*.yaml test/testdata/**/*.hs + test/testdata/**/*.cabal + test/testdata/**/*.project library exposed-modules: Ide.Plugin.ModuleName diff --git a/plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs b/plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs index 55685778f4..05dd03ab56 100644 --- a/plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs +++ b/plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs @@ -4,6 +4,7 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ViewPatterns #-} {-# OPTIONS_GHC -Wall -Wwarn -fno-warn-type-defaults #-} +{-# OPTIONS_GHC -Wno-name-shadowing #-} {- | Keep the module name in sync with its file path. @@ -15,55 +16,62 @@ module Ide.Plugin.ModuleName ( descriptor, ) where -import Control.Monad (forM_, void) -import Control.Monad.IO.Class (liftIO) -import Control.Monad.Trans.Class (lift) +import Control.Monad (forM_, void) +import Control.Monad.IO.Class (liftIO) +import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Maybe -import Data.Aeson (Value (Null), toJSON) -import Data.Char (isLower) -import qualified Data.HashMap.Strict as HashMap -import Data.List (intercalate, isPrefixOf, minimumBy) -import qualified Data.List.NonEmpty as NE -import Data.Maybe (maybeToList) -import Data.Ord (comparing) -import Data.String (IsString) -import qualified Data.Text as T -import Development.IDE (GetParsedModule (GetParsedModule), - GhcSession (GhcSession), IdeState, - evalGhcEnv, hscEnvWithImportPaths, - realSrcSpanToRange, runAction, - uriToFilePath', use, use_) -import Development.IDE.GHC.Compat (GenLocated (L), getSessionDynFlags, - hsmodName, importPaths, locA, - moduleNameString, - pattern RealSrcSpan, - pm_parsed_source, unLoc) +import Data.Aeson (Value (Null), toJSON) +import Data.Char (isLower) +import qualified Data.HashMap.Strict as HashMap +import Data.List (intercalate, isPrefixOf, + minimumBy) +import qualified Data.List.NonEmpty as NE +import Data.Maybe (maybeToList) +import Data.Ord (comparing) +import Data.String (IsString) +import qualified Data.Text as T +import Development.IDE (GetParsedModule (GetParsedModule), + GhcSession (GhcSession), + IdeState, Pretty, + Priority (Debug, Info), Recorder, + WithPriority, colon, evalGhcEnv, + hscEnvWithImportPaths, logWith, + realSrcSpanToRange, runAction, + uriToFilePath', use, use_, (<+>)) +import Development.IDE.GHC.Compat (GenLocated (L), + getSessionDynFlags, hsmodName, + importPaths, locA, + moduleNameString, + pattern RealSrcSpan, + pm_parsed_source, unLoc) +import Development.IDE.Types.Logger (Pretty (..)) import Ide.Types import Language.LSP.Server -import Language.LSP.Types hiding - (SemanticTokenAbsolute (length, line), - SemanticTokenRelative (length), - SemanticTokensEdit (_start)) -import Language.LSP.VFS (virtualFileText) -import System.Directory (makeAbsolute) -import System.FilePath (dropExtension, splitDirectories, - takeFileName) +import Language.LSP.Types hiding + (SemanticTokenAbsolute (length, line), + SemanticTokenRelative (length), + SemanticTokensEdit (_start)) +import Language.LSP.VFS (virtualFileText) +import System.Directory (makeAbsolute) +import System.FilePath (dropExtension, normalise, + pathSeparator, splitDirectories, + takeFileName) -- |Plugin descriptor -descriptor :: PluginId -> PluginDescriptor IdeState -descriptor plId = +descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState +descriptor recorder plId = (defaultPluginDescriptor plId) - { pluginHandlers = mkPluginHandler STextDocumentCodeLens codeLens - , pluginCommands = [PluginCommand updateModuleNameCommand "set name of module to match with file path" command] + { pluginHandlers = mkPluginHandler STextDocumentCodeLens (codeLens recorder) + , pluginCommands = [PluginCommand updateModuleNameCommand "set name of module to match with file path" (command recorder)] } updateModuleNameCommand :: IsString p => p updateModuleNameCommand = "updateModuleName" -- | Generate code lenses -codeLens :: PluginMethodHandler IdeState 'TextDocumentCodeLens -codeLens state pluginId CodeLensParams{_textDocument=TextDocumentIdentifier uri} = - Right . List . maybeToList . (asCodeLens <$>) <$> action state uri +codeLens :: Recorder (WithPriority Log) -> PluginMethodHandler IdeState 'TextDocumentCodeLens +codeLens recorder state pluginId CodeLensParams{_textDocument=TextDocumentIdentifier uri} = + Right . List . maybeToList . (asCodeLens <$>) <$> action recorder state uri where asCodeLens :: Action -> CodeLens asCodeLens Replace{..} = CodeLens aRange (Just cmd) Nothing @@ -71,9 +79,9 @@ codeLens state pluginId CodeLensParams{_textDocument=TextDocumentIdentifier uri} cmd = mkLspCommand pluginId updateModuleNameCommand aTitle (Just [toJSON aUri]) -- | (Quasi) Idempotent command execution: recalculate action to execute on command request -command :: CommandFunction IdeState Uri -command state uri = do - actMaybe <- action state uri +command :: Recorder (WithPriority Log) -> CommandFunction IdeState Uri +command recorder state uri = do + actMaybe <- action recorder state uri forM_ actMaybe $ \Replace{..} -> let -- | Convert an Action to the corresponding edit operation @@ -92,19 +100,22 @@ data Action = Replace deriving (Show) -- | Required action (that can be converted to either CodeLenses or CodeActions) -action :: IdeState -> Uri -> LspM c (Maybe Action) -action state uri = - traceAs "action" <$> runMaybeT $ do +action :: Recorder (WithPriority Log) -> IdeState -> Uri -> LspM c (Maybe Action) +action recorder state uri = + runMaybeT $ do nfp <- MaybeT . pure . uriToNormalizedFilePath $ toNormalizedUri uri fp <- MaybeT . pure $ uriToFilePath' uri contents <- lift . getVirtualFile $ toNormalizedUri uri let emptyModule = maybe True (T.null . T.strip . virtualFileText) contents - correctNames <- liftIO $ traceAs "correctNames" <$> pathModuleNames state nfp fp + correctNames <- liftIO $ pathModuleNames recorder state nfp fp + logWith recorder Debug (CorrectNames correctNames) bestName <- minimumBy (comparing T.length) <$> (MaybeT . pure $ NE.nonEmpty correctNames) + logWith recorder Info (BestName bestName) - statedNameMaybe <- liftIO $ traceAs "statedName" <$> codeModuleName state nfp + statedNameMaybe <- liftIO $ codeModuleName state nfp + logWith recorder Debug (ModuleName $ snd <$> statedNameMaybe) case statedNameMaybe of Just (nameRange, statedName) | statedName `notElem` correctNames -> @@ -118,14 +129,23 @@ action state uri = -- | Possible module names, as derived by the position of the module in the -- source directories. There may be more than one possible name, if the source -- directories are nested inside each other. -pathModuleNames :: IdeState -> NormalizedFilePath -> String -> IO [T.Text] -pathModuleNames state normFilePath filePath +pathModuleNames :: Recorder (WithPriority Log) -> IdeState -> NormalizedFilePath -> FilePath -> IO [T.Text] +pathModuleNames recorder state normFilePath filePath | isLower . head $ takeFileName filePath = return ["Main"] | otherwise = do session <- runAction "ModuleName.ghcSession" state $ use_ GhcSession normFilePath srcPaths <- evalGhcEnv (hscEnvWithImportPaths session) $ importPaths <$> getSessionDynFlags - paths <- mapM makeAbsolute srcPaths + logWith recorder Debug (SrcPaths srcPaths) + + -- Append a `pathSeparator` to make the path looks like a directory, + -- and then we can drop it uniformly. + -- See https://github.com/haskell/haskell-language-server/pull/3092 for details. + let paths = map (normalise . (<> pure pathSeparator)) srcPaths + logWith recorder Debug (NormalisedPaths paths) + mdlPath <- makeAbsolute filePath + logWith recorder Debug (AbsoluteFilePath mdlPath) + let prefixes = filter (`isPrefixOf` mdlPath) paths pure (map (moduleNameFrom mdlPath) prefixes) where @@ -133,7 +153,7 @@ pathModuleNames state normFilePath filePath T.pack . intercalate "." . splitDirectories - . drop (length prefix + 1) + . drop (length prefix) $ dropExtension mdlPath -- | The module name, as stated in the module @@ -143,8 +163,20 @@ codeModuleName state nfp = runMaybeT $ do L (locA -> (RealSrcSpan l _)) m <- MaybeT . pure . hsmodName . unLoc $ pm_parsed_source pm pure (realSrcSpanToRange l, T.pack $ moduleNameString m) --- traceAs :: Show a => String -> a -> a --- traceAs lbl a = trace (lbl ++ " = " ++ show a) a - -traceAs :: b -> a -> a -traceAs _ a = a +data Log = + CorrectNames [T.Text] + | BestName T.Text + | ModuleName (Maybe T.Text) + | SrcPaths [FilePath] + | NormalisedPaths [FilePath] + | AbsoluteFilePath FilePath + deriving Show + +instance Pretty Log where + pretty log = "ModuleName." <> case log of + CorrectNames log -> "CorrectNames" <> colon <+> pretty log + BestName log -> "BestName" <> colon <+> pretty log + ModuleName log -> "StatedNameMaybe" <> colon <+> pretty log + SrcPaths log -> "SrcPaths" <> colon <+> pretty log + NormalisedPaths log -> "NormalisedPaths" <> colon <+> pretty log + AbsoluteFilePath log -> "AbsoluteFilePath" <> colon <+> pretty log diff --git a/plugins/hls-module-name-plugin/test/Main.hs b/plugins/hls-module-name-plugin/test/Main.hs index ce0fa1e746..914fcb69dd 100644 --- a/plugins/hls-module-name-plugin/test/Main.hs +++ b/plugins/hls-module-name-plugin/test/Main.hs @@ -13,7 +13,7 @@ main :: IO () main = defaultTestRunner tests moduleNamePlugin :: PluginDescriptor IdeState -moduleNamePlugin = ModuleName.descriptor "moduleName" +moduleNamePlugin = ModuleName.descriptor mempty "moduleName" tests :: TestTree tests = @@ -39,10 +39,15 @@ tests = void $ skipManyTill anyMessage (message SWorkspaceApplyEdit) , testCase "Should not show code lens if the module name is correct" $ runSessionWithServer moduleNamePlugin testDataDir $ do - doc <- openDoc "CorrectName.hs" "haskell" - lenses <- getCodeLenses doc - liftIO $ lenses @?= [] - closeDoc doc + doc <- openDoc "CorrectName.hs" "haskell" + lenses <- getCodeLenses doc + liftIO $ lenses @?= [] + closeDoc doc + -- https://github.com/haskell/haskell-language-server/issues/3047 + , goldenWithModuleName "Fix#3047" "canonicalize/Lib/A" $ \doc -> do + [CodeLens { _command = Just c }] <- getCodeLenses doc + executeCommand c + void $ skipManyTill anyMessage (message SWorkspaceApplyEdit) ] goldenWithModuleName :: TestName -> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree diff --git a/plugins/hls-module-name-plugin/test/testdata/cabal.project b/plugins/hls-module-name-plugin/test/testdata/cabal.project new file mode 100644 index 0000000000..1406cd0907 --- /dev/null +++ b/plugins/hls-module-name-plugin/test/testdata/cabal.project @@ -0,0 +1 @@ +packages: ./canonicalize diff --git a/plugins/hls-module-name-plugin/test/testdata/canonicalize/Lib/A.expected.hs b/plugins/hls-module-name-plugin/test/testdata/canonicalize/Lib/A.expected.hs new file mode 100644 index 0000000000..c5877f7100 --- /dev/null +++ b/plugins/hls-module-name-plugin/test/testdata/canonicalize/Lib/A.expected.hs @@ -0,0 +1 @@ +module Lib.A where diff --git a/plugins/hls-module-name-plugin/test/testdata/canonicalize/Lib/A.hs b/plugins/hls-module-name-plugin/test/testdata/canonicalize/Lib/A.hs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/hls-module-name-plugin/test/testdata/canonicalize/canonicalize.cabal b/plugins/hls-module-name-plugin/test/testdata/canonicalize/canonicalize.cabal new file mode 100644 index 0000000000..dc0e099ed3 --- /dev/null +++ b/plugins/hls-module-name-plugin/test/testdata/canonicalize/canonicalize.cabal @@ -0,0 +1,7 @@ +cabal-version: 2.4 +name: canonicalize +version: 0.1.0.0 + +library + build-depends: base + hs-source-dirs: ./ diff --git a/plugins/hls-module-name-plugin/test/testdata/hie.yaml b/plugins/hls-module-name-plugin/test/testdata/hie.yaml index 022fee55a1..fb1c7521c3 100644 --- a/plugins/hls-module-name-plugin/test/testdata/hie.yaml +++ b/plugins/hls-module-name-plugin/test/testdata/hie.yaml @@ -1,8 +1,17 @@ cradle: - direct: - arguments: - - "-isubdir" - - "TEmptyModule" - - "TWrongModuleName" - - "mainlike" - - "CorrectName" + multi: + - path: "./" + config: + cradle: + direct: + arguments: + - "-isubdir" + - "TEmptyModule" + - "TWrongModuleName" + - "CorrectName" + - path: "./canonicalize" + config: + cradle: + cabal: + - path: "./" + component: "lib:canonicalize" From e55004a0cc9b727423a16cec4f5103a4df685eb6 Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Mon, 15 Aug 2022 13:51:58 +0200 Subject: [PATCH 055/213] fix lsp-types benchmark (#3079) * fix lsp-types paths * Fail if there are no documents to benchmark * use modules with suitable positions * Fix 8.6.5 build * fix imports for 8.6.5 * Fix benchmark artifact uploads * format --- .github/workflows/bench.yml | 17 ++++++++--------- ghcide/bench/config.yaml | 8 ++++---- ghcide/bench/lib/Experiments.hs | 13 ++++++++++--- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 1691ca2152..2945ac2812 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -140,24 +140,23 @@ jobs: run: | column -s, -t < ghcide/bench-results/unprofiled/${{ matrix.example }}/results.csv | tee ghcide/bench-results/unprofiled/${{ matrix.example }}/results.txt + - name: tar benchmarking artifacts + run: find ghcide/bench-results -name "*.csv" -or -name "*.svg" -or -name "*.html" | xargs tar -czf benchmark-artifacts.tar.gz + - name: Archive benchmarking artifacts uses: actions/upload-artifact@v3 with: name: bench-results-${{ runner.os }}-${{ matrix.ghc }} - path: | - ghcide/bench-results/results.* - ghcide/bench-results/**/*.csv - ghcide/bench-results/**/*.svg - ghcide/bench-results/**/*.eventlog.html + path: benchmark-artifacts.tar.gz + + - name: tar benchmarking logs + run: find ghcide/bench-results -name "*.log" -or -name "*.eventlog" -or -name "*.hp" | xargs tar -czf benchmark-logs.tar.gz - name: Archive benchmark logs uses: actions/upload-artifact@v3 with: name: bench-logs-${{ runner.os }}-${{ matrix.ghc }} - path: | - ghcide/bench-results/**/*.log - ghcide/bench-results/**/*.eventlog - ghcide/bench-results/**/*.hp + path: benchmark-logs.tar.gz bench_post_job: if: always() diff --git a/ghcide/bench/config.yaml b/ghcide/bench/config.yaml index bcec13dfbe..a744f56e17 100644 --- a/ghcide/bench/config.yaml +++ b/ghcide/bench/config.yaml @@ -37,14 +37,14 @@ examples: package: lsp-types version: 1.5.0.0 modules: - - src/Language/LSP/VFS.hs - - src/Language/LSP/Types/Lens.hs + - src/Language/LSP/Types/WatchedFiles.hs + - src/Language/LSP/Types/CallHierarchy.hs - name: lsp-types-conservative package: lsp-types version: 1.5.0.0 modules: - - src/Language/LSP/VFS.hs - - src/Language/LSP/Types/Lens.hs + - src/Language/LSP/Types/WatchedFiles.hs + - src/Language/LSP/Types/CallHierarchy.hs extra-args: - --conservative-change-tracking # Small-sized project with TH diff --git a/ghcide/bench/lib/Experiments.hs b/ghcide/bench/lib/Experiments.hs index df58577e6c..081df51984 100644 --- a/ghcide/bench/lib/Experiments.hs +++ b/ghcide/bench/lib/Experiments.hs @@ -24,7 +24,9 @@ module Experiments ) where import Control.Applicative.Combinators (skipManyTill) import Control.Exception.Safe (IOException, handleAny, try) -import Control.Monad.Extra +import Control.Monad.Extra (allM, forM, forM_, unless, + void, whenJust, (&&^)) +import Control.Monad.Fail (MonadFail) import Control.Monad.IO.Class import Data.Aeson (Value (Null), toJSON) import Data.Either (fromRight) @@ -72,8 +74,13 @@ data DocumentPositions = DocumentPositions { doc :: !TextDocumentIdentifier } -allWithIdentifierPos :: Monad m => (DocumentPositions -> m Bool) -> [DocumentPositions] -> m Bool -allWithIdentifierPos f docs = allM f (filter (isJust . identifierP) docs) +allWithIdentifierPos :: MonadFail m => (DocumentPositions -> m Bool) -> [DocumentPositions] -> m Bool +allWithIdentifierPos f docs = case applicableDocs of + -- fail if there are no documents to benchmark + [] -> fail "None of the example modules have identifier positions" + docs' -> allM f docs' + where + applicableDocs = filter (isJust . identifierP) docs experiments :: [Bench] experiments = From 92f4bd4b28ffd4ddddca52d9b4cbb4c0bdd8eeeb Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Wed, 17 Aug 2022 03:46:29 +0800 Subject: [PATCH 056/213] Remove unused config (#3107) --- .../hls-class-plugin/src/Ide/Plugin/Class.hs | 2 - .../src/Ide/Plugin/Class/CodeLens.hs | 68 +++++++++---------- .../src/Ide/Plugin/Class/ExactPrint.hs | 1 - .../src/Ide/Plugin/Class/Types.hs | 21 ------ plugins/hls-class-plugin/test/Main.hs | 9 --- 5 files changed, 33 insertions(+), 68 deletions(-) diff --git a/plugins/hls-class-plugin/src/Ide/Plugin/Class.hs b/plugins/hls-class-plugin/src/Ide/Plugin/Class.hs index 9bbc376f66..5eed650a17 100644 --- a/plugins/hls-class-plugin/src/Ide/Plugin/Class.hs +++ b/plugins/hls-class-plugin/src/Ide/Plugin/Class.hs @@ -13,8 +13,6 @@ descriptor recorder plId = (defaultPluginDescriptor plId) , pluginRules = rules recorder , pluginHandlers = mkPluginHandler STextDocumentCodeAction (codeAction recorder) <> mkPluginHandler STextDocumentCodeLens codeLens - , pluginConfigDescriptor = - defaultConfigDescriptor { configCustomConfig = mkCustomConfig properties } } commands :: PluginId -> [PluginCommand IdeState] diff --git a/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeLens.hs b/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeLens.hs index 5ea74fe780..042c46c52b 100644 --- a/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeLens.hs +++ b/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeLens.hs @@ -23,41 +23,39 @@ import Language.LSP.Types import qualified Language.LSP.Types.Lens as J codeLens :: PluginMethodHandler IdeState TextDocumentCodeLens -codeLens state plId CodeLensParams{..} = do - enabled <- enableTypeLens <$> getCompletionsConfig plId - if not enabled then pure $ pure $ List [] else pluginResponse $ do - nfp <- getNormalizedFilePath plId uri - tmr <- handleMaybeM "Unable to typecheck" - $ liftIO - $ runAction "classplugin.TypeCheck" state - $ use TypeCheck nfp - - -- All instance binds - InstanceBindTypeSigsResult allBinds <- - handleMaybeM "Unable to get InstanceBindTypeSigsResult" - $ liftIO - $ runAction "classplugin.GetInstanceBindTypeSigs" state - $ use GetInstanceBindTypeSigs nfp - - pragmaInsertion <- insertPragmaIfNotPresent state nfp InstanceSigs - - let (hsGroup, _, _, _) = tmrRenamed tmr - tycls = hs_tyclds hsGroup - -- declared instance methods without signatures - bindInfos = [ bind - | instds <- map group_instds tycls -- class instance decls - , instd <- instds - , inst <- maybeToList $ getClsInstD (unLoc instd) - , bind <- getBindSpanWithoutSig inst - ] - targetSigs = matchBind bindInfos allBinds - makeLens (range, title) = - generateLens plId range title - $ workspaceEdit pragmaInsertion - $ makeEdit range title - codeLens = makeLens <$> mapMaybe getRangeWithSig targetSigs - - pure $ List codeLens +codeLens state plId CodeLensParams{..} = pluginResponse $ do + nfp <- getNormalizedFilePath plId uri + tmr <- handleMaybeM "Unable to typecheck" + $ liftIO + $ runAction "classplugin.TypeCheck" state + $ use TypeCheck nfp + + -- All instance binds + InstanceBindTypeSigsResult allBinds <- + handleMaybeM "Unable to get InstanceBindTypeSigsResult" + $ liftIO + $ runAction "classplugin.GetInstanceBindTypeSigs" state + $ use GetInstanceBindTypeSigs nfp + + pragmaInsertion <- insertPragmaIfNotPresent state nfp InstanceSigs + + let (hsGroup, _, _, _) = tmrRenamed tmr + tycls = hs_tyclds hsGroup + -- declared instance methods without signatures + bindInfos = [ bind + | instds <- map group_instds tycls -- class instance decls + , instd <- instds + , inst <- maybeToList $ getClsInstD (unLoc instd) + , bind <- getBindSpanWithoutSig inst + ] + targetSigs = matchBind bindInfos allBinds + makeLens (range, title) = + generateLens plId range title + $ workspaceEdit pragmaInsertion + $ makeEdit range title + codeLens = makeLens <$> mapMaybe getRangeWithSig targetSigs + + pure $ List codeLens where uri = _textDocument ^. J.uri diff --git a/plugins/hls-class-plugin/src/Ide/Plugin/Class/ExactPrint.hs b/plugins/hls-class-plugin/src/Ide/Plugin/Class/ExactPrint.hs index 15ba17c2b2..dc2128397d 100644 --- a/plugins/hls-class-plugin/src/Ide/Plugin/Class/ExactPrint.hs +++ b/plugins/hls-class-plugin/src/Ide/Plugin/Class/ExactPrint.hs @@ -5,7 +5,6 @@ module Ide.Plugin.Class.ExactPrint where -import Control.Lens (Identity) import Control.Monad.Trans.Maybe import qualified Data.Text as T import Development.IDE.GHC.Compat diff --git a/plugins/hls-class-plugin/src/Ide/Plugin/Class/Types.hs b/plugins/hls-class-plugin/src/Ide/Plugin/Class/Types.hs index 988c226c1b..b572549325 100644 --- a/plugins/hls-class-plugin/src/Ide/Plugin/Class/Types.hs +++ b/plugins/hls-class-plugin/src/Ide/Plugin/Class/Types.hs @@ -2,7 +2,6 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} -{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} @@ -20,11 +19,7 @@ import Development.IDE.GHC.Compat hiding ((<+>)) import Development.IDE.Graph.Classes import GHC.Generics import Ide.Plugin.Class.Utils -import Ide.Plugin.Config -import Ide.Plugin.Properties -import Ide.PluginUtils import Ide.Types -import Language.LSP.Server typeLensCommandId :: CommandId typeLensCommandId = "classplugin.typelens" @@ -112,19 +107,3 @@ rules recorder = do (prettyBindingNameString (printOutputable name) <> " :: " <> T.pack (showDoc ty)) Nothing instanceBindType _ _ = pure Nothing - -properties :: Properties - '[ 'PropertyKey "typelensOn" 'TBoolean] -properties = emptyProperties - & defineBooleanProperty #typelensOn - "Enable type lens on instance methods" - True - -getCompletionsConfig :: (MonadLsp Config m) => PluginId -> m ClassConfig -getCompletionsConfig plId = - ClassConfig - <$> usePropertyLsp #typelensOn plId properties - -newtype ClassConfig = ClassConfig - { enableTypeLens :: Bool - } diff --git a/plugins/hls-class-plugin/test/Main.hs b/plugins/hls-class-plugin/test/Main.hs index 2b74979c7a..b15efb7498 100644 --- a/plugins/hls-class-plugin/test/Main.hs +++ b/plugins/hls-class-plugin/test/Main.hs @@ -92,15 +92,6 @@ codeLensTests recorder = testGroup [ "(==) :: B -> B -> Bool" , "(==) :: A -> A -> Bool" ] - , testCase "Should no lens if disabled" $ do - runSessionWithServer (classPlugin recorder) testDataDir $ do - sendConfigurationChanged - $ toJSON - $ def { Plugin.plugins = [("class", def { plcConfig = "typelensOn" .= False })] } - doc <- openDoc "CodeLensSimple.hs" "haskell" - lens <- getCodeLenses doc - let titles = map (^. J.title) $ mapMaybe (^. J.command) lens - liftIO $ titles @?= [] , goldenCodeLens recorder "Apply code lens" "CodeLensSimple" 1 , goldenCodeLens recorder "Apply code lens for local class" "LocalClassDefine" 0 , goldenCodeLens recorder "Apply code lens on the same line" "Inline" 0 From c9ed045f7b0cda324354325513d59037ad17d41b Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Thu, 18 Aug 2022 08:25:01 +0200 Subject: [PATCH 057/213] Do not send Heap Stats to the LSP log (#3111) --- exe/Main.hs | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/exe/Main.hs b/exe/Main.hs index 098a427ef9..083f76a1b4 100644 --- a/exe/Main.hs +++ b/exe/Main.hs @@ -5,9 +5,11 @@ {-# LANGUAGE OverloadedStrings #-} module Main(main) where +import Control.Arrow ((&&&)) import Control.Monad.IO.Class (liftIO) import Data.Function ((&)) import Data.Text (Text) +import qualified Development.IDE.Main as GhcideMain import Development.IDE.Types.Logger (Doc, Priority (Debug, Error, Info), WithPriority (WithPriority, priority), @@ -15,7 +17,7 @@ import Development.IDE.Types.Logger (Doc, defaultLayoutOptions, layoutPretty, makeDefaultStderrRecorder, - renderStrict, + payload, renderStrict, withDefaultRecorder) import qualified Development.IDE.Types.Logger as Logger import Ide.Arguments (Arguments (..), @@ -62,24 +64,28 @@ main = do liftIO $ (cb1 <> cb2) env } - let (minPriority, logFilePath, includeExamplePlugins) = + let (argsTesting, minPriority, logFilePath, includeExamplePlugins) = case args of Ghcide GhcideArguments{ argsTesting, argsDebugOn, argsLogFile, argsExamplePlugin } -> let minPriority = if argsDebugOn || argsTesting then Debug else Info - in (minPriority, argsLogFile, argsExamplePlugin) - _ -> (Info, Nothing, False) + in (argsTesting, minPriority, argsLogFile, argsExamplePlugin) + _ -> (False, Info, Nothing, False) withDefaultRecorder logFilePath Nothing minPriority $ \textWithPriorityRecorder -> do let - recorder = cmapWithPrio pretty $ mconcat + recorder = cmapWithPrio (pretty &&& id) $ mconcat [textWithPriorityRecorder & cfilter (\WithPriority{ priority } -> priority >= minPriority) + & cmapWithPrio fst , lspMessageRecorder & cfilter (\WithPriority{ priority } -> priority >= Error) - & cmapWithPrio renderDoc + & cmapWithPrio (renderDoc . fst) , lspLogRecorder & cfilter (\WithPriority{ priority } -> priority >= minPriority) - & cmapWithPrio (renderStrict . layoutPretty defaultLayoutOptions) + & cmapWithPrio (renderStrict . layoutPretty defaultLayoutOptions . fst) + -- do not log heap stats to the LSP log as they interfere with the + -- ability of lsp-test to detect a stuck server in tests and benchmarks + & if argsTesting then cfilter (not . heapStats . snd . payload) else id ] plugins = (Plugins.idePlugins (cmapWithPrio LogPlugins recorder) includeExamplePlugins) @@ -96,3 +102,7 @@ renderDoc d = renderStrict $ layoutPretty defaultLayoutOptions $ vsep issueTrackerUrl :: Doc a issueTrackerUrl = "https://github.com/haskell/haskell-language-server/issues" + +heapStats :: Log -> Bool +heapStats (LogIdeMain (IdeMain.LogIDEMain (GhcideMain.LogHeapStats _))) = True +heapStats _ = False From f4ce73f87cebaf96f15a9bec5d900dac8b9c9ee6 Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Wed, 17 Aug 2022 17:40:26 +0200 Subject: [PATCH 058/213] Send begin progress message synchronously Currently the Begin progress message is sent asynchronously, so it can happen that it's never sent if the async is cancelled immediately because a new kick has started. This causes trouble in tests and benchmarks which make assumptions about progress updates. To address this, we send the Begin progress message synchronously, and only then do the rest of the progress reporting stuff (including waiting for the response) asynchronously --- .../Development/IDE/Core/ProgressReporting.hs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/ghcide/src/Development/IDE/Core/ProgressReporting.hs b/ghcide/src/Development/IDE/Core/ProgressReporting.hs index 7bb6e11944..7436ca56ff 100644 --- a/ghcide/src/Development/IDE/Core/ProgressReporting.hs +++ b/ghcide/src/Development/IDE/Core/ProgressReporting.hs @@ -63,10 +63,10 @@ data State -- | State transitions used in 'delayedProgressReporting' data Transition = Event ProgressEvent | StopProgress -updateState :: IO () -> Transition -> State -> IO State +updateState :: IO (Async ()) -> Transition -> State -> IO State updateState _ _ Stopped = pure Stopped -updateState start (Event KickStarted) NotStarted = Running <$> async start -updateState start (Event KickStarted) (Running a) = cancel a >> Running <$> async start +updateState start (Event KickStarted) NotStarted = Running <$> start +updateState start (Event KickStarted) (Running a) = cancel a >> Running <$> start updateState _ (Event KickCompleted) (Running a) = cancel a $> NotStarted updateState _ (Event KickCompleted) st = pure st updateState _ StopProgress (Running a) = cancel a $> Stopped @@ -110,12 +110,13 @@ delayedProgressReporting -> Maybe (LSP.LanguageContextEnv c) -> ProgressReportingStyle -> IO ProgressReporting -delayedProgressReporting before after lspEnv optProgressStyle = do +delayedProgressReporting before after Nothing optProgressStyle = noProgressReporting +delayedProgressReporting before after (Just lspEnv) optProgressStyle = do inProgressState <- newInProgress progressState <- newVar NotStarted let progressUpdate event = updateStateVar $ Event event progressStop = updateStateVar StopProgress - updateStateVar = modifyVar_ progressState . updateState (mRunLspT lspEnv $ lspShakeProgress inProgressState) + updateStateVar = modifyVar_ progressState . updateState (lspShakeProgress inProgressState) inProgress = updateStateForFile inProgressState return ProgressReporting{..} @@ -127,11 +128,11 @@ delayedProgressReporting before after lspEnv optProgressStyle = do u <- ProgressTextToken . T.pack . show . hashUnique <$> liftIO newUnique b <- liftIO newBarrier - void $ LSP.sendRequest LSP.SWindowWorkDoneProgressCreate + void $ LSP.runLspT lspEnv $ LSP.sendRequest LSP.SWindowWorkDoneProgressCreate LSP.WorkDoneProgressCreateParams { _token = u } $ liftIO . signalBarrier b - ready <- liftIO $ waitBarrier b - - for_ ready $ const $ bracket_ (start u) (stop u) (loop u 0) + liftIO $ async $ do + ready <- waitBarrier b + LSP.runLspT lspEnv $ for_ ready $ const $ bracket_ (start u) (stop u) (loop u 0) where start id = LSP.sendNotification LSP.SProgress $ LSP.ProgressParams From fe00b3ae370c2b018fa147fc39a4e632af0d7eb3 Mon Sep 17 00:00:00 2001 From: Kobayashi Date: Fri, 19 Aug 2022 10:24:02 +0800 Subject: [PATCH 059/213] upgrade ghc-check to fix #3002 (#3034) --- cabal.project | 2 +- ghcide/ghcide.cabal | 2 +- stack-lts16.yaml | 2 +- stack-lts19.yaml | 2 +- stack.yaml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cabal.project b/cabal.project index 9a49ac4fa5..8bfcb20265 100644 --- a/cabal.project +++ b/cabal.project @@ -45,7 +45,7 @@ package * write-ghc-environment-files: never -index-state: 2022-08-09T13:13:41Z +index-state: 2022-08-15T06:53:13Z constraints: hyphenation +embed, diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index 7d037dcc38..c1a62f679b 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -108,7 +108,7 @@ library ghc-boot-th, ghc-boot, ghc >= 8.6, - ghc-check >=0.5.0.4, + ghc-check >=0.5.0.8, ghc-paths, cryptohash-sha1 >=0.11.100 && <0.12, hie-bios ^>= 0.9.1, diff --git a/stack-lts16.yaml b/stack-lts16.yaml index 5460e8940f..bbc53ea853 100644 --- a/stack-lts16.yaml +++ b/stack-lts16.yaml @@ -48,7 +48,7 @@ extra-deps: - extra-1.7.10 - floskell-0.10.4 - fourmolu-0.3.0.0 - - ghc-check-0.5.0.4 + - ghc-check-0.5.0.8 - ghc-exactprint-0.6.4 - ghc-lib-8.10.7.20210828 - ghc-lib-parser-8.10.7.20210828 diff --git a/stack-lts19.yaml b/stack-lts19.yaml index 219dc7e820..3c9bd98508 100644 --- a/stack-lts19.yaml +++ b/stack-lts19.yaml @@ -1,4 +1,4 @@ -resolver: lts-19.18 +resolver: lts-19.19 packages: - . diff --git a/stack.yaml b/stack.yaml index 6ea299746c..923990c885 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,4 +1,4 @@ -resolver: nightly-2022-08-04 +resolver: nightly-2022-08-15 packages: - . From df88cd56763a7d5ed018dd3e390b9d4fe648c24e Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Tue, 16 Aug 2022 11:55:44 +0200 Subject: [PATCH 060/213] Deduplicate HLS plugins Use a smart constructor to prevent duplicated plugins. We cannot use a set since order matters --- hls-plugin-api/src/Ide/Types.hs | 42 ++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index a9f2bb3050..d6261c9323 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -12,6 +12,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -20,6 +21,30 @@ {-# LANGUAGE ViewPatterns #-} module Ide.Types +( PluginDescriptor(..), defaultPluginDescriptor, defaultCabalPluginDescriptor +, IdeCommand(..) +, IdeMethod(..) +, IdeNotification(..) +, IdePlugins(IdePlugins, ipMap) +, DynFlagsModifications(..) +, ConfigDescriptor(..), defaultConfigDescriptor, configForPlugin, pluginEnabledConfig +, CustomConfig(..), mkCustomConfig +, FallbackCodeActionParams(..) +, FormattingType(..), FormattingMethod, FormattingHandler, mkFormattingHandlers +, HasTracing(..) +, PluginCommand(..), CommandId(..), CommandFunction, mkLspCommand, mkLspCmdId +, PluginId(..) +, PluginHandler(..), mkPluginHandler +, PluginHandlers(..) +, PluginMethod(..) +, PluginMethodHandler +, PluginNotificationHandler(..), mkPluginNotificationHandler +, PluginNotificationHandlers(..) +, PluginRequestMethod(..) +, getProcessID, getPid +, installSigUsr1Handler +, responseError +) where #ifdef mingw32_HOST_OS @@ -36,6 +61,7 @@ import Data.Dependent.Map (DMap) import qualified Data.Dependent.Map as DMap import qualified Data.DList as DList import Data.GADT.Compare +import Data.List.Extra (nubOrdOn) import Data.List.NonEmpty (NonEmpty (..), toList) import qualified Data.Map as Map import Data.Maybe @@ -76,9 +102,19 @@ import Text.Regex.TDFA.Text () -- --------------------------------------------------------------------- -newtype IdePlugins ideState = IdePlugins - { ipMap :: [(PluginId, PluginDescriptor ideState)]} - deriving newtype (Monoid, Semigroup) +newtype IdePlugins ideState = IdePlugins_ + { ipMap_ :: [(PluginId, PluginDescriptor ideState)]} + deriving newtype Monoid + +-- | Smart constructor that deduplicates plugins +pattern IdePlugins :: [(PluginId, PluginDescriptor ideState)] -> IdePlugins ideState +pattern IdePlugins{ipMap} <- IdePlugins_ ipMap + where + IdePlugins ipMap = IdePlugins_{ipMap_ = nubOrdOn fst ipMap} +{-# COMPLETE IdePlugins #-} + +instance Semigroup (IdePlugins s) where + IdePlugins a <> IdePlugins b = IdePlugins(a <> b) -- | Hooks for modifying the 'DynFlags' at different times of the compilation -- process. Plugins can install a 'DynFlagsModifications' via From 5894b9a306b53435ca9ecf3c4ae71f9667dbd77a Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Wed, 17 Aug 2022 20:57:26 +0200 Subject: [PATCH 061/213] formatting --- hls-plugin-api/src/Ide/Types.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index d6261c9323..4a469f2d66 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -61,7 +61,7 @@ import Data.Dependent.Map (DMap) import qualified Data.Dependent.Map as DMap import qualified Data.DList as DList import Data.GADT.Compare -import Data.List.Extra (nubOrdOn) +import Data.List.Extra (nubOrdOn) import Data.List.NonEmpty (NonEmpty (..), toList) import qualified Data.Map as Map import Data.Maybe From 9ef66c9679724592455fdea7b93955cfc9bf35e1 Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Thu, 18 Aug 2022 11:54:28 +0200 Subject: [PATCH 062/213] Add plugin priorities --- exe/Plugins.hs | 4 --- ghcide/ghcide.cabal | 2 +- .../src/Development/IDE/LSP/Notifications.hs | 11 +++++++- hls-plugin-api/src/Ide/Types.hs | 28 +++++++++++++------ .../src/Ide/Plugin/ExplicitFixity.hs | 4 +++ 5 files changed, 34 insertions(+), 15 deletions(-) diff --git a/exe/Plugins.hs b/exe/Plugins.hs index 8dafaa3495..cba0c73658 100644 --- a/exe/Plugins.hs +++ b/exe/Plugins.hs @@ -211,12 +211,8 @@ idePlugins recorder includeExamples = pluginDescToIdePlugins allPlugins #if hls_gadt GADT.descriptor "gadt" : #endif - -- The ghcide descriptors should come last so that the notification handlers - -- (which restart the Shake build) run after everything else GhcIde.descriptors pluginRecorder #if explicitFixity - -- Make this plugin has a lower priority than ghcide's plugin to ensure - -- type info display first. ++ [ExplicitFixity.descriptor pluginRecorder] #endif examplePlugins = diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index c1a62f679b..8db4b73e50 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -192,6 +192,7 @@ library Development.IDE.Monitoring.EKG Development.IDE.LSP.HoverDefinition Development.IDE.LSP.LanguageServer + Development.IDE.LSP.Notifications Development.IDE.LSP.Outline Development.IDE.LSP.Server Development.IDE.Session @@ -225,7 +226,6 @@ library Development.IDE.Core.FileExists Development.IDE.GHC.CPP Development.IDE.GHC.Warnings - Development.IDE.LSP.Notifications Development.IDE.Plugin.CodeAction.PositionIndexed Development.IDE.Plugin.CodeAction.Args Development.IDE.Plugin.Completions.Logic diff --git a/ghcide/src/Development/IDE/LSP/Notifications.hs b/ghcide/src/Development/IDE/LSP/Notifications.hs index fbdd35489d..3830358af8 100644 --- a/ghcide/src/Development/IDE/LSP/Notifications.hs +++ b/ghcide/src/Development/IDE/LSP/Notifications.hs @@ -10,6 +10,7 @@ module Development.IDE.LSP.Notifications ( whenUriFile , descriptor , Log(..) + , ghcideNotificationsPluginPriority ) where import Language.LSP.Types @@ -38,6 +39,7 @@ import Development.IDE.Types.Location import Development.IDE.Types.Logger import Development.IDE.Types.Shake (toKey) import Ide.Types +import Numeric.Natural data Log = LogShake Shake.Log @@ -138,5 +140,12 @@ descriptor recorder plId = (defaultPluginDescriptor plId) { pluginNotificationHa success <- registerFileWatches globs unless success $ liftIO $ logDebug (ideLogger ide) "Warning: Client does not support watched files. Falling back to OS polling" - ] + ], + + -- The ghcide descriptors should come last'ish so that the notification handlers + -- (which restart the Shake build) run after everything else + pluginPriority = ghcideNotificationsPluginPriority } + +ghcideNotificationsPluginPriority :: Natural +ghcideNotificationsPluginPriority = defaultPluginPriority - 900 diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index 4a469f2d66..3ff2e60413 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -22,6 +22,7 @@ module Ide.Types ( PluginDescriptor(..), defaultPluginDescriptor, defaultCabalPluginDescriptor +, defaultPluginPriority , IdeCommand(..) , IdeMethod(..) , IdeNotification(..) @@ -61,10 +62,14 @@ import Data.Dependent.Map (DMap) import qualified Data.Dependent.Map as DMap import qualified Data.DList as DList import Data.GADT.Compare -import Data.List.Extra (nubOrdOn) +import Data.Hashable (Hashable) +import Data.HashMap.Strict (HashMap) +import qualified Data.HashMap.Strict as HashMap +import Data.List.Extra (sortOn) import Data.List.NonEmpty (NonEmpty (..), toList) import qualified Data.Map as Map import Data.Maybe +import Data.Ord import Data.Semigroup import Data.String import qualified Data.Text as T @@ -94,6 +99,7 @@ import Language.LSP.Types.Lens as J (HasChildren (children), HasTitle (title), HasUri (..)) import Language.LSP.VFS +import Numeric.Natural import OpenTelemetry.Eventlog import Options.Applicative (ParserInfo) import System.FilePath @@ -102,20 +108,16 @@ import Text.Regex.TDFA.Text () -- --------------------------------------------------------------------- -newtype IdePlugins ideState = IdePlugins_ - { ipMap_ :: [(PluginId, PluginDescriptor ideState)]} - deriving newtype Monoid +newtype IdePlugins ideState = IdePlugins_ { ipMap_ :: HashMap PluginId (PluginDescriptor ideState)} + deriving newtype (Semigroup, Monoid) -- | Smart constructor that deduplicates plugins pattern IdePlugins :: [(PluginId, PluginDescriptor ideState)] -> IdePlugins ideState -pattern IdePlugins{ipMap} <- IdePlugins_ ipMap +pattern IdePlugins{ipMap} <- IdePlugins_ (sortOn (Down . pluginPriority . snd) . HashMap.toList -> ipMap) where - IdePlugins ipMap = IdePlugins_{ipMap_ = nubOrdOn fst ipMap} + IdePlugins ipMap = IdePlugins_{ipMap_ = HashMap.fromList ipMap} {-# COMPLETE IdePlugins #-} -instance Semigroup (IdePlugins s) where - IdePlugins a <> IdePlugins b = IdePlugins(a <> b) - -- | Hooks for modifying the 'DynFlags' at different times of the compilation -- process. Plugins can install a 'DynFlagsModifications' via -- 'pluginModifyDynflags' in their 'PluginDescriptor'. @@ -149,6 +151,8 @@ instance Show (IdeCommand st) where show _ = "" data PluginDescriptor (ideState :: *) = PluginDescriptor { pluginId :: !PluginId -- ^ Unique identifier of the plugin. + , pluginPriority :: Natural + -- ^ Plugin handlers are called in priority order, higher priority first , pluginRules :: !(Rules ()) , pluginCommands :: ![PluginCommand ideState] , pluginHandlers :: PluginHandlers ideState @@ -631,6 +635,9 @@ mkPluginNotificationHandler m f where f' pid ide vfs = f ide vfs pid +defaultPluginPriority :: Natural +defaultPluginPriority = 1000 + -- | Set up a plugin descriptor, initialized with default values. -- This is plugin descriptor is prepared for @haskell@ files, such as -- @@ -644,6 +651,7 @@ defaultPluginDescriptor :: PluginId -> PluginDescriptor ideState defaultPluginDescriptor plId = PluginDescriptor plId + defaultPluginPriority mempty mempty mempty @@ -663,6 +671,7 @@ defaultCabalPluginDescriptor :: PluginId -> PluginDescriptor ideState defaultCabalPluginDescriptor plId = PluginDescriptor plId + defaultPluginPriority mempty mempty mempty @@ -694,6 +703,7 @@ type CommandFunction ideState a newtype PluginId = PluginId T.Text deriving (Show, Read, Eq, Ord) + deriving newtype Hashable instance IsString PluginId where fromString = PluginId . T.pack diff --git a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs index fb4e1c1d06..fa0985b17b 100644 --- a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs +++ b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs @@ -28,6 +28,7 @@ import qualified Development.IDE.Core.Shake as Shake import Development.IDE.GHC.Compat import Development.IDE.GHC.Compat.Util (FastString) import qualified Development.IDE.GHC.Compat.Util as Util +import Development.IDE.LSP.Notifications (ghcideNotificationsPluginPriority) import GHC.Generics (Generic) import Ide.PluginUtils (getNormalizedFilePath, handleMaybeM, @@ -42,6 +43,9 @@ descriptor :: Recorder (WithPriority Log) -> PluginDescriptor IdeState descriptor recorder = (defaultPluginDescriptor pluginId) { pluginRules = fixityRule recorder , pluginHandlers = mkPluginHandler STextDocumentHover hover + -- Make this plugin has a lower priority than ghcide's plugin to ensure + -- type info display first. + , pluginPriority = ghcideNotificationsPluginPriority - 1 } hover :: PluginMethodHandler IdeState TextDocumentHover From 583904ea1b90b430792b07a97204eae8b838c3f1 Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Thu, 18 Aug 2022 16:28:31 +0200 Subject: [PATCH 063/213] Fix tests --- ghcide/src/Development/IDE/Main.hs | 4 ++-- ghcide/src/Development/IDE/Plugin/HLS.hs | 3 +-- ghcide/test/exe/Main.hs | 12 +++++++----- hls-plugin-api/src/Ide/Plugin/ConfigUtils.hs | 4 ++-- hls-plugin-api/src/Ide/PluginUtils.hs | 8 +++----- hls-plugin-api/src/Ide/Types.hs | 7 ++++--- src/Ide/Main.hs | 6 +++--- 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/ghcide/src/Development/IDE/Main.hs b/ghcide/src/Development/IDE/Main.hs index 7ce2ac47d5..3b74d97870 100644 --- a/ghcide/src/Development/IDE/Main.hs +++ b/ghcide/src/Development/IDE/Main.hs @@ -122,7 +122,7 @@ import Ide.Types (IdeCommand (IdeComman IdePlugins, PluginDescriptor (PluginDescriptor, pluginCli), PluginId (PluginId), - ipMap) + ipMap, pluginId) import qualified Language.LSP.Server as LSP import qualified "list-t" ListT import Numeric.Natural (Natural) @@ -224,7 +224,7 @@ commandP plugins = pluginCommands = mconcat [ command (T.unpack pId) (Custom <$> p) - | (PluginId pId, PluginDescriptor{pluginCli = Just p}) <- ipMap plugins + | PluginDescriptor{pluginCli = Just p, pluginId = PluginId pId} <- ipMap plugins ] diff --git a/ghcide/src/Development/IDE/Plugin/HLS.hs b/ghcide/src/Development/IDE/Plugin/HLS.hs index bd192f18e8..447e516771 100644 --- a/ghcide/src/Development/IDE/Plugin/HLS.hs +++ b/ghcide/src/Development/IDE/Plugin/HLS.hs @@ -13,7 +13,6 @@ import Control.Exception (SomeException) import Control.Lens ((^.)) import Control.Monad import qualified Data.Aeson as J -import Data.Bifunctor import Data.Dependent.Map (DMap) import qualified Data.Dependent.Map as DMap import Data.Dependent.Sum @@ -96,7 +95,7 @@ asGhcIdePlugin recorder (IdePlugins ls) = mkPlugin :: ([(PluginId, b)] -> Plugin Config) -> (PluginDescriptor IdeState -> b) -> Plugin Config mkPlugin maker selector = - case map (second selector) ls of + case map (\p -> (pluginId p, selector p)) ls of -- If there are no plugins that provide a descriptor, use mempty to -- create the plugin – otherwise we we end up declaring handlers for -- capabilities that there are no plugins for diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index c4a4b082a7..82047a0603 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -6750,24 +6750,26 @@ unitTests recorder logger = do let expected = "1:2-3:4" assertBool (unwords ["expected to find range", expected, "in diagnostic", shown]) $ expected `isInfixOf` shown - , testCase "notification handlers run sequentially" $ do + , testCase "notification handlers run in priority order" $ do orderRef <- newIORef [] let plugins = pluginDescToIdePlugins $ - [ (defaultPluginDescriptor $ fromString $ show i) + [ (priorityPluginDescriptor i) { pluginNotificationHandlers = mconcat [ mkPluginNotificationHandler LSP.STextDocumentDidOpen $ \_ _ _ _ -> liftIO $ atomicModifyIORef_ orderRef (i:) ] } - | i <- [(1::Int)..20] + | i <- [1..20] ] ++ Ghcide.descriptors (cmapWithPrio LogGhcIde recorder) + priorityPluginDescriptor i = (defaultPluginDescriptor $ fromString $ show i){pluginPriority = i} testIde recorder (IDE.testing (cmapWithPrio LogIDEMain recorder) logger){IDE.argsHlsPlugins = plugins} $ do _ <- createDoc "A.hs" "haskell" "module A where" waitForProgressDone - actualOrder <- liftIO $ readIORef orderRef + actualOrder <- liftIO $ reverse <$> readIORef orderRef - liftIO $ actualOrder @?= reverse [(1::Int)..20] + -- Handlers are run in priority descending order + liftIO $ actualOrder @?= [20, 19 .. 1] , ignoreTestBecause "The test fails sometimes showing 10000us" $ testCase "timestamps have millisecond resolution" $ do resolution_us <- findResolution_us 1 diff --git a/hls-plugin-api/src/Ide/Plugin/ConfigUtils.hs b/hls-plugin-api/src/Ide/Plugin/ConfigUtils.hs index cea719a995..8e07af801d 100644 --- a/hls-plugin-api/src/Ide/Plugin/ConfigUtils.hs +++ b/hls-plugin-api/src/Ide/Plugin/ConfigUtils.hs @@ -34,7 +34,7 @@ pluginsToDefaultConfig IdePlugins {..} = A.toJSON defaultConfig & ix "haskell" . _Object . at "plugin" ?~ elems where defaultConfig@Config {} = def - elems = A.object $ mconcat $ singlePlugin <$> map snd ipMap + elems = A.object $ mconcat $ singlePlugin <$> ipMap -- Splice genericDefaultConfig and dedicatedDefaultConfig -- Example: -- @@ -96,7 +96,7 @@ pluginsToDefaultConfig IdePlugins {..} = -- | Generates json schema used in haskell vscode extension -- Similar to 'pluginsToDefaultConfig' but simpler, since schema has a flatten structure pluginsToVSCodeExtensionSchema :: IdePlugins a -> A.Value -pluginsToVSCodeExtensionSchema IdePlugins {..} = A.object $ mconcat $ singlePlugin <$> map snd ipMap +pluginsToVSCodeExtensionSchema IdePlugins {..} = A.object $ mconcat $ singlePlugin <$> ipMap where singlePlugin PluginDescriptor {pluginConfigDescriptor = ConfigDescriptor {..}, ..} = genericSchema <> dedicatedSchema where diff --git a/hls-plugin-api/src/Ide/PluginUtils.hs b/hls-plugin-api/src/Ide/PluginUtils.hs index 1f10b1cc70..4c9e936faf 100644 --- a/hls-plugin-api/src/Ide/PluginUtils.hs +++ b/hls-plugin-api/src/Ide/PluginUtils.hs @@ -42,7 +42,6 @@ import Control.Monad.Trans.Except (ExceptT, runExceptT, throwE) import Data.Algorithm.Diff import Data.Algorithm.DiffOutput import Data.Bifunctor (Bifunctor (first)) -import Data.Containers.ListUtils (nubOrdOn) import qualified Data.HashMap.Strict as H import Data.String (IsString (fromString)) import qualified Data.Text as T @@ -159,11 +158,10 @@ clientSupportsDocumentChanges caps = -- --------------------------------------------------------------------- pluginDescToIdePlugins :: [PluginDescriptor ideState] -> IdePlugins ideState -pluginDescToIdePlugins plugins = - IdePlugins $ map (\p -> (pluginId p, p)) $ nubOrdOn pluginId plugins +pluginDescToIdePlugins = IdePlugins idePluginsToPluginDesc :: IdePlugins ideState -> [PluginDescriptor ideState] -idePluginsToPluginDesc (IdePlugins pp) = map snd pp +idePluginsToPluginDesc (IdePlugins pp) = pp -- --------------------------------------------------------------------- -- | Returns the current client configuration. It is not wise to permanently @@ -233,7 +231,7 @@ allLspCmdIds' pid (IdePlugins ls) = mkPlugin (allLspCmdIds pid) (Just . pluginCo mkPlugin maker selector - = maker $ concatMap (\(pid, p) -> justs (pid, selector p)) ls + = maker $ concatMap (\p -> justs (pluginId p, selector p)) ls allLspCmdIds :: T.Text -> [(PluginId, [PluginCommand ideState])] -> [T.Text] diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index 3ff2e60413..95c04f24c5 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -55,6 +55,7 @@ import Control.Monad (void) import qualified System.Posix.Process as P (getProcessID) import System.Posix.Signals #endif +import Control.Arrow ((&&&)) import Control.Lens ((^.)) import Data.Aeson hiding (defaultOptions) import qualified Data.Default @@ -112,10 +113,10 @@ newtype IdePlugins ideState = IdePlugins_ { ipMap_ :: HashMap PluginId (PluginDe deriving newtype (Semigroup, Monoid) -- | Smart constructor that deduplicates plugins -pattern IdePlugins :: [(PluginId, PluginDescriptor ideState)] -> IdePlugins ideState -pattern IdePlugins{ipMap} <- IdePlugins_ (sortOn (Down . pluginPriority . snd) . HashMap.toList -> ipMap) +pattern IdePlugins :: [PluginDescriptor ideState] -> IdePlugins ideState +pattern IdePlugins{ipMap} <- IdePlugins_ (sortOn (Down . pluginPriority) . HashMap.elems -> ipMap) where - IdePlugins ipMap = IdePlugins_{ipMap_ = HashMap.fromList ipMap} + IdePlugins ipMap = IdePlugins_{ipMap_ = HashMap.fromList $ (pluginId &&& id) <$> ipMap} {-# COMPLETE IdePlugins #-} -- | Hooks for modifying the 'DynFlags' at different times of the compilation diff --git a/src/Ide/Main.hs b/src/Ide/Main.hs index d8d1df432f..a5a5acd594 100644 --- a/src/Ide/Main.hs +++ b/src/Ide/Main.hs @@ -32,7 +32,7 @@ import Ide.Arguments import Ide.Plugin.ConfigUtils (pluginsToDefaultConfig, pluginsToVSCodeExtensionSchema) import Ide.Types (IdePlugins, PluginId (PluginId), - ipMap) + ipMap, pluginId) import Ide.Version import System.Directory import qualified System.Directory.Extra as IO @@ -80,7 +80,7 @@ defaultMain recorder args idePlugins = do ListPluginsMode -> do let pluginNames = sort - $ map ((\(PluginId t) -> T.unpack t) . fst) + $ map ((\(PluginId t) -> T.unpack t) . pluginId) $ ipMap idePlugins mapM_ putStrLn pluginNames @@ -118,7 +118,7 @@ runLspMode recorder ghcideArgs@GhcideArguments{..} idePlugins = withTelemetryLog log Info $ LogDirectory dir when (isLSP argsCommand) $ do - log Info $ LogLspStart ghcideArgs (map fst $ ipMap idePlugins) + log Info $ LogLspStart ghcideArgs (map pluginId $ ipMap idePlugins) -- exists so old-style logging works. intended to be phased out let logger = Logger $ \p m -> logger_ recorder (WithPriority p emptyCallStack $ LogOther m) From 8d564d1518ccd3f71a97c83a934277ef45465727 Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Thu, 18 Aug 2022 18:44:07 +0200 Subject: [PATCH 064/213] Fix test --- ghcide/src/Development/IDE/Main.hs | 18 +++++++++++------- ghcide/test/exe/Main.hs | 3 ++- hls-plugin-api/src/Ide/PluginUtils.hs | 12 +++--------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/ghcide/src/Development/IDE/Main.hs b/ghcide/src/Development/IDE/Main.hs index 3b74d97870..d5e19856a7 100644 --- a/ghcide/src/Development/IDE/Main.hs +++ b/ghcide/src/Development/IDE/Main.hs @@ -21,6 +21,7 @@ import Control.Exception.Safe (SomeException, import Control.Monad.Extra (concatMapM, unless, when) import qualified Data.Aeson.Encode.Pretty as A +import Data.Coerce (coerce) import Data.Default (Default (def)) import Data.Foldable (traverse_) import Data.Hashable (hashed) @@ -92,7 +93,8 @@ import Development.IDE.Types.Logger (Logger, Recorder, WithPriority, cmapWithPrio, - logWith, vsep, (<+>)) + logWith, nest, vsep, + (<+>)) import Development.IDE.Types.Monitoring (Monitoring) import Development.IDE.Types.Options (IdeGhcSession, IdeOptions (optCheckParents, optCheckProject, optReportProgress, optRunSubset), @@ -146,7 +148,7 @@ import Text.Printf (printf) data Log = LogHeapStats !HeapStats.Log - | LogLspStart + | LogLspStart [PluginId] | LogLspStartDuration !Seconds | LogShouldRunSubset !Bool | LogOnlyPartialGhc92Support @@ -163,10 +165,12 @@ data Log instance Pretty Log where pretty = \case LogHeapStats log -> pretty log - LogLspStart -> - vsep - [ "Staring LSP server..." - , "If you are seeing this in a terminal, you probably should have run WITHOUT the --lsp option!"] + LogLspStart pluginIds -> + nest 2 $ vsep + [ "Starting LSP server..." + , "If you are seeing this in a terminal, you probably should have run WITHOUT the --lsp option!" + , "PluginIds:" <+> pretty (coerce @_ @[T.Text] pluginIds) + ] LogLspStartDuration duration -> "Started LSP server in" <+> pretty (showDuration duration) LogShouldRunSubset shouldRunSubset -> @@ -336,7 +340,7 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re LT.putStrLn $ decodeUtf8 $ A.encodePretty $ pluginsToDefaultConfig argsHlsPlugins LSP -> withNumCapabilities (maybe (numProcessors `div` 2) fromIntegral argsThreads) $ do t <- offsetTime - log Info LogLspStart + log Info $ LogLspStart (pluginId <$> ipMap argsHlsPlugins) let getIdeState :: LSP.LanguageContextEnv Config -> Maybe FilePath -> WithHieDb -> IndexQueue -> IO IdeState getIdeState env rootPath withHieDb hieChan = do diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 82047a0603..73caa02437 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -290,7 +290,8 @@ initializeResponseTests = withResource acquire release tests where doTest = do ir <- getInitializeResponse let Just ExecuteCommandOptions {_commands = List commands} = getActual $ innerCaps ir - zipWithM_ (\e o -> T.isSuffixOf e o @? show (e,o)) expected commands + commandNames = (!! 2) . T.splitOn ":" <$> commands + zipWithM_ (\e o -> T.isSuffixOf e o @? show (e,o)) (sort expected) (sort commandNames) innerCaps :: ResponseMessage Initialize -> ServerCapabilities innerCaps (ResponseMessage _ _ (Right (InitializeResult c _))) = c diff --git a/hls-plugin-api/src/Ide/PluginUtils.hs b/hls-plugin-api/src/Ide/PluginUtils.hs index 4c9e936faf..b194c429c9 100644 --- a/hls-plugin-api/src/Ide/PluginUtils.hs +++ b/hls-plugin-api/src/Ide/PluginUtils.hs @@ -36,6 +36,7 @@ module Ide.PluginUtils where +import Control.Arrow ((&&&)) import Control.Monad.Extra (maybeM) import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Except (ExceptT, runExceptT, throwE) @@ -224,15 +225,8 @@ positionInRange p (Range sp ep) = sp <= p && p < ep -- Range's end position is e -- --------------------------------------------------------------------- allLspCmdIds' :: T.Text -> IdePlugins ideState -> [T.Text] -allLspCmdIds' pid (IdePlugins ls) = mkPlugin (allLspCmdIds pid) (Just . pluginCommands) - where - justs (p, Just x) = [(p, x)] - justs (_, Nothing) = [] - - - mkPlugin maker selector - = maker $ concatMap (\p -> justs (pluginId p, selector p)) ls - +allLspCmdIds' pid (IdePlugins ls) = + allLspCmdIds pid $ map (pluginId &&& pluginCommands) ls allLspCmdIds :: T.Text -> [(PluginId, [PluginCommand ideState])] -> [T.Text] allLspCmdIds pid commands = concatMap go commands From 753042c0f4a3d220238db7d7ae08d1cf18fb5aa9 Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Thu, 18 Aug 2022 23:25:36 +0200 Subject: [PATCH 065/213] fix another test that relies on plugin ordering --- test/functional/Format.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/functional/Format.hs b/test/functional/Format.hs index af90fc7a9c..9b853d527e 100644 --- a/test/functional/Format.hs +++ b/test/functional/Format.hs @@ -47,7 +47,10 @@ providerTests = testGroup "formatting provider" [ testCase "respects none" $ runSessionWithConfig (formatConfig "none") hlsCommand fullCaps "test/testdata/format" $ do doc <- openDoc "Format.hs" "haskell" resp <- request STextDocumentFormatting $ DocumentFormattingParams Nothing doc (FormattingOptions 2 True Nothing Nothing Nothing) - liftIO $ resp ^. LSP.result @?= Left (ResponseError InvalidRequest "No plugin enabled for STextDocumentFormatting, available:\nPluginId \"floskell\"\nPluginId \"fourmolu\"\nPluginId \"ormolu\"\nPluginId \"stylish-haskell\"\nPluginId \"brittany\"\n" Nothing) + liftIO $ resp ^. LSP.result @?= Left (ResponseError InvalidRequest + ("No plugin enabled for STextDocumentFormatting, available:\n" + <> "PluginId \"floskell\"\nPluginId \"fourmolu\"\nPluginId \"stylish-haskell\"\nPluginId \"brittany\"\nPluginId \"ormolu\"\n") + Nothing) , requiresOrmoluPlugin . requiresFloskellPlugin $ testCase "can change on the fly" $ runSession hlsCommand fullCaps "test/testdata/format" $ do formattedOrmolu <- liftIO $ T.readFile "test/testdata/format/Format.ormolu.formatted.hs" From 099b4e7b88f9b02b81a6234ab79a55b4d60ae35c Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Fri, 19 Aug 2022 07:11:17 +0200 Subject: [PATCH 066/213] Fix priority for pragma completions --- .../src/Development/IDE/Plugin/Completions.hs | 6 +++++ .../src/Ide/Plugin/Pragmas.hs | 26 ++++++++++--------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index c0a76bc360..4e5e96a3bd 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -5,6 +5,7 @@ module Development.IDE.Plugin.Completions ( descriptor , Log(..) + , ghcideCompletionsPluginPriority ) where import Control.Concurrent.Async (concurrently) @@ -49,6 +50,7 @@ import Ide.Types import qualified Language.LSP.Server as LSP import Language.LSP.Types import qualified Language.LSP.VFS as VFS +import Numeric.Natural import Text.Fuzzy.Parallel (Scored (..)) data Log = LogShake Shake.Log deriving Show @@ -57,12 +59,16 @@ instance Pretty Log where pretty = \case LogShake log -> pretty log +ghcideCompletionsPluginPriority :: Natural +ghcideCompletionsPluginPriority = defaultPluginPriority + descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState descriptor recorder plId = (defaultPluginDescriptor plId) { pluginRules = produceCompletions recorder , pluginHandlers = mkPluginHandler STextDocumentCompletion getCompletionsLSP , pluginCommands = [extendImportCommand] , pluginConfigDescriptor = defaultConfigDescriptor {configCustomConfig = mkCustomConfig properties} + , pluginPriority = ghcideCompletionsPluginPriority } produceCompletions :: Recorder (WithPriority Log) -> Rules () diff --git a/plugins/hls-pragmas-plugin/src/Ide/Plugin/Pragmas.hs b/plugins/hls-pragmas-plugin/src/Ide/Plugin/Pragmas.hs index 75f53e26a5..c26d9cbc79 100644 --- a/plugins/hls-pragmas-plugin/src/Ide/Plugin/Pragmas.hs +++ b/plugins/hls-pragmas-plugin/src/Ide/Plugin/Pragmas.hs @@ -14,21 +14,22 @@ module Ide.Plugin.Pragmas , validPragmas ) where -import Control.Lens hiding (List) -import Control.Monad.IO.Class (MonadIO (liftIO)) -import qualified Data.HashMap.Strict as H -import Data.List.Extra (nubOrdOn) -import Data.Maybe (catMaybes) -import qualified Data.Text as T +import Control.Lens hiding (List) +import Control.Monad.IO.Class (MonadIO (liftIO)) +import qualified Data.HashMap.Strict as H +import Data.List.Extra (nubOrdOn) +import Data.Maybe (catMaybes) +import qualified Data.Text as T import Development.IDE import Development.IDE.GHC.Compat -import qualified Development.IDE.Spans.Pragmas as Pragmas +import Development.IDE.Plugin.Completions (ghcideCompletionsPluginPriority) +import qualified Development.IDE.Spans.Pragmas as Pragmas import Ide.Types -import qualified Language.LSP.Server as LSP -import qualified Language.LSP.Types as J -import qualified Language.LSP.Types.Lens as J -import qualified Language.LSP.VFS as VFS -import qualified Text.Fuzzy as Fuzzy +import qualified Language.LSP.Server as LSP +import qualified Language.LSP.Types as J +import qualified Language.LSP.Types.Lens as J +import qualified Language.LSP.VFS as VFS +import qualified Text.Fuzzy as Fuzzy -- --------------------------------------------------------------------- @@ -36,6 +37,7 @@ descriptor :: PluginId -> PluginDescriptor IdeState descriptor plId = (defaultPluginDescriptor plId) { pluginHandlers = mkPluginHandler J.STextDocumentCodeAction codeActionProvider <> mkPluginHandler J.STextDocumentCompletion completion + , pluginPriority = ghcideCompletionsPluginPriority + 1 } -- --------------------------------------------------------------------- From 7317750248663622bae7d9953d1ddc7e1a84a4ef Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Sat, 20 Aug 2022 03:10:04 +0200 Subject: [PATCH 067/213] Fix --testing (#3113) * Fix --testing Fix the --testing flag for HLS to correctly link the ghcide test plugin * fix ghcide options --- src/Ide/Main.hs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Ide/Main.hs b/src/Ide/Main.hs index a5a5acd594..92bedba4fa 100644 --- a/src/Ide/Main.hs +++ b/src/Ide/Main.hs @@ -122,16 +122,15 @@ runLspMode recorder ghcideArgs@GhcideArguments{..} idePlugins = withTelemetryLog -- exists so old-style logging works. intended to be phased out let logger = Logger $ \p m -> logger_ recorder (WithPriority p emptyCallStack $ LogOther m) + args = (if argsTesting then IDEMain.testing else IDEMain.defaultArguments) + (cmapWithPrio LogIDEMain recorder) logger - IDEMain.defaultMain (cmapWithPrio LogIDEMain recorder) (IDEMain.defaultArguments (cmapWithPrio LogIDEMain recorder) logger) + IDEMain.defaultMain (cmapWithPrio LogIDEMain recorder) args { IDEMain.argCommand = argsCommand - , IDEMain.argsHlsPlugins = idePlugins + , IDEMain.argsHlsPlugins = IDEMain.argsHlsPlugins args <> idePlugins , IDEMain.argsLogger = pure logger <> pure telemetryLogger , IDEMain.argsThreads = if argsThreads == 0 then Nothing else Just $ fromIntegral argsThreads - , IDEMain.argsIdeOptions = \_config sessionLoader -> - let defOptions = Ghcide.defaultIdeOptions sessionLoader - in defOptions - { Ghcide.optShakeProfiling = argsShakeProfiling - , Ghcide.optTesting = Ghcide.IdeTesting argsTesting - } + , IDEMain.argsIdeOptions = \config sessionLoader -> + let defOptions = IDEMain.argsIdeOptions args config sessionLoader + in defOptions { Ghcide.optShakeProfiling = argsShakeProfiling } } From 9a0684ec28022f644de4f8a0eb3610ea7a8dd2eb Mon Sep 17 00:00:00 2001 From: Nick Suchecki <40047416+drsooch@users.noreply.github.com> Date: Sun, 21 Aug 2022 13:07:53 -0400 Subject: [PATCH 068/213] Remove pluginId from getNormalizedFilePath error message (#3118) The error message displayed by `prettyResponseError` in `Development.IDE.Plugin.HLS` looks awkward with the pluginId. This error is not the fault of plugins anyways, so let's just be generic. --- ghcide/src/Development/IDE/Plugin/HLS.hs | 2 +- hls-plugin-api/src/Ide/PluginUtils.hs | 6 +++--- .../src/Ide/Plugin/AlternateNumberFormat.hs | 4 ++-- .../src/Ide/Plugin/CallHierarchy/Internal.hs | 4 ++-- .../src/Ide/Plugin/ChangeTypeSignature.hs | 4 ++-- plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeAction.hs | 6 +++--- plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeLens.hs | 2 +- .../src/Ide/Plugin/ExplicitFixity.hs | 4 ++-- plugins/hls-gadt-plugin/src/Ide/Plugin/GADT.hs | 6 +++--- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/HLS.hs b/ghcide/src/Development/IDE/Plugin/HLS.hs index 447e516771..ea30eb2c53 100644 --- a/ghcide/src/Development/IDE/Plugin/HLS.hs +++ b/ghcide/src/Development/IDE/Plugin/HLS.hs @@ -197,7 +197,7 @@ extensiblePlugins recorder xs = mempty { P.pluginHandlers = handlers } case nonEmpty fs of Nothing -> logAndReturnError recorder InvalidRequest (pluginNotEnabled m fs') Just fs -> do - let msg e pid = "Exception in plugin " <> T.pack (show pid) <> "while processing " <> T.pack (show m) <> ": " <> T.pack (show e) + let msg e pid = "Exception in plugin " <> T.pack (show pid) <> " while processing " <> T.pack (show m) <> ": " <> T.pack (show e) handlers = fmap (\(plid,_,handler) -> (plid,handler)) fs es <- runConcurrently msg (show m) handlers ide params let (errs,succs) = partitionEithers $ toList es diff --git a/hls-plugin-api/src/Ide/PluginUtils.hs b/hls-plugin-api/src/Ide/PluginUtils.hs index b194c429c9..209569a2bd 100644 --- a/hls-plugin-api/src/Ide/PluginUtils.hs +++ b/hls-plugin-api/src/Ide/PluginUtils.hs @@ -235,12 +235,12 @@ allLspCmdIds pid commands = concatMap go commands -- --------------------------------------------------------------------- -getNormalizedFilePath :: Monad m => PluginId -> Uri -> ExceptT String m NormalizedFilePath -getNormalizedFilePath (PluginId plId) uri = handleMaybe errMsg +getNormalizedFilePath :: Monad m => Uri -> ExceptT String m NormalizedFilePath +getNormalizedFilePath uri = handleMaybe errMsg $ uriToNormalizedFilePath $ toNormalizedUri uri where - errMsg = T.unpack $ "Error(" <> plId <> "): converting " <> getUri uri <> " to NormalizedFilePath" + errMsg = T.unpack $ "Failed converting " <> getUri uri <> " to NormalizedFilePath" -- --------------------------------------------------------------------- throwPluginError :: Monad m => String -> ExceptT String m b diff --git a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs index 530ced8f7a..c7ae1bae19 100644 --- a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs +++ b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs @@ -87,8 +87,8 @@ collectLiteralsRule recorder = define (cmapWithPrio LogShake recorder) $ \Collec getExtensions = map GhcExtension . toList . extensionFlags . ms_hspp_opts . pm_mod_summary codeActionHandler :: PluginMethodHandler IdeState 'TextDocumentCodeAction -codeActionHandler state plId (CodeActionParams _ _ docId currRange _) = pluginResponse $ do - nfp <- getNormalizedFilePath plId (docId ^. L.uri) +codeActionHandler state _ (CodeActionParams _ _ docId currRange _) = pluginResponse $ do + nfp <- getNormalizedFilePath (docId ^. L.uri) CLR{..} <- requestLiterals state nfp pragma <- getFirstPragma state nfp -- remove any invalid literals (see validTarget comment) diff --git a/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Internal.hs b/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Internal.hs index ed6ad5e534..8219862cc7 100644 --- a/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Internal.hs +++ b/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Internal.hs @@ -49,8 +49,8 @@ callHierarchyId = PluginId "callHierarchy" -- | Render prepare call hierarchy request. prepareCallHierarchy :: PluginMethodHandler IdeState TextDocumentPrepareCallHierarchy -prepareCallHierarchy state pluginId param = pluginResponse $ do - nfp <- getNormalizedFilePath pluginId (param ^. L.textDocument ^. L.uri) +prepareCallHierarchy state _ param = pluginResponse $ do + nfp <- getNormalizedFilePath (param ^. L.textDocument ^. L.uri) items <- liftIO (runAction "CallHierarchy.prepareHierarchy" state (prepareCallHierarchyItem nfp (param ^. L.position))) pure (List <$> items) diff --git a/plugins/hls-change-type-signature-plugin/src/Ide/Plugin/ChangeTypeSignature.hs b/plugins/hls-change-type-signature-plugin/src/Ide/Plugin/ChangeTypeSignature.hs index 594543dd30..f3a39fd03c 100644 --- a/plugins/hls-change-type-signature-plugin/src/Ide/Plugin/ChangeTypeSignature.hs +++ b/plugins/hls-change-type-signature-plugin/src/Ide/Plugin/ChangeTypeSignature.hs @@ -38,8 +38,8 @@ descriptor :: PluginDescriptor IdeState descriptor = (defaultPluginDescriptor changeTypeSignatureId) { pluginHandlers = mkPluginHandler STextDocumentCodeAction codeActionHandler } codeActionHandler :: PluginMethodHandler IdeState 'TextDocumentCodeAction -codeActionHandler ideState plId CodeActionParams {_textDocument = TextDocumentIdentifier uri, _context = CodeActionContext (List diags) _} = pluginResponse $ do - nfp <- getNormalizedFilePath plId uri +codeActionHandler ideState _ CodeActionParams {_textDocument = TextDocumentIdentifier uri, _context = CodeActionContext (List diags) _} = pluginResponse $ do + nfp <- getNormalizedFilePath uri decls <- getDecls ideState nfp let actions = mapMaybe (generateAction uri decls) diags pure $ List actions diff --git a/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeAction.hs b/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeAction.hs index 8c0d14f9d0..05c73beda8 100644 --- a/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeAction.hs +++ b/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeAction.hs @@ -37,10 +37,10 @@ import Language.LSP.Types import qualified Language.LSP.Types.Lens as J addMethodPlaceholders :: PluginId -> CommandFunction IdeState AddMinimalMethodsParams -addMethodPlaceholders plId state param@AddMinimalMethodsParams{..} = do +addMethodPlaceholders _ state param@AddMinimalMethodsParams{..} = do caps <- getClientCapabilities pluginResponse $ do - nfp <- getNormalizedFilePath plId uri + nfp <- getNormalizedFilePath uri pm <- handleMaybeM "Unable to GetParsedModule" $ liftIO $ runAction "classplugin.addMethodPlaceholders.GetParsedModule" state @@ -81,7 +81,7 @@ addMethodPlaceholders plId state param@AddMinimalMethodsParams{..} = do -- sensitive to the format of diagnostic messages from GHC. codeAction :: Recorder (WithPriority Log) -> PluginMethodHandler IdeState TextDocumentCodeAction codeAction recorder state plId (CodeActionParams _ _ docId _ context) = pluginResponse $ do - nfp <- getNormalizedFilePath plId uri + nfp <- getNormalizedFilePath uri actions <- join <$> mapM (mkActions nfp) methodDiags pure $ List actions where diff --git a/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeLens.hs b/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeLens.hs index 042c46c52b..3f7facf668 100644 --- a/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeLens.hs +++ b/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeLens.hs @@ -24,7 +24,7 @@ import qualified Language.LSP.Types.Lens as J codeLens :: PluginMethodHandler IdeState TextDocumentCodeLens codeLens state plId CodeLensParams{..} = pluginResponse $ do - nfp <- getNormalizedFilePath plId uri + nfp <- getNormalizedFilePath uri tmr <- handleMaybeM "Unable to typecheck" $ liftIO $ runAction "classplugin.TypeCheck" state diff --git a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs index fa0985b17b..1fb1c5aa11 100644 --- a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs +++ b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs @@ -49,8 +49,8 @@ descriptor recorder = (defaultPluginDescriptor pluginId) } hover :: PluginMethodHandler IdeState TextDocumentHover -hover state plId (HoverParams (TextDocumentIdentifier uri) pos _) = pluginResponse $ do - nfp <- getNormalizedFilePath plId uri +hover state _ (HoverParams (TextDocumentIdentifier uri) pos _) = pluginResponse $ do + nfp <- getNormalizedFilePath uri fixityTrees <- handleMaybeM "ExplicitFixity: Unable to get fixity" $ liftIO $ runAction "ExplicitFixity.GetFixity" state diff --git a/plugins/hls-gadt-plugin/src/Ide/Plugin/GADT.hs b/plugins/hls-gadt-plugin/src/Ide/Plugin/GADT.hs index f1c7d993d9..fe68a74be8 100644 --- a/plugins/hls-gadt-plugin/src/Ide/Plugin/GADT.hs +++ b/plugins/hls-gadt-plugin/src/Ide/Plugin/GADT.hs @@ -52,8 +52,8 @@ toGADTSyntaxCommandId = "GADT.toGADT" -- | A command replaces H98 data decl with GADT decl in place toGADTCommand :: PluginId -> CommandFunction IdeState ToGADTParams -toGADTCommand plId state ToGADTParams{..} = pluginResponse $ do - nfp <- getNormalizedFilePath plId uri +toGADTCommand _ state ToGADTParams{..} = pluginResponse $ do + nfp <- getNormalizedFilePath uri (decls, exts) <- getInRangeH98DeclsAndExts state range nfp (L ann decl) <- case decls of [d] -> pure d @@ -83,7 +83,7 @@ toGADTCommand plId state ToGADTParams{..} = pluginResponse $ do codeActionHandler :: PluginMethodHandler IdeState TextDocumentCodeAction codeActionHandler state plId (CodeActionParams _ _ doc range _) = pluginResponse $ do - nfp <- getNormalizedFilePath plId (doc ^. L.uri) + nfp <- getNormalizedFilePath (doc ^. L.uri) (inRangeH98Decls, _) <- getInRangeH98DeclsAndExts state range nfp let actions = map (mkAction . printOutputable . tcdLName . unLoc) inRangeH98Decls pure $ List actions From 55d90241440c5282b9e60cf80a98fd8698e1f8d9 Mon Sep 17 00:00:00 2001 From: Matt Parsons Date: Tue, 23 Aug 2022 09:46:53 -0600 Subject: [PATCH 069/213] Add link to homepage and issues (#3119) --- hie-compat/hie-compat.cabal | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hie-compat/hie-compat.cabal b/hie-compat/hie-compat.cabal index 3313b49b62..8166d2fcd2 100644 --- a/hie-compat/hie-compat.cabal +++ b/hie-compat/hie-compat.cabal @@ -14,6 +14,8 @@ maintainer: zubin.duggal@gmail.com build-type: Simple extra-source-files: CHANGELOG.md README.md category: Development +homepage: https://github.com/haskell/haskell-language-server/tree/master/hie-compat#readme +bug-reports: https://github.com/haskell/haskell-language-server/issues flag ghc-lib description: build against ghc-lib instead of the ghc package From d0e3e0fe3f1d14dd51a40d43d3272269b445cede Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Thu, 25 Aug 2022 15:08:57 +0100 Subject: [PATCH 070/213] HLS benchmarks (#3117) * extract ghcide:experiments-types * extract haskell-language-server:plugins and let go of examples The main goal here is to move the Plugins module into an internal library so that it can be reused from the benchmark suite. In order to make that easier, and since they hardly serve a purpose in a repository with 25 plugins, I delete the Example and Example2 plugin descriptors and their dependencies. * HLS benchmark suite Port the ghcide benchmark suite to HLS and benchmark plugin "configurations" independently. This includes the following changes to the ghcide benchmark suite and HLS: - Support for "configurations" which are defined as sets of plugin ids. The benchmark will be run with only these plugins enabled and all others disabled - Support for configurable concurrency. This relies on RTS -ol and -po flags to place the RTS traces in the target location rather than in the cwd This change requires two commits, the next one places ghcide/bench/hist/Main.hs into its final location to help 'git' recognize the change as a file move * ghcide/bench/hist/Main.hs -> bench/Main.hs * CI - fix artifact names for uniqueness * disable shorten HLS step * Do not store eventlogs to avoid out of disk space * render durations up to milliseconds * shorten titles Goal is to display the formatted CSV (via column) one row per line * exclude formatting plugin configurations * Extract ghcide-bench to a standalone package * ghcide-bench: fix stderr capturing * Fix mem stats We parse maxResidency and allocatedBytes from the RTS -S output, but runSessionWithHandles kills the server without waiting for it to exit and these stats don't get logged. The solution is to use runSessionWithHandles', but unfortunately it is internal and not exposed. I have raised a PR to expose it and in the meantime we need a source repo package. * feedbacks * delete Example plugins --- .github/workflows/bench.yml | 26 +- .gitignore | 3 +- bench/Main.hs | 282 ++++++++++++++++++ {ghcide/bench => bench}/README.md | 22 +- bench/config.yaml | 175 +++++++++++ cabal.project | 9 + docs/contributing/contributing.md | 6 +- exe/Main.hs | 14 +- ghcide-bench/LICENSE | 201 +++++++++++++ ghcide-bench/README.md | 61 ++++ {ghcide/bench => ghcide-bench}/exe/Main.hs | 0 ghcide-bench/ghcide-bench.cabal | 137 +++++++++ .../src/Development/IDE/Test/Diagnostic.hs | 48 +++ .../lib => ghcide-bench/src}/Experiments.hs | 166 +++++++---- .../src}/Experiments/Types.hs | 19 +- ghcide-bench/test/Main.hs | 48 +++ ghcide/.gitignore | 5 - ghcide/bench-results/.artifactignore | 4 - ghcide/bench/config.yaml | 116 ------- ghcide/bench/hist/Main.hs | 192 ------------ ghcide/ghcide.cabal | 110 +------ ghcide/test/exe/Main.hs | 21 -- ghcide/test/src/Development/IDE/Test.hs | 20 -- haskell-language-server.cabal | 68 ++++- hls-plugin-api/src/Ide/Types.hs | 2 +- plugins/default/src/Ide/Plugin/Example.hs | 253 ---------------- plugins/default/src/Ide/Plugin/Example2.hs | 237 --------------- .../default/src/Ide/Plugin/ExampleCabal.hs | 75 ----- shake-bench/shake-bench.cabal | 1 + .../src/Development/Benchmark/Rules.hs | 159 ++++++---- exe/Plugins.hs => src/HlsPlugins.hs | 19 +- test/functional/Diagnostic.hs | 17 +- 32 files changed, 1296 insertions(+), 1220 deletions(-) create mode 100644 bench/Main.hs rename {ghcide/bench => bench}/README.md (65%) create mode 100644 bench/config.yaml create mode 100644 ghcide-bench/LICENSE create mode 100644 ghcide-bench/README.md rename {ghcide/bench => ghcide-bench}/exe/Main.hs (100%) create mode 100644 ghcide-bench/ghcide-bench.cabal create mode 100644 ghcide-bench/src/Development/IDE/Test/Diagnostic.hs rename {ghcide/bench/lib => ghcide-bench/src}/Experiments.hs (85%) rename {ghcide/bench/lib => ghcide-bench/src}/Experiments/Types.hs (82%) create mode 100644 ghcide-bench/test/Main.hs delete mode 100644 ghcide/bench-results/.artifactignore delete mode 100644 ghcide/bench/config.yaml delete mode 100644 ghcide/bench/hist/Main.hs delete mode 100644 plugins/default/src/Ide/Plugin/Example.hs delete mode 100644 plugins/default/src/Ide/Plugin/Example2.hs delete mode 100644 plugins/default/src/Ide/Plugin/ExampleCabal.hs rename exe/Plugins.hs => src/HlsPlugins.hs (88%) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 2945ac2812..a9d7bdfae4 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -61,16 +61,17 @@ jobs: with: ghc: ${{ matrix.ghc }} os: ${{ runner.os }} + shorten-hls: "false" # max-backjumps is increased as a temporary solution # for dependency resolution failure - run: cabal configure --enable-benchmarks --max-backjumps 12000 - name: Build - run: cabal build ghcide:benchHist + run: cabal build haskell-language-server:benchmark - name: Bench init - run: cabal bench ghcide:benchHist -j --benchmark-options="all-binaries" + run: cabal bench -j --benchmark-options="all-binaries" # tar is required to preserve file permissions # compression speeds up upload/download nicely @@ -85,14 +86,14 @@ jobs: - name: Upload workspace uses: actions/upload-artifact@v3 with: - name: workspace + name: workspace-${{ matrix.ghc }}-${{ matrix.os }} retention-days: 1 path: workspace.tar.gz - name: Upload .cabal uses: actions/upload-artifact@v3 with: - name: cabal-home + name: cabal-home-${{ matrix.ghc }}-${{ matrix.os }} retention-days: 1 path: ~/.cabal/cabal.tar.gz @@ -118,13 +119,13 @@ jobs: - name: Download cabal home uses: actions/download-artifact@v3 with: - name: cabal-home + name: cabal-home-${{ matrix.ghc }}-${{ matrix.os }} path: . - name: Download workspace uses: actions/download-artifact@v3 with: - name: workspace + name: workspace-${{ matrix.ghc }}-${{ matrix.os }} path: . - name: untar @@ -134,28 +135,29 @@ jobs: tar xzf cabal.tar.gz --directory ~/.cabal - name: Bench - run: cabal bench ghcide:benchHist -j --benchmark-options="${{ matrix.example }}" + run: cabal bench -j --benchmark-options="${{ matrix.example }}" - name: Display results run: | - column -s, -t < ghcide/bench-results/unprofiled/${{ matrix.example }}/results.csv | tee ghcide/bench-results/unprofiled/${{ matrix.example }}/results.txt + column -s, -t < bench-results/unprofiled/${{ matrix.example }}/results.csv | tee bench-results/unprofiled/${{ matrix.example }}/results.txt - name: tar benchmarking artifacts - run: find ghcide/bench-results -name "*.csv" -or -name "*.svg" -or -name "*.html" | xargs tar -czf benchmark-artifacts.tar.gz + run: find bench-results -name "*.csv" -or -name "*.svg" -or -name "*.html" | xargs tar -czf benchmark-artifacts.tar.gz - name: Archive benchmarking artifacts uses: actions/upload-artifact@v3 with: - name: bench-results-${{ runner.os }}-${{ matrix.ghc }} + name: bench-results-${{ matrix.example }}-${{ runner.os }}-${{ matrix.ghc }} path: benchmark-artifacts.tar.gz - name: tar benchmarking logs - run: find ghcide/bench-results -name "*.log" -or -name "*.eventlog" -or -name "*.hp" | xargs tar -czf benchmark-logs.tar.gz + # We dont' store the eventlogs because the CI workers risk running out of disk space + run: find bench-results -name "*.log" -or -name "*.hp" | xargs tar -czf benchmark-logs.tar.gz - name: Archive benchmark logs uses: actions/upload-artifact@v3 with: - name: bench-logs-${{ runner.os }}-${{ matrix.ghc }} + name: bench-logs-${{ matrix.example }}-${{ runner.os }}-${{ matrix.ghc }} path: benchmark-logs.tar.gz bench_post_job: diff --git a/.gitignore b/.gitignore index ed983e69c8..1cf8b239ad 100644 --- a/.gitignore +++ b/.gitignore @@ -34,9 +34,10 @@ test/testdata/**/hie.yaml /.direnv/ /.envrc -# ghcide-bench +# bench *.identifierPosition /bench/example +/bench-results # nix result diff --git a/bench/Main.hs b/bench/Main.hs new file mode 100644 index 0000000000..97d01f9537 --- /dev/null +++ b/bench/Main.hs @@ -0,0 +1,282 @@ + +{- Bench history + + A Shake script to analyze the performance of HLS over the git history of the project + + Driven by a config file `bench/config.yaml` containing the list of Git references to analyze. + + Builds each one of them and executes a set of experiments using the ghcide-bench suite. + + The results of the benchmarks and the analysis are recorded in the file + system with the following structure: + + bench-results + ├── + │  ├── ghc.path - path to ghc used to build the binary + │  └── haskell-language-server - binary for this version + ├─ + │ ├── results.csv - aggregated results for all the versions + │ └── + | └── + │   ├── .gcStats.log - RTS -s output + │   ├── .csv - stats for the experiment + │   ├── .svg - Graph of bytes over elapsed time + │   ├── .diff.svg - idem, including the previous version + │   ├── .log - ghcide-bench output + │   └── results.csv - results of all the experiments for the example + ├── results.csv - aggregated results of all the experiments and versions + └── .svg - graph of bytes over elapsed time, for all the included versions + + For diff graphs, the "previous version" is the preceding entry in the list of versions + in the config file. A possible improvement is to obtain this info via `git rev-list`. + + To execute the script: + + > cabal/stack bench + + To build a specific analysis, enumerate the desired file artifacts + + > stack bench --ba "bench-results/HEAD/results.csv bench-results/HEAD/edit.diff.svg" + > cabal bench --benchmark-options "bench-results/HEAD/results.csv bench-results/HEAD/edit.diff.svg" + + -} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE TypeFamilies #-} +{-# OPTIONS -Wno-orphans #-} +{-# LANGUAGE PackageImports #-} + +import Control.Lens (preview, (^.)) +import Control.Monad.Extra +import Data.Aeson (Value (..), encode) +import Data.Aeson.Lens +import Data.Default +import Data.Foldable (find) +import qualified Data.Map.Strict as Map +import Data.Maybe +import Data.Text (pack, unpack) +import Data.Yaml (FromJSON (..), ToJSON (toJSON), + decodeFileThrow) +import Development.Benchmark.Rules hiding (parallelism) +import Development.Shake (Action, + Change (ChangeModtimeAndDigestInput), + CmdOption (Cwd, StdinBS), + RuleResult, Rules, + ShakeOptions (shakeChange, shakeThreads), + actionBracket, addOracle, + askOracle, command, command_, + getDirectoryFiles, liftIO, need, + newCache, shakeArgsWith, + shakeOptions, versioned, want) +import Development.Shake.Classes +import Experiments.Types (Example (exampleName), + exampleToOptions) +import GHC.Exts (toList) +import GHC.Generics (Generic) +import HlsPlugins (idePlugins) +import qualified Ide.Plugin.Config as Plugin +import Ide.Types +import Numeric.Natural (Natural) +import System.Console.GetOpt +import System.Directory +import System.FilePath +import System.IO.Error (tryIOError) + +configPath :: FilePath +configPath = "bench/config.yaml" + +configOpt :: OptDescr (Either String FilePath) +configOpt = Option [] ["config"] (ReqArg Right configPath) "config file" + +binaryName :: String +binaryName = "haskell-language-server" + +-- | Read the config without dependency +readConfigIO :: FilePath -> IO (Config BuildSystem) +readConfigIO = decodeFileThrow + +instance IsExample Example where getExampleName = exampleName +type instance RuleResult GetExample = Maybe Example +type instance RuleResult GetExamples = [Example] + +shakeOpts :: ShakeOptions +shakeOpts = + shakeOptions{shakeChange = ChangeModtimeAndDigestInput, shakeThreads = 0} + +main :: IO () +main = shakeArgsWith shakeOpts [configOpt] $ \configs wants -> pure $ Just $ do + let config = fromMaybe configPath $ listToMaybe configs + _configStatic <- createBuildSystem config + case wants of + [] -> want ["all"] + _ -> want wants + +hlsBuildRules :: MkBuildRules BuildSystem +hlsBuildRules = MkBuildRules findGhcForBuildSystem binaryName projectDepends buildHls + where + recordDepends path = + need . map (path ) =<< getDirectoryFiles path ["//*.hs"] + projectDepends = do + recordDepends "src" + recordDepends "exe" + recordDepends "plugins" + recordDepends "ghcide/session-loader" + recordDepends "ghcide/src" + recordDepends "hls-graph/src" + recordDepends "hls-plugin-api/src" + need =<< getDirectoryFiles "." ["*.cabal"] + +-------------------------------------------------------------------------------- +data Config buildSystem = Config + { experiments :: [Unescaped String], + configurations :: [ConfigurationDescriptor], + examples :: [Example], + samples :: Natural, + versions :: [GitCommit], + -- | Output folder ('foo' works, 'foo/bar' does not) + outputFolder :: String, + buildTool :: buildSystem, + profileInterval :: Maybe Double, + parallelism :: Natural + } + deriving (Generic, Show) + deriving anyclass (FromJSON) + +createBuildSystem :: FilePath -> Rules (Config BuildSystem) +createBuildSystem config = do + readConfig <- newCache $ \fp -> need [fp] >> liftIO (readConfigIO fp) + + _ <- addOracle $ \GetExperiments {} -> experiments <$> readConfig config + _ <- addOracle $ \GetVersions {} -> versions <$> readConfig config + _ <- versioned 1 $ addOracle $ \GetExamples{} -> examples <$> readConfig config + _ <- versioned 1 $ addOracle $ \(GetExample name) -> find (\e -> getExampleName e == name) . examples <$> readConfig config + _ <- addOracle $ \GetBuildSystem {} -> buildTool <$> readConfig config + _ <- addOracle $ \GetSamples{} -> samples <$> readConfig config + _ <- addOracle $ \GetConfigurations{} -> do + Config{configurations} <- readConfig config + return [ Configuration confName (encode $ disableAllPluginsBut (`elem` confPlugins)) + | ConfigurationDescriptor{..} <- configurations + ] + + configStatic <- liftIO $ readConfigIO config + let build = outputFolder configStatic + + buildRules build hlsBuildRules + benchRules build (MkBenchRules (askOracle $ GetSamples ()) benchHls warmupHls "haskell-language-server" (parallelism configStatic)) + csvRules build + svgRules build + heapProfileRules build + phonyRules "" binaryName NoProfiling build (examples configStatic) + + whenJust (profileInterval configStatic) $ \i -> do + phonyRules "profiled-" binaryName (CheapHeapProfiling i) build (examples configStatic) + + return configStatic + +disableAllPluginsBut :: (PluginId -> Bool) -> Plugin.Config +disableAllPluginsBut pred = def {Plugin.plugins = pluginsMap} where + pluginsMap = Map.fromList + [ (p, def { Plugin.plcGlobalOn = globalOn}) + | PluginDescriptor{pluginId = plugin@(PluginId p)} <- plugins + , let globalOn = + -- ghcide-core is required, nothing works without it + plugin == PluginId (pack "ghcide-core") + -- document symbols is required by the benchmark suite + || plugin == PluginId (pack "ghcide-hover-and-symbols") + || pred plugin + ] + IdePlugins plugins = idePlugins mempty + +newtype GetSamples = GetSamples () deriving newtype (Binary, Eq, Hashable, NFData, Show) +type instance RuleResult GetSamples = Natural + +-------------------------------------------------------------------------------- + +buildHls :: BuildSystem -> ProjectRoot -> OutputFolder -> Action () +buildHls Cabal root out = actionBracket + (do + projectLocalExists <- liftIO $ doesFileExist projectLocal + when projectLocalExists $ liftIO $ do + void $ tryIOError $ removeFile (projectLocal <.> "restore-after-benchmark") + renameFile projectLocal (projectLocal <.> "restore-after-benchmark") + liftIO $ writeFile projectLocal $ unlines + ["package haskell-language-server" + ," ghc-options: -eventlog -rtsopts" + ,"package ghcide" + ," flags: +ekg" + ] + return projectLocalExists) + (\projectLocalExists -> do + removeFile projectLocal + when projectLocalExists $ + renameFile (projectLocal <.> "restore-after-benchmark") projectLocal + ) $ \_ -> command_ [Cwd root] "cabal" + ["install" + ,"haskell-language-server:exe:haskell-language-server" + ,"--installdir=" ++ out + ,"--install-method=copy" + ,"--overwrite-policy=always" + ] + where + projectLocal = root "cabal.project.local" + +buildHls Stack root out = + command_ [Cwd root] "stack" + ["--local-bin-path=" <> out + ,"build" + ,"haskell-language-server:haskell-language-server" + ,"--copy-bins" + ,"--ghc-options=-rtsopts" + ,"--ghc-options=-eventlog" + ] + +benchHls + :: Natural -> BuildSystem -> [CmdOption] -> BenchProject Example -> Action () +benchHls samples buildSystem args BenchProject{..} = do + command_ ([StdinBS configuration] ++ args) "ghcide-bench" $ + [ "--timeout=300", + "--no-clean", + "-v", + "--samples=" <> show samples, + "--csv=" <> outcsv, + "--ghcide=" <> exePath, + "--select", + unescaped (unescapeExperiment experiment), + "--lsp-config" + ] ++ + exampleToOptions example exeExtraArgs ++ + [ "--stack" | Stack == buildSystem + ] + +warmupHls :: BuildSystem -> FilePath -> [CmdOption] -> Example -> Action () +warmupHls buildSystem exePath args example = do + command args "ghcide-bench" $ + [ "--no-clean", + "-v", + "--samples=1", + "--ghcide=" <> exePath, + "--select=hover" + ] ++ + exampleToOptions example [] ++ + [ "--stack" | Stack == buildSystem + ] + +-------------------------------------------------------------------------------- +data ConfigurationDescriptor = ConfigurationDescriptor + { confName :: String + , confPlugins :: [PluginId] + } + deriving Show + +instance FromJSON ConfigurationDescriptor where + parseJSON (String s) = pure $ ConfigurationDescriptor (unpack s) [PluginId s] + parseJSON o@Object{} = do + let keymap = o ^. _Object + matchKey = preview _String . toJSON + case toList keymap of + -- excuse the aeson 2.0 compatibility hack + [(matchKey -> Just name, Array values)] -> do + pluginIds <- traverse parseJSON values + pure $ ConfigurationDescriptor (unpack name) (map PluginId $ toList pluginIds) + other -> fail $ "Expected object with name and array of plugin ids: " <> show other + parseJSON _ = fail "Expected plugin id or object with name and array of plugin ids" diff --git a/ghcide/bench/README.md b/bench/README.md similarity index 65% rename from ghcide/bench/README.md rename to bench/README.md index 783ab70985..557fcc1420 100644 --- a/ghcide/bench/README.md +++ b/bench/README.md @@ -1,22 +1,17 @@ # Benchmarks -This folder contains two Haskell programs that work together to simplify the -performance analysis of ghcide: - -- `exe/Main.hs` - a standalone benchmark runner. Run with `stack run ghcide-bench` -- `hist/Main.hs` - a Shake script for running the benchmark suite over a set of commits. - - Run with `stack bench ghcide` or `cabal bench ghcide`, - - Requires a `ghcide-bench` binary in the PATH (usually provided by stack/cabal), - - Calls `cabal` (or `stack`, configurable) internally to build the project, - - Driven by the `bench/config.yaml` configuration file. - By default it compares HEAD with "master" +This folder contains a Shake script to simplify the performance analysis of HLS. +It drives the `ghcide-bench` benchmark suite over a set of commits and experiments. +To run it, use `cabal bench`. +To configure it, edit `bench/config.yaml`. +By default it compares HEAD with "origin/master" # Examples and experiments The benchmark suites runs a set of experiments (hover, completion, edit, etc.) over all the defined examples (currently Cabal and lsp-types). Examples are defined -in `ghcide/bench/config.yaml` whereas experiments are coded in `ghcide/bench/lib/Experiments.hs`. +in `bench/config.yaml` whereas experiments are coded in `ghcide-bench/src/Experiments.hs`. # Phony targets @@ -34,11 +29,14 @@ The Shake script supports a number of phony targets that allow running a subset * profiled-Cabal-3.0.0.0 : runs the Cabal example, with heap profiling +* all-binaries +: build all the HLS binaries for each of the versions under analysis + * etc `--help` lists all the phony targets. Invoke it with: - cabal bench ghcide --benchmark-options="--help" + cabal bench --benchmark-options="--help" ``` Targets: diff --git a/bench/config.yaml b/bench/config.yaml new file mode 100644 index 0000000000..19e014a485 --- /dev/null +++ b/bench/config.yaml @@ -0,0 +1,175 @@ +# The number of samples to run per experiment. +# At least 100 is recommended in order to observe space leaks +samples: 50 + +buildTool: cabal + +# Output folder for the experiments +outputFolder: bench-results + +# Heap profile interval in seconds (+RTS -i) +# Comment out to disable heap profiling +profileInterval: 1 + +# Number of concurrent benchmark and warmup runs +parallelism: 1 + +# Example project used to run the experiments +# Can either be a Hackage package (name,version) +# or a local project (path) with a valid `hie.yaml` file +examples: + # Medium-sized project without TH + - name: cabal + package: Cabal + version: 3.6.3.0 + modules: + - src/Distribution/Simple.hs + - src/Distribution/Types/Module.hs + extra-args: [] # extra HLS command line args + # Small-sized project with TH + - name: lsp-types + package: lsp-types + version: 1.5.0.0 + modules: + - src/Language/LSP/Types/WatchedFiles.hs + - src/Language/LSP/Types/CallHierarchy.hs + # Small but heavily multi-component example + # Disabled as it is far to slow. hie-bios >0.7.2 should help + # - name: HLS + # path: bench/example/HLS + # modules: + # - hls-plugin-api/src/Ide/Plugin/Config.hs + # - ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs + # - ghcide/bench/hist/Main.hs + # - ghcide/bench/lib/Experiments/Types.hs + # - ghcide/test/exe/Main.hs + # - exe/Plugins.hs + +# The set of experiments to execute +experiments: + - "edit" + - "hover" + - "hover after edit" + # - "hover after cradle edit" + - "getDefinition" + - "getDefinition after edit" + - "completions" + - "completions after edit" + - "code actions" + - "code actions after edit" + - "code actions after cradle edit" + - "documentSymbols after edit" + - "hole fit suggestions" + +# An ordered list of versions to analyze +versions: +# A version can be defined briefly: +# - +# - +# - + +# Or in extended form, where all the fields are optional: +# - : +# git: +# include: true # whether to include in comparison graphs +# parent: # version to compare with in .diff graphs + + +# - 1.8.0.0 +# - upstream: origin/master +# - HEAD~1 +- HEAD + +# A list of plugin configurations to analyze +configurations: +# A configuration contains one or more plugins: +# - ConfigurationName: +# - plugin1 +# - plugin2 +# +# There is short-hand notation for defining singleton configurations. +# Simply give the plugin name top level: +# - plugin1 +# +# Some plugins are implicitly included since they are required by the benchmark driver: +# The implicitly included plugins are: +# - ghcide-core +# - ghcide-hover-and-symbols +- None: [] +- Core: + - callHierarchy + - codeRange + - eval + - ghcide-code-actions-bindings + - ghcide-code-actions-fill-holes + - ghcide-code-actions-imports-exports + - ghcide-code-actions-type-signatures + - ghcide-completions + - ghcide-type-lenses + - pragmas +- Ghcide: + - ghcide-code-actions-bindings + - ghcide-code-actions-fill-holes + - ghcide-code-actions-imports-exports + - ghcide-code-actions-type-signatures + - ghcide-completions + - ghcide-type-lenses +- All: + - alternateNumberFormat + - callHierarchy + - changeTypeSignature + - class + - codeRange + - eval + - explicitFixity + - floskell + - fourmolu + - gadt + - ghcide-code-actions-bindings + - ghcide-code-actions-fill-holes + - ghcide-code-actions-imports-exports + - ghcide-code-actions-type-signatures + - ghcide-completions + - ghcide-type-lenses + - hlint + - importLens + - moduleName + - ormolu + - pragmas + - qualifyImportedNames + - refineImports + - rename + - stylish-haskell +- alternateNumberFormat +# - brittany +- callHierarchy +- changeTypeSignature +- class +- codeRange +- eval +- explicitFixity +# - floskell +# - fourmolu +- gadt +- ghcide-code-actions-bindings +- ghcide-code-actions-fill-holes +- ghcide-code-actions-imports-exports +- ghcide-code-actions-type-signatures +- ghcide-completions +# - ghcide-core # implicitly included in all configurations +# - ghcide-hover-and-symbols # implicitly included in all configurations +- ghcide-type-lenses +- haddockComments +- hlint +- importLens +- moduleName +# - ormolu +- pragmas +- qualifyImportedNames +- refineImports +- rename +- retrie +- splice +- stan +# - stylish-haskell +- tactics diff --git a/cabal.project b/cabal.project index 8bfcb20265..047c2efcc2 100644 --- a/cabal.project +++ b/cabal.project @@ -4,6 +4,7 @@ packages: ./shake-bench ./hls-graph ./ghcide + ./ghcide-bench ./hls-plugin-api ./hls-test-utils ./plugins/hls-tactics-plugin @@ -64,6 +65,14 @@ source-repository-package tag: 7a0af7a8fd38045fd15fb13445bdcc7085325460 -- https://github.com/tibbe/ekg-json/pull/12 +-- Needed for ghcide-bench until a new release of lsp-test is out +source-repository-package + type:git + location: https://github.com/haskell/lsp + subdir: lsp-test + tag: c95eb06c70c35f1e13c37ed11a7d9e5b36bfa2e8 + -- https://github.com/haskell/lsp/pull/450 + allow-newer: -- ghc-9.2 ---------- diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index a3fd5660b3..4d7aae78a5 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -208,11 +208,11 @@ If you are touching performance sensitive code, take the time to run a different benchmark between HEAD and master using the benchHist script. This assumes that "master" points to the upstream master. -Run the benchmarks with `cabal bench ghcide`. +Run the benchmarks with `cabal bench`. -It should take around 25 minutes and the results will be stored in the `ghcide/bench-results` folder. To interpret the results, see the comments in the `ghcide/bench/hist/Main.hs` module. +It should take around 25 minutes and the results will be stored in the `bench-results` folder. To interpret the results, see the comments in the `bench/Main.hs` module. -More details in [bench/README](../../ghcide/bench/README.md) +More details in [bench/README](../../bench/README.md) ### Tracing diff --git a/exe/Main.hs b/exe/Main.hs index 083f76a1b4..ca8c885f43 100644 --- a/exe/Main.hs +++ b/exe/Main.hs @@ -20,6 +20,7 @@ import Development.IDE.Types.Logger (Doc, payload, renderStrict, withDefaultRecorder) import qualified Development.IDE.Types.Logger as Logger +import qualified HlsPlugins as Plugins import Ide.Arguments (Arguments (..), GhcideArguments (..), getArguments) @@ -31,7 +32,6 @@ import Ide.Types (PluginDescriptor (pluginNotificat mkPluginNotificationHandler) import Language.LSP.Server as LSP import Language.LSP.Types as LSP -import qualified Plugins #if MIN_VERSION_prettyprinter(1,7,0) import Prettyprinter (Pretty (pretty), vsep) #else @@ -52,7 +52,7 @@ main = do -- plugin cli commands use stderr logger for now unless we change the args -- parser to get logging arguments first or do more complicated things pluginCliRecorder <- cmapWithPrio pretty <$> makeDefaultStderrRecorder Nothing Info - args <- getArguments "haskell-language-server" (Plugins.idePlugins (cmapWithPrio LogPlugins pluginCliRecorder) False) + args <- getArguments "haskell-language-server" (Plugins.idePlugins (cmapWithPrio LogPlugins pluginCliRecorder)) (lspLogRecorder, cb1) <- Logger.withBacklog Logger.lspClientLogRecorder (lspMessageRecorder, cb2) <- Logger.withBacklog Logger.lspClientMessageRecorder @@ -64,12 +64,12 @@ main = do liftIO $ (cb1 <> cb2) env } - let (argsTesting, minPriority, logFilePath, includeExamplePlugins) = + let (argsTesting, minPriority, logFilePath) = case args of - Ghcide GhcideArguments{ argsTesting, argsDebugOn, argsLogFile, argsExamplePlugin } -> + Ghcide GhcideArguments{ argsTesting, argsDebugOn, argsLogFile} -> let minPriority = if argsDebugOn || argsTesting then Debug else Info - in (argsTesting, minPriority, argsLogFile, argsExamplePlugin) - _ -> (False, Info, Nothing, False) + in (argsTesting, minPriority, argsLogFile) + _ -> (False, Info, Nothing) withDefaultRecorder logFilePath Nothing minPriority $ \textWithPriorityRecorder -> do let @@ -87,7 +87,7 @@ main = do -- ability of lsp-test to detect a stuck server in tests and benchmarks & if argsTesting then cfilter (not . heapStats . snd . payload) else id ] - plugins = (Plugins.idePlugins (cmapWithPrio LogPlugins recorder) includeExamplePlugins) + plugins = (Plugins.idePlugins (cmapWithPrio LogPlugins recorder)) defaultMain (cmapWithPrio LogIdeMain recorder) diff --git a/ghcide-bench/LICENSE b/ghcide-bench/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/ghcide-bench/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ghcide-bench/README.md b/ghcide-bench/README.md new file mode 100644 index 0000000000..f815635157 --- /dev/null +++ b/ghcide-bench/README.md @@ -0,0 +1,61 @@ +A benchmark suite for measuring various performance-related metrics on ghcide and HLS. + +## Usage + +Run with `cabal ghcide bench`, point it to a `haskell-language-server` or `ghcide` binary, specify: +- the experiment to run, from the ones defined in `src/Experiments.hs`, +- the example codebase (either a local folder or a Hackage package), +- one or more module paths to run the experiment on, +- the number of samples, +- any extra command line options to pass to the binary, + +``` +Usage: ghcide-bench [(-v|--verbose) | (-q|--quiet)] [--shake-profiling PATH] + [--ot-profiling DIR] [--csv PATH] [--stack] + [--ghcide-options ARG] [-s|--select ARG] [--samples NAT] + [--ghcide PATH] [--timeout ARG] + [[--example-package-name ARG] + [--example-package-version ARG] + [(--example-module PATH)] | + --example-path ARG (--example-module PATH)] [--lsp-config] + [--no-clean] + +Available options: + --ot-profiling DIR Enable OpenTelemetry and write eventlog for each + benchmark in DIR + --stack Use stack (by default cabal is used) + --ghcide-options ARG additional options for ghcide + -s,--select ARG select which benchmarks to run + --samples NAT override sampling count + --ghcide PATH path to ghcide + --timeout ARG timeout for waiting for a ghcide response + --lsp-config Read an LSP config payload from standard input + -h,--help Show this help text +``` + +## Experiments + +Experiments are LSP sessions defined using the `lsp-test` DSL that run on one or +more modules. + +Currently the following experiments are defined: +- *edit*: makes an edit and waits for re-typechecking +- *hover*: asks for hover on an identifier +- *getDefinition*: asks for the definitions of an identifier +- *documentsymbols* +- *completions*: asks for completions on an identifier position +- *code actions*: makes an edit that breaks typechecking and asks for code actions +- *hole fit suggestions*: measures the performance of hole fits +- *X after edit*: combines the *edit* and X experiments +- *X after cradle edit*: combines the X experiments with an edit to the `hie.yaml` file + +One can define additional experiments easily, for e.g. formatting, code lenses, renames, etc. +Experiments are defined in the `src/Experiments.hs` module. + +### Positions +`ghcide-bench` will analyze the modules prior to running the experiments, +and try to identify the following designated source locations in the module: + +- *stringLiteralP*: a location that can be mutated without generating a diagnostic, +- *identifierP*: a location with an identifier that is not locally defined in the module. +- *docP*: a location containing a comment diff --git a/ghcide/bench/exe/Main.hs b/ghcide-bench/exe/Main.hs similarity index 100% rename from ghcide/bench/exe/Main.hs rename to ghcide-bench/exe/Main.hs diff --git a/ghcide-bench/ghcide-bench.cabal b/ghcide-bench/ghcide-bench.cabal new file mode 100644 index 0000000000..89a9fc1080 --- /dev/null +++ b/ghcide-bench/ghcide-bench.cabal @@ -0,0 +1,137 @@ +cabal-version: 3.0 +build-type: Simple +category: Development +name: ghcide-bench +version: 0.1 +license: Apache-2.0 +license-file: LICENSE +author: The Haskell IDE team +maintainer: pepeiborra@gmail.com +copyright: The Haskell IDE team +synopsis: An LSP client for running performance experiments on HLS +description: An LSP client for running performance experiments on HLS +homepage: https://github.com/haskell/haskell-language-server/tree/master/ghcide#readme +bug-reports: https://github.com/haskell/haskell-language-server/issues +tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 + +executable ghcide-bench + default-language: Haskell2010 + build-depends: + aeson, + base, + bytestring, + containers, + data-default, + directory, + extra, + filepath, + hls-plugin-api, + lens, + ghcide-bench, + lsp-test, + lsp-types, + optparse-applicative, + process, + safe-exceptions, + hls-graph, + shake, + tasty-hunit >= 0.10, + text + hs-source-dirs: exe + ghc-options: -threaded -Wall -Wno-name-shadowing -rtsopts + main-is: Main.hs + default-extensions: + BangPatterns + DeriveFunctor + DeriveGeneric + FlexibleContexts + GeneralizedNewtypeDeriving + LambdaCase + NamedFieldPuns + OverloadedStrings + RecordWildCards + ScopedTypeVariables + StandaloneDeriving + TupleSections + TypeApplications + ViewPatterns + +library + default-language: Haskell2010 + hs-source-dirs: src + ghc-options: -Wall -Wno-name-shadowing + exposed-modules: + Experiments.Types + Experiments + other-modules: + Development.IDE.Test.Diagnostic + build-depends: + aeson, + async, + base == 4.*, + binary, + bytestring, + deepseq, + directory, + extra, + filepath, + ghcide, + hashable, + lens, + lsp-test, + lsp-types, + optparse-applicative, + parser-combinators, + process, + safe-exceptions, + shake, + text, + default-extensions: + BangPatterns + DeriveFunctor + DeriveGeneric + FlexibleContexts + GeneralizedNewtypeDeriving + LambdaCase + NamedFieldPuns + RecordWildCards + ScopedTypeVariables + StandaloneDeriving + TupleSections + TypeApplications + ViewPatterns + +test-suite test + type: exitcode-stdio-1.0 + default-language: Haskell2010 + build-tool-depends: + ghcide:ghcide, + implicit-hie:gen-hie + main-is: Main.hs + hs-source-dirs: test + ghc-options: -Wunused-packages + ghc-options: -threaded -Wall + build-depends: + base, + extra, + ghcide-bench, + lsp-test ^>= 0.14, + tasty, + tasty-hunit >= 0.10, + tasty-rerun, + default-extensions: + BangPatterns + DeriveFunctor + DeriveGeneric + FlexibleContexts + GeneralizedNewtypeDeriving + LambdaCase + NamedFieldPuns + OverloadedStrings + RecordWildCards + ScopedTypeVariables + StandaloneDeriving + TupleSections + TypeApplications + ViewPatterns + diff --git a/ghcide-bench/src/Development/IDE/Test/Diagnostic.hs b/ghcide-bench/src/Development/IDE/Test/Diagnostic.hs new file mode 100644 index 0000000000..a1ea88ec28 --- /dev/null +++ b/ghcide-bench/src/Development/IDE/Test/Diagnostic.hs @@ -0,0 +1,48 @@ +-- Duplicate of ghcide/test/Development/IDE/Test/Diagnostic.hs +module Development.IDE.Test.Diagnostic where + +import Control.Lens ((^.)) +import qualified Data.Text as T +import GHC.Stack (HasCallStack) +import Language.LSP.Types +import Language.LSP.Types.Lens as Lsp + +-- | (0-based line number, 0-based column number) +type Cursor = (UInt, UInt) + +cursorPosition :: Cursor -> Position +cursorPosition (line, col) = Position line col + +type ErrorMsg = String + +requireDiagnostic + :: (Foldable f, Show (f Diagnostic), HasCallStack) + => f Diagnostic + -> (DiagnosticSeverity, Cursor, T.Text, Maybe DiagnosticTag) + -> Maybe ErrorMsg +requireDiagnostic actuals expected@(severity, cursor, expectedMsg, expectedTag) + | any match actuals = Nothing + | otherwise = Just $ + "Could not find " <> show expected <> + " in " <> show actuals + where + match :: Diagnostic -> Bool + match d = + Just severity == _severity d + && cursorPosition cursor == d ^. range . start + && standardizeQuotes (T.toLower expectedMsg) `T.isInfixOf` + standardizeQuotes (T.toLower $ d ^. message) + && hasTag expectedTag (d ^. tags) + + hasTag :: Maybe DiagnosticTag -> Maybe (List DiagnosticTag) -> Bool + hasTag Nothing _ = True + hasTag (Just _) Nothing = False + hasTag (Just actualTag) (Just (List tags)) = actualTag `elem` tags + +standardizeQuotes :: T.Text -> T.Text +standardizeQuotes msg = let + repl '‘' = '\'' + repl '’' = '\'' + repl '`' = '\'' + repl c = c + in T.map repl msg diff --git a/ghcide/bench/lib/Experiments.hs b/ghcide-bench/src/Experiments.hs similarity index 85% rename from ghcide/bench/lib/Experiments.hs rename to ghcide-bench/src/Experiments.hs index 081df51984..1d8d6f6c5b 100644 --- a/ghcide/bench/lib/Experiments.hs +++ b/ghcide-bench/src/Experiments.hs @@ -3,6 +3,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE ImplicitParams #-} {-# LANGUAGE ImpredicativeTypes #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PolyKinds #-} {-# OPTIONS_GHC -Wno-deprecations -Wno-unticked-promoted-constructors #-} @@ -23,24 +24,24 @@ module Experiments , exampleToOptions ) where import Control.Applicative.Combinators (skipManyTill) +import Control.Concurrent.Async (withAsync) import Control.Exception.Safe (IOException, handleAny, try) -import Control.Monad.Extra (allM, forM, forM_, unless, - void, whenJust, (&&^)) +import Control.Monad.Extra (allM, forM, forM_, forever, + unless, void, when, whenJust, + (&&^)) import Control.Monad.Fail (MonadFail) import Control.Monad.IO.Class -import Data.Aeson (Value (Null), toJSON) +import Data.Aeson (Value (Null), + eitherDecodeStrict', toJSON) +import qualified Data.Aeson as A +import qualified Data.ByteString as BS import Data.Either (fromRight) import Data.List import Data.Maybe +import Data.Text (Text) import qualified Data.Text as T import Data.Version import Development.IDE.Plugin.Test -import Development.IDE.Test (getBuildEdgesCount, - getBuildKeysBuilt, - getBuildKeysChanged, - getBuildKeysVisited, - getRebuildsCount, - getStoredKeys) import Development.IDE.Test.Diagnostic import Development.Shake (CmdOption (Cwd, FileStdout), cmd_) @@ -56,9 +57,11 @@ import Options.Applicative import System.Directory import System.Environment.Blank (getEnv) import System.FilePath ((<.>), ()) +import System.IO import System.Process import System.Time.Extra import Text.ParserCombinators.ReadP (readP_to_S) +import Text.Printf charEdit :: Position -> TextDocumentContentChangeEvent charEdit p = @@ -69,8 +72,11 @@ charEdit p = } data DocumentPositions = DocumentPositions { + -- | A position that can be used to generate non null goto-def and completion responses identifierP :: Maybe Position, + -- | A position that can be modified without generating a new diagnostic stringLiteralP :: !Position, + -- | The document containing the above positions doc :: !TextDocumentIdentifier } @@ -82,7 +88,7 @@ allWithIdentifierPos f docs = case applicableDocs of where applicableDocs = filter (isJust . identifierP) docs -experiments :: [Bench] +experiments :: HasConfig => [Bench] experiments = [ --------------------------------------------------------------------------------------- bench "hover" $ allWithIdentifierPos $ \DocumentPositions{..} -> @@ -94,6 +100,7 @@ experiments = -- wait for a fresh build start waitForProgressStart -- wait for the build to be finished + output "edit: waitForProgressDone" waitForProgressDone return True, --------------------------------------------------------------------------------------- @@ -267,6 +274,7 @@ configP = <$> (Left <$> pathP) <*> some moduleOption <*> pure []) + <*> switch (long "lsp-config" <> help "Read an LSP config payload from standard input") where moduleOption = strOption (long "example-module" <> metavar "PATH") @@ -324,9 +332,30 @@ runBenchmarksFun dir allBenchmarks = do whenJust (otMemoryProfiling ?config) $ \eventlogDir -> createDirectoryIfMissing True eventlogDir - results <- forM benchmarks $ \b@Bench{name} -> do - let run = runSessionWithConfig conf (cmd name dir) lspTestCaps dir - (b,) <$> runBench run b + lspConfig <- if Experiments.Types.lspConfig ?config + then either error Just . eitherDecodeStrict' <$> BS.getContents + else return Nothing + + let conf = defaultConfig + { logStdErr = verbose ?config, + logMessages = verbose ?config, + logColor = False, + Language.LSP.Test.lspConfig = lspConfig, + messageTimeout = timeoutLsp ?config + } + results <- forM benchmarks $ \b@Bench{name} -> do + let p = (proc (ghcide ?config) (allArgs name dir)) + { std_in = CreatePipe, std_out = CreatePipe, std_err = CreatePipe } + run sess = withCreateProcess p $ \(Just inH) (Just outH) (Just errH) pH -> do + -- Need to continuously consume to stderr else it gets blocked + -- Can't pass NoStream either to std_err + hSetBuffering errH NoBuffering + hSetBinaryMode errH True + let errSinkThread = + forever $ hGetLine errH >>= when (verbose ?config). putStrLn + withAsync errSinkThread $ \_ -> do + runSessionWithHandles' (Just pH) inH outH conf lspTestCaps dir sess + (b,) <$> runBench run b -- output raw data as CSV let headers = @@ -335,31 +364,31 @@ runBenchmarksFun dir allBenchmarks = do , "samples" , "startup" , "setup" - , "userTime" - , "delayedTime" - , "firstBuildTime" - , "averageTimePerResponse" - , "totalTime" - , "buildRulesBuilt" - , "buildRulesChanged" - , "buildRulesVisited" - , "buildRulesTotal" - , "buildEdges" + , "userT" + , "delayedT" + , "1stBuildT" + , "avgPerRespT" + , "totalT" + , "rulesBuilt" + , "rulesChanged" + , "rulesVisited" + , "rulesTotal" + , "ruleEdges" , "ghcRebuilds" ] rows = [ [ name, show success, show samples, - show startup, - show runSetup', - show userWaits, - show delayedWork, - show $ firstResponse+firstResponseDelayed, + showMs startup, + showMs runSetup', + showMs userWaits, + showMs delayedWork, + showMs $ firstResponse+firstResponseDelayed, -- Exclude first response as it has a lot of setup time included -- Assume that number of requests = number of modules * number of samples - show ((userWaits - firstResponse)/((fromIntegral samples - 1)*modules)), - show runExperiment, + showMs ((userWaits - firstResponse)/((fromIntegral samples - 1)*modules)), + showMs runExperiment, show rulesBuilt, show rulesChanged, show rulesVisited, @@ -402,36 +431,32 @@ runBenchmarksFun dir allBenchmarks = do outputRow $ (map . map) (const '-') paddedHeaders forM_ rowsHuman $ \row -> outputRow $ zipWith pad pads row where - ghcideCmd dir = - [ ghcide ?config, - "--lsp", + ghcideArgs dir = + [ "--lsp", "--test", "--cwd", - dir, - "+RTS" + dir ] - cmd name dir = - unwords $ - ghcideCmd dir - ++ case otMemoryProfiling ?config of - Just dir -> ["-l", "-ol" ++ (dir map (\c -> if c == ' ' then '-' else c) name <.> "eventlog")] - Nothing -> [] - ++ [ "-RTS" ] + allArgs name dir = + ghcideArgs dir + ++ concat + [ [ "+RTS" + , "-l" + , "-ol" ++ (dir map (\c -> if c == ' ' then '-' else c) name <.> "eventlog") + , "-RTS" + ] + | Just dir <- [otMemoryProfiling ?config] + ] ++ ghcideOptions ?config ++ concat [ ["--shake-profiling", path] | Just path <- [shakeProfiling ?config] ] - ++ ["--verbose" | verbose ?config] ++ ["--ot-memory-profiling" | Just _ <- [otMemoryProfiling ?config]] lspTestCaps = fullCaps {_window = Just $ WindowClientCapabilities (Just True) Nothing Nothing } - conf = - defaultConfig - { logStdErr = verbose ?config, - logMessages = verbose ?config, - logColor = False, - messageTimeout = timeoutLsp ?config - } + +showMs :: Seconds -> String +showMs = printf "%.2f" data BenchRun = BenchRun { startup :: !Seconds, @@ -483,7 +508,7 @@ waitForBuildQueue = do _ -> return 0 runBench :: - (?config :: Config) => + HasConfig => (Session BenchRun -> IO BenchRun) -> Bench -> IO BenchRun @@ -688,3 +713,42 @@ searchSymbol doc@TextDocumentIdentifier{_uri} fileContents pos = do checkCompletions pos = not . null <$> getCompletions doc pos + +getBuildKeysBuilt :: Session (Either ResponseError [T.Text]) +getBuildKeysBuilt = tryCallTestPlugin GetBuildKeysBuilt + +getBuildKeysVisited :: Session (Either ResponseError [T.Text]) +getBuildKeysVisited = tryCallTestPlugin GetBuildKeysVisited + +getBuildKeysChanged :: Session (Either ResponseError [T.Text]) +getBuildKeysChanged = tryCallTestPlugin GetBuildKeysChanged + +getBuildEdgesCount :: Session (Either ResponseError Int) +getBuildEdgesCount = tryCallTestPlugin GetBuildEdgesCount + +getRebuildsCount :: Session (Either ResponseError Int) +getRebuildsCount = tryCallTestPlugin GetRebuildsCount + +-- Copy&paste from ghcide/test/Development.IDE.Test +getStoredKeys :: Session [Text] +getStoredKeys = callTestPlugin GetStoredKeys + +-- Copy&paste from ghcide/test/Development.IDE.Test +tryCallTestPlugin :: (A.FromJSON b) => TestRequest -> Session (Either ResponseError b) +tryCallTestPlugin cmd = do + let cm = SCustomMethod "test" + waitId <- sendRequest cm (A.toJSON cmd) + ResponseMessage{_result} <- skipManyTill anyMessage $ responseForId cm waitId + return $ case _result of + Left e -> Left e + Right json -> case A.fromJSON json of + A.Success a -> Right a + A.Error e -> error e + +-- Copy&paste from ghcide/test/Development.IDE.Test +callTestPlugin :: (A.FromJSON b) => TestRequest -> Session b +callTestPlugin cmd = do + res <- tryCallTestPlugin cmd + case res of + Left (ResponseError t err _) -> error $ show t <> ": " <> T.unpack err + Right a -> pure a diff --git a/ghcide/bench/lib/Experiments/Types.hs b/ghcide-bench/src/Experiments/Types.hs similarity index 82% rename from ghcide/bench/lib/Experiments/Types.hs rename to ghcide-bench/src/Experiments/Types.hs index 633052efd6..303abaf8cd 100644 --- a/ghcide/bench/lib/Experiments/Types.hs +++ b/ghcide-bench/src/Experiments/Types.hs @@ -3,10 +3,12 @@ {-# LANGUAGE OverloadedStrings #-} module Experiments.Types (module Experiments.Types ) where +import Control.DeepSeq import Data.Aeson -import Data.Maybe (fromMaybe) +import Data.Binary (Binary) +import Data.Hashable (Hashable) +import Data.Maybe (fromMaybe) import Data.Version -import Development.Shake.Classes import GHC.Generics import Numeric.Natural @@ -27,7 +29,8 @@ data Config = Config repetitions :: Maybe Natural, ghcide :: FilePath, timeoutLsp :: Int, - example :: Example + example :: Example, + lspConfig :: Bool } deriving (Eq, Show) @@ -64,11 +67,13 @@ exampleToOptions :: Example -> [String] -> [String] exampleToOptions Example{exampleDetails = Right ExamplePackage{..}, ..} extraArgs = ["--example-package-name", packageName ,"--example-package-version", showVersion packageVersion - ,"--ghcide-options", unwords $ exampleExtraArgs ++ extraArgs ] ++ - ["--example-module=" <> m | m <- exampleModules] + ["--example-module=" <> m | m <- exampleModules + ] ++ + ["--ghcide-options=" <> o | o <- exampleExtraArgs ++ extraArgs] exampleToOptions Example{exampleDetails = Left examplePath, ..} extraArgs = ["--example-path", examplePath - ,"--ghcide-options", unwords $ exampleExtraArgs ++ extraArgs ] ++ - ["--example-module=" <> m | m <- exampleModules] + ["--example-module=" <> m | m <- exampleModules + ] ++ + ["--ghcide-options=" <> o | o <- exampleExtraArgs ++ extraArgs] diff --git a/ghcide-bench/test/Main.hs b/ghcide-bench/test/Main.hs new file mode 100644 index 0000000000..beb5066ddb --- /dev/null +++ b/ghcide-bench/test/Main.hs @@ -0,0 +1,48 @@ +-- Copyright (c) 2019 The DAML Authors. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE ImplicitParams #-} +{-# LANGUAGE PolyKinds #-} +{-# OPTIONS_GHC -Wno-deprecations -Wno-unticked-promoted-constructors #-} + +module Main (main) where + +import Data.List.Extra +import qualified Experiments as Bench +import Language.LSP.Test +import Test.Tasty +import Test.Tasty.HUnit +import Test.Tasty.Ingredients.Rerun (defaultMainWithRerun) + +main :: IO () +main = defaultMainWithRerun benchmarkTests + +benchmarkTests :: TestTree +benchmarkTests = + let ?config = Bench.defConfig + { Bench.verbosity = Bench.Quiet + , Bench.repetitions = Just 3 + , Bench.buildTool = Bench.Cabal + } in + withResource Bench.setup Bench.cleanUp $ \getResource -> testGroup "benchmark experiments" + [ testCase (Bench.name e) $ do + Bench.SetupResult{Bench.benchDir} <- getResource + res <- Bench.runBench (runInDir benchDir) e + assertBool "did not successfully complete 5 repetitions" $ Bench.success res + | e <- Bench.experiments + , Bench.name e /= "edit" -- the edit experiment does not ever fail + , Bench.name e /= "hole fit suggestions" -- is too slow! + -- the cradle experiments are way too slow + , not ("cradle" `isInfixOf` Bench.name e) + ] + +runInDir :: FilePath -> Session a -> IO a +runInDir dir = runSessionWithConfig defaultConfig cmd fullCaps dir + where + -- TODO use HLS instead of ghcide + cmd = "ghcide --lsp --test --verbose -j2 --cwd " <> dir diff --git a/ghcide/.gitignore b/ghcide/.gitignore index 3544e898b0..8370c00874 100644 --- a/ghcide/.gitignore +++ b/ghcide/.gitignore @@ -7,11 +7,6 @@ cabal.project.local /.tasty-rerun-log .vscode /.hlint-* -bench/example/* -# don't ignore the example file, we need it! -!bench/example/HLS -bench-results/ -bench-temp/ .shake/ ghcide ghcide-bench diff --git a/ghcide/bench-results/.artifactignore b/ghcide/bench-results/.artifactignore deleted file mode 100644 index 326f663a2b..0000000000 --- a/ghcide/bench-results/.artifactignore +++ /dev/null @@ -1,4 +0,0 @@ -ghcide -ghcide-bench -ghcide-preprocessor -*.benchmark-gcStats diff --git a/ghcide/bench/config.yaml b/ghcide/bench/config.yaml deleted file mode 100644 index a744f56e17..0000000000 --- a/ghcide/bench/config.yaml +++ /dev/null @@ -1,116 +0,0 @@ -# The number of samples to run per experiment. -# At least 100 is recommended in order to observe space leaks -samples: 50 - -buildTool: cabal - -# Output folder for the experiments -outputFolder: bench-results - -# Example project used to run the experiments -# Can either be a Hackage package (name,version) -# or a local project (path) with a valid `hie.yaml` file -examples: - # Medium-sized project without TH - - name: cabal - package: Cabal - version: 3.6.3.0 - modules: - - src/Distribution/Simple.hs - - src/Distribution/Types/Module.hs - extra-args: [] # extra ghcide command line args - - name: cabal-1module - package: Cabal - version: 3.6.3.0 - modules: - - src/Distribution/Simple.hs - - name: cabal-conservative - package: Cabal - version: 3.6.3.0 - modules: - - src/Distribution/Simple.hs - - src/Distribution/Types/Module.hs - extra-args: # extra ghcide command line args - - --conservative-change-tracking - # Small-sized project with TH - - name: lsp-types - package: lsp-types - version: 1.5.0.0 - modules: - - src/Language/LSP/Types/WatchedFiles.hs - - src/Language/LSP/Types/CallHierarchy.hs - - name: lsp-types-conservative - package: lsp-types - version: 1.5.0.0 - modules: - - src/Language/LSP/Types/WatchedFiles.hs - - src/Language/LSP/Types/CallHierarchy.hs - extra-args: - - --conservative-change-tracking - # Small-sized project with TH - # Small but heavily multi-component example - # Disabled as it is far to slow. hie-bios >0.7.2 should help - # - name: HLS - # path: bench/example/HLS - # modules: - # - hls-plugin-api/src/Ide/Plugin/Config.hs - # - ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs - # - ghcide/bench/hist/Main.hs - # - ghcide/bench/lib/Experiments/Types.hs - # - ghcide/test/exe/Main.hs - # - exe/Plugins.hs - -# The set of experiments to execute -experiments: - - "edit" - - "hover" - - "hover after edit" - # - "hover after cradle edit" - - "getDefinition" - - "getDefinition after edit" - - "completions" - - "completions after edit" - - "code actions" - - "code actions after edit" - - "code actions after cradle edit" - - "documentSymbols after edit" - - "hole fit suggestions" - -# An ordered list of versions to analyze -versions: -# A version can be defined briefly: -# - -# - -# - - -# Or in extended form, where all the fields are optional: -# - : -# git: -# include: true # whether to include in comparison graphs -# parent: # version to compare with in .diff graphs - - -# - ghcide-v0.0.5 -# - ghcide-v0.0.6 -# - ghcide-v0.1.0 -# - ghcide-v0.2.0 -# - ghcide-v0.3.0 -# - ghcide-v0.4.0 -# - ghcide-v0.5.0 -# - ghcide-v0.6.0 -# - ghcide-v0.7.0 -# - ghcide-v0.7.1 -# - ghcide-v0.7.2 -# - ghcide-v0.7.3 -# - ghcide-v0.7.4 -# - ghcide-v0.7.5 -# - 1.0.0 -# - ghcide-v1.1.0 -# - ghcide-v1.2.0 -# - ghcide-v1.3.0 -- upstream: origin/master -- HEAD - -# Heap profile interval in seconds (+RTS -i) -# Comment out to disable heap profiling -profileInterval: 1 diff --git a/ghcide/bench/hist/Main.hs b/ghcide/bench/hist/Main.hs deleted file mode 100644 index f09e247268..0000000000 --- a/ghcide/bench/hist/Main.hs +++ /dev/null @@ -1,192 +0,0 @@ -{- Bench history - - A Shake script to analyze the performance of ghcide over the git history of the project - - Driven by a config file `bench/config.yaml` containing the list of Git references to analyze. - - Builds each one of them and executes a set of experiments using the ghcide-bench suite. - - The results of the benchmarks and the analysis are recorded in the file - system with the following structure: - - bench-results - ├── - │  ├── ghc.path - path to ghc used to build the binary - │  ├── ghcide - binary for this version - ├─ - │ ├── results.csv - aggregated results for all the versions - │ └── - │   ├── .gcStats.log - RTS -s output - │   ├── .csv - stats for the experiment - │   ├── .svg - Graph of bytes over elapsed time - │   ├── .diff.svg - idem, including the previous version - │   ├── .log - ghcide-bench output - │   └── results.csv - results of all the experiments for the example - ├── results.csv - aggregated results of all the experiments and versions - └── .svg - graph of bytes over elapsed time, for all the included versions - - For diff graphs, the "previous version" is the preceding entry in the list of versions - in the config file. A possible improvement is to obtain this info via `git rev-list`. - - To execute the script: - - > cabal/stack bench - - To build a specific analysis, enumerate the desired file artifacts - - > stack bench --ba "bench-results/HEAD/results.csv bench-results/HEAD/edit.diff.svg" - > cabal bench --benchmark-options "bench-results/HEAD/results.csv bench-results/HEAD/edit.diff.svg" - - -} -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE TypeFamilies #-} -{-# OPTIONS -Wno-orphans #-} - -import Control.Monad.Extra -import Data.Foldable (find) -import Data.Maybe -import Data.Yaml (FromJSON (..), decodeFileThrow) -import Development.Benchmark.Rules -import Development.Shake -import Development.Shake.Classes -import Experiments.Types (Example (exampleName), - exampleToOptions) -import GHC.Generics (Generic) -import Numeric.Natural (Natural) -import System.Console.GetOpt -import System.FilePath - -configPath :: FilePath -configPath = "bench/config.yaml" - -configOpt :: OptDescr (Either String FilePath) -configOpt = Option [] ["config"] (ReqArg Right configPath) "config file" - --- | Read the config without dependency -readConfigIO :: FilePath -> IO (Config BuildSystem) -readConfigIO = decodeFileThrow - -instance IsExample Example where getExampleName = exampleName -type instance RuleResult GetExample = Maybe Example -type instance RuleResult GetExamples = [Example] - -shakeOpts :: ShakeOptions -shakeOpts = - shakeOptions{shakeChange = ChangeModtimeAndDigestInput, shakeThreads = 0} - -main :: IO () -main = shakeArgsWith shakeOpts [configOpt] $ \configs wants -> pure $ Just $ do - let config = fromMaybe configPath $ listToMaybe configs - _configStatic <- createBuildSystem config - case wants of - [] -> want ["all"] - _ -> want wants - -ghcideBuildRules :: MkBuildRules BuildSystem -ghcideBuildRules = MkBuildRules findGhcForBuildSystem "ghcide" projectDepends buildGhcide - where - projectDepends = do - need . map ("../hls-graph/src" ) =<< getDirectoryFiles "../hls-graph/src" ["//*.hs"] - need . map ("../hls-plugin-api/src" ) =<< getDirectoryFiles "../hls-plugin-api/src" ["//*.hs"] - need . map ("src" ) =<< getDirectoryFiles "src" ["//*.hs"] - need . map ("session-loader" ) =<< getDirectoryFiles "session-loader" ["//*.hs"] - need =<< getDirectoryFiles "." ["*.cabal"] - --------------------------------------------------------------------------------- - -data Config buildSystem = Config - { experiments :: [Unescaped String], - examples :: [Example], - samples :: Natural, - versions :: [GitCommit], - -- | Output folder ('foo' works, 'foo/bar' does not) - outputFolder :: String, - buildTool :: buildSystem, - profileInterval :: Maybe Double - } - deriving (Generic, Show) - deriving anyclass (FromJSON) - -createBuildSystem :: FilePath -> Rules (Config BuildSystem ) -createBuildSystem config = do - readConfig <- newCache $ \fp -> need [fp] >> liftIO (readConfigIO fp) - - _ <- addOracle $ \GetExperiments {} -> experiments <$> readConfig config - _ <- addOracle $ \GetVersions {} -> versions <$> readConfig config - _ <- versioned 1 $ addOracle $ \GetExamples{} -> examples <$> readConfig config - _ <- versioned 1 $ addOracle $ \(GetExample name) -> find (\e -> getExampleName e == name) . examples <$> readConfig config - _ <- addOracle $ \GetBuildSystem {} -> buildTool <$> readConfig config - _ <- addOracle $ \GetSamples{} -> samples <$> readConfig config - - configStatic <- liftIO $ readConfigIO config - let build = outputFolder configStatic - - buildRules build ghcideBuildRules - benchRules build (MkBenchRules (askOracle $ GetSamples ()) benchGhcide warmupGhcide "ghcide") - csvRules build - svgRules build - heapProfileRules build - phonyRules "" "ghcide" NoProfiling build (examples configStatic) - - whenJust (profileInterval configStatic) $ \i -> do - phonyRules "profiled-" "ghcide" (CheapHeapProfiling i) build (examples configStatic) - - return configStatic - -newtype GetSamples = GetSamples () deriving newtype (Binary, Eq, Hashable, NFData, Show) -type instance RuleResult GetSamples = Natural - --------------------------------------------------------------------------------- - -buildGhcide :: BuildSystem -> [CmdOption] -> FilePath -> Action () -buildGhcide Cabal args out = do - command_ args "cabal" - ["install" - ,"exe:ghcide" - ,"--installdir=" ++ out - ,"--install-method=copy" - ,"--overwrite-policy=always" - ,"--ghc-options=-rtsopts" - ,"--ghc-options=-eventlog" - ] - -buildGhcide Stack args out = - command_ args "stack" - ["--local-bin-path=" <> out - ,"build" - ,"ghcide:ghcide" - ,"--copy-bins" - ,"--ghc-options=-rtsopts" - ,"--ghc-options=-eventlog" - ] - -benchGhcide - :: Natural -> BuildSystem -> [CmdOption] -> BenchProject Example -> Action () -benchGhcide samples buildSystem args BenchProject{..} = do - command_ args "ghcide-bench" $ - [ "--timeout=300", - "--no-clean", - "-v", - "--samples=" <> show samples, - "--csv=" <> outcsv, - "--ghcide=" <> exePath, - "--select", - unescaped (unescapeExperiment experiment) - ] ++ - exampleToOptions example exeExtraArgs ++ - [ "--stack" | Stack == buildSystem - ] - -warmupGhcide :: BuildSystem -> FilePath -> [CmdOption] -> Example -> Action () -warmupGhcide buildSystem exePath args example = do - command args "ghcide-bench" $ - [ "--no-clean", - "-v", - "--samples=1", - "--ghcide=" <> exePath, - "--select=hover" - ] ++ - exampleToOptions example [] ++ - [ "--stack" | Stack == buildSystem - ] diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index 8db4b73e50..e3af7960ce 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -1,4 +1,4 @@ -cabal-version: 2.4 +cabal-version: 3.0 build-type: Simple category: Development name: ghcide @@ -267,45 +267,6 @@ executable ghcide-test-preprocessor if !flag(test-exe) buildable: False -benchmark benchHist - type: exitcode-stdio-1.0 - default-language: Haskell2010 - ghc-options: -Wall -Wno-name-shadowing -threaded - main-is: Main.hs - hs-source-dirs: bench/hist bench/lib - other-modules: Experiments.Types - build-tool-depends: - ghcide:ghcide-bench, - hp2pretty:hp2pretty, - implicit-hie:gen-hie - default-extensions: - BangPatterns - DeriveFunctor - DeriveGeneric - FlexibleContexts - GeneralizedNewtypeDeriving - LambdaCase - NamedFieldPuns - RecordWildCards - ScopedTypeVariables - StandaloneDeriving - TupleSections - TypeApplications - ViewPatterns - - build-depends: - aeson, - base == 4.*, - shake-bench == 0.1.*, - directory, - extra, - filepath, - lens, - optparse-applicative, - shake, - text, - yaml - flag executable description: Build the ghcide executable default: True @@ -389,8 +350,6 @@ test-suite ghcide-tests aeson, async, base, - binary, - bytestring, containers, data-default, directory, @@ -407,7 +366,6 @@ test-suite ghcide-tests -------------------------------------------------------------- ghcide, ghc-typelits-knownnat, - haddock-library, lsp, lsp-types, hls-plugin-api, @@ -416,20 +374,13 @@ test-suite ghcide-tests lsp-test ^>= 0.14, monoid-subclasses, network-uri, - optparse-applicative, - parallel, - process, QuickCheck, - quickcheck-instances, random, regex-tdfa ^>= 1.3.1, - safe, - safe-exceptions, shake, sqlite-simple, stm, stm-containers, - hls-graph, tasty, tasty-expected-failure, tasty-hunit >= 0.10, @@ -438,7 +389,6 @@ test-suite ghcide-tests text, text-rope, unordered-containers, - vector, if (impl(ghc >= 8.6) && impl(ghc < 9.2)) build-depends: record-dot-preprocessor, @@ -450,8 +400,6 @@ test-suite ghcide-tests Development.IDE.Test Development.IDE.Test.Diagnostic Development.IDE.Test.Runfiles - Experiments - Experiments.Types FuzzySearch Progress HieDbRetry @@ -470,59 +418,3 @@ test-suite ghcide-tests TupleSections TypeApplications ViewPatterns - -flag bench-exe - description: Build the ghcide-bench executable - default: True - -executable ghcide-bench - default-language: Haskell2010 - build-tool-depends: - ghcide:ghcide - build-depends: - aeson, - base, - bytestring, - containers, - data-default, - directory, - extra, - filepath, - ghcide, - hls-plugin-api, - lens, - lsp-test, - lsp-types, - optparse-applicative, - process, - safe-exceptions, - hls-graph, - shake, - tasty-hunit >= 0.10, - text - hs-source-dirs: bench/lib bench/exe test/src - ghc-options: -threaded -Wall -Wno-name-shadowing -rtsopts - main-is: Main.hs - other-modules: - Development.IDE.Test - Development.IDE.Test.Diagnostic - Experiments - Experiments.Types - default-extensions: - BangPatterns - DeriveFunctor - DeriveGeneric - FlexibleContexts - GeneralizedNewtypeDeriving - LambdaCase - NamedFieldPuns - OverloadedStrings - RecordWildCards - ScopedTypeVariables - StandaloneDeriving - TupleSections - TypeApplications - ViewPatterns - - if !flag(bench-exe) - buildable: False diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 73caa02437..787e6941c4 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -65,7 +65,6 @@ import Development.IDE.Test.Runfiles import qualified Development.IDE.Types.Diagnostics as Diagnostics import Development.IDE.Types.Location import Development.Shake (getDirectoryFilesIO) -import qualified Experiments as Bench import Ide.Plugin.Config import Language.LSP.Test import Language.LSP.Types hiding @@ -221,7 +220,6 @@ main = do , cradleTests , dependentFileTest , nonLspCommandLine - , benchmarkTests , ifaceTests , bootTests , rootUriTests @@ -6311,25 +6309,6 @@ nonLspCommandLine = testGroup "ghcide command line" ec @?= ExitSuccess ] -benchmarkTests :: TestTree -benchmarkTests = - let ?config = Bench.defConfig - { Bench.verbosity = Bench.Quiet - , Bench.repetitions = Just 3 - , Bench.buildTool = Bench.Cabal - } in - withResource Bench.setup Bench.cleanUp $ \getResource -> testGroup "benchmark experiments" - [ testCase (Bench.name e) $ do - Bench.SetupResult{Bench.benchDir} <- getResource - res <- Bench.runBench (runInDir benchDir) e - assertBool "did not successfully complete 5 repetitions" $ Bench.success res - | e <- Bench.experiments - , Bench.name e /= "edit" -- the edit experiment does not ever fail - , Bench.name e /= "hole fit suggestions" -- is too slow! - -- the cradle experiments are way too slow - , not ("cradle" `isInfixOf` Bench.name e) - ] - -- | checks if we use InitializeParams.rootUri for loading session rootUriTests :: TestTree rootUriTests = testCase "use rootUri" . runTest "dirA" "dirB" $ \dir -> do diff --git a/ghcide/test/src/Development/IDE/Test.hs b/ghcide/test/src/Development/IDE/Test.hs index b4385043be..216020a89e 100644 --- a/ghcide/test/src/Development/IDE/Test.hs +++ b/ghcide/test/src/Development/IDE/Test.hs @@ -29,11 +29,6 @@ module Development.IDE.Test , getStoredKeys , waitForCustomMessage , waitForGC - , getBuildKeysBuilt - , getBuildKeysVisited - , getBuildKeysChanged - , getBuildEdgesCount - , getRebuildsCount , configureCheckProject , isReferenceReady , referenceReady) where @@ -214,21 +209,6 @@ waitForAction :: String -> TextDocumentIdentifier -> Session WaitForIdeRuleResul waitForAction key TextDocumentIdentifier{_uri} = callTestPlugin (WaitForIdeRule key _uri) -getBuildKeysBuilt :: Session (Either ResponseError [T.Text]) -getBuildKeysBuilt = tryCallTestPlugin GetBuildKeysBuilt - -getBuildKeysVisited :: Session (Either ResponseError [T.Text]) -getBuildKeysVisited = tryCallTestPlugin GetBuildKeysVisited - -getBuildKeysChanged :: Session (Either ResponseError [T.Text]) -getBuildKeysChanged = tryCallTestPlugin GetBuildKeysChanged - -getBuildEdgesCount :: Session (Either ResponseError Int) -getBuildEdgesCount = tryCallTestPlugin GetBuildEdgesCount - -getRebuildsCount :: Session (Either ResponseError Int) -getRebuildsCount = tryCallTestPlugin GetRebuildsCount - getInterfaceFilesDir :: TextDocumentIdentifier -> Session FilePath getInterfaceFilesDir TextDocumentIdentifier{_uri} = callTestPlugin (GetInterfaceFilesDir _uri) diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index d786e71530..694f057534 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -1,4 +1,4 @@ -cabal-version: 2.4 +cabal-version: 3.0 category: Development name: haskell-language-server version: 1.7.0.0 @@ -233,12 +233,6 @@ flag dynamic default: True manual: True -common example-plugins - hs-source-dirs: plugins/default/src - other-modules: Ide.Plugin.Example, - Ide.Plugin.Example2, - Ide.Plugin.ExampleCabal - common class if flag(class) build-depends: hls-class-plugin ^>= 1.0 @@ -366,13 +360,12 @@ common brittany build-depends: hls-brittany-plugin ^>= 1.0 cpp-options: -Dhls_brittany -executable haskell-language-server +library plugins import: common-deps -- configuration , warnings , pedantic -- plugins - , example-plugins , callHierarchy , changeTypeSignature , class @@ -398,10 +391,20 @@ executable haskell-language-server , ormolu , stylishHaskell , brittany + exposed-modules: HlsPlugins + hs-source-dirs: src + + build-depends: ghcide, hls-plugin-api + default-language: Haskell2010 + default-extensions: DataKinds, TypeOperators +executable haskell-language-server + import: common-deps + -- configuration + , warnings + , pedantic main-is: Main.hs hs-source-dirs: exe - other-modules: Plugins ghc-options: -threaded @@ -438,6 +441,7 @@ executable haskell-language-server , ghcide , hashable , haskell-language-server + , haskell-language-server:plugins , lsp , hie-bios , hiedb @@ -579,3 +583,47 @@ test-suite wrapper-test hs-source-dirs: test/wrapper main-is: Main.hs + +benchmark benchmark + type: exitcode-stdio-1.0 + default-language: Haskell2010 + ghc-options: -Wall -Wno-name-shadowing -threaded + main-is: Main.hs + hs-source-dirs: bench + build-tool-depends: + ghcide-bench:ghcide-bench, + hp2pretty:hp2pretty, + implicit-hie:gen-hie + default-extensions: + BangPatterns + DeriveFunctor + DeriveGeneric + FlexibleContexts + GeneralizedNewtypeDeriving + LambdaCase + NamedFieldPuns + RecordWildCards + ScopedTypeVariables + StandaloneDeriving + TupleSections + TypeApplications + ViewPatterns + + build-depends: + aeson, + base == 4.*, + containers, + data-default, + directory, + extra, + filepath, + ghcide-bench, + haskell-language-server:plugins, + hls-plugin-api, + lens, + lens-aeson, + optparse-applicative, + shake, + shake-bench == 0.1.*, + text, + yaml diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index 95c04f24c5..4877b5271b 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -704,7 +704,7 @@ type CommandFunction ideState a newtype PluginId = PluginId T.Text deriving (Show, Read, Eq, Ord) - deriving newtype Hashable + deriving newtype (FromJSON, Hashable) instance IsString PluginId where fromString = PluginId . T.pack diff --git a/plugins/default/src/Ide/Plugin/Example.hs b/plugins/default/src/Ide/Plugin/Example.hs deleted file mode 100644 index 33bf8720fa..0000000000 --- a/plugins/default/src/Ide/Plugin/Example.hs +++ /dev/null @@ -1,253 +0,0 @@ -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TupleSections #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE ViewPatterns #-} - -module Ide.Plugin.Example - ( - descriptor - , Log(..) - ) where - -import Control.Concurrent.STM -import Control.DeepSeq (NFData) -import Control.Monad.IO.Class -import Control.Monad.Trans.Maybe -import Data.Aeson -import Data.Functor -import Data.Hashable -import qualified Data.HashMap.Strict as Map -import qualified Data.Text as T -import Data.Typeable -import Development.IDE as D -import Development.IDE.Core.Shake (getDiagnostics, - getHiddenDiagnostics) -import qualified Development.IDE.Core.Shake as Shake -import Development.IDE.GHC.Compat -import GHC.Generics -import Ide.PluginUtils -import Ide.Types -import Language.LSP.Server -import Language.LSP.Types -import Options.Applicative (ParserInfo, info) -import Text.Regex.TDFA.Text () - --- --------------------------------------------------------------------- - -newtype Log = LogShake Shake.Log deriving Show - -instance Pretty Log where - pretty = \case - LogShake log -> pretty log - -descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState -descriptor recorder plId = (defaultPluginDescriptor plId) - { pluginRules = exampleRules recorder - , pluginCommands = [PluginCommand "codelens.todo" "example adding" addTodoCmd] - , pluginHandlers = mkPluginHandler STextDocumentCodeAction codeAction - <> mkPluginHandler STextDocumentCodeLens codeLens - <> mkPluginHandler STextDocumentHover hover - <> mkPluginHandler STextDocumentDocumentSymbol symbols - <> mkPluginHandler STextDocumentCompletion completion - , pluginCli = Just exampleCli - } - -exampleCli :: ParserInfo (IdeCommand IdeState) -exampleCli = info p mempty - where p = pure $ IdeCommand $ \_ideState -> putStrLn "hello HLS" - --- --------------------------------------------------------------------- - -hover :: PluginMethodHandler IdeState TextDocumentHover -hover ide _ HoverParams{..} = liftIO $ request "Hover" blah (Right Nothing) foundHover ide TextDocumentPositionParams{..} - -blah :: NormalizedFilePath -> Position -> Action (Maybe (Maybe Range, [T.Text])) -blah _ (Position line col) - = return $ Just (Just (Range (Position line col) (Position (line+1) 0)), ["example hover 1\n"]) - --- --------------------------------------------------------------------- --- Generating Diagnostics via rules --- --------------------------------------------------------------------- - -data Example = Example - deriving (Eq, Show, Typeable, Generic) -instance Hashable Example -instance NFData Example - -type instance RuleResult Example = () - -exampleRules :: Recorder (WithPriority Log) -> Rules () -exampleRules recorder = do - define (cmapWithPrio LogShake recorder) $ \Example file -> do - _pm <- getParsedModule file - let diag = mkDiag file "example" DsError (Range (Position 0 0) (Position 1 0)) "example diagnostic, hello world" - return ([diag], Just ()) - - action $ do - files <- getFilesOfInterestUntracked - void $ uses Example $ Map.keys files - -mkDiag :: NormalizedFilePath - -> DiagnosticSource - -> DiagnosticSeverity - -> Range - -> T.Text - -> FileDiagnostic -mkDiag file diagSource sev loc msg = (file, D.ShowDiag,) - Diagnostic - { _range = loc - , _severity = Just sev - , _source = Just diagSource - , _message = msg - , _code = Nothing - , _tags = Nothing - , _relatedInformation = Nothing - } - --- --------------------------------------------------------------------- --- code actions --- --------------------------------------------------------------------- - --- | Generate code actions. -codeAction :: PluginMethodHandler IdeState TextDocumentCodeAction -codeAction state _pid (CodeActionParams _ _ (TextDocumentIdentifier uri) _range CodeActionContext{_diagnostics=List _xs}) = liftIO $ do - let mbnfp = uriToNormalizedFilePath $ toNormalizedUri uri - case mbnfp of - Just nfp -> do - Just (ParsedModule{},_) <- runIdeAction "example" (shakeExtras state) $ useWithStaleFast GetParsedModule nfp - let - title = "Add TODO Item 1" - tedit = [TextEdit (Range (Position 2 0) (Position 2 0)) - "-- TODO1 added by Example Plugin directly\n"] - edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing Nothing - pure $ Right $ List - [ InR $ CodeAction title (Just CodeActionQuickFix) (Just $ List []) Nothing Nothing (Just edit) Nothing Nothing] - Nothing -> error $ "Unable to get a normalized file path from the uri: " ++ show uri - --- --------------------------------------------------------------------- - -codeLens :: PluginMethodHandler IdeState TextDocumentCodeLens -codeLens ideState plId CodeLensParams{_textDocument=TextDocumentIdentifier uri} = liftIO $ do - logInfo (ideLogger ideState) "Example.codeLens entered (ideLogger)" -- AZ - case uriToFilePath' uri of - Just (toNormalizedFilePath -> filePath) -> do - _ <- runIdeAction "Example.codeLens" (shakeExtras ideState) $ runMaybeT $ useE TypeCheck filePath - _diag <- atomically $ getDiagnostics ideState - _hDiag <- atomically $ getHiddenDiagnostics ideState - let - title = "Add TODO Item via Code Lens" - -- tedit = [TextEdit (Range (Position 3 0) (Position 3 0)) - -- "-- TODO added by Example Plugin via code lens action\n"] - -- edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing - range = Range (Position 3 0) (Position 4 0) - let cmdParams = AddTodoParams uri "do abc" - cmd = mkLspCommand plId "codelens.todo" title (Just [toJSON cmdParams]) - pure $ Right $ List [ CodeLens range (Just cmd) Nothing ] - Nothing -> pure $ Right $ List [] - --- --------------------------------------------------------------------- --- | Parameters for the addTodo PluginCommand. -data AddTodoParams = AddTodoParams - { file :: Uri -- ^ Uri of the file to add the pragma to - , todoText :: T.Text - } - deriving (Show, Eq, Generic, ToJSON, FromJSON) - -addTodoCmd :: CommandFunction IdeState AddTodoParams -addTodoCmd _ide (AddTodoParams uri todoText) = do - let - pos = Position 3 0 - textEdits = List - [TextEdit (Range pos pos) - ("-- TODO:" <> todoText <> "\n") - ] - res = WorkspaceEdit - (Just $ Map.singleton uri textEdits) - Nothing - Nothing - _ <- sendRequest SWorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing res) (\_ -> pure ()) - return $ Right Null - --- --------------------------------------------------------------------- - -foundHover :: (Maybe Range, [T.Text]) -> Either ResponseError (Maybe Hover) -foundHover (mbRange, contents) = - Right $ Just $ Hover (HoverContents $ MarkupContent MkMarkdown - $ T.intercalate sectionSeparator contents) mbRange - - --- | Respond to and log a hover or go-to-definition request -request - :: T.Text - -> (NormalizedFilePath -> Position -> Action (Maybe a)) - -> Either ResponseError b - -> (a -> Either ResponseError b) - -> IdeState - -> TextDocumentPositionParams - -> IO (Either ResponseError b) -request label getResults notFound found ide (TextDocumentPositionParams (TextDocumentIdentifier uri) pos) = do - mbResult <- case uriToFilePath' uri of - Just path -> logAndRunRequest label getResults ide pos path - Nothing -> pure Nothing - pure $ maybe notFound found mbResult - -logAndRunRequest :: T.Text -> (NormalizedFilePath -> Position -> Action b) - -> IdeState -> Position -> String -> IO b -logAndRunRequest label getResults ide pos path = do - let filePath = toNormalizedFilePath path - logInfo (ideLogger ide) $ - label <> " request at position " <> T.pack (showPosition pos) <> - " in file: " <> T.pack path - runAction "Example" ide $ getResults filePath pos - --- --------------------------------------------------------------------- - -symbols :: PluginMethodHandler IdeState TextDocumentDocumentSymbol -symbols _ide _pid (DocumentSymbolParams _ _ _doc) - = pure $ Right $ InL $ List [r] - where - r = DocumentSymbol name detail kind Nothing deprecation range selR chList - name = "Example_symbol_name" - detail = Nothing - kind = SkVariable - deprecation = Nothing - range = Range (Position 2 0) (Position 2 5) - selR = range - chList = Nothing - --- --------------------------------------------------------------------- - -completion :: PluginMethodHandler IdeState TextDocumentCompletion -completion _ide _pid (CompletionParams _doc _pos _ _ _mctxt) - = pure $ Right $ InL $ List [r] - where - r = CompletionItem label kind tags detail documentation deprecated preselect - sortText filterText insertText insertTextFormat insertTextMode - textEdit additionalTextEdits commitCharacters - command xd - label = "Example completion" - kind = Nothing - tags = Nothing - detail = Nothing - documentation = Nothing - deprecated = Nothing - preselect = Nothing - sortText = Nothing - filterText = Nothing - insertText = Nothing - insertTextMode = Nothing - insertTextFormat = Nothing - textEdit = Nothing - additionalTextEdits = Nothing - commitCharacters = Nothing - command = Nothing - xd = Nothing - --- --------------------------------------------------------------------- diff --git a/plugins/default/src/Ide/Plugin/Example2.hs b/plugins/default/src/Ide/Plugin/Example2.hs deleted file mode 100644 index 8ba3a69b68..0000000000 --- a/plugins/default/src/Ide/Plugin/Example2.hs +++ /dev/null @@ -1,237 +0,0 @@ -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TupleSections #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE ViewPatterns #-} - -module Ide.Plugin.Example2 - ( - descriptor - , Log(..) - ) where - -import Control.Concurrent.STM -import Control.DeepSeq (NFData) -import Control.Monad.IO.Class -import Control.Monad.Trans.Maybe -import Data.Aeson -import Data.Functor -import Data.Hashable -import qualified Data.HashMap.Strict as Map -import qualified Data.Text as T -import Data.Typeable -import Development.IDE as D -import Development.IDE.Core.Shake hiding (Log) -import qualified Development.IDE.Core.Shake as Shake -import GHC.Generics -import Ide.PluginUtils -import Ide.Types -import Language.LSP.Server -import Language.LSP.Types -import Text.Regex.TDFA.Text () - --- --------------------------------------------------------------------- - -newtype Log = LogShake Shake.Log deriving Show - -instance Pretty Log where - pretty = \case - LogShake log -> pretty log - -descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState -descriptor recorder plId = (defaultPluginDescriptor plId) - { pluginRules = exampleRules recorder - , pluginCommands = [PluginCommand "codelens.todo" "example adding" addTodoCmd] - , pluginHandlers = mkPluginHandler STextDocumentCodeAction codeAction - <> mkPluginHandler STextDocumentCodeLens codeLens - <> mkPluginHandler STextDocumentHover hover - <> mkPluginHandler STextDocumentDocumentSymbol symbols - <> mkPluginHandler STextDocumentCompletion completion - } - --- --------------------------------------------------------------------- - -hover :: PluginMethodHandler IdeState TextDocumentHover -hover ide _ HoverParams{..} = liftIO $ request "Hover" blah (Right Nothing) foundHover ide TextDocumentPositionParams{..} - -blah :: NormalizedFilePath -> Position -> Action (Maybe (Maybe Range, [T.Text])) -blah _ (Position line col) - = return $ Just (Just (Range (Position line col) (Position (line+1) 0)), ["example hover 2\n"]) - --- --------------------------------------------------------------------- --- Generating Diagnostics via rules --- --------------------------------------------------------------------- - -data Example2 = Example2 - deriving (Eq, Show, Typeable, Generic) -instance Hashable Example2 -instance NFData Example2 - -type instance RuleResult Example2 = () - -exampleRules :: Recorder (WithPriority Log) -> Rules () -exampleRules recorder = do - define (cmapWithPrio LogShake recorder) $ \Example2 file -> do - _pm <- getParsedModule file - let diag = mkDiag file "example2" DsError (Range (Position 0 0) (Position 1 0)) "example2 diagnostic, hello world" - return ([diag], Just ()) - - action $ do - files <- getFilesOfInterestUntracked - void $ uses Example2 $ Map.keys files - -mkDiag :: NormalizedFilePath - -> DiagnosticSource - -> DiagnosticSeverity - -> Range - -> T.Text - -> FileDiagnostic -mkDiag file diagSource sev loc msg = (file, D.ShowDiag,) - Diagnostic - { _range = loc - , _severity = Just sev - , _source = Just diagSource - , _message = msg - , _code = Nothing - , _tags = Nothing - , _relatedInformation = Nothing - } - --- --------------------------------------------------------------------- --- code actions --- --------------------------------------------------------------------- - --- | Generate code actions. -codeAction :: PluginMethodHandler IdeState TextDocumentCodeAction -codeAction _state _pid (CodeActionParams _ _ (TextDocumentIdentifier uri) _range CodeActionContext{_diagnostics=List _xs}) = do - let - title = "Add TODO2 Item" - tedit = [TextEdit (Range (Position 3 0) (Position 3 0)) - "-- TODO2 added by Example2 Plugin directly\n"] - edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing Nothing - pure $ Right $ List - [ InR $ CodeAction title (Just CodeActionQuickFix) (Just $ List []) Nothing Nothing (Just edit) Nothing Nothing] - --- --------------------------------------------------------------------- - -codeLens :: PluginMethodHandler IdeState TextDocumentCodeLens -codeLens ideState plId CodeLensParams{_textDocument=TextDocumentIdentifier uri} = liftIO $ do - logInfo (ideLogger ideState) "Example2.codeLens entered (ideLogger)" -- AZ - case uriToFilePath' uri of - Just (toNormalizedFilePath -> filePath) -> do - _ <- runIdeAction (fromNormalizedFilePath filePath) (shakeExtras ideState) $ runMaybeT $ useE TypeCheck filePath - _diag <- atomically $ getDiagnostics ideState - _hDiag <- atomically $ getHiddenDiagnostics ideState - let - title = "Add TODO2 Item via Code Lens" - range = Range (Position 3 0) (Position 4 0) - let cmdParams = AddTodoParams uri "do abc" - cmd = mkLspCommand plId "codelens.todo" title (Just [toJSON cmdParams]) - pure $ Right $ List [ CodeLens range (Just cmd) Nothing ] - Nothing -> pure $ Right $ List [] - --- --------------------------------------------------------------------- --- | Parameters for the addTodo PluginCommand. -data AddTodoParams = AddTodoParams - { file :: Uri -- ^ Uri of the file to add the pragma to - , todoText :: T.Text - } - deriving (Show, Eq, Generic, ToJSON, FromJSON) - -addTodoCmd :: CommandFunction IdeState AddTodoParams -addTodoCmd _ide (AddTodoParams uri todoText) = do - let - pos = Position 5 0 - textEdits = List - [TextEdit (Range pos pos) - ("-- TODO2:" <> todoText <> "\n") - ] - res = WorkspaceEdit - (Just $ Map.singleton uri textEdits) - Nothing - Nothing - _ <- sendRequest SWorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing res) (\_ -> pure ()) - return $ Right Null - --- --------------------------------------------------------------------- - -foundHover :: (Maybe Range, [T.Text]) -> Either ResponseError (Maybe Hover) -foundHover (mbRange, contents) = - Right $ Just $ Hover (HoverContents $ MarkupContent MkMarkdown - $ T.intercalate sectionSeparator contents) mbRange - - --- | Respond to and log a hover or go-to-definition request -request - :: T.Text - -> (NormalizedFilePath -> Position -> Action (Maybe a)) - -> Either ResponseError b - -> (a -> Either ResponseError b) - -> IdeState - -> TextDocumentPositionParams - -> IO (Either ResponseError b) -request label getResults notFound found ide (TextDocumentPositionParams (TextDocumentIdentifier uri) pos) = do - mbResult <- case uriToFilePath' uri of - Just path -> logAndRunRequest label getResults ide pos path - Nothing -> pure Nothing - pure $ maybe notFound found mbResult - -logAndRunRequest :: T.Text -> (NormalizedFilePath -> Position -> Action b) - -> IdeState -> Position -> String -> IO b -logAndRunRequest label getResults ide pos path = do - let filePath = toNormalizedFilePath path - logInfo (ideLogger ide) $ - label <> " request at position " <> T.pack (showPosition pos) <> - " in file: " <> T.pack path - runAction "Example2" ide $ getResults filePath pos - --- --------------------------------------------------------------------- - -symbols :: PluginMethodHandler IdeState TextDocumentDocumentSymbol -symbols _ide _ (DocumentSymbolParams _ _ _doc) - = pure $ Right $ InL $ List [r] - where - r = DocumentSymbol name detail kind Nothing deprecation range selR chList - name = "Example2_symbol_name" - detail = Nothing - kind = SkVariable - deprecation = Nothing - range = Range (Position 4 1) (Position 4 7) - selR = range - chList = Nothing - --- --------------------------------------------------------------------- - -completion :: PluginMethodHandler IdeState TextDocumentCompletion -completion _ide _pid (CompletionParams _doc _pos _ _ _mctxt) - = pure $ Right $ InL $ List [r] - where - r = CompletionItem label kind tags detail documentation deprecated preselect - sortText filterText insertText insertTextFormat insertTextMode - textEdit additionalTextEdits commitCharacters - command xd - label = "Example2 completion" - kind = Nothing - tags = Nothing - detail = Nothing - documentation = Nothing - deprecated = Nothing - preselect = Nothing - sortText = Nothing - filterText = Nothing - insertText = Nothing - insertTextMode = Nothing - insertTextFormat = Nothing - textEdit = Nothing - additionalTextEdits = Nothing - commitCharacters = Nothing - command = Nothing - xd = Nothing - --- --------------------------------------------------------------------- diff --git a/plugins/default/src/Ide/Plugin/ExampleCabal.hs b/plugins/default/src/Ide/Plugin/ExampleCabal.hs deleted file mode 100644 index 39a64f220a..0000000000 --- a/plugins/default/src/Ide/Plugin/ExampleCabal.hs +++ /dev/null @@ -1,75 +0,0 @@ -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TupleSections #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE ViewPatterns #-} - -module Ide.Plugin.ExampleCabal where - -import Control.Monad.IO.Class -import Data.Aeson -import qualified Data.HashMap.Strict as Map -import qualified Data.Text as T -import Development.IDE as D hiding (pluginHandlers) -import GHC.Generics -import Ide.PluginUtils -import Ide.Types -import Language.LSP.Server -import Language.LSP.Types - -newtype Log = LogText T.Text deriving Show - -instance Pretty Log where - pretty = \case - LogText log -> pretty log - -descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState -descriptor recorder plId = (defaultCabalPluginDescriptor plId) - { pluginCommands = [PluginCommand "codelens.todo" "example adding" addTodoCmd] - , pluginHandlers = mkPluginHandler STextDocumentCodeLens (codeLens recorder) - } - --- --------------------------------------------------------------------- - -codeLens :: Recorder (WithPriority Log) -> PluginMethodHandler IdeState TextDocumentCodeLens -codeLens recorder _ideState plId CodeLensParams{_textDocument=TextDocumentIdentifier uri} = liftIO $ do - log Debug $ LogText "ExampleCabal.codeLens entered (ideLogger)" - case uriToFilePath' uri of - Just (toNormalizedFilePath -> _filePath) -> do - let - title = "Add TODO Item via Code Lens" - range = Range (Position 3 0) (Position 4 0) - let cmdParams = AddTodoParams uri "do abc" - cmd = mkLspCommand plId "codelens.todo" title (Just [toJSON cmdParams]) - pure $ Right $ List [ CodeLens range (Just cmd) Nothing ] - Nothing -> pure $ Right $ List [] - where - log = logWith recorder - --- --------------------------------------------------------------------- --- | Parameters for the addTodo PluginCommand. -data AddTodoParams = AddTodoParams - { file :: Uri -- ^ Uri of the file to add the pragma to - , todoText :: T.Text - } - deriving (Show, Eq, Generic, ToJSON, FromJSON) - -addTodoCmd :: CommandFunction IdeState AddTodoParams -addTodoCmd _ide (AddTodoParams uri todoText) = do - let - pos = Position 5 0 - textEdits = List - [TextEdit (Range pos pos) - ("-- TODO2:" <> todoText <> "\n") - ] - res = WorkspaceEdit - (Just $ Map.singleton uri textEdits) - Nothing - Nothing - _ <- sendRequest SWorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing res) (\_ -> pure ()) - return $ Right Null diff --git a/shake-bench/shake-bench.cabal b/shake-bench/shake-bench.cabal index cd4474f36e..ec1649ccd5 100644 --- a/shake-bench/shake-bench.cabal +++ b/shake-bench/shake-bench.cabal @@ -17,6 +17,7 @@ library build-depends: aeson, base == 4.*, + bytestring, Chart, Chart-diagrams, diagrams-contrib, diff --git a/shake-bench/src/Development/Benchmark/Rules.hs b/shake-bench/src/Development/Benchmark/Rules.hs index a68507e604..7d5e4dcef9 100644 --- a/shake-bench/src/Development/Benchmark/Rules.hs +++ b/shake-bench/src/Development/Benchmark/Rules.hs @@ -26,20 +26,22 @@ ├── binaries │ └── │  ├── ghc.path - path to ghc used to build the executable - │  └── - binary for this version + │  ├── - binary for this version │  └── commitid - Git commit id for this reference ├─ - │ ├── results.csv - aggregated results for all the versions - │ └── - │   ├── .gcStats.log - RTS -s output - │   ├── .csv - stats for the experiment - │   ├── .svg - Graph of bytes over elapsed time - │   ├── .diff.svg - idem, including the previous version - │   ├── .heap.svg - Heap profile - │   ├── .log - bench stdout - │   └── results.csv - results of all the experiments for the example - ├── results.csv - aggregated results of all the experiments and versions - └── .svg - graph of bytes over elapsed time, for all the included versions + │ ├── results.csv - aggregated results for all the versions and configurations + │ ├── .svg - graph of bytes over elapsed time, for all the versions and configurations + | └── + │ └── + │   ├── .gcStats.log - RTS -s output + │   ├── .csv - stats for the experiment + │   ├── .svg - Graph of bytes over elapsed time + │   ├── .diff.svg - idem, including the previous version + │   ├── .heap.svg - Heap profile + │   ├── .log - bench stdout + │   └── results.csv - results of all the experiments for the example + ├── results.csv - aggregated results of all the examples, experiments, versions and configurations + └── .svg - graph of bytes over elapsed time, for all the examples, experiments, versions and configuratiof For diff graphs, the "previous version" is the preceding entry in the list of versions in the config file. A possible improvement is to obtain this info via `git rev-list`. @@ -47,7 +49,7 @@ {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} module Development.Benchmark.Rules ( - buildRules, MkBuildRules(..), + buildRules, MkBuildRules(..), OutputFolder, ProjectRoot, benchRules, MkBenchRules(..), BenchProject(..), ProfilingMode(..), csvRules, svgRules, @@ -60,6 +62,7 @@ module Development.Benchmark.Rules GetVersions(..), GetCommitId(..), GetBuildSystem(..), + GetConfigurations(..), Configuration(..), BuildSystem(..), findGhcForBuildSystem, Escaped(..), Unescaped(..), escapeExperiment, unescapeExperiment, GitCommit @@ -76,6 +79,7 @@ import Data.Aeson (FromJSON (..), (.!=), (.:?), (.=)) import Data.Aeson.Lens (AsJSON (_JSON), _Object, _String) +import Data.ByteString.Lazy (ByteString) import Data.Char (isDigit) import Data.List (find, isInfixOf, stripPrefix, @@ -94,6 +98,7 @@ import GHC.Generics (Generic) import GHC.Stack (HasCallStack) import qualified Graphics.Rendering.Chart.Backend.Diagrams as E import qualified Graphics.Rendering.Chart.Easy as E +import Numeric.Natural import System.Directory (createDirectoryIfMissing, findExecutable, renameFile) @@ -112,6 +117,7 @@ newtype GetCommitId = GetCommitId String deriving newtype (Binary, Eq, Hashable, newtype GetBuildSystem = GetBuildSystem () deriving newtype (Binary, Eq, Hashable, NFData, Show) newtype GetExample = GetExample String deriving newtype (Binary, Eq, Hashable, NFData, Show) newtype GetExamples = GetExamples () deriving newtype (Binary, Eq, Hashable, NFData, Show) +newtype GetConfigurations = GetConfigurations () deriving newtype (Binary, Eq, Hashable, NFData, Show) type instance RuleResult GetExperiments = [Unescaped String] type instance RuleResult GetVersions = [GitCommit] @@ -124,6 +130,10 @@ type RuleResultForExample e = , RuleResult GetExamples ~ [e] , IsExample e) +data Configuration = Configuration {confName :: String, confValue :: ByteString} + deriving (Binary, Eq, Generic, Hashable, NFData, Show, Typeable) +type instance RuleResult GetConfigurations = [Configuration] + -- | Knowledge needed to run an example class (Binary e, Eq e, Hashable e, NFData e, Show e, Typeable e) => IsExample e where getExampleName :: e -> String @@ -134,6 +144,7 @@ allTargetsForExample :: IsExample e => ProfilingMode -> FilePath -> e -> Action allTargetsForExample prof baseFolder ex = do experiments <- askOracle $ GetExperiments () versions <- askOracle $ GetVersions () + configurations <- askOracle $ GetConfigurations () let buildFolder = baseFolder profilingPath prof return $ [buildFolder getExampleName ex "results.csv"] @@ -143,9 +154,12 @@ allTargetsForExample prof baseFolder ex = do ++ [ buildFolder getExampleName ex T.unpack (humanName ver) - escaped (escapeExperiment e) <.> mode + confName + escaped (escapeExperiment e) <.> + mode | e <- experiments, ver <- versions, + Configuration{confName} <- configurations, mode <- ["svg", "diff.svg"] ++ ["heap.svg" | prof /= NoProfiling] ] @@ -179,6 +193,7 @@ phonyRules prefix executableName prof buildFolder examples = do phony (prefix <> "all-binaries") $ need =<< allBinaries buildFolder executableName -------------------------------------------------------------------------------- type OutputFolder = FilePath +type ProjectRoot = FilePath data MkBuildRules buildSystem = MkBuildRules { -- | Return the path to the GHC executable to use for the project found in the cwd @@ -187,9 +202,9 @@ data MkBuildRules buildSystem = MkBuildRules , executableName :: String -- | An action that captures the source dependencies, used for the HEAD build , projectDepends :: Action () - -- | Build the project found in the cwd and save the build artifacts in the output folder + -- | Build the project found in the given path and save the build artifacts in the output folder , buildProject :: buildSystem - -> [CmdOption] + -> ProjectRoot -> OutputFolder -> Action () } @@ -217,7 +232,7 @@ buildRules build MkBuildRules{..} = do projectDepends liftIO $ createDirectoryIfMissing True $ dropFileName out buildSystem <- askOracle $ GetBuildSystem () - buildProject buildSystem [Cwd "."] (takeDirectory out) + buildProject buildSystem "." (takeDirectory out) ghcLoc <- liftIO $ findGhc buildSystem "." writeFile' ghcpath ghcLoc @@ -232,7 +247,7 @@ buildRules build MkBuildRules{..} = do buildSystem <- askOracle $ GetBuildSystem () flip actionFinally (cmd_ ("git worktree remove bench-temp-" <> ver <> " --force" :: String)) $ do ghcLoc <- liftIO $ findGhc buildSystem ver - buildProject buildSystem [Cwd $ "bench-temp-" <> ver] (".." takeDirectory out) + buildProject buildSystem ("bench-temp-" <> ver) (".." takeDirectory out) writeFile' ghcPath ghcLoc -------------------------------------------------------------------------------- @@ -246,14 +261,17 @@ data MkBenchRules buildSystem example = forall setup. MkBenchRules , warmupProject :: buildSystem -> FilePath -> [CmdOption] -> example -> Action () -- | Name of the executable to benchmark. Should match the one used to 'MkBuildRules' , executableName :: String + -- | Number of concurrent benchmarks to run + , parallelism :: Natural } data BenchProject example = BenchProject - { outcsv :: FilePath -- ^ where to save the CSV output - , exePath :: FilePath -- ^ where to find the executable for benchmarking - , exeExtraArgs :: [String] -- ^ extra args for the executable - , example :: example -- ^ example to benchmark - , experiment :: Escaped String -- ^ experiment to run + { outcsv :: FilePath -- ^ where to save the CSV output + , exePath :: FilePath -- ^ where to find the executable for benchmarking + , exeExtraArgs :: [String] -- ^ extra args for the executable + , example :: example -- ^ example to benchmark + , experiment :: Escaped String -- ^ experiment to run + , configuration :: ByteString -- ^ configuration to use } data ProfilingMode = NoProfiling | CheapHeapProfiling Seconds @@ -272,7 +290,7 @@ profilingPath (CheapHeapProfiling i) = "profiled-" <> show i benchRules :: RuleResultForExample example => FilePattern -> MkBenchRules BuildSystem example -> Rules () benchRules build MkBenchRules{..} = do - benchResource <- newResource "ghcide-bench" 1 + benchResource <- newResource "ghcide-bench" (fromIntegral parallelism) -- warmup an example build -/- "binaries/*/*.warmup" %> \out -> do let [_, _, ver, exampleName] = splitDirectories (dropExtension out) @@ -295,33 +313,38 @@ benchRules build MkBenchRules{..} = do example -- run an experiment priority 0 $ - [ build -/- "*/*/*/*.csv", - build -/- "*/*/*/*.gcStats.log", - build -/- "*/*/*/*.output.log", - build -/- "*/*/*/*.eventlog", - build -/- "*/*/*/*.hp" + [ build -/- "*/*/*/*/*.csv", + build -/- "*/*/*/*/*.gcStats.log", + build -/- "*/*/*/*/*.output.log", + build -/- "*/*/*/*/*.eventlog", + build -/- "*/*/*/*/*.hp" ] &%> \[outcsv, outGc, outLog, outEventlog, outHp] -> do - let [_, flavour, exampleName, ver, exp] = splitDirectories outcsv + let [_, flavour, exampleName, ver, conf, exp] = splitDirectories outcsv prof = fromMaybe (error $ "Not a valid profiling mode: " <> flavour) $ profilingP flavour example <- fromMaybe (error $ "Unknown example " <> exampleName) <$> askOracle (GetExample exampleName) buildSystem <- askOracle $ GetBuildSystem () + configurations <- askOracle $ GetConfigurations () setupRes <- setupProject liftIO $ createDirectoryIfMissing True $ dropFileName outcsv let exePath = build "binaries" ver executableName exeExtraArgs = [ "+RTS" , "-l" + , "-ol" <> outEventlog , "-S" <> outGc] ++ concat [[ "-h" , "-i" <> show i + , "-po" <> outHp , "-qg"] | CheapHeapProfiling i <- [prof]] ++ ["-RTS"] ghcPath = build "binaries" ver "ghc.path" warmupPath = build "binaries" ver exampleName <.> "warmup" experiment = Escaped $ dropExtension exp + Just Configuration{..} = find (\Configuration{confName} -> confName == conf) configurations + configuration = confValue need [exePath, ghcPath, warmupPath] ghcPath <- readFile' ghcPath withResource benchResource 1 $ do @@ -333,10 +356,9 @@ benchRules build MkBenchRules{..} = do AddPath [takeDirectory ghcPath, "."] [] ] BenchProject {..} - liftIO $ renameFile "ghcide.eventlog" outEventlog liftIO $ case prof of - CheapHeapProfiling{} -> renameFile "ghcide.hp" outHp - NoProfiling -> writeFile outHp dummyHp + NoProfiling -> writeFile outHp dummyHp + _ -> return () -- extend csv output with allocation data csvContents <- liftIO $ lines <$> readFile outcsv @@ -370,7 +392,7 @@ parseMaxResidencyAndAllocations input = csvRules :: forall example . RuleResultForExample example => FilePattern -> Rules () csvRules build = do -- build results for every experiment*example - build -/- "*/*/*/results.csv" %> \out -> do + build -/- "*/*/*/*/results.csv" %> \out -> do experiments <- askOracle $ GetExperiments () let allResultFiles = [takeDirectory out escaped (escapeExperiment e) <.> "csv" | e <- experiments] @@ -380,6 +402,20 @@ csvRules build = do results = map tail allResults writeFileChanged out $ unlines $ header : concat results + -- aggregate all configurations for an experiment + build -/- "*/*/*/results.csv" %> \out -> do + configurations <- map confName <$> askOracle (GetConfigurations ()) + let allResultFiles = [takeDirectory out c "results.csv" | c <- configurations ] + + allResults <- traverse readFileLines allResultFiles + + let header = head $ head allResults + results = map tail allResults + header' = "configuration, " <> header + results' = zipWith (\v -> map (\l -> v <> ", " <> l)) configurations results + + writeFileChanged out $ unlines $ header' : interleave results' + -- aggregate all experiments for an example build -/- "*/*/results.csv" %> \out -> do versions <- map (T.unpack . humanName) <$> askOracle (GetVersions ()) @@ -416,44 +452,60 @@ svgRules build = do void $ addOracle $ \(GetParent name) -> findPrev name <$> askOracle (GetVersions ()) -- chart GC stats for an experiment on a given revision priority 1 $ - build -/- "*/*/*/*.svg" %> \out -> do - let [_, _, _example, ver, _exp] = splitDirectories out - runLog <- loadRunLog (Escaped $ replaceExtension out "csv") ver + build -/- "*/*/*/*/*.svg" %> \out -> do + let [_, _, _example, ver, conf, _exp] = splitDirectories out + runLog <- loadRunLog (Escaped $ replaceExtension out "csv") ver conf let diagram = Diagram Live [runLog] title title = ver <> " live bytes over time" plotDiagram True diagram out -- chart of GC stats for an experiment on this and the previous revision priority 2 $ - build -/- "*/*/*/*.diff.svg" %> \out -> do - let [b, flav, example, ver, exp_] = splitDirectories out + build -/- "*/*/*/*/*.diff.svg" %> \out -> do + let [b, flav, example, ver, conf, exp_] = splitDirectories out exp = Escaped $ dropExtension2 exp_ prev <- fmap T.unpack $ askOracle $ GetParent $ T.pack ver - runLog <- loadRunLog (Escaped $ replaceExtension (dropExtension out) "csv") ver - runLogPrev <- loadRunLog (Escaped $ joinPath [b,flav, example, prev, replaceExtension (dropExtension exp_) "csv"]) prev + runLog <- loadRunLog (Escaped $ replaceExtension (dropExtension out) "csv") ver conf + runLogPrev <- loadRunLog (Escaped $ joinPath [b,flav, example, prev, conf, replaceExtension (dropExtension exp_) "csv"]) prev conf let diagram = Diagram Live [runLog, runLogPrev] title title = show (unescapeExperiment exp) <> " - live bytes over time compared" plotDiagram True diagram out + -- aggregated chart of GC stats for all the configurations + build -/- "*/*/*/*.svg" %> \out -> do + let exp = Escaped $ dropExtension $ takeFileName out + [b, flav, example, ver] = splitDirectories out + versions <- askOracle $ GetVersions () + configurations <- askOracle $ GetConfigurations () + + runLogs <- forM configurations $ \Configuration{confName} -> do + loadRunLog (Escaped $ takeDirectory out confName replaceExtension (takeFileName out) "csv") ver confName + + let diagram = Diagram Live runLogs title + title = show (unescapeExperiment exp) <> " - live bytes over time" + plotDiagram False diagram out + -- aggregated chart of GC stats for all the revisions build -/- "*/*/*.svg" %> \out -> do let exp = Escaped $ dropExtension $ takeFileName out versions <- askOracle $ GetVersions () + configurations <- askOracle $ GetConfigurations () - runLogs <- forM (filter include versions) $ \v -> do + runLogs <- forM (filter include versions) $ \v -> + forM configurations $ \Configuration{confName} -> do let v' = T.unpack (humanName v) - loadRunLog (Escaped $ takeDirectory out v' replaceExtension (takeFileName out) "csv") v' + loadRunLog (Escaped $ takeDirectory out v' confName replaceExtension (takeFileName out) "csv") v' confName - let diagram = Diagram Live runLogs title + let diagram = Diagram Live (concat runLogs) title title = show (unescapeExperiment exp) <> " - live bytes over time" plotDiagram False diagram out heapProfileRules :: FilePattern -> Rules () heapProfileRules build = do priority 3 $ - build -/- "*/*/*/*.heap.svg" %> \out -> do + build -/- "*/*/*/*/*.heap.svg" %> \out -> do let hpFile = dropExtension2 out <.> "hp" need [hpFile] cmd_ ("hp2pretty" :: String) [hpFile] @@ -563,14 +615,15 @@ instance Read Frame where -- | A file path containing the output of -S for a given run data RunLog = RunLog - { runVersion :: !String, - runFrames :: ![Frame], - runSuccess :: !Bool, - runFirstReponse :: !(Maybe Seconds) + { runVersion :: !String, + runConfiguration :: !String, + runFrames :: ![Frame], + runSuccess :: !Bool, + runFirstReponse :: !(Maybe Seconds) } -loadRunLog :: HasCallStack => Escaped FilePath -> String -> Action RunLog -loadRunLog (Escaped csv_fp) ver = do +loadRunLog :: HasCallStack => Escaped FilePath -> String -> String -> Action RunLog +loadRunLog (Escaped csv_fp) ver conf = do let log_fp = replaceExtension csv_fp "gcStats.log" log <- readFileLines log_fp csv <- readFileLines csv_fp @@ -591,7 +644,7 @@ loadRunLog (Escaped csv_fp) ver = do , Just s <- readMaybe (T.unpack s) -> (s,timeForFirstResponse) _ -> error $ "Cannot parse: " <> csv_fp - return $ RunLog ver frames success firstResponse + return $ RunLog ver conf frames success firstResponse -------------------------------------------------------------------------------- @@ -631,7 +684,7 @@ plotDiagram includeFailed t@Diagram {traceMetric, runLogs} out = do ~(c:_) <- E.liftCState $ S.gets (E.view E.colors) E.plot $ do lplot <- E.line - (runVersion rl ++ if runSuccess rl then "" else " (FAILED)") + (runVersion rl ++ " " ++ runConfiguration rl ++ if runSuccess rl then "" else " (FAILED)") [ [ (totElapsed f, extract f) | f <- runFrames rl ] diff --git a/exe/Plugins.hs b/src/HlsPlugins.hs similarity index 88% rename from exe/Plugins.hs rename to src/HlsPlugins.hs index cba0c73658..2fc1e41235 100644 --- a/exe/Plugins.hs +++ b/src/HlsPlugins.hs @@ -1,7 +1,7 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE OverloadedStrings #-} -module Plugins where +module HlsPlugins where import Development.IDE.Types.Logger (Pretty (pretty), Recorder, WithPriority, cmapWithPrio) @@ -11,9 +11,6 @@ import Ide.Types (IdePlugins) -- fixed plugins import Development.IDE (IdeState) import qualified Development.IDE.Plugin.HLS.GhcIde as GhcIde -import qualified Ide.Plugin.Example as Example -import qualified Ide.Plugin.Example2 as Example2 -import qualified Ide.Plugin.ExampleCabal as ExampleCabal -- haskell-language-server optional plugins #if hls_qualifyImportedNames @@ -130,15 +127,12 @@ instance Pretty Log where -- These can be freely added or removed to tailor the available -- features of the server. -idePlugins :: Recorder (WithPriority Log) -> Bool -> IdePlugins IdeState -idePlugins recorder includeExamples = pluginDescToIdePlugins allPlugins +idePlugins :: Recorder (WithPriority Log) -> IdePlugins IdeState +idePlugins recorder = pluginDescToIdePlugins allPlugins where pluginRecorder :: forall log. (Pretty log) => Recorder (WithPriority log) pluginRecorder = cmapWithPrio Log recorder - allPlugins = if includeExamples - then basePlugins ++ examplePlugins - else basePlugins - basePlugins = + allPlugins = #if hls_pragmas Pragmas.descriptor "pragmas" : #endif @@ -215,9 +209,4 @@ idePlugins recorder includeExamples = pluginDescToIdePlugins allPlugins #if explicitFixity ++ [ExplicitFixity.descriptor pluginRecorder] #endif - examplePlugins = - [Example.descriptor pluginRecorder "eg" - ,Example2.descriptor pluginRecorder "eg2" - ,ExampleCabal.descriptor pluginRecorder "ec" - ] diff --git a/test/functional/Diagnostic.hs b/test/functional/Diagnostic.hs index bf2aab31cd..089a3ecbe2 100644 --- a/test/functional/Diagnostic.hs +++ b/test/functional/Diagnostic.hs @@ -10,23 +10,8 @@ import Test.Hls.Command -- --------------------------------------------------------------------- tests :: TestTree -tests = testGroup "diagnostics providers" [ - basicTests - , warningTests - ] +tests = testGroup "diagnostics providers" [ warningTests ] -basicTests :: TestTree -basicTests = testGroup "Diagnostics work" [ - testCase "example plugin produces diagnostics" $ - runSession hlsCommandExamplePlugin fullCaps "test/testdata/diagnostics" $ do - doc <- openDoc "Foo.hs" "haskell" - diags <- waitForDiagnosticsFromSource doc "example2" - reduceDiag <- liftIO $ inspectDiagnostic diags ["example2 diagnostic, hello world"] - liftIO $ do - length diags @?= 1 - reduceDiag ^. LSP.range @?= Range (Position 0 0) (Position 1 0) - reduceDiag ^. LSP.severity @?= Just DsError - ] warningTests :: TestTree warningTests = testGroup "Warnings are warnings" [ From 7760340e999693d07fdbea49c9e20a3dd5458ad3 Mon Sep 17 00:00:00 2001 From: Akshay Mankar Date: Sat, 27 Aug 2022 13:41:50 +0200 Subject: [PATCH 071/213] flake.nix Add ghcide-bench to sourceDirs (#3125) This is required for nix-based dev shells to work. --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index 738bcde4da..47dcc62798 100644 --- a/flake.nix +++ b/flake.nix @@ -138,6 +138,7 @@ sourceDirs = { haskell-language-server = ./.; ghcide = ./ghcide; + ghcide-bench = ./ghcide-bench; hls-graph = ./hls-graph; shake-bench = ./shake-bench; hie-compat = ./hie-compat; From a9f1c538ac163b5cb30f1aa258260563c90bd18d Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Thu, 4 Aug 2022 18:12:38 +0530 Subject: [PATCH 072/213] Remove exactprint dependencies from ghcide by introducing hls-refactor-plugin. All code actions have been moved to hls-refactor-plugin Mostly straightforward, only slight complication was that completion auto imports depends on exactprint, but I didn't want to remove all completion logic from ghcide just for this. Instead, I added logic to dynamically lookup the plugin that provides the extend import command, so that auto imports work as expected when you have hls-refactor-plugin enabled. Move lookupPluginId out of loop Replace code actions with lenses in benchmarks Allow plugins to use GetAnnotatedParsedSource by linking it in when depended on Move lookupPluginId to IdePlugins Add default extensions Move traceAst --- .github/workflows/test.yml | 4 + cabal.project | 1 + ghcide-bench/src/Experiments.hs | 14 +- ghcide/ghcide.cabal | 60 +- ghcide/src/Development/IDE/Core/Rules.hs | 5 - ghcide/src/Development/IDE/Core/Service.hs | 5 +- ghcide/src/Development/IDE/Core/Shake.hs | 6 +- ghcide/src/Development/IDE/GHC/Compat.hs | 2 - ghcide/src/Development/IDE/GHC/Orphans.hs | 8 - ghcide/src/Development/IDE/GHC/Util.hs | 32 - ghcide/src/Development/IDE/Main.hs | 5 +- .../src/Development/IDE/Plugin/Completions.hs | 81 +- .../IDE/Plugin/Completions/Logic.hs | 20 +- ghcide/src/Development/IDE/Plugin/HLS.hs | 2 +- .../src/Development/IDE/Plugin/HLS/GhcIde.hs | 5 - ghcide/test/exe/Main.hs | 3567 +--------------- haskell-language-server.cabal | 11 + .../Development/IDE/Graph/Internal/Rules.hs | 2 +- hls-plugin-api/src/Ide/PluginUtils.hs | 4 +- hls-plugin-api/src/Ide/Types.hs | 28 +- .../hls-code-range-plugin.cabal | 1 + .../src/Ide/Plugin/CodeRange.hs | 6 +- .../src/Ide/Plugin/CodeRange/Rules.hs | 3 +- plugins/hls-gadt-plugin/hls-gadt-plugin.cabal | 1 + plugins/hls-gadt-plugin/src/Ide/Plugin/GHC.hs | 1 + .../hls-haddock-comments-plugin.cabal | 1 + .../src/Ide/Plugin/HaddockComments.hs | 7 +- .../hls-haddock-comments-plugin/test/Main.hs | 2 +- plugins/hls-refactor-plugin/LICENSE | 201 + .../hls-refactor-plugin.cabal | 116 + .../Development/IDE/GHC/Compat/ExactPrint.hs | 4 - .../src/Development/IDE/GHC/Dump.hs | 1 + .../src/Development/IDE/GHC/ExactPrint.hs | 17 +- .../src/Development/IDE/Plugin/CodeAction.hs | 144 +- .../Development/IDE/Plugin/CodeAction/Args.hs | 4 +- .../IDE/Plugin/CodeAction/ExactPrint.hs | 12 +- .../IDE/Plugin/CodeAction/PositionIndexed.hs | 0 .../IDE/Plugin/CodeAction/RuleTypes.hs | 1 - .../Development/IDE/Plugin/CodeAction/Util.hs | 56 + plugins/hls-refactor-plugin/test/Main.hs | 3747 +++++++++++++++++ .../test/data/hiding/AVec.hs | 0 .../test/data/hiding/BVec.hs | 0 .../test/data/hiding/CVec.hs | 0 .../test/data/hiding/DVec.hs | 0 .../test/data/hiding/EVec.hs | 0 .../test/data/hiding/FVec.hs | 0 .../hiding/HideFunction.expected.append.E.hs | 0 .../HideFunction.expected.append.Prelude.hs | 0 .../HideFunction.expected.fromList.A.hs | 0 .../HideFunction.expected.fromList.B.hs | 0 ...ction.expected.qualified.append.Prelude.hs | 0 ...eFunction.expected.qualified.fromList.E.hs | 0 .../test/data/hiding/HideFunction.hs | 0 .../HideFunctionWithoutLocal.expected.hs | 0 .../data/hiding/HideFunctionWithoutLocal.hs | 0 .../hiding/HidePreludeIndented.expected.hs | 0 .../test/data/hiding/HidePreludeIndented.hs | 0 .../hiding/HidePreludeLocalInfix.expected.hs | 0 .../test/data/hiding/HidePreludeLocalInfix.hs | 0 ...deQualifyDuplicateRecordFields.expected.hs | 0 .../HideQualifyDuplicateRecordFields.hs | 0 .../HideQualifyDuplicateRecordFieldsSelf.hs | 0 .../data/hiding/HideQualifyInfix.expected.hs | 0 .../test/data/hiding/HideQualifyInfix.hs | 0 .../hiding/HideQualifySectionLeft.expected.hs | 0 .../data/hiding/HideQualifySectionLeft.hs | 0 .../HideQualifySectionRight.expected.hs | 0 .../data/hiding/HideQualifySectionRight.hs | 0 .../test/data/hiding/HideType.expected.A.hs | 0 .../test/data/hiding/HideType.expected.E.hs | 0 .../test/data/hiding/HideType.hs | 0 .../test/data/hiding/hie.yaml | 1 + .../test/data/hover/Bar.hs | 4 + .../test/data/hover/Foo.hs | 6 + .../test/data/hover/GotoHover.hs | 66 + .../test/data/hover/RecordDotSyntax.hs | 21 + .../test/data/hover/hie.yaml | 1 + .../import-placement/CommentAtTop.expected.hs | 0 .../data/import-placement/CommentAtTop.hs | 0 .../CommentAtTopMultipleComments.expected.hs | 0 .../CommentAtTopMultipleComments.hs | 0 .../CommentCurlyBraceAtTop.expected.hs | 0 .../CommentCurlyBraceAtTop.hs | 0 .../import-placement/DataAtTop.expected.hs | 0 .../test/data/import-placement/DataAtTop.hs | 0 .../import-placement/ImportAtTop.expected.hs | 0 .../test/data/import-placement/ImportAtTop.hs | 0 .../LangPragmaModuleAtTop.expected.hs | 0 .../import-placement/LangPragmaModuleAtTop.hs | 0 ...angPragmaModuleExplicitExports.expected.hs | 0 .../LangPragmaModuleExplicitExports.hs | 0 .../LangPragmaModuleWithComment.expected.hs | 0 .../LangPragmaModuleWithComment.hs | 0 .../LanguagePragmaAtTop.expected.hs | 0 .../import-placement/LanguagePragmaAtTop.hs | 0 ...LanguagePragmaAtTopWithComment.expected.hs | 0 .../LanguagePragmaAtTopWithComment.hs | 0 .../LanguagePragmasThenShebangs.expected.hs | 0 .../LanguagePragmasThenShebangs.hs | 0 .../ModuleDeclAndImports.expected.hs | 0 .../import-placement/ModuleDeclAndImports.hs | 0 .../MultiLineCommentAtTop.expected.hs | 0 .../import-placement/MultiLineCommentAtTop.hs | 0 .../MultiLinePragma.expected.hs | 0 .../data/import-placement/MultiLinePragma.hs | 0 .../MultipleImportsAtTop.expected.hs | 0 .../import-placement/MultipleImportsAtTop.hs | 0 ...uagePragmasNoModuleDeclaration.expected.hs | 0 ...tipleLanguagePragmasNoModuleDeclaration.hs | 0 .../import-placement/NewTypeAtTop.expected.hs | 0 .../data/import-placement/NewTypeAtTop.hs | 0 .../NoExplicitExportCommentAtTop.expected.hs | 0 .../NoExplicitExportCommentAtTop.hs | 0 .../NoExplicitExports.expected.hs | 0 .../import-placement/NoExplicitExports.hs | 0 .../NoModuleDeclaration.expected.hs | 0 .../import-placement/NoModuleDeclaration.hs | 0 ...oModuleDeclarationCommentAtTop.expected.hs | 0 .../NoModuleDeclarationCommentAtTop.hs | 0 .../OptionsNotAtTopWithSpaces.expected.hs | 0 .../OptionsNotAtTopWithSpaces.hs | 0 .../OptionsPragmaNotAtTop.expected.hs | 0 .../import-placement/OptionsPragmaNotAtTop.hs | 0 ...PragmaNotAtTopMultipleComments.expected.hs | 0 .../PragmaNotAtTopMultipleComments.hs | 0 ...ragmaNotAtTopWithCommentsAtTop.expected.hs | 0 .../PragmaNotAtTopWithCommentsAtTop.hs | 0 .../PragmaNotAtTopWithImports.expected.hs | 0 .../PragmaNotAtTopWithImports.hs | 0 .../PragmaNotAtTopWithModuleDecl.expected.hs | 0 .../PragmaNotAtTopWithModuleDecl.hs | 0 .../PragmasAndShebangsNoComment.expected.hs | 0 .../PragmasAndShebangsNoComment.hs | 0 .../PragmasShebangsAndModuleDecl.expected.hs | 0 .../PragmasShebangsAndModuleDecl.hs | 0 ...sShebangsModuleExplicitExports.expected.hs | 0 .../PragmasShebangsModuleExplicitExports.hs | 0 ...asThenShebangsMultilineComment.expected.hs | 0 .../PragmasThenShebangsMultilineComment.hs | 0 .../ShebangNotAtTop.expected.hs | 0 .../data/import-placement/ShebangNotAtTop.hs | 0 .../ShebangNotAtTopNoSpace.expected.hs | 0 .../ShebangNotAtTopNoSpace.hs | 0 .../ShebangNotAtTopWithSpaces.expected.hs | 0 .../ShebangNotAtTopWithSpaces.hs | 0 .../TwoDashOnlyComment.expected.hs | 0 .../import-placement/TwoDashOnlyComment.hs | 0 .../WhereDeclLowerInFile.expected.hs | 0 .../import-placement/WhereDeclLowerInFile.hs | 0 ...owerInFileWithCommentsBeforeIt.expected.hs | 0 ...hereDeclLowerInFileWithCommentsBeforeIt.hs | 0 ...ereKeywordLowerInFileNoExports.expected.hs | 0 .../WhereKeywordLowerInFileNoExports.hs | 0 .../hls-rename-plugin/hls-rename-plugin.cabal | 1 + .../src/Ide/Plugin/Rename.hs | 7 +- plugins/hls-rename-plugin/test/Main.hs | 2 +- .../hls-splice-plugin/hls-splice-plugin.cabal | 1 + .../src/Ide/Plugin/Splice.hs | 1 + .../hls-tactics-plugin.cabal | 1 + .../src/Wingman/LanguageServer.hs | 1 + .../hls-tactics-plugin/src/Wingman/Plugin.hs | 9 +- src/HlsPlugins.hs | 15 +- stack-lts16.yaml | 1 + stack-lts19.yaml | 1 + stack.yaml | 1 + 165 files changed, 4529 insertions(+), 3798 deletions(-) create mode 100644 plugins/hls-refactor-plugin/LICENSE create mode 100644 plugins/hls-refactor-plugin/hls-refactor-plugin.cabal rename {ghcide => plugins/hls-refactor-plugin}/src/Development/IDE/GHC/Compat/ExactPrint.hs (91%) rename {ghcide => plugins/hls-refactor-plugin}/src/Development/IDE/GHC/Dump.hs (99%) rename {ghcide => plugins/hls-refactor-plugin}/src/Development/IDE/GHC/ExactPrint.hs (98%) rename {ghcide => plugins/hls-refactor-plugin}/src/Development/IDE/Plugin/CodeAction.hs (93%) rename {ghcide => plugins/hls-refactor-plugin}/src/Development/IDE/Plugin/CodeAction/Args.hs (99%) rename {ghcide => plugins/hls-refactor-plugin}/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs (99%) rename {ghcide => plugins/hls-refactor-plugin}/src/Development/IDE/Plugin/CodeAction/PositionIndexed.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/src/Development/IDE/Plugin/CodeAction/RuleTypes.hs (96%) create mode 100644 plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Util.hs create mode 100644 plugins/hls-refactor-plugin/test/Main.hs rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/AVec.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/BVec.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/CVec.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/DVec.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/EVec.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/FVec.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideFunction.expected.append.E.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideFunction.expected.append.Prelude.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideFunction.expected.fromList.A.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideFunction.expected.fromList.B.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideFunction.expected.qualified.append.Prelude.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideFunction.expected.qualified.fromList.E.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideFunction.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideFunctionWithoutLocal.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideFunctionWithoutLocal.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HidePreludeIndented.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HidePreludeIndented.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HidePreludeLocalInfix.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HidePreludeLocalInfix.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideQualifyDuplicateRecordFields.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideQualifyDuplicateRecordFields.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideQualifyDuplicateRecordFieldsSelf.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideQualifyInfix.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideQualifyInfix.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideQualifySectionLeft.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideQualifySectionLeft.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideQualifySectionRight.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideQualifySectionRight.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideType.expected.A.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideType.expected.E.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/HideType.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/hiding/hie.yaml (90%) create mode 100644 plugins/hls-refactor-plugin/test/data/hover/Bar.hs create mode 100644 plugins/hls-refactor-plugin/test/data/hover/Foo.hs create mode 100644 plugins/hls-refactor-plugin/test/data/hover/GotoHover.hs create mode 100644 plugins/hls-refactor-plugin/test/data/hover/RecordDotSyntax.hs create mode 100644 plugins/hls-refactor-plugin/test/data/hover/hie.yaml rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/CommentAtTop.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/CommentAtTop.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/CommentAtTopMultipleComments.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/CommentAtTopMultipleComments.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/CommentCurlyBraceAtTop.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/CommentCurlyBraceAtTop.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/DataAtTop.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/DataAtTop.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/ImportAtTop.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/ImportAtTop.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/LangPragmaModuleAtTop.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/LangPragmaModuleAtTop.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/LangPragmaModuleExplicitExports.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/LangPragmaModuleExplicitExports.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/LangPragmaModuleWithComment.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/LangPragmaModuleWithComment.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/LanguagePragmaAtTop.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/LanguagePragmaAtTop.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/LanguagePragmaAtTopWithComment.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/LanguagePragmaAtTopWithComment.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/LanguagePragmasThenShebangs.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/LanguagePragmasThenShebangs.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/ModuleDeclAndImports.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/ModuleDeclAndImports.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/MultiLineCommentAtTop.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/MultiLineCommentAtTop.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/MultiLinePragma.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/MultiLinePragma.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/MultipleImportsAtTop.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/MultipleImportsAtTop.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/MultipleLanguagePragmasNoModuleDeclaration.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/MultipleLanguagePragmasNoModuleDeclaration.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/NewTypeAtTop.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/NewTypeAtTop.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/NoExplicitExportCommentAtTop.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/NoExplicitExportCommentAtTop.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/NoExplicitExports.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/NoExplicitExports.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/NoModuleDeclaration.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/NoModuleDeclaration.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/NoModuleDeclarationCommentAtTop.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/NoModuleDeclarationCommentAtTop.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/OptionsNotAtTopWithSpaces.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/OptionsNotAtTopWithSpaces.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/OptionsPragmaNotAtTop.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/OptionsPragmaNotAtTop.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/PragmaNotAtTopMultipleComments.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/PragmaNotAtTopMultipleComments.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/PragmaNotAtTopWithCommentsAtTop.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/PragmaNotAtTopWithCommentsAtTop.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/PragmaNotAtTopWithImports.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/PragmaNotAtTopWithImports.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/PragmaNotAtTopWithModuleDecl.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/PragmaNotAtTopWithModuleDecl.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/PragmasAndShebangsNoComment.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/PragmasAndShebangsNoComment.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/PragmasShebangsAndModuleDecl.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/PragmasShebangsAndModuleDecl.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/PragmasShebangsModuleExplicitExports.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/PragmasShebangsModuleExplicitExports.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/PragmasThenShebangsMultilineComment.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/PragmasThenShebangsMultilineComment.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/ShebangNotAtTop.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/ShebangNotAtTop.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/ShebangNotAtTopNoSpace.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/ShebangNotAtTopNoSpace.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/ShebangNotAtTopWithSpaces.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/ShebangNotAtTopWithSpaces.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/TwoDashOnlyComment.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/TwoDashOnlyComment.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/WhereDeclLowerInFile.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/WhereDeclLowerInFile.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/WhereKeywordLowerInFileNoExports.expected.hs (100%) rename {ghcide => plugins/hls-refactor-plugin}/test/data/import-placement/WhereKeywordLowerInFileNoExports.hs (100%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5b963a662d..4891398af9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -160,6 +160,10 @@ jobs: name: Test hls-brittany-plugin run: cabal test hls-brittany-plugin --test-options="$TEST_OPTS" || cabal test hls-brittany-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-brittany-plugin --test-options="$TEST_OPTS" + - if: matrix.test + name: Test hls-refactor-plugin + run: cabal test hls-refactor-plugin --test-options="$TEST_OPTS" || cabal test hls-refactor-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-refactor-plugin --test-options="$TEST_OPTS" + - if: matrix.test name: Test hls-floskell-plugin run: cabal test hls-floskell-plugin --test-options="$TEST_OPTS" || cabal test hls-floskell-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-floskell-plugin --test-options="$TEST_OPTS" diff --git a/cabal.project b/cabal.project index 047c2efcc2..91d017ccdf 100644 --- a/cabal.project +++ b/cabal.project @@ -32,6 +32,7 @@ packages: ./plugins/hls-stan-plugin ./plugins/hls-gadt-plugin ./plugins/hls-explicit-fixity-plugin + ./plugins/hls-refactor-plugin -- Standard location for temporary packages needed for particular environments -- For example it is used in the project gitlab mirror to help in the MAcOS M1 build script diff --git a/ghcide-bench/src/Experiments.hs b/ghcide-bench/src/Experiments.hs index 1d8d6f6c5b..2d756d73fd 100644 --- a/ghcide-bench/src/Experiments.hs +++ b/ghcide-bench/src/Experiments.hs @@ -139,7 +139,7 @@ experiments = not . null <$> getCompletions doc (fromJust identifierP), --------------------------------------------------------------------------------------- benchWithSetup - "code actions" + "code lens" ( \docs -> do unless (any (isJust . identifierP) docs) $ error "None of the example modules is suitable for this experiment" @@ -148,13 +148,12 @@ experiments = waitForProgressStart waitForProgressDone ) - ( \docs -> not . null . catMaybes <$> forM docs (\DocumentPositions{..} -> - forM identifierP $ \p -> - getCodeActions doc (Range p p)) + ( \docs -> not . null <$> forM docs (\DocumentPositions{..} -> + getCodeLenses doc) ), --------------------------------------------------------------------------------------- benchWithSetup - "code actions after edit" + "code lens after edit" ( \docs -> do unless (any (isJust . identifierP) docs) $ error "None of the example modules is suitable for this experiment" @@ -166,9 +165,8 @@ experiments = changeDoc doc [charEdit stringLiteralP] waitForProgressStart waitForProgressDone - not . null . catMaybes <$> forM docs (\DocumentPositions{..} -> do - forM identifierP $ \p -> - getCodeActions doc (Range p p)) + not . null <$> forM docs (\DocumentPositions{..} -> do + getCodeLenses doc) ), --------------------------------------------------------------------------------------- benchWithSetup diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index e3af7960ce..e3a7a89ca0 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -60,7 +60,6 @@ library filepath, fingertree, focus, - ghc-exactprint < 1 || >= 1.4, ghc-trace-events, Glob, haddock-library >= 1.8 && < 1.11, @@ -74,16 +73,13 @@ library lsp ^>= 1.5.0.0 , monoid-subclasses, mtl, - network-uri, optparse-applicative, parallel, prettyprinter-ansi-terminal, prettyprinter >= 1.6, random, regex-tdfa >= 1.3.1.0, - retrie, text-rope, - safe, safe-exceptions, hls-graph ^>= 1.7, sorted-list, @@ -95,9 +91,7 @@ library time, transformers, unordered-containers >= 0.2.10.0, - utf8-string, vector, - vector-algorithms, hslogger, Diff ^>=0.4.0, vector, @@ -114,9 +108,6 @@ library hie-bios ^>= 0.9.1, implicit-hie-cradle ^>= 0.3.0.5 || ^>= 0.5, base16-bytestring >=0.1.1 && <1.1 - if impl(ghc >= 9.2) - build-depends: - ghc-exactprint >= 1.4 if os(windows) build-depends: Win32 @@ -172,7 +163,6 @@ library Development.IDE.GHC.Compat Development.IDE.GHC.Compat.Core Development.IDE.GHC.Compat.Env - Development.IDE.GHC.Compat.ExactPrint Development.IDE.GHC.Compat.Iface Development.IDE.GHC.Compat.Logger Development.IDE.GHC.Compat.Outputable @@ -182,9 +172,7 @@ library Development.IDE.GHC.Compat.Util Development.IDE.Core.Compile Development.IDE.GHC.CoreFile - Development.IDE.GHC.Dump Development.IDE.GHC.Error - Development.IDE.GHC.ExactPrint Development.IDE.GHC.Orphans Development.IDE.GHC.Util Development.IDE.Import.DependencyInformation @@ -214,8 +202,6 @@ library Development.IDE.Plugin Development.IDE.Plugin.Completions Development.IDE.Plugin.Completions.Types - Development.IDE.Plugin.CodeAction - Development.IDE.Plugin.CodeAction.ExactPrint Development.IDE.Plugin.HLS Development.IDE.Plugin.HLS.GhcIde Development.IDE.Plugin.Test @@ -226,8 +212,6 @@ library Development.IDE.Core.FileExists Development.IDE.GHC.CPP Development.IDE.GHC.Warnings - Development.IDE.Plugin.CodeAction.PositionIndexed - Development.IDE.Plugin.CodeAction.Args Development.IDE.Plugin.Completions.Logic Development.IDE.Session.VersionCheck Development.IDE.Types.Action @@ -365,6 +349,7 @@ test-suite ghcide-tests ghc, -------------------------------------------------------------- ghcide, + ghcide-test-utils, ghc-typelits-knownnat, lsp, lsp-types, @@ -393,12 +378,10 @@ test-suite ghcide-tests build-depends: record-dot-preprocessor, record-hasfield - hs-source-dirs: test/cabal test/exe test/src bench/lib + hs-source-dirs: test/cabal test/exe bench/lib ghc-options: -threaded -Wall -Wno-name-shadowing -O0 -Wno-unticked-promoted-constructors main-is: Main.hs other-modules: - Development.IDE.Test - Development.IDE.Test.Diagnostic Development.IDE.Test.Runfiles FuzzySearch Progress @@ -418,3 +401,42 @@ test-suite ghcide-tests TupleSections TypeApplications ViewPatterns + +library ghcide-test-utils + visibility: public + default-language: Haskell2010 + build-depends: + aeson, + base, + containers, + data-default, + directory, + extra, + filepath, + ghcide, + lsp-types, + hls-plugin-api, + lens, + lsp-test ^>= 0.14, + tasty-hunit >= 0.10, + text, + hs-source-dirs: test/src + ghc-options: -Wunused-packages + exposed-modules: + Development.IDE.Test + Development.IDE.Test.Diagnostic + default-extensions: + BangPatterns + DeriveFunctor + DeriveGeneric + FlexibleContexts + GeneralizedNewtypeDeriving + LambdaCase + NamedFieldPuns + OverloadedStrings + RecordWildCards + ScopedTypeVariables + StandaloneDeriving + TupleSections + TypeApplications + ViewPatterns diff --git a/ghcide/src/Development/IDE/Core/Rules.hs b/ghcide/src/Development/IDE/Core/Rules.hs index e3f1c4aaa3..dfba6a32e7 100644 --- a/ghcide/src/Development/IDE/Core/Rules.hs +++ b/ghcide/src/Development/IDE/Core/Rules.hs @@ -119,7 +119,6 @@ import Development.IDE.GHC.Compat hiding import qualified Development.IDE.GHC.Compat as Compat hiding (vcat, nest) import qualified Development.IDE.GHC.Compat.Util as Util import Development.IDE.GHC.Error -import Development.IDE.GHC.ExactPrint hiding (LogShake, Log) import Development.IDE.GHC.Util hiding (modifyDynFlags) import Development.IDE.Graph @@ -154,7 +153,6 @@ import System.Info.Extra (isWindows) import HIE.Bios.Ghc.Gap (hostIsDynamic) import Development.IDE.Types.Logger (Recorder, logWith, cmapWithPrio, WithPriority, Pretty (pretty), (<+>), nest, vcat) import qualified Development.IDE.Core.Shake as Shake -import qualified Development.IDE.GHC.ExactPrint as ExactPrint hiding (LogShake) import qualified Development.IDE.Types.Logger as Logger import qualified Development.IDE.Types.Shake as Shake import Development.IDE.GHC.CoreFile @@ -167,7 +165,6 @@ data Log | LogLoadingHieFile !NormalizedFilePath | LogLoadingHieFileFail !FilePath !SomeException | LogLoadingHieFileSuccess !FilePath - | LogExactPrint ExactPrint.Log | LogTypecheckedFOI !NormalizedFilePath deriving Show @@ -185,7 +182,6 @@ instance Pretty Log where , pretty (displayException e) ] LogLoadingHieFileSuccess path -> "SUCCEEDED LOADING HIE FILE FOR" <+> pretty path - LogExactPrint log -> pretty log LogTypecheckedFOI path -> vcat [ "Typechecked a file which is not currently open in the editor:" <+> pretty (fromNormalizedFilePath path) , "This can indicate a bug which results in excessive memory usage." @@ -1230,7 +1226,6 @@ mainRule recorder RulesConfig{..} = do else defineNoDiagnostics (cmapWithPrio LogShake recorder) $ \NeedsCompilation _ -> return $ Just Nothing generateCoreRule recorder getImportMapRule recorder - getAnnotatedParsedSourceRule (cmapWithPrio LogExactPrint recorder) persistentHieFileRule recorder persistentDocMapRule persistentImportMapRule diff --git a/ghcide/src/Development/IDE/Core/Service.hs b/ghcide/src/Development/IDE/Core/Service.hs index 8ef090e84e..9118dc68d7 100644 --- a/ghcide/src/Development/IDE/Core/Service.hs +++ b/ghcide/src/Development/IDE/Core/Service.hs @@ -41,6 +41,7 @@ import Development.IDE.Core.Shake hiding (Log) import qualified Development.IDE.Core.Shake as Shake import Development.IDE.Types.Monitoring (Monitoring) import Development.IDE.Types.Shake (WithHieDb) +import Ide.Types (IdePlugins) import System.Environment (lookupEnv) data Log @@ -61,6 +62,7 @@ instance Pretty Log where -- | Initialise the Compiler Service. initialise :: Recorder (WithPriority Log) -> Config + -> IdePlugins IdeState -> Rules () -> Maybe (LSP.LanguageContextEnv Config) -> Logger @@ -70,7 +72,7 @@ initialise :: Recorder (WithPriority Log) -> IndexQueue -> Monitoring -> IO IdeState -initialise recorder defaultConfig mainRule lspEnv logger debouncer options withHieDb hiedbChan metrics = do +initialise recorder defaultConfig plugins mainRule lspEnv logger debouncer options withHieDb hiedbChan metrics = do shakeProfiling <- do let fromConf = optShakeProfiling options fromEnv <- lookupEnv "GHCIDE_BUILD_PROFILING" @@ -79,6 +81,7 @@ initialise recorder defaultConfig mainRule lspEnv logger debouncer options withH (cmapWithPrio LogShake recorder) lspEnv defaultConfig + plugins logger debouncer shakeProfiling diff --git a/ghcide/src/Development/IDE/Core/Shake.hs b/ghcide/src/Development/IDE/Core/Shake.hs index d341838e0a..211c5468a2 100644 --- a/ghcide/src/Development/IDE/Core/Shake.hs +++ b/ghcide/src/Development/IDE/Core/Shake.hs @@ -158,7 +158,7 @@ import GHC.Stack (HasCallStack) import HieDb.Types import Ide.Plugin.Config import qualified Ide.PluginUtils as HLS -import Ide.Types (PluginId) +import Ide.Types (PluginId, IdePlugins) import Language.LSP.Diagnostics import qualified Language.LSP.Server as LSP import Language.LSP.Types @@ -239,6 +239,7 @@ data ShakeExtras = ShakeExtras lspEnv :: Maybe (LSP.LanguageContextEnv Config) ,debouncer :: Debouncer NormalizedUri ,logger :: Logger + ,idePlugins :: IdePlugins IdeState ,globals :: TVar (HMap.HashMap TypeRep Dynamic) -- ^ Registry of global state used by rules. -- Small and immutable after startup, so not worth using an STM.Map. @@ -552,6 +553,7 @@ seqValue val = case val of shakeOpen :: Recorder (WithPriority Log) -> Maybe (LSP.LanguageContextEnv Config) -> Config + -> IdePlugins IdeState -> Logger -> Debouncer NormalizedUri -> Maybe FilePath @@ -563,7 +565,7 @@ shakeOpen :: Recorder (WithPriority Log) -> Monitoring -> Rules () -> IO IdeState -shakeOpen recorder lspEnv defaultConfig logger debouncer +shakeOpen recorder lspEnv defaultConfig idePlugins logger debouncer shakeProfileDir (IdeReportProgress reportProgress) ideTesting@(IdeTesting testing) withHieDb indexQueue opts monitoring rules = mdo diff --git a/ghcide/src/Development/IDE/GHC/Compat.hs b/ghcide/src/Development/IDE/GHC/Compat.hs index 8bb3c5fd1c..43cb524256 100644 --- a/ghcide/src/Development/IDE/GHC/Compat.hs +++ b/ghcide/src/Development/IDE/GHC/Compat.hs @@ -63,7 +63,6 @@ module Development.IDE.GHC.Compat( -- * Compat modules module Development.IDE.GHC.Compat.Core, module Development.IDE.GHC.Compat.Env, - module Development.IDE.GHC.Compat.ExactPrint, module Development.IDE.GHC.Compat.Iface, module Development.IDE.GHC.Compat.Logger, module Development.IDE.GHC.Compat.Outputable, @@ -119,7 +118,6 @@ module Development.IDE.GHC.Compat( import Data.Bifunctor import Development.IDE.GHC.Compat.Core import Development.IDE.GHC.Compat.Env -import Development.IDE.GHC.Compat.ExactPrint import Development.IDE.GHC.Compat.Iface import Development.IDE.GHC.Compat.Logger import Development.IDE.GHC.Compat.Outputable diff --git a/ghcide/src/Development/IDE/GHC/Orphans.hs b/ghcide/src/Development/IDE/GHC/Orphans.hs index 9ec6bfb5b8..3d506fbe4a 100644 --- a/ghcide/src/Development/IDE/GHC/Orphans.hs +++ b/ghcide/src/Development/IDE/GHC/Orphans.hs @@ -29,8 +29,6 @@ import Unique (getKey) #endif -import Retrie.ExactPrint (Annotated) - import Development.IDE.GHC.Compat import Development.IDE.GHC.Util @@ -195,12 +193,6 @@ instance NFData ModGuts where instance NFData (ImportDecl GhcPs) where rnf = rwhnf -instance Show (Annotated ParsedSource) where - show _ = "" - -instance NFData (Annotated ParsedSource) where - rnf = rwhnf - #if MIN_VERSION_ghc(9,0,1) instance (NFData HsModule) where #else diff --git a/ghcide/src/Development/IDE/GHC/Util.hs b/ghcide/src/Development/IDE/GHC/Util.hs index 0ddd12faf6..4776626aa6 100644 --- a/ghcide/src/Development/IDE/GHC/Util.hs +++ b/ghcide/src/Development/IDE/GHC/Util.hs @@ -26,7 +26,6 @@ module Development.IDE.GHC.Util( setHieDir, dontWriteHieFiles, disableWarningsAsErrors, - traceAst, printOutputable ) where @@ -70,7 +69,6 @@ import Debug.Trace import Development.IDE.GHC.Compat as GHC import qualified Development.IDE.GHC.Compat.Parser as Compat import qualified Development.IDE.GHC.Compat.Units as Compat -import Development.IDE.GHC.Dump (showAstDataHtml) import Development.IDE.Types.Location import Foreign.ForeignPtr import Foreign.Ptr @@ -281,36 +279,6 @@ ioe_dupHandlesNotCompatible h = -------------------------------------------------------------------------------- -- Tracing exactprint terms -{-# NOINLINE timestamp #-} -timestamp :: POSIXTime -timestamp = utcTimeToPOSIXSeconds $ unsafePerformIO getCurrentTime - -debugAST :: Bool -debugAST = unsafePerformIO (getEnvDefault "GHCIDE_DEBUG_AST" "0") == "1" - --- | Prints an 'Outputable' value to stderr and to an HTML file for further inspection -traceAst :: (Data a, ExactPrint a, Outputable a, HasCallStack) => String -> a -> a -traceAst lbl x - | debugAST = trace doTrace x - | otherwise = x - where -#if MIN_VERSION_ghc(9,2,0) - renderDump = renderWithContext defaultSDocContext{sdocStyle = defaultDumpStyle, sdocPprDebug = True} -#else - renderDump = showSDocUnsafe . ppr -#endif - htmlDump = showAstDataHtml x - doTrace = unsafePerformIO $ do - u <- U.newUnique - let htmlDumpFileName = printf "/tmp/hls/%s-%s-%d.html" (show timestamp) lbl (U.hashUnique u) - writeFile htmlDumpFileName $ renderDump htmlDump - return $ unlines - [prettyCallStack callStack ++ ":" -#if MIN_VERSION_ghc(9,2,0) - , exactPrint x -#endif - , "file://" ++ htmlDumpFileName] - -- Should in `Development.IDE.GHC.Orphans`, -- leave it here to prevent cyclic module dependency #if !MIN_VERSION_ghc(8,10,0) diff --git a/ghcide/src/Development/IDE/Main.hs b/ghcide/src/Development/IDE/Main.hs index d5e19856a7..d342b1bb5d 100644 --- a/ghcide/src/Development/IDE/Main.hs +++ b/ghcide/src/Development/IDE/Main.hs @@ -378,6 +378,7 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re initialise (cmapWithPrio LogService recorder) argsDefaultHlsConfig + argsHlsPlugins rules (Just env) logger @@ -422,7 +423,7 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re , optCheckProject = pure False , optModifyDynFlags = optModifyDynFlags def_options <> pluginModifyDynflags plugins } - ide <- initialise (cmapWithPrio LogService recorder) argsDefaultHlsConfig rules Nothing logger debouncer options hiedb hieChan mempty + ide <- initialise (cmapWithPrio LogService recorder) argsDefaultHlsConfig argsHlsPlugins rules Nothing logger debouncer options hiedb hieChan mempty shakeSessionInit (cmapWithPrio LogShake recorder) ide registerIdeConfiguration (shakeExtras ide) $ IdeConfiguration mempty (hashed Nothing) @@ -475,7 +476,7 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re , optCheckProject = pure False , optModifyDynFlags = optModifyDynFlags def_options <> pluginModifyDynflags plugins } - ide <- initialise (cmapWithPrio LogService recorder) argsDefaultHlsConfig rules Nothing logger debouncer options hiedb hieChan mempty + ide <- initialise (cmapWithPrio LogService recorder) argsDefaultHlsConfig argsHlsPlugins rules Nothing logger debouncer options hiedb hieChan mempty shakeSessionInit (cmapWithPrio LogShake recorder) ide registerIdeConfiguration (shakeExtras ide) $ IdeConfiguration mempty (hashed Nothing) c ide diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index 4e5e96a3bd..8e95614a27 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -27,12 +27,8 @@ import Development.IDE.Core.Shake hiding (Log) import qualified Development.IDE.Core.Shake as Shake import Development.IDE.GHC.Compat import Development.IDE.GHC.Error (rangeToSrcSpan) -import Development.IDE.GHC.ExactPrint (GetAnnotatedParsedSource (GetAnnotatedParsedSource)) import Development.IDE.GHC.Util (printOutputable) import Development.IDE.Graph -import Development.IDE.Plugin.CodeAction (newImport, - newImportToEdit) -import Development.IDE.Plugin.CodeAction.ExactPrint import Development.IDE.Plugin.Completions.Logic import Development.IDE.Plugin.Completions.Types import Development.IDE.Types.Exports @@ -66,7 +62,6 @@ descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeSta descriptor recorder plId = (defaultPluginDescriptor plId) { pluginRules = produceCompletions recorder , pluginHandlers = mkPluginHandler STextDocumentCompletion getCompletionsLSP - , pluginCommands = [extendImportCommand] , pluginConfigDescriptor = defaultConfigDescriptor {configCustomConfig = mkCustomConfig properties} , pluginPriority = ghcideCompletionsPluginPriority } @@ -155,8 +150,9 @@ getCompletionsLSP ide plId -> return (InL $ List []) (Just pfix', _) -> do let clientCaps = clientCapabilities $ shakeExtras ide + plugins = idePlugins $ shakeExtras ide config <- getCompletionsConfig plId - allCompletions <- liftIO $ getCompletions plId ideOpts cci' parsedMod bindMap pfix' clientCaps config moduleExports + allCompletions <- liftIO $ getCompletions plugins ideOpts cci' parsedMod bindMap pfix' clientCaps config moduleExports pure $ InL (List $ orderedCompletions allCompletions) _ -> return (InL $ List []) _ -> return (InL $ List []) @@ -201,76 +197,3 @@ toModueNameText :: KT.Target -> T.Text toModueNameText target = case target of KT.TargetModule m -> T.pack $ moduleNameString m _ -> T.empty - -extendImportCommand :: PluginCommand IdeState -extendImportCommand = - PluginCommand (CommandId extendImportCommandId) "additional edits for a completion" extendImportHandler - -extendImportHandler :: CommandFunction IdeState ExtendImport -extendImportHandler ideState edit@ExtendImport {..} = do - res <- liftIO $ runMaybeT $ extendImportHandler' ideState edit - whenJust res $ \(nfp, wedit@WorkspaceEdit {_changes}) -> do - let (_, List (head -> TextEdit {_range})) = fromJust $ _changes >>= listToMaybe . toList - srcSpan = rangeToSrcSpan nfp _range - LSP.sendNotification SWindowShowMessage $ - ShowMessageParams MtInfo $ - "Import " - <> maybe ("‘" <> newThing) (\x -> "‘" <> x <> " (" <> newThing <> ")") thingParent - <> "’ from " - <> importName - <> " (at " - <> printOutputable srcSpan - <> ")" - void $ LSP.sendRequest SWorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing wedit) (\_ -> pure ()) - return $ Right Null - -extendImportHandler' :: IdeState -> ExtendImport -> MaybeT IO (NormalizedFilePath, WorkspaceEdit) -extendImportHandler' ideState ExtendImport {..} - | Just fp <- uriToFilePath doc, - nfp <- toNormalizedFilePath' fp = - do - (ModSummaryResult {..}, ps, contents) <- MaybeT $ liftIO $ - runAction "extend import" ideState $ - runMaybeT $ do - -- We want accurate edits, so do not use stale data here - msr <- MaybeT $ use GetModSummaryWithoutTimestamps nfp - ps <- MaybeT $ use GetAnnotatedParsedSource nfp - (_, contents) <- MaybeT $ use GetFileContents nfp - return (msr, ps, contents) - let df = ms_hspp_opts msrModSummary - wantedModule = mkModuleName (T.unpack importName) - wantedQual = mkModuleName . T.unpack <$> importQual - existingImport = find (isWantedModule wantedModule wantedQual) msrImports - case existingImport of - Just imp -> do - fmap (nfp,) $ liftEither $ - rewriteToWEdit df doc -#if !MIN_VERSION_ghc(9,2,0) - (annsA ps) -#endif - $ - extendImport (T.unpack <$> thingParent) (T.unpack newThing) (makeDeltaAst imp) - Nothing -> do - let n = newImport importName sym importQual False - sym = if isNothing importQual then Just it else Nothing - it = case thingParent of - Nothing -> newThing - Just p -> p <> "(" <> newThing <> ")" - t <- liftMaybe $ snd <$> newImportToEdit n ps (fromMaybe "" contents) - return (nfp, WorkspaceEdit {_changes=Just (fromList [(doc,List [t])]), _documentChanges=Nothing, _changeAnnotations=Nothing}) - | otherwise = - mzero - -isWantedModule :: ModuleName -> Maybe ModuleName -> GenLocated l (ImportDecl GhcPs) -> Bool -isWantedModule wantedModule Nothing (L _ it@ImportDecl{ideclName, ideclHiding = Just (False, _)}) = - not (isQualifiedImport it) && unLoc ideclName == wantedModule -isWantedModule wantedModule (Just qual) (L _ ImportDecl{ideclAs, ideclName, ideclHiding = Just (False, _)}) = - unLoc ideclName == wantedModule && (wantedModule == qual || (unLoc . reLoc <$> ideclAs) == Just qual) -isWantedModule _ _ _ = False - -liftMaybe :: Monad m => Maybe a -> MaybeT m a -liftMaybe a = MaybeT $ pure a - -liftEither :: Monad m => Either e a -> MaybeT m a -liftEither (Left _) = mzero -liftEither (Right x) = return x diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index 2269cb3914..c703ec7a66 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -59,7 +59,7 @@ import GHC.Plugins (Depth (AllTheWay), #endif import Ide.PluginUtils (mkLspCommand) import Ide.Types (CommandId (..), - PluginId) + IdePlugins(..), PluginId) import Language.LSP.Types import Language.LSP.Types.Capabilities import qualified Language.LSP.VFS as VFS @@ -161,7 +161,8 @@ occNameToComKind ty oc showModName :: ModuleName -> T.Text showModName = T.pack . moduleNameString -mkCompl :: PluginId -> IdeOptions -> CompItem -> CompletionItem +mkCompl :: Maybe PluginId -- ^ Plugin to use for the extend import command + -> IdeOptions -> CompItem -> CompletionItem mkCompl pId IdeOptions {..} @@ -175,7 +176,7 @@ mkCompl docs, additionalTextEdits } = do - let mbCommand = mkAdditionalEditsCommand pId `fmap` additionalTextEdits + let mbCommand = mkAdditionalEditsCommand pId =<< additionalTextEdits let ci = CompletionItem {_label = label, _kind = kind, @@ -217,9 +218,9 @@ mkCompl "line " <> printOutputable (srcLocLine loc) <> ", column " <> printOutputable (srcLocCol loc) -mkAdditionalEditsCommand :: PluginId -> ExtendImport -> Command -mkAdditionalEditsCommand pId edits = - mkLspCommand pId (CommandId extendImportCommandId) "extend import" (Just [toJSON edits]) +mkAdditionalEditsCommand :: Maybe PluginId -> ExtendImport -> Maybe Command +mkAdditionalEditsCommand (Just pId) edits = Just $ mkLspCommand pId (CommandId extendImportCommandId) "extend import" (Just [toJSON edits]) +mkAdditionalEditsCommand _ _ = Nothing mkNameCompItem :: Uri -> Maybe T.Text -> OccName -> Provenance -> Maybe Type -> Maybe Backtick -> SpanDoc -> Maybe (LImportDecl GhcPs) -> CompItem mkNameCompItem doc thingParent origName provenance thingType isInfix docs !imp = CI {..} @@ -553,7 +554,7 @@ removeSnippetsWhen condition x = -- | Returns the cached completions for the given module and position. getCompletions - :: PluginId + :: IdePlugins a -> IdeOptions -> CachedCompletions -> Maybe (ParsedModule, PositionMapping) @@ -563,7 +564,7 @@ getCompletions -> CompletionsConfig -> HM.HashMap T.Text (HashSet.HashSet IdentInfo) -> IO [Scored CompletionItem] -getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qualCompls, importableModules} +getCompletions plugins ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qualCompls, importableModules} maybe_parsed (localBindings, bmapping) prefixInfo caps config moduleExportsMap = do let VFS.PosPrefixInfo { fullLine, prefixModule, prefixText } = prefixInfo enteredQual = if T.null prefixModule then "" else prefixModule <> "." @@ -663,7 +664,8 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu | otherwise -> do -- assumes that nubOrdBy is stable let uniqueFiltCompls = nubOrdBy (uniqueCompl `on` snd . Fuzzy.original) filtCompls - let compls = (fmap.fmap.fmap) (mkCompl plId ideOpts) uniqueFiltCompls + let compls = (fmap.fmap.fmap) (mkCompl pId ideOpts) uniqueFiltCompls + pId = lookupCommandProvider plugins (CommandId extendImportCommandId) return $ (fmap.fmap) snd $ sortBy (compare `on` lexicographicOrdering) $ diff --git a/ghcide/src/Development/IDE/Plugin/HLS.hs b/ghcide/src/Development/IDE/Plugin/HLS.hs index ea30eb2c53..8749a9efda 100644 --- a/ghcide/src/Development/IDE/Plugin/HLS.hs +++ b/ghcide/src/Development/IDE/Plugin/HLS.hs @@ -127,7 +127,7 @@ executeCommandPlugins recorder ecs = mempty { P.pluginHandlers = executeCommandH executeCommandHandlers :: Recorder (WithPriority Log) -> [(PluginId, [PluginCommand IdeState])] -> LSP.Handlers (ServerM Config) executeCommandHandlers recorder ecs = requestHandler SWorkspaceExecuteCommand execCmd where - pluginMap = Map.fromList ecs + pluginMap = Map.fromListWith (++) ecs parseCmdId :: T.Text -> Maybe (PluginId, CommandId) parseCmdId x = case T.splitOn ":" x of diff --git a/ghcide/src/Development/IDE/Plugin/HLS/GhcIde.hs b/ghcide/src/Development/IDE/Plugin/HLS/GhcIde.hs index 83f1f07130..7e6d924d16 100644 --- a/ghcide/src/Development/IDE/Plugin/HLS/GhcIde.hs +++ b/ghcide/src/Development/IDE/Plugin/HLS/GhcIde.hs @@ -12,7 +12,6 @@ import Development.IDE import Development.IDE.LSP.HoverDefinition import qualified Development.IDE.LSP.Notifications as Notifications import Development.IDE.LSP.Outline -import qualified Development.IDE.Plugin.CodeAction as CodeAction import qualified Development.IDE.Plugin.Completions as Completions import qualified Development.IDE.Plugin.TypeLenses as TypeLenses import Ide.Types @@ -35,10 +34,6 @@ instance Pretty Log where descriptors :: Recorder (WithPriority Log) -> [PluginDescriptor IdeState] descriptors recorder = [ descriptor "ghcide-hover-and-symbols", - CodeAction.iePluginDescriptor "ghcide-code-actions-imports-exports", - CodeAction.typeSigsPluginDescriptor "ghcide-code-actions-type-signatures", - CodeAction.bindingsPluginDescriptor "ghcide-code-actions-bindings", - CodeAction.fillHolePluginDescriptor "ghcide-code-actions-fill-holes", Completions.descriptor (cmapWithPrio LogCompletions recorder) "ghcide-completions", TypeLenses.descriptor (cmapWithPrio LogTypeLenses recorder) "ghcide-type-lenses", Notifications.descriptor (cmapWithPrio LogNotifications recorder) "ghcide-core" diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 787e6941c4..12fce8ce67 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -40,7 +40,6 @@ import Development.IDE.GHC.Compat (GhcVersion (..), ghcVersion) import Development.IDE.GHC.Util import qualified Development.IDE.Main as IDE -import Development.IDE.Plugin.Completions.Types (extendImportCommandId) import Development.IDE.Plugin.TypeLenses (typeLensCommandId) import Development.IDE.Spans.Common import Development.IDE.Test (Cursor, @@ -50,7 +49,6 @@ import Development.IDE.Test (Cursor, expectCurrentDiagnostics, expectDiagnostics, expectDiagnosticsWithTags, - expectMessages, expectNoMoreDiagnostics, flushMessages, getInterfaceFilesDir, @@ -103,7 +101,6 @@ import Data.IORef.Extra (atomicModifyIORef_) import Data.String (IsString (fromString)) import Data.Tuple.Extra import Development.IDE.Core.FileStore (getModTime) -import Development.IDE.Plugin.CodeAction (matchRegExMultipleImports) import qualified Development.IDE.Plugin.HLS.GhcIde as Ghcide import Development.IDE.Plugin.Test (TestRequest (BlockSeconds), WaitForIdeRuleResult (..), @@ -198,7 +195,6 @@ main = do waitForProgressBegin closeDoc doc waitForProgressDone - , codeActionTests , initializeResponseTests , completionTests , cppTests @@ -225,7 +221,6 @@ main = do , rootUriTests , asyncTests , clientSettingsTest - , codeActionHelperFunctionTests , referenceTests , garbageCollectionTests , HieDbRetry.tests @@ -255,7 +250,7 @@ initializeResponseTests = withResource acquire release tests where , chk " doc highlight" _documentHighlightProvider (Just $ InL True) , chk " doc symbol" _documentSymbolProvider (Just $ InL True) , chk " workspace symbol" _workspaceSymbolProvider (Just $ InL True) - , chk " code action" _codeActionProvider (Just $ InL True) + , chk " code action" _codeActionProvider (Just $ InL False) , chk " code lens" _codeLensProvider (Just $ CodeLensOptions (Just False) (Just False)) , chk "NO doc formatting" _documentFormattingProvider (Just $ InL False) , chk "NO doc range formatting" @@ -266,7 +261,7 @@ initializeResponseTests = withResource acquire release tests where , chk "NO doc link" _documentLinkProvider Nothing , chk "NO color" _colorProvider (Just $ InL False) , chk "NO folding range" _foldingRangeProvider (Just $ InL False) - , che " execute command" _executeCommandProvider [extendImportCommandId, typeLensCommandId, blockCommandId] + , che " execute command" _executeCommandProvider [typeLensCommandId, blockCommandId] , chk " workspace" _workspace (Just $ WorkspaceServerCapabilities (Just WorkspaceFoldersServerCapabilities{_supported = Just True, _changeNotifications = Just ( InR True )})) , chk "NO experimental" _experimental Nothing ] where @@ -817,40 +812,6 @@ cancellationTemplate (edit, undoEdit) mbKey = testCase (maybe "-" fst mbKey) $ r -- flush messages to ensure current diagnostics state is updated flushMessages -codeActionTests :: TestTree -codeActionTests = testGroup "code actions" - [ insertImportTests - , extendImportTests - , renameActionTests - , typeWildCardActionTests - , removeImportTests - , suggestImportClassMethodTests - , suggestImportTests - , suggestHideShadowTests - , suggestImportDisambiguationTests - , fixConstructorImportTests - , fixModuleImportTypoTests - , importRenameActionTests - , fillTypedHoleTests - , addSigActionTests - , insertNewDefinitionTests - , deleteUnusedDefinitionTests - , addInstanceConstraintTests - , addFunctionConstraintTests - , removeRedundantConstraintsTests - , addTypeAnnotationsToLiteralsTest - , exportUnusedTests - , addImplicitParamsConstraintTests - , removeExportTests - ] - -codeActionHelperFunctionTests :: TestTree -codeActionHelperFunctionTests = testGroup "code action helpers" - [ - extendImportTestsRegEx - ] - - codeLensesTests :: TestTree codeLensesTests = testGroup "code lenses" [ addSigLensesTests @@ -905,3291 +866,6 @@ watchedFilesTests = testGroup "watched files" ] ] -insertImportTests :: TestTree -insertImportTests = testGroup "insert import" - [ checkImport - "module where keyword lower in file no exports" - "WhereKeywordLowerInFileNoExports.hs" - "WhereKeywordLowerInFileNoExports.expected.hs" - "import Data.Int" - , checkImport - "module where keyword lower in file with exports" - "WhereDeclLowerInFile.hs" - "WhereDeclLowerInFile.expected.hs" - "import Data.Int" - , checkImport - "module where keyword lower in file with comments before it" - "WhereDeclLowerInFileWithCommentsBeforeIt.hs" - "WhereDeclLowerInFileWithCommentsBeforeIt.expected.hs" - "import Data.Int" - , expectFailBecause - "'findNextPragmaPosition' function doesn't account for case when shebang is not placed at top of file" - (checkImport - "Shebang not at top with spaces" - "ShebangNotAtTopWithSpaces.hs" - "ShebangNotAtTopWithSpaces.expected.hs" - "import Data.Monoid") - , expectFailBecause - "'findNextPragmaPosition' function doesn't account for case when shebang is not placed at top of file" - (checkImport - "Shebang not at top no space" - "ShebangNotAtTopNoSpace.hs" - "ShebangNotAtTopNoSpace.expected.hs" - "import Data.Monoid") - , expectFailBecause - ("'findNextPragmaPosition' function doesn't account for case " - ++ "when OPTIONS_GHC pragma is not placed at top of file") - (checkImport - "OPTIONS_GHC pragma not at top with spaces" - "OptionsNotAtTopWithSpaces.hs" - "OptionsNotAtTopWithSpaces.expected.hs" - "import Data.Monoid") - , expectFailBecause - ("'findNextPragmaPosition' function doesn't account for " - ++ "case when shebang is not placed at top of file") - (checkImport - "Shebang not at top of file" - "ShebangNotAtTop.hs" - "ShebangNotAtTop.expected.hs" - "import Data.Monoid") - , expectFailBecause - ("'findNextPragmaPosition' function doesn't account for case " - ++ "when OPTIONS_GHC is not placed at top of file") - (checkImport - "OPTIONS_GHC pragma not at top of file" - "OptionsPragmaNotAtTop.hs" - "OptionsPragmaNotAtTop.expected.hs" - "import Data.Monoid") - , expectFailBecause - ("'findNextPragmaPosition' function doesn't account for case when " - ++ "OPTIONS_GHC pragma is not placed at top of file") - (checkImport - "pragma not at top with comment at top" - "PragmaNotAtTopWithCommentsAtTop.hs" - "PragmaNotAtTopWithCommentsAtTop.expected.hs" - "import Data.Monoid") - , expectFailBecause - ("'findNextPragmaPosition' function doesn't account for case when " - ++ "OPTIONS_GHC pragma is not placed at top of file") - (checkImport - "pragma not at top multiple comments" - "PragmaNotAtTopMultipleComments.hs" - "PragmaNotAtTopMultipleComments.expected.hs" - "import Data.Monoid") - , expectFailBecause - "'findNextPragmaPosition' function doesn't account for case of multiline pragmas" - (checkImport - "after multiline language pragmas" - "MultiLinePragma.hs" - "MultiLinePragma.expected.hs" - "import Data.Monoid") - , checkImport - "pragmas not at top with module declaration" - "PragmaNotAtTopWithModuleDecl.hs" - "PragmaNotAtTopWithModuleDecl.expected.hs" - "import Data.Monoid" - , checkImport - "pragmas not at top with imports" - "PragmaNotAtTopWithImports.hs" - "PragmaNotAtTopWithImports.expected.hs" - "import Data.Monoid" - , checkImport - "above comment at top of module" - "CommentAtTop.hs" - "CommentAtTop.expected.hs" - "import Data.Monoid" - , checkImport - "above multiple comments below" - "CommentAtTopMultipleComments.hs" - "CommentAtTopMultipleComments.expected.hs" - "import Data.Monoid" - , checkImport - "above curly brace comment" - "CommentCurlyBraceAtTop.hs" - "CommentCurlyBraceAtTop.expected.hs" - "import Data.Monoid" - , checkImport - "above multi-line comment" - "MultiLineCommentAtTop.hs" - "MultiLineCommentAtTop.expected.hs" - "import Data.Monoid" - , checkImport - "above comment with no module explicit exports" - "NoExplicitExportCommentAtTop.hs" - "NoExplicitExportCommentAtTop.expected.hs" - "import Data.Monoid" - , checkImport - "above two-dash comment with no pipe" - "TwoDashOnlyComment.hs" - "TwoDashOnlyComment.expected.hs" - "import Data.Monoid" - , checkImport - "above comment with no (module .. where) decl" - "NoModuleDeclarationCommentAtTop.hs" - "NoModuleDeclarationCommentAtTop.expected.hs" - "import Data.Monoid" - , checkImport - "comment not at top with no (module .. where) decl" - "NoModuleDeclaration.hs" - "NoModuleDeclaration.expected.hs" - "import Data.Monoid" - , checkImport - "comment not at top (data dec is)" - "DataAtTop.hs" - "DataAtTop.expected.hs" - "import Data.Monoid" - , checkImport - "comment not at top (newtype is)" - "NewTypeAtTop.hs" - "NewTypeAtTop.expected.hs" - "import Data.Monoid" - , checkImport - "with no explicit module exports" - "NoExplicitExports.hs" - "NoExplicitExports.expected.hs" - "import Data.Monoid" - , checkImport - "add to correctly placed exisiting import" - "ImportAtTop.hs" - "ImportAtTop.expected.hs" - "import Data.Monoid" - , checkImport - "add to multiple correctly placed exisiting imports" - "MultipleImportsAtTop.hs" - "MultipleImportsAtTop.expected.hs" - "import Data.Monoid" - , checkImport - "with language pragma at top of module" - "LangPragmaModuleAtTop.hs" - "LangPragmaModuleAtTop.expected.hs" - "import Data.Monoid" - , checkImport - "with language pragma and explicit module exports" - "LangPragmaModuleWithComment.hs" - "LangPragmaModuleWithComment.expected.hs" - "import Data.Monoid" - , checkImport - "with language pragma at top and no module declaration" - "LanguagePragmaAtTop.hs" - "LanguagePragmaAtTop.expected.hs" - "import Data.Monoid" - , checkImport - "with multiple lang pragmas and no module declaration" - "MultipleLanguagePragmasNoModuleDeclaration.hs" - "MultipleLanguagePragmasNoModuleDeclaration.expected.hs" - "import Data.Monoid" - , checkImport - "with pragmas and shebangs" - "LanguagePragmasThenShebangs.hs" - "LanguagePragmasThenShebangs.expected.hs" - "import Data.Monoid" - , checkImport - "with pragmas and shebangs but no comment at top" - "PragmasAndShebangsNoComment.hs" - "PragmasAndShebangsNoComment.expected.hs" - "import Data.Monoid" - , checkImport - "module decl no exports under pragmas and shebangs" - "PragmasShebangsAndModuleDecl.hs" - "PragmasShebangsAndModuleDecl.expected.hs" - "import Data.Monoid" - , checkImport - "module decl with explicit import under pragmas and shebangs" - "PragmasShebangsModuleExplicitExports.hs" - "PragmasShebangsModuleExplicitExports.expected.hs" - "import Data.Monoid" - , checkImport - "module decl and multiple imports" - "ModuleDeclAndImports.hs" - "ModuleDeclAndImports.expected.hs" - "import Data.Monoid" - ] - -checkImport :: String -> FilePath -> FilePath -> T.Text -> TestTree -checkImport testComment originalPath expectedPath action = - testSessionWithExtraFiles "import-placement" testComment $ \dir -> - check (dir originalPath) (dir expectedPath) action - where - check :: FilePath -> FilePath -> T.Text -> Session () - check originalPath expectedPath action = do - oSrc <- liftIO $ readFileUtf8 originalPath - eSrc <- liftIO $ readFileUtf8 expectedPath - originalDoc <- createDoc originalPath "haskell" oSrc - _ <- waitForDiagnostics - shouldBeDoc <- createDoc expectedPath "haskell" eSrc - actionsOrCommands <- getAllCodeActions originalDoc - chosenAction <- liftIO $ pickActionWithTitle action actionsOrCommands - executeCodeAction chosenAction - originalDocAfterAction <- documentContents originalDoc - shouldBeDocContents <- documentContents shouldBeDoc - liftIO $ T.replace "\r\n" "\n" shouldBeDocContents @=? T.replace "\r\n" "\n" originalDocAfterAction - -renameActionTests :: TestTree -renameActionTests = testGroup "rename actions" - [ testSession "change to local variable name" $ do - let content = T.unlines - [ "module Testing where" - , "foo :: Int -> Int" - , "foo argName = argNme" - ] - doc <- createDoc "Testing.hs" "haskell" content - _ <- waitForDiagnostics - action <- findCodeAction doc (Range (Position 2 14) (Position 2 20)) "Replace with ‘argName’" - executeCodeAction action - contentAfterAction <- documentContents doc - let expectedContentAfterAction = T.unlines - [ "module Testing where" - , "foo :: Int -> Int" - , "foo argName = argName" - ] - liftIO $ expectedContentAfterAction @=? contentAfterAction - , testSession "change to name of imported function" $ do - let content = T.unlines - [ "module Testing where" - , "import Data.Maybe (maybeToList)" - , "foo :: Maybe a -> [a]" - , "foo = maybToList" - ] - doc <- createDoc "Testing.hs" "haskell" content - _ <- waitForDiagnostics - action <- findCodeAction doc (Range (Position 3 6) (Position 3 16)) "Replace with ‘maybeToList’" - executeCodeAction action - contentAfterAction <- documentContents doc - let expectedContentAfterAction = T.unlines - [ "module Testing where" - , "import Data.Maybe (maybeToList)" - , "foo :: Maybe a -> [a]" - , "foo = maybeToList" - ] - liftIO $ expectedContentAfterAction @=? contentAfterAction - , testSession "suggest multiple local variable names" $ do - let content = T.unlines - [ "module Testing where" - , "foo :: Char -> Char -> Char -> Char" - , "foo argument1 argument2 argument3 = argumentX" - ] - doc <- createDoc "Testing.hs" "haskell" content - _ <- waitForDiagnostics - _ <- findCodeActions doc (Range (Position 2 36) (Position 2 45)) - ["Replace with ‘argument1’", "Replace with ‘argument2’", "Replace with ‘argument3’"] - return() - , testSession "change infix function" $ do - let content = T.unlines - [ "module Testing where" - , "monus :: Int -> Int" - , "monus x y = max 0 (x - y)" - , "foo x y = x `monnus` y" - ] - doc <- createDoc "Testing.hs" "haskell" content - _ <- waitForDiagnostics - actionsOrCommands <- getCodeActions doc (Range (Position 3 12) (Position 3 20)) - [fixTypo] <- pure [action | InR action@CodeAction{ _title = actionTitle } <- actionsOrCommands, "monus" `T.isInfixOf` actionTitle ] - executeCodeAction fixTypo - contentAfterAction <- documentContents doc - let expectedContentAfterAction = T.unlines - [ "module Testing where" - , "monus :: Int -> Int" - , "monus x y = max 0 (x - y)" - , "foo x y = x `monus` y" - ] - liftIO $ expectedContentAfterAction @=? contentAfterAction - ] - -typeWildCardActionTests :: TestTree -typeWildCardActionTests = testGroup "type wildcard actions" - [ testUseTypeSignature "global signature" - [ "func :: _" - , "func x = x" - ] - [ "func :: p -> p" - , "func x = x" - ] - , testUseTypeSignature "local signature" - [ "func :: Int -> Int" - , "func x =" - , " let y :: _" - , " y = x * 2" - , " in y" - ] - [ "func :: Int -> Int" - , "func x =" - , " let y :: Int" - , " y = x * 2" - , " in y" - ] - , testUseTypeSignature "multi-line message 1" - [ "func :: _" - , "func x y = x + y" - ] - [ "func :: Integer -> Integer -> Integer" - , "func x y = x + y" - ] - , testUseTypeSignature "type in parentheses" - [ "func :: a -> _" - , "func x = (x, const x)" - ] - [ "func :: a -> (a, b -> a)" - , "func x = (x, const x)" - ] - , testUseTypeSignature "type in brackets" - [ "func :: _ -> Maybe a" - , "func xs = head xs" - ] - [ "func :: [Maybe a] -> Maybe a" - , "func xs = head xs" - ] - , testUseTypeSignature "unit type" - [ "func :: IO _" - , "func = putChar 'H'" - ] - [ "func :: IO ()" - , "func = putChar 'H'" - ] - , testUseTypeSignature "no spaces around '::'" - [ "func::_" - , "func x y = x + y" - ] - [ "func::Integer -> Integer -> Integer" - , "func x y = x + y" - ] - , testGroup "add parens if hole is part of bigger type" - [ testUseTypeSignature "subtype 1" - [ "func :: _ -> Integer -> Integer" - , "func x y = x + y" - ] - [ "func :: Integer -> Integer -> Integer" - , "func x y = x + y" - ] - , testUseTypeSignature "subtype 2" - [ "func :: Integer -> _ -> Integer" - , "func x y = x + y" - ] - [ "func :: Integer -> Integer -> Integer" - , "func x y = x + y" - ] - , testUseTypeSignature "subtype 3" - [ "func :: Integer -> Integer -> _" - , "func x y = x + y" - ] - [ "func :: Integer -> Integer -> Integer" - , "func x y = x + y" - ] - , testUseTypeSignature "subtype 4" - [ "func :: Integer -> _" - , "func x y = x + y" - ] - [ "func :: Integer -> (Integer -> Integer)" - , "func x y = x + y" - ] - ] - ] - where - -- | Test session of given name, checking action "Use type signature..." - -- on a test file with given content and comparing to expected result. - testUseTypeSignature name textIn textOut = testSession name $ do - let fileStart = "module Testing where" - content = T.unlines $ fileStart : textIn - expectedContentAfterAction = T.unlines $ fileStart : textOut - doc <- createDoc "Testing.hs" "haskell" content - _ <- waitForDiagnostics - actionsOrCommands <- getAllCodeActions doc - let [addSignature] = [action | InR action@CodeAction { _title = actionTitle } <- actionsOrCommands - , "Use type signature" `T.isInfixOf` actionTitle - ] - executeCodeAction addSignature - contentAfterAction <- documentContents doc - liftIO $ expectedContentAfterAction @=? contentAfterAction - -{-# HLINT ignore "Use nubOrd" #-} -removeImportTests :: TestTree -removeImportTests = testGroup "remove import actions" - [ testSession "redundant" $ do - let contentA = T.unlines - [ "module ModuleA where" - ] - _docA <- createDoc "ModuleA.hs" "haskell" contentA - let contentB = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "import ModuleA" - , "stuffB :: Integer" - , "stuffB = 123" - ] - docB <- createDoc "ModuleB.hs" "haskell" contentB - _ <- waitForDiagnostics - [InR action@CodeAction { _title = actionTitle }, _] - <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) - liftIO $ "Remove import" @=? actionTitle - executeCodeAction action - contentAfterAction <- documentContents docB - let expectedContentAfterAction = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "stuffB :: Integer" - , "stuffB = 123" - ] - liftIO $ expectedContentAfterAction @=? contentAfterAction - , testSession "qualified redundant" $ do - let contentA = T.unlines - [ "module ModuleA where" - ] - _docA <- createDoc "ModuleA.hs" "haskell" contentA - let contentB = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "import qualified ModuleA" - , "stuffB :: Integer" - , "stuffB = 123" - ] - docB <- createDoc "ModuleB.hs" "haskell" contentB - _ <- waitForDiagnostics - [InR action@CodeAction { _title = actionTitle }, _] - <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) - liftIO $ "Remove import" @=? actionTitle - executeCodeAction action - contentAfterAction <- documentContents docB - let expectedContentAfterAction = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "stuffB :: Integer" - , "stuffB = 123" - ] - liftIO $ expectedContentAfterAction @=? contentAfterAction - , testSession "redundant binding" $ do - let contentA = T.unlines - [ "module ModuleA where" - , "stuffA = False" - , "stuffB :: Integer" - , "stuffB = 123" - , "stuffC = ()" - , "_stuffD = '_'" - ] - _docA <- createDoc "ModuleA.hs" "haskell" contentA - let contentB = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "import ModuleA (stuffA, stuffB, _stuffD, stuffC, stuffA)" - , "main = print stuffB" - ] - docB <- createDoc "ModuleB.hs" "haskell" contentB - _ <- waitForDiagnostics - [InR action@CodeAction { _title = actionTitle }, _] - <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) - liftIO $ "Remove _stuffD, stuffA, stuffC from import" @=? actionTitle - executeCodeAction action - contentAfterAction <- documentContents docB - let expectedContentAfterAction = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "import ModuleA (stuffB)" - , "main = print stuffB" - ] - liftIO $ expectedContentAfterAction @=? contentAfterAction - , testSession "redundant binding - unicode regression " $ do - let contentA = T.unlines - [ "module ModuleA where" - , "data A = A" - , "ε :: Double" - , "ε = 0.5" - ] - _docA <- createDoc "ModuleA.hs" "haskell" contentA - let contentB = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "import ModuleA (A(..), ε)" - , "a = A" - ] - docB <- createDoc "ModuleB.hs" "haskell" contentB - _ <- waitForDiagnostics - [InR action@CodeAction { _title = actionTitle }, _] - <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) - liftIO $ "Remove ε from import" @=? actionTitle - executeCodeAction action - contentAfterAction <- documentContents docB - let expectedContentAfterAction = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "import ModuleA (A(..))" - , "a = A" - ] - liftIO $ expectedContentAfterAction @=? contentAfterAction - , testSession "redundant operator" $ do - let contentA = T.unlines - [ "module ModuleA where" - , "a !! _b = a" - , "a _b = a" - , "stuffB :: Integer" - , "stuffB = 123" - ] - _docA <- createDoc "ModuleA.hs" "haskell" contentA - let contentB = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "import qualified ModuleA as A ((), stuffB, (!!))" - , "main = print A.stuffB" - ] - docB <- createDoc "ModuleB.hs" "haskell" contentB - _ <- waitForDiagnostics - [InR action@CodeAction { _title = actionTitle }, _] - <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) - liftIO $ "Remove !!, from import" @=? actionTitle - executeCodeAction action - contentAfterAction <- documentContents docB - let expectedContentAfterAction = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "import qualified ModuleA as A (stuffB)" - , "main = print A.stuffB" - ] - liftIO $ expectedContentAfterAction @=? contentAfterAction - , testSession "redundant all import" $ do - let contentA = T.unlines - [ "module ModuleA where" - , "data A = A" - , "stuffB :: Integer" - , "stuffB = 123" - ] - _docA <- createDoc "ModuleA.hs" "haskell" contentA - let contentB = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "import ModuleA (A(..), stuffB)" - , "main = print stuffB" - ] - docB <- createDoc "ModuleB.hs" "haskell" contentB - _ <- waitForDiagnostics - [InR action@CodeAction { _title = actionTitle }, _] - <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) - liftIO $ "Remove A from import" @=? actionTitle - executeCodeAction action - contentAfterAction <- documentContents docB - let expectedContentAfterAction = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "import ModuleA (stuffB)" - , "main = print stuffB" - ] - liftIO $ expectedContentAfterAction @=? contentAfterAction - , testSession "redundant constructor import" $ do - let contentA = T.unlines - [ "module ModuleA where" - , "data D = A | B" - , "data E = F" - ] - _docA <- createDoc "ModuleA.hs" "haskell" contentA - let contentB = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "import ModuleA (D(A,B), E(F))" - , "main = B" - ] - docB <- createDoc "ModuleB.hs" "haskell" contentB - _ <- waitForDiagnostics - [InR action@CodeAction { _title = actionTitle }, _] - <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) - liftIO $ "Remove A, E, F from import" @=? actionTitle - executeCodeAction action - contentAfterAction <- documentContents docB - let expectedContentAfterAction = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "import ModuleA (D(B))" - , "main = B" - ] - liftIO $ expectedContentAfterAction @=? contentAfterAction - , testSession "import containing the identifier Strict" $ do - let contentA = T.unlines - [ "module Strict where" - ] - _docA <- createDoc "Strict.hs" "haskell" contentA - let contentB = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "import Strict" - ] - docB <- createDoc "ModuleB.hs" "haskell" contentB - _ <- waitForDiagnostics - [InR action@CodeAction { _title = actionTitle }, _] - <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) - liftIO $ "Remove import" @=? actionTitle - executeCodeAction action - contentAfterAction <- documentContents docB - let expectedContentAfterAction = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - ] - liftIO $ expectedContentAfterAction @=? contentAfterAction - , testSession "remove all" $ do - let content = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleA where" - , "import Data.Function (fix, (&))" - , "import qualified Data.Functor.Const" - , "import Data.Functor.Identity" - , "import Data.Functor.Sum (Sum (InL, InR))" - , "import qualified Data.Kind as K (Constraint, Type)" - , "x = InL (Identity 123)" - , "y = fix id" - , "type T = K.Type" - ] - doc <- createDoc "ModuleC.hs" "haskell" content - _ <- waitForDiagnostics - [_, _, _, _, InR action@CodeAction { _title = actionTitle }] - <- nub <$> getAllCodeActions doc - liftIO $ "Remove all redundant imports" @=? actionTitle - executeCodeAction action - contentAfterAction <- documentContents doc - let expectedContentAfterAction = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleA where" - , "import Data.Function (fix)" - , "import Data.Functor.Identity" - , "import Data.Functor.Sum (Sum (InL))" - , "import qualified Data.Kind as K (Type)" - , "x = InL (Identity 123)" - , "y = fix id" - , "type T = K.Type" - ] - liftIO $ expectedContentAfterAction @=? contentAfterAction - , testSession "remove unused operators whose name ends with '.'" $ do - let contentA = T.unlines - [ "module ModuleA where" - , "(@.) = 0 -- Must have an operator whose name ends with '.'" - , "a = 1 -- .. but also something else" - ] - _docA <- createDoc "ModuleA.hs" "haskell" contentA - let contentB = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "import ModuleA (a, (@.))" - , "x = a -- Must use something from module A, but not (@.)" - ] - docB <- createDoc "ModuleB.hs" "haskell" contentB - _ <- waitForDiagnostics - [InR action@CodeAction { _title = actionTitle }, _] - <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) - liftIO $ "Remove @. from import" @=? actionTitle - executeCodeAction action - contentAfterAction <- documentContents docB - let expectedContentAfterAction = T.unlines - [ "{-# OPTIONS_GHC -Wunused-imports #-}" - , "module ModuleB where" - , "import ModuleA (a)" - , "x = a -- Must use something from module A, but not (@.)" - ] - liftIO $ expectedContentAfterAction @=? contentAfterAction - ] - -extendImportTests :: TestTree -extendImportTests = testGroup "extend import actions" - [ testGroup "with checkAll" $ tests True - , testGroup "without checkAll" $ tests False - ] - where - tests overrideCheckProject = - [ testSession "extend all constructors for record field" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "data A = B { a :: Int }" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import ModuleA (A(B))" - , "f = a" - ]) - (Range (Position 2 4) (Position 2 5)) - [ "Add A(..) to the import list of ModuleA" - , "Add A(a) to the import list of ModuleA" - , "Add a to the import list of ModuleA" - ] - (T.unlines - [ "module ModuleB where" - , "import ModuleA (A(..))" - , "f = a" - ]) - , testSession "extend all constructors with sibling" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "data Foo" - , "data Bar" - , "data A = B | C" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import ModuleA ( Foo, A (C) , Bar ) " - , "f = B" - ]) - (Range (Position 2 4) (Position 2 5)) - [ "Add A(..) to the import list of ModuleA" - , "Add A(B) to the import list of ModuleA" - ] - (T.unlines - [ "module ModuleB where" - , "import ModuleA ( Foo, A (..) , Bar ) " - , "f = B" - ]) - , testSession "extend all constructors with comment" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "data Foo" - , "data Bar" - , "data A = B | C" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import ModuleA ( Foo, A (C{-comment--}) , Bar ) " - , "f = B" - ]) - (Range (Position 2 4) (Position 2 5)) - [ "Add A(..) to the import list of ModuleA" - , "Add A(B) to the import list of ModuleA" - ] - (T.unlines - [ "module ModuleB where" - , "import ModuleA ( Foo, A (..{-comment--}) , Bar ) " - , "f = B" - ]) - , testSession "extend all constructors for type operator" $ template - [] - ("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "import Data.Type.Equality ((:~:))" - , "x :: (:~:) [] []" - , "x = Refl" - ]) - (Range (Position 3 17) (Position 3 18)) - [ "Add (:~:)(..) to the import list of Data.Type.Equality" - , "Add type (:~:)(Refl) to the import list of Data.Type.Equality"] - (T.unlines - [ "module ModuleA where" - , "import Data.Type.Equality ((:~:) (..))" - , "x :: (:~:) [] []" - , "x = Refl" - ]) - , testSession "extend all constructors for class" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "class C a where" - , " m1 :: a -> a" - , " m2 :: a -> a" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import ModuleA (C(m1))" - , "b = m2" - ]) - (Range (Position 2 5) (Position 2 5)) - [ "Add C(..) to the import list of ModuleA" - , "Add C(m2) to the import list of ModuleA" - , "Add m2 to the import list of ModuleA" - ] - (T.unlines - [ "module ModuleB where" - , "import ModuleA (C(..))" - , "b = m2" - ]) - , testSession "extend single line import with value" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "stuffA :: Double" - , "stuffA = 0.00750" - , "stuffB :: Integer" - , "stuffB = 123" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import ModuleA as A (stuffB)" - , "main = print (stuffA, stuffB)" - ]) - (Range (Position 2 17) (Position 2 18)) - ["Add stuffA to the import list of ModuleA"] - (T.unlines - [ "module ModuleB where" - , "import ModuleA as A (stuffB, stuffA)" - , "main = print (stuffA, stuffB)" - ]) - , testSession "extend single line import with operator" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "(.*) :: Integer -> Integer -> Integer" - , "x .* y = x * y" - , "stuffB :: Integer" - , "stuffB = 123" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import ModuleA as A (stuffB)" - , "main = print (stuffB .* stuffB)" - ]) - (Range (Position 2 17) (Position 2 18)) - ["Add (.*) to the import list of ModuleA"] - (T.unlines - [ "module ModuleB where" - , "import ModuleA as A (stuffB, (.*))" - , "main = print (stuffB .* stuffB)" - ]) - , testSession "extend single line import with infix constructor" $ template - [] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import Data.List.NonEmpty (fromList)" - , "main = case (fromList []) of _ :| _ -> pure ()" - ]) - (Range (Position 2 5) (Position 2 6)) - [ "Add NonEmpty((:|)) to the import list of Data.List.NonEmpty" - , "Add NonEmpty(..) to the import list of Data.List.NonEmpty" - ] - (T.unlines - [ "module ModuleB where" - , "import Data.List.NonEmpty (fromList, NonEmpty ((:|)))" - , "main = case (fromList []) of _ :| _ -> pure ()" - ]) - , testSession "extend single line import with prefix constructor" $ template - [] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import Prelude hiding (Maybe(..))" - , "import Data.Maybe (catMaybes)" - , "x = Just 10" - ]) - (Range (Position 3 5) (Position 2 6)) - [ "Add Maybe(Just) to the import list of Data.Maybe" - , "Add Maybe(..) to the import list of Data.Maybe" - ] - (T.unlines - [ "module ModuleB where" - , "import Prelude hiding (Maybe(..))" - , "import Data.Maybe (catMaybes, Maybe (Just))" - , "x = Just 10" - ]) - , testSession "extend single line import with type" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "type A = Double" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import ModuleA ()" - , "b :: A" - , "b = 0" - ]) - (Range (Position 2 5) (Position 2 5)) - ["Add A to the import list of ModuleA"] - (T.unlines - [ "module ModuleB where" - , "import ModuleA (A)" - , "b :: A" - , "b = 0" - ]) - , testSession "extend single line import with constructor" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "data A = Constructor" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import ModuleA (A)" - , "b :: A" - , "b = Constructor" - ]) - (Range (Position 3 5) (Position 3 5)) - [ "Add A(Constructor) to the import list of ModuleA" - , "Add A(..) to the import list of ModuleA" - ] - (T.unlines - [ "module ModuleB where" - , "import ModuleA (A (Constructor))" - , "b :: A" - , "b = Constructor" - ]) - , testSession "extend single line import with constructor (with comments)" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "data A = Constructor" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import ModuleA (A ({-Constructor-}))" - , "b :: A" - , "b = Constructor" - ]) - (Range (Position 3 5) (Position 3 5)) - [ "Add A(Constructor) to the import list of ModuleA" - , "Add A(..) to the import list of ModuleA" - ] - (T.unlines - [ "module ModuleB where" - , "import ModuleA (A (Constructor{-Constructor-}))" - , "b :: A" - , "b = Constructor" - ]) - , testSession "extend single line import with mixed constructors" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "data A = ConstructorFoo | ConstructorBar" - , "a = 1" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import ModuleA (A (ConstructorBar), a)" - , "b :: A" - , "b = ConstructorFoo" - ]) - (Range (Position 3 5) (Position 3 5)) - [ "Add A(ConstructorFoo) to the import list of ModuleA" - , "Add A(..) to the import list of ModuleA" - ] - (T.unlines - [ "module ModuleB where" - , "import ModuleA (A (ConstructorBar, ConstructorFoo), a)" - , "b :: A" - , "b = ConstructorFoo" - ]) - , testSession "extend single line qualified import with value" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "stuffA :: Double" - , "stuffA = 0.00750" - , "stuffB :: Integer" - , "stuffB = 123" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import qualified ModuleA as A (stuffB)" - , "main = print (A.stuffA, A.stuffB)" - ]) - (Range (Position 2 17) (Position 2 18)) - ["Add stuffA to the import list of ModuleA"] - (T.unlines - [ "module ModuleB where" - , "import qualified ModuleA as A (stuffB, stuffA)" - , "main = print (A.stuffA, A.stuffB)" - ]) - , testSession "extend multi line import with value" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "stuffA :: Double" - , "stuffA = 0.00750" - , "stuffB :: Integer" - , "stuffB = 123" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import ModuleA (stuffB" - , " )" - , "main = print (stuffA, stuffB)" - ]) - (Range (Position 3 17) (Position 3 18)) - ["Add stuffA to the import list of ModuleA"] - (T.unlines - [ "module ModuleB where" - , "import ModuleA (stuffB, stuffA" - , " )" - , "main = print (stuffA, stuffB)" - ]) - , testSession "extend multi line import with trailing comma" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "stuffA :: Double" - , "stuffA = 0.00750" - , "stuffB :: Integer" - , "stuffB = 123" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import ModuleA (stuffB," - , " )" - , "main = print (stuffA, stuffB)" - ]) - (Range (Position 3 17) (Position 3 18)) - ["Add stuffA to the import list of ModuleA"] - (T.unlines - [ "module ModuleB where" - , "import ModuleA (stuffB, stuffA," - , " )" - , "main = print (stuffA, stuffB)" - ]) - , testSession "extend single line import with method within class" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "class C a where" - , " m1 :: a -> a" - , " m2 :: a -> a" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import ModuleA (C(m1))" - , "b = m2" - ]) - (Range (Position 2 5) (Position 2 5)) - [ "Add C(m2) to the import list of ModuleA" - , "Add m2 to the import list of ModuleA" - , "Add C(..) to the import list of ModuleA" - ] - (T.unlines - [ "module ModuleB where" - , "import ModuleA (C(m1, m2))" - , "b = m2" - ]) - , testSession "extend single line import with method without class" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "class C a where" - , " m1 :: a -> a" - , " m2 :: a -> a" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import ModuleA (C(m1))" - , "b = m2" - ]) - (Range (Position 2 5) (Position 2 5)) - [ "Add m2 to the import list of ModuleA" - , "Add C(m2) to the import list of ModuleA" - , "Add C(..) to the import list of ModuleA" - ] - (T.unlines - [ "module ModuleB where" - , "import ModuleA (C(m1), m2)" - , "b = m2" - ]) - , testSession "extend import list with multiple choices" $ template - [("ModuleA.hs", T.unlines - -- this is just a dummy module to help the arguments needed for this test - [ "module ModuleA (bar) where" - , "bar = 10" - ]), - ("ModuleB.hs", T.unlines - -- this is just a dummy module to help the arguments needed for this test - [ "module ModuleB (bar) where" - , "bar = 10" - ])] - ("ModuleC.hs", T.unlines - [ "module ModuleC where" - , "import ModuleB ()" - , "import ModuleA ()" - , "foo = bar" - ]) - (Range (Position 3 17) (Position 3 18)) - ["Add bar to the import list of ModuleA", - "Add bar to the import list of ModuleB"] - (T.unlines - [ "module ModuleC where" - , "import ModuleB ()" - , "import ModuleA (bar)" - , "foo = bar" - ]) - , testSession "extend import list with constructor of type operator" $ template - [] - ("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "import Data.Type.Equality ((:~:))" - , "x :: (:~:) [] []" - , "x = Refl" - ]) - (Range (Position 3 17) (Position 3 18)) - [ "Add type (:~:)(Refl) to the import list of Data.Type.Equality" - , "Add (:~:)(..) to the import list of Data.Type.Equality"] - (T.unlines - [ "module ModuleA where" - , "import Data.Type.Equality ((:~:) (Refl))" - , "x :: (:~:) [] []" - , "x = Refl" - ]) - , expectFailBecause "importing pattern synonyms is unsupported" - $ testSession "extend import list with pattern synonym" $ template - [("ModuleA.hs", T.unlines - [ "{-# LANGUAGE PatternSynonyms #-}" - , "module ModuleA where" - , "pattern Some x = Just x" - ]) - ] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import A ()" - , "k (Some x) = x" - ]) - (Range (Position 2 3) (Position 2 7)) - ["Add pattern Some to the import list of A"] - (T.unlines - [ "module ModuleB where" - , "import A (pattern Some)" - , "k (Some x) = x" - ]) - , ignoreForGHC92 "Diagnostic message has no suggestions" $ - testSession "type constructor name same as data constructor name" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "newtype Foo = Foo Int" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import ModuleA(Foo)" - , "f :: Foo" - , "f = Foo 1" - ]) - (Range (Position 3 4) (Position 3 6)) - ["Add Foo(Foo) to the import list of ModuleA", "Add Foo(..) to the import list of ModuleA"] - (T.unlines - [ "module ModuleB where" - , "import ModuleA(Foo (Foo))" - , "f :: Foo" - , "f = Foo 1" - ]) - , testSession "type constructor name same as data constructor name, data constructor extraneous" $ template - [("ModuleA.hs", T.unlines - [ "module ModuleA where" - , "data Foo = Foo" - ])] - ("ModuleB.hs", T.unlines - [ "module ModuleB where" - , "import ModuleA()" - , "f :: Foo" - , "f = undefined" - ]) - (Range (Position 2 4) (Position 2 6)) - ["Add Foo to the import list of ModuleA"] - (T.unlines - [ "module ModuleB where" - , "import ModuleA(Foo)" - , "f :: Foo" - , "f = undefined" - ]) - ] - where - codeActionTitle CodeAction{_title=x} = x - - template setUpModules moduleUnderTest range expectedTitles expectedContentB = do - configureCheckProject overrideCheckProject - - mapM_ (\x -> createDoc (fst x) "haskell" (snd x)) setUpModules - docB <- createDoc (fst moduleUnderTest) "haskell" (snd moduleUnderTest) - _ <- waitForDiagnostics - waitForProgressDone - actionsOrCommands <- getCodeActions docB range - let codeActions = - filter - (T.isPrefixOf "Add" . codeActionTitle) - [ca | InR ca <- actionsOrCommands] - actualTitles = codeActionTitle <$> codeActions - -- Note that we are not testing the order of the actions, as the - -- order of the expected actions indicates which one we'll execute - -- in this test, i.e., the first one. - liftIO $ sort expectedTitles @=? sort actualTitles - - -- Execute the action with the same title as the first expected one. - -- Since we tested that both lists have the same elements (possibly - -- in a different order), this search cannot fail. - let firstTitle:_ = expectedTitles - action = fromJust $ - find ((firstTitle ==) . codeActionTitle) codeActions - executeCodeAction action - contentAfterAction <- documentContents docB - liftIO $ expectedContentB @=? contentAfterAction - -fixModuleImportTypoTests :: TestTree -fixModuleImportTypoTests = testGroup "fix module import typo" - [ testSession "works when single module suggested" $ do - doc <- createDoc "A.hs" "haskell" "import Data.Cha" - _ <- waitForDiagnostics - InR action@CodeAction { _title = actionTitle } : _ <- getCodeActions doc (R 0 0 0 10) - liftIO $ actionTitle @?= "replace with Data.Char" - executeCodeAction action - contentAfterAction <- documentContents doc - liftIO $ contentAfterAction @?= "import Data.Char" - , testSession "works when multiple modules suggested" $ do - doc <- createDoc "A.hs" "haskell" "import Data.I" - _ <- waitForDiagnostics - actions <- sortOn (\(InR CodeAction{_title=x}) -> x) <$> getCodeActions doc (R 0 0 0 10) - let actionTitles = [ title | InR CodeAction{_title=title} <- actions ] - liftIO $ actionTitles @?= [ "replace with Data.Eq" - , "replace with Data.Int" - , "replace with Data.Ix" - ] - let InR replaceWithDataEq : _ = actions - executeCodeAction replaceWithDataEq - contentAfterAction <- documentContents doc - liftIO $ contentAfterAction @?= "import Data.Eq" - ] - -extendImportTestsRegEx :: TestTree -extendImportTestsRegEx = testGroup "regex parsing" - [ - testCase "parse invalid multiple imports" $ template "foo bar foo" Nothing - , testCase "parse malformed import list" $ template - "\n\8226 Perhaps you want to add \8216fromList\8217 to one of these import lists:\n \8216Data.Map\8217)" - Nothing - , testCase "parse multiple imports" $ template - "\n\8226 Perhaps you want to add \8216fromList\8217 to one of these import lists:\n \8216Data.Map\8217 (app/testlsp.hs:7:1-18)\n \8216Data.HashMap.Strict\8217 (app/testlsp.hs:8:1-29)" - $ Just ("fromList",[("Data.Map","app/testlsp.hs:7:1-18"),("Data.HashMap.Strict","app/testlsp.hs:8:1-29")]) - ] - where - template message expected = do - liftIO $ matchRegExMultipleImports message @=? expected - -suggestImportClassMethodTests :: TestTree -suggestImportClassMethodTests = - testGroup - "suggest import class methods" - [ testGroup - "new" - [ testSession "via parent" $ - template' - "import Data.Semigroup (Semigroup(stimes))" - (Range (Position 4 2) (Position 4 8)), - testSession "top level" $ - template' - "import Data.Semigroup (stimes)" - (Range (Position 4 2) (Position 4 8)), - testSession "all" $ - template' - "import Data.Semigroup" - (Range (Position 4 2) (Position 4 8)) - ], - testGroup - "extend" - [ testSession "via parent" $ - template - [ "module A where", - "", - "import Data.Semigroup ()" - ] - (Range (Position 6 2) (Position 6 8)) - "Add Semigroup(stimes) to the import list of Data.Semigroup" - [ "module A where", - "", - "import Data.Semigroup (Semigroup (stimes))" - ], - testSession "top level" $ - template - [ "module A where", - "", - "import Data.Semigroup ()" - ] - (Range (Position 6 2) (Position 6 8)) - "Add stimes to the import list of Data.Semigroup" - [ "module A where", - "", - "import Data.Semigroup (stimes)" - ] - ] - ] - where - decls = - [ "data X = X", - "instance Semigroup X where", - " (<>) _ _ = X", - " stimes _ _ = X" - ] - template beforeContent range executeTitle expectedContent = do - doc <- createDoc "A.hs" "haskell" $ T.unlines (beforeContent <> decls) - _ <- waitForDiagnostics - waitForProgressDone - actions <- getCodeActions doc range - let actions' = [x | InR x <- actions] - titles = [_title | CodeAction {_title} <- actions'] - liftIO $ executeTitle `elem` titles @? T.unpack executeTitle <> " does not in " <> show titles - executeCodeAction $ fromJust $ find (\CodeAction {_title} -> _title == executeTitle) actions' - content <- documentContents doc - liftIO $ T.unlines (expectedContent <> decls) @=? content - template' executeTitle range = let c = ["module A where"] in template c range executeTitle $ c <> [executeTitle] - -suggestImportTests :: TestTree -suggestImportTests = testGroup "suggest import actions" - [ testGroup "Dont want suggestion" - [ -- extend import - test False ["Data.List.NonEmpty ()"] "f = nonEmpty" [] "import Data.List.NonEmpty (nonEmpty)" - -- data constructor - , test False [] "f = First" [] "import Data.Monoid (First)" - -- internal module - , test False [] "f :: Typeable a => a" ["f = undefined"] "import Data.Typeable.Internal (Typeable)" - -- package not in scope - , test False [] "f = quickCheck" [] "import Test.QuickCheck (quickCheck)" - -- don't omit the parent data type of a constructor - , test False [] "f ExitSuccess = ()" [] "import System.Exit (ExitSuccess)" - -- don't suggest data constructor when we only need the type - , test False [] "f :: Bar" [] "import Bar (Bar(Bar))" - -- don't suggest all data constructors for the data type - , test False [] "f :: Bar" [] "import Bar (Bar(..))" - ] - , testGroup "want suggestion" - [ wantWait [] "f = foo" [] "import Foo (foo)" - , wantWait [] "f = Bar" [] "import Bar (Bar(Bar))" - , wantWait [] "f :: Bar" [] "import Bar (Bar)" - , wantWait [] "f = Bar" [] "import Bar (Bar(..))" - , test True [] "f = nonEmpty" [] "import Data.List.NonEmpty (nonEmpty)" - , test True [] "f = (:|)" [] "import Data.List.NonEmpty (NonEmpty((:|)))" - , test True [] "f :: Natural" ["f = undefined"] "import Numeric.Natural (Natural)" - , test True [] "f :: Natural" ["f = undefined"] "import Numeric.Natural" - , test True [] "f :: NonEmpty ()" ["f = () :| []"] "import Data.List.NonEmpty (NonEmpty)" - , test True [] "f :: NonEmpty ()" ["f = () :| []"] "import Data.List.NonEmpty" - , test True [] "f = First" [] "import Data.Monoid (First(First))" - , test True [] "f = Endo" [] "import Data.Monoid (Endo(Endo))" - , test True [] "f = Version" [] "import Data.Version (Version(Version))" - , test True [] "f ExitSuccess = ()" [] "import System.Exit (ExitCode(ExitSuccess))" - , test True [] "f = AssertionFailed" [] "import Control.Exception (AssertionFailed(AssertionFailed))" - , test True ["Prelude"] "f = nonEmpty" [] "import Data.List.NonEmpty (nonEmpty)" - , test True [] "f :: Alternative f => f ()" ["f = undefined"] "import Control.Applicative (Alternative)" - , test True [] "f :: Alternative f => f ()" ["f = undefined"] "import Control.Applicative" - , test True [] "f = empty" [] "import Control.Applicative (Alternative(empty))" - , test True [] "f = empty" [] "import Control.Applicative (empty)" - , test True [] "f = empty" [] "import Control.Applicative" - , test True [] "f = (&)" [] "import Data.Function ((&))" - , test True [] "f = NE.nonEmpty" [] "import qualified Data.List.NonEmpty as NE" - , test True [] "f = Data.List.NonEmpty.nonEmpty" [] "import qualified Data.List.NonEmpty" - , test True [] "f :: Typeable a => a" ["f = undefined"] "import Data.Typeable (Typeable)" - , test True [] "f = pack" [] "import Data.Text (pack)" - , test True [] "f :: Text" ["f = undefined"] "import Data.Text (Text)" - , test True [] "f = [] & id" [] "import Data.Function ((&))" - , test True [] "f = (&) [] id" [] "import Data.Function ((&))" - , test True [] "f = (.|.)" [] "import Data.Bits (Bits((.|.)))" - , test True [] "f = (.|.)" [] "import Data.Bits ((.|.))" - , test True [] "f :: a ~~ b" [] "import Data.Type.Equality (type (~~))" - , test True - ["qualified Data.Text as T" - ] "f = T.putStrLn" [] "import qualified Data.Text.IO as T" - , test True - [ "qualified Data.Text as T" - , "qualified Data.Function as T" - ] "f = T.putStrLn" [] "import qualified Data.Text.IO as T" - , test True - [ "qualified Data.Text as T" - , "qualified Data.Function as T" - , "qualified Data.Functor as T" - , "qualified Data.Data as T" - ] "f = T.putStrLn" [] "import qualified Data.Text.IO as T" - , test True [] "f = (.|.)" [] "import Data.Bits (Bits(..))" - , test True [] "f = empty" [] "import Control.Applicative (Alternative(..))" - ] - , expectFailBecause "importing pattern synonyms is unsupported" $ test True [] "k (Some x) = x" [] "import B (pattern Some)" - ] - where - test = test' False - wantWait = test' True True - - test' waitForCheckProject wanted imps def other newImp = testSessionWithExtraFiles "hover" (T.unpack def) $ \dir -> do - configureCheckProject waitForCheckProject - let before = T.unlines $ "module A where" : ["import " <> x | x <- imps] ++ def : other - after = T.unlines $ "module A where" : ["import " <> x | x <- imps] ++ [newImp] ++ def : other - cradle = "cradle: {direct: {arguments: [-hide-all-packages, -package, base, -package, text, -package-env, -, A, Bar, Foo, B]}}" - liftIO $ writeFileUTF8 (dir "hie.yaml") cradle - liftIO $ writeFileUTF8 (dir "B.hs") $ unlines ["{-# LANGUAGE PatternSynonyms #-}", "module B where", "pattern Some x = Just x"] - doc <- createDoc "Test.hs" "haskell" before - waitForProgressDone - _ <- waitForDiagnostics - -- there isn't a good way to wait until the whole project is checked atm - when waitForCheckProject $ liftIO $ sleep 0.5 - let defLine = fromIntegral $ length imps + 1 - range = Range (Position defLine 0) (Position defLine maxBound) - actions <- getCodeActions doc range - if wanted - then do - action <- liftIO $ pickActionWithTitle newImp actions - executeCodeAction action - contentAfterAction <- documentContents doc - liftIO $ after @=? contentAfterAction - else - liftIO $ [_title | InR CodeAction{_title} <- actions, _title == newImp ] @?= [] - -suggestImportDisambiguationTests :: TestTree -suggestImportDisambiguationTests = testGroup "suggest import disambiguation actions" - [ testGroup "Hiding strategy works" - [ testGroup "fromList" - [ testCase "AVec" $ - compareHideFunctionTo [(8,9),(10,8)] - "Use AVec for fromList, hiding other imports" - "HideFunction.expected.fromList.A.hs" - , testCase "BVec" $ - compareHideFunctionTo [(8,9),(10,8)] - "Use BVec for fromList, hiding other imports" - "HideFunction.expected.fromList.B.hs" - ] - , testGroup "(++)" - [ testCase "EVec" $ - compareHideFunctionTo [(8,9),(10,8)] - "Use EVec for ++, hiding other imports" - "HideFunction.expected.append.E.hs" - , testCase "Hide functions without local" $ - compareTwo - "HideFunctionWithoutLocal.hs" [(8,8)] - "Use local definition for ++, hiding other imports" - "HideFunctionWithoutLocal.expected.hs" - , testCase "Prelude" $ - compareHideFunctionTo [(8,9),(10,8)] - "Use Prelude for ++, hiding other imports" - "HideFunction.expected.append.Prelude.hs" - , testCase "Prelude and local definition, infix" $ - compareTwo - "HidePreludeLocalInfix.hs" [(2,19)] - "Use local definition for ++, hiding other imports" - "HidePreludeLocalInfix.expected.hs" - , testCase "AVec, indented" $ - compareTwo "HidePreludeIndented.hs" [(3,8)] - "Use AVec for ++, hiding other imports" - "HidePreludeIndented.expected.hs" - - ] - , testGroup "Vec (type)" - [ testCase "AVec" $ - compareTwo - "HideType.hs" [(8,15)] - "Use AVec for Vec, hiding other imports" - "HideType.expected.A.hs" - , testCase "EVec" $ - compareTwo - "HideType.hs" [(8,15)] - "Use EVec for Vec, hiding other imports" - "HideType.expected.E.hs" - ] - ] - , testGroup "Qualify strategy" - [ testCase "won't suggest full name for qualified module" $ - withHideFunction [(8,9),(10,8)] $ \_ actions -> do - liftIO $ - assertBool "EVec.fromList must not be suggested" $ - "Replace with qualified: EVec.fromList" `notElem` - [ actionTitle - | InR CodeAction { _title = actionTitle } <- actions - ] - liftIO $ - assertBool "EVec.++ must not be suggested" $ - "Replace with qualified: EVec.++" `notElem` - [ actionTitle - | InR CodeAction { _title = actionTitle } <- actions - ] - , testGroup "fromList" - [ testCase "EVec" $ - compareHideFunctionTo [(8,9),(10,8)] - "Replace with qualified: E.fromList" - "HideFunction.expected.qualified.fromList.E.hs" - , testCase "Hide DuplicateRecordFields" $ - compareTwo - "HideQualifyDuplicateRecordFields.hs" [(9, 9)] - "Replace with qualified: AVec.fromList" - "HideQualifyDuplicateRecordFields.expected.hs" - , testCase "Duplicate record fields should not be imported" $ do - withTarget ("HideQualifyDuplicateRecordFields" <.> ".hs") [(9, 9)] $ - \_ actions -> do - liftIO $ - assertBool "Hidings should not be presented while DuplicateRecordFields exists" $ - all not [ actionTitle =~ T.pack "Use ([A-Za-z][A-Za-z0-9]*) for fromList, hiding other imports" - | InR CodeAction { _title = actionTitle } <- actions] - withTarget ("HideQualifyDuplicateRecordFieldsSelf" <.> ".hs") [(4, 4)] $ - \_ actions -> do - liftIO $ - assertBool "ambiguity from DuplicateRecordFields should not be imported" $ - null actions - ] - , testGroup "(++)" - [ testCase "Prelude, parensed" $ - compareHideFunctionTo [(8,9),(10,8)] - "Replace with qualified: Prelude.++" - "HideFunction.expected.qualified.append.Prelude.hs" - , testCase "Prelude, infix" $ - compareTwo - "HideQualifyInfix.hs" [(4,19)] - "Replace with qualified: Prelude.++" - "HideQualifyInfix.expected.hs" - , testCase "Prelude, left section" $ - compareTwo - "HideQualifySectionLeft.hs" [(4,15)] - "Replace with qualified: Prelude.++" - "HideQualifySectionLeft.expected.hs" - , testCase "Prelude, right section" $ - compareTwo - "HideQualifySectionRight.hs" [(4,18)] - "Replace with qualified: Prelude.++" - "HideQualifySectionRight.expected.hs" - ] - ] - ] - where - hidingDir = "test/data/hiding" - compareTwo original locs cmd expected = - withTarget original locs $ \doc actions -> do - expected <- liftIO $ - readFileUtf8 (hidingDir expected) - action <- liftIO $ pickActionWithTitle cmd actions - executeCodeAction action - contentAfterAction <- documentContents doc - liftIO $ T.replace "\r\n" "\n" expected @=? contentAfterAction - compareHideFunctionTo = compareTwo "HideFunction.hs" - auxFiles = ["AVec.hs", "BVec.hs", "CVec.hs", "DVec.hs", "EVec.hs", "FVec.hs"] - withTarget file locs k = withTempDir $ \dir -> runInDir dir $ do - liftIO $ mapM_ (\fp -> copyFile (hidingDir fp) $ dir fp) - $ file : auxFiles - doc <- openDoc file "haskell" - waitForProgressDone - void $ expectDiagnostics [(file, [(DsError, loc, "Ambiguous occurrence") | loc <- locs])] - actions <- getAllCodeActions doc - k doc actions - withHideFunction = withTarget ("HideFunction" <.> "hs") - -suggestHideShadowTests :: TestTree -suggestHideShadowTests = - testGroup - "suggest hide shadow" - [ testGroup - "single" - [ testOneCodeAction - "hide unsued" - "Hide on from Data.Function" - (1, 2) - (1, 4) - [ "import Data.Function" - , "f on = on" - , "g on = on" - ] - [ "import Data.Function hiding (on)" - , "f on = on" - , "g on = on" - ] - , testOneCodeAction - "extend hiding unsued" - "Hide on from Data.Function" - (1, 2) - (1, 4) - [ "import Data.Function hiding ((&))" - , "f on = on" - ] - [ "import Data.Function hiding (on, (&))" - , "f on = on" - ] - , testOneCodeAction - "delete unsued" - "Hide on from Data.Function" - (1, 2) - (1, 4) - [ "import Data.Function ((&), on)" - , "f on = on" - ] - [ "import Data.Function ((&))" - , "f on = on" - ] - , testOneCodeAction - "hide operator" - "Hide & from Data.Function" - (1, 2) - (1, 5) - [ "import Data.Function" - , "f (&) = (&)" - ] - [ "import Data.Function hiding ((&))" - , "f (&) = (&)" - ] - , testOneCodeAction - "remove operator" - "Hide & from Data.Function" - (1, 2) - (1, 5) - [ "import Data.Function ((&), on)" - , "f (&) = (&)" - ] - [ "import Data.Function ( on)" - , "f (&) = (&)" - ] - , noCodeAction - "don't remove already used" - (2, 2) - (2, 4) - [ "import Data.Function" - , "g = on" - , "f on = on" - ] - ] - , testGroup - "multi" - [ testOneCodeAction - "hide from B" - "Hide ++ from B" - (2, 2) - (2, 6) - [ "import B" - , "import C" - , "f (++) = (++)" - ] - [ "import B hiding ((++))" - , "import C" - , "f (++) = (++)" - ] - , testOneCodeAction - "hide from C" - "Hide ++ from C" - (2, 2) - (2, 6) - [ "import B" - , "import C" - , "f (++) = (++)" - ] - [ "import B" - , "import C hiding ((++))" - , "f (++) = (++)" - ] - , testOneCodeAction - "hide from Prelude" - "Hide ++ from Prelude" - (2, 2) - (2, 6) - [ "import B" - , "import C" - , "f (++) = (++)" - ] - [ "import B" - , "import C" - , "import Prelude hiding ((++))" - , "f (++) = (++)" - ] - , testMultiCodeActions - "manual hide all" - [ "Hide ++ from Prelude" - , "Hide ++ from C" - , "Hide ++ from B" - ] - (2, 2) - (2, 6) - [ "import B" - , "import C" - , "f (++) = (++)" - ] - [ "import B hiding ((++))" - , "import C hiding ((++))" - , "import Prelude hiding ((++))" - , "f (++) = (++)" - ] - , testOneCodeAction - "auto hide all" - "Hide ++ from all occurence imports" - (2, 2) - (2, 6) - [ "import B" - , "import C" - , "f (++) = (++)" - ] - [ "import B hiding ((++))" - , "import C hiding ((++))" - , "import Prelude hiding ((++))" - , "f (++) = (++)" - ] - ] - ] - where - testOneCodeAction testName actionName start end origin expected = - helper testName start end origin expected $ \cas -> do - action <- liftIO $ pickActionWithTitle actionName cas - executeCodeAction action - noCodeAction testName start end origin = - helper testName start end origin origin $ \cas -> do - liftIO $ cas @?= [] - testMultiCodeActions testName actionNames start end origin expected = - helper testName start end origin expected $ \cas -> do - let r = [ca | (InR ca) <- cas, ca ^. L.title `elem` actionNames] - liftIO $ - (length r == length actionNames) - @? "Expected " <> show actionNames <> ", but got " <> show cas <> " which is not its superset" - forM_ r executeCodeAction - helper testName (line1, col1) (line2, col2) origin expected k = testSession testName $ do - void $ createDoc "B.hs" "haskell" $ T.unlines docB - void $ createDoc "C.hs" "haskell" $ T.unlines docC - doc <- createDoc "A.hs" "haskell" $ T.unlines (header <> origin) - void waitForDiagnostics - waitForProgressDone - cas <- getCodeActions doc (Range (Position (fromIntegral $ line1 + length header) col1) (Position (fromIntegral $ line2 + length header) col2)) - void $ k [x | x@(InR ca) <- cas, "Hide" `T.isPrefixOf` (ca ^. L.title)] - contentAfter <- documentContents doc - liftIO $ contentAfter @?= T.unlines (header <> expected) - header = - [ "{-# OPTIONS_GHC -Wname-shadowing #-}" - , "module A where" - , "" - ] - -- for multi group - docB = - [ "module B where" - , "(++) = id" - ] - docC = - [ "module C where" - , "(++) = id" - ] - -insertNewDefinitionTests :: TestTree -insertNewDefinitionTests = testGroup "insert new definition actions" - [ testSession "insert new function definition" $ do - let txtB = - ["foo True = select [True]" - , "" - ,"foo False = False" - ] - txtB' = - ["" - ,"someOtherCode = ()" - ] - docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB') - _ <- waitForDiagnostics - InR action@CodeAction { _title = actionTitle } : _ - <- sortOn (\(InR CodeAction{_title=x}) -> x) <$> - getCodeActions docB (R 0 0 0 50) - liftIO $ actionTitle @?= "Define select :: [Bool] -> Bool" - executeCodeAction action - contentAfterAction <- documentContents docB - liftIO $ contentAfterAction @?= T.unlines (txtB ++ - [ "" - , "select :: [Bool] -> Bool" - , "select = _" - ] - ++ txtB') - , testSession "define a hole" $ do - let txtB = - ["foo True = _select [True]" - , "" - ,"foo False = False" - ] - txtB' = - ["" - ,"someOtherCode = ()" - ] - docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB') - _ <- waitForDiagnostics - InR action@CodeAction { _title = actionTitle } : _ - <- sortOn (\(InR CodeAction{_title=x}) -> x) <$> - getCodeActions docB (R 0 0 0 50) - liftIO $ actionTitle @?= "Define select :: [Bool] -> Bool" - executeCodeAction action - contentAfterAction <- documentContents docB - liftIO $ contentAfterAction @?= T.unlines ( - ["foo True = select [True]" - , "" - ,"foo False = False" - , "" - , "select :: [Bool] -> Bool" - , "select = _" - ] - ++ txtB') - , testSession "insert new function definition - Haddock comments" $ do - let start = ["foo :: Int -> Bool" - , "foo x = select (x + 1)" - , "" - , "-- | This is a haddock comment" - , "haddock :: Int -> Int" - , "haddock = undefined" - ] - let expected = ["foo :: Int -> Bool" - , "foo x = select (x + 1)" - , "" - , "select :: Int -> Bool" - , "select = _" - , "" - , "-- | This is a haddock comment" - , "haddock :: Int -> Int" - , "haddock = undefined"] - docB <- createDoc "ModuleB.hs" "haskell" (T.unlines start) - _ <- waitForDiagnostics - InR action@CodeAction { _title = actionTitle } : _ - <- sortOn (\(InR CodeAction{_title=x}) -> x) <$> - getCodeActions docB (R 1 0 0 50) - liftIO $ actionTitle @?= "Define select :: Int -> Bool" - executeCodeAction action - contentAfterAction <- documentContents docB - liftIO $ contentAfterAction @?= T.unlines expected - , testSession "insert new function definition - normal comments" $ do - let start = ["foo :: Int -> Bool" - , "foo x = select (x + 1)" - , "" - , "-- This is a normal comment" - , "normal :: Int -> Int" - , "normal = undefined" - ] - let expected = ["foo :: Int -> Bool" - , "foo x = select (x + 1)" - , "" - , "select :: Int -> Bool" - , "select = _" - , "" - , "-- This is a normal comment" - , "normal :: Int -> Int" - , "normal = undefined"] - docB <- createDoc "ModuleB.hs" "haskell" (T.unlines start) - _ <- waitForDiagnostics - InR action@CodeAction { _title = actionTitle } : _ - <- sortOn (\(InR CodeAction{_title=x}) -> x) <$> - getCodeActions docB (R 1 0 0 50) - liftIO $ actionTitle @?= "Define select :: Int -> Bool" - executeCodeAction action - contentAfterAction <- documentContents docB - liftIO $ contentAfterAction @?= T.unlines expected - ] - - -deleteUnusedDefinitionTests :: TestTree -deleteUnusedDefinitionTests = testGroup "delete unused definition action" - [ testSession "delete unused top level binding" $ - testFor - (T.unlines [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A (some) where" - , "" - , "f :: Int -> Int" - , "f 1 = let a = 1" - , " in a" - , "f 2 = 2" - , "" - , "some = ()" - ]) - (4, 0) - "Delete ‘f’" - (T.unlines [ - "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A (some) where" - , "" - , "some = ()" - ]) - - , testSession "delete unused top level binding defined in infix form" $ - testFor - (T.unlines [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A (some) where" - , "" - , "myPlus :: Int -> Int -> Int" - , "a `myPlus` b = a + b" - , "" - , "some = ()" - ]) - (4, 2) - "Delete ‘myPlus’" - (T.unlines [ - "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A (some) where" - , "" - , "some = ()" - ]) - , testSession "delete unused binding in where clause" $ - testFor - (T.unlines [ "{-# OPTIONS_GHC -Wunused-binds #-}" - , "module A (h, g) where" - , "" - , "h :: Int" - , "h = 3" - , "" - , "g :: Int" - , "g = 6" - , " where" - , " h :: Int" - , " h = 4" - , "" - ]) - (10, 4) - "Delete ‘h’" - (T.unlines [ "{-# OPTIONS_GHC -Wunused-binds #-}" - , "module A (h, g) where" - , "" - , "h :: Int" - , "h = 3" - , "" - , "g :: Int" - , "g = 6" - , " where" - , "" - ]) - , testSession "delete unused binding with multi-oneline signatures front" $ - testFor - (T.unlines [ "{-# OPTIONS_GHC -Wunused-binds #-}" - , "module A (b, c) where" - , "" - , "a, b, c :: Int" - , "a = 3" - , "b = 4" - , "c = 5" - ]) - (4, 0) - "Delete ‘a’" - (T.unlines [ "{-# OPTIONS_GHC -Wunused-binds #-}" - , "module A (b, c) where" - , "" - , "b, c :: Int" - , "b = 4" - , "c = 5" - ]) - , testSession "delete unused binding with multi-oneline signatures mid" $ - testFor - (T.unlines [ "{-# OPTIONS_GHC -Wunused-binds #-}" - , "module A (a, c) where" - , "" - , "a, b, c :: Int" - , "a = 3" - , "b = 4" - , "c = 5" - ]) - (5, 0) - "Delete ‘b’" - (T.unlines [ "{-# OPTIONS_GHC -Wunused-binds #-}" - , "module A (a, c) where" - , "" - , "a, c :: Int" - , "a = 3" - , "c = 5" - ]) - , testSession "delete unused binding with multi-oneline signatures end" $ - testFor - (T.unlines [ "{-# OPTIONS_GHC -Wunused-binds #-}" - , "module A (a, b) where" - , "" - , "a, b, c :: Int" - , "a = 3" - , "b = 4" - , "c = 5" - ]) - (6, 0) - "Delete ‘c’" - (T.unlines [ "{-# OPTIONS_GHC -Wunused-binds #-}" - , "module A (a, b) where" - , "" - , "a, b :: Int" - , "a = 3" - , "b = 4" - ]) - ] - where - testFor source pos expectedTitle expectedResult = do - docId <- createDoc "A.hs" "haskell" source - expectDiagnostics [ ("A.hs", [(DsWarning, pos, "not used")]) ] - - (action, title) <- extractCodeAction docId "Delete" pos - - liftIO $ title @?= expectedTitle - executeCodeAction action - contentAfterAction <- documentContents docId - liftIO $ contentAfterAction @?= expectedResult - - extractCodeAction docId actionPrefix (l, c) = do - [action@CodeAction { _title = actionTitle }] <- findCodeActionsByPrefix docId (R l c l c) [actionPrefix] - return (action, actionTitle) - -addTypeAnnotationsToLiteralsTest :: TestTree -addTypeAnnotationsToLiteralsTest = testGroup "add type annotations to literals to satisfy constraints" - [ - testSession "add default type to satisfy one constraint" $ - testFor - (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" - , "module A (f) where" - , "" - , "f = 1" - ]) - [ (DsWarning, (3, 4), "Defaulting the following constraint") ] - "Add type annotation ‘Integer’ to ‘1’" - (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" - , "module A (f) where" - , "" - , "f = (1 :: Integer)" - ]) - - , testSession "add default type to satisfy one constraint in nested expressions" $ - testFor - (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" - , "module A where" - , "" - , "f =" - , " let x = 3" - , " in x" - ]) - [ (DsWarning, (4, 12), "Defaulting the following constraint") ] - "Add type annotation ‘Integer’ to ‘3’" - (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" - , "module A where" - , "" - , "f =" - , " let x = (3 :: Integer)" - , " in x" - ]) - , testSession "add default type to satisfy one constraint in more nested expressions" $ - testFor - (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" - , "module A where" - , "" - , "f =" - , " let x = let y = 5 in y" - , " in x" - ]) - [ (DsWarning, (4, 20), "Defaulting the following constraint") ] - "Add type annotation ‘Integer’ to ‘5’" - (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" - , "module A where" - , "" - , "f =" - , " let x = let y = (5 :: Integer) in y" - , " in x" - ]) - , testSession "add default type to satisfy one constraint with duplicate literals" $ - testFor - (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" - , "{-# LANGUAGE OverloadedStrings #-}" - , "module A (f) where" - , "" - , "import Debug.Trace" - , "" - , "f = seq \"debug\" traceShow \"debug\"" - ]) - [ (DsWarning, (6, 8), "Defaulting the following constraint") - , (DsWarning, (6, 16), "Defaulting the following constraint") - ] - ("Add type annotation ‘" <> listOfChar <> "’ to ‘\"debug\"’") - (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" - , "{-# LANGUAGE OverloadedStrings #-}" - , "module A (f) where" - , "" - , "import Debug.Trace" - , "" - , "f = seq (\"debug\" :: " <> listOfChar <> ") traceShow \"debug\"" - ]) - , knownBrokenForGhcVersions [GHC92] "GHC 9.2 only has 'traceShow' in error span" $ - testSession "add default type to satisfy two constraints" $ - testFor - (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" - , "{-# LANGUAGE OverloadedStrings #-}" - , "module A (f) where" - , "" - , "import Debug.Trace" - , "" - , "f a = traceShow \"debug\" a" - ]) - [ (DsWarning, (6, 6), "Defaulting the following constraint") ] - ("Add type annotation ‘" <> listOfChar <> "’ to ‘\"debug\"’") - (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" - , "{-# LANGUAGE OverloadedStrings #-}" - , "module A (f) where" - , "" - , "import Debug.Trace" - , "" - , "f a = traceShow (\"debug\" :: " <> listOfChar <> ") a" - ]) - , knownBrokenForGhcVersions [GHC92] "GHC 9.2 only has 'traceShow' in error span" $ - testSession "add default type to satisfy two constraints with duplicate literals" $ - testFor - (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" - , "{-# LANGUAGE OverloadedStrings #-}" - , "module A (f) where" - , "" - , "import Debug.Trace" - , "" - , "f = seq (\"debug\" :: [Char]) (seq (\"debug\" :: [Char]) (traceShow \"debug\"))" - ]) - [ (DsWarning, (6, 54), "Defaulting the following constraint") ] - ("Add type annotation ‘" <> listOfChar <> "’ to ‘\"debug\"’") - (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" - , "{-# LANGUAGE OverloadedStrings #-}" - , "module A (f) where" - , "" - , "import Debug.Trace" - , "" - , "f = seq (\"debug\" :: [Char]) (seq (\"debug\" :: [Char]) (traceShow (\"debug\" :: " <> listOfChar <> ")))" - ]) - ] - where - testFor source diag expectedTitle expectedResult = do - docId <- createDoc "A.hs" "haskell" source - expectDiagnostics [ ("A.hs", diag) ] - - let cursors = map snd3 diag - (action, title) <- extractCodeAction docId "Add type annotation" (minimum cursors) (maximum cursors) - - liftIO $ title @?= expectedTitle - executeCodeAction action - contentAfterAction <- documentContents docId - liftIO $ contentAfterAction @?= expectedResult - - extractCodeAction docId actionPrefix (l,c) (l', c')= do - [action@CodeAction { _title = actionTitle }] <- findCodeActionsByPrefix docId (R l c l' c') [actionPrefix] - return (action, actionTitle) - - -fixConstructorImportTests :: TestTree -fixConstructorImportTests = testGroup "fix import actions" - [ testSession "fix constructor import" $ template - (T.unlines - [ "module ModuleA where" - , "data A = Constructor" - ]) - (T.unlines - [ "module ModuleB where" - , "import ModuleA(Constructor)" - ]) - (Range (Position 1 10) (Position 1 11)) - "Fix import of A(Constructor)" - (T.unlines - [ "module ModuleB where" - , "import ModuleA(A(Constructor))" - ]) - ] - where - template contentA contentB range expectedAction expectedContentB = do - _docA <- createDoc "ModuleA.hs" "haskell" contentA - docB <- createDoc "ModuleB.hs" "haskell" contentB - _diags <- waitForDiagnostics - InR action@CodeAction { _title = actionTitle } : _ - <- sortOn (\(InR CodeAction{_title=x}) -> x) <$> - getCodeActions docB range - liftIO $ expectedAction @=? actionTitle - executeCodeAction action - contentAfterAction <- documentContents docB - liftIO $ expectedContentB @=? contentAfterAction - -importRenameActionTests :: TestTree -importRenameActionTests = testGroup "import rename actions" - [ testSession "Data.Mape -> Data.Map" $ check "Map" - , testSession "Data.Mape -> Data.Maybe" $ check "Maybe" ] where - check modname = do - let content = T.unlines - [ "module Testing where" - , "import Data.Mape" - ] - doc <- createDoc "Testing.hs" "haskell" content - _ <- waitForDiagnostics - actionsOrCommands <- getCodeActions doc (Range (Position 1 8) (Position 1 16)) - let [changeToMap] = [action | InR action@CodeAction{ _title = actionTitle } <- actionsOrCommands, ("Data." <> modname) `T.isInfixOf` actionTitle ] - executeCodeAction changeToMap - contentAfterAction <- documentContents doc - let expectedContentAfterAction = T.unlines - [ "module Testing where" - , "import Data." <> modname - ] - liftIO $ expectedContentAfterAction @=? contentAfterAction - -fillTypedHoleTests :: TestTree -fillTypedHoleTests = let - - sourceCode :: T.Text -> T.Text -> T.Text -> T.Text - sourceCode a b c = T.unlines - [ "module Testing where" - , "" - , "globalConvert :: Int -> String" - , "globalConvert = undefined" - , "" - , "globalInt :: Int" - , "globalInt = 3" - , "" - , "bar :: Int -> Int -> String" - , "bar n parameterInt = " <> a <> " (n + " <> b <> " + " <> c <> ") where" - , " localConvert = (flip replicate) 'x'" - , "" - , "foo :: () -> Int -> String" - , "foo = undefined" - - ] - - check :: T.Text -> T.Text -> T.Text -> T.Text -> T.Text -> T.Text -> T.Text -> TestTree - check actionTitle - oldA oldB oldC - newA newB newC = testSession (T.unpack actionTitle) $ do - let originalCode = sourceCode oldA oldB oldC - let expectedCode = sourceCode newA newB newC - doc <- createDoc "Testing.hs" "haskell" originalCode - _ <- waitForDiagnostics - actionsOrCommands <- getCodeActions doc (Range (Position 9 0) (Position 9 maxBound)) - chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands - executeCodeAction chosenAction - modifiedCode <- documentContents doc - liftIO $ expectedCode @=? modifiedCode - in - testGroup "fill typed holes" - [ check "replace _ with show" - "_" "n" "n" - "show" "n" "n" - - , check "replace _ with globalConvert" - "_" "n" "n" - "globalConvert" "n" "n" - - , check "replace _convertme with localConvert" - "_convertme" "n" "n" - "localConvert" "n" "n" - - , check "replace _b with globalInt" - "_a" "_b" "_c" - "_a" "globalInt" "_c" - - , check "replace _c with globalInt" - "_a" "_b" "_c" - "_a" "_b" "globalInt" - - , check "replace _c with parameterInt" - "_a" "_b" "_c" - "_a" "_b" "parameterInt" - , check "replace _ with foo _" - "_" "n" "n" - "(foo _)" "n" "n" - , testSession "replace _toException with E.toException" $ do - let mkDoc x = T.unlines - [ "module Testing where" - , "import qualified Control.Exception as E" - , "ioToSome :: E.IOException -> E.SomeException" - , "ioToSome = " <> x ] - doc <- createDoc "Test.hs" "haskell" $ mkDoc "_toException" - _ <- waitForDiagnostics - actions <- getCodeActions doc (Range (Position 3 0) (Position 3 maxBound)) - chosen <- liftIO $ pickActionWithTitle "replace _toException with E.toException" actions - executeCodeAction chosen - modifiedCode <- documentContents doc - liftIO $ mkDoc "E.toException" @=? modifiedCode - , testSession "filling infix type hole uses prefix notation" $ do - let mkDoc x = T.unlines - [ "module Testing where" - , "data A = A" - , "foo :: A -> A -> A" - , "foo A A = A" - , "test :: A -> A -> A" - , "test a1 a2 = a1 " <> x <> " a2" - ] - doc <- createDoc "Test.hs" "haskell" $ mkDoc "`_`" - _ <- waitForDiagnostics - actions <- getCodeActions doc (Range (Position 5 16) (Position 5 19)) - chosen <- liftIO $ pickActionWithTitle "replace _ with foo" actions - executeCodeAction chosen - modifiedCode <- documentContents doc - liftIO $ mkDoc "`foo`" @=? modifiedCode - , testSession "postfix hole uses postfix notation of infix operator" $ do - let mkDoc x = T.unlines - [ "module Testing where" - , "test :: Int -> Int -> Int" - , "test a1 a2 = " <> x <> " a1 a2" - ] - doc <- createDoc "Test.hs" "haskell" $ mkDoc "_" - _ <- waitForDiagnostics - actions <- getCodeActions doc (Range (Position 2 13) (Position 2 14)) - chosen <- liftIO $ pickActionWithTitle "replace _ with (+)" actions - executeCodeAction chosen - modifiedCode <- documentContents doc - liftIO $ mkDoc "(+)" @=? modifiedCode - , testSession "filling infix type hole uses infix operator" $ do - let mkDoc x = T.unlines - [ "module Testing where" - , "test :: Int -> Int -> Int" - , "test a1 a2 = a1 " <> x <> " a2" - ] - doc <- createDoc "Test.hs" "haskell" $ mkDoc "`_`" - _ <- waitForDiagnostics - actions <- getCodeActions doc (Range (Position 2 16) (Position 2 19)) - chosen <- liftIO $ pickActionWithTitle "replace _ with (+)" actions - executeCodeAction chosen - modifiedCode <- documentContents doc - liftIO $ mkDoc "+" @=? modifiedCode - ] - -addInstanceConstraintTests :: TestTree -addInstanceConstraintTests = let - missingConstraintSourceCode :: Maybe T.Text -> T.Text - missingConstraintSourceCode mConstraint = - let constraint = maybe "" (<> " => ") mConstraint - in T.unlines - [ "module Testing where" - , "" - , "data Wrap a = Wrap a" - , "" - , "instance " <> constraint <> "Eq (Wrap a) where" - , " (Wrap x) == (Wrap y) = x == y" - ] - - incompleteConstraintSourceCode :: Maybe T.Text -> T.Text - incompleteConstraintSourceCode mConstraint = - let constraint = maybe "Eq a" (\c -> "(Eq a, " <> c <> ")") mConstraint - in T.unlines - [ "module Testing where" - , "" - , "data Pair a b = Pair a b" - , "" - , "instance " <> constraint <> " => Eq (Pair a b) where" - , " (Pair x y) == (Pair x' y') = x == x' && y == y'" - ] - - incompleteConstraintSourceCode2 :: Maybe T.Text -> T.Text - incompleteConstraintSourceCode2 mConstraint = - let constraint = maybe "(Eq a, Eq b)" (\c -> "(Eq a, Eq b, " <> c <> ")") mConstraint - in T.unlines - [ "module Testing where" - , "" - , "data Three a b c = Three a b c" - , "" - , "instance " <> constraint <> " => Eq (Three a b c) where" - , " (Three x y z) == (Three x' y' z') = x == x' && y == y' && z == z'" - ] - - check :: T.Text -> T.Text -> T.Text -> TestTree - check actionTitle originalCode expectedCode = testSession (T.unpack actionTitle) $ do - doc <- createDoc "Testing.hs" "haskell" originalCode - _ <- waitForDiagnostics - actionsOrCommands <- getAllCodeActions doc - chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands - executeCodeAction chosenAction - modifiedCode <- documentContents doc - liftIO $ expectedCode @=? modifiedCode - - in testGroup "add instance constraint" - [ check - "Add `Eq a` to the context of the instance declaration" - (missingConstraintSourceCode Nothing) - (missingConstraintSourceCode $ Just "Eq a") - , check - "Add `Eq b` to the context of the instance declaration" - (incompleteConstraintSourceCode Nothing) - (incompleteConstraintSourceCode $ Just "Eq b") - , check - "Add `Eq c` to the context of the instance declaration" - (incompleteConstraintSourceCode2 Nothing) - (incompleteConstraintSourceCode2 $ Just "Eq c") - ] - -addFunctionConstraintTests :: TestTree -addFunctionConstraintTests = let - missingConstraintSourceCode :: T.Text -> T.Text - missingConstraintSourceCode constraint = - T.unlines - [ "module Testing where" - , "" - , "eq :: " <> constraint <> "a -> a -> Bool" - , "eq x y = x == y" - ] - - missingConstraintWithForAllSourceCode :: T.Text -> T.Text - missingConstraintWithForAllSourceCode constraint = - T.unlines - [ "{-# LANGUAGE ExplicitForAll #-}" - , "module Testing where" - , "" - , "eq :: forall a. " <> constraint <> "a -> a -> Bool" - , "eq x y = x == y" - ] - - incompleteConstraintWithForAllSourceCode :: T.Text -> T.Text - incompleteConstraintWithForAllSourceCode constraint = - T.unlines - [ "{-# LANGUAGE ExplicitForAll #-}" - , "module Testing where" - , "" - , "data Pair a b = Pair a b" - , "" - , "eq :: " <> constraint <> " => Pair a b -> Pair a b -> Bool" - , "eq (Pair x y) (Pair x' y') = x == x' && y == y'" - ] - - incompleteConstraintSourceCode :: T.Text -> T.Text - incompleteConstraintSourceCode constraint = - T.unlines - [ "module Testing where" - , "" - , "data Pair a b = Pair a b" - , "" - , "eq :: " <> constraint <> " => Pair a b -> Pair a b -> Bool" - , "eq (Pair x y) (Pair x' y') = x == x' && y == y'" - ] - - incompleteConstraintSourceCode2 :: T.Text -> T.Text - incompleteConstraintSourceCode2 constraint = - T.unlines - [ "module Testing where" - , "" - , "data Three a b c = Three a b c" - , "" - , "eq :: " <> constraint <> " => Three a b c -> Three a b c -> Bool" - , "eq (Three x y z) (Three x' y' z') = x == x' && y == y' && z == z'" - ] - - incompleteConstraintSourceCodeWithExtraCharsInContext :: T.Text -> T.Text - incompleteConstraintSourceCodeWithExtraCharsInContext constraint = - T.unlines - [ "module Testing where" - , "" - , "data Pair a b = Pair a b" - , "" - , "eq :: ( " <> constraint <> " ) => Pair a b -> Pair a b -> Bool" - , "eq (Pair x y) (Pair x' y') = x == x' && y == y'" - ] - - incompleteConstraintSourceCodeWithNewlinesInTypeSignature :: T.Text -> T.Text - incompleteConstraintSourceCodeWithNewlinesInTypeSignature constraint = - T.unlines - [ "module Testing where" - , "data Pair a b = Pair a b" - , "eq " - , " :: (" <> constraint <> ")" - , " => Pair a b -> Pair a b -> Bool" - , "eq (Pair x y) (Pair x' y') = x == x' && y == y'" - ] - - missingMonadConstraint constraint = T.unlines - [ "module Testing where" - , "f :: " <> constraint <> "m ()" - , "f = do " - , " return ()" - ] - - in testGroup "add function constraint" - [ checkCodeAction - "no preexisting constraint" - "Add `Eq a` to the context of the type signature for `eq`" - (missingConstraintSourceCode "") - (missingConstraintSourceCode "Eq a => ") - , checkCodeAction - "no preexisting constraint, with forall" - "Add `Eq a` to the context of the type signature for `eq`" - (missingConstraintWithForAllSourceCode "") - (missingConstraintWithForAllSourceCode "Eq a => ") - , checkCodeAction - "preexisting constraint, no parenthesis" - "Add `Eq b` to the context of the type signature for `eq`" - (incompleteConstraintSourceCode "Eq a") - (incompleteConstraintSourceCode "(Eq a, Eq b)") - , checkCodeAction - "preexisting constraints in parenthesis" - "Add `Eq c` to the context of the type signature for `eq`" - (incompleteConstraintSourceCode2 "(Eq a, Eq b)") - (incompleteConstraintSourceCode2 "(Eq a, Eq b, Eq c)") - , checkCodeAction - "preexisting constraints with forall" - "Add `Eq b` to the context of the type signature for `eq`" - (incompleteConstraintWithForAllSourceCode "Eq a") - (incompleteConstraintWithForAllSourceCode "(Eq a, Eq b)") - , checkCodeAction - "preexisting constraint, with extra spaces in context" - "Add `Eq b` to the context of the type signature for `eq`" - (incompleteConstraintSourceCodeWithExtraCharsInContext "Eq a") - (incompleteConstraintSourceCodeWithExtraCharsInContext "Eq a, Eq b") - , checkCodeAction - "preexisting constraint, with newlines in type signature" - "Add `Eq b` to the context of the type signature for `eq`" - (incompleteConstraintSourceCodeWithNewlinesInTypeSignature "Eq a") - (incompleteConstraintSourceCodeWithNewlinesInTypeSignature "Eq a, Eq b") - , checkCodeAction - "missing Monad constraint" - "Add `Monad m` to the context of the type signature for `f`" - (missingMonadConstraint "") - (missingMonadConstraint "Monad m => ") - ] - -checkCodeAction :: String -> T.Text -> T.Text -> T.Text -> TestTree -checkCodeAction testName actionTitle originalCode expectedCode = testSession testName $ do - doc <- createDoc "Testing.hs" "haskell" originalCode - _ <- waitForDiagnostics - actionsOrCommands <- getAllCodeActions doc - chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands - executeCodeAction chosenAction - modifiedCode <- documentContents doc - liftIO $ expectedCode @=? modifiedCode - -addImplicitParamsConstraintTests :: TestTree -addImplicitParamsConstraintTests = - testGroup - "add missing implicit params constraints" - [ testGroup - "introduced" - [ let ex ctxtA = exampleCode "?a" ctxtA "" - in checkCodeAction "at top level" "Add ?a::() to the context of fBase" (ex "") (ex "?a::()"), - let ex ctxA = exampleCode "x where x = ?a" ctxA "" - in checkCodeAction "in nested def" "Add ?a::() to the context of fBase" (ex "") (ex "?a::()") - ], - testGroup - "inherited" - [ let ex = exampleCode "()" "?a::()" - in checkCodeAction - "with preexisting context" - "Add `?a::()` to the context of the type signature for `fCaller`" - (ex "Eq ()") - (ex "Eq (), ?a::()"), - let ex = exampleCode "()" "?a::()" - in checkCodeAction "without preexisting context" "Add ?a::() to the context of fCaller" (ex "") (ex "?a::()") - ] - ] - where - mkContext "" = "" - mkContext contents = "(" <> contents <> ") => " - - exampleCode bodyBase contextBase contextCaller = - T.unlines - [ "{-# LANGUAGE FlexibleContexts, ImplicitParams #-}", - "module Testing where", - "fBase :: " <> mkContext contextBase <> "()", - "fBase = " <> bodyBase, - "fCaller :: " <> mkContext contextCaller <> "()", - "fCaller = fBase" - ] - -removeRedundantConstraintsTests :: TestTree -removeRedundantConstraintsTests = let - header = - [ "{-# OPTIONS_GHC -Wredundant-constraints #-}" - , "module Testing where" - , "" - ] - - headerExt :: [T.Text] -> [T.Text] - headerExt exts = - redunt : extTxt ++ ["module Testing where"] - where - redunt = "{-# OPTIONS_GHC -Wredundant-constraints #-}" - extTxt = map (\ext -> "{-# LANGUAGE " <> ext <> " #-}") exts - - redundantConstraintsCode :: Maybe T.Text -> T.Text - redundantConstraintsCode mConstraint = - let constraint = maybe "" (\c -> "" <> c <> " => ") mConstraint - in T.unlines $ header <> - [ "foo :: " <> constraint <> "a -> a" - , "foo = id" - ] - - redundantMixedConstraintsCode :: Maybe T.Text -> T.Text - redundantMixedConstraintsCode mConstraint = - let constraint = maybe "(Num a, Eq a)" (\c -> "(Num a, Eq a, " <> c <> ")") mConstraint - in T.unlines $ header <> - [ "foo :: " <> constraint <> " => a -> Bool" - , "foo x = x == 1" - ] - - typeSignatureSpaces :: Maybe T.Text -> T.Text - typeSignatureSpaces mConstraint = - let constraint = maybe "(Num a, Eq a)" (\c -> "(Num a, Eq a, " <> c <> ")") mConstraint - in T.unlines $ header <> - [ "foo :: " <> constraint <> " => a -> Bool" - , "foo x = x == 1" - ] - - redundantConstraintsForall :: Maybe T.Text -> T.Text - redundantConstraintsForall mConstraint = - let constraint = maybe "" (\c -> "" <> c <> " => ") mConstraint - in T.unlines $ headerExt ["RankNTypes"] <> - [ "foo :: forall a. " <> constraint <> "a -> a" - , "foo = id" - ] - - typeSignatureDo :: Maybe T.Text -> T.Text - typeSignatureDo mConstraint = - let constraint = maybe "" (\c -> "" <> c <> " => ") mConstraint - in T.unlines $ header <> - [ "f :: Int -> IO ()" - , "f n = do" - , " let foo :: " <> constraint <> "a -> IO ()" - , " foo _ = return ()" - , " r n" - ] - - typeSignatureNested :: Maybe T.Text -> T.Text - typeSignatureNested mConstraint = - let constraint = maybe "" (\c -> "" <> c <> " => ") mConstraint - in T.unlines $ header <> - [ "f :: Int -> ()" - , "f = g" - , " where" - , " g :: " <> constraint <> "a -> ()" - , " g _ = ()" - ] - - typeSignatureNested' :: Maybe T.Text -> T.Text - typeSignatureNested' mConstraint = - let constraint = maybe "" (\c -> "" <> c <> " => ") mConstraint - in T.unlines $ header <> - [ "f :: Int -> ()" - , "f =" - , " let" - , " g :: Int -> ()" - , " g = h" - , " where" - , " h :: " <> constraint <> "a -> ()" - , " h _ = ()" - , " in g" - ] - - typeSignatureNested'' :: Maybe T.Text -> T.Text - typeSignatureNested'' mConstraint = - let constraint = maybe "" (\c -> "" <> c <> " => ") mConstraint - in T.unlines $ header <> - [ "f :: Int -> ()" - , "f = g" - , " where" - , " g :: Int -> ()" - , " g = " - , " let" - , " h :: " <> constraint <> "a -> ()" - , " h _ = ()" - , " in h" - ] - - typeSignatureLined1 = T.unlines $ header <> - [ "foo :: Eq a =>" - , " a -> Bool" - , "foo _ = True" - ] - - typeSignatureLined2 = T.unlines $ header <> - [ "foo :: (Eq a, Show a)" - , " => a -> Bool" - , "foo _ = True" - ] - - typeSignatureOneLine = T.unlines $ header <> - [ "foo :: a -> Bool" - , "foo _ = True" - ] - - typeSignatureLined3 = T.unlines $ header <> - [ "foo :: ( Eq a" - , " , Show a" - , " )" - , " => a -> Bool" - , "foo x = x == x" - ] - - typeSignatureLined3' = T.unlines $ header <> - [ "foo :: ( Eq a" - , " )" - , " => a -> Bool" - , "foo x = x == x" - ] - - - check :: T.Text -> T.Text -> T.Text -> TestTree - check actionTitle originalCode expectedCode = testSession (T.unpack actionTitle) $ do - doc <- createDoc "Testing.hs" "haskell" originalCode - _ <- waitForDiagnostics - actionsOrCommands <- getAllCodeActions doc - chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands - executeCodeAction chosenAction - modifiedCode <- documentContents doc - liftIO $ expectedCode @=? modifiedCode - - in testGroup "remove redundant function constraints" - [ check - "Remove redundant constraint `Eq a` from the context of the type signature for `foo`" - (redundantConstraintsCode $ Just "Eq a") - (redundantConstraintsCode Nothing) - , check - "Remove redundant constraints `(Eq a, Monoid a)` from the context of the type signature for `foo`" - (redundantConstraintsCode $ Just "(Eq a, Monoid a)") - (redundantConstraintsCode Nothing) - , check - "Remove redundant constraints `(Monoid a, Show a)` from the context of the type signature for `foo`" - (redundantMixedConstraintsCode $ Just "Monoid a, Show a") - (redundantMixedConstraintsCode Nothing) - , check - "Remove redundant constraint `Eq a` from the context of the type signature for `g`" - (typeSignatureNested $ Just "Eq a") - (typeSignatureNested Nothing) - , check - "Remove redundant constraint `Eq a` from the context of the type signature for `h`" - (typeSignatureNested' $ Just "Eq a") - (typeSignatureNested' Nothing) - , check - "Remove redundant constraint `Eq a` from the context of the type signature for `h`" - (typeSignatureNested'' $ Just "Eq a") - (typeSignatureNested'' Nothing) - , check - "Remove redundant constraint `Eq a` from the context of the type signature for `foo`" - (redundantConstraintsForall $ Just "Eq a") - (redundantConstraintsForall Nothing) - , check - "Remove redundant constraint `Eq a` from the context of the type signature for `foo`" - (typeSignatureDo $ Just "Eq a") - (typeSignatureDo Nothing) - , check - "Remove redundant constraints `(Monoid a, Show a)` from the context of the type signature for `foo`" - (typeSignatureSpaces $ Just "Monoid a, Show a") - (typeSignatureSpaces Nothing) - , check - "Remove redundant constraint `Eq a` from the context of the type signature for `foo`" - typeSignatureLined1 - typeSignatureOneLine - , check - "Remove redundant constraints `(Eq a, Show a)` from the context of the type signature for `foo`" - typeSignatureLined2 - typeSignatureOneLine - , check - "Remove redundant constraint `Show a` from the context of the type signature for `foo`" - typeSignatureLined3 - typeSignatureLined3' - ] - -addSigActionTests :: TestTree -addSigActionTests = let - header = [ "{-# OPTIONS_GHC -Wmissing-signatures -Wmissing-pattern-synonym-signatures #-}" - , "{-# LANGUAGE PatternSynonyms,BangPatterns,GADTs #-}" - , "module Sigs where" - , "data T1 a where" - , " MkT1 :: (Show b) => a -> b -> T1 a" - ] - before def = T.unlines $ header ++ [def] - after' def sig = T.unlines $ header ++ [sig, def] - - def >:: sig = testSession (T.unpack $ T.replace "\n" "\\n" def) $ do - let originalCode = before def - let expectedCode = after' def sig - doc <- createDoc "Sigs.hs" "haskell" originalCode - _ <- waitForDiagnostics - actionsOrCommands <- getCodeActions doc (Range (Position 5 1) (Position 5 maxBound)) - chosenAction <- liftIO $ pickActionWithTitle ("add signature: " <> sig) actionsOrCommands - executeCodeAction chosenAction - modifiedCode <- documentContents doc - liftIO $ expectedCode @=? modifiedCode - in - testGroup "add signature" - [ "abc = True" >:: "abc :: Bool" - , "foo a b = a + b" >:: "foo :: Num a => a -> a -> a" - , "bar a b = show $ a + b" >:: "bar :: (Show a, Num a) => a -> a -> String" - , "(!!!) a b = a > b" >:: "(!!!) :: Ord a => a -> a -> Bool" - , "a >>>> b = a + b" >:: "(>>>>) :: Num a => a -> a -> a" - , "a `haha` b = a b" >:: "haha :: (t1 -> t2) -> t1 -> t2" - , "pattern Some a = Just a" >:: "pattern Some :: a -> Maybe a" - , "pattern Some a <- Just a" >:: "pattern Some :: a -> Maybe a" - , "pattern Some a <- Just a\n where Some a = Just a" >:: "pattern Some :: a -> Maybe a" - , "pattern Some a <- Just !a\n where Some !a = Just a" >:: "pattern Some :: a -> Maybe a" - , "pattern Point{x, y} = (x, y)" >:: "pattern Point :: a -> b -> (a, b)" - , "pattern Point{x, y} <- (x, y)" >:: "pattern Point :: a -> b -> (a, b)" - , "pattern Point{x, y} <- (x, y)\n where Point x y = (x, y)" >:: "pattern Point :: a -> b -> (a, b)" - , "pattern MkT1' b = MkT1 42 b" >:: "pattern MkT1' :: (Eq a, Num a) => Show b => b -> T1 a" - , "pattern MkT1' b <- MkT1 42 b" >:: "pattern MkT1' :: (Eq a, Num a) => Show b => b -> T1 a" - , "pattern MkT1' b <- MkT1 42 b\n where MkT1' b = MkT1 42 b" >:: "pattern MkT1' :: (Eq a, Num a) => Show b => b -> T1 a" - ] - -exportUnusedTests :: TestTree -exportUnusedTests = testGroup "export unused actions" - [ testGroup "don't want suggestion" - [ testSession "implicit exports" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "{-# OPTIONS_GHC -Wmissing-signatures #-}" - , "module A where" - , "foo = id"]) - (R 3 0 3 3) - "Export ‘foo’" - Nothing -- codeaction should not be available - , testSession "not top-level" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "{-# OPTIONS_GHC -Wunused-binds #-}" - , "module A (foo,bar) where" - , "foo = ()" - , " where bar = ()" - , "bar = ()"]) - (R 2 0 2 11) - "Export ‘bar’" - Nothing - , ignoreForGHC92 "Diagnostic message has no suggestions" $ - testSession "type is exported but not the constructor of same name" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A (Foo) where" - , "data Foo = Foo"]) - (R 2 0 2 8) - "Export ‘Foo’" - Nothing -- codeaction should not be available - , testSession "unused data field" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A (Foo(Foo)) where" - , "data Foo = Foo {foo :: ()}"]) - (R 2 0 2 20) - "Export ‘foo’" - Nothing -- codeaction should not be available - ] - , testGroup "want suggestion" - [ testSession "empty exports" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A (" - , ") where" - , "foo = id"]) - (R 3 0 3 3) - "Export ‘foo’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A (" - , "foo) where" - , "foo = id"]) - , testSession "single line explicit exports" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A (foo) where" - , "foo = id" - , "bar = foo"]) - (R 3 0 3 3) - "Export ‘bar’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A (foo, bar) where" - , "foo = id" - , "bar = foo"]) - , testSession "multi line explicit exports" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A" - , " (" - , " foo) where" - , "foo = id" - , "bar = foo"]) - (R 5 0 5 3) - "Export ‘bar’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A" - , " (" - , " foo, bar) where" - , "foo = id" - , "bar = foo"]) - , testSession "export list ends in comma" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A" - , " (foo," - , " ) where" - , "foo = id" - , "bar = foo"]) - (R 5 0 5 3) - "Export ‘bar’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A" - , " (foo," - , " bar) where" - , "foo = id" - , "bar = foo"]) - , testSession "style of multiple exports is preserved 1" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A" - , " ( foo" - , " , bar" - , " ) where" - , "foo = id" - , "bar = foo" - , "baz = bar" - ]) - (R 7 0 7 3) - "Export ‘baz’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A" - , " ( foo" - , " , bar" - , " , baz" - , " ) where" - , "foo = id" - , "bar = foo" - , "baz = bar" - ]) - , testSession "style of multiple exports is preserved 2" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A" - , " ( foo," - , " bar" - , " ) where" - , "foo = id" - , "bar = foo" - , "baz = bar" - ]) - (R 7 0 7 3) - "Export ‘baz’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A" - , " ( foo," - , " bar," - , " baz" - , " ) where" - , "foo = id" - , "bar = foo" - , "baz = bar" - ]) - , testSession "style of multiple exports is preserved and selects smallest export separator" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A" - , " ( foo" - , " , bar" - , " -- * For testing" - , " , baz" - , " ) where" - , "foo = id" - , "bar = foo" - , "baz = bar" - , "quux = bar" - ]) - (R 10 0 10 4) - "Export ‘quux’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A" - , " ( foo" - , " , bar" - , " -- * For testing" - , " , baz" - , " , quux" - , " ) where" - , "foo = id" - , "bar = foo" - , "baz = bar" - , "quux = bar" - ]) - , testSession "unused pattern synonym" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "{-# LANGUAGE PatternSynonyms #-}" - , "module A () where" - , "pattern Foo a <- (a, _)"]) - (R 3 0 3 10) - "Export ‘Foo’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "{-# LANGUAGE PatternSynonyms #-}" - , "module A (pattern Foo) where" - , "pattern Foo a <- (a, _)"]) - , testSession "unused data type" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A () where" - , "data Foo = Foo"]) - (R 2 0 2 7) - "Export ‘Foo’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A (Foo(..)) where" - , "data Foo = Foo"]) - , testSession "unused newtype" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A () where" - , "newtype Foo = Foo ()"]) - (R 2 0 2 10) - "Export ‘Foo’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A (Foo(..)) where" - , "newtype Foo = Foo ()"]) - , testSession "unused type synonym" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A () where" - , "type Foo = ()"]) - (R 2 0 2 7) - "Export ‘Foo’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A (Foo) where" - , "type Foo = ()"]) - , testSession "unused type family" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "{-# LANGUAGE TypeFamilies #-}" - , "module A () where" - , "type family Foo p"]) - (R 3 0 3 15) - "Export ‘Foo’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "{-# LANGUAGE TypeFamilies #-}" - , "module A (Foo) where" - , "type family Foo p"]) - , testSession "unused typeclass" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A () where" - , "class Foo a"]) - (R 2 0 2 8) - "Export ‘Foo’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A (Foo(..)) where" - , "class Foo a"]) - , testSession "infix" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A () where" - , "a `f` b = ()"]) - (R 2 0 2 11) - "Export ‘f’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A (f) where" - , "a `f` b = ()"]) - , testSession "function operator" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A () where" - , "(<|) = ($)"]) - (R 2 0 2 9) - "Export ‘<|’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "module A ((<|)) where" - , "(<|) = ($)"]) - , testSession "type synonym operator" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "{-# LANGUAGE TypeOperators #-}" - , "module A () where" - , "type (:<) = ()"]) - (R 3 0 3 13) - "Export ‘:<’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "{-# LANGUAGE TypeOperators #-}" - , "module A ((:<)) where" - , "type (:<) = ()"]) - , testSession "type family operator" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "{-# LANGUAGE TypeFamilies #-}" - , "{-# LANGUAGE TypeOperators #-}" - , "module A () where" - , "type family (:<)"]) - (R 4 0 4 15) - "Export ‘:<’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "{-# LANGUAGE TypeFamilies #-}" - , "{-# LANGUAGE TypeOperators #-}" - , "module A (type (:<)) where" - , "type family (:<)"]) - , testSession "typeclass operator" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "{-# LANGUAGE TypeOperators #-}" - , "module A () where" - , "class (:<) a"]) - (R 3 0 3 11) - "Export ‘:<’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "{-# LANGUAGE TypeOperators #-}" - , "module A (type (:<)(..)) where" - , "class (:<) a"]) - , testSession "newtype operator" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "{-# LANGUAGE TypeOperators #-}" - , "module A () where" - , "newtype (:<) = Foo ()"]) - (R 3 0 3 20) - "Export ‘:<’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "{-# LANGUAGE TypeOperators #-}" - , "module A (type (:<)(..)) where" - , "newtype (:<) = Foo ()"]) - , testSession "data type operator" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "{-# LANGUAGE TypeOperators #-}" - , "module A () where" - , "data (:<) = Foo ()"]) - (R 3 0 3 17) - "Export ‘:<’" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" - , "{-# LANGUAGE TypeOperators #-}" - , "module A (type (:<)(..)) where" - , "data (:<) = Foo ()"]) - ] - ] - where - template doc range = exportTemplate (Just range) doc - -exportTemplate :: Maybe Range -> T.Text -> T.Text -> Maybe T.Text -> Session () -exportTemplate mRange initialContent expectedAction expectedContents = do - doc <- createDoc "A.hs" "haskell" initialContent - _ <- waitForDiagnostics - actions <- case mRange of - Nothing -> getAllCodeActions doc - Just range -> getCodeActions doc range - case expectedContents of - Just content -> do - action <- liftIO $ pickActionWithTitle expectedAction actions - executeCodeAction action - contentAfterAction <- documentContents doc - liftIO $ content @=? contentAfterAction - Nothing -> - liftIO $ [_title | InR CodeAction{_title} <- actions, _title == expectedAction ] @?= [] - -removeExportTests :: TestTree -removeExportTests = testGroup "remove export actions" - [ testSession "single export" $ template - (T.unlines - [ "module A ( a ) where" - , "b :: ()" - , "b = ()"]) - "Remove ‘a’ from export" - (Just $ T.unlines - [ "module A ( ) where" - , "b :: ()" - , "b = ()"]) - , testSession "ending comma" $ template - (T.unlines - [ "module A ( a, ) where" - , "b :: ()" - , "b = ()"]) - "Remove ‘a’ from export" - (Just $ T.unlines - [ "module A ( ) where" - , "b :: ()" - , "b = ()"]) - , testSession "multiple exports" $ template - (T.unlines - [ "module A (a , c, b ) where" - , "a, c :: ()" - , "a = ()" - , "c = ()"]) - "Remove ‘b’ from export" - (Just $ T.unlines - [ "module A (a , c ) where" - , "a, c :: ()" - , "a = ()" - , "c = ()"]) - , testSession "not in scope constructor" $ template - (T.unlines - [ "module A (A (X,Y,Z,(:<)), ab) where" - , "data A = X Int | Y | (:<) Int" - , "ab :: ()" - , "ab = ()" - ]) - "Remove ‘Z’ from export" - (Just $ T.unlines - [ "module A (A (X,Y,(:<)), ab) where" - , "data A = X Int | Y | (:<) Int" - , "ab :: ()" - , "ab = ()"]) - , testSession "multiline export" $ template - (T.unlines - [ "module A (a" - , " , b" - , " , (:*:)" - , " , ) where" - , "a,b :: ()" - , "a = ()" - , "b = ()"]) - "Remove ‘:*:’ from export" - (Just $ T.unlines - [ "module A (a" - , " , b" - , " " - , " , ) where" - , "a,b :: ()" - , "a = ()" - , "b = ()"]) - , testSession "qualified re-export" $ template - (T.unlines - [ "module A (M.x,a) where" - , "import qualified Data.List as M" - , "a :: ()" - , "a = ()"]) - "Remove ‘M.x’ from export" - (Just $ T.unlines - [ "module A (a) where" - , "import qualified Data.List as M" - , "a :: ()" - , "a = ()"]) - , testSession "qualified re-export ending in '.'" $ template - (T.unlines - [ "module A ((M.@.),a) where" - , "import qualified Data.List as M" - , "a :: ()" - , "a = ()"]) - "Remove ‘M.@.’ from export" - (Just $ T.unlines - [ "module A (a) where" - , "import qualified Data.List as M" - , "a :: ()" - , "a = ()"]) - , testSession "export module" $ template - (T.unlines - [ "module A (module B) where" - , "a :: ()" - , "a = ()"]) - "Remove ‘module B’ from export" - (Just $ T.unlines - [ "module A () where" - , "a :: ()" - , "a = ()"]) - , testSession "dodgy export" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wall #-}" - , "module A (A (..)) where" - , "data X = X" - , "type A = X"]) - "Remove ‘A(..)’ from export" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wall #-}" - , "module A () where" - , "data X = X" - , "type A = X"]) - , testSession "dodgy export" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wall #-}" - , "module A (A (..)) where" - , "data X = X" - , "type A = X"]) - "Remove ‘A(..)’ from export" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wall #-}" - , "module A () where" - , "data X = X" - , "type A = X"]) - , testSession "duplicate module export" $ template - (T.unlines - [ "{-# OPTIONS_GHC -Wall #-}" - , "module A (module L,module L) where" - , "import Data.List as L" - , "a :: ()" - , "a = ()"]) - "Remove ‘Module L’ from export" - (Just $ T.unlines - [ "{-# OPTIONS_GHC -Wall #-}" - , "module A (module L) where" - , "import Data.List as L" - , "a :: ()" - , "a = ()"]) - , testSession "remove all exports single" $ template - (T.unlines - [ "module A (x) where" - , "a :: ()" - , "a = ()"]) - "Remove all redundant exports" - (Just $ T.unlines - [ "module A () where" - , "a :: ()" - , "a = ()"]) - , testSession "remove all exports two" $ template - (T.unlines - [ "module A (x,y) where" - , "a :: ()" - , "a = ()"]) - "Remove all redundant exports" - (Just $ T.unlines - [ "module A () where" - , "a :: ()" - , "a = ()"]) - , testSession "remove all exports three" $ template - (T.unlines - [ "module A (a,x,y) where" - , "a :: ()" - , "a = ()"]) - "Remove all redundant exports" - (Just $ T.unlines - [ "module A (a) where" - , "a :: ()" - , "a = ()"]) - , testSession "remove all exports composite" $ template - (T.unlines - [ "module A (x,y,b, module Ls, a, A(X,getW, Y, Z,(:-),getV), (-+), B(B)) where" - , "data A = X {getV :: Int} | Y {getV :: Int}" - , "data B = B" - , "a,b :: ()" - , "a = ()" - , "b = ()"]) - "Remove all redundant exports" - (Just $ T.unlines - [ "module A (b, a, A(X, Y,getV), B(B)) where" - , "data A = X {getV :: Int} | Y {getV :: Int}" - , "data B = B" - , "a,b :: ()" - , "a = ()" - , "b = ()"]) - ] - where - template = exportTemplate Nothing - addSigLensesTests :: TestTree addSigLensesTests = let pragmas = "{-# OPTIONS_GHC -Wmissing-signatures -Wmissing-pattern-synonym-signatures #-}" @@ -4826,55 +1502,6 @@ completionTest name src pos expected = testSessionWait name $ do when expectedDocs $ assertBool ("Missing docs: " <> T.unpack _label) (isJust _documentation) -completionCommandTest :: - String -> - [T.Text] -> - Position -> - T.Text -> - [T.Text] -> - TestTree -completionCommandTest name src pos wanted expected = testSession name $ do - docId <- createDoc "A.hs" "haskell" (T.unlines src) - _ <- waitForDiagnostics - compls <- skipManyTill anyMessage (getCompletions docId pos) - let wantedC = find ( \case - CompletionItem {_insertText = Just x} -> wanted `T.isPrefixOf` x - _ -> False - ) compls - case wantedC of - Nothing -> - liftIO $ assertFailure $ "Cannot find expected completion in: " <> show [_label | CompletionItem {_label} <- compls] - Just CompletionItem {..} -> do - c <- assertJust "Expected a command" _command - executeCommand c - if src /= expected - then do - void $ skipManyTill anyMessage loggingNotification - modifiedCode <- skipManyTill anyMessage (getDocumentEdit docId) - liftIO $ modifiedCode @?= T.unlines expected - else do - expectMessages SWorkspaceApplyEdit 1 $ \edit -> - liftIO $ assertFailure $ "Expected no edit but got: " <> show edit - -completionNoCommandTest :: - String -> - [T.Text] -> - Position -> - T.Text -> - TestTree -completionNoCommandTest name src pos wanted = testSession name $ do - docId <- createDoc "A.hs" "haskell" (T.unlines src) - _ <- waitForDiagnostics - compls <- getCompletions docId pos - let wantedC = find ( \case - CompletionItem {_insertText = Just x} -> wanted `T.isPrefixOf` x - _ -> False - ) compls - case wantedC of - Nothing -> - liftIO $ assertFailure $ "Cannot find expected completion in: " <> show [_label | CompletionItem {_label} <- compls] - Just CompletionItem{..} -> liftIO . assertBool ("Expected no command but got: " <> show _command) $ null _command - topLevelCompletionTests :: [TestTree] topLevelCompletionTests = [ @@ -5069,120 +1696,6 @@ nonLocalCompletionTests = (Position 2 10) [("readFile", CiFunction, "readFile ${1:FilePath}", True, True, Nothing)] ], - testGroup "auto import snippets" - [ completionCommandTest - "show imports not in list - simple" - ["{-# LANGUAGE NoImplicitPrelude #-}", - "module A where", "import Control.Monad (msum)", "f = joi"] - (Position 3 6) - "join" - ["{-# LANGUAGE NoImplicitPrelude #-}", - "module A where", "import Control.Monad (msum, join)", "f = joi"] - , completionCommandTest - "show imports not in list - multi-line" - ["{-# LANGUAGE NoImplicitPrelude #-}", - "module A where", "import Control.Monad (\n msum)", "f = joi"] - (Position 4 6) - "join" - ["{-# LANGUAGE NoImplicitPrelude #-}", - "module A where", "import Control.Monad (\n msum, join)", "f = joi"] - , completionCommandTest - "show imports not in list - names with _" - ["{-# LANGUAGE NoImplicitPrelude #-}", - "module A where", "import Control.Monad as M (msum)", "f = M.mapM_"] - (Position 3 11) - "mapM_" - ["{-# LANGUAGE NoImplicitPrelude #-}", - "module A where", "import Control.Monad as M (msum, mapM_)", "f = M.mapM_"] - , completionCommandTest - "show imports not in list - initial empty list" - ["{-# LANGUAGE NoImplicitPrelude #-}", - "module A where", "import Control.Monad as M ()", "f = M.joi"] - (Position 3 10) - "join" - ["{-# LANGUAGE NoImplicitPrelude #-}", - "module A where", "import Control.Monad as M (join)", "f = M.joi"] - , testGroup "qualified imports" - [ completionCommandTest - "single" - ["{-# LANGUAGE NoImplicitPrelude #-}", - "module A where", "import Control.Monad ()", "f = Control.Monad.joi"] - (Position 3 22) - "join" - ["{-# LANGUAGE NoImplicitPrelude #-}", - "module A where", "import Control.Monad (join)", "f = Control.Monad.joi"] - , completionCommandTest - "as" - ["{-# LANGUAGE NoImplicitPrelude #-}", - "module A where", "import Control.Monad as M ()", "f = M.joi"] - (Position 3 10) - "join" - ["{-# LANGUAGE NoImplicitPrelude #-}", - "module A where", "import Control.Monad as M (join)", "f = M.joi"] - , completionCommandTest - "multiple" - ["{-# LANGUAGE NoImplicitPrelude #-}", - "module A where", "import Control.Monad as M ()", "import Control.Monad as N ()", "f = N.joi"] - (Position 4 10) - "join" - ["{-# LANGUAGE NoImplicitPrelude #-}", - "module A where", "import Control.Monad as M ()", "import Control.Monad as N (join)", "f = N.joi"] - ] - , testGroup "Data constructor" - [ completionCommandTest - "not imported" - ["module A where", "import Text.Printf ()", "ZeroPad"] - (Position 2 4) - "ZeroPad" - ["module A where", "import Text.Printf (FormatAdjustment (ZeroPad))", "ZeroPad"] - , completionCommandTest - "parent imported abs" - ["module A where", "import Text.Printf (FormatAdjustment)", "ZeroPad"] - (Position 2 4) - "ZeroPad" - ["module A where", "import Text.Printf (FormatAdjustment (ZeroPad))", "ZeroPad"] - , completionNoCommandTest - "parent imported all" - ["module A where", "import Text.Printf (FormatAdjustment (..))", "ZeroPad"] - (Position 2 4) - "ZeroPad" - , completionNoCommandTest - "already imported" - ["module A where", "import Text.Printf (FormatAdjustment (ZeroPad))", "ZeroPad"] - (Position 2 4) - "ZeroPad" - , completionNoCommandTest - "function from Prelude" - ["module A where", "import Data.Maybe ()", "Nothing"] - (Position 2 4) - "Nothing" - , completionCommandTest - "type operator parent" - ["module A where", "import Data.Type.Equality ()", "f = Ref"] - (Position 2 8) - "Refl" - ["module A where", "import Data.Type.Equality (type (:~:) (Refl))", "f = Ref"] - ] - , testGroup "Record completion" - [ completionCommandTest - "not imported" - ["module A where", "import Text.Printf ()", "FormatParse"] - (Position 2 10) - "FormatParse {" - ["module A where", "import Text.Printf (FormatParse (FormatParse))", "FormatParse"] - , completionCommandTest - "parent imported" - ["module A where", "import Text.Printf (FormatParse)", "FormatParse"] - (Position 2 10) - "FormatParse {" - ["module A where", "import Text.Printf (FormatParse (FormatParse))", "FormatParse"] - , completionNoCommandTest - "already imported" - ["module A where", "import Text.Printf (FormatParse (FormatParse))", "FormatParse"] - (Position 2 10) - "FormatParse {" - ] - ], -- we need this test to make sure the ghcide completions module does not return completions for language pragmas. this functionality is turned on in hls completionTest "do not show pragma completions" @@ -5329,21 +1842,6 @@ packageCompletionTests = ] liftIO $ take 3 compls' @?= map Just ["fromList ${1:([Item l])}"] - , testGroup "auto import snippets" - [ completionCommandTest - "import Data.Sequence" - ["module A where", "foo :: Seq"] - (Position 1 9) - "Seq" - ["module A where", "import Data.Sequence (Seq)", "foo :: Seq"] - - , completionCommandTest - "qualified import" - ["module A where", "foo :: Seq.Seq"] - (Position 1 13) - "Seq" - ["module A where", "import qualified Data.Sequence as Seq", "foo :: Seq.Seq"] - ] ] projectCompletionTests :: [TestTree] @@ -6336,9 +2834,9 @@ asyncTests = testGroup "async" , "foo = id" ] void waitForDiagnostics - actions <- getCodeActions doc (Range (Position 1 0) (Position 1 0)) - liftIO $ [ _title | InR CodeAction{_title} <- actions] @=? - [ "add signature: foo :: a -> a" ] + codeLenses <- getCodeLenses doc + liftIO $ [ _title | CodeLens{_command = Just Command{_title}} <- codeLenses] @=? + [ "foo :: a -> a" ] , testSession "request" $ do -- Execute a custom request that will block for 1000 seconds void $ sendRequest (SCustomMethod "test") $ toJSON $ BlockSeconds 1000 @@ -6348,9 +2846,9 @@ asyncTests = testGroup "async" , "foo = id" ] void waitForDiagnostics - actions <- getCodeActions doc (Range (Position 1 0) (Position 1 0)) - liftIO $ [ _title | InR CodeAction{_title} <- actions] @=? - [ "add signature: foo :: a -> a" ] + codeLenses <- getCodeLenses doc + liftIO $ [ _title | CodeLens{_command = Just Command{_title}} <- codeLenses] @=? + [ "foo :: a -> a" ] ] @@ -6564,21 +3062,6 @@ testSessionWait name = testSession name . -- Experimentally, 0.5s seems to be long enough to wait for any final diagnostics to appear. ( >> expectNoMoreDiagnostics 0.5) -pickActionWithTitle :: T.Text -> [Command |? CodeAction] -> IO CodeAction -pickActionWithTitle title actions = do - assertBool ("Found no matching actions for " <> show title <> " in " <> show titles) (not $ null matches) - return $ head matches - where - titles = - [ actionTitle - | InR CodeAction { _title = actionTitle } <- actions - ] - matches = - [ action - | InR action@CodeAction { _title = actionTitle } <- actions - , title == actionTitle - ] - mkRange :: UInt -> UInt -> UInt -> UInt -> Range mkRange a b c d = Range (Position a b) (Position c d) @@ -6670,35 +3153,6 @@ openTestDataDoc path = do source <- liftIO $ readFileUtf8 $ "test/data" path createDoc path "haskell" source -findCodeActions :: TextDocumentIdentifier -> Range -> [T.Text] -> Session [CodeAction] -findCodeActions = findCodeActions' (==) "is not a superset of" - -findCodeActionsByPrefix :: TextDocumentIdentifier -> Range -> [T.Text] -> Session [CodeAction] -findCodeActionsByPrefix = findCodeActions' T.isPrefixOf "is not prefix of" - -findCodeActions' :: (T.Text -> T.Text -> Bool) -> String -> TextDocumentIdentifier -> Range -> [T.Text] -> Session [CodeAction] -findCodeActions' op errMsg doc range expectedTitles = do - actions <- getCodeActions doc range - let matches = sequence - [ listToMaybe - [ action - | InR action@CodeAction { _title = actionTitle } <- actions - , expectedTitle `op` actionTitle] - | expectedTitle <- expectedTitles] - let msg = show - [ actionTitle - | InR CodeAction { _title = actionTitle } <- actions - ] - ++ " " <> errMsg <> " " - ++ show expectedTitles - liftIO $ case matches of - Nothing -> assertFailure msg - Just _ -> pure () - return (fromJust matches) - -findCodeAction :: TextDocumentIdentifier -> Range -> T.Text -> Session CodeAction -findCodeAction doc range t = head <$> findCodeActions doc range [t] - unitTests :: Recorder (WithPriority Log) -> Logger -> TestTree unitTests recorder logger = do testGroup "Unit" @@ -7047,11 +3501,6 @@ withTempDir f = System.IO.Extra.withTempDir $ \dir -> do dir' <- canonicalizePath dir f dir' --- | Assert that a value is not 'Nothing', and extract the value. -assertJust :: MonadIO m => String -> Maybe a -> m a -assertJust s = \case - Nothing -> liftIO $ assertFailure s - Just x -> pure x -- | Before ghc9, lists of Char is displayed as [Char], but with ghc9 and up, it's displayed as String listOfChar :: T.Text diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 694f057534..6ea7ad476e 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -228,6 +228,11 @@ flag brittany default: True manual: True +flag refactor + description: Enable refactor plugin + default: True + manual: True + flag dynamic description: Build with the dyn rts default: True @@ -360,6 +365,11 @@ common brittany build-depends: hls-brittany-plugin ^>= 1.0 cpp-options: -Dhls_brittany +common refactor + if flag(refactor) + build-depends: hls-refactor-plugin ^>= 1.0 + cpp-options: -Dhls_refactor + library plugins import: common-deps -- configuration @@ -391,6 +401,7 @@ library plugins , ormolu , stylishHaskell , brittany + , refactor exposed-modules: HlsPlugins hs-source-dirs: src diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Rules.hs b/hls-graph/src/Development/IDE/Graph/Internal/Rules.hs index 7470c0e33e..97ea11eff7 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Rules.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Rules.hs @@ -46,7 +46,7 @@ addRule f = do runRule :: TheRules -> Key -> Maybe BS.ByteString -> RunMode -> Action (RunResult Value) runRule rules key@(Key t) bs mode = case Map.lookup (typeOf t) rules of - Nothing -> liftIO $ errorIO "Could not find key" + Nothing -> liftIO $ errorIO $ "Could not find key: " ++ show key Just x -> unwrapDynamic x key bs mode runRules :: Dynamic -> Rules () -> IO (TheRules, [Action ()]) diff --git a/hls-plugin-api/src/Ide/PluginUtils.hs b/hls-plugin-api/src/Ide/PluginUtils.hs index 209569a2bd..617b898fcb 100644 --- a/hls-plugin-api/src/Ide/PluginUtils.hs +++ b/hls-plugin-api/src/Ide/PluginUtils.hs @@ -31,7 +31,7 @@ module Ide.PluginUtils pluginResponse, handleMaybe, handleMaybeM, - throwPluginError + throwPluginError, ) where @@ -44,6 +44,7 @@ import Data.Algorithm.Diff import Data.Algorithm.DiffOutput import Data.Bifunctor (Bifunctor (first)) import qualified Data.HashMap.Strict as H +import Data.List (find) import Data.String (IsString (fromString)) import qualified Data.Text as T import Ide.Plugin.Config @@ -233,6 +234,7 @@ allLspCmdIds pid commands = concatMap go commands where go (plid, cmds) = map (mkLspCmdId pid plid . commandId) cmds + -- --------------------------------------------------------------------- getNormalizedFilePath :: Monad m => Uri -> ExceptT String m NormalizedFilePath diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index 4877b5271b..e9fbe8a28d 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -45,6 +45,7 @@ module Ide.Types , getProcessID, getPid , installSigUsr1Handler , responseError +, lookupCommandProvider ) where @@ -66,7 +67,7 @@ import Data.GADT.Compare import Data.Hashable (Hashable) import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HashMap -import Data.List.Extra (sortOn) +import Data.List.Extra (sortOn, find) import Data.List.NonEmpty (NonEmpty (..), toList) import qualified Data.Map as Map import Data.Maybe @@ -106,19 +107,36 @@ import Options.Applicative (ParserInfo) import System.FilePath import System.IO.Unsafe import Text.Regex.TDFA.Text () +import Control.Applicative ((<|>)) -- --------------------------------------------------------------------- -newtype IdePlugins ideState = IdePlugins_ { ipMap_ :: HashMap PluginId (PluginDescriptor ideState)} - deriving newtype (Semigroup, Monoid) +data IdePlugins ideState = IdePlugins_ + { ipMap_ :: HashMap PluginId (PluginDescriptor ideState) + , lookupCommandProvider :: CommandId -> Maybe PluginId + } -- | Smart constructor that deduplicates plugins pattern IdePlugins :: [PluginDescriptor ideState] -> IdePlugins ideState -pattern IdePlugins{ipMap} <- IdePlugins_ (sortOn (Down . pluginPriority) . HashMap.elems -> ipMap) +pattern IdePlugins{ipMap} <- IdePlugins_ (sortOn (Down . pluginPriority) . HashMap.elems -> ipMap) _ where - IdePlugins ipMap = IdePlugins_{ipMap_ = HashMap.fromList $ (pluginId &&& id) <$> ipMap} + IdePlugins ipMap = IdePlugins_{ipMap_ = HashMap.fromList $ (pluginId &&& id) <$> ipMap + , lookupCommandProvider = lookupPluginId ipMap + } {-# COMPLETE IdePlugins #-} +instance Semigroup (IdePlugins a) where + (IdePlugins_ a f) <> (IdePlugins_ b g) = IdePlugins_ (a <> b) (\x -> f x <|> g x) + +instance Monoid (IdePlugins a) where + mempty = IdePlugins_ mempty (const Nothing) + +-- | Lookup the plugin that exposes a particular command +lookupPluginId :: [PluginDescriptor a] -> CommandId -> Maybe PluginId +lookupPluginId ls cmd = pluginId <$> find go ls + where + go desc = cmd `elem` map commandId (pluginCommands desc) + -- | Hooks for modifying the 'DynFlags' at different times of the compilation -- process. Plugins can install a 'DynFlagsModifications' via -- 'pluginModifyDynflags' in their 'PluginDescriptor'. diff --git a/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal b/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal index e51ad55268..d726b48ee8 100644 --- a/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal +++ b/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal @@ -38,6 +38,7 @@ library , ghcide ^>=1.6 || ^>=1.7 , hashable , hls-plugin-api ^>=1.3 || ^>=1.4 + , hls-refactor-plugin , lens , lsp , mtl diff --git a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs index 0a48a3467b..23a02cfb60 100644 --- a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs +++ b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs @@ -31,6 +31,8 @@ import Development.IDE.Core.PositionMapping (PositionMapping, fromCurrentPosition, toCurrentRange) import Development.IDE.Types.Logger (Pretty (..)) +import qualified Development.IDE.GHC.ExactPrint as E +import Development.IDE.Plugin.CodeAction import Ide.Plugin.CodeRange.Rules (CodeRange (..), GetCodeRange (..), codeRangeRule) @@ -55,7 +57,7 @@ import Language.LSP.Types (List (List), import Prelude hiding (log, span) descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState -descriptor recorder plId = (defaultPluginDescriptor plId) +descriptor recorder plId = mkExactprintPluginDescriptor (cmapWithPrio LogExactPrint recorder) $ (defaultPluginDescriptor plId) { pluginHandlers = mkPluginHandler STextDocumentSelectionRange selectionRangeHandler -- TODO @sloorush add folding range -- <> mkPluginHandler STextDocumentFoldingRange foldingRangeHandler @@ -63,10 +65,12 @@ descriptor recorder plId = (defaultPluginDescriptor plId) } data Log = LogRules Rules.Log + | LogExactPrint E.Log instance Pretty Log where pretty log = case log of LogRules codeRangeLog -> pretty codeRangeLog + LogExactPrint exactPrintLog -> pretty exactPrintLog selectionRangeHandler :: IdeState -> PluginId -> SelectionRangeParams -> LspM c (Either ResponseError (List SelectionRange)) selectionRangeHandler ide _ SelectionRangeParams{..} = do diff --git a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs index 8a573d9ebb..fcd96ffea1 100644 --- a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs +++ b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs @@ -44,7 +44,8 @@ import qualified Data.Vector as V import Development.IDE import Development.IDE.Core.Rules (toIdeResult) import qualified Development.IDE.Core.Shake as Shake -import Development.IDE.GHC.Compat (Annotated, HieAST (..), +import Development.IDE.GHC.Compat.ExactPrint (Annotated) +import Development.IDE.GHC.Compat (HieAST (..), HieASTs (getAsts), ParsedSource, RefMap) import Development.IDE.GHC.Compat.Util diff --git a/plugins/hls-gadt-plugin/hls-gadt-plugin.cabal b/plugins/hls-gadt-plugin/hls-gadt-plugin.cabal index 23fac32e33..f109ea05d3 100644 --- a/plugins/hls-gadt-plugin/hls-gadt-plugin.cabal +++ b/plugins/hls-gadt-plugin/hls-gadt-plugin.cabal @@ -30,6 +30,7 @@ library , ghc-boot-th , ghc-exactprint , hls-plugin-api ^>= 1.4 + , hls-refactor-plugin , lens , lsp >=1.2.0.1 , mtl diff --git a/plugins/hls-gadt-plugin/src/Ide/Plugin/GHC.hs b/plugins/hls-gadt-plugin/src/Ide/Plugin/GHC.hs index 1a59ec2089..fe4ea1876c 100644 --- a/plugins/hls-gadt-plugin/src/Ide/Plugin/GHC.hs +++ b/plugins/hls-gadt-plugin/src/Ide/Plugin/GHC.hs @@ -16,6 +16,7 @@ import Data.List.Extra (stripInfix) import qualified Data.Text as T import Development.IDE import Development.IDE.GHC.Compat +import Development.IDE.GHC.Compat.ExactPrint import Ide.PluginUtils (subRange) import Language.Haskell.GHC.ExactPrint.Parsers (parseDecl) diff --git a/plugins/hls-haddock-comments-plugin/hls-haddock-comments-plugin.cabal b/plugins/hls-haddock-comments-plugin/hls-haddock-comments-plugin.cabal index 9acc5e916c..6de352816b 100644 --- a/plugins/hls-haddock-comments-plugin/hls-haddock-comments-plugin.cabal +++ b/plugins/hls-haddock-comments-plugin/hls-haddock-comments-plugin.cabal @@ -35,6 +35,7 @@ library , ghc-exactprint < 1 , ghcide ^>=1.6 || ^>=1.7 , hls-plugin-api ^>=1.3 || ^>=1.4 + , hls-refactor-plugin , lsp-types , text , unordered-containers diff --git a/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments.hs b/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments.hs index 4b162ac574..2993219893 100644 --- a/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments.hs +++ b/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments.hs @@ -15,7 +15,10 @@ import qualified Data.Map as Map import qualified Data.Text as T import Development.IDE hiding (pluginHandlers) import Development.IDE.GHC.Compat +import Development.IDE.Plugin.CodeAction +import Development.IDE.GHC.Compat.ExactPrint import Development.IDE.GHC.ExactPrint (GetAnnotatedParsedSource (..)) +import qualified Development.IDE.GHC.ExactPrint as E import Ide.Types import Language.Haskell.GHC.ExactPrint import Language.Haskell.GHC.ExactPrint.Types hiding (GhcPs) @@ -23,8 +26,8 @@ import Language.Haskell.GHC.ExactPrint.Utils import Language.LSP.Types ----------------------------------------------------------------------------- -descriptor :: PluginId -> PluginDescriptor IdeState -descriptor plId = +descriptor :: Recorder (WithPriority E.Log) -> PluginId -> PluginDescriptor IdeState +descriptor recorder plId = mkExactprintPluginDescriptor recorder $ (defaultPluginDescriptor plId) { pluginHandlers = mkPluginHandler STextDocumentCodeAction codeActionProvider } diff --git a/plugins/hls-haddock-comments-plugin/test/Main.hs b/plugins/hls-haddock-comments-plugin/test/Main.hs index 3eadb93416..22189c2590 100644 --- a/plugins/hls-haddock-comments-plugin/test/Main.hs +++ b/plugins/hls-haddock-comments-plugin/test/Main.hs @@ -19,7 +19,7 @@ main :: IO () main = defaultTestRunner tests haddockCommentsPlugin :: PluginDescriptor IdeState -haddockCommentsPlugin = HaddockComments.descriptor "haddockComments" +haddockCommentsPlugin = HaddockComments.descriptor mempty "haddockComments" tests :: TestTree tests = diff --git a/plugins/hls-refactor-plugin/LICENSE b/plugins/hls-refactor-plugin/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/plugins/hls-refactor-plugin/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal b/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal new file mode 100644 index 0000000000..9461819f08 --- /dev/null +++ b/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal @@ -0,0 +1,116 @@ +cabal-version: 3.0 +name: hls-refactor-plugin +version: 1.0.0.0 +synopsis: Exactprint refactorings for Haskell Language Server +description: + Please see the README on GitHub at + +license: Apache-2.0 +license-file: LICENSE +author: The Haskell IDE Team +copyright: The Haskell IDE Team +maintainer: zubin.duggal@gmail.com +category: Development +build-type: Simple +extra-source-files: + LICENSE + test/data/**/*.hs + test/data/**/*.yaml + +library + exposed-modules: Development.IDE.GHC.ExactPrint + Development.IDE.GHC.Compat.ExactPrint + Development.IDE.Plugin.CodeAction + Development.IDE.Plugin.CodeAction.Util + Development.IDE.GHC.Dump + other-modules: Development.IDE.Plugin.CodeAction.Args + Development.IDE.Plugin.CodeAction.ExactPrint + Development.IDE.Plugin.CodeAction.PositionIndexed + default-extensions: + BangPatterns + CPP + DataKinds + DeriveGeneric + DerivingStrategies + DerivingVia + DuplicateRecordFields + ExplicitNamespaces + FlexibleContexts + FlexibleInstances + FunctionalDependencies + GeneralizedNewtypeDeriving + LambdaCase + NamedFieldPuns + OverloadedStrings + PatternSynonyms + RankNTypes + RecordWildCards + ScopedTypeVariables + TupleSections + TypeApplications + TypeOperators + ViewPatterns + hs-source-dirs: src + build-depends: + , aeson + , base >=4.12 && <5 + , ghc + , bytestring + , ghc-boot + , regex-tdfa + , text-rope + , ghcide ^>=1.7 + , hls-plugin-api ^>=1.3 || ^>=1.4 + , lsp + , text + , transformers + , unordered-containers + , containers + , ghc-exactprint < 1 || >= 1.4 + , extra + , retrie + , syb + , hls-graph + , dlist + , deepseq + , mtl + , lens + , data-default + , time + ghc-options: -Wall -Wno-name-shadowing + default-language: Haskell2010 + +test-suite tests + type: exitcode-stdio-1.0 + default-language: Haskell2010 + hs-source-dirs: test + main-is: Main.hs + ghc-options: -O0 -threaded -rtsopts -with-rtsopts=-N -Wunused-imports + build-depends: + , base + , filepath + , hls-refactor-plugin + , hls-test-utils ^>=1.3 + , lens + , lsp-types + , text + , aeson + , hls-plugin-api + , parser-combinators + , data-default + , extra + , text-rope + , containers + , ghcide + , ghcide:ghcide-test-utils + , shake + , hls-plugin-api + , lsp-test + , network-uri + , directory + , async + , regex-tdfa + , tasty-rerun + , tasty-hunit + , tasty-expected-failure + , tasty diff --git a/ghcide/src/Development/IDE/GHC/Compat/ExactPrint.hs b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Compat/ExactPrint.hs similarity index 91% rename from ghcide/src/Development/IDE/GHC/Compat/ExactPrint.hs rename to plugins/hls-refactor-plugin/src/Development/IDE/GHC/Compat/ExactPrint.hs index a071ef4606..a80f251998 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/ExactPrint.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Compat/ExactPrint.hs @@ -1,7 +1,3 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE PatternSynonyms #-} - -- | This module contains compatibility constructs to write type signatures across -- multiple ghc-exactprint versions, accepting that anything more ambitious is -- pretty much impossible with the GHC 9.2 redesign of ghc-exactprint diff --git a/ghcide/src/Development/IDE/GHC/Dump.hs b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Dump.hs similarity index 99% rename from ghcide/src/Development/IDE/GHC/Dump.hs rename to plugins/hls-refactor-plugin/src/Development/IDE/GHC/Dump.hs index a81d6e1215..abaaa81cfb 100644 --- a/ghcide/src/Development/IDE/GHC/Dump.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Dump.hs @@ -2,6 +2,7 @@ module Development.IDE.GHC.Dump(showAstDataHtml) where import Data.Data hiding (Fixity) import Development.IDE.GHC.Compat hiding (NameAnn) +import Development.IDE.GHC.Compat.ExactPrint #if MIN_VERSION_ghc(8,10,1) import GHC.Hs.Dump #else diff --git a/ghcide/src/Development/IDE/GHC/ExactPrint.hs b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs similarity index 98% rename from ghcide/src/Development/IDE/GHC/ExactPrint.hs rename to plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs index f7a67d75bc..a0d8ce135e 100644 --- a/ghcide/src/Development/IDE/GHC/ExactPrint.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs @@ -1,10 +1,5 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE DerivingVia #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE FunctionalDependencies #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE GADTs #-} -- | This module hosts various abstractions and utility functions to work with ghc-exactprint. module Development.IDE.GHC.ExactPrint @@ -49,6 +44,7 @@ where import Control.Applicative (Alternative) import Control.Arrow (right, (***)) +import Control.DeepSeq import Control.Monad import qualified Control.Monad.Fail as Fail import Control.Monad.IO.Class (MonadIO) @@ -72,6 +68,7 @@ import qualified Development.IDE.Core.Shake as Shake import Development.IDE.GHC.Compat hiding (parseImport, parsePattern, parseType) +import Development.IDE.GHC.Compat.ExactPrint import Development.IDE.Graph (RuleResult, Rules) import Development.IDE.Graph.Classes import Development.IDE.Types.Location @@ -112,6 +109,12 @@ instance Pretty Log where pretty = \case LogShake shakeLog -> pretty shakeLog +instance Show (Annotated ParsedSource) where + show _ = "" + +instance NFData (Annotated ParsedSource) where + rnf = rwhnf + data GetAnnotatedParsedSource = GetAnnotatedParsedSource deriving (Eq, Show, Typeable, GHC.Generic) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs similarity index 93% rename from ghcide/src/Development/IDE/Plugin/CodeAction.hs rename to plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index 35f89ac108..d883e84e89 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -1,20 +1,17 @@ -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 - -{-# LANGUAGE CPP #-} -{-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE GADTs #-} +{-# LANGUAGE GADTs #-} module Development.IDE.Plugin.CodeAction ( + mkExactprintPluginDescriptor, iePluginDescriptor, typeSigsPluginDescriptor, bindingsPluginDescriptor, fillHolePluginDescriptor, - newImport, - newImportToEdit + extendImportPluginDescriptor, -- * For testing - , matchRegExMultipleImports + matchRegExMultipleImports ) where import Control.Applicative ((<|>)) @@ -22,8 +19,10 @@ import Control.Arrow (second, (&&&), (>>>)) import Control.Concurrent.STM.Stats (atomically) -import Control.Monad (guard, join) import Control.Monad.IO.Class +import Control.Monad.Trans.Maybe +import Control.Monad.Extra +import Data.Aeson import Data.Char import qualified Data.DList as DL import Data.Function @@ -40,18 +39,24 @@ import qualified Data.Set as S import qualified Data.Text as T import qualified Data.Text.Utf16.Rope as Rope import Data.Tuple.Extra (fst3) +import Development.IDE.Types.Logger hiding (group) import Development.IDE.Core.Rules import Development.IDE.Core.RuleTypes import Development.IDE.Core.Service import Development.IDE.GHC.Compat +import Development.IDE.GHC.Compat.ExactPrint import Development.IDE.GHC.Compat.Util import Development.IDE.GHC.Error +import Development.IDE.GHC.ExactPrint +import qualified Development.IDE.GHC.ExactPrint as E import Development.IDE.GHC.Util (printOutputable, - printRdrName, - traceAst) + printRdrName) +import Development.IDE.Core.Shake hiding (Log) import Development.IDE.Plugin.CodeAction.Args import Development.IDE.Plugin.CodeAction.ExactPrint +import Development.IDE.Plugin.CodeAction.Util import Development.IDE.Plugin.CodeAction.PositionIndexed +import Development.IDE.Plugin.Completions.Types import Development.IDE.Plugin.TypeLenses (suggestSignature) import Development.IDE.Types.Exports import Development.IDE.Types.Location @@ -60,21 +65,24 @@ import qualified GHC.LanguageExtensions as Lang import Ide.PluginUtils (subRange) import Ide.Types import qualified Language.LSP.Server as LSP -import Language.LSP.Types (CodeAction (..), +import Language.LSP.Types (ApplyWorkspaceEditParams(..), CodeAction (..), CodeActionContext (CodeActionContext, _diagnostics), CodeActionKind (CodeActionQuickFix, CodeActionUnknown), CodeActionParams (CodeActionParams), Command, Diagnostic (..), + MessageType (..), + ShowMessageParams (..), List (..), ResponseError, - SMethod (STextDocumentCodeAction), + SMethod (..), TextDocumentIdentifier (TextDocumentIdentifier), - TextEdit (TextEdit), + TextEdit (TextEdit, _range), UInt, WorkspaceEdit (WorkspaceEdit, _changeAnnotations, _changes, _documentChanges), type (|?) (InR), uriToFilePath) +import GHC.Exts (fromList) import Language.LSP.VFS (VirtualFile, _file_text) import Text.Regex.TDFA (mrAfter, @@ -90,7 +98,6 @@ import GHC (AddEpAnn (Ad LEpaComment, LocatedA) -import Control.Monad (msum) #else import Language.Haskell.GHC.ExactPrint.Types (Annotation (annsDP), DeltaPos, @@ -121,8 +128,8 @@ codeAction state _ (CodeActionParams _ _ (TextDocumentIdentifier uri) _range Cod ------------------------------------------------------------------------------------------------- -iePluginDescriptor :: PluginId -> PluginDescriptor IdeState -iePluginDescriptor plId = +iePluginDescriptor :: Recorder (WithPriority E.Log) -> PluginId -> PluginDescriptor IdeState +iePluginDescriptor recorder plId = let old = mkGhcideCAsPlugin [ wrap suggestExtendImport @@ -135,10 +142,10 @@ iePluginDescriptor plId = , wrap suggestExportUnusedTopBinding ] plId - in old {pluginHandlers = pluginHandlers old <> mkPluginHandler STextDocumentCodeAction codeAction} + in mkExactprintPluginDescriptor recorder $ old {pluginHandlers = pluginHandlers old <> mkPluginHandler STextDocumentCodeAction codeAction } -typeSigsPluginDescriptor :: PluginId -> PluginDescriptor IdeState -typeSigsPluginDescriptor = +typeSigsPluginDescriptor :: Recorder (WithPriority E.Log) -> PluginId -> PluginDescriptor IdeState +typeSigsPluginDescriptor recorder plId = mkExactprintPluginDescriptor recorder $ mkGhcideCAsPlugin [ wrap $ suggestSignature True , wrap suggestFillTypeWildcard @@ -146,18 +153,107 @@ typeSigsPluginDescriptor = , wrap suggestAddTypeAnnotationToSatisfyContraints , wrap suggestConstraint ] + plId -bindingsPluginDescriptor :: PluginId -> PluginDescriptor IdeState -bindingsPluginDescriptor = +bindingsPluginDescriptor :: Recorder (WithPriority E.Log) -> PluginId -> PluginDescriptor IdeState +bindingsPluginDescriptor recorder plId = mkExactprintPluginDescriptor recorder $ mkGhcideCAsPlugin [ wrap suggestReplaceIdentifier , wrap suggestImplicitParameter , wrap suggestNewDefinition , wrap suggestDeleteUnusedBinding ] + plId + +fillHolePluginDescriptor :: Recorder (WithPriority E.Log) -> PluginId -> PluginDescriptor IdeState +fillHolePluginDescriptor recorder plId = mkExactprintPluginDescriptor recorder (mkGhcideCAPlugin (wrap suggestFillHole) plId) + +extendImportPluginDescriptor :: Recorder (WithPriority E.Log) -> PluginId -> PluginDescriptor IdeState +extendImportPluginDescriptor recorder plId = mkExactprintPluginDescriptor recorder $ (defaultPluginDescriptor plId) + { pluginCommands = [extendImportCommand] } -fillHolePluginDescriptor :: PluginId -> PluginDescriptor IdeState -fillHolePluginDescriptor = mkGhcideCAPlugin $ wrap suggestFillHole + +-- | Add the ability for a plugin to call GetAnnotatedParsedSource +mkExactprintPluginDescriptor :: Recorder (WithPriority E.Log) -> PluginDescriptor a -> PluginDescriptor a +mkExactprintPluginDescriptor recorder desc = desc { pluginRules = pluginRules desc >> getAnnotatedParsedSourceRule recorder } + +------------------------------------------------------------------------------------------------- + + +extendImportCommand :: PluginCommand IdeState +extendImportCommand = + PluginCommand (CommandId extendImportCommandId) "additional edits for a completion" extendImportHandler + +extendImportHandler :: CommandFunction IdeState ExtendImport +extendImportHandler ideState edit@ExtendImport {..} = do + res <- liftIO $ runMaybeT $ extendImportHandler' ideState edit + whenJust res $ \(nfp, wedit@WorkspaceEdit {_changes}) -> do + let (_, List (head -> TextEdit {_range})) = fromJust $ _changes >>= listToMaybe . Map.toList + srcSpan = rangeToSrcSpan nfp _range + LSP.sendNotification SWindowShowMessage $ + ShowMessageParams MtInfo $ + "Import " + <> maybe ("‘" <> newThing) (\x -> "‘" <> x <> " (" <> newThing <> ")") thingParent + <> "’ from " + <> importName + <> " (at " + <> printOutputable srcSpan + <> ")" + void $ LSP.sendRequest SWorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing wedit) (\_ -> pure ()) + return $ Right Null + +extendImportHandler' :: IdeState -> ExtendImport -> MaybeT IO (NormalizedFilePath, WorkspaceEdit) +extendImportHandler' ideState ExtendImport {..} + | Just fp <- uriToFilePath doc, + nfp <- toNormalizedFilePath' fp = + do + (ModSummaryResult {..}, ps, contents) <- MaybeT $ liftIO $ + runAction "extend import" ideState $ + runMaybeT $ do + -- We want accurate edits, so do not use stale data here + msr <- MaybeT $ use GetModSummaryWithoutTimestamps nfp + ps <- MaybeT $ use GetAnnotatedParsedSource nfp + (_, contents) <- MaybeT $ use GetFileContents nfp + return (msr, ps, contents) + let df = ms_hspp_opts msrModSummary + wantedModule = mkModuleName (T.unpack importName) + wantedQual = mkModuleName . T.unpack <$> importQual + existingImport = find (isWantedModule wantedModule wantedQual) msrImports + case existingImport of + Just imp -> do + fmap (nfp,) $ liftEither $ + rewriteToWEdit df doc +#if !MIN_VERSION_ghc(9,2,0) + (annsA ps) +#endif + $ + extendImport (T.unpack <$> thingParent) (T.unpack newThing) (makeDeltaAst imp) + + Nothing -> do + let n = newImport importName sym importQual False + sym = if isNothing importQual then Just it else Nothing + it = case thingParent of + Nothing -> newThing + Just p -> p <> "(" <> newThing <> ")" + t <- liftMaybe $ snd <$> newImportToEdit n ps (fromMaybe "" contents) + return (nfp, WorkspaceEdit {_changes=Just (fromList [(doc,List [t])]), _documentChanges=Nothing, _changeAnnotations=Nothing}) + | otherwise = + mzero + +isWantedModule :: ModuleName -> Maybe ModuleName -> GenLocated l (ImportDecl GhcPs) -> Bool +isWantedModule wantedModule Nothing (L _ it@ImportDecl{ideclName, ideclHiding = Just (False, _)}) = + not (isQualifiedImport it) && unLoc ideclName == wantedModule +isWantedModule wantedModule (Just qual) (L _ ImportDecl{ideclAs, ideclName, ideclHiding = Just (False, _)}) = + unLoc ideclName == wantedModule && (wantedModule == qual || (unLoc . reLoc <$> ideclAs) == Just qual) +isWantedModule _ _ _ = False + + +liftMaybe :: Monad m => Maybe a -> MaybeT m a +liftMaybe a = MaybeT $ pure a + +liftEither :: Monad m => Either e a -> MaybeT m a +liftEither (Left _) = mzero +liftEither (Right x) = return x ------------------------------------------------------------------------------------------------- @@ -278,7 +374,7 @@ suggestHideShadow ps fileContents mTcM mHar Diagnostic {_message, _range} | otherwise = [] where L _ HsModule {hsmodImports} = astA ps - + suggests identifier modName s | Just tcM <- mTcM, Just har <- mHar, diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/Args.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs similarity index 99% rename from ghcide/src/Development/IDE/Plugin/CodeAction/Args.hs rename to plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs index 85f100ca66..59c0ac868f 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/Args.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs @@ -1,6 +1,3 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE FlexibleInstances #-} - module Development.IDE.Plugin.CodeAction.Args ( CodeActionTitle, CodeActionPreferred, @@ -27,6 +24,7 @@ import Development.IDE hiding (pluginHandlers) import Development.IDE.Core.Shake import Development.IDE.GHC.Compat +import Development.IDE.GHC.Compat.ExactPrint import Development.IDE.GHC.ExactPrint import Development.IDE.Plugin.CodeAction.ExactPrint (Rewrite, rewriteToEdit) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs similarity index 99% rename from ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs rename to plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index 4b516a16ab..57da3ee2f6 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -1,11 +1,5 @@ -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE CPP #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE ScopedTypeVariables #-} - +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE GADTs #-} module Development.IDE.Plugin.CodeAction.ExactPrint ( Rewrite (..), rewriteToEdit, @@ -40,6 +34,8 @@ import GHC.Stack (HasCallStack) import Language.Haskell.GHC.ExactPrint import Language.LSP.Types +import Development.IDE.Plugin.CodeAction.Util + -- GHC version specific imports. For any supported GHC version, make sure there is no warning in imports. #if MIN_VERSION_ghc(9,2,0) import Control.Lens (_head, _last, over) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/PositionIndexed.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/PositionIndexed.hs similarity index 100% rename from ghcide/src/Development/IDE/Plugin/CodeAction/PositionIndexed.hs rename to plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/PositionIndexed.hs diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/RuleTypes.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/RuleTypes.hs similarity index 96% rename from ghcide/src/Development/IDE/Plugin/CodeAction/RuleTypes.hs rename to plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/RuleTypes.hs index 05d6a7f33a..c338903d35 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/RuleTypes.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/RuleTypes.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE TypeFamilies #-} module Development.IDE.Plugin.CodeAction.RuleTypes (PackageExports(..) ,IdentInfo(..) diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Util.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Util.hs new file mode 100644 index 0000000000..bfcb0d7a37 --- /dev/null +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Util.hs @@ -0,0 +1,56 @@ +module Development.IDE.Plugin.CodeAction.Util where + +#if MIN_VERSION_ghc(9,2,0) +import GHC.Utils.Outputable +#else +import Development.IDE.GHC.Util +import Development.IDE.GHC.Compat.Util +import Development.IDE.GHC.Compat +#endif +import Data.Data (Data) +import qualified Data.Unique as U +import Debug.Trace +import Development.IDE.GHC.Compat.ExactPrint as GHC +import GHC.Stack +import System.Environment.Blank (getEnvDefault) +import System.IO.Unsafe +import Text.Printf +import Development.IDE.GHC.Dump (showAstDataHtml) +import Data.Time.Clock.POSIX (POSIXTime, getCurrentTime, + utcTimeToPOSIXSeconds) +-------------------------------------------------------------------------------- +-- Tracing exactprint terms + +-- Should in `Development.IDE.GHC.Orphans`, +-- leave it here to prevent cyclic module dependency + +{-# NOINLINE timestamp #-} +timestamp :: POSIXTime +timestamp = utcTimeToPOSIXSeconds $ unsafePerformIO getCurrentTime + +debugAST :: Bool +debugAST = unsafePerformIO (getEnvDefault "GHCIDE_DEBUG_AST" "0") == "1" + +-- | Prints an 'Outputable' value to stderr and to an HTML file for further inspection +traceAst :: (Data a, ExactPrint a, Outputable a, HasCallStack) => String -> a -> a +traceAst lbl x + | debugAST = trace doTrace x + | otherwise = x + where +#if MIN_VERSION_ghc(9,2,0) + renderDump = renderWithContext defaultSDocContext{sdocStyle = defaultDumpStyle, sdocPprDebug = True} +#else + renderDump = showSDocUnsafe . ppr +#endif + htmlDump = showAstDataHtml x + doTrace = unsafePerformIO $ do + u <- U.newUnique + let htmlDumpFileName = printf "/tmp/hls/%s-%s-%d.html" (show timestamp) lbl (U.hashUnique u) + writeFile htmlDumpFileName $ renderDump htmlDump + return $ unlines + [prettyCallStack callStack ++ ":" +#if MIN_VERSION_ghc(9,2,0) + , exactPrint x +#endif + , "file://" ++ htmlDumpFileName] + diff --git a/plugins/hls-refactor-plugin/test/Main.hs b/plugins/hls-refactor-plugin/test/Main.hs new file mode 100644 index 0000000000..dafbd1e843 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/Main.hs @@ -0,0 +1,3747 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE ImplicitParams #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE RecordWildCards #-} +{-# OPTIONS_GHC -Wno-deprecations -Wno-unticked-promoted-constructors #-} + +module Main + ( main + ) where + +import Control.Applicative.Combinators +import Control.Monad +import Data.Default +import Data.Foldable +import Data.List.Extra +import Data.Maybe +import qualified Data.Text as T +import Development.IDE.Test +import Development.IDE.GHC.Util +import Development.IDE.Plugin.Completions.Types (extendImportCommandId) +import Development.IDE.Types.Location +import Development.Shake (getDirectoryFilesIO) +import Language.LSP.Test +import Language.LSP.Types hiding + (SemanticTokenAbsolute (length, line), + SemanticTokenRelative (length), + SemanticTokensEdit (_start), + mkRange) +import qualified Language.LSP.Types.Lens as L +import Language.LSP.Types.Capabilities +import System.Directory +import System.FilePath +import System.Info.Extra (isMac, isWindows) +import qualified System.IO.Extra +import System.IO.Extra hiding (withTempDir) +import Control.Lens ((^.)) +import Data.Tuple.Extra +import Ide.Types +import qualified Language.LSP.Types as LSP +import System.Time.Extra +import Test.Tasty +import Test.Tasty.ExpectedFailure +import Test.Tasty.HUnit +import Text.Regex.TDFA ((=~)) + + +import Test.Hls +import Development.IDE.Plugin.CodeAction (matchRegExMultipleImports) + +import qualified Development.IDE.Plugin.CodeAction as Refactor +import qualified Development.IDE.Plugin.HLS.GhcIde as GhcIde + +main :: IO () +main = defaultTestRunner tests + +refactorPlugin :: [PluginDescriptor IdeState] +refactorPlugin = + [ Refactor.iePluginDescriptor mempty "ghcide-code-actions-imports-exports" + , Refactor.typeSigsPluginDescriptor mempty "ghcide-code-actions-type-signatures" + , Refactor.bindingsPluginDescriptor mempty "ghcide-code-actions-bindings" + , Refactor.fillHolePluginDescriptor mempty "ghcide-code-actions-fill-holes" + , Refactor.extendImportPluginDescriptor mempty "ghcide-completions-1" + ] ++ GhcIde.descriptors mempty + +tests :: TestTree +tests = + testGroup "refactor" + [ initializeTests + , codeActionTests + , codeActionHelperFunctionTests + , completionTests + ] + +initializeTests = withResource acquire release tests + where + tests :: IO (ResponseMessage Initialize) -> TestTree + tests getInitializeResponse = testGroup "initialize response capabilities" + [ chk " code action" _codeActionProvider (Just $ InL True) + , che " execute command" _executeCommandProvider [extendImportCommandId] + ] + where + chk :: (Eq a, Show a) => TestName -> (ServerCapabilities -> a) -> a -> TestTree + chk title getActual expected = + testCase title $ getInitializeResponse >>= \ir -> expected @=? (getActual . innerCaps) ir + + che :: TestName -> (ServerCapabilities -> Maybe ExecuteCommandOptions) -> [T.Text] -> TestTree + che title getActual expected = testCase title doTest + where + doTest = do + ir <- getInitializeResponse + let Just ExecuteCommandOptions {_commands = List commands} = getActual $ innerCaps ir + zipWithM_ (\e o -> T.isSuffixOf e o @? show (e,o)) expected commands + + acquire :: IO (ResponseMessage Initialize) + acquire = run initializeResponse + + + release :: ResponseMessage Initialize -> IO () + release = const $ pure () + + innerCaps :: ResponseMessage Initialize -> ServerCapabilities + innerCaps (ResponseMessage _ _ (Right (InitializeResult c _))) = c + innerCaps (ResponseMessage _ _ (Left _)) = error "Initialization error" + +completionTests :: TestTree +completionTests = + testGroup "auto import snippets" + [ completionCommandTest + "show imports not in list - simple" + ["{-# LANGUAGE NoImplicitPrelude #-}", + "module A where", "import Control.Monad (msum)", "f = joi"] + (Position 3 6) + "join" + ["{-# LANGUAGE NoImplicitPrelude #-}", + "module A where", "import Control.Monad (msum, join)", "f = joi"] + , completionCommandTest + "show imports not in list - multi-line" + ["{-# LANGUAGE NoImplicitPrelude #-}", + "module A where", "import Control.Monad (\n msum)", "f = joi"] + (Position 4 6) + "join" + ["{-# LANGUAGE NoImplicitPrelude #-}", + "module A where", "import Control.Monad (\n msum, join)", "f = joi"] + , completionCommandTest + "show imports not in list - names with _" + ["{-# LANGUAGE NoImplicitPrelude #-}", + "module A where", "import Control.Monad as M (msum)", "f = M.mapM_"] + (Position 3 11) + "mapM_" + ["{-# LANGUAGE NoImplicitPrelude #-}", + "module A where", "import Control.Monad as M (msum, mapM_)", "f = M.mapM_"] + , completionCommandTest + "show imports not in list - initial empty list" + ["{-# LANGUAGE NoImplicitPrelude #-}", + "module A where", "import Control.Monad as M ()", "f = M.joi"] + (Position 3 10) + "join" + ["{-# LANGUAGE NoImplicitPrelude #-}", + "module A where", "import Control.Monad as M (join)", "f = M.joi"] + , testGroup "qualified imports" + [ completionCommandTest + "single" + ["{-# LANGUAGE NoImplicitPrelude #-}", + "module A where", "import Control.Monad ()", "f = Control.Monad.joi"] + (Position 3 22) + "join" + ["{-# LANGUAGE NoImplicitPrelude #-}", + "module A where", "import Control.Monad (join)", "f = Control.Monad.joi"] + , completionCommandTest + "as" + ["{-# LANGUAGE NoImplicitPrelude #-}", + "module A where", "import Control.Monad as M ()", "f = M.joi"] + (Position 3 10) + "join" + ["{-# LANGUAGE NoImplicitPrelude #-}", + "module A where", "import Control.Monad as M (join)", "f = M.joi"] + , completionCommandTest + "multiple" + ["{-# LANGUAGE NoImplicitPrelude #-}", + "module A where", "import Control.Monad as M ()", "import Control.Monad as N ()", "f = N.joi"] + (Position 4 10) + "join" + ["{-# LANGUAGE NoImplicitPrelude #-}", + "module A where", "import Control.Monad as M ()", "import Control.Monad as N (join)", "f = N.joi"] + ] + , testGroup "Data constructor" + [ completionCommandTest + "not imported" + ["module A where", "import Text.Printf ()", "ZeroPad"] + (Position 2 4) + "ZeroPad" + ["module A where", "import Text.Printf (FormatAdjustment (ZeroPad))", "ZeroPad"] + , completionCommandTest + "parent imported abs" + ["module A where", "import Text.Printf (FormatAdjustment)", "ZeroPad"] + (Position 2 4) + "ZeroPad" + ["module A where", "import Text.Printf (FormatAdjustment (ZeroPad))", "ZeroPad"] + , completionNoCommandTest + "parent imported all" + ["module A where", "import Text.Printf (FormatAdjustment (..))", "ZeroPad"] + (Position 2 4) + "ZeroPad" + , completionNoCommandTest + "already imported" + ["module A where", "import Text.Printf (FormatAdjustment (ZeroPad))", "ZeroPad"] + (Position 2 4) + "ZeroPad" + , completionNoCommandTest + "function from Prelude" + ["module A where", "import Data.Maybe ()", "Nothing"] + (Position 2 4) + "Nothing" + , completionCommandTest + "type operator parent" + ["module A where", "import Data.Type.Equality ()", "f = Ref"] + (Position 2 8) + "Refl" + ["module A where", "import Data.Type.Equality (type (:~:) (Refl))", "f = Ref"] + ] + , testGroup "Record completion" + [ completionCommandTest + "not imported" + ["module A where", "import Text.Printf ()", "FormatParse"] + (Position 2 10) + "FormatParse {" + ["module A where", "import Text.Printf (FormatParse (FormatParse))", "FormatParse"] + , completionCommandTest + "parent imported" + ["module A where", "import Text.Printf (FormatParse)", "FormatParse"] + (Position 2 10) + "FormatParse {" + ["module A where", "import Text.Printf (FormatParse (FormatParse))", "FormatParse"] + , completionNoCommandTest + "already imported" + ["module A where", "import Text.Printf (FormatParse (FormatParse))", "FormatParse"] + (Position 2 10) + "FormatParse {" + ] + , testGroup "Package completion" + [ completionCommandTest + "import Data.Sequence" + ["module A where", "foo :: Seq"] + (Position 1 9) + "Seq" + ["module A where", "import Data.Sequence (Seq)", "foo :: Seq"] + + , completionCommandTest + "qualified import" + ["module A where", "foo :: Seq.Seq"] + (Position 1 13) + "Seq" + ["module A where", "import qualified Data.Sequence as Seq", "foo :: Seq.Seq"] + ] + ] + +completionCommandTest :: + String -> + [T.Text] -> + Position -> + T.Text -> + [T.Text] -> + TestTree +completionCommandTest name src pos wanted expected = testSession name $ do + docId <- createDoc "A.hs" "haskell" (T.unlines src) + _ <- waitForDiagnostics + compls <- skipManyTill anyMessage (getCompletions docId pos) + let wantedC = find ( \case + CompletionItem {_insertText = Just x} -> wanted `T.isPrefixOf` x + _ -> False + ) compls + case wantedC of + Nothing -> + liftIO $ assertFailure $ "Cannot find expected completion in: " <> show [_label | CompletionItem {_label} <- compls] + Just CompletionItem {..} -> do + c <- assertJust "Expected a command" _command + executeCommand c + if src /= expected + then do + void $ skipManyTill anyMessage loggingNotification + modifiedCode <- skipManyTill anyMessage (getDocumentEdit docId) + liftIO $ modifiedCode @?= T.unlines expected + else do + expectMessages SWorkspaceApplyEdit 1 $ \edit -> + liftIO $ assertFailure $ "Expected no edit but got: " <> show edit + +completionNoCommandTest :: + String -> + [T.Text] -> + Position -> + T.Text -> + TestTree +completionNoCommandTest name src pos wanted = testSession name $ do + docId <- createDoc "A.hs" "haskell" (T.unlines src) + _ <- waitForDiagnostics + compls <- getCompletions docId pos + let wantedC = find ( \case + CompletionItem {_insertText = Just x} -> wanted `T.isPrefixOf` x + _ -> False + ) compls + case wantedC of + Nothing -> + liftIO $ assertFailure $ "Cannot find expected completion in: " <> show [_label | CompletionItem {_label} <- compls] + Just CompletionItem{..} -> liftIO . assertBool ("Expected no command but got: " <> show _command) $ null _command + + +codeActionTests :: TestTree +codeActionTests = testGroup "code actions" + [ suggestImportDisambiguationTests + , insertImportTests + , extendImportTests + , renameActionTests + , typeWildCardActionTests + , removeImportTests + , suggestImportClassMethodTests + , suggestImportTests + , suggestHideShadowTests + , fixConstructorImportTests + , fixModuleImportTypoTests + , importRenameActionTests + , fillTypedHoleTests + , addSigActionTests + , insertNewDefinitionTests + , deleteUnusedDefinitionTests + , addInstanceConstraintTests + , addFunctionConstraintTests + , removeRedundantConstraintsTests + , addTypeAnnotationsToLiteralsTest + , exportUnusedTests + , addImplicitParamsConstraintTests + , removeExportTests + ] + +insertImportTests :: TestTree +insertImportTests = testGroup "insert import" + [ checkImport + "module where keyword lower in file no exports" + "WhereKeywordLowerInFileNoExports.hs" + "WhereKeywordLowerInFileNoExports.expected.hs" + "import Data.Int" + , checkImport + "module where keyword lower in file with exports" + "WhereDeclLowerInFile.hs" + "WhereDeclLowerInFile.expected.hs" + "import Data.Int" + , checkImport + "module where keyword lower in file with comments before it" + "WhereDeclLowerInFileWithCommentsBeforeIt.hs" + "WhereDeclLowerInFileWithCommentsBeforeIt.expected.hs" + "import Data.Int" + , expectFailBecause + "'findNextPragmaPosition' function doesn't account for case when shebang is not placed at top of file" + (checkImport + "Shebang not at top with spaces" + "ShebangNotAtTopWithSpaces.hs" + "ShebangNotAtTopWithSpaces.expected.hs" + "import Data.Monoid") + , expectFailBecause + "'findNextPragmaPosition' function doesn't account for case when shebang is not placed at top of file" + (checkImport + "Shebang not at top no space" + "ShebangNotAtTopNoSpace.hs" + "ShebangNotAtTopNoSpace.expected.hs" + "import Data.Monoid") + , expectFailBecause + ("'findNextPragmaPosition' function doesn't account for case " + ++ "when OPTIONS_GHC pragma is not placed at top of file") + (checkImport + "OPTIONS_GHC pragma not at top with spaces" + "OptionsNotAtTopWithSpaces.hs" + "OptionsNotAtTopWithSpaces.expected.hs" + "import Data.Monoid") + , expectFailBecause + ("'findNextPragmaPosition' function doesn't account for " + ++ "case when shebang is not placed at top of file") + (checkImport + "Shebang not at top of file" + "ShebangNotAtTop.hs" + "ShebangNotAtTop.expected.hs" + "import Data.Monoid") + , expectFailBecause + ("'findNextPragmaPosition' function doesn't account for case " + ++ "when OPTIONS_GHC is not placed at top of file") + (checkImport + "OPTIONS_GHC pragma not at top of file" + "OptionsPragmaNotAtTop.hs" + "OptionsPragmaNotAtTop.expected.hs" + "import Data.Monoid") + , expectFailBecause + ("'findNextPragmaPosition' function doesn't account for case when " + ++ "OPTIONS_GHC pragma is not placed at top of file") + (checkImport + "pragma not at top with comment at top" + "PragmaNotAtTopWithCommentsAtTop.hs" + "PragmaNotAtTopWithCommentsAtTop.expected.hs" + "import Data.Monoid") + , expectFailBecause + ("'findNextPragmaPosition' function doesn't account for case when " + ++ "OPTIONS_GHC pragma is not placed at top of file") + (checkImport + "pragma not at top multiple comments" + "PragmaNotAtTopMultipleComments.hs" + "PragmaNotAtTopMultipleComments.expected.hs" + "import Data.Monoid") + , expectFailBecause + "'findNextPragmaPosition' function doesn't account for case of multiline pragmas" + (checkImport + "after multiline language pragmas" + "MultiLinePragma.hs" + "MultiLinePragma.expected.hs" + "import Data.Monoid") + , checkImport + "pragmas not at top with module declaration" + "PragmaNotAtTopWithModuleDecl.hs" + "PragmaNotAtTopWithModuleDecl.expected.hs" + "import Data.Monoid" + , checkImport + "pragmas not at top with imports" + "PragmaNotAtTopWithImports.hs" + "PragmaNotAtTopWithImports.expected.hs" + "import Data.Monoid" + , checkImport + "above comment at top of module" + "CommentAtTop.hs" + "CommentAtTop.expected.hs" + "import Data.Monoid" + , checkImport + "above multiple comments below" + "CommentAtTopMultipleComments.hs" + "CommentAtTopMultipleComments.expected.hs" + "import Data.Monoid" + , checkImport + "above curly brace comment" + "CommentCurlyBraceAtTop.hs" + "CommentCurlyBraceAtTop.expected.hs" + "import Data.Monoid" + , checkImport + "above multi-line comment" + "MultiLineCommentAtTop.hs" + "MultiLineCommentAtTop.expected.hs" + "import Data.Monoid" + , checkImport + "above comment with no module explicit exports" + "NoExplicitExportCommentAtTop.hs" + "NoExplicitExportCommentAtTop.expected.hs" + "import Data.Monoid" + , checkImport + "above two-dash comment with no pipe" + "TwoDashOnlyComment.hs" + "TwoDashOnlyComment.expected.hs" + "import Data.Monoid" + , checkImport + "above comment with no (module .. where) decl" + "NoModuleDeclarationCommentAtTop.hs" + "NoModuleDeclarationCommentAtTop.expected.hs" + "import Data.Monoid" + , checkImport + "comment not at top with no (module .. where) decl" + "NoModuleDeclaration.hs" + "NoModuleDeclaration.expected.hs" + "import Data.Monoid" + , checkImport + "comment not at top (data dec is)" + "DataAtTop.hs" + "DataAtTop.expected.hs" + "import Data.Monoid" + , checkImport + "comment not at top (newtype is)" + "NewTypeAtTop.hs" + "NewTypeAtTop.expected.hs" + "import Data.Monoid" + , checkImport + "with no explicit module exports" + "NoExplicitExports.hs" + "NoExplicitExports.expected.hs" + "import Data.Monoid" + , checkImport + "add to correctly placed exisiting import" + "ImportAtTop.hs" + "ImportAtTop.expected.hs" + "import Data.Monoid" + , checkImport + "add to multiple correctly placed exisiting imports" + "MultipleImportsAtTop.hs" + "MultipleImportsAtTop.expected.hs" + "import Data.Monoid" + , checkImport + "with language pragma at top of module" + "LangPragmaModuleAtTop.hs" + "LangPragmaModuleAtTop.expected.hs" + "import Data.Monoid" + , checkImport + "with language pragma and explicit module exports" + "LangPragmaModuleWithComment.hs" + "LangPragmaModuleWithComment.expected.hs" + "import Data.Monoid" + , checkImport + "with language pragma at top and no module declaration" + "LanguagePragmaAtTop.hs" + "LanguagePragmaAtTop.expected.hs" + "import Data.Monoid" + , checkImport + "with multiple lang pragmas and no module declaration" + "MultipleLanguagePragmasNoModuleDeclaration.hs" + "MultipleLanguagePragmasNoModuleDeclaration.expected.hs" + "import Data.Monoid" + , checkImport + "with pragmas and shebangs" + "LanguagePragmasThenShebangs.hs" + "LanguagePragmasThenShebangs.expected.hs" + "import Data.Monoid" + , checkImport + "with pragmas and shebangs but no comment at top" + "PragmasAndShebangsNoComment.hs" + "PragmasAndShebangsNoComment.expected.hs" + "import Data.Monoid" + , checkImport + "module decl no exports under pragmas and shebangs" + "PragmasShebangsAndModuleDecl.hs" + "PragmasShebangsAndModuleDecl.expected.hs" + "import Data.Monoid" + , checkImport + "module decl with explicit import under pragmas and shebangs" + "PragmasShebangsModuleExplicitExports.hs" + "PragmasShebangsModuleExplicitExports.expected.hs" + "import Data.Monoid" + , checkImport + "module decl and multiple imports" + "ModuleDeclAndImports.hs" + "ModuleDeclAndImports.expected.hs" + "import Data.Monoid" + ] + +checkImport :: String -> FilePath -> FilePath -> T.Text -> TestTree +checkImport testComment originalPath expectedPath action = + testSessionWithExtraFiles "import-placement" testComment $ \dir -> + check (dir originalPath) (dir expectedPath) action + where + check :: FilePath -> FilePath -> T.Text -> Session () + check originalPath expectedPath action = do + oSrc <- liftIO $ readFileUtf8 originalPath + eSrc <- liftIO $ readFileUtf8 expectedPath + originalDoc <- createDoc originalPath "haskell" oSrc + _ <- waitForDiagnostics + shouldBeDoc <- createDoc expectedPath "haskell" eSrc + actionsOrCommands <- getAllCodeActions originalDoc + chosenAction <- liftIO $ pickActionWithTitle action actionsOrCommands + executeCodeAction chosenAction + originalDocAfterAction <- documentContents originalDoc + shouldBeDocContents <- documentContents shouldBeDoc + liftIO $ T.replace "\r\n" "\n" shouldBeDocContents @=? T.replace "\r\n" "\n" originalDocAfterAction + +renameActionTests :: TestTree +renameActionTests = testGroup "rename actions" + [ testSession "change to local variable name" $ do + let content = T.unlines + [ "module Testing where" + , "foo :: Int -> Int" + , "foo argName = argNme" + ] + doc <- createDoc "Testing.hs" "haskell" content + _ <- waitForDiagnostics + action <- findCodeAction doc (Range (Position 2 14) (Position 2 20)) "Replace with ‘argName’" + executeCodeAction action + contentAfterAction <- documentContents doc + let expectedContentAfterAction = T.unlines + [ "module Testing where" + , "foo :: Int -> Int" + , "foo argName = argName" + ] + liftIO $ expectedContentAfterAction @=? contentAfterAction + , testSession "change to name of imported function" $ do + let content = T.unlines + [ "module Testing where" + , "import Data.Maybe (maybeToList)" + , "foo :: Maybe a -> [a]" + , "foo = maybToList" + ] + doc <- createDoc "Testing.hs" "haskell" content + _ <- waitForDiagnostics + action <- findCodeAction doc (Range (Position 3 6) (Position 3 16)) "Replace with ‘maybeToList’" + executeCodeAction action + contentAfterAction <- documentContents doc + let expectedContentAfterAction = T.unlines + [ "module Testing where" + , "import Data.Maybe (maybeToList)" + , "foo :: Maybe a -> [a]" + , "foo = maybeToList" + ] + liftIO $ expectedContentAfterAction @=? contentAfterAction + , testSession "suggest multiple local variable names" $ do + let content = T.unlines + [ "module Testing where" + , "foo :: Char -> Char -> Char -> Char" + , "foo argument1 argument2 argument3 = argumentX" + ] + doc <- createDoc "Testing.hs" "haskell" content + _ <- waitForDiagnostics + _ <- findCodeActions doc (Range (Position 2 36) (Position 2 45)) + ["Replace with ‘argument1’", "Replace with ‘argument2’", "Replace with ‘argument3’"] + return() + , testSession "change infix function" $ do + let content = T.unlines + [ "module Testing where" + , "monus :: Int -> Int" + , "monus x y = max 0 (x - y)" + , "foo x y = x `monnus` y" + ] + doc <- createDoc "Testing.hs" "haskell" content + _ <- waitForDiagnostics + actionsOrCommands <- getCodeActions doc (Range (Position 3 12) (Position 3 20)) + [fixTypo] <- pure [action | InR action@CodeAction{ _title = actionTitle } <- actionsOrCommands, "monus" `T.isInfixOf` actionTitle ] + executeCodeAction fixTypo + contentAfterAction <- documentContents doc + let expectedContentAfterAction = T.unlines + [ "module Testing where" + , "monus :: Int -> Int" + , "monus x y = max 0 (x - y)" + , "foo x y = x `monus` y" + ] + liftIO $ expectedContentAfterAction @=? contentAfterAction + ] + +typeWildCardActionTests :: TestTree +typeWildCardActionTests = testGroup "type wildcard actions" + [ testUseTypeSignature "global signature" + [ "func :: _" + , "func x = x" + ] + [ "func :: p -> p" + , "func x = x" + ] + , testUseTypeSignature "local signature" + [ "func :: Int -> Int" + , "func x =" + , " let y :: _" + , " y = x * 2" + , " in y" + ] + [ "func :: Int -> Int" + , "func x =" + , " let y :: Int" + , " y = x * 2" + , " in y" + ] + , testUseTypeSignature "multi-line message 1" + [ "func :: _" + , "func x y = x + y" + ] + [ "func :: Integer -> Integer -> Integer" + , "func x y = x + y" + ] + , testUseTypeSignature "type in parentheses" + [ "func :: a -> _" + , "func x = (x, const x)" + ] + [ "func :: a -> (a, b -> a)" + , "func x = (x, const x)" + ] + , testUseTypeSignature "type in brackets" + [ "func :: _ -> Maybe a" + , "func xs = head xs" + ] + [ "func :: [Maybe a] -> Maybe a" + , "func xs = head xs" + ] + , testUseTypeSignature "unit type" + [ "func :: IO _" + , "func = putChar 'H'" + ] + [ "func :: IO ()" + , "func = putChar 'H'" + ] + , testUseTypeSignature "no spaces around '::'" + [ "func::_" + , "func x y = x + y" + ] + [ "func::Integer -> Integer -> Integer" + , "func x y = x + y" + ] + , testGroup "add parens if hole is part of bigger type" + [ testUseTypeSignature "subtype 1" + [ "func :: _ -> Integer -> Integer" + , "func x y = x + y" + ] + [ "func :: Integer -> Integer -> Integer" + , "func x y = x + y" + ] + , testUseTypeSignature "subtype 2" + [ "func :: Integer -> _ -> Integer" + , "func x y = x + y" + ] + [ "func :: Integer -> Integer -> Integer" + , "func x y = x + y" + ] + , testUseTypeSignature "subtype 3" + [ "func :: Integer -> Integer -> _" + , "func x y = x + y" + ] + [ "func :: Integer -> Integer -> Integer" + , "func x y = x + y" + ] + , testUseTypeSignature "subtype 4" + [ "func :: Integer -> _" + , "func x y = x + y" + ] + [ "func :: Integer -> (Integer -> Integer)" + , "func x y = x + y" + ] + ] + ] + where + -- | Test session of given name, checking action "Use type signature..." + -- on a test file with given content and comparing to expected result. + testUseTypeSignature name textIn textOut = testSession name $ do + let fileStart = "module Testing where" + content = T.unlines $ fileStart : textIn + expectedContentAfterAction = T.unlines $ fileStart : textOut + doc <- createDoc "Testing.hs" "haskell" content + _ <- waitForDiagnostics + actionsOrCommands <- getAllCodeActions doc + let [addSignature] = [action | InR action@CodeAction { _title = actionTitle } <- actionsOrCommands + , "Use type signature" `T.isInfixOf` actionTitle + ] + executeCodeAction addSignature + contentAfterAction <- documentContents doc + liftIO $ expectedContentAfterAction @=? contentAfterAction + +{-# HLINT ignore "Use nubOrd" #-} +removeImportTests :: TestTree +removeImportTests = testGroup "remove import actions" + [ testSession "redundant" $ do + let contentA = T.unlines + [ "module ModuleA where" + ] + _docA <- createDoc "ModuleA.hs" "haskell" contentA + let contentB = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "import ModuleA" + , "stuffB :: Integer" + , "stuffB = 123" + ] + docB <- createDoc "ModuleB.hs" "haskell" contentB + _ <- waitForDiagnostics + [InR action@CodeAction { _title = actionTitle }, _] + <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) + liftIO $ "Remove import" @=? actionTitle + executeCodeAction action + contentAfterAction <- documentContents docB + let expectedContentAfterAction = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "stuffB :: Integer" + , "stuffB = 123" + ] + liftIO $ expectedContentAfterAction @=? contentAfterAction + , testSession "qualified redundant" $ do + let contentA = T.unlines + [ "module ModuleA where" + ] + _docA <- createDoc "ModuleA.hs" "haskell" contentA + let contentB = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "import qualified ModuleA" + , "stuffB :: Integer" + , "stuffB = 123" + ] + docB <- createDoc "ModuleB.hs" "haskell" contentB + _ <- waitForDiagnostics + [InR action@CodeAction { _title = actionTitle }, _] + <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) + liftIO $ "Remove import" @=? actionTitle + executeCodeAction action + contentAfterAction <- documentContents docB + let expectedContentAfterAction = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "stuffB :: Integer" + , "stuffB = 123" + ] + liftIO $ expectedContentAfterAction @=? contentAfterAction + , testSession "redundant binding" $ do + let contentA = T.unlines + [ "module ModuleA where" + , "stuffA = False" + , "stuffB :: Integer" + , "stuffB = 123" + , "stuffC = ()" + , "_stuffD = '_'" + ] + _docA <- createDoc "ModuleA.hs" "haskell" contentA + let contentB = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "import ModuleA (stuffA, stuffB, _stuffD, stuffC, stuffA)" + , "main = print stuffB" + ] + docB <- createDoc "ModuleB.hs" "haskell" contentB + _ <- waitForDiagnostics + [InR action@CodeAction { _title = actionTitle }, _] + <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) + liftIO $ "Remove _stuffD, stuffA, stuffC from import" @=? actionTitle + executeCodeAction action + contentAfterAction <- documentContents docB + let expectedContentAfterAction = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "import ModuleA (stuffB)" + , "main = print stuffB" + ] + liftIO $ expectedContentAfterAction @=? contentAfterAction + , testSession "redundant binding - unicode regression " $ do + let contentA = T.unlines + [ "module ModuleA where" + , "data A = A" + , "ε :: Double" + , "ε = 0.5" + ] + _docA <- createDoc "ModuleA.hs" "haskell" contentA + let contentB = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "import ModuleA (A(..), ε)" + , "a = A" + ] + docB <- createDoc "ModuleB.hs" "haskell" contentB + _ <- waitForDiagnostics + [InR action@CodeAction { _title = actionTitle }, _] + <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) + liftIO $ "Remove ε from import" @=? actionTitle + executeCodeAction action + contentAfterAction <- documentContents docB + let expectedContentAfterAction = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "import ModuleA (A(..))" + , "a = A" + ] + liftIO $ expectedContentAfterAction @=? contentAfterAction + , testSession "redundant operator" $ do + let contentA = T.unlines + [ "module ModuleA where" + , "a !! _b = a" + , "a _b = a" + , "stuffB :: Integer" + , "stuffB = 123" + ] + _docA <- createDoc "ModuleA.hs" "haskell" contentA + let contentB = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "import qualified ModuleA as A ((), stuffB, (!!))" + , "main = print A.stuffB" + ] + docB <- createDoc "ModuleB.hs" "haskell" contentB + _ <- waitForDiagnostics + [InR action@CodeAction { _title = actionTitle }, _] + <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) + liftIO $ "Remove !!, from import" @=? actionTitle + executeCodeAction action + contentAfterAction <- documentContents docB + let expectedContentAfterAction = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "import qualified ModuleA as A (stuffB)" + , "main = print A.stuffB" + ] + liftIO $ expectedContentAfterAction @=? contentAfterAction + , testSession "redundant all import" $ do + let contentA = T.unlines + [ "module ModuleA where" + , "data A = A" + , "stuffB :: Integer" + , "stuffB = 123" + ] + _docA <- createDoc "ModuleA.hs" "haskell" contentA + let contentB = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "import ModuleA (A(..), stuffB)" + , "main = print stuffB" + ] + docB <- createDoc "ModuleB.hs" "haskell" contentB + _ <- waitForDiagnostics + [InR action@CodeAction { _title = actionTitle }, _] + <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) + liftIO $ "Remove A from import" @=? actionTitle + executeCodeAction action + contentAfterAction <- documentContents docB + let expectedContentAfterAction = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "import ModuleA (stuffB)" + , "main = print stuffB" + ] + liftIO $ expectedContentAfterAction @=? contentAfterAction + , testSession "redundant constructor import" $ do + let contentA = T.unlines + [ "module ModuleA where" + , "data D = A | B" + , "data E = F" + ] + _docA <- createDoc "ModuleA.hs" "haskell" contentA + let contentB = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "import ModuleA (D(A,B), E(F))" + , "main = B" + ] + docB <- createDoc "ModuleB.hs" "haskell" contentB + _ <- waitForDiagnostics + [InR action@CodeAction { _title = actionTitle }, _] + <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) + liftIO $ "Remove A, E, F from import" @=? actionTitle + executeCodeAction action + contentAfterAction <- documentContents docB + let expectedContentAfterAction = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "import ModuleA (D(B))" + , "main = B" + ] + liftIO $ expectedContentAfterAction @=? contentAfterAction + , testSession "import containing the identifier Strict" $ do + let contentA = T.unlines + [ "module Strict where" + ] + _docA <- createDoc "Strict.hs" "haskell" contentA + let contentB = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "import Strict" + ] + docB <- createDoc "ModuleB.hs" "haskell" contentB + _ <- waitForDiagnostics + [InR action@CodeAction { _title = actionTitle }, _] + <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) + liftIO $ "Remove import" @=? actionTitle + executeCodeAction action + contentAfterAction <- documentContents docB + let expectedContentAfterAction = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + ] + liftIO $ expectedContentAfterAction @=? contentAfterAction + , testSession "remove all" $ do + let content = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleA where" + , "import Data.Function (fix, (&))" + , "import qualified Data.Functor.Const" + , "import Data.Functor.Identity" + , "import Data.Functor.Sum (Sum (InL, InR))" + , "import qualified Data.Kind as K (Constraint, Type)" + , "x = InL (Identity 123)" + , "y = fix id" + , "type T = K.Type" + ] + doc <- createDoc "ModuleC.hs" "haskell" content + _ <- waitForDiagnostics + [_, _, _, _, InR action@CodeAction { _title = actionTitle }] + <- nub <$> getAllCodeActions doc + liftIO $ "Remove all redundant imports" @=? actionTitle + executeCodeAction action + contentAfterAction <- documentContents doc + let expectedContentAfterAction = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleA where" + , "import Data.Function (fix)" + , "import Data.Functor.Identity" + , "import Data.Functor.Sum (Sum (InL))" + , "import qualified Data.Kind as K (Type)" + , "x = InL (Identity 123)" + , "y = fix id" + , "type T = K.Type" + ] + liftIO $ expectedContentAfterAction @=? contentAfterAction + , testSession "remove unused operators whose name ends with '.'" $ do + let contentA = T.unlines + [ "module ModuleA where" + , "(@.) = 0 -- Must have an operator whose name ends with '.'" + , "a = 1 -- .. but also something else" + ] + _docA <- createDoc "ModuleA.hs" "haskell" contentA + let contentB = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "import ModuleA (a, (@.))" + , "x = a -- Must use something from module A, but not (@.)" + ] + docB <- createDoc "ModuleB.hs" "haskell" contentB + _ <- waitForDiagnostics + [InR action@CodeAction { _title = actionTitle }, _] + <- getCodeActions docB (Range (Position 2 0) (Position 2 5)) + liftIO $ "Remove @. from import" @=? actionTitle + executeCodeAction action + contentAfterAction <- documentContents docB + let expectedContentAfterAction = T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "module ModuleB where" + , "import ModuleA (a)" + , "x = a -- Must use something from module A, but not (@.)" + ] + liftIO $ expectedContentAfterAction @=? contentAfterAction + ] + +extendImportTests :: TestTree +extendImportTests = testGroup "extend import actions" + [ testGroup "with checkAll" $ tests True + , testGroup "without checkAll" $ tests False + ] + where + tests overrideCheckProject = + [ testSession "extend all constructors for record field" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "data A = B { a :: Int }" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA (A(B))" + , "f = a" + ]) + (Range (Position 2 4) (Position 2 5)) + [ "Add A(..) to the import list of ModuleA" + , "Add A(a) to the import list of ModuleA" + , "Add a to the import list of ModuleA" + ] + (T.unlines + [ "module ModuleB where" + , "import ModuleA (A(..))" + , "f = a" + ]) + , testSession "extend all constructors with sibling" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "data Foo" + , "data Bar" + , "data A = B | C" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA ( Foo, A (C) , Bar ) " + , "f = B" + ]) + (Range (Position 2 4) (Position 2 5)) + [ "Add A(..) to the import list of ModuleA" + , "Add A(B) to the import list of ModuleA" + ] + (T.unlines + [ "module ModuleB where" + , "import ModuleA ( Foo, A (..) , Bar ) " + , "f = B" + ]) + , testSession "extend all constructors with comment" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "data Foo" + , "data Bar" + , "data A = B | C" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA ( Foo, A (C{-comment--}) , Bar ) " + , "f = B" + ]) + (Range (Position 2 4) (Position 2 5)) + [ "Add A(..) to the import list of ModuleA" + , "Add A(B) to the import list of ModuleA" + ] + (T.unlines + [ "module ModuleB where" + , "import ModuleA ( Foo, A (..{-comment--}) , Bar ) " + , "f = B" + ]) + , testSession "extend all constructors for type operator" $ template + [] + ("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "import Data.Type.Equality ((:~:))" + , "x :: (:~:) [] []" + , "x = Refl" + ]) + (Range (Position 3 17) (Position 3 18)) + [ "Add (:~:)(..) to the import list of Data.Type.Equality" + , "Add type (:~:)(Refl) to the import list of Data.Type.Equality"] + (T.unlines + [ "module ModuleA where" + , "import Data.Type.Equality ((:~:) (..))" + , "x :: (:~:) [] []" + , "x = Refl" + ]) + , testSession "extend all constructors for class" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "class C a where" + , " m1 :: a -> a" + , " m2 :: a -> a" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA (C(m1))" + , "b = m2" + ]) + (Range (Position 2 5) (Position 2 5)) + [ "Add C(..) to the import list of ModuleA" + , "Add C(m2) to the import list of ModuleA" + , "Add m2 to the import list of ModuleA" + ] + (T.unlines + [ "module ModuleB where" + , "import ModuleA (C(..))" + , "b = m2" + ]) + , testSession "extend single line import with value" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "stuffA :: Double" + , "stuffA = 0.00750" + , "stuffB :: Integer" + , "stuffB = 123" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA as A (stuffB)" + , "main = print (stuffA, stuffB)" + ]) + (Range (Position 2 17) (Position 2 18)) + ["Add stuffA to the import list of ModuleA"] + (T.unlines + [ "module ModuleB where" + , "import ModuleA as A (stuffB, stuffA)" + , "main = print (stuffA, stuffB)" + ]) + , testSession "extend single line import with operator" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "(.*) :: Integer -> Integer -> Integer" + , "x .* y = x * y" + , "stuffB :: Integer" + , "stuffB = 123" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA as A (stuffB)" + , "main = print (stuffB .* stuffB)" + ]) + (Range (Position 2 17) (Position 2 18)) + ["Add (.*) to the import list of ModuleA"] + (T.unlines + [ "module ModuleB where" + , "import ModuleA as A (stuffB, (.*))" + , "main = print (stuffB .* stuffB)" + ]) + , testSession "extend single line import with infix constructor" $ template + [] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import Data.List.NonEmpty (fromList)" + , "main = case (fromList []) of _ :| _ -> pure ()" + ]) + (Range (Position 2 5) (Position 2 6)) + [ "Add NonEmpty((:|)) to the import list of Data.List.NonEmpty" + , "Add NonEmpty(..) to the import list of Data.List.NonEmpty" + ] + (T.unlines + [ "module ModuleB where" + , "import Data.List.NonEmpty (fromList, NonEmpty ((:|)))" + , "main = case (fromList []) of _ :| _ -> pure ()" + ]) + , testSession "extend single line import with prefix constructor" $ template + [] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import Prelude hiding (Maybe(..))" + , "import Data.Maybe (catMaybes)" + , "x = Just 10" + ]) + (Range (Position 3 5) (Position 2 6)) + [ "Add Maybe(Just) to the import list of Data.Maybe" + , "Add Maybe(..) to the import list of Data.Maybe" + ] + (T.unlines + [ "module ModuleB where" + , "import Prelude hiding (Maybe(..))" + , "import Data.Maybe (catMaybes, Maybe (Just))" + , "x = Just 10" + ]) + , testSession "extend single line import with type" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "type A = Double" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA ()" + , "b :: A" + , "b = 0" + ]) + (Range (Position 2 5) (Position 2 5)) + ["Add A to the import list of ModuleA"] + (T.unlines + [ "module ModuleB where" + , "import ModuleA (A)" + , "b :: A" + , "b = 0" + ]) + , testSession "extend single line import with constructor" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "data A = Constructor" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA (A)" + , "b :: A" + , "b = Constructor" + ]) + (Range (Position 3 5) (Position 3 5)) + [ "Add A(Constructor) to the import list of ModuleA" + , "Add A(..) to the import list of ModuleA" + ] + (T.unlines + [ "module ModuleB where" + , "import ModuleA (A (Constructor))" + , "b :: A" + , "b = Constructor" + ]) + , testSession "extend single line import with constructor (with comments)" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "data A = Constructor" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA (A ({-Constructor-}))" + , "b :: A" + , "b = Constructor" + ]) + (Range (Position 3 5) (Position 3 5)) + [ "Add A(Constructor) to the import list of ModuleA" + , "Add A(..) to the import list of ModuleA" + ] + (T.unlines + [ "module ModuleB where" + , "import ModuleA (A (Constructor{-Constructor-}))" + , "b :: A" + , "b = Constructor" + ]) + , testSession "extend single line import with mixed constructors" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "data A = ConstructorFoo | ConstructorBar" + , "a = 1" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA (A (ConstructorBar), a)" + , "b :: A" + , "b = ConstructorFoo" + ]) + (Range (Position 3 5) (Position 3 5)) + [ "Add A(ConstructorFoo) to the import list of ModuleA" + , "Add A(..) to the import list of ModuleA" + ] + (T.unlines + [ "module ModuleB where" + , "import ModuleA (A (ConstructorBar, ConstructorFoo), a)" + , "b :: A" + , "b = ConstructorFoo" + ]) + , testSession "extend single line qualified import with value" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "stuffA :: Double" + , "stuffA = 0.00750" + , "stuffB :: Integer" + , "stuffB = 123" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import qualified ModuleA as A (stuffB)" + , "main = print (A.stuffA, A.stuffB)" + ]) + (Range (Position 2 17) (Position 2 18)) + ["Add stuffA to the import list of ModuleA"] + (T.unlines + [ "module ModuleB where" + , "import qualified ModuleA as A (stuffB, stuffA)" + , "main = print (A.stuffA, A.stuffB)" + ]) + , testSession "extend multi line import with value" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "stuffA :: Double" + , "stuffA = 0.00750" + , "stuffB :: Integer" + , "stuffB = 123" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA (stuffB" + , " )" + , "main = print (stuffA, stuffB)" + ]) + (Range (Position 3 17) (Position 3 18)) + ["Add stuffA to the import list of ModuleA"] + (T.unlines + [ "module ModuleB where" + , "import ModuleA (stuffB, stuffA" + , " )" + , "main = print (stuffA, stuffB)" + ]) + , testSession "extend multi line import with trailing comma" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "stuffA :: Double" + , "stuffA = 0.00750" + , "stuffB :: Integer" + , "stuffB = 123" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA (stuffB," + , " )" + , "main = print (stuffA, stuffB)" + ]) + (Range (Position 3 17) (Position 3 18)) + ["Add stuffA to the import list of ModuleA"] + (T.unlines + [ "module ModuleB where" + , "import ModuleA (stuffB, stuffA," + , " )" + , "main = print (stuffA, stuffB)" + ]) + , testSession "extend single line import with method within class" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "class C a where" + , " m1 :: a -> a" + , " m2 :: a -> a" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA (C(m1))" + , "b = m2" + ]) + (Range (Position 2 5) (Position 2 5)) + [ "Add C(m2) to the import list of ModuleA" + , "Add m2 to the import list of ModuleA" + , "Add C(..) to the import list of ModuleA" + ] + (T.unlines + [ "module ModuleB where" + , "import ModuleA (C(m1, m2))" + , "b = m2" + ]) + , testSession "extend single line import with method without class" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "class C a where" + , " m1 :: a -> a" + , " m2 :: a -> a" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA (C(m1))" + , "b = m2" + ]) + (Range (Position 2 5) (Position 2 5)) + [ "Add m2 to the import list of ModuleA" + , "Add C(m2) to the import list of ModuleA" + , "Add C(..) to the import list of ModuleA" + ] + (T.unlines + [ "module ModuleB where" + , "import ModuleA (C(m1), m2)" + , "b = m2" + ]) + , testSession "extend import list with multiple choices" $ template + [("ModuleA.hs", T.unlines + -- this is just a dummy module to help the arguments needed for this test + [ "module ModuleA (bar) where" + , "bar = 10" + ]), + ("ModuleB.hs", T.unlines + -- this is just a dummy module to help the arguments needed for this test + [ "module ModuleB (bar) where" + , "bar = 10" + ])] + ("ModuleC.hs", T.unlines + [ "module ModuleC where" + , "import ModuleB ()" + , "import ModuleA ()" + , "foo = bar" + ]) + (Range (Position 3 17) (Position 3 18)) + ["Add bar to the import list of ModuleA", + "Add bar to the import list of ModuleB"] + (T.unlines + [ "module ModuleC where" + , "import ModuleB ()" + , "import ModuleA (bar)" + , "foo = bar" + ]) + , testSession "extend import list with constructor of type operator" $ template + [] + ("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "import Data.Type.Equality ((:~:))" + , "x :: (:~:) [] []" + , "x = Refl" + ]) + (Range (Position 3 17) (Position 3 18)) + [ "Add type (:~:)(Refl) to the import list of Data.Type.Equality" + , "Add (:~:)(..) to the import list of Data.Type.Equality"] + (T.unlines + [ "module ModuleA where" + , "import Data.Type.Equality ((:~:) (Refl))" + , "x :: (:~:) [] []" + , "x = Refl" + ]) + , expectFailBecause "importing pattern synonyms is unsupported" + $ testSession "extend import list with pattern synonym" $ template + [("ModuleA.hs", T.unlines + [ "{-# LANGUAGE PatternSynonyms #-}" + , "module ModuleA where" + , "pattern Some x = Just x" + ]) + ] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import A ()" + , "k (Some x) = x" + ]) + (Range (Position 2 3) (Position 2 7)) + ["Add pattern Some to the import list of A"] + (T.unlines + [ "module ModuleB where" + , "import A (pattern Some)" + , "k (Some x) = x" + ]) + , ignoreForGHC92 "Diagnostic message has no suggestions" $ + testSession "type constructor name same as data constructor name" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "newtype Foo = Foo Int" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA(Foo)" + , "f :: Foo" + , "f = Foo 1" + ]) + (Range (Position 3 4) (Position 3 6)) + ["Add Foo(Foo) to the import list of ModuleA", "Add Foo(..) to the import list of ModuleA"] + (T.unlines + [ "module ModuleB where" + , "import ModuleA(Foo (Foo))" + , "f :: Foo" + , "f = Foo 1" + ]) + , testSession "type constructor name same as data constructor name, data constructor extraneous" $ template + [("ModuleA.hs", T.unlines + [ "module ModuleA where" + , "data Foo = Foo" + ])] + ("ModuleB.hs", T.unlines + [ "module ModuleB where" + , "import ModuleA()" + , "f :: Foo" + , "f = undefined" + ]) + (Range (Position 2 4) (Position 2 6)) + ["Add Foo to the import list of ModuleA"] + (T.unlines + [ "module ModuleB where" + , "import ModuleA(Foo)" + , "f :: Foo" + , "f = undefined" + ]) + ] + where + codeActionTitle CodeAction{_title=x} = x + + template setUpModules moduleUnderTest range expectedTitles expectedContentB = do + configureCheckProject overrideCheckProject + + mapM_ (\x -> createDoc (fst x) "haskell" (snd x)) setUpModules + docB <- createDoc (fst moduleUnderTest) "haskell" (snd moduleUnderTest) + _ <- waitForDiagnostics + waitForProgressDone + actionsOrCommands <- getCodeActions docB range + let codeActions = + filter + (T.isPrefixOf "Add" . codeActionTitle) + [ca | InR ca <- actionsOrCommands] + actualTitles = codeActionTitle <$> codeActions + -- Note that we are not testing the order of the actions, as the + -- order of the expected actions indicates which one we'll execute + -- in this test, i.e., the first one. + liftIO $ sort expectedTitles @=? sort actualTitles + + -- Execute the action with the same title as the first expected one. + -- Since we tested that both lists have the same elements (possibly + -- in a different order), this search cannot fail. + let firstTitle:_ = expectedTitles + action = fromJust $ + find ((firstTitle ==) . codeActionTitle) codeActions + executeCodeAction action + contentAfterAction <- documentContents docB + liftIO $ expectedContentB @=? contentAfterAction + +fixModuleImportTypoTests :: TestTree +fixModuleImportTypoTests = testGroup "fix module import typo" + [ testSession "works when single module suggested" $ do + doc <- createDoc "A.hs" "haskell" "import Data.Cha" + _ <- waitForDiagnostics + InR action@CodeAction { _title = actionTitle } : _ <- getCodeActions doc (R 0 0 0 10) + liftIO $ actionTitle @?= "replace with Data.Char" + executeCodeAction action + contentAfterAction <- documentContents doc + liftIO $ contentAfterAction @?= "import Data.Char" + , testSession "works when multiple modules suggested" $ do + doc <- createDoc "A.hs" "haskell" "import Data.I" + _ <- waitForDiagnostics + actions <- sortOn (\(InR CodeAction{_title=x}) -> x) <$> getCodeActions doc (R 0 0 0 10) + let actionTitles = [ title | InR CodeAction{_title=title} <- actions ] + liftIO $ actionTitles @?= [ "replace with Data.Eq" + , "replace with Data.Int" + , "replace with Data.Ix" + ] + let InR replaceWithDataEq : _ = actions + executeCodeAction replaceWithDataEq + contentAfterAction <- documentContents doc + liftIO $ contentAfterAction @?= "import Data.Eq" + ] + +suggestImportClassMethodTests :: TestTree +suggestImportClassMethodTests = + testGroup + "suggest import class methods" + [ testGroup + "new" + [ testSession "via parent" $ + template' + "import Data.Semigroup (Semigroup(stimes))" + (Range (Position 4 2) (Position 4 8)), + testSession "top level" $ + template' + "import Data.Semigroup (stimes)" + (Range (Position 4 2) (Position 4 8)), + testSession "all" $ + template' + "import Data.Semigroup" + (Range (Position 4 2) (Position 4 8)) + ], + testGroup + "extend" + [ testSession "via parent" $ + template + [ "module A where", + "", + "import Data.Semigroup ()" + ] + (Range (Position 6 2) (Position 6 8)) + "Add Semigroup(stimes) to the import list of Data.Semigroup" + [ "module A where", + "", + "import Data.Semigroup (Semigroup (stimes))" + ], + testSession "top level" $ + template + [ "module A where", + "", + "import Data.Semigroup ()" + ] + (Range (Position 6 2) (Position 6 8)) + "Add stimes to the import list of Data.Semigroup" + [ "module A where", + "", + "import Data.Semigroup (stimes)" + ] + ] + ] + where + decls = + [ "data X = X", + "instance Semigroup X where", + " (<>) _ _ = X", + " stimes _ _ = X" + ] + template beforeContent range executeTitle expectedContent = do + doc <- createDoc "A.hs" "haskell" $ T.unlines (beforeContent <> decls) + _ <- waitForDiagnostics + waitForProgressDone + actions <- getCodeActions doc range + let actions' = [x | InR x <- actions] + titles = [_title | CodeAction {_title} <- actions'] + liftIO $ executeTitle `elem` titles @? T.unpack executeTitle <> " does not in " <> show titles + executeCodeAction $ fromJust $ find (\CodeAction {_title} -> _title == executeTitle) actions' + content <- documentContents doc + liftIO $ T.unlines (expectedContent <> decls) @=? content + template' executeTitle range = let c = ["module A where"] in template c range executeTitle $ c <> [executeTitle] + +suggestImportTests :: TestTree +suggestImportTests = testGroup "suggest import actions" + [ testGroup "Dont want suggestion" + [ -- extend import + test False ["Data.List.NonEmpty ()"] "f = nonEmpty" [] "import Data.List.NonEmpty (nonEmpty)" + -- data constructor + , test False [] "f = First" [] "import Data.Monoid (First)" + -- internal module + , test False [] "f :: Typeable a => a" ["f = undefined"] "import Data.Typeable.Internal (Typeable)" + -- package not in scope + , test False [] "f = quickCheck" [] "import Test.QuickCheck (quickCheck)" + -- don't omit the parent data type of a constructor + , test False [] "f ExitSuccess = ()" [] "import System.Exit (ExitSuccess)" + -- don't suggest data constructor when we only need the type + , test False [] "f :: Bar" [] "import Bar (Bar(Bar))" + -- don't suggest all data constructors for the data type + , test False [] "f :: Bar" [] "import Bar (Bar(..))" + ] + , testGroup "want suggestion" + [ wantWait [] "f = foo" [] "import Foo (foo)" + , wantWait [] "f = Bar" [] "import Bar (Bar(Bar))" + , wantWait [] "f :: Bar" [] "import Bar (Bar)" + , wantWait [] "f = Bar" [] "import Bar (Bar(..))" + , test True [] "f = nonEmpty" [] "import Data.List.NonEmpty (nonEmpty)" + , test True [] "f = (:|)" [] "import Data.List.NonEmpty (NonEmpty((:|)))" + , test True [] "f :: Natural" ["f = undefined"] "import Numeric.Natural (Natural)" + , test True [] "f :: Natural" ["f = undefined"] "import Numeric.Natural" + , test True [] "f :: NonEmpty ()" ["f = () :| []"] "import Data.List.NonEmpty (NonEmpty)" + , test True [] "f :: NonEmpty ()" ["f = () :| []"] "import Data.List.NonEmpty" + , test True [] "f = First" [] "import Data.Monoid (First(First))" + , test True [] "f = Endo" [] "import Data.Monoid (Endo(Endo))" + , test True [] "f = Version" [] "import Data.Version (Version(Version))" + , test True [] "f ExitSuccess = ()" [] "import System.Exit (ExitCode(ExitSuccess))" + , test True [] "f = AssertionFailed" [] "import Control.Exception (AssertionFailed(AssertionFailed))" + , test True ["Prelude"] "f = nonEmpty" [] "import Data.List.NonEmpty (nonEmpty)" + , test True [] "f :: Alternative f => f ()" ["f = undefined"] "import Control.Applicative (Alternative)" + , test True [] "f :: Alternative f => f ()" ["f = undefined"] "import Control.Applicative" + , test True [] "f = empty" [] "import Control.Applicative (Alternative(empty))" + , test True [] "f = empty" [] "import Control.Applicative (empty)" + , test True [] "f = empty" [] "import Control.Applicative" + , test True [] "f = (&)" [] "import Data.Function ((&))" + , test True [] "f = NE.nonEmpty" [] "import qualified Data.List.NonEmpty as NE" + , test True [] "f = Data.List.NonEmpty.nonEmpty" [] "import qualified Data.List.NonEmpty" + , test True [] "f :: Typeable a => a" ["f = undefined"] "import Data.Typeable (Typeable)" + , test True [] "f = pack" [] "import Data.Text (pack)" + , test True [] "f :: Text" ["f = undefined"] "import Data.Text (Text)" + , test True [] "f = [] & id" [] "import Data.Function ((&))" + , test True [] "f = (&) [] id" [] "import Data.Function ((&))" + , test True [] "f = (.|.)" [] "import Data.Bits (Bits((.|.)))" + , test True [] "f = (.|.)" [] "import Data.Bits ((.|.))" + , test True [] "f :: a ~~ b" [] "import Data.Type.Equality (type (~~))" + , test True + ["qualified Data.Text as T" + ] "f = T.putStrLn" [] "import qualified Data.Text.IO as T" + , test True + [ "qualified Data.Text as T" + , "qualified Data.Function as T" + ] "f = T.putStrLn" [] "import qualified Data.Text.IO as T" + , test True + [ "qualified Data.Text as T" + , "qualified Data.Function as T" + , "qualified Data.Functor as T" + , "qualified Data.Data as T" + ] "f = T.putStrLn" [] "import qualified Data.Text.IO as T" + , test True [] "f = (.|.)" [] "import Data.Bits (Bits(..))" + , test True [] "f = empty" [] "import Control.Applicative (Alternative(..))" + ] + , expectFailBecause "importing pattern synonyms is unsupported" $ test True [] "k (Some x) = x" [] "import B (pattern Some)" + ] + where + test = test' False + wantWait = test' True True + + test' waitForCheckProject wanted imps def other newImp = testSessionWithExtraFiles "hover" (T.unpack def) $ \dir -> do + configureCheckProject waitForCheckProject + let before = T.unlines $ "module A where" : ["import " <> x | x <- imps] ++ def : other + after = T.unlines $ "module A where" : ["import " <> x | x <- imps] ++ [newImp] ++ def : other + cradle = "cradle: {direct: {arguments: [-hide-all-packages, -package, base, -package, text, -package-env, -, A, Bar, Foo, B]}}" + liftIO $ writeFileUTF8 (dir "hie.yaml") cradle + liftIO $ writeFileUTF8 (dir "B.hs") $ unlines ["{-# LANGUAGE PatternSynonyms #-}", "module B where", "pattern Some x = Just x"] + doc <- createDoc "Test.hs" "haskell" before + waitForProgressDone + _ <- waitForDiagnostics + -- there isn't a good way to wait until the whole project is checked atm + when waitForCheckProject $ liftIO $ sleep 0.5 + let defLine = fromIntegral $ length imps + 1 + range = Range (Position defLine 0) (Position defLine maxBound) + actions <- getCodeActions doc range + if wanted + then do + action <- liftIO $ pickActionWithTitle newImp actions + executeCodeAction action + contentAfterAction <- documentContents doc + liftIO $ after @=? contentAfterAction + else + liftIO $ [_title | InR CodeAction{_title} <- actions, _title == newImp ] @?= [] + +suggestImportDisambiguationTests :: TestTree +suggestImportDisambiguationTests = testGroup "suggest import disambiguation actions" + [ testGroup "Hiding strategy works" + [ testGroup "fromList" + [ testCase "AVec" $ + compareHideFunctionTo [(8,9),(10,8)] + "Use AVec for fromList, hiding other imports" + "HideFunction.expected.fromList.A.hs" + , testCase "BVec" $ + compareHideFunctionTo [(8,9),(10,8)] + "Use BVec for fromList, hiding other imports" + "HideFunction.expected.fromList.B.hs" + ] + , testGroup "(++)" + [ testCase "EVec" $ + compareHideFunctionTo [(8,9),(10,8)] + "Use EVec for ++, hiding other imports" + "HideFunction.expected.append.E.hs" + , testCase "Hide functions without local" $ + compareTwo + "HideFunctionWithoutLocal.hs" [(8,8)] + "Use local definition for ++, hiding other imports" + "HideFunctionWithoutLocal.expected.hs" + , testCase "Prelude" $ + compareHideFunctionTo [(8,9),(10,8)] + "Use Prelude for ++, hiding other imports" + "HideFunction.expected.append.Prelude.hs" + , testCase "Prelude and local definition, infix" $ + compareTwo + "HidePreludeLocalInfix.hs" [(2,19)] + "Use local definition for ++, hiding other imports" + "HidePreludeLocalInfix.expected.hs" + , testCase "AVec, indented" $ + compareTwo "HidePreludeIndented.hs" [(3,8)] + "Use AVec for ++, hiding other imports" + "HidePreludeIndented.expected.hs" + + ] + , testGroup "Vec (type)" + [ testCase "AVec" $ + compareTwo + "HideType.hs" [(8,15)] + "Use AVec for Vec, hiding other imports" + "HideType.expected.A.hs" + , testCase "EVec" $ + compareTwo + "HideType.hs" [(8,15)] + "Use EVec for Vec, hiding other imports" + "HideType.expected.E.hs" + ] + ] + , testGroup "Qualify strategy" + [ testCase "won't suggest full name for qualified module" $ + withHideFunction [(8,9),(10,8)] $ \_ _ actions -> do + liftIO $ + assertBool "EVec.fromList must not be suggested" $ + "Replace with qualified: EVec.fromList" `notElem` + [ actionTitle + | InR CodeAction { _title = actionTitle } <- actions + ] + liftIO $ + assertBool "EVec.++ must not be suggested" $ + "Replace with qualified: EVec.++" `notElem` + [ actionTitle + | InR CodeAction { _title = actionTitle } <- actions + ] + , testGroup "fromList" + [ testCase "EVec" $ + compareHideFunctionTo [(8,9),(10,8)] + "Replace with qualified: E.fromList" + "HideFunction.expected.qualified.fromList.E.hs" + , testCase "Hide DuplicateRecordFields" $ + compareTwo + "HideQualifyDuplicateRecordFields.hs" [(9, 9)] + "Replace with qualified: AVec.fromList" + "HideQualifyDuplicateRecordFields.expected.hs" + , testCase "Duplicate record fields should not be imported" $ do + withTarget ("HideQualifyDuplicateRecordFields" <.> ".hs") [(9, 9)] $ + \_ _ actions -> do + liftIO $ + assertBool "Hidings should not be presented while DuplicateRecordFields exists" $ + all not [ actionTitle =~ T.pack "Use ([A-Za-z][A-Za-z0-9]*) for fromList, hiding other imports" + | InR CodeAction { _title = actionTitle } <- actions] + withTarget ("HideQualifyDuplicateRecordFieldsSelf" <.> ".hs") [(4, 4)] $ + \_ _ actions -> do + liftIO $ + assertBool "ambiguity from DuplicateRecordFields should not be imported" $ + null actions + ] + , testGroup "(++)" + [ testCase "Prelude, parensed" $ + compareHideFunctionTo [(8,9),(10,8)] + "Replace with qualified: Prelude.++" + "HideFunction.expected.qualified.append.Prelude.hs" + , testCase "Prelude, infix" $ + compareTwo + "HideQualifyInfix.hs" [(4,19)] + "Replace with qualified: Prelude.++" + "HideQualifyInfix.expected.hs" + , testCase "Prelude, left section" $ + compareTwo + "HideQualifySectionLeft.hs" [(4,15)] + "Replace with qualified: Prelude.++" + "HideQualifySectionLeft.expected.hs" + , testCase "Prelude, right section" $ + compareTwo + "HideQualifySectionRight.hs" [(4,18)] + "Replace with qualified: Prelude.++" + "HideQualifySectionRight.expected.hs" + ] + ] + ] + where + compareTwo original locs cmd expected = + withTarget original locs $ \dir doc actions -> do + expected <- liftIO $ + readFileUtf8 (dir expected) + action <- liftIO $ pickActionWithTitle cmd actions + executeCodeAction action + contentAfterAction <- documentContents doc + liftIO $ T.replace "\r\n" "\n" expected @=? contentAfterAction + compareHideFunctionTo = compareTwo "HideFunction.hs" + auxFiles = ["AVec.hs", "BVec.hs", "CVec.hs", "DVec.hs", "EVec.hs", "FVec.hs"] + withTarget file locs k = runWithExtraFiles "hiding" $ \dir -> do + doc <- openDoc file "haskell" + waitForProgressDone + void $ expectDiagnostics [(file, [(DsError, loc, "Ambiguous occurrence") | loc <- locs])] + actions <- getAllCodeActions doc + k dir doc actions + withHideFunction = withTarget ("HideFunction" <.> "hs") + +suggestHideShadowTests :: TestTree +suggestHideShadowTests = + testGroup + "suggest hide shadow" + [ testGroup + "single" + [ testOneCodeAction + "hide unsued" + "Hide on from Data.Function" + (1, 2) + (1, 4) + [ "import Data.Function" + , "f on = on" + , "g on = on" + ] + [ "import Data.Function hiding (on)" + , "f on = on" + , "g on = on" + ] + , testOneCodeAction + "extend hiding unsued" + "Hide on from Data.Function" + (1, 2) + (1, 4) + [ "import Data.Function hiding ((&))" + , "f on = on" + ] + [ "import Data.Function hiding (on, (&))" + , "f on = on" + ] + , testOneCodeAction + "delete unsued" + "Hide on from Data.Function" + (1, 2) + (1, 4) + [ "import Data.Function ((&), on)" + , "f on = on" + ] + [ "import Data.Function ((&))" + , "f on = on" + ] + , testOneCodeAction + "hide operator" + "Hide & from Data.Function" + (1, 2) + (1, 5) + [ "import Data.Function" + , "f (&) = (&)" + ] + [ "import Data.Function hiding ((&))" + , "f (&) = (&)" + ] + , testOneCodeAction + "remove operator" + "Hide & from Data.Function" + (1, 2) + (1, 5) + [ "import Data.Function ((&), on)" + , "f (&) = (&)" + ] + [ "import Data.Function ( on)" + , "f (&) = (&)" + ] + , noCodeAction + "don't remove already used" + (2, 2) + (2, 4) + [ "import Data.Function" + , "g = on" + , "f on = on" + ] + ] + , testGroup + "multi" + [ testOneCodeAction + "hide from B" + "Hide ++ from B" + (2, 2) + (2, 6) + [ "import B" + , "import C" + , "f (++) = (++)" + ] + [ "import B hiding ((++))" + , "import C" + , "f (++) = (++)" + ] + , testOneCodeAction + "hide from C" + "Hide ++ from C" + (2, 2) + (2, 6) + [ "import B" + , "import C" + , "f (++) = (++)" + ] + [ "import B" + , "import C hiding ((++))" + , "f (++) = (++)" + ] + , testOneCodeAction + "hide from Prelude" + "Hide ++ from Prelude" + (2, 2) + (2, 6) + [ "import B" + , "import C" + , "f (++) = (++)" + ] + [ "import B" + , "import C" + , "import Prelude hiding ((++))" + , "f (++) = (++)" + ] + , testMultiCodeActions + "manual hide all" + [ "Hide ++ from Prelude" + , "Hide ++ from C" + , "Hide ++ from B" + ] + (2, 2) + (2, 6) + [ "import B" + , "import C" + , "f (++) = (++)" + ] + [ "import B hiding ((++))" + , "import C hiding ((++))" + , "import Prelude hiding ((++))" + , "f (++) = (++)" + ] + , testOneCodeAction + "auto hide all" + "Hide ++ from all occurence imports" + (2, 2) + (2, 6) + [ "import B" + , "import C" + , "f (++) = (++)" + ] + [ "import B hiding ((++))" + , "import C hiding ((++))" + , "import Prelude hiding ((++))" + , "f (++) = (++)" + ] + ] + ] + where + testOneCodeAction testName actionName start end origin expected = + helper testName start end origin expected $ \cas -> do + action <- liftIO $ pickActionWithTitle actionName cas + executeCodeAction action + noCodeAction testName start end origin = + helper testName start end origin origin $ \cas -> do + liftIO $ cas @?= [] + testMultiCodeActions testName actionNames start end origin expected = + helper testName start end origin expected $ \cas -> do + let r = [ca | (InR ca) <- cas, ca ^. L.title `elem` actionNames] + liftIO $ + (length r == length actionNames) + @? "Expected " <> show actionNames <> ", but got " <> show cas <> " which is not its superset" + forM_ r executeCodeAction + helper testName (line1, col1) (line2, col2) origin expected k = testSession testName $ do + void $ createDoc "B.hs" "haskell" $ T.unlines docB + void $ createDoc "C.hs" "haskell" $ T.unlines docC + doc <- createDoc "A.hs" "haskell" $ T.unlines (header <> origin) + void waitForDiagnostics + waitForProgressDone + cas <- getCodeActions doc (Range (Position (fromIntegral $ line1 + length header) col1) (Position (fromIntegral $ line2 + length header) col2)) + void $ k [x | x@(InR ca) <- cas, "Hide" `T.isPrefixOf` (ca ^. L.title)] + contentAfter <- documentContents doc + liftIO $ contentAfter @?= T.unlines (header <> expected) + header = + [ "{-# OPTIONS_GHC -Wname-shadowing #-}" + , "module A where" + , "" + ] + -- for multi group + docB = + [ "module B where" + , "(++) = id" + ] + docC = + [ "module C where" + , "(++) = id" + ] + +insertNewDefinitionTests :: TestTree +insertNewDefinitionTests = testGroup "insert new definition actions" + [ testSession "insert new function definition" $ do + let txtB = + ["foo True = select [True]" + , "" + ,"foo False = False" + ] + txtB' = + ["" + ,"someOtherCode = ()" + ] + docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB') + _ <- waitForDiagnostics + InR action@CodeAction { _title = actionTitle } : _ + <- sortOn (\(InR CodeAction{_title=x}) -> x) <$> + getCodeActions docB (R 0 0 0 50) + liftIO $ actionTitle @?= "Define select :: [Bool] -> Bool" + executeCodeAction action + contentAfterAction <- documentContents docB + liftIO $ contentAfterAction @?= T.unlines (txtB ++ + [ "" + , "select :: [Bool] -> Bool" + , "select = _" + ] + ++ txtB') + , testSession "define a hole" $ do + let txtB = + ["foo True = _select [True]" + , "" + ,"foo False = False" + ] + txtB' = + ["" + ,"someOtherCode = ()" + ] + docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB') + _ <- waitForDiagnostics + InR action@CodeAction { _title = actionTitle } : _ + <- sortOn (\(InR CodeAction{_title=x}) -> x) <$> + getCodeActions docB (R 0 0 0 50) + liftIO $ actionTitle @?= "Define select :: [Bool] -> Bool" + executeCodeAction action + contentAfterAction <- documentContents docB + liftIO $ contentAfterAction @?= T.unlines ( + ["foo True = select [True]" + , "" + ,"foo False = False" + , "" + , "select :: [Bool] -> Bool" + , "select = _" + ] + ++ txtB') + , testSession "insert new function definition - Haddock comments" $ do + let start = ["foo :: Int -> Bool" + , "foo x = select (x + 1)" + , "" + , "-- | This is a haddock comment" + , "haddock :: Int -> Int" + , "haddock = undefined" + ] + let expected = ["foo :: Int -> Bool" + , "foo x = select (x + 1)" + , "" + , "select :: Int -> Bool" + , "select = _" + , "" + , "-- | This is a haddock comment" + , "haddock :: Int -> Int" + , "haddock = undefined"] + docB <- createDoc "ModuleB.hs" "haskell" (T.unlines start) + _ <- waitForDiagnostics + InR action@CodeAction { _title = actionTitle } : _ + <- sortOn (\(InR CodeAction{_title=x}) -> x) <$> + getCodeActions docB (R 1 0 0 50) + liftIO $ actionTitle @?= "Define select :: Int -> Bool" + executeCodeAction action + contentAfterAction <- documentContents docB + liftIO $ contentAfterAction @?= T.unlines expected + , testSession "insert new function definition - normal comments" $ do + let start = ["foo :: Int -> Bool" + , "foo x = select (x + 1)" + , "" + , "-- This is a normal comment" + , "normal :: Int -> Int" + , "normal = undefined" + ] + let expected = ["foo :: Int -> Bool" + , "foo x = select (x + 1)" + , "" + , "select :: Int -> Bool" + , "select = _" + , "" + , "-- This is a normal comment" + , "normal :: Int -> Int" + , "normal = undefined"] + docB <- createDoc "ModuleB.hs" "haskell" (T.unlines start) + _ <- waitForDiagnostics + InR action@CodeAction { _title = actionTitle } : _ + <- sortOn (\(InR CodeAction{_title=x}) -> x) <$> + getCodeActions docB (R 1 0 0 50) + liftIO $ actionTitle @?= "Define select :: Int -> Bool" + executeCodeAction action + contentAfterAction <- documentContents docB + liftIO $ contentAfterAction @?= T.unlines expected + ] + + +deleteUnusedDefinitionTests :: TestTree +deleteUnusedDefinitionTests = testGroup "delete unused definition action" + [ testSession "delete unused top level binding" $ + testFor + (T.unlines [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A (some) where" + , "" + , "f :: Int -> Int" + , "f 1 = let a = 1" + , " in a" + , "f 2 = 2" + , "" + , "some = ()" + ]) + (4, 0) + "Delete ‘f’" + (T.unlines [ + "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A (some) where" + , "" + , "some = ()" + ]) + + , testSession "delete unused top level binding defined in infix form" $ + testFor + (T.unlines [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A (some) where" + , "" + , "myPlus :: Int -> Int -> Int" + , "a `myPlus` b = a + b" + , "" + , "some = ()" + ]) + (4, 2) + "Delete ‘myPlus’" + (T.unlines [ + "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A (some) where" + , "" + , "some = ()" + ]) + , testSession "delete unused binding in where clause" $ + testFor + (T.unlines [ "{-# OPTIONS_GHC -Wunused-binds #-}" + , "module A (h, g) where" + , "" + , "h :: Int" + , "h = 3" + , "" + , "g :: Int" + , "g = 6" + , " where" + , " h :: Int" + , " h = 4" + , "" + ]) + (10, 4) + "Delete ‘h’" + (T.unlines [ "{-# OPTIONS_GHC -Wunused-binds #-}" + , "module A (h, g) where" + , "" + , "h :: Int" + , "h = 3" + , "" + , "g :: Int" + , "g = 6" + , " where" + , "" + ]) + , testSession "delete unused binding with multi-oneline signatures front" $ + testFor + (T.unlines [ "{-# OPTIONS_GHC -Wunused-binds #-}" + , "module A (b, c) where" + , "" + , "a, b, c :: Int" + , "a = 3" + , "b = 4" + , "c = 5" + ]) + (4, 0) + "Delete ‘a’" + (T.unlines [ "{-# OPTIONS_GHC -Wunused-binds #-}" + , "module A (b, c) where" + , "" + , "b, c :: Int" + , "b = 4" + , "c = 5" + ]) + , testSession "delete unused binding with multi-oneline signatures mid" $ + testFor + (T.unlines [ "{-# OPTIONS_GHC -Wunused-binds #-}" + , "module A (a, c) where" + , "" + , "a, b, c :: Int" + , "a = 3" + , "b = 4" + , "c = 5" + ]) + (5, 0) + "Delete ‘b’" + (T.unlines [ "{-# OPTIONS_GHC -Wunused-binds #-}" + , "module A (a, c) where" + , "" + , "a, c :: Int" + , "a = 3" + , "c = 5" + ]) + , testSession "delete unused binding with multi-oneline signatures end" $ + testFor + (T.unlines [ "{-# OPTIONS_GHC -Wunused-binds #-}" + , "module A (a, b) where" + , "" + , "a, b, c :: Int" + , "a = 3" + , "b = 4" + , "c = 5" + ]) + (6, 0) + "Delete ‘c’" + (T.unlines [ "{-# OPTIONS_GHC -Wunused-binds #-}" + , "module A (a, b) where" + , "" + , "a, b :: Int" + , "a = 3" + , "b = 4" + ]) + ] + where + testFor source pos expectedTitle expectedResult = do + docId <- createDoc "A.hs" "haskell" source + expectDiagnostics [ ("A.hs", [(DsWarning, pos, "not used")]) ] + + (action, title) <- extractCodeAction docId "Delete" pos + + liftIO $ title @?= expectedTitle + executeCodeAction action + contentAfterAction <- documentContents docId + liftIO $ contentAfterAction @?= expectedResult + + extractCodeAction docId actionPrefix (l, c) = do + [action@CodeAction { _title = actionTitle }] <- findCodeActionsByPrefix docId (R l c l c) [actionPrefix] + return (action, actionTitle) + +addTypeAnnotationsToLiteralsTest :: TestTree +addTypeAnnotationsToLiteralsTest = testGroup "add type annotations to literals to satisfy constraints" + [ + testSession "add default type to satisfy one constraint" $ + testFor + (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" + , "module A (f) where" + , "" + , "f = 1" + ]) + [ (DsWarning, (3, 4), "Defaulting the following constraint") ] + "Add type annotation ‘Integer’ to ‘1’" + (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" + , "module A (f) where" + , "" + , "f = (1 :: Integer)" + ]) + + , testSession "add default type to satisfy one constraint in nested expressions" $ + testFor + (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" + , "module A where" + , "" + , "f =" + , " let x = 3" + , " in x" + ]) + [ (DsWarning, (4, 12), "Defaulting the following constraint") ] + "Add type annotation ‘Integer’ to ‘3’" + (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" + , "module A where" + , "" + , "f =" + , " let x = (3 :: Integer)" + , " in x" + ]) + , testSession "add default type to satisfy one constraint in more nested expressions" $ + testFor + (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" + , "module A where" + , "" + , "f =" + , " let x = let y = 5 in y" + , " in x" + ]) + [ (DsWarning, (4, 20), "Defaulting the following constraint") ] + "Add type annotation ‘Integer’ to ‘5’" + (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" + , "module A where" + , "" + , "f =" + , " let x = let y = (5 :: Integer) in y" + , " in x" + ]) + , testSession "add default type to satisfy one constraint with duplicate literals" $ + testFor + (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" + , "{-# LANGUAGE OverloadedStrings #-}" + , "module A (f) where" + , "" + , "import Debug.Trace" + , "" + , "f = seq \"debug\" traceShow \"debug\"" + ]) + [ (DsWarning, (6, 8), "Defaulting the following constraint") + , (DsWarning, (6, 16), "Defaulting the following constraint") + ] + ("Add type annotation ‘" <> listOfChar <> "’ to ‘\"debug\"’") + (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" + , "{-# LANGUAGE OverloadedStrings #-}" + , "module A (f) where" + , "" + , "import Debug.Trace" + , "" + , "f = seq (\"debug\" :: " <> listOfChar <> ") traceShow \"debug\"" + ]) + , knownBrokenForGhcVersions [GHC92] "GHC 9.2 only has 'traceShow' in error span" $ + testSession "add default type to satisfy two constraints" $ + testFor + (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" + , "{-# LANGUAGE OverloadedStrings #-}" + , "module A (f) where" + , "" + , "import Debug.Trace" + , "" + , "f a = traceShow \"debug\" a" + ]) + [ (DsWarning, (6, 6), "Defaulting the following constraint") ] + ("Add type annotation ‘" <> listOfChar <> "’ to ‘\"debug\"’") + (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" + , "{-# LANGUAGE OverloadedStrings #-}" + , "module A (f) where" + , "" + , "import Debug.Trace" + , "" + , "f a = traceShow (\"debug\" :: " <> listOfChar <> ") a" + ]) + , knownBrokenForGhcVersions [GHC92] "GHC 9.2 only has 'traceShow' in error span" $ + testSession "add default type to satisfy two constraints with duplicate literals" $ + testFor + (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" + , "{-# LANGUAGE OverloadedStrings #-}" + , "module A (f) where" + , "" + , "import Debug.Trace" + , "" + , "f = seq (\"debug\" :: [Char]) (seq (\"debug\" :: [Char]) (traceShow \"debug\"))" + ]) + [ (DsWarning, (6, 54), "Defaulting the following constraint") ] + ("Add type annotation ‘" <> listOfChar <> "’ to ‘\"debug\"’") + (T.unlines [ "{-# OPTIONS_GHC -Wtype-defaults #-}" + , "{-# LANGUAGE OverloadedStrings #-}" + , "module A (f) where" + , "" + , "import Debug.Trace" + , "" + , "f = seq (\"debug\" :: [Char]) (seq (\"debug\" :: [Char]) (traceShow (\"debug\" :: " <> listOfChar <> ")))" + ]) + ] + where + testFor source diag expectedTitle expectedResult = do + docId <- createDoc "A.hs" "haskell" source + expectDiagnostics [ ("A.hs", diag) ] + + let cursors = map snd3 diag + (action, title) <- extractCodeAction docId "Add type annotation" (minimum cursors) (maximum cursors) + + liftIO $ title @?= expectedTitle + executeCodeAction action + contentAfterAction <- documentContents docId + liftIO $ contentAfterAction @?= expectedResult + + extractCodeAction docId actionPrefix (l,c) (l', c')= do + [action@CodeAction { _title = actionTitle }] <- findCodeActionsByPrefix docId (R l c l' c') [actionPrefix] + return (action, actionTitle) + + +fixConstructorImportTests :: TestTree +fixConstructorImportTests = testGroup "fix import actions" + [ testSession "fix constructor import" $ template + (T.unlines + [ "module ModuleA where" + , "data A = Constructor" + ]) + (T.unlines + [ "module ModuleB where" + , "import ModuleA(Constructor)" + ]) + (Range (Position 1 10) (Position 1 11)) + "Fix import of A(Constructor)" + (T.unlines + [ "module ModuleB where" + , "import ModuleA(A(Constructor))" + ]) + ] + where + template contentA contentB range expectedAction expectedContentB = do + _docA <- createDoc "ModuleA.hs" "haskell" contentA + docB <- createDoc "ModuleB.hs" "haskell" contentB + _diags <- waitForDiagnostics + InR action@CodeAction { _title = actionTitle } : _ + <- sortOn (\(InR CodeAction{_title=x}) -> x) <$> + getCodeActions docB range + liftIO $ expectedAction @=? actionTitle + executeCodeAction action + contentAfterAction <- documentContents docB + liftIO $ expectedContentB @=? contentAfterAction + +importRenameActionTests :: TestTree +importRenameActionTests = testGroup "import rename actions" + [ testSession "Data.Mape -> Data.Map" $ check "Map" + , testSession "Data.Mape -> Data.Maybe" $ check "Maybe" ] where + check modname = do + let content = T.unlines + [ "module Testing where" + , "import Data.Mape" + ] + doc <- createDoc "Testing.hs" "haskell" content + _ <- waitForDiagnostics + actionsOrCommands <- getCodeActions doc (Range (Position 1 8) (Position 1 16)) + let [changeToMap] = [action | InR action@CodeAction{ _title = actionTitle } <- actionsOrCommands, ("Data." <> modname) `T.isInfixOf` actionTitle ] + executeCodeAction changeToMap + contentAfterAction <- documentContents doc + let expectedContentAfterAction = T.unlines + [ "module Testing where" + , "import Data." <> modname + ] + liftIO $ expectedContentAfterAction @=? contentAfterAction + +fillTypedHoleTests :: TestTree +fillTypedHoleTests = let + + sourceCode :: T.Text -> T.Text -> T.Text -> T.Text + sourceCode a b c = T.unlines + [ "module Testing where" + , "" + , "globalConvert :: Int -> String" + , "globalConvert = undefined" + , "" + , "globalInt :: Int" + , "globalInt = 3" + , "" + , "bar :: Int -> Int -> String" + , "bar n parameterInt = " <> a <> " (n + " <> b <> " + " <> c <> ") where" + , " localConvert = (flip replicate) 'x'" + , "" + , "foo :: () -> Int -> String" + , "foo = undefined" + + ] + + check :: T.Text -> T.Text -> T.Text -> T.Text -> T.Text -> T.Text -> T.Text -> TestTree + check actionTitle + oldA oldB oldC + newA newB newC = testSession (T.unpack actionTitle) $ do + let originalCode = sourceCode oldA oldB oldC + let expectedCode = sourceCode newA newB newC + doc <- createDoc "Testing.hs" "haskell" originalCode + _ <- waitForDiagnostics + actionsOrCommands <- getCodeActions doc (Range (Position 9 0) (Position 9 maxBound)) + chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands + executeCodeAction chosenAction + modifiedCode <- documentContents doc + liftIO $ expectedCode @=? modifiedCode + in + testGroup "fill typed holes" + [ check "replace _ with show" + "_" "n" "n" + "show" "n" "n" + + , check "replace _ with globalConvert" + "_" "n" "n" + "globalConvert" "n" "n" + + , check "replace _convertme with localConvert" + "_convertme" "n" "n" + "localConvert" "n" "n" + + , check "replace _b with globalInt" + "_a" "_b" "_c" + "_a" "globalInt" "_c" + + , check "replace _c with globalInt" + "_a" "_b" "_c" + "_a" "_b" "globalInt" + + , check "replace _c with parameterInt" + "_a" "_b" "_c" + "_a" "_b" "parameterInt" + , check "replace _ with foo _" + "_" "n" "n" + "(foo _)" "n" "n" + , testSession "replace _toException with E.toException" $ do + let mkDoc x = T.unlines + [ "module Testing where" + , "import qualified Control.Exception as E" + , "ioToSome :: E.IOException -> E.SomeException" + , "ioToSome = " <> x ] + doc <- createDoc "Test.hs" "haskell" $ mkDoc "_toException" + _ <- waitForDiagnostics + actions <- getCodeActions doc (Range (Position 3 0) (Position 3 maxBound)) + chosen <- liftIO $ pickActionWithTitle "replace _toException with E.toException" actions + executeCodeAction chosen + modifiedCode <- documentContents doc + liftIO $ mkDoc "E.toException" @=? modifiedCode + , testSession "filling infix type hole uses prefix notation" $ do + let mkDoc x = T.unlines + [ "module Testing where" + , "data A = A" + , "foo :: A -> A -> A" + , "foo A A = A" + , "test :: A -> A -> A" + , "test a1 a2 = a1 " <> x <> " a2" + ] + doc <- createDoc "Test.hs" "haskell" $ mkDoc "`_`" + _ <- waitForDiagnostics + actions <- getCodeActions doc (Range (Position 5 16) (Position 5 19)) + chosen <- liftIO $ pickActionWithTitle "replace _ with foo" actions + executeCodeAction chosen + modifiedCode <- documentContents doc + liftIO $ mkDoc "`foo`" @=? modifiedCode + , testSession "postfix hole uses postfix notation of infix operator" $ do + let mkDoc x = T.unlines + [ "module Testing where" + , "test :: Int -> Int -> Int" + , "test a1 a2 = " <> x <> " a1 a2" + ] + doc <- createDoc "Test.hs" "haskell" $ mkDoc "_" + _ <- waitForDiagnostics + actions <- getCodeActions doc (Range (Position 2 13) (Position 2 14)) + chosen <- liftIO $ pickActionWithTitle "replace _ with (+)" actions + executeCodeAction chosen + modifiedCode <- documentContents doc + liftIO $ mkDoc "(+)" @=? modifiedCode + , testSession "filling infix type hole uses infix operator" $ do + let mkDoc x = T.unlines + [ "module Testing where" + , "test :: Int -> Int -> Int" + , "test a1 a2 = a1 " <> x <> " a2" + ] + doc <- createDoc "Test.hs" "haskell" $ mkDoc "`_`" + _ <- waitForDiagnostics + actions <- getCodeActions doc (Range (Position 2 16) (Position 2 19)) + chosen <- liftIO $ pickActionWithTitle "replace _ with (+)" actions + executeCodeAction chosen + modifiedCode <- documentContents doc + liftIO $ mkDoc "+" @=? modifiedCode + ] + +addInstanceConstraintTests :: TestTree +addInstanceConstraintTests = let + missingConstraintSourceCode :: Maybe T.Text -> T.Text + missingConstraintSourceCode mConstraint = + let constraint = maybe "" (<> " => ") mConstraint + in T.unlines + [ "module Testing where" + , "" + , "data Wrap a = Wrap a" + , "" + , "instance " <> constraint <> "Eq (Wrap a) where" + , " (Wrap x) == (Wrap y) = x == y" + ] + + incompleteConstraintSourceCode :: Maybe T.Text -> T.Text + incompleteConstraintSourceCode mConstraint = + let constraint = maybe "Eq a" (\c -> "(Eq a, " <> c <> ")") mConstraint + in T.unlines + [ "module Testing where" + , "" + , "data Pair a b = Pair a b" + , "" + , "instance " <> constraint <> " => Eq (Pair a b) where" + , " (Pair x y) == (Pair x' y') = x == x' && y == y'" + ] + + incompleteConstraintSourceCode2 :: Maybe T.Text -> T.Text + incompleteConstraintSourceCode2 mConstraint = + let constraint = maybe "(Eq a, Eq b)" (\c -> "(Eq a, Eq b, " <> c <> ")") mConstraint + in T.unlines + [ "module Testing where" + , "" + , "data Three a b c = Three a b c" + , "" + , "instance " <> constraint <> " => Eq (Three a b c) where" + , " (Three x y z) == (Three x' y' z') = x == x' && y == y' && z == z'" + ] + + check :: T.Text -> T.Text -> T.Text -> TestTree + check actionTitle originalCode expectedCode = testSession (T.unpack actionTitle) $ do + doc <- createDoc "Testing.hs" "haskell" originalCode + _ <- waitForDiagnostics + actionsOrCommands <- getAllCodeActions doc + chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands + executeCodeAction chosenAction + modifiedCode <- documentContents doc + liftIO $ expectedCode @=? modifiedCode + + in testGroup "add instance constraint" + [ check + "Add `Eq a` to the context of the instance declaration" + (missingConstraintSourceCode Nothing) + (missingConstraintSourceCode $ Just "Eq a") + , check + "Add `Eq b` to the context of the instance declaration" + (incompleteConstraintSourceCode Nothing) + (incompleteConstraintSourceCode $ Just "Eq b") + , check + "Add `Eq c` to the context of the instance declaration" + (incompleteConstraintSourceCode2 Nothing) + (incompleteConstraintSourceCode2 $ Just "Eq c") + ] + +addFunctionConstraintTests :: TestTree +addFunctionConstraintTests = let + missingConstraintSourceCode :: T.Text -> T.Text + missingConstraintSourceCode constraint = + T.unlines + [ "module Testing where" + , "" + , "eq :: " <> constraint <> "a -> a -> Bool" + , "eq x y = x == y" + ] + + missingConstraintWithForAllSourceCode :: T.Text -> T.Text + missingConstraintWithForAllSourceCode constraint = + T.unlines + [ "{-# LANGUAGE ExplicitForAll #-}" + , "module Testing where" + , "" + , "eq :: forall a. " <> constraint <> "a -> a -> Bool" + , "eq x y = x == y" + ] + + incompleteConstraintWithForAllSourceCode :: T.Text -> T.Text + incompleteConstraintWithForAllSourceCode constraint = + T.unlines + [ "{-# LANGUAGE ExplicitForAll #-}" + , "module Testing where" + , "" + , "data Pair a b = Pair a b" + , "" + , "eq :: " <> constraint <> " => Pair a b -> Pair a b -> Bool" + , "eq (Pair x y) (Pair x' y') = x == x' && y == y'" + ] + + incompleteConstraintSourceCode :: T.Text -> T.Text + incompleteConstraintSourceCode constraint = + T.unlines + [ "module Testing where" + , "" + , "data Pair a b = Pair a b" + , "" + , "eq :: " <> constraint <> " => Pair a b -> Pair a b -> Bool" + , "eq (Pair x y) (Pair x' y') = x == x' && y == y'" + ] + + incompleteConstraintSourceCode2 :: T.Text -> T.Text + incompleteConstraintSourceCode2 constraint = + T.unlines + [ "module Testing where" + , "" + , "data Three a b c = Three a b c" + , "" + , "eq :: " <> constraint <> " => Three a b c -> Three a b c -> Bool" + , "eq (Three x y z) (Three x' y' z') = x == x' && y == y' && z == z'" + ] + + incompleteConstraintSourceCodeWithExtraCharsInContext :: T.Text -> T.Text + incompleteConstraintSourceCodeWithExtraCharsInContext constraint = + T.unlines + [ "module Testing where" + , "" + , "data Pair a b = Pair a b" + , "" + , "eq :: ( " <> constraint <> " ) => Pair a b -> Pair a b -> Bool" + , "eq (Pair x y) (Pair x' y') = x == x' && y == y'" + ] + + incompleteConstraintSourceCodeWithNewlinesInTypeSignature :: T.Text -> T.Text + incompleteConstraintSourceCodeWithNewlinesInTypeSignature constraint = + T.unlines + [ "module Testing where" + , "data Pair a b = Pair a b" + , "eq " + , " :: (" <> constraint <> ")" + , " => Pair a b -> Pair a b -> Bool" + , "eq (Pair x y) (Pair x' y') = x == x' && y == y'" + ] + + missingMonadConstraint constraint = T.unlines + [ "module Testing where" + , "f :: " <> constraint <> "m ()" + , "f = do " + , " return ()" + ] + + in testGroup "add function constraint" + [ checkCodeAction + "no preexisting constraint" + "Add `Eq a` to the context of the type signature for `eq`" + (missingConstraintSourceCode "") + (missingConstraintSourceCode "Eq a => ") + , checkCodeAction + "no preexisting constraint, with forall" + "Add `Eq a` to the context of the type signature for `eq`" + (missingConstraintWithForAllSourceCode "") + (missingConstraintWithForAllSourceCode "Eq a => ") + , checkCodeAction + "preexisting constraint, no parenthesis" + "Add `Eq b` to the context of the type signature for `eq`" + (incompleteConstraintSourceCode "Eq a") + (incompleteConstraintSourceCode "(Eq a, Eq b)") + , checkCodeAction + "preexisting constraints in parenthesis" + "Add `Eq c` to the context of the type signature for `eq`" + (incompleteConstraintSourceCode2 "(Eq a, Eq b)") + (incompleteConstraintSourceCode2 "(Eq a, Eq b, Eq c)") + , checkCodeAction + "preexisting constraints with forall" + "Add `Eq b` to the context of the type signature for `eq`" + (incompleteConstraintWithForAllSourceCode "Eq a") + (incompleteConstraintWithForAllSourceCode "(Eq a, Eq b)") + , checkCodeAction + "preexisting constraint, with extra spaces in context" + "Add `Eq b` to the context of the type signature for `eq`" + (incompleteConstraintSourceCodeWithExtraCharsInContext "Eq a") + (incompleteConstraintSourceCodeWithExtraCharsInContext "Eq a, Eq b") + , checkCodeAction + "preexisting constraint, with newlines in type signature" + "Add `Eq b` to the context of the type signature for `eq`" + (incompleteConstraintSourceCodeWithNewlinesInTypeSignature "Eq a") + (incompleteConstraintSourceCodeWithNewlinesInTypeSignature "Eq a, Eq b") + , checkCodeAction + "missing Monad constraint" + "Add `Monad m` to the context of the type signature for `f`" + (missingMonadConstraint "") + (missingMonadConstraint "Monad m => ") + ] + +checkCodeAction :: String -> T.Text -> T.Text -> T.Text -> TestTree +checkCodeAction testName actionTitle originalCode expectedCode = testSession testName $ do + doc <- createDoc "Testing.hs" "haskell" originalCode + _ <- waitForDiagnostics + actionsOrCommands <- getAllCodeActions doc + chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands + executeCodeAction chosenAction + modifiedCode <- documentContents doc + liftIO $ expectedCode @=? modifiedCode + +addImplicitParamsConstraintTests :: TestTree +addImplicitParamsConstraintTests = + testGroup + "add missing implicit params constraints" + [ testGroup + "introduced" + [ let ex ctxtA = exampleCode "?a" ctxtA "" + in checkCodeAction "at top level" "Add ?a::() to the context of fBase" (ex "") (ex "?a::()"), + let ex ctxA = exampleCode "x where x = ?a" ctxA "" + in checkCodeAction "in nested def" "Add ?a::() to the context of fBase" (ex "") (ex "?a::()") + ], + testGroup + "inherited" + [ let ex = exampleCode "()" "?a::()" + in checkCodeAction + "with preexisting context" + "Add `?a::()` to the context of the type signature for `fCaller`" + (ex "Eq ()") + (ex "Eq (), ?a::()"), + let ex = exampleCode "()" "?a::()" + in checkCodeAction "without preexisting context" "Add ?a::() to the context of fCaller" (ex "") (ex "?a::()") + ] + ] + where + mkContext "" = "" + mkContext contents = "(" <> contents <> ") => " + + exampleCode bodyBase contextBase contextCaller = + T.unlines + [ "{-# LANGUAGE FlexibleContexts, ImplicitParams #-}", + "module Testing where", + "fBase :: " <> mkContext contextBase <> "()", + "fBase = " <> bodyBase, + "fCaller :: " <> mkContext contextCaller <> "()", + "fCaller = fBase" + ] + +removeRedundantConstraintsTests :: TestTree +removeRedundantConstraintsTests = let + header = + [ "{-# OPTIONS_GHC -Wredundant-constraints #-}" + , "module Testing where" + , "" + ] + + headerExt :: [T.Text] -> [T.Text] + headerExt exts = + redunt : extTxt ++ ["module Testing where"] + where + redunt = "{-# OPTIONS_GHC -Wredundant-constraints #-}" + extTxt = map (\ext -> "{-# LANGUAGE " <> ext <> " #-}") exts + + redundantConstraintsCode :: Maybe T.Text -> T.Text + redundantConstraintsCode mConstraint = + let constraint = maybe "" (\c -> "" <> c <> " => ") mConstraint + in T.unlines $ header <> + [ "foo :: " <> constraint <> "a -> a" + , "foo = id" + ] + + redundantMixedConstraintsCode :: Maybe T.Text -> T.Text + redundantMixedConstraintsCode mConstraint = + let constraint = maybe "(Num a, Eq a)" (\c -> "(Num a, Eq a, " <> c <> ")") mConstraint + in T.unlines $ header <> + [ "foo :: " <> constraint <> " => a -> Bool" + , "foo x = x == 1" + ] + + typeSignatureSpaces :: Maybe T.Text -> T.Text + typeSignatureSpaces mConstraint = + let constraint = maybe "(Num a, Eq a)" (\c -> "(Num a, Eq a, " <> c <> ")") mConstraint + in T.unlines $ header <> + [ "foo :: " <> constraint <> " => a -> Bool" + , "foo x = x == 1" + ] + + redundantConstraintsForall :: Maybe T.Text -> T.Text + redundantConstraintsForall mConstraint = + let constraint = maybe "" (\c -> "" <> c <> " => ") mConstraint + in T.unlines $ headerExt ["RankNTypes"] <> + [ "foo :: forall a. " <> constraint <> "a -> a" + , "foo = id" + ] + + typeSignatureDo :: Maybe T.Text -> T.Text + typeSignatureDo mConstraint = + let constraint = maybe "" (\c -> "" <> c <> " => ") mConstraint + in T.unlines $ header <> + [ "f :: Int -> IO ()" + , "f n = do" + , " let foo :: " <> constraint <> "a -> IO ()" + , " foo _ = return ()" + , " r n" + ] + + typeSignatureNested :: Maybe T.Text -> T.Text + typeSignatureNested mConstraint = + let constraint = maybe "" (\c -> "" <> c <> " => ") mConstraint + in T.unlines $ header <> + [ "f :: Int -> ()" + , "f = g" + , " where" + , " g :: " <> constraint <> "a -> ()" + , " g _ = ()" + ] + + typeSignatureNested' :: Maybe T.Text -> T.Text + typeSignatureNested' mConstraint = + let constraint = maybe "" (\c -> "" <> c <> " => ") mConstraint + in T.unlines $ header <> + [ "f :: Int -> ()" + , "f =" + , " let" + , " g :: Int -> ()" + , " g = h" + , " where" + , " h :: " <> constraint <> "a -> ()" + , " h _ = ()" + , " in g" + ] + + typeSignatureNested'' :: Maybe T.Text -> T.Text + typeSignatureNested'' mConstraint = + let constraint = maybe "" (\c -> "" <> c <> " => ") mConstraint + in T.unlines $ header <> + [ "f :: Int -> ()" + , "f = g" + , " where" + , " g :: Int -> ()" + , " g = " + , " let" + , " h :: " <> constraint <> "a -> ()" + , " h _ = ()" + , " in h" + ] + + typeSignatureLined1 = T.unlines $ header <> + [ "foo :: Eq a =>" + , " a -> Bool" + , "foo _ = True" + ] + + typeSignatureLined2 = T.unlines $ header <> + [ "foo :: (Eq a, Show a)" + , " => a -> Bool" + , "foo _ = True" + ] + + typeSignatureOneLine = T.unlines $ header <> + [ "foo :: a -> Bool" + , "foo _ = True" + ] + + typeSignatureLined3 = T.unlines $ header <> + [ "foo :: ( Eq a" + , " , Show a" + , " )" + , " => a -> Bool" + , "foo x = x == x" + ] + + typeSignatureLined3' = T.unlines $ header <> + [ "foo :: ( Eq a" + , " )" + , " => a -> Bool" + , "foo x = x == x" + ] + + + check :: T.Text -> T.Text -> T.Text -> TestTree + check actionTitle originalCode expectedCode = testSession (T.unpack actionTitle) $ do + doc <- createDoc "Testing.hs" "haskell" originalCode + _ <- waitForDiagnostics + actionsOrCommands <- getAllCodeActions doc + chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands + executeCodeAction chosenAction + modifiedCode <- documentContents doc + liftIO $ expectedCode @=? modifiedCode + + in testGroup "remove redundant function constraints" + [ check + "Remove redundant constraint `Eq a` from the context of the type signature for `foo`" + (redundantConstraintsCode $ Just "Eq a") + (redundantConstraintsCode Nothing) + , check + "Remove redundant constraints `(Eq a, Monoid a)` from the context of the type signature for `foo`" + (redundantConstraintsCode $ Just "(Eq a, Monoid a)") + (redundantConstraintsCode Nothing) + , check + "Remove redundant constraints `(Monoid a, Show a)` from the context of the type signature for `foo`" + (redundantMixedConstraintsCode $ Just "Monoid a, Show a") + (redundantMixedConstraintsCode Nothing) + , check + "Remove redundant constraint `Eq a` from the context of the type signature for `g`" + (typeSignatureNested $ Just "Eq a") + (typeSignatureNested Nothing) + , check + "Remove redundant constraint `Eq a` from the context of the type signature for `h`" + (typeSignatureNested' $ Just "Eq a") + (typeSignatureNested' Nothing) + , check + "Remove redundant constraint `Eq a` from the context of the type signature for `h`" + (typeSignatureNested'' $ Just "Eq a") + (typeSignatureNested'' Nothing) + , check + "Remove redundant constraint `Eq a` from the context of the type signature for `foo`" + (redundantConstraintsForall $ Just "Eq a") + (redundantConstraintsForall Nothing) + , check + "Remove redundant constraint `Eq a` from the context of the type signature for `foo`" + (typeSignatureDo $ Just "Eq a") + (typeSignatureDo Nothing) + , check + "Remove redundant constraints `(Monoid a, Show a)` from the context of the type signature for `foo`" + (typeSignatureSpaces $ Just "Monoid a, Show a") + (typeSignatureSpaces Nothing) + , check + "Remove redundant constraint `Eq a` from the context of the type signature for `foo`" + typeSignatureLined1 + typeSignatureOneLine + , check + "Remove redundant constraints `(Eq a, Show a)` from the context of the type signature for `foo`" + typeSignatureLined2 + typeSignatureOneLine + , check + "Remove redundant constraint `Show a` from the context of the type signature for `foo`" + typeSignatureLined3 + typeSignatureLined3' + ] + +addSigActionTests :: TestTree +addSigActionTests = let + header = [ "{-# OPTIONS_GHC -Wmissing-signatures -Wmissing-pattern-synonym-signatures #-}" + , "{-# LANGUAGE PatternSynonyms,BangPatterns,GADTs #-}" + , "module Sigs where" + , "data T1 a where" + , " MkT1 :: (Show b) => a -> b -> T1 a" + ] + before def = T.unlines $ header ++ [def] + after' def sig = T.unlines $ header ++ [sig, def] + + def >:: sig = testSession (T.unpack $ T.replace "\n" "\\n" def) $ do + let originalCode = before def + let expectedCode = after' def sig + doc <- createDoc "Sigs.hs" "haskell" originalCode + _ <- waitForDiagnostics + actionsOrCommands <- getCodeActions doc (Range (Position 5 1) (Position 5 maxBound)) + chosenAction <- liftIO $ pickActionWithTitle ("add signature: " <> sig) actionsOrCommands + executeCodeAction chosenAction + modifiedCode <- documentContents doc + liftIO $ expectedCode @=? modifiedCode + in + testGroup "add signature" + [ "abc = True" >:: "abc :: Bool" + , "foo a b = a + b" >:: "foo :: Num a => a -> a -> a" + , "bar a b = show $ a + b" >:: "bar :: (Show a, Num a) => a -> a -> String" + , "(!!!) a b = a > b" >:: "(!!!) :: Ord a => a -> a -> Bool" + , "a >>>> b = a + b" >:: "(>>>>) :: Num a => a -> a -> a" + , "a `haha` b = a b" >:: "haha :: (t1 -> t2) -> t1 -> t2" + , "pattern Some a = Just a" >:: "pattern Some :: a -> Maybe a" + , "pattern Some a <- Just a" >:: "pattern Some :: a -> Maybe a" + , "pattern Some a <- Just a\n where Some a = Just a" >:: "pattern Some :: a -> Maybe a" + , "pattern Some a <- Just !a\n where Some !a = Just a" >:: "pattern Some :: a -> Maybe a" + , "pattern Point{x, y} = (x, y)" >:: "pattern Point :: a -> b -> (a, b)" + , "pattern Point{x, y} <- (x, y)" >:: "pattern Point :: a -> b -> (a, b)" + , "pattern Point{x, y} <- (x, y)\n where Point x y = (x, y)" >:: "pattern Point :: a -> b -> (a, b)" + , "pattern MkT1' b = MkT1 42 b" >:: "pattern MkT1' :: (Eq a, Num a) => Show b => b -> T1 a" + , "pattern MkT1' b <- MkT1 42 b" >:: "pattern MkT1' :: (Eq a, Num a) => Show b => b -> T1 a" + , "pattern MkT1' b <- MkT1 42 b\n where MkT1' b = MkT1 42 b" >:: "pattern MkT1' :: (Eq a, Num a) => Show b => b -> T1 a" + ] + +exportUnusedTests :: TestTree +exportUnusedTests = testGroup "export unused actions" + [ testGroup "don't want suggestion" + [ testSession "implicit exports" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "{-# OPTIONS_GHC -Wmissing-signatures #-}" + , "module A where" + , "foo = id"]) + (R 3 0 3 3) + "Export ‘foo’" + Nothing -- codeaction should not be available + , testSession "not top-level" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "{-# OPTIONS_GHC -Wunused-binds #-}" + , "module A (foo,bar) where" + , "foo = ()" + , " where bar = ()" + , "bar = ()"]) + (R 2 0 2 11) + "Export ‘bar’" + Nothing + , ignoreForGHC92 "Diagnostic message has no suggestions" $ + testSession "type is exported but not the constructor of same name" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A (Foo) where" + , "data Foo = Foo"]) + (R 2 0 2 8) + "Export ‘Foo’" + Nothing -- codeaction should not be available + , testSession "unused data field" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A (Foo(Foo)) where" + , "data Foo = Foo {foo :: ()}"]) + (R 2 0 2 20) + "Export ‘foo’" + Nothing -- codeaction should not be available + ] + , testGroup "want suggestion" + [ testSession "empty exports" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A (" + , ") where" + , "foo = id"]) + (R 3 0 3 3) + "Export ‘foo’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A (" + , "foo) where" + , "foo = id"]) + , testSession "single line explicit exports" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A (foo) where" + , "foo = id" + , "bar = foo"]) + (R 3 0 3 3) + "Export ‘bar’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A (foo, bar) where" + , "foo = id" + , "bar = foo"]) + , testSession "multi line explicit exports" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A" + , " (" + , " foo) where" + , "foo = id" + , "bar = foo"]) + (R 5 0 5 3) + "Export ‘bar’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A" + , " (" + , " foo, bar) where" + , "foo = id" + , "bar = foo"]) + , testSession "export list ends in comma" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A" + , " (foo," + , " ) where" + , "foo = id" + , "bar = foo"]) + (R 5 0 5 3) + "Export ‘bar’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A" + , " (foo," + , " bar) where" + , "foo = id" + , "bar = foo"]) + , testSession "style of multiple exports is preserved 1" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A" + , " ( foo" + , " , bar" + , " ) where" + , "foo = id" + , "bar = foo" + , "baz = bar" + ]) + (R 7 0 7 3) + "Export ‘baz’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A" + , " ( foo" + , " , bar" + , " , baz" + , " ) where" + , "foo = id" + , "bar = foo" + , "baz = bar" + ]) + , testSession "style of multiple exports is preserved 2" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A" + , " ( foo," + , " bar" + , " ) where" + , "foo = id" + , "bar = foo" + , "baz = bar" + ]) + (R 7 0 7 3) + "Export ‘baz’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A" + , " ( foo," + , " bar," + , " baz" + , " ) where" + , "foo = id" + , "bar = foo" + , "baz = bar" + ]) + , testSession "style of multiple exports is preserved and selects smallest export separator" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A" + , " ( foo" + , " , bar" + , " -- * For testing" + , " , baz" + , " ) where" + , "foo = id" + , "bar = foo" + , "baz = bar" + , "quux = bar" + ]) + (R 10 0 10 4) + "Export ‘quux’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A" + , " ( foo" + , " , bar" + , " -- * For testing" + , " , baz" + , " , quux" + , " ) where" + , "foo = id" + , "bar = foo" + , "baz = bar" + , "quux = bar" + ]) + , testSession "unused pattern synonym" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "{-# LANGUAGE PatternSynonyms #-}" + , "module A () where" + , "pattern Foo a <- (a, _)"]) + (R 3 0 3 10) + "Export ‘Foo’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "{-# LANGUAGE PatternSynonyms #-}" + , "module A (pattern Foo) where" + , "pattern Foo a <- (a, _)"]) + , testSession "unused data type" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A () where" + , "data Foo = Foo"]) + (R 2 0 2 7) + "Export ‘Foo’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A (Foo(..)) where" + , "data Foo = Foo"]) + , testSession "unused newtype" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A () where" + , "newtype Foo = Foo ()"]) + (R 2 0 2 10) + "Export ‘Foo’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A (Foo(..)) where" + , "newtype Foo = Foo ()"]) + , testSession "unused type synonym" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A () where" + , "type Foo = ()"]) + (R 2 0 2 7) + "Export ‘Foo’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A (Foo) where" + , "type Foo = ()"]) + , testSession "unused type family" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "{-# LANGUAGE TypeFamilies #-}" + , "module A () where" + , "type family Foo p"]) + (R 3 0 3 15) + "Export ‘Foo’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "{-# LANGUAGE TypeFamilies #-}" + , "module A (Foo) where" + , "type family Foo p"]) + , testSession "unused typeclass" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A () where" + , "class Foo a"]) + (R 2 0 2 8) + "Export ‘Foo’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A (Foo(..)) where" + , "class Foo a"]) + , testSession "infix" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A () where" + , "a `f` b = ()"]) + (R 2 0 2 11) + "Export ‘f’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A (f) where" + , "a `f` b = ()"]) + , testSession "function operator" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A () where" + , "(<|) = ($)"]) + (R 2 0 2 9) + "Export ‘<|’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "module A ((<|)) where" + , "(<|) = ($)"]) + , testSession "type synonym operator" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "{-# LANGUAGE TypeOperators #-}" + , "module A () where" + , "type (:<) = ()"]) + (R 3 0 3 13) + "Export ‘:<’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "{-# LANGUAGE TypeOperators #-}" + , "module A ((:<)) where" + , "type (:<) = ()"]) + , testSession "type family operator" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "{-# LANGUAGE TypeFamilies #-}" + , "{-# LANGUAGE TypeOperators #-}" + , "module A () where" + , "type family (:<)"]) + (R 4 0 4 15) + "Export ‘:<’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "{-# LANGUAGE TypeFamilies #-}" + , "{-# LANGUAGE TypeOperators #-}" + , "module A (type (:<)) where" + , "type family (:<)"]) + , testSession "typeclass operator" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "{-# LANGUAGE TypeOperators #-}" + , "module A () where" + , "class (:<) a"]) + (R 3 0 3 11) + "Export ‘:<’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "{-# LANGUAGE TypeOperators #-}" + , "module A (type (:<)(..)) where" + , "class (:<) a"]) + , testSession "newtype operator" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "{-# LANGUAGE TypeOperators #-}" + , "module A () where" + , "newtype (:<) = Foo ()"]) + (R 3 0 3 20) + "Export ‘:<’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "{-# LANGUAGE TypeOperators #-}" + , "module A (type (:<)(..)) where" + , "newtype (:<) = Foo ()"]) + , testSession "data type operator" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "{-# LANGUAGE TypeOperators #-}" + , "module A () where" + , "data (:<) = Foo ()"]) + (R 3 0 3 17) + "Export ‘:<’" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wunused-top-binds #-}" + , "{-# LANGUAGE TypeOperators #-}" + , "module A (type (:<)(..)) where" + , "data (:<) = Foo ()"]) + ] + ] + where + template doc range = exportTemplate (Just range) doc + +exportTemplate :: Maybe Range -> T.Text -> T.Text -> Maybe T.Text -> Session () +exportTemplate mRange initialContent expectedAction expectedContents = do + doc <- createDoc "A.hs" "haskell" initialContent + _ <- waitForDiagnostics + actions <- case mRange of + Nothing -> getAllCodeActions doc + Just range -> getCodeActions doc range + case expectedContents of + Just content -> do + action <- liftIO $ pickActionWithTitle expectedAction actions + executeCodeAction action + contentAfterAction <- documentContents doc + liftIO $ content @=? contentAfterAction + Nothing -> + liftIO $ [_title | InR CodeAction{_title} <- actions, _title == expectedAction ] @?= [] + +removeExportTests :: TestTree +removeExportTests = testGroup "remove export actions" + [ testSession "single export" $ template + (T.unlines + [ "module A ( a ) where" + , "b :: ()" + , "b = ()"]) + "Remove ‘a’ from export" + (Just $ T.unlines + [ "module A ( ) where" + , "b :: ()" + , "b = ()"]) + , testSession "ending comma" $ template + (T.unlines + [ "module A ( a, ) where" + , "b :: ()" + , "b = ()"]) + "Remove ‘a’ from export" + (Just $ T.unlines + [ "module A ( ) where" + , "b :: ()" + , "b = ()"]) + , testSession "multiple exports" $ template + (T.unlines + [ "module A (a , c, b ) where" + , "a, c :: ()" + , "a = ()" + , "c = ()"]) + "Remove ‘b’ from export" + (Just $ T.unlines + [ "module A (a , c ) where" + , "a, c :: ()" + , "a = ()" + , "c = ()"]) + , testSession "not in scope constructor" $ template + (T.unlines + [ "module A (A (X,Y,Z,(:<)), ab) where" + , "data A = X Int | Y | (:<) Int" + , "ab :: ()" + , "ab = ()" + ]) + "Remove ‘Z’ from export" + (Just $ T.unlines + [ "module A (A (X,Y,(:<)), ab) where" + , "data A = X Int | Y | (:<) Int" + , "ab :: ()" + , "ab = ()"]) + , testSession "multiline export" $ template + (T.unlines + [ "module A (a" + , " , b" + , " , (:*:)" + , " , ) where" + , "a,b :: ()" + , "a = ()" + , "b = ()"]) + "Remove ‘:*:’ from export" + (Just $ T.unlines + [ "module A (a" + , " , b" + , " " + , " , ) where" + , "a,b :: ()" + , "a = ()" + , "b = ()"]) + , testSession "qualified re-export" $ template + (T.unlines + [ "module A (M.x,a) where" + , "import qualified Data.List as M" + , "a :: ()" + , "a = ()"]) + "Remove ‘M.x’ from export" + (Just $ T.unlines + [ "module A (a) where" + , "import qualified Data.List as M" + , "a :: ()" + , "a = ()"]) + , testSession "qualified re-export ending in '.'" $ template + (T.unlines + [ "module A ((M.@.),a) where" + , "import qualified Data.List as M" + , "a :: ()" + , "a = ()"]) + "Remove ‘M.@.’ from export" + (Just $ T.unlines + [ "module A (a) where" + , "import qualified Data.List as M" + , "a :: ()" + , "a = ()"]) + , testSession "export module" $ template + (T.unlines + [ "module A (module B) where" + , "a :: ()" + , "a = ()"]) + "Remove ‘module B’ from export" + (Just $ T.unlines + [ "module A () where" + , "a :: ()" + , "a = ()"]) + , testSession "dodgy export" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wall #-}" + , "module A (A (..)) where" + , "data X = X" + , "type A = X"]) + "Remove ‘A(..)’ from export" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wall #-}" + , "module A () where" + , "data X = X" + , "type A = X"]) + , testSession "dodgy export" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wall #-}" + , "module A (A (..)) where" + , "data X = X" + , "type A = X"]) + "Remove ‘A(..)’ from export" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wall #-}" + , "module A () where" + , "data X = X" + , "type A = X"]) + , testSession "duplicate module export" $ template + (T.unlines + [ "{-# OPTIONS_GHC -Wall #-}" + , "module A (module L,module L) where" + , "import Data.List as L" + , "a :: ()" + , "a = ()"]) + "Remove ‘Module L’ from export" + (Just $ T.unlines + [ "{-# OPTIONS_GHC -Wall #-}" + , "module A (module L) where" + , "import Data.List as L" + , "a :: ()" + , "a = ()"]) + , testSession "remove all exports single" $ template + (T.unlines + [ "module A (x) where" + , "a :: ()" + , "a = ()"]) + "Remove all redundant exports" + (Just $ T.unlines + [ "module A () where" + , "a :: ()" + , "a = ()"]) + , testSession "remove all exports two" $ template + (T.unlines + [ "module A (x,y) where" + , "a :: ()" + , "a = ()"]) + "Remove all redundant exports" + (Just $ T.unlines + [ "module A () where" + , "a :: ()" + , "a = ()"]) + , testSession "remove all exports three" $ template + (T.unlines + [ "module A (a,x,y) where" + , "a :: ()" + , "a = ()"]) + "Remove all redundant exports" + (Just $ T.unlines + [ "module A (a) where" + , "a :: ()" + , "a = ()"]) + , testSession "remove all exports composite" $ template + (T.unlines + [ "module A (x,y,b, module Ls, a, A(X,getW, Y, Z,(:-),getV), (-+), B(B)) where" + , "data A = X {getV :: Int} | Y {getV :: Int}" + , "data B = B" + , "a,b :: ()" + , "a = ()" + , "b = ()"]) + "Remove all redundant exports" + (Just $ T.unlines + [ "module A (b, a, A(X, Y,getV), B(B)) where" + , "data A = X {getV :: Int} | Y {getV :: Int}" + , "data B = B" + , "a,b :: ()" + , "a = ()" + , "b = ()"]) + ] + where + template = exportTemplate Nothing + + +codeActionHelperFunctionTests :: TestTree +codeActionHelperFunctionTests = testGroup "code action helpers" + [ + extendImportTestsRegEx + ] + +extendImportTestsRegEx :: TestTree +extendImportTestsRegEx = testGroup "regex parsing" + [ + testCase "parse invalid multiple imports" $ template "foo bar foo" Nothing + , testCase "parse malformed import list" $ template + "\n\8226 Perhaps you want to add \8216fromList\8217 to one of these import lists:\n \8216Data.Map\8217)" + Nothing + , testCase "parse multiple imports" $ template + "\n\8226 Perhaps you want to add \8216fromList\8217 to one of these import lists:\n \8216Data.Map\8217 (app/testlsp.hs:7:1-18)\n \8216Data.HashMap.Strict\8217 (app/testlsp.hs:8:1-29)" + $ Just ("fromList",[("Data.Map","app/testlsp.hs:7:1-18"),("Data.HashMap.Strict","app/testlsp.hs:8:1-29")]) + ] + where + template message expected = do + liftIO $ matchRegExMultipleImports message @=? expected + +pickActionWithTitle :: T.Text -> [Command |? CodeAction] -> IO CodeAction +pickActionWithTitle title actions = do + assertBool ("Found no matching actions for " <> show title <> " in " <> show titles) (not $ null matches) + return $ head matches + where + titles = + [ actionTitle + | InR CodeAction { _title = actionTitle } <- actions + ] + matches = + [ action + | InR action@CodeAction { _title = actionTitle } <- actions + , title == actionTitle + ] + +findCodeActions :: TextDocumentIdentifier -> Range -> [T.Text] -> Session [CodeAction] +findCodeActions = findCodeActions' (==) "is not a superset of" + +findCodeActionsByPrefix :: TextDocumentIdentifier -> Range -> [T.Text] -> Session [CodeAction] +findCodeActionsByPrefix = findCodeActions' T.isPrefixOf "is not prefix of" + +findCodeActions' :: (T.Text -> T.Text -> Bool) -> String -> TextDocumentIdentifier -> Range -> [T.Text] -> Session [CodeAction] +findCodeActions' op errMsg doc range expectedTitles = do + actions <- getCodeActions doc range + let matches = sequence + [ listToMaybe + [ action + | InR action@CodeAction { _title = actionTitle } <- actions + , expectedTitle `op` actionTitle] + | expectedTitle <- expectedTitles] + let msg = show + [ actionTitle + | InR CodeAction { _title = actionTitle } <- actions + ] + ++ " " <> errMsg <> " " + ++ show expectedTitles + liftIO $ case matches of + Nothing -> assertFailure msg + Just _ -> pure () + return (fromJust matches) + +findCodeAction :: TextDocumentIdentifier -> Range -> T.Text -> Session CodeAction +findCodeAction doc range t = head <$> findCodeActions doc range [t] + +testSession :: String -> Session () -> TestTree +testSession name = testCase name . run + +testSessionWithExtraFiles :: HasCallStack => FilePath -> String -> (FilePath -> Session ()) -> TestTree +testSessionWithExtraFiles prefix name = testCase name . runWithExtraFiles prefix + +runWithExtraFiles :: HasCallStack => FilePath -> (FilePath -> Session a) -> IO a +runWithExtraFiles prefix s = withTempDir $ \dir -> do + copyTestDataFiles dir prefix + runInDir dir (s dir) + +copyTestDataFiles :: HasCallStack => FilePath -> FilePath -> IO () +copyTestDataFiles dir prefix = do + -- Copy all the test data files to the temporary workspace + testDataFiles <- getDirectoryFilesIO ("test/data" prefix) ["//*"] + for_ testDataFiles $ \f -> do + createDirectoryIfMissing True $ dir takeDirectory f + copyFile ("test/data" prefix f) (dir f) + +run :: Session a -> IO a +run s = run' (const s) + +run' :: (FilePath -> Session a) -> IO a +run' s = withTempDir $ \dir -> runInDir dir (s dir) + +runInDir :: FilePath -> Session a -> IO a +runInDir dir = runSessionWithServer' refactorPlugin def def lspTestCaps dir + +lspTestCaps :: ClientCapabilities +lspTestCaps = fullCaps { _window = Just $ WindowClientCapabilities (Just True) Nothing Nothing } + +pattern R :: UInt -> UInt -> UInt -> UInt -> Range +pattern R x y x' y' = Range (Position x y) (Position x' y') + +-- | Version of 'System.IO.Extra.withTempDir' that canonicalizes the path +-- Which we need to do on macOS since the $TMPDIR can be in @/private/var@ or +-- @/var@ +withTempDir :: (FilePath -> IO a) -> IO a +withTempDir f = System.IO.Extra.withTempDir $ \dir -> do + dir' <- canonicalizePath dir + f dir' + +ignoreForGHC92 :: String -> TestTree -> TestTree +ignoreForGHC92 = ignoreFor (BrokenForGHC [GHC92]) + +data BrokenTarget = + BrokenSpecific OS [GhcVersion] + -- ^Broken for `BrokenOS` with `GhcVersion` + | BrokenForOS OS + -- ^Broken for `BrokenOS` + | BrokenForGHC [GhcVersion] + -- ^Broken for `GhcVersion` + deriving (Show) + +-- | Ignore test for specific os and ghc with reason. +ignoreFor :: BrokenTarget -> String -> TestTree -> TestTree +ignoreFor = knownIssueFor Ignore + +-- | Deal with `IssueSolution` for specific OS and GHC. +knownIssueFor :: IssueSolution -> BrokenTarget -> String -> TestTree -> TestTree +knownIssueFor solution = go . \case + BrokenSpecific bos vers -> isTargetOS bos && isTargetGhc vers + BrokenForOS bos -> isTargetOS bos + BrokenForGHC vers -> isTargetGhc vers + where + isTargetOS = \case + Windows -> isWindows + MacOS -> isMac + Linux -> not isWindows && not isMac + + isTargetGhc = elem ghcVersion + + go True = case solution of + Broken -> expectFailBecause + Ignore -> ignoreTestBecause + go False = \_ -> id + + +data IssueSolution = Broken | Ignore deriving (Show) + +-- | Assert that a value is not 'Nothing', and extract the value. +assertJust :: MonadIO m => String -> Maybe a -> m a +assertJust s = \case + Nothing -> liftIO $ assertFailure s + Just x -> pure x + +-- | Before ghc9, lists of Char is displayed as [Char], but with ghc9 and up, it's displayed as String +listOfChar :: T.Text +listOfChar | ghcVersion >= GHC90 = "String" + | otherwise = "[Char]" + diff --git a/ghcide/test/data/hiding/AVec.hs b/plugins/hls-refactor-plugin/test/data/hiding/AVec.hs similarity index 100% rename from ghcide/test/data/hiding/AVec.hs rename to plugins/hls-refactor-plugin/test/data/hiding/AVec.hs diff --git a/ghcide/test/data/hiding/BVec.hs b/plugins/hls-refactor-plugin/test/data/hiding/BVec.hs similarity index 100% rename from ghcide/test/data/hiding/BVec.hs rename to plugins/hls-refactor-plugin/test/data/hiding/BVec.hs diff --git a/ghcide/test/data/hiding/CVec.hs b/plugins/hls-refactor-plugin/test/data/hiding/CVec.hs similarity index 100% rename from ghcide/test/data/hiding/CVec.hs rename to plugins/hls-refactor-plugin/test/data/hiding/CVec.hs diff --git a/ghcide/test/data/hiding/DVec.hs b/plugins/hls-refactor-plugin/test/data/hiding/DVec.hs similarity index 100% rename from ghcide/test/data/hiding/DVec.hs rename to plugins/hls-refactor-plugin/test/data/hiding/DVec.hs diff --git a/ghcide/test/data/hiding/EVec.hs b/plugins/hls-refactor-plugin/test/data/hiding/EVec.hs similarity index 100% rename from ghcide/test/data/hiding/EVec.hs rename to plugins/hls-refactor-plugin/test/data/hiding/EVec.hs diff --git a/ghcide/test/data/hiding/FVec.hs b/plugins/hls-refactor-plugin/test/data/hiding/FVec.hs similarity index 100% rename from ghcide/test/data/hiding/FVec.hs rename to plugins/hls-refactor-plugin/test/data/hiding/FVec.hs diff --git a/ghcide/test/data/hiding/HideFunction.expected.append.E.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideFunction.expected.append.E.hs similarity index 100% rename from ghcide/test/data/hiding/HideFunction.expected.append.E.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideFunction.expected.append.E.hs diff --git a/ghcide/test/data/hiding/HideFunction.expected.append.Prelude.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideFunction.expected.append.Prelude.hs similarity index 100% rename from ghcide/test/data/hiding/HideFunction.expected.append.Prelude.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideFunction.expected.append.Prelude.hs diff --git a/ghcide/test/data/hiding/HideFunction.expected.fromList.A.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideFunction.expected.fromList.A.hs similarity index 100% rename from ghcide/test/data/hiding/HideFunction.expected.fromList.A.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideFunction.expected.fromList.A.hs diff --git a/ghcide/test/data/hiding/HideFunction.expected.fromList.B.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideFunction.expected.fromList.B.hs similarity index 100% rename from ghcide/test/data/hiding/HideFunction.expected.fromList.B.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideFunction.expected.fromList.B.hs diff --git a/ghcide/test/data/hiding/HideFunction.expected.qualified.append.Prelude.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideFunction.expected.qualified.append.Prelude.hs similarity index 100% rename from ghcide/test/data/hiding/HideFunction.expected.qualified.append.Prelude.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideFunction.expected.qualified.append.Prelude.hs diff --git a/ghcide/test/data/hiding/HideFunction.expected.qualified.fromList.E.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideFunction.expected.qualified.fromList.E.hs similarity index 100% rename from ghcide/test/data/hiding/HideFunction.expected.qualified.fromList.E.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideFunction.expected.qualified.fromList.E.hs diff --git a/ghcide/test/data/hiding/HideFunction.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideFunction.hs similarity index 100% rename from ghcide/test/data/hiding/HideFunction.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideFunction.hs diff --git a/ghcide/test/data/hiding/HideFunctionWithoutLocal.expected.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideFunctionWithoutLocal.expected.hs similarity index 100% rename from ghcide/test/data/hiding/HideFunctionWithoutLocal.expected.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideFunctionWithoutLocal.expected.hs diff --git a/ghcide/test/data/hiding/HideFunctionWithoutLocal.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideFunctionWithoutLocal.hs similarity index 100% rename from ghcide/test/data/hiding/HideFunctionWithoutLocal.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideFunctionWithoutLocal.hs diff --git a/ghcide/test/data/hiding/HidePreludeIndented.expected.hs b/plugins/hls-refactor-plugin/test/data/hiding/HidePreludeIndented.expected.hs similarity index 100% rename from ghcide/test/data/hiding/HidePreludeIndented.expected.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HidePreludeIndented.expected.hs diff --git a/ghcide/test/data/hiding/HidePreludeIndented.hs b/plugins/hls-refactor-plugin/test/data/hiding/HidePreludeIndented.hs similarity index 100% rename from ghcide/test/data/hiding/HidePreludeIndented.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HidePreludeIndented.hs diff --git a/ghcide/test/data/hiding/HidePreludeLocalInfix.expected.hs b/plugins/hls-refactor-plugin/test/data/hiding/HidePreludeLocalInfix.expected.hs similarity index 100% rename from ghcide/test/data/hiding/HidePreludeLocalInfix.expected.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HidePreludeLocalInfix.expected.hs diff --git a/ghcide/test/data/hiding/HidePreludeLocalInfix.hs b/plugins/hls-refactor-plugin/test/data/hiding/HidePreludeLocalInfix.hs similarity index 100% rename from ghcide/test/data/hiding/HidePreludeLocalInfix.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HidePreludeLocalInfix.hs diff --git a/ghcide/test/data/hiding/HideQualifyDuplicateRecordFields.expected.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideQualifyDuplicateRecordFields.expected.hs similarity index 100% rename from ghcide/test/data/hiding/HideQualifyDuplicateRecordFields.expected.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideQualifyDuplicateRecordFields.expected.hs diff --git a/ghcide/test/data/hiding/HideQualifyDuplicateRecordFields.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideQualifyDuplicateRecordFields.hs similarity index 100% rename from ghcide/test/data/hiding/HideQualifyDuplicateRecordFields.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideQualifyDuplicateRecordFields.hs diff --git a/ghcide/test/data/hiding/HideQualifyDuplicateRecordFieldsSelf.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideQualifyDuplicateRecordFieldsSelf.hs similarity index 100% rename from ghcide/test/data/hiding/HideQualifyDuplicateRecordFieldsSelf.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideQualifyDuplicateRecordFieldsSelf.hs diff --git a/ghcide/test/data/hiding/HideQualifyInfix.expected.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideQualifyInfix.expected.hs similarity index 100% rename from ghcide/test/data/hiding/HideQualifyInfix.expected.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideQualifyInfix.expected.hs diff --git a/ghcide/test/data/hiding/HideQualifyInfix.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideQualifyInfix.hs similarity index 100% rename from ghcide/test/data/hiding/HideQualifyInfix.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideQualifyInfix.hs diff --git a/ghcide/test/data/hiding/HideQualifySectionLeft.expected.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideQualifySectionLeft.expected.hs similarity index 100% rename from ghcide/test/data/hiding/HideQualifySectionLeft.expected.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideQualifySectionLeft.expected.hs diff --git a/ghcide/test/data/hiding/HideQualifySectionLeft.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideQualifySectionLeft.hs similarity index 100% rename from ghcide/test/data/hiding/HideQualifySectionLeft.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideQualifySectionLeft.hs diff --git a/ghcide/test/data/hiding/HideQualifySectionRight.expected.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideQualifySectionRight.expected.hs similarity index 100% rename from ghcide/test/data/hiding/HideQualifySectionRight.expected.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideQualifySectionRight.expected.hs diff --git a/ghcide/test/data/hiding/HideQualifySectionRight.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideQualifySectionRight.hs similarity index 100% rename from ghcide/test/data/hiding/HideQualifySectionRight.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideQualifySectionRight.hs diff --git a/ghcide/test/data/hiding/HideType.expected.A.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideType.expected.A.hs similarity index 100% rename from ghcide/test/data/hiding/HideType.expected.A.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideType.expected.A.hs diff --git a/ghcide/test/data/hiding/HideType.expected.E.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideType.expected.E.hs similarity index 100% rename from ghcide/test/data/hiding/HideType.expected.E.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideType.expected.E.hs diff --git a/ghcide/test/data/hiding/HideType.hs b/plugins/hls-refactor-plugin/test/data/hiding/HideType.hs similarity index 100% rename from ghcide/test/data/hiding/HideType.hs rename to plugins/hls-refactor-plugin/test/data/hiding/HideType.hs diff --git a/ghcide/test/data/hiding/hie.yaml b/plugins/hls-refactor-plugin/test/data/hiding/hie.yaml similarity index 90% rename from ghcide/test/data/hiding/hie.yaml rename to plugins/hls-refactor-plugin/test/data/hiding/hie.yaml index 075686555e..538f854ddf 100644 --- a/ghcide/test/data/hiding/hie.yaml +++ b/plugins/hls-refactor-plugin/test/data/hiding/hie.yaml @@ -8,3 +8,4 @@ cradle: - CVec.hs - DVec.hs - EVec.hs + - FVec.hs diff --git a/plugins/hls-refactor-plugin/test/data/hover/Bar.hs b/plugins/hls-refactor-plugin/test/data/hover/Bar.hs new file mode 100644 index 0000000000..f9fde2a7cc --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/hover/Bar.hs @@ -0,0 +1,4 @@ +module Bar (Bar(..)) where + +-- | Bar Haddock +data Bar = Bar diff --git a/plugins/hls-refactor-plugin/test/data/hover/Foo.hs b/plugins/hls-refactor-plugin/test/data/hover/Foo.hs new file mode 100644 index 0000000000..489a6ccd6b --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/hover/Foo.hs @@ -0,0 +1,6 @@ +module Foo (Bar, foo) where + +import Bar + +-- | foo Haddock +foo = Bar diff --git a/plugins/hls-refactor-plugin/test/data/hover/GotoHover.hs b/plugins/hls-refactor-plugin/test/data/hover/GotoHover.hs new file mode 100644 index 0000000000..e1802580e2 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/hover/GotoHover.hs @@ -0,0 +1,66 @@ +{-# LANGUAGE OverloadedStrings, TemplateHaskell #-} +{- HLINT ignore -} +module GotoHover ( module GotoHover) where +import Data.Text (Text, pack) +import Foo (Bar, foo) + + +data TypeConstructor = DataConstructor + { fff :: Text + , ggg :: Int } +aaa :: TypeConstructor +aaa = DataConstructor + { fff = "dfgy" + , ggg = 832 + } +bbb :: TypeConstructor +bbb = DataConstructor "mjgp" 2994 +ccc :: (Text, Int) +ccc = (fff bbb, ggg aaa) +ddd :: Num a => a -> a -> a +ddd vv ww = vv +! ww +a +! b = a - b +hhh (Just a) (><) = a >< a +iii a b = a `b` a +jjj s = pack $ s <> s +class MyClass a where + method :: a -> Int +instance MyClass Int where + method = succ +kkk :: MyClass a => Int -> a -> Int +kkk n c = n + method c + +doBind :: Maybe () +doBind = do unwrapped <- Just () + return unwrapped + +listCompBind :: [Char] +listCompBind = [ succ c | c <- "ptfx" ] + +multipleClause :: Bool -> Char +multipleClause True = 't' +multipleClause False = 'f' + +-- | Recognizable docs: kpqz +documented :: Monad m => Either Int (m a) +documented = Left 7518 + +listOfInt = [ 8391 :: Int, 6268 ] + +outer :: Bool +outer = undefined inner where + + inner :: Char + inner = undefined + +imported :: Bar +imported = foo + +aa2 :: Bool +aa2 = $(id [| True |]) + +hole :: Int +hole = _ + +hole2 :: a -> Maybe a +hole2 = _ diff --git a/plugins/hls-refactor-plugin/test/data/hover/RecordDotSyntax.hs b/plugins/hls-refactor-plugin/test/data/hover/RecordDotSyntax.hs new file mode 100644 index 0000000000..2f43b99977 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/hover/RecordDotSyntax.hs @@ -0,0 +1,21 @@ +{-# LANGUAGE CPP #-} +#if __GLASGOW_HASKELL__ >= 902 +{-# LANGUAGE OverloadedRecordDot, DuplicateRecordFields, NoFieldSelectors #-} + +module RecordDotSyntax ( module RecordDotSyntax) where + +import qualified Data.Maybe as M + +data MyRecord = MyRecord + { a :: String + , b :: Integer + , c :: MyChild + } deriving (Eq, Show) + +newtype MyChild = MyChild + { z :: String + } deriving (Eq, Show) + +x = MyRecord { a = "Hello", b = 12, c = MyChild { z = "there" } } +y = x.a ++ show x.b ++ x.c.z +#endif diff --git a/plugins/hls-refactor-plugin/test/data/hover/hie.yaml b/plugins/hls-refactor-plugin/test/data/hover/hie.yaml new file mode 100644 index 0000000000..e2b3e97c5d --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/hover/hie.yaml @@ -0,0 +1 @@ +cradle: {direct: {arguments: ["Foo", "Bar", "GotoHover", "RecordDotSyntax"]}} diff --git a/ghcide/test/data/import-placement/CommentAtTop.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/CommentAtTop.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/CommentAtTop.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/CommentAtTop.expected.hs diff --git a/ghcide/test/data/import-placement/CommentAtTop.hs b/plugins/hls-refactor-plugin/test/data/import-placement/CommentAtTop.hs similarity index 100% rename from ghcide/test/data/import-placement/CommentAtTop.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/CommentAtTop.hs diff --git a/ghcide/test/data/import-placement/CommentAtTopMultipleComments.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/CommentAtTopMultipleComments.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/CommentAtTopMultipleComments.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/CommentAtTopMultipleComments.expected.hs diff --git a/ghcide/test/data/import-placement/CommentAtTopMultipleComments.hs b/plugins/hls-refactor-plugin/test/data/import-placement/CommentAtTopMultipleComments.hs similarity index 100% rename from ghcide/test/data/import-placement/CommentAtTopMultipleComments.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/CommentAtTopMultipleComments.hs diff --git a/ghcide/test/data/import-placement/CommentCurlyBraceAtTop.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/CommentCurlyBraceAtTop.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/CommentCurlyBraceAtTop.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/CommentCurlyBraceAtTop.expected.hs diff --git a/ghcide/test/data/import-placement/CommentCurlyBraceAtTop.hs b/plugins/hls-refactor-plugin/test/data/import-placement/CommentCurlyBraceAtTop.hs similarity index 100% rename from ghcide/test/data/import-placement/CommentCurlyBraceAtTop.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/CommentCurlyBraceAtTop.hs diff --git a/ghcide/test/data/import-placement/DataAtTop.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/DataAtTop.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/DataAtTop.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/DataAtTop.expected.hs diff --git a/ghcide/test/data/import-placement/DataAtTop.hs b/plugins/hls-refactor-plugin/test/data/import-placement/DataAtTop.hs similarity index 100% rename from ghcide/test/data/import-placement/DataAtTop.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/DataAtTop.hs diff --git a/ghcide/test/data/import-placement/ImportAtTop.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/ImportAtTop.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/ImportAtTop.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/ImportAtTop.expected.hs diff --git a/ghcide/test/data/import-placement/ImportAtTop.hs b/plugins/hls-refactor-plugin/test/data/import-placement/ImportAtTop.hs similarity index 100% rename from ghcide/test/data/import-placement/ImportAtTop.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/ImportAtTop.hs diff --git a/ghcide/test/data/import-placement/LangPragmaModuleAtTop.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/LangPragmaModuleAtTop.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/LangPragmaModuleAtTop.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/LangPragmaModuleAtTop.expected.hs diff --git a/ghcide/test/data/import-placement/LangPragmaModuleAtTop.hs b/plugins/hls-refactor-plugin/test/data/import-placement/LangPragmaModuleAtTop.hs similarity index 100% rename from ghcide/test/data/import-placement/LangPragmaModuleAtTop.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/LangPragmaModuleAtTop.hs diff --git a/ghcide/test/data/import-placement/LangPragmaModuleExplicitExports.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/LangPragmaModuleExplicitExports.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/LangPragmaModuleExplicitExports.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/LangPragmaModuleExplicitExports.expected.hs diff --git a/ghcide/test/data/import-placement/LangPragmaModuleExplicitExports.hs b/plugins/hls-refactor-plugin/test/data/import-placement/LangPragmaModuleExplicitExports.hs similarity index 100% rename from ghcide/test/data/import-placement/LangPragmaModuleExplicitExports.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/LangPragmaModuleExplicitExports.hs diff --git a/ghcide/test/data/import-placement/LangPragmaModuleWithComment.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/LangPragmaModuleWithComment.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/LangPragmaModuleWithComment.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/LangPragmaModuleWithComment.expected.hs diff --git a/ghcide/test/data/import-placement/LangPragmaModuleWithComment.hs b/plugins/hls-refactor-plugin/test/data/import-placement/LangPragmaModuleWithComment.hs similarity index 100% rename from ghcide/test/data/import-placement/LangPragmaModuleWithComment.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/LangPragmaModuleWithComment.hs diff --git a/ghcide/test/data/import-placement/LanguagePragmaAtTop.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/LanguagePragmaAtTop.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/LanguagePragmaAtTop.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/LanguagePragmaAtTop.expected.hs diff --git a/ghcide/test/data/import-placement/LanguagePragmaAtTop.hs b/plugins/hls-refactor-plugin/test/data/import-placement/LanguagePragmaAtTop.hs similarity index 100% rename from ghcide/test/data/import-placement/LanguagePragmaAtTop.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/LanguagePragmaAtTop.hs diff --git a/ghcide/test/data/import-placement/LanguagePragmaAtTopWithComment.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/LanguagePragmaAtTopWithComment.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/LanguagePragmaAtTopWithComment.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/LanguagePragmaAtTopWithComment.expected.hs diff --git a/ghcide/test/data/import-placement/LanguagePragmaAtTopWithComment.hs b/plugins/hls-refactor-plugin/test/data/import-placement/LanguagePragmaAtTopWithComment.hs similarity index 100% rename from ghcide/test/data/import-placement/LanguagePragmaAtTopWithComment.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/LanguagePragmaAtTopWithComment.hs diff --git a/ghcide/test/data/import-placement/LanguagePragmasThenShebangs.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/LanguagePragmasThenShebangs.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/LanguagePragmasThenShebangs.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/LanguagePragmasThenShebangs.expected.hs diff --git a/ghcide/test/data/import-placement/LanguagePragmasThenShebangs.hs b/plugins/hls-refactor-plugin/test/data/import-placement/LanguagePragmasThenShebangs.hs similarity index 100% rename from ghcide/test/data/import-placement/LanguagePragmasThenShebangs.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/LanguagePragmasThenShebangs.hs diff --git a/ghcide/test/data/import-placement/ModuleDeclAndImports.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/ModuleDeclAndImports.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/ModuleDeclAndImports.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/ModuleDeclAndImports.expected.hs diff --git a/ghcide/test/data/import-placement/ModuleDeclAndImports.hs b/plugins/hls-refactor-plugin/test/data/import-placement/ModuleDeclAndImports.hs similarity index 100% rename from ghcide/test/data/import-placement/ModuleDeclAndImports.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/ModuleDeclAndImports.hs diff --git a/ghcide/test/data/import-placement/MultiLineCommentAtTop.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/MultiLineCommentAtTop.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/MultiLineCommentAtTop.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/MultiLineCommentAtTop.expected.hs diff --git a/ghcide/test/data/import-placement/MultiLineCommentAtTop.hs b/plugins/hls-refactor-plugin/test/data/import-placement/MultiLineCommentAtTop.hs similarity index 100% rename from ghcide/test/data/import-placement/MultiLineCommentAtTop.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/MultiLineCommentAtTop.hs diff --git a/ghcide/test/data/import-placement/MultiLinePragma.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/MultiLinePragma.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/MultiLinePragma.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/MultiLinePragma.expected.hs diff --git a/ghcide/test/data/import-placement/MultiLinePragma.hs b/plugins/hls-refactor-plugin/test/data/import-placement/MultiLinePragma.hs similarity index 100% rename from ghcide/test/data/import-placement/MultiLinePragma.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/MultiLinePragma.hs diff --git a/ghcide/test/data/import-placement/MultipleImportsAtTop.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/MultipleImportsAtTop.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/MultipleImportsAtTop.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/MultipleImportsAtTop.expected.hs diff --git a/ghcide/test/data/import-placement/MultipleImportsAtTop.hs b/plugins/hls-refactor-plugin/test/data/import-placement/MultipleImportsAtTop.hs similarity index 100% rename from ghcide/test/data/import-placement/MultipleImportsAtTop.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/MultipleImportsAtTop.hs diff --git a/ghcide/test/data/import-placement/MultipleLanguagePragmasNoModuleDeclaration.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/MultipleLanguagePragmasNoModuleDeclaration.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/MultipleLanguagePragmasNoModuleDeclaration.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/MultipleLanguagePragmasNoModuleDeclaration.expected.hs diff --git a/ghcide/test/data/import-placement/MultipleLanguagePragmasNoModuleDeclaration.hs b/plugins/hls-refactor-plugin/test/data/import-placement/MultipleLanguagePragmasNoModuleDeclaration.hs similarity index 100% rename from ghcide/test/data/import-placement/MultipleLanguagePragmasNoModuleDeclaration.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/MultipleLanguagePragmasNoModuleDeclaration.hs diff --git a/ghcide/test/data/import-placement/NewTypeAtTop.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/NewTypeAtTop.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/NewTypeAtTop.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/NewTypeAtTop.expected.hs diff --git a/ghcide/test/data/import-placement/NewTypeAtTop.hs b/plugins/hls-refactor-plugin/test/data/import-placement/NewTypeAtTop.hs similarity index 100% rename from ghcide/test/data/import-placement/NewTypeAtTop.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/NewTypeAtTop.hs diff --git a/ghcide/test/data/import-placement/NoExplicitExportCommentAtTop.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/NoExplicitExportCommentAtTop.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/NoExplicitExportCommentAtTop.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/NoExplicitExportCommentAtTop.expected.hs diff --git a/ghcide/test/data/import-placement/NoExplicitExportCommentAtTop.hs b/plugins/hls-refactor-plugin/test/data/import-placement/NoExplicitExportCommentAtTop.hs similarity index 100% rename from ghcide/test/data/import-placement/NoExplicitExportCommentAtTop.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/NoExplicitExportCommentAtTop.hs diff --git a/ghcide/test/data/import-placement/NoExplicitExports.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/NoExplicitExports.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/NoExplicitExports.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/NoExplicitExports.expected.hs diff --git a/ghcide/test/data/import-placement/NoExplicitExports.hs b/plugins/hls-refactor-plugin/test/data/import-placement/NoExplicitExports.hs similarity index 100% rename from ghcide/test/data/import-placement/NoExplicitExports.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/NoExplicitExports.hs diff --git a/ghcide/test/data/import-placement/NoModuleDeclaration.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/NoModuleDeclaration.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/NoModuleDeclaration.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/NoModuleDeclaration.expected.hs diff --git a/ghcide/test/data/import-placement/NoModuleDeclaration.hs b/plugins/hls-refactor-plugin/test/data/import-placement/NoModuleDeclaration.hs similarity index 100% rename from ghcide/test/data/import-placement/NoModuleDeclaration.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/NoModuleDeclaration.hs diff --git a/ghcide/test/data/import-placement/NoModuleDeclarationCommentAtTop.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/NoModuleDeclarationCommentAtTop.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/NoModuleDeclarationCommentAtTop.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/NoModuleDeclarationCommentAtTop.expected.hs diff --git a/ghcide/test/data/import-placement/NoModuleDeclarationCommentAtTop.hs b/plugins/hls-refactor-plugin/test/data/import-placement/NoModuleDeclarationCommentAtTop.hs similarity index 100% rename from ghcide/test/data/import-placement/NoModuleDeclarationCommentAtTop.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/NoModuleDeclarationCommentAtTop.hs diff --git a/ghcide/test/data/import-placement/OptionsNotAtTopWithSpaces.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/OptionsNotAtTopWithSpaces.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/OptionsNotAtTopWithSpaces.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/OptionsNotAtTopWithSpaces.expected.hs diff --git a/ghcide/test/data/import-placement/OptionsNotAtTopWithSpaces.hs b/plugins/hls-refactor-plugin/test/data/import-placement/OptionsNotAtTopWithSpaces.hs similarity index 100% rename from ghcide/test/data/import-placement/OptionsNotAtTopWithSpaces.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/OptionsNotAtTopWithSpaces.hs diff --git a/ghcide/test/data/import-placement/OptionsPragmaNotAtTop.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/OptionsPragmaNotAtTop.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/OptionsPragmaNotAtTop.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/OptionsPragmaNotAtTop.expected.hs diff --git a/ghcide/test/data/import-placement/OptionsPragmaNotAtTop.hs b/plugins/hls-refactor-plugin/test/data/import-placement/OptionsPragmaNotAtTop.hs similarity index 100% rename from ghcide/test/data/import-placement/OptionsPragmaNotAtTop.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/OptionsPragmaNotAtTop.hs diff --git a/ghcide/test/data/import-placement/PragmaNotAtTopMultipleComments.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/PragmaNotAtTopMultipleComments.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/PragmaNotAtTopMultipleComments.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/PragmaNotAtTopMultipleComments.expected.hs diff --git a/ghcide/test/data/import-placement/PragmaNotAtTopMultipleComments.hs b/plugins/hls-refactor-plugin/test/data/import-placement/PragmaNotAtTopMultipleComments.hs similarity index 100% rename from ghcide/test/data/import-placement/PragmaNotAtTopMultipleComments.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/PragmaNotAtTopMultipleComments.hs diff --git a/ghcide/test/data/import-placement/PragmaNotAtTopWithCommentsAtTop.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/PragmaNotAtTopWithCommentsAtTop.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/PragmaNotAtTopWithCommentsAtTop.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/PragmaNotAtTopWithCommentsAtTop.expected.hs diff --git a/ghcide/test/data/import-placement/PragmaNotAtTopWithCommentsAtTop.hs b/plugins/hls-refactor-plugin/test/data/import-placement/PragmaNotAtTopWithCommentsAtTop.hs similarity index 100% rename from ghcide/test/data/import-placement/PragmaNotAtTopWithCommentsAtTop.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/PragmaNotAtTopWithCommentsAtTop.hs diff --git a/ghcide/test/data/import-placement/PragmaNotAtTopWithImports.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/PragmaNotAtTopWithImports.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/PragmaNotAtTopWithImports.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/PragmaNotAtTopWithImports.expected.hs diff --git a/ghcide/test/data/import-placement/PragmaNotAtTopWithImports.hs b/plugins/hls-refactor-plugin/test/data/import-placement/PragmaNotAtTopWithImports.hs similarity index 100% rename from ghcide/test/data/import-placement/PragmaNotAtTopWithImports.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/PragmaNotAtTopWithImports.hs diff --git a/ghcide/test/data/import-placement/PragmaNotAtTopWithModuleDecl.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/PragmaNotAtTopWithModuleDecl.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/PragmaNotAtTopWithModuleDecl.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/PragmaNotAtTopWithModuleDecl.expected.hs diff --git a/ghcide/test/data/import-placement/PragmaNotAtTopWithModuleDecl.hs b/plugins/hls-refactor-plugin/test/data/import-placement/PragmaNotAtTopWithModuleDecl.hs similarity index 100% rename from ghcide/test/data/import-placement/PragmaNotAtTopWithModuleDecl.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/PragmaNotAtTopWithModuleDecl.hs diff --git a/ghcide/test/data/import-placement/PragmasAndShebangsNoComment.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/PragmasAndShebangsNoComment.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/PragmasAndShebangsNoComment.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/PragmasAndShebangsNoComment.expected.hs diff --git a/ghcide/test/data/import-placement/PragmasAndShebangsNoComment.hs b/plugins/hls-refactor-plugin/test/data/import-placement/PragmasAndShebangsNoComment.hs similarity index 100% rename from ghcide/test/data/import-placement/PragmasAndShebangsNoComment.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/PragmasAndShebangsNoComment.hs diff --git a/ghcide/test/data/import-placement/PragmasShebangsAndModuleDecl.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/PragmasShebangsAndModuleDecl.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/PragmasShebangsAndModuleDecl.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/PragmasShebangsAndModuleDecl.expected.hs diff --git a/ghcide/test/data/import-placement/PragmasShebangsAndModuleDecl.hs b/plugins/hls-refactor-plugin/test/data/import-placement/PragmasShebangsAndModuleDecl.hs similarity index 100% rename from ghcide/test/data/import-placement/PragmasShebangsAndModuleDecl.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/PragmasShebangsAndModuleDecl.hs diff --git a/ghcide/test/data/import-placement/PragmasShebangsModuleExplicitExports.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/PragmasShebangsModuleExplicitExports.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/PragmasShebangsModuleExplicitExports.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/PragmasShebangsModuleExplicitExports.expected.hs diff --git a/ghcide/test/data/import-placement/PragmasShebangsModuleExplicitExports.hs b/plugins/hls-refactor-plugin/test/data/import-placement/PragmasShebangsModuleExplicitExports.hs similarity index 100% rename from ghcide/test/data/import-placement/PragmasShebangsModuleExplicitExports.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/PragmasShebangsModuleExplicitExports.hs diff --git a/ghcide/test/data/import-placement/PragmasThenShebangsMultilineComment.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/PragmasThenShebangsMultilineComment.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/PragmasThenShebangsMultilineComment.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/PragmasThenShebangsMultilineComment.expected.hs diff --git a/ghcide/test/data/import-placement/PragmasThenShebangsMultilineComment.hs b/plugins/hls-refactor-plugin/test/data/import-placement/PragmasThenShebangsMultilineComment.hs similarity index 100% rename from ghcide/test/data/import-placement/PragmasThenShebangsMultilineComment.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/PragmasThenShebangsMultilineComment.hs diff --git a/ghcide/test/data/import-placement/ShebangNotAtTop.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/ShebangNotAtTop.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/ShebangNotAtTop.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/ShebangNotAtTop.expected.hs diff --git a/ghcide/test/data/import-placement/ShebangNotAtTop.hs b/plugins/hls-refactor-plugin/test/data/import-placement/ShebangNotAtTop.hs similarity index 100% rename from ghcide/test/data/import-placement/ShebangNotAtTop.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/ShebangNotAtTop.hs diff --git a/ghcide/test/data/import-placement/ShebangNotAtTopNoSpace.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/ShebangNotAtTopNoSpace.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/ShebangNotAtTopNoSpace.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/ShebangNotAtTopNoSpace.expected.hs diff --git a/ghcide/test/data/import-placement/ShebangNotAtTopNoSpace.hs b/plugins/hls-refactor-plugin/test/data/import-placement/ShebangNotAtTopNoSpace.hs similarity index 100% rename from ghcide/test/data/import-placement/ShebangNotAtTopNoSpace.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/ShebangNotAtTopNoSpace.hs diff --git a/ghcide/test/data/import-placement/ShebangNotAtTopWithSpaces.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/ShebangNotAtTopWithSpaces.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/ShebangNotAtTopWithSpaces.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/ShebangNotAtTopWithSpaces.expected.hs diff --git a/ghcide/test/data/import-placement/ShebangNotAtTopWithSpaces.hs b/plugins/hls-refactor-plugin/test/data/import-placement/ShebangNotAtTopWithSpaces.hs similarity index 100% rename from ghcide/test/data/import-placement/ShebangNotAtTopWithSpaces.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/ShebangNotAtTopWithSpaces.hs diff --git a/ghcide/test/data/import-placement/TwoDashOnlyComment.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/TwoDashOnlyComment.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/TwoDashOnlyComment.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/TwoDashOnlyComment.expected.hs diff --git a/ghcide/test/data/import-placement/TwoDashOnlyComment.hs b/plugins/hls-refactor-plugin/test/data/import-placement/TwoDashOnlyComment.hs similarity index 100% rename from ghcide/test/data/import-placement/TwoDashOnlyComment.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/TwoDashOnlyComment.hs diff --git a/ghcide/test/data/import-placement/WhereDeclLowerInFile.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/WhereDeclLowerInFile.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/WhereDeclLowerInFile.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/WhereDeclLowerInFile.expected.hs diff --git a/ghcide/test/data/import-placement/WhereDeclLowerInFile.hs b/plugins/hls-refactor-plugin/test/data/import-placement/WhereDeclLowerInFile.hs similarity index 100% rename from ghcide/test/data/import-placement/WhereDeclLowerInFile.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/WhereDeclLowerInFile.hs diff --git a/ghcide/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.expected.hs diff --git a/ghcide/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.hs b/plugins/hls-refactor-plugin/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.hs similarity index 100% rename from ghcide/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/WhereDeclLowerInFileWithCommentsBeforeIt.hs diff --git a/ghcide/test/data/import-placement/WhereKeywordLowerInFileNoExports.expected.hs b/plugins/hls-refactor-plugin/test/data/import-placement/WhereKeywordLowerInFileNoExports.expected.hs similarity index 100% rename from ghcide/test/data/import-placement/WhereKeywordLowerInFileNoExports.expected.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/WhereKeywordLowerInFileNoExports.expected.hs diff --git a/ghcide/test/data/import-placement/WhereKeywordLowerInFileNoExports.hs b/plugins/hls-refactor-plugin/test/data/import-placement/WhereKeywordLowerInFileNoExports.hs similarity index 100% rename from ghcide/test/data/import-placement/WhereKeywordLowerInFileNoExports.hs rename to plugins/hls-refactor-plugin/test/data/import-placement/WhereKeywordLowerInFileNoExports.hs diff --git a/plugins/hls-rename-plugin/hls-rename-plugin.cabal b/plugins/hls-rename-plugin/hls-rename-plugin.cabal index c6f20198fe..e0c295dc12 100644 --- a/plugins/hls-rename-plugin/hls-rename-plugin.cabal +++ b/plugins/hls-rename-plugin/hls-rename-plugin.cabal @@ -29,6 +29,7 @@ library , hashable , hiedb , hls-plugin-api ^>= 1.3 || ^>=1.4 + , hls-refactor-plugin , lsp , lsp-types , mod diff --git a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs index b83423254a..c6c1238b61 100644 --- a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs +++ b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs @@ -29,6 +29,7 @@ import qualified Data.Map as M import Data.Maybe import Data.Mod.Word import qualified Data.Text as T +import Development.IDE (Recorder, WithPriority) import Development.IDE.Core.PositionMapping import Development.IDE.Core.RuleTypes import Development.IDE.Core.Service @@ -39,8 +40,10 @@ import Development.IDE.GHC.Compat.Parser import Development.IDE.GHC.Compat.Units import Development.IDE.GHC.Error import Development.IDE.GHC.ExactPrint +import qualified Development.IDE.GHC.ExactPrint as E import Development.IDE.Spans.AtPoint import Development.IDE.Types.Location +import Development.IDE.Plugin.CodeAction import HieDb.Query import Ide.Plugin.Properties import Ide.PluginUtils @@ -50,8 +53,8 @@ import Language.LSP.Types instance Hashable (Mod a) where hash n = hash (unMod n) -descriptor :: PluginId -> PluginDescriptor IdeState -descriptor pluginId = (defaultPluginDescriptor pluginId) +descriptor :: Recorder (WithPriority E.Log) -> PluginId -> PluginDescriptor IdeState +descriptor recorder pluginId = mkExactprintPluginDescriptor recorder $ (defaultPluginDescriptor pluginId) { pluginHandlers = mkPluginHandler STextDocumentRename renameProvider , pluginConfigDescriptor = defaultConfigDescriptor { configCustomConfig = mkCustomConfig properties } diff --git a/plugins/hls-rename-plugin/test/Main.hs b/plugins/hls-rename-plugin/test/Main.hs index 21151dec1a..5d662b1ad6 100644 --- a/plugins/hls-rename-plugin/test/Main.hs +++ b/plugins/hls-rename-plugin/test/Main.hs @@ -13,7 +13,7 @@ main :: IO () main = defaultTestRunner tests renamePlugin :: PluginDescriptor IdeState -renamePlugin = Rename.descriptor "rename" +renamePlugin = Rename.descriptor mempty "rename" -- See https://github.com/wz1000/HieDb/issues/45 recordConstructorIssue :: String diff --git a/plugins/hls-splice-plugin/hls-splice-plugin.cabal b/plugins/hls-splice-plugin/hls-splice-plugin.cabal index ae13452eaa..4dc8f7fdd9 100644 --- a/plugins/hls-splice-plugin/hls-splice-plugin.cabal +++ b/plugins/hls-splice-plugin/hls-splice-plugin.cabal @@ -40,6 +40,7 @@ library , ghc-exactprint , ghcide ^>=1.6 || ^>=1.7 , hls-plugin-api ^>=1.3 || ^>=1.4 + , hls-refactor-plugin , lens , lsp , retrie diff --git a/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs b/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs index 14ce391783..41b5774706 100644 --- a/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs +++ b/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs @@ -47,6 +47,7 @@ import Data.Maybe (fromMaybe, listToMaybe, import qualified Data.Text as T import Development.IDE import Development.IDE.GHC.Compat as Compat hiding (getLoc) +import Development.IDE.GHC.Compat.ExactPrint import qualified Development.IDE.GHC.Compat.Util as Util import Development.IDE.GHC.ExactPrint import GHC.Exts diff --git a/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal b/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal index 781c39028a..14faae448e 100644 --- a/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal +++ b/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal @@ -85,6 +85,7 @@ library , ghcide ^>=1.7 , hls-graph , hls-plugin-api ^>=1.4 + , hls-refactor-plugin , hyphenation , lens , lsp diff --git a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs b/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs index ed896a99eb..d80e336864 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs @@ -34,6 +34,7 @@ import Development.IDE.Core.Shake (IdeState (..), uses, define, use, a import qualified Development.IDE.Core.Shake as IDE import Development.IDE.Core.UseStale import Development.IDE.GHC.Compat hiding (empty) +import Development.IDE.GHC.Compat.ExactPrint import qualified Development.IDE.GHC.Compat.Util as FastString import Development.IDE.GHC.Error (realSrcSpanToRange) import Development.IDE.GHC.ExactPrint hiding (LogShake, Log) diff --git a/plugins/hls-tactics-plugin/src/Wingman/Plugin.hs b/plugins/hls-tactics-plugin/src/Wingman/Plugin.hs index 6473a725d5..b55ee31ae3 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/Plugin.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/Plugin.hs @@ -3,6 +3,8 @@ module Wingman.Plugin where import Control.Monad import Development.IDE.Core.Shake (IdeState (..)) +import Development.IDE.Plugin.CodeAction +import qualified Development.IDE.GHC.ExactPrint as E import Ide.Types import Language.LSP.Types import Prelude hiding (span) @@ -15,17 +17,20 @@ import Wingman.LanguageServer.Metaprogram (hoverProvider) import Wingman.StaticPlugin import Development.IDE.Types.Logger (Recorder, cmapWithPrio, WithPriority, Pretty (pretty)) -newtype Log +data Log = LogWingmanLanguageServer WingmanLanguageServer.Log + | LogExactPrint E.Log deriving Show instance Pretty Log where pretty = \case LogWingmanLanguageServer log -> pretty log + LogExactPrint exactPrintLog -> pretty exactPrintLog descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState descriptor recorder plId - = installInteractions + = mkExactprintPluginDescriptor (cmapWithPrio LogExactPrint recorder) + $ installInteractions ( emptyCaseInteraction : fmap makeTacticInteraction [minBound .. maxBound] ) diff --git a/src/HlsPlugins.hs b/src/HlsPlugins.hs index 2fc1e41235..2adef201dc 100644 --- a/src/HlsPlugins.hs +++ b/src/HlsPlugins.hs @@ -115,6 +115,10 @@ import qualified Ide.Plugin.StylishHaskell as StylishHaskell import qualified Ide.Plugin.Brittany as Brittany #endif +#if hls_refactor +import qualified Development.IDE.Plugin.CodeAction as Refactor +#endif + data Log = forall a. (Pretty a) => Log a instance Pretty Log where @@ -152,7 +156,7 @@ idePlugins recorder = pluginDescToIdePlugins allPlugins StylishHaskell.descriptor "stylish-haskell" : #endif #if hls_rename - Rename.descriptor "rename" : + Rename.descriptor pluginRecorder "rename" : #endif #if hls_retrie Retrie.descriptor "retrie" : @@ -167,7 +171,7 @@ idePlugins recorder = pluginDescToIdePlugins allPlugins Class.descriptor pluginRecorder "class" : #endif #if hls_haddockComments - HaddockComments.descriptor "haddockComments" : + HaddockComments.descriptor pluginRecorder "haddockComments" : #endif #if hls_eval Eval.descriptor pluginRecorder "eval" : @@ -204,6 +208,13 @@ idePlugins recorder = pluginDescToIdePlugins allPlugins #endif #if hls_gadt GADT.descriptor "gadt" : +#endif +#if hls_refactor + Refactor.iePluginDescriptor pluginRecorder "ghcide-code-actions-imports-exports" : + Refactor.typeSigsPluginDescriptor pluginRecorder "ghcide-code-actions-type-signatures" : + Refactor.bindingsPluginDescriptor pluginRecorder "ghcide-code-actions-bindings" : + Refactor.fillHolePluginDescriptor pluginRecorder "ghcide-code-actions-fill-holes" : + Refactor.extendImportPluginDescriptor pluginRecorder "ghcide-extend-import-action" : #endif GhcIde.descriptors pluginRecorder #if explicitFixity diff --git a/stack-lts16.yaml b/stack-lts16.yaml index bbc53ea853..6660850114 100644 --- a/stack-lts16.yaml +++ b/stack-lts16.yaml @@ -33,6 +33,7 @@ packages: - ./plugins/hls-change-type-signature-plugin - ./plugins/hls-gadt-plugin - ./plugins/hls-explicit-fixity-plugin + - ./plugins/hls-refactor-plugin ghc-options: "$everything": -haddock diff --git a/stack-lts19.yaml b/stack-lts19.yaml index 3c9bd98508..3a1e1278ea 100644 --- a/stack-lts19.yaml +++ b/stack-lts19.yaml @@ -32,6 +32,7 @@ packages: - ./plugins/hls-change-type-signature-plugin - ./plugins/hls-gadt-plugin - ./plugins/hls-explicit-fixity-plugin + - ./plugins/hls-refactor-plugin ghc-options: "$everything": -haddock diff --git a/stack.yaml b/stack.yaml index 923990c885..2d11b73c05 100644 --- a/stack.yaml +++ b/stack.yaml @@ -32,6 +32,7 @@ packages: - ./plugins/hls-change-type-signature-plugin - ./plugins/hls-gadt-plugin - ./plugins/hls-explicit-fixity-plugin +- ./plugins/hls-refactor-plugin extra-deps: - floskell-0.10.6@sha256:e77d194189e8540abe2ace2c7cb8efafc747ca35881a2fefcbd2d40a1292e036,3819 From 2e484cd1d4bbbcc01e272679c5e9baf0f82fb8fe Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Tue, 30 Aug 2022 16:51:49 +0530 Subject: [PATCH 073/213] Move ghcide-test-utils to its own package --- cabal.project | 1 + ghcide-bench/ghcide-bench.cabal | 3 +- .../src/Development/IDE/Test/Diagnostic.hs | 48 ----- ghcide/ghcide.cabal | 5 +- ghcide/test/LICENSE | 201 ++++++++++++++++++ ghcide/test/ghcide-test-utils.cabal | 59 +++++ .../hls-refactor-plugin.cabal | 2 +- stack-lts16.yaml | 1 + stack-lts19.yaml | 1 + stack.yaml | 1 + 10 files changed, 268 insertions(+), 54 deletions(-) delete mode 100644 ghcide-bench/src/Development/IDE/Test/Diagnostic.hs create mode 100644 ghcide/test/LICENSE create mode 100644 ghcide/test/ghcide-test-utils.cabal diff --git a/cabal.project b/cabal.project index 91d017ccdf..4022fb1c0c 100644 --- a/cabal.project +++ b/cabal.project @@ -5,6 +5,7 @@ packages: ./hls-graph ./ghcide ./ghcide-bench + ./ghcide/test ./hls-plugin-api ./hls-test-utils ./plugins/hls-tactics-plugin diff --git a/ghcide-bench/ghcide-bench.cabal b/ghcide-bench/ghcide-bench.cabal index 89a9fc1080..450aadba50 100644 --- a/ghcide-bench/ghcide-bench.cabal +++ b/ghcide-bench/ghcide-bench.cabal @@ -63,8 +63,6 @@ library exposed-modules: Experiments.Types Experiments - other-modules: - Development.IDE.Test.Diagnostic build-depends: aeson, async, @@ -76,6 +74,7 @@ library extra, filepath, ghcide, + ghcide-test-utils, hashable, lens, lsp-test, diff --git a/ghcide-bench/src/Development/IDE/Test/Diagnostic.hs b/ghcide-bench/src/Development/IDE/Test/Diagnostic.hs deleted file mode 100644 index a1ea88ec28..0000000000 --- a/ghcide-bench/src/Development/IDE/Test/Diagnostic.hs +++ /dev/null @@ -1,48 +0,0 @@ --- Duplicate of ghcide/test/Development/IDE/Test/Diagnostic.hs -module Development.IDE.Test.Diagnostic where - -import Control.Lens ((^.)) -import qualified Data.Text as T -import GHC.Stack (HasCallStack) -import Language.LSP.Types -import Language.LSP.Types.Lens as Lsp - --- | (0-based line number, 0-based column number) -type Cursor = (UInt, UInt) - -cursorPosition :: Cursor -> Position -cursorPosition (line, col) = Position line col - -type ErrorMsg = String - -requireDiagnostic - :: (Foldable f, Show (f Diagnostic), HasCallStack) - => f Diagnostic - -> (DiagnosticSeverity, Cursor, T.Text, Maybe DiagnosticTag) - -> Maybe ErrorMsg -requireDiagnostic actuals expected@(severity, cursor, expectedMsg, expectedTag) - | any match actuals = Nothing - | otherwise = Just $ - "Could not find " <> show expected <> - " in " <> show actuals - where - match :: Diagnostic -> Bool - match d = - Just severity == _severity d - && cursorPosition cursor == d ^. range . start - && standardizeQuotes (T.toLower expectedMsg) `T.isInfixOf` - standardizeQuotes (T.toLower $ d ^. message) - && hasTag expectedTag (d ^. tags) - - hasTag :: Maybe DiagnosticTag -> Maybe (List DiagnosticTag) -> Bool - hasTag Nothing _ = True - hasTag (Just _) Nothing = False - hasTag (Just actualTag) (Just (List tags)) = actualTag `elem` tags - -standardizeQuotes :: T.Text -> T.Text -standardizeQuotes msg = let - repl '‘' = '\'' - repl '’' = '\'' - repl '`' = '\'' - repl c = c - in T.map repl msg diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index e3a7a89ca0..0160398733 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -349,7 +349,7 @@ test-suite ghcide-tests ghc, -------------------------------------------------------------- ghcide, - ghcide-test-utils, + ghcide-test-utils-internal, ghc-typelits-knownnat, lsp, lsp-types, @@ -402,8 +402,7 @@ test-suite ghcide-tests TypeApplications ViewPatterns -library ghcide-test-utils - visibility: public +library ghcide-test-utils-internal default-language: Haskell2010 build-depends: aeson, diff --git a/ghcide/test/LICENSE b/ghcide/test/LICENSE new file mode 100644 index 0000000000..d1f5c9033f --- /dev/null +++ b/ghcide/test/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Digital Asset (Switzerland) GmbH and/or its affiliates + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ghcide/test/ghcide-test-utils.cabal b/ghcide/test/ghcide-test-utils.cabal new file mode 100644 index 0000000000..3bdac32071 --- /dev/null +++ b/ghcide/test/ghcide-test-utils.cabal @@ -0,0 +1,59 @@ +cabal-version: 3.0 +-- This library is a copy of the sublibrary ghcide-test-utils until stack and hackage support public sublibraries +build-type: Simple +category: Development +name: ghcide-test-utils +version: 1.7.0.1 +license: Apache-2.0 +license-file: LICENSE +author: Digital Asset and Ghcide contributors +maintainer: Ghcide contributors +copyright: Digital Asset and Ghcide contributors 2018-2022 +synopsis: Test utils for ghcide +description: + Test utils for ghcide +homepage: https://github.com/haskell/haskell-language-server/tree/master/ghcide#readme +bug-reports: https://github.com/haskell/haskell-language-server/issues +tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 + +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + + +library + default-language: Haskell2010 + build-depends: + aeson, + base, + containers, + data-default, + directory, + extra, + filepath, + ghcide, + lsp-types, + hls-plugin-api, + lens, + lsp-test ^>= 0.14, + tasty-hunit >= 0.10, + text, + hs-source-dirs: src + exposed-modules: + Development.IDE.Test + Development.IDE.Test.Diagnostic + default-extensions: + BangPatterns + DeriveFunctor + DeriveGeneric + FlexibleContexts + GeneralizedNewtypeDeriving + LambdaCase + NamedFieldPuns + OverloadedStrings + RecordWildCards + ScopedTypeVariables + StandaloneDeriving + TupleSections + TypeApplications + ViewPatterns diff --git a/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal b/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal index 9461819f08..da2d1683c1 100644 --- a/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal +++ b/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal @@ -102,7 +102,7 @@ test-suite tests , text-rope , containers , ghcide - , ghcide:ghcide-test-utils + , ghcide-test-utils , shake , hls-plugin-api , lsp-test diff --git a/stack-lts16.yaml b/stack-lts16.yaml index 6660850114..50120b1841 100644 --- a/stack-lts16.yaml +++ b/stack-lts16.yaml @@ -5,6 +5,7 @@ packages: - ./hie-compat - ./hls-graph - ./ghcide/ + - ./ghcide/test - ./shake-bench - ./hls-plugin-api - ./hls-test-utils diff --git a/stack-lts19.yaml b/stack-lts19.yaml index 3a1e1278ea..c13433ac13 100644 --- a/stack-lts19.yaml +++ b/stack-lts19.yaml @@ -5,6 +5,7 @@ packages: - ./hie-compat - ./hls-graph - ./ghcide/ + - ./ghcide/test - ./hls-plugin-api - ./hls-test-utils # - ./shake-bench diff --git a/stack.yaml b/stack.yaml index 2d11b73c05..5a0649322a 100644 --- a/stack.yaml +++ b/stack.yaml @@ -5,6 +5,7 @@ packages: - ./hie-compat - ./hls-graph - ./ghcide/ +- ./ghcide/test - ./hls-plugin-api - ./hls-test-utils - ./shake-bench From e03111921bb49ea400d7de1ba16c56977bcec2fb Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Tue, 30 Aug 2022 16:57:26 +0530 Subject: [PATCH 074/213] Update hlint ignore --- .hlint.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.hlint.yaml b/.hlint.yaml index bb41a16e64..8caaebd8f6 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -54,6 +54,7 @@ within: - Development.IDE.Core.Shake - Development.IDE.GHC.Util + - Development.IDE.Plugin.CodeAction.Util - Development.IDE.Graph.Internal.Database - Development.IDE.Graph.Internal.Paths - Development.IDE.Graph.Internal.Profile @@ -184,6 +185,7 @@ - Development.IDE.Core.Shake - Development.IDE.Plugin.Completions - Development.IDE.Plugin.CodeAction.ExactPrint + - Development.IDE.Plugin.CodeAction - Development.IDE.Test - Development.IDE.Graph.Internal.Profile - Development.IDE.Graph.Internal.Rules @@ -221,6 +223,7 @@ - Development.IDE.Core.Compile - Development.IDE.Graph.Internal.Database - Development.IDE.GHC.Util + - Development.IDE.Plugin.CodeAction.Util - Wingman.Debug # We really do not want novel usages of restricted functions, and mere From fdbc555a9245cb3761c2bf7335f3d18b8cf7673c Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Tue, 30 Aug 2022 17:31:38 +0530 Subject: [PATCH 075/213] Fix benchmarks --- bench/config.yaml | 5 +++-- ghcide-bench/src/Experiments.hs | 14 ++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/bench/config.yaml b/bench/config.yaml index 19e014a485..4e99f1c8db 100644 --- a/bench/config.yaml +++ b/bench/config.yaml @@ -108,12 +108,13 @@ configurations: - ghcide-type-lenses - pragmas - Ghcide: + - ghcide-completions + - ghcide-type-lenses +- Refactor: - ghcide-code-actions-bindings - ghcide-code-actions-fill-holes - ghcide-code-actions-imports-exports - ghcide-code-actions-type-signatures - - ghcide-completions - - ghcide-type-lenses - All: - alternateNumberFormat - callHierarchy diff --git a/ghcide-bench/src/Experiments.hs b/ghcide-bench/src/Experiments.hs index 2d756d73fd..1d8d6f6c5b 100644 --- a/ghcide-bench/src/Experiments.hs +++ b/ghcide-bench/src/Experiments.hs @@ -139,7 +139,7 @@ experiments = not . null <$> getCompletions doc (fromJust identifierP), --------------------------------------------------------------------------------------- benchWithSetup - "code lens" + "code actions" ( \docs -> do unless (any (isJust . identifierP) docs) $ error "None of the example modules is suitable for this experiment" @@ -148,12 +148,13 @@ experiments = waitForProgressStart waitForProgressDone ) - ( \docs -> not . null <$> forM docs (\DocumentPositions{..} -> - getCodeLenses doc) + ( \docs -> not . null . catMaybes <$> forM docs (\DocumentPositions{..} -> + forM identifierP $ \p -> + getCodeActions doc (Range p p)) ), --------------------------------------------------------------------------------------- benchWithSetup - "code lens after edit" + "code actions after edit" ( \docs -> do unless (any (isJust . identifierP) docs) $ error "None of the example modules is suitable for this experiment" @@ -165,8 +166,9 @@ experiments = changeDoc doc [charEdit stringLiteralP] waitForProgressStart waitForProgressDone - not . null <$> forM docs (\DocumentPositions{..} -> do - getCodeLenses doc) + not . null . catMaybes <$> forM docs (\DocumentPositions{..} -> do + forM identifierP $ \p -> + getCodeActions doc (Range p p)) ), --------------------------------------------------------------------------------------- benchWithSetup From 6c99563a9f82f2545448e54be93180b935513ccc Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Mon, 27 Jun 2022 11:58:43 +0530 Subject: [PATCH 076/213] 9.4 support + MHU --- .github/actions/setup-build/action.yml | 2 +- .github/workflows/test.yml | 10 +- .gitlab-ci.yml | 6 +- bindist/ghcs | 2 + bindist/ghcs-Msys | 2 + cabal.project | 13 + ghcide/ghcide.cabal | 3 +- .../session-loader/Development/IDE/Session.hs | 47 +- ghcide/src/Development/IDE/Core/Compile.hs | 403 ++++++++++++++---- .../Development/IDE/Core/IdeConfiguration.hs | 4 +- .../src/Development/IDE/Core/Preprocessor.hs | 14 + ghcide/src/Development/IDE/Core/Rules.hs | 35 +- ghcide/src/Development/IDE/Core/Shake.hs | 22 +- ghcide/src/Development/IDE/GHC/CPP.hs | 3 + ghcide/src/Development/IDE/GHC/Compat.hs | 99 ++++- ghcide/src/Development/IDE/GHC/Compat/Core.hs | 185 ++++++-- ghcide/src/Development/IDE/GHC/Compat/Env.hs | 18 +- .../src/Development/IDE/GHC/Compat/Iface.hs | 7 +- .../src/Development/IDE/GHC/Compat/Logger.hs | 13 + .../Development/IDE/GHC/Compat/Outputable.hs | 32 +- .../src/Development/IDE/GHC/Compat/Parser.hs | 4 + .../src/Development/IDE/GHC/Compat/Plugins.hs | 21 +- .../src/Development/IDE/GHC/Compat/Units.hs | 101 +++-- ghcide/src/Development/IDE/GHC/Compat/Util.hs | 2 + ghcide/src/Development/IDE/GHC/Error.hs | 13 +- ghcide/src/Development/IDE/GHC/Orphans.hs | 17 + ghcide/src/Development/IDE/GHC/Util.hs | 2 +- ghcide/src/Development/IDE/GHC/Warnings.hs | 23 +- .../src/Development/IDE/Import/FindImports.hs | 81 +++- ghcide/src/Development/IDE/LSP/Outline.hs | 30 +- .../IDE/Plugin/Completions/Logic.hs | 4 + ghcide/src/Development/IDE/Spans/Common.hs | 11 +- .../Development/IDE/Spans/Documentation.hs | 17 +- ghcide/test/exe/Main.hs | 47 +- haskell-language-server.cabal | 40 +- hie-compat/hie-compat.cabal | 2 + .../src-reexport-ghc92/Compat/HieAst.hs | 3 + .../src-reexport-ghc92/Compat/HieBin.hs | 8 + .../src-reexport-ghc92/Compat/HieDebug.hs | 10 + .../src-reexport-ghc92/Compat/HieTypes.hs | 3 + .../src-reexport-ghc92/Compat/HieUtils.hs | 3 + .../hls-alternate-number-format-plugin.cabal | 8 + .../hls-call-hierarchy-plugin.cabal | 8 + .../hls-change-type-signature-plugin.cabal | 8 + .../hls-class-plugin/hls-class-plugin.cabal | 8 + .../hls-code-range-plugin.cabal | 8 + plugins/hls-eval-plugin/hls-eval-plugin.cabal | 8 + .../hls-explicit-fixity-plugin.cabal | 8 + .../hls-explicit-imports-plugin.cabal | 8 + .../hls-floskell-plugin.cabal | 8 + .../hls-fourmolu-plugin.cabal | 8 + plugins/hls-gadt-plugin/hls-gadt-plugin.cabal | 8 + .../hls-hlint-plugin/hls-hlint-plugin.cabal | 8 + .../hls-module-name-plugin.cabal | 8 + .../hls-ormolu-plugin/hls-ormolu-plugin.cabal | 8 + .../hls-pragmas-plugin.cabal | 8 + .../hls-qualify-imported-names-plugin.cabal | 8 + .../hls-refactor-plugin.cabal | 8 + .../Development/IDE/GHC/Compat/ExactPrint.hs | 5 + .../src/Development/IDE/GHC/Dump.hs | 8 +- .../src/Development/IDE/GHC/ExactPrint.hs | 5 + .../src/Development/IDE/Plugin/CodeAction.hs | 39 +- .../Development/IDE/Plugin/CodeAction/Args.hs | 19 + .../hls-refine-imports-plugin.cabal | 8 + .../hls-rename-plugin/hls-rename-plugin.cabal | 8 + .../hls-retrie-plugin/hls-retrie-plugin.cabal | 4 + .../hls-splice-plugin/hls-splice-plugin.cabal | 8 + plugins/hls-stan-plugin/hls-stan-plugin.cabal | 2 +- .../hls-stylish-haskell-plugin.cabal | 8 + .../hls-tactics-plugin.cabal | 8 + 70 files changed, 1372 insertions(+), 248 deletions(-) create mode 100644 hie-compat/src-reexport-ghc92/Compat/HieAst.hs create mode 100644 hie-compat/src-reexport-ghc92/Compat/HieBin.hs create mode 100644 hie-compat/src-reexport-ghc92/Compat/HieDebug.hs create mode 100644 hie-compat/src-reexport-ghc92/Compat/HieTypes.hs create mode 100644 hie-compat/src-reexport-ghc92/Compat/HieUtils.hs diff --git a/.github/actions/setup-build/action.yml b/.github/actions/setup-build/action.yml index 15b309926e..0bc80aca7f 100644 --- a/.github/actions/setup-build/action.yml +++ b/.github/actions/setup-build/action.yml @@ -7,7 +7,7 @@ inputs: cabal: description: "Cabal version" required: false - default: "3.6" + default: "3.8.1.0" os: description: "Operating system: Linux, Windows or macOS" required: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4891398af9..059a804125 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -57,7 +57,9 @@ jobs: strategy: fail-fast: true matrix: - ghc: [ "9.2.4" + ghc: [ "9.4.2" + , "9.4.1" + , "9.2.4" , "9.2.3" , "9.0.2" , "8.10.7" @@ -69,6 +71,9 @@ jobs: ] include: # only test supported ghc major versions + - os: ubuntu-latest + ghc: '9.4.2' + test: true - os: ubuntu-latest ghc: '9.2.4' test: true @@ -84,6 +89,9 @@ jobs: - os: ubuntu-latest ghc: '8.6.5' test: true + - os: windows-latest + ghc: '9.4.2' + test: true - os: windows-latest ghc: '9.2.4' test: true diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d0bed4d60d..1152d09d36 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,7 +9,7 @@ variables: # Commit of ghc/ci-images repository from which to pull Docker images DOCKER_REV: "4ed1a4f27828ba96a34662dc954335e29b470cd2" - CABAL_INSTALL_VERSION: 3.6.2.0 + CABAL_INSTALL_VERSION: 3.8.1.0 .windows_matrix: &windows_matrix matrix: @@ -21,6 +21,10 @@ variables: CABAL_PROJECT: cabal.project - GHC_VERSION: 9.2.4 CABAL_PROJECT: cabal.project + - GHC_VERSION: 9.4.1 + CABAL_PROJECT: cabal.project + - GHC_VERSION: 9.4.2 + CABAL_PROJECT: cabal.project workflow: rules: diff --git a/bindist/ghcs b/bindist/ghcs index b5009e2c78..151afa1251 100644 --- a/bindist/ghcs +++ b/bindist/ghcs @@ -4,3 +4,5 @@ 9.0.2,cabal.project 9.2.3,cabal.project 9.2.4,cabal.project +9.4.1,cabal.project +9.4.2,cabal.project diff --git a/bindist/ghcs-Msys b/bindist/ghcs-Msys index b4ed5601d5..17e3ffea1c 100644 --- a/bindist/ghcs-Msys +++ b/bindist/ghcs-Msys @@ -2,3 +2,5 @@ 9.0.2,cabal.project 9.2.3,cabal.project 9.2.4,cabal.project +9.4.1,cabal.project +9.4.2,cabal.project diff --git a/cabal.project b/cabal.project index 4022fb1c0c..eb2a58c05c 100644 --- a/cabal.project +++ b/cabal.project @@ -67,6 +67,16 @@ source-repository-package tag: 7a0af7a8fd38045fd15fb13445bdcc7085325460 -- https://github.com/tibbe/ekg-json/pull/12 +source-repository-package + type:git + location: https://github.com/wz1000/hiedb + tag: 67b92df2359558091df9102db5b701327308b930 + +source-repository-package + type:git + location: https://github.com/wz1000/hie-bios + tag: aa73d3d2eb89df0003d2468a105e326d71b62cc7 + -- Needed for ghcide-bench until a new release of lsp-test is out source-repository-package type:git @@ -76,6 +86,9 @@ source-repository-package -- https://github.com/haskell/lsp/pull/450 allow-newer: + base, ghc-prim, ghc-bignum, ghc, Cabal, binary, bytestring, unix, time, template-haskell, + ghc-paths:Cabal, + -- ghc-9.2 ---------- hiedb:base, diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index 0160398733..810d8a47f1 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -350,7 +350,6 @@ test-suite ghcide-tests -------------------------------------------------------------- ghcide, ghcide-test-utils-internal, - ghc-typelits-knownnat, lsp, lsp-types, hls-plugin-api, @@ -378,6 +377,8 @@ test-suite ghcide-tests build-depends: record-dot-preprocessor, record-hasfield + if impl(ghc < 9.3) + build-depends: ghc-typelits-knownnat hs-source-dirs: test/cabal test/exe bench/lib ghc-options: -threaded -Wall -Wno-name-shadowing -O0 -Wno-unticked-promoted-constructors main-is: Main.hs diff --git a/ghcide/session-loader/Development/IDE/Session.hs b/ghcide/session-loader/Development/IDE/Session.hs index 4a3e932025..da0a7dd5e3 100644 --- a/ghcide/session-loader/Development/IDE/Session.hs +++ b/ghcide/session-loader/Development/IDE/Session.hs @@ -1,6 +1,7 @@ {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE CPP #-} {-| The logic for setting up a ghcide session by tapping into hie-bios. @@ -100,6 +101,9 @@ import HieDb.Types import HieDb.Utils import qualified System.Random as Random import System.Random (RandomGen) +import Control.Monad.IO.Unlift (MonadUnliftIO) +import Control.Exception (evaluate) +import Control.DeepSeq data Log = LogSettingInitialDynFlags @@ -208,11 +212,13 @@ data SessionLoadingOptions = SessionLoadingOptions , getCacheDirs :: String -> [String] -> IO CacheDirs -- | Return the GHC lib dir to use for the 'unsafeGlobalDynFlags' , getInitialGhcLibDir :: Recorder (WithPriority Log) -> FilePath -> IO (Maybe LibDir) +#if !MIN_VERSION_ghc(9,3,0) , fakeUid :: UnitId -- ^ unit id used to tag the internal component built by ghcide -- To reuse external interface files the unit ids must match, -- thus make sure to build them with `--this-unit-id` set to the -- same value as the ghcide fake uid +#endif } instance Default SessionLoadingOptions where @@ -221,7 +227,9 @@ instance Default SessionLoadingOptions where ,loadCradle = loadWithImplicitCradle ,getCacheDirs = getCacheDirsDefault ,getInitialGhcLibDir = getInitialGhcLibDirDefault +#if !MIN_VERSION_ghc(9,3,0) ,fakeUid = Compat.toUnitId (Compat.stringToUnit "main") +#endif } -- | Find the cradle for a given 'hie.yaml' configuration. @@ -494,7 +502,11 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} dir = do new_deps' <- forM new_deps $ \RawComponentInfo{..} -> do -- Remove all inplace dependencies from package flags for -- components in this HscEnv +#if MIN_VERSION_ghc(9,3,0) + let (df2, uids) = (rawComponentDynFlags, []) +#else let (df2, uids) = removeInplacePackages fakeUid inplace rawComponentDynFlags +#endif let prefix = show rawComponentUnitId -- See Note [Avoiding bad interface files] let hscComponents = sort $ map show uids @@ -517,10 +529,14 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} dir = do -- that I do not fully understand log Info $ LogMakingNewHscEnv inplace hscEnv <- emptyHscEnv ideNc libDir - newHscEnv <- + !newHscEnv <- -- Add the options for the current component to the HscEnv evalGhcEnv hscEnv $ do - _ <- setSessionDynFlags $ setHomeUnitId_ fakeUid df + _ <- setSessionDynFlags +#if !MIN_VERSION_ghc(9,3,0) + $ setHomeUnitId_ fakeUid +#endif + df getSession -- Modify the map so the hieYaml now maps to the newly created @@ -718,7 +734,11 @@ cradleToOptsAndLibDir recorder cradle file = do logWith recorder Info $ LogNoneCradleFound file return (Left []) +#if MIN_VERSION_ghc(9,3,0) +emptyHscEnv :: NameCache -> FilePath -> IO HscEnv +#else emptyHscEnv :: IORef NameCache -> FilePath -> IO HscEnv +#endif emptyHscEnv nc libDir = do env <- runGhc (Just libDir) getSession initDynLinker env @@ -757,7 +777,11 @@ toFlagsMap TargetDetails{..} = [ (l, (targetEnv, targetDepends)) | l <- targetLocations] +#if MIN_VERSION_ghc(9,3,0) +setNameCache :: NameCache -> HscEnv -> HscEnv +#else setNameCache :: IORef NameCache -> HscEnv -> HscEnv +#endif setNameCache nc hsc = hsc { hsc_NC = nc } -- | Create a mapping from FilePaths to HscEnvEqs @@ -773,6 +797,11 @@ newComponentCache newComponentCache recorder exts cradlePath cfp hsc_env uids ci = do let df = componentDynFlags ci hscEnv' <- +#if MIN_VERSION_ghc(9,3,0) + -- Set up a multi component session with the other units on GHC 9.4 + Compat.initUnits (map snd uids) (hscSetFlags df hsc_env) +#elif MIN_VERSION_ghc(9,2,0) + -- This initializes the units for GHC 9.2 -- Add the options for the current component to the HscEnv -- We want to call `setSessionDynFlags` instead of `hscSetFlags` -- because `setSessionDynFlags` also initializes the package database, @@ -782,7 +811,10 @@ newComponentCache recorder exts cradlePath cfp hsc_env uids ci = do evalGhcEnv hsc_env $ do _ <- setSessionDynFlags $ df getSession - +#else + -- getOptions is enough to initialize units on GHC <9.2 + pure $ hscSetFlags df hsc_env { hsc_IC = (hsc_IC hsc_env) { ic_dflags = df } } +#endif let newFunc = maybe newHscEnvEqPreserveImportPaths newHscEnvEq cradlePath henv <- newFunc hscEnv' uids @@ -790,6 +822,7 @@ newComponentCache recorder exts cradlePath cfp hsc_env uids ci = do targetDepends = componentDependencyInfo ci res = (targetEnv, targetDepends) logWith recorder Debug $ LogNewComponentCache res + evaluate $ liftRnf rwhnf $ componentTargets ci let mk t = fromTargetId (importPaths df) exts (targetId t) targetEnv targetDepends ctargets <- concatMapM mk (componentTargets ci) @@ -998,9 +1031,11 @@ setOptions (ComponentOptions theOpts compRoot _) dflags = do -- initPackages parses the -package flags and -- sets up the visibility for each component. -- Throws if a -package flag cannot be satisfied. - env <- hscSetFlags dflags'' <$> getSession - final_env' <- liftIO $ wrapPackageSetupException $ Compat.initUnits env - return (hsc_dflags final_env', targets) + -- This only works for GHC <9.2 + -- For GHC >= 9.2, we need to modify the unit env in the hsc_dflags, which + -- is done later in newComponentCache + final_flags <- liftIO $ wrapPackageSetupException $ Compat.oldInitUnits dflags'' + return (final_flags, targets) setIgnoreInterfacePragmas :: DynFlags -> DynFlags setIgnoreInterfacePragmas df = diff --git a/ghcide/src/Development/IDE/Core/Compile.hs b/ghcide/src/Development/IDE/Core/Compile.hs index 2430ec719a..564078b513 100644 --- a/ghcide/src/Development/IDE/Core/Compile.hs +++ b/ghcide/src/Development/IDE/Core/Compile.hs @@ -63,6 +63,7 @@ import Data.IORef import Data.List.Extra import Data.Map (Map) import qualified Data.Map.Strict as Map +import qualified Data.Set as Set import Data.Maybe import qualified Data.Text as T import Data.Time (UTCTime (..)) @@ -220,7 +221,12 @@ captureSplicesAndDeps TypecheckHelpers{..} env k = do -- names in the compiled bytecode, recording the modules that those names -- come from in the IORef,, as these are the modules on whose implementation -- we depend. - compile_bco_hook :: IORef (ModuleEnv BS.ByteString) -> HscEnv -> SrcSpan -> CoreExpr -> IO ForeignHValue + compile_bco_hook :: IORef (ModuleEnv BS.ByteString) -> HscEnv -> SrcSpan -> CoreExpr +#if MIN_VERSION_ghc(9,3,0) + -> IO (ForeignHValue, [Linkable], PkgsLoaded) +#else + -> IO ForeignHValue +#endif compile_bco_hook var hsc_env srcspan ds_expr = do { let dflags = hsc_dflags hsc_env @@ -241,13 +247,21 @@ captureSplicesAndDeps TypecheckHelpers{..} env k = do ; let iNTERACTIVELoc = G.ModLocation{ ml_hs_file = Nothing, ml_hi_file = panic "hscCompileCoreExpr':ml_hi_file", ml_obj_file = panic "hscCompileCoreExpr':ml_obj_file", - ml_hie_file = panic "hscCompileCoreExpr':ml_hie_file" } +#if MIN_VERSION_ghc(9,3,0) + ml_dyn_obj_file = panic "hscCompileCoreExpr':ml_dyn_obj_file", + ml_dyn_hi_file = panic "hscCompileCoreExpr':ml_dyn_hi_file", +#endif + ml_hie_file = panic "hscCompileCoreExpr':ml_hie_file" + } ; let ictxt = hsc_IC hsc_env ; (binding_id, stg_expr, _, _) <- myCoreToStgExpr (hsc_logger hsc_env) (hsc_dflags hsc_env) ictxt +#if MIN_VERSION_ghc(9,3,0) + True -- for bytecode +#endif (icInteractiveModule ictxt) iNTERACTIVELoc prepd_expr @@ -269,7 +283,13 @@ captureSplicesAndDeps TypecheckHelpers{..} env k = do -- by default, so we can safely ignore them here. -- Find the linkables for the modules we need - ; let needed_mods = mkUniqSet [ moduleName mod + ; let needed_mods = mkUniqSet [ +#if MIN_VERSION_ghc(9,3,0) + mod -- We need the whole module for 9.4 because of multiple home units modules may have different unit ids +#else + moduleName mod -- On <= 9.2, just the name is enough because all unit ids will be the same +#endif + #if MIN_VERSION_ghc(9,2,0) | n <- concatMap (uniqDSetToList . bcoFreeNames) $ bc_bcos bcos #else @@ -277,32 +297,55 @@ captureSplicesAndDeps TypecheckHelpers{..} env k = do #endif , Just mod <- [nameModule_maybe n] -- Names from other modules , not (isWiredInName n) -- Exclude wired-in names - , moduleUnitId mod == uid -- Only care about stuff from the home package + , moduleUnitId mod `elem` home_unit_ids -- Only care about stuff from the home package set ] - hpt = hsc_HPT hsc_env - uid = homeUnitId_ dflags - mods_transitive = getTransitiveMods hpt needed_mods - -- Non det OK as we will put it into maps later anyway - mods_transitive_list = nonDetEltsUniqSet mods_transitive - - ; lbs <- getLinkables [toNormalizedFilePath' file | mod <- mkHomeModule + home_unit_ids = +#if MIN_VERSION_ghc(9,3,0) + map fst (hugElts $ hsc_HUG hsc_env) +#else + [homeUnitId_ dflags] +#endif + mods_transitive = getTransitiveMods hsc_env needed_mods + -- If we don't support multiple home units, ModuleNames are sufficient because all the units will be the same + mods_transitive_list = +#if MIN_VERSION_ghc(9,3,0) + mapMaybe nodeKeyToInstalledModule $ Set.toList mods_transitive +#else + map #if MIN_VERSION_ghc(9,0,0) - (hscHomeUnit hsc_env) + (mkModule (homeUnitId_ dflags)) +#else + (InstalledModule (toInstalledUnitId $ homeUnitId_ dflags)) +#endif + -- Non det OK as we will put it into maps later anyway + $ nonDetEltsUniqSet mods_transitive +#endif + +#if MIN_VERSION_ghc(9,3,0) + ; moduleLocs <- readIORef (fcModuleCache $ hsc_FC hsc_env) #else - uid + ; moduleLocs <- readIORef (hsc_FC hsc_env) #endif - <$> mods_transitive_list - , let ms = fromJust $ mgLookupModule (hsc_mod_graph hsc_env) mod - , let file = fromJust $ ml_hs_file $ ms_location ms - ] - ; let hsc_env' = hsc_env { hsc_HPT = addListToHpt hpt [(moduleName $ mi_module $ hm_iface hm, hm) | lb <- lbs, let hm = linkableHomeMod lb] } + ; lbs <- getLinkables [toNormalizedFilePath' file + | mod <- mods_transitive_list + , let ifr = fromJust $ lookupInstalledModuleEnv moduleLocs mod + file = case ifr of + InstalledFound loc _ -> + fromJust $ ml_hs_file loc + _ -> panic "hscCompileCoreExprHook: module not found" + ] + ; let hsc_env' = loadModulesHome (map linkableHomeMod lbs) hsc_env -- Essential to do this here after we load the linkables ; keep_lbls <- getLinkablesToKeep ; unload hsc_env' $ map (\(mod, time) -> LM time mod []) $ moduleEnvToList keep_lbls -#if MIN_VERSION_ghc(9,2,0) +#if MIN_VERSION_ghc(9,3,0) + {- load it -} + ; (fv_hvs, lbss, pkgs) <- loadDecls (hscInterp hsc_env') hsc_env' srcspan bcos + ; let hval = ((expectJust "hscCompileCoreExpr'" $ lookup (idName binding_id) fv_hvs), lbss, pkgs) +#elif MIN_VERSION_ghc(9,2,0) {- load it -} ; fv_hvs <- loadDecls (hscInterp hsc_env') hsc_env' srcspan bcos ; let hval = (expectJust "hscCompileCoreExpr'" $ lookup (idName binding_id) fv_hvs) @@ -314,9 +357,26 @@ captureSplicesAndDeps TypecheckHelpers{..} env k = do ; modifyIORef' var (flip extendModuleEnvList [(mi_module $ hm_iface hm, linkableHash lb) | lb <- lbs, let hm = linkableHomeMod lb]) ; return hval } +#if MIN_VERSION_ghc(9,3,0) + -- TODO: support backpack + nodeKeyToInstalledModule :: NodeKey -> Maybe InstalledModule + nodeKeyToInstalledModule (NodeKey_Module (ModNodeKeyWithUid (GWIB mod _) uid)) = Just $ mkModule uid mod + nodeKeyToInstalledModule _ = Nothing + moduleToNodeKey :: Module -> NodeKey + moduleToNodeKey mod = NodeKey_Module $ ModNodeKeyWithUid (GWIB (moduleName mod) NotBoot) (moduleUnitId mod) +#endif + -- Compute the transitive set of linkables required - getTransitiveMods hpt needed_mods = go emptyUniqSet needed_mods + getTransitiveMods hsc_env needed_mods +#if MIN_VERSION_ghc(9,3,0) + = Set.unions (Set.fromList (map moduleToNodeKey mods) : [ dep | m <- mods + , Just dep <- [Map.lookup (moduleToNodeKey m) (mgTransDeps (hsc_mod_graph hsc_env))] + ]) + where mods = nonDetEltsUniqSet needed_mods -- OK because we put them into a set immediately after +#else + = go emptyUniqSet needed_mods where + hpt = hsc_HPT hsc_env go seen new | isEmptyUniqSet new = seen | otherwise = go seen' new' @@ -325,8 +385,7 @@ captureSplicesAndDeps TypecheckHelpers{..} env k = do new' = new_deps `minusUniqSet` seen' new_deps = unionManyUniqSets [ mkUniqSet $ getDependentMods $ hm_iface mod_info | mod_info <- eltsUDFM $ udfmIntersectUFM hpt (getUniqSet new)] - - +#endif -- | Add a Hook to the DynFlags which captures and returns the -- typechecked splices before they are run. This information @@ -390,11 +449,7 @@ mkHiFileResultNoCompile session tcm = do tcGblEnv = tmrTypechecked tcm details <- makeSimpleDetails hsc_env_tmp tcGblEnv sf <- finalSafeMode (ms_hspp_opts ms) tcGblEnv -#if MIN_VERSION_ghc(8,10,0) - iface <- mkIfaceTc hsc_env_tmp sf details tcGblEnv -#else - (iface, _) <- mkIfaceTc hsc_env_tmp Nothing sf details tcGblEnv -#endif + iface <- mkIfaceTc hsc_env_tmp sf details ms tcGblEnv pure $! mkHiFileResult ms iface details (tmrRuntimeModules tcm) Nothing mkHiFileResultCompile @@ -416,12 +471,22 @@ mkHiFileResultCompile se session' tcm simplified_guts = catchErrs $ do else do -- write core file -- give variables unique OccNames - (guts, details) <- tidyProgram session simplified_guts + tidy_opts <- initTidyOpts session + (guts, details) <- tidyProgram tidy_opts simplified_guts pure (details, Just guts) #if MIN_VERSION_ghc(9,0,1) - let !partial_iface = force (mkPartialIface session details simplified_guts) + let !partial_iface = force $ mkPartialIface session details +#if MIN_VERSION_ghc(9,3,0) + ms +#endif + simplified_guts + final_iface <- mkFullIface session partial_iface Nothing +#if MIN_VERSION_ghc(9,4,2) + Nothing +#endif + #elif MIN_VERSION_ghc(8,10,0) let !partial_iface = force (mkPartialIface session details simplified_guts) final_iface <- mkFullIface session partial_iface @@ -464,8 +529,18 @@ mkHiFileResultCompile se session' tcm simplified_guts = catchErrs $ do -- Run corePrep first as we want to test the final version of the program that will -- get translated to STG/Bytecode - (prepd_binds , _) <- corePrepPgm session mod (ms_location ms) unprep_binds data_tycons - (prepd_binds', _) <- corePrepPgm session mod (ms_location ms) unprep_binds' data_tycons +#if MIN_VERSION_ghc(9,3,0) + prepd_binds +#else + (prepd_binds , _) +#endif + <- corePrepPgm session mod (ms_location ms) unprep_binds data_tycons +#if MIN_VERSION_ghc(9,3,0) + prepd_binds' +#else + (prepd_binds', _) +#endif + <- corePrepPgm session mod (ms_location ms) unprep_binds' data_tycons let binds = noUnfoldings $ (map flattenBinds . (:[])) $ prepd_binds binds' = noUnfoldings $ (map flattenBinds . (:[])) $ prepd_binds' @@ -552,9 +627,17 @@ generateObjectCode session summary guts = do withWarnings "object" $ \tweak -> do let env' = tweak (hscSetFlags (ms_hspp_opts summary) session) target = platformDefaultBackend (hsc_dflags env') - newFlags = setBackend target $ updOptLevel 0 $ setOutputFile dot_o $ hsc_dflags env' + newFlags = setBackend target $ updOptLevel 0 $ setOutputFile +#if MIN_VERSION_ghc(9,3,0) + (Just dot_o) +#else + dot_o +#endif + $ hsc_dflags env' session' = hscSetFlags newFlags session -#if MIN_VERSION_ghc(9,0,1) +#if MIN_VERSION_ghc(9,4,2) + (outputFilename, _mStub, _foreign_files, _cinfos, _stgcinfos) <- hscGenHardCode session' guts +#elif MIN_VERSION_ghc(9,0,1) (outputFilename, _mStub, _foreign_files, _cinfos) <- hscGenHardCode session' guts #else (outputFilename, _mStub, _foreign_files) <- hscGenHardCode session' guts @@ -565,7 +648,14 @@ generateObjectCode session summary guts = do summary #endif fp - compileFile session' StopLn (outputFilename, Just (As False)) + obj <- compileFile session' driverNoStop (outputFilename, Just (As False)) +#if MIN_VERSION_ghc(9,3,0) + case obj of + Nothing -> throwGhcExceptionIO $ Panic "compileFile didn't generate object code" + Just x -> pure x +#else + return obj +#endif let unlinked = DotO dot_o_fp -- Need time to be the modification time for recompilation checking t <- liftIO $ getModificationTime dot_o_fp @@ -614,10 +704,17 @@ update_pm_mod_summary :: (ModSummary -> ModSummary) -> ParsedModule -> ParsedMod update_pm_mod_summary up pm = pm{pm_mod_summary = up $ pm_mod_summary pm} +#if MIN_VERSION_ghc(9,3,0) +unDefer :: (Maybe DiagnosticReason, FileDiagnostic) -> (Bool, FileDiagnostic) +unDefer (Just (WarningWithFlag Opt_WarnDeferredTypeErrors) , fd) = (True, upgradeWarningToError fd) +unDefer (Just (WarningWithFlag Opt_WarnTypedHoles) , fd) = (True, upgradeWarningToError fd) +unDefer (Just (WarningWithFlag Opt_WarnDeferredOutOfScopeVariables), fd) = (True, upgradeWarningToError fd) +#else unDefer :: (WarnReason, FileDiagnostic) -> (Bool, FileDiagnostic) unDefer (Reason Opt_WarnDeferredTypeErrors , fd) = (True, upgradeWarningToError fd) unDefer (Reason Opt_WarnTypedHoles , fd) = (True, upgradeWarningToError fd) unDefer (Reason Opt_WarnDeferredOutOfScopeVariables, fd) = (True, upgradeWarningToError fd) +#endif unDefer ( _ , fd) = (False, fd) upgradeWarningToError :: FileDiagnostic -> FileDiagnostic @@ -626,10 +723,15 @@ upgradeWarningToError (nfp, sh, fd) = warn2err :: T.Text -> T.Text warn2err = T.intercalate ": error:" . T.splitOn ": warning:" +#if MIN_VERSION_ghc(9,3,0) +hideDiag :: DynFlags -> (Maybe DiagnosticReason, FileDiagnostic) -> (Maybe DiagnosticReason, FileDiagnostic) +hideDiag originalFlags (w@(Just (WarningWithFlag warning)), (nfp, _sh, fd)) +#else hideDiag :: DynFlags -> (WarnReason, FileDiagnostic) -> (WarnReason, FileDiagnostic) -hideDiag originalFlags (Reason warning, (nfp, _sh, fd)) +hideDiag originalFlags (w@(Reason warning), (nfp, _sh, fd)) +#endif | not (wopt warning originalFlags) - = (Reason warning, (nfp, HideDiag, fd)) + = (w, (nfp, HideDiag, fd)) hideDiag _originalFlags t = t -- | Warnings which lead to a diagnostic tag @@ -650,10 +752,15 @@ unnecessaryDeprecationWarningFlags ] -- | Add a unnecessary/deprecated tag to the required diagnostics. +#if MIN_VERSION_ghc(9,3,0) +tagDiag :: (Maybe DiagnosticReason, FileDiagnostic) -> (Maybe DiagnosticReason, FileDiagnostic) +tagDiag (w@(Just (WarningWithFlag warning)), (nfp, sh, fd)) +#else tagDiag :: (WarnReason, FileDiagnostic) -> (WarnReason, FileDiagnostic) -tagDiag (Reason warning, (nfp, sh, fd)) +tagDiag (w@(Reason warning), (nfp, sh, fd)) +#endif | Just tag <- requiresTag warning - = (Reason warning, (nfp, sh, fd { _tags = addTag tag (_tags fd) })) + = (w, (nfp, sh, fd { _tags = addTag tag (_tags fd) })) where requiresTag :: WarningFlag -> Maybe DiagnosticTag requiresTag Opt_WarnWarningsDeprecations @@ -695,7 +802,12 @@ generateHieAsts hscEnv tcm = insts = tcg_insts ts :: [ClsInst] tcs = tcg_tcs ts :: [TyCon] run ts $ - Just <$> GHC.enrichHie (fake_splice_binds `Util.unionBags` real_binds) (tmrRenamed tcm) top_ev_binds insts tcs +#if MIN_VERSION_ghc(9,3,0) + pure $ Just $ +#else + Just <$> +#endif + GHC.enrichHie (fake_splice_binds `Util.unionBags` real_binds) (tmrRenamed tcm) top_ev_binds insts tcs #else Just <$> GHC.enrichHie (fake_splice_binds `Util.unionBags` real_binds) (tmrRenamed tcm) #endif @@ -703,7 +815,7 @@ generateHieAsts hscEnv tcm = dflags = hsc_dflags hscEnv #if MIN_VERSION_ghc(9,0,0) run ts = -#if MIN_VERSION_ghc(9,2,0) +#if MIN_VERSION_ghc(9,2,0) && !MIN_VERSION_ghc(9,3,0) fmap (join . snd) . liftIO . initDs hscEnv ts #else id @@ -905,13 +1017,59 @@ loadModulesHome -> HscEnv -> HscEnv loadModulesHome mod_infos e = +#if MIN_VERSION_ghc(9,3,0) + hscUpdateHUG (\hug -> foldr addHomeModInfoToHug hug mod_infos) (e { hsc_type_env_vars = emptyKnotVars }) +#else let !new_modules = addListToHpt (hsc_HPT e) [(mod_name x, x) | x <- mod_infos] in e { hsc_HPT = new_modules - , hsc_type_env_var = Nothing } + , hsc_type_env_var = Nothing + } where mod_name = moduleName . mi_module . hm_iface +#endif -- Merge the HPTs, module graphs and FinderCaches +#if MIN_VERSION_ghc(9,3,0) +mergeEnvs :: HscEnv -> [ModuleGraphNode] -> [HomeModInfo] -> [HscEnv] -> IO HscEnv +mergeEnvs env extraNodes extraMods envs = do + let extraModSummaries = mapMaybe moduleGraphNodeModSum extraNodes + ims = map (\ms -> Compat.installedModule (toUnitId $ moduleUnit $ ms_mod ms) (moduleName (ms_mod ms))) extraModSummaries + ifrs = zipWith (\ms -> InstalledFound (ms_location ms)) extraModSummaries ims + curFinderCache = + foldl' + (\fc (im, ifr) -> Compat.extendInstalledModuleEnv fc im ifr) Compat.emptyInstalledModuleEnv + $ zip ims ifrs + -- Very important to force this as otherwise the hsc_mod_graph field is not + -- forced and ends up retaining a reference to all the old hsc_envs we have merged to get + -- this new one, which in turn leads to the EPS referencing the HPT. + module_graph_nodes = + extraNodes ++ nubOrdOn mkNodeKey (concatMap (mgModSummaries' . hsc_mod_graph) envs) + + newFinderCache <- concatFC curFinderCache (map hsc_FC envs) + liftRnf rwhnf module_graph_nodes `seq` (return $ loadModulesHome extraMods $ + let newHug = foldl' mergeHUG (hsc_HUG env) (map hsc_HUG envs) in + (hscUpdateHUG (const newHug) env){ + hsc_FC = newFinderCache, + hsc_mod_graph = mkModuleGraph module_graph_nodes + }) + + where + mergeHUG (UnitEnvGraph a) (UnitEnvGraph b) = UnitEnvGraph $ Map.unionWith mergeHUE a b + mergeHUE a b = a { homeUnitEnv_hpt = mergeUDFM (homeUnitEnv_hpt a) (homeUnitEnv_hpt b) } + mergeUDFM = plusUDFM_C combineModules + + combineModules a b + | HsSrcFile <- mi_hsc_src (hm_iface a) = a + | otherwise = b + concatFC :: FinderCacheState -> [FinderCache] -> IO FinderCache + concatFC cur xs = do + fcModules <- mapM (readIORef . fcModuleCache) xs + fcFiles <- mapM (readIORef . fcFileCache) xs + fcModules' <- newIORef (foldl' (plusInstalledModuleEnv const) cur fcModules) + fcFiles' <- newIORef (Map.unions fcFiles) + pure $ FinderCache fcModules' fcFiles' + +#else mergeEnvs :: HscEnv -> [ModSummary] -> [HomeModInfo] -> [HscEnv] -> IO HscEnv mergeEnvs env extraModSummaries extraMods envs = do prevFinderCache <- concatFC <$> mapM (readIORef . hsc_FC) envs @@ -933,11 +1091,13 @@ mergeEnvs env extraModSummaries extraMods envs = do foldl' (\fc (im, ifr) -> Compat.extendInstalledModuleEnv fc im ifr) prevFinderCache $ zip ims ifrs - liftRnf rwhnf module_graph_nodes `seq` (return $ loadModulesHome extraMods $ env{ - hsc_HPT = foldMapBy mergeUDFM emptyUDFM hsc_HPT envs, - hsc_FC = newFinderCache, - hsc_mod_graph = mkModuleGraph module_graph_nodes - }) + liftRnf rwhnf module_graph_nodes `seq` (return $ loadModulesHome extraMods $ + env{ + hsc_HPT = foldMapBy mergeUDFM emptyUDFM hsc_HPT envs, + hsc_FC = newFinderCache, + hsc_mod_graph = mkModuleGraph module_graph_nodes + }) + where mergeUDFM = plusUDFM_C combineModules combineModules a b @@ -950,6 +1110,7 @@ mergeEnvs env extraModSummaries extraMods envs = do -- To remove this, I plan to upstream the missing Monoid instance concatFC :: [FinderCache] -> FinderCache concatFC = unsafeCoerce (mconcat @(Map InstalledModule InstalledFindResult)) +#endif withBootSuffix :: HscSource -> ModLocation -> ModLocation withBootSuffix HsBootFile = addBootSuffixLocnOut @@ -978,26 +1139,45 @@ getModSummaryFromImports env fp modTime contents = do (src_idecls, ord_idecls) = partition ((== IsBoot) . ideclSource.unLoc) imps -- GHC.Prim doesn't exist physically, so don't go looking for it. - ordinary_imps = filter ((/= moduleName gHC_PRIM) . unLoc - . ideclName . unLoc) - ord_idecls + (ordinary_imps, ghc_prim_imports) + = partition ((/= moduleName gHC_PRIM) . unLoc + . ideclName . unLoc) + ord_idecls implicit_prelude = xopt LangExt.ImplicitPrelude dflags implicit_imports = mkPrelImports mod main_loc implicit_prelude imps - convImport (L _ i) = (fmap sl_fs (ideclPkgQual i) - , reLoc $ ideclName i) + convImport (L _ i) = ( +#if !MIN_VERSION_ghc (9,3,0) + fmap sl_fs +#endif + (ideclPkgQual i) + , reLoc $ ideclName i) + + msrImports = implicit_imports ++ imps + +#if MIN_VERSION_ghc (9,3,0) + rn_pkg_qual = renameRawPkgQual (hsc_unit_env env) + rn_imps = fmap (\(pk, lmn@(L _ mn)) -> (rn_pkg_qual mn pk, lmn)) + srcImports = rn_imps $ map convImport src_idecls + textualImports = rn_imps $ map convImport (implicit_imports ++ ordinary_imps) + ghc_prim_import = not (null ghc_prim_imports) +#else srcImports = map convImport src_idecls textualImports = map convImport (implicit_imports ++ ordinary_imps) +#endif - msrImports = implicit_imports ++ imps -- Force bits that might keep the string buffer and DynFlags alive unnecessarily liftIO $ evaluate $ rnf srcImports liftIO $ evaluate $ rnf textualImports +#if MIN_VERSION_ghc (9,3,0) + !src_hash <- liftIO $ fingerprintFromStringBuffer contents +#endif + modLoc <- liftIO $ if mod == mAIN_NAME -- specially in tests it's common to have lots of nameless modules -- mkHomeModLocation will map them to the same hi/hie locations @@ -1012,7 +1192,14 @@ getModSummaryFromImports env fp modTime contents = do #if MIN_VERSION_ghc(8,8,0) , ms_hie_date = Nothing #endif +#if MIN_VERSION_ghc(9,3,0) + , ms_dyn_obj_date = Nothing + , ms_ghc_prim_import = ghc_prim_import + , ms_hs_hash = src_hash + +#else , ms_hs_date = modTime +#endif , ms_hsc_src = sourceType -- The contents are used by the GetModSummary rule , ms_hspp_buf = Just contents @@ -1036,7 +1223,14 @@ getModSummaryFromImports env fp modTime contents = do put $ Util.uniq $ moduleNameFS $ moduleName ms_mod forM_ (ms_srcimps ++ ms_textual_imps) $ \(mb_p, m) -> do put $ Util.uniq $ moduleNameFS $ unLoc m +#if MIN_VERSION_ghc(9,3,0) + case mb_p of + G.NoPkgQual -> pure () + G.ThisPkg uid -> put $ getKey $ getUnique uid + G.OtherPkg uid -> put $ getKey $ getUnique uid +#else whenJust mb_p $ put . Util.uniq +#endif return $! Util.fingerprintFingerprints $ [ Util.fingerprintString fp , fingerPrintImports @@ -1130,7 +1324,12 @@ parseFileContents env customPreprocessor filename ms = do -- - filter out the .hs/.lhs source filename if we have one -- let n_hspp = normalise filename - srcs0 = nubOrd $ filter (not . (tmpDir dflags `isPrefixOf`)) +#if MIN_VERSION_ghc(9,3,0) + TempDir tmp_dir = tmpDir dflags +#else + tmp_dir = tmpDir dflags +#endif + srcs0 = nubOrd $ filter (not . (tmp_dir `isPrefixOf`)) $ filter (/= n_hspp) $ map normalise $ filter (not . isPrefixOf "<") @@ -1272,7 +1471,13 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do -- If mb_old_iface is nothing then checkOldIface will load it for us -- given that the source is unmodified (recomp_iface_reqd, mb_checked_iface) +#if MIN_VERSION_ghc(9,3,0) + <- liftIO $ checkOldIface sessionWithMsDynFlags ms mb_old_iface >>= \case + UpToDateItem x -> pure (UpToDate, Just x) + OutOfDateItem reason x -> pure (NeedsRecompile reason, x) +#else <- liftIO $ checkOldIface sessionWithMsDynFlags ms sourceMod mb_old_iface +#endif let do_regenerate _reason = withTrace "regenerate interface" $ \setTag -> do setTag "Module" $ moduleNameString $ moduleName mod @@ -1309,14 +1514,14 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do readBinCoreFile (mkUpdater $ hsc_NC session) core_file if cf_iface_hash == getModuleHash iface then return ([], Just $ mkHiFileResult ms iface details runtime_deps (Just (core_file, fingerprintToBS core_hash))) - else do_regenerate (RecompBecause "Core file out of date (doesn't match iface hash)") + else do_regenerate (recompBecause "Core file out of date (doesn't match iface hash)") | otherwise -> return ([], Just $ mkHiFileResult ms iface details runtime_deps Nothing) where handleErrs = flip catches - [Handler $ \(e :: IOException) -> do_regenerate (RecompBecause $ "Reading core file failed (" ++ show e ++ ")") + [Handler $ \(e :: IOException) -> do_regenerate (recompBecause $ "Reading core file failed (" ++ show e ++ ")") ,Handler $ \(e :: GhcException) -> case e of Signal _ -> throw e Panic _ -> throw e - _ -> do_regenerate (RecompBecause $ "Reading core file failed (" ++ show e ++ ")") + _ -> do_regenerate (recompBecause $ "Reading core file failed (" ++ show e ++ ")") ] (_, _reason) -> do_regenerate _reason @@ -1351,18 +1556,36 @@ checkLinkableDependencies get_linkable_hashes graph runtime_deps = do let out_of_date = [core_file | ((core_file, expected_hash), actual_hash) <- zip fs store_hashes, expected_hash /= actual_hash] case out_of_date of [] -> pure Nothing - _ -> pure $ Just $ - RecompBecause $ "out of date runtime dependencies: " ++ intercalate ", " (map show out_of_date) + _ -> pure $ Just $ recompBecause + $ "out of date runtime dependencies: " ++ intercalate ", " (map show out_of_date) + +recompBecause = +#if MIN_VERSION_ghc(9,3,0) + NeedsRecompile . +#endif + RecompBecause +#if MIN_VERSION_ghc(9,3,0) + . CustomReason +#endif + +#if MIN_VERSION_ghc(9,3,0) +data SourceModified = SourceModified | SourceUnmodified deriving (Eq, Ord, Show) +#endif showReason :: RecompileRequired -> String showReason UpToDate = "UpToDate" +#if MIN_VERSION_ghc(9,3,0) +showReason (NeedsRecompile MustCompile) = "MustCompile" +showReason (NeedsRecompile s) = printWithoutUniques s +#else showReason MustCompile = "MustCompile" showReason (RecompBecause s) = s +#endif mkDetailsFromIface :: HscEnv -> ModIface -> IO ModDetails mkDetailsFromIface session iface = do fixIO $ \details -> do - let hsc' = session { hsc_HPT = addToHpt (hsc_HPT session) (moduleName $ mi_module iface) (HomeModInfo iface details Nothing) } + let hsc' = hscUpdateHPT (\hpt -> addToHpt hpt (moduleName $ mi_module iface) (HomeModInfo iface details Nothing)) session initIfaceLoad hsc' (typecheckIface iface) coreFileToCgGuts :: HscEnv -> ModIface -> ModDetails -> CoreFile -> IO CgGuts @@ -1371,28 +1594,26 @@ coreFileToCgGuts session iface details core_file = do (HomeModInfo iface details Nothing) this_mod = mi_module iface types_var <- newIORef (md_types details) - let kv = Just (this_mod, types_var) - hsc_env' = session { hsc_HPT = act (hsc_HPT session) - , hsc_type_env_var = kv } + let hsc_env' = hscUpdateHPT act (session { +#if MIN_VERSION_ghc(9,3,0) + hsc_type_env_vars = knotVarsFromModuleEnv (mkModuleEnv [(this_mod, types_var)]) +#else + hsc_type_env_var = Just (this_mod, types_var) +#endif + }) core_binds <- initIfaceCheck (text "l") hsc_env' $ typecheckCoreFile this_mod types_var core_file -- Implicit binds aren't saved, so we need to regenerate them ourselves. let implicit_binds = concatMap getImplicitBinds tyCons tyCons = typeEnvTyCons (md_types details) +#if MIN_VERSION_ghc(9,3,0) + pure $ CgGuts this_mod tyCons (implicit_binds ++ core_binds) [] NoStubs [] mempty (emptyHpcInfo False) Nothing [] +#else pure $ CgGuts this_mod tyCons (implicit_binds ++ core_binds) NoStubs [] [] (emptyHpcInfo False) Nothing [] +#endif coreFileToLinkable :: LinkableType -> HscEnv -> ModSummary -> ModIface -> ModDetails -> CoreFile -> UTCTime -> IO ([FileDiagnostic], Maybe HomeModInfo) coreFileToLinkable linkableType session ms iface details core_file t = do - let act hpt = addToHpt hpt (moduleName this_mod) - (HomeModInfo iface details Nothing) - this_mod = mi_module iface - types_var <- newIORef (md_types details) - let kv = Just (this_mod, types_var) - hsc_env' = session { hsc_HPT = act (hsc_HPT session) - , hsc_type_env_var = kv } - core_binds <- initIfaceCheck (text "l") hsc_env' $ typecheckCoreFile this_mod types_var core_file - let implicit_binds = concatMap getImplicitBinds tyCons - tyCons = typeEnvTyCons (md_types details) - let cgi_guts = CgGuts this_mod tyCons (implicit_binds ++ core_binds) NoStubs [] [] (emptyHpcInfo False) Nothing [] + cgi_guts <- coreFileToCgGuts session iface details core_file (warns, lb) <- case linkableType of BCOLinkable -> generateByteCode (CoreFileTime t) session ms cgi_guts ObjectLinkable -> generateObjectCode session ms cgi_guts @@ -1405,27 +1626,55 @@ getDocsBatch :: HscEnv -> Module -- ^ a moudle where the names are in scope -> [Name] +#if MIN_VERSION_ghc(9,3,0) + -> IO [Either String (Maybe [HsDoc GhcRn], IntMap (HsDoc GhcRn))] +#else -> IO [Either String (Maybe HsDocString, IntMap HsDocString)] +#endif getDocsBatch hsc_env _mod _names = do (msgs, res) <- initTc hsc_env HsSrcFile False _mod fakeSpan $ forM _names $ \name -> case nameModule_maybe name of Nothing -> return (Left $ NameHasNoModule name) Just mod -> do - ModIface { mi_doc_hdr = mb_doc_hdr + ModIface { +#if MIN_VERSION_ghc(9,3,0) + mi_docs = Just Docs{ docs_mod_hdr = mb_doc_hdr + , docs_decls = dmap + , docs_args = amap + } +#else + mi_doc_hdr = mb_doc_hdr , mi_decl_docs = DeclDocMap dmap , mi_arg_docs = ArgDocMap amap +#endif } <- loadModuleInterface "getModuleInterface" mod +#if MIN_VERSION_ghc(9,3,0) + if isNothing mb_doc_hdr && isNullUniqMap dmap && isNullUniqMap amap +#else if isNothing mb_doc_hdr && Map.null dmap && null amap +#endif then pure (Left (NoDocsInIface mod $ compiled name)) - else pure (Right ( Map.lookup name dmap , + else pure (Right ( +#if MIN_VERSION_ghc(9,3,0) + lookupUniqMap dmap name, +#else + Map.lookup name dmap , +#endif #if !MIN_VERSION_ghc(9,2,0) IntMap.fromAscList $ Map.toAscList $ #endif +#if MIN_VERSION_ghc(9,3,0) + lookupWithDefaultUniqMap amap mempty name)) +#else Map.findWithDefault mempty name amap)) +#endif case res of - Just x -> return $ map (first $ T.unpack . printOutputable) x + Just x -> return $ map (first $ T.unpack . printOutputable) + $ x Nothing -> throwErrors -#if MIN_VERSION_ghc(9,2,0) +#if MIN_VERSION_ghc(9,3,0) + $ fmap GhcTcRnMessage msgs +#elif MIN_VERSION_ghc(9,2,0) $ Error.getErrorMessages msgs #else $ snd msgs diff --git a/ghcide/src/Development/IDE/Core/IdeConfiguration.hs b/ghcide/src/Development/IDE/Core/IdeConfiguration.hs index c3b5323548..45f6e8c3da 100644 --- a/ghcide/src/Development/IDE/Core/IdeConfiguration.hs +++ b/ghcide/src/Development/IDE/Core/IdeConfiguration.hs @@ -56,8 +56,8 @@ parseConfiguration InitializeParams {..} = clientSettings = hashed _initializationOptions parseWorkspaceFolder :: WorkspaceFolder -> NormalizedUri -parseWorkspaceFolder = - toNormalizedUri . Uri . (_uri :: WorkspaceFolder -> Text) +parseWorkspaceFolder WorkspaceFolder{_uri} = + toNormalizedUri (Uri _uri) modifyWorkspaceFolders :: IdeState -> (HashSet NormalizedUri -> HashSet NormalizedUri) -> IO () diff --git a/ghcide/src/Development/IDE/Core/Preprocessor.hs b/ghcide/src/Development/IDE/Core/Preprocessor.hs index 678471c9c1..08a41b0ed4 100644 --- a/ghcide/src/Development/IDE/Core/Preprocessor.hs +++ b/ghcide/src/Development/IDE/Core/Preprocessor.hs @@ -1,5 +1,6 @@ -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 +{-# LANGUAGE CPP #-} module Development.IDE.Core.Preprocessor ( preprocessor @@ -28,6 +29,10 @@ import Development.IDE.Types.Location import qualified GHC.LanguageExtensions as LangExt import System.FilePath import System.IO.Extra +#if MIN_VERSION_ghc(9,3,0) +import GHC.Utils.Logger (LogFlags(..)) +import GHC.Utils.Outputable (renderWithContext) +#endif -- | Given a file and some contents, apply any necessary preprocessors, -- e.g. unlit/cpp. Return the resulting buffer and the DynFlags it implies. @@ -76,10 +81,15 @@ preprocessor env0 filename mbContents = do where logAction :: IORef [CPPLog] -> LogActionCompat logAction cppLogs dflags _reason severity srcSpan _style msg = do +#if MIN_VERSION_ghc(9,3,0) + let log = CPPLog (fromMaybe SevWarning severity) srcSpan $ T.pack $ renderWithContext (log_default_user_context dflags) msg +#else let log = CPPLog severity srcSpan $ T.pack $ showSDoc dflags msg +#endif modifyIORef cppLogs (log :) + data CPPLog = CPPLog Severity SrcSpan Text deriving (Show) @@ -133,7 +143,11 @@ parsePragmasIntoDynFlags -> Util.StringBuffer -> IO (Either [FileDiagnostic] ([String], DynFlags)) parsePragmasIntoDynFlags env fp contents = catchSrcErrors dflags0 "pragmas" $ do +#if MIN_VERSION_ghc(9,3,0) + let (_warns,opts) = getOptions (initParserOpts dflags0) contents fp +#else let opts = getOptions dflags0 contents fp +#endif -- Force bits that might keep the dflags and stringBuffer alive unnecessarily evaluate $ rnf opts diff --git a/ghcide/src/Development/IDE/Core/Rules.hs b/ghcide/src/Development/IDE/Core/Rules.hs index dfba6a32e7..07cf081c21 100644 --- a/ghcide/src/Development/IDE/Core/Rules.hs +++ b/ghcide/src/Development/IDE/Core/Rules.hs @@ -158,6 +158,10 @@ import qualified Development.IDE.Types.Shake as Shake import Development.IDE.GHC.CoreFile import Data.Time.Clock.POSIX (posixSecondsToUTCTime, utcTimeToPOSIXSeconds) import Control.Monad.IO.Unlift +#if MIN_VERSION_ghc(9,3,0) +import GHC.Unit.Module.Graph +import GHC.Unit.Env +#endif data Log = LogShake Shake.Log @@ -664,7 +668,7 @@ typeCheckRule recorder = define (cmapWithPrio LogShake recorder) $ \TypeCheck fi -- very expensive. when (foi == NotFOI) $ logWith recorder Logger.Warning $ LogTypecheckedFOI file - typeCheckRuleDefinition hsc pm + typeCheckRuleDefinition hsc pm file knownFilesRule :: Recorder (WithPriority Log) -> Rules () knownFilesRule recorder = defineEarlyCutOffNoFile (cmapWithPrio LogShake recorder) $ \GetKnownTargets -> do @@ -685,8 +689,9 @@ getModuleGraphRule recorder = defineNoFile (cmapWithPrio LogShake recorder) $ \G typeCheckRuleDefinition :: HscEnv -> ParsedModule + -> NormalizedFilePath -> Action (IdeResult TcModuleResult) -typeCheckRuleDefinition hsc pm = do +typeCheckRuleDefinition hsc pm file = do setPriority priorityTypeCheck IdeOptions { optDefer = defer } <- getIdeOptions @@ -772,9 +777,21 @@ ghcSessionDepsDefinition fullModSummary GhcSessionDepsConfig{..} env file = do depSessions <- map hscEnv <$> uses_ (GhcSessionDeps_ fullModSummary) deps ifaces <- uses_ GetModIface deps - - let inLoadOrder = map (\HiFileResult{..} -> HomeModInfo hirModIface hirModDetails Nothing) ifaces - session' <- liftIO $ mergeEnvs hsc mss inLoadOrder depSessions + let inLoadOrder = map (\HiFileResult{..} -> HomeModInfo hirModIface hirModDetails Nothing) ifaces +#if MIN_VERSION_ghc(9,3,0) + mss_imports <- uses_ GetLocatedImports (file : deps) + final_deps <- forM mss_imports $ \imports -> do + let fs = mapMaybe (fmap artifactFilePath . snd) imports + dep_mss <- map msrModSummary <$> if fullModSummary + then uses_ GetModSummary fs + else uses_ GetModSummaryWithoutTimestamps fs + return (map (NodeKey_Module . msKey) dep_mss) + ms <- msrModSummary <$> use_ GetModSummary file + let moduleNodes = zipWith ModuleNode final_deps (ms : mss) +#else + let moduleNodes = mss +#endif + session' <- liftIO $ mergeEnvs hsc moduleNodes inLoadOrder depSessions Just <$> liftIO (newHscEnvEqWithImportPaths (envImportPaths env) session' []) @@ -880,8 +897,12 @@ getModSummaryRule displayTHWarning recorder = do when (uses_th_qq $ msrModSummary res) $ do DisplayTHWarning act <- getIdeGlobalAction liftIO act +#if MIN_VERSION_ghc(9,3,0) + let bufFingerPrint = ms_hs_hash (msrModSummary res) +#else bufFingerPrint <- liftIO $ fingerprintFromStringBuffer $ fromJust $ ms_hspp_buf $ msrModSummary res +#endif let fingerPrint = Util.fingerprintFingerprints [ msrFingerprint res, bufFingerPrint ] return ( Just (fingerprintToBS fingerPrint) , ([], Just res)) @@ -892,7 +913,9 @@ getModSummaryRule displayTHWarning recorder = do case ms of Just res@ModSummaryResult{..} -> do let ms = msrModSummary { +#if !MIN_VERSION_ghc(9,3,0) ms_hs_date = error "use GetModSummary instead of GetModSummaryWithoutTimestamps", +#endif ms_hspp_buf = error "use GetModSummary instead of GetModSummaryWithoutTimestamps" } fp = fingerprintToBS msrFingerprint @@ -973,7 +996,7 @@ regenerateHiFile sess f ms compNeeded = do Just pm -> do -- Invoke typechecking directly to update it without incurring a dependency -- on the parsed module and the typecheck rules - (diags', mtmr) <- typeCheckRuleDefinition hsc pm + (diags', mtmr) <- typeCheckRuleDefinition hsc pm f case mtmr of Nothing -> pure (diags', Nothing) Just tmr -> do diff --git a/ghcide/src/Development/IDE/Core/Shake.hs b/ghcide/src/Development/IDE/Core/Shake.hs index 211c5468a2..0b31b83ac7 100644 --- a/ghcide/src/Development/IDE/Core/Shake.hs +++ b/ghcide/src/Development/IDE/Core/Shake.hs @@ -10,6 +10,7 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecursiveDo #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE CPP #-} -- | A Shake implementation of the compiler service. -- @@ -129,8 +130,11 @@ import Development.IDE.GHC.Compat (NameCache, NameCacheUpdater (..), initNameCache, knownKeyNames, - mkSplitUniqSupply, - upNameCache) +#if !MIN_VERSION_ghc(9,3,0) + upNameCache, +#endif + mkSplitUniqSupply + ) import Development.IDE.GHC.Orphans () import Development.IDE.Graph hiding (ShakeValue) import qualified Development.IDE.Graph as Shake @@ -262,7 +266,11 @@ data ShakeExtras = ShakeExtras -> String -> [DelayedAction ()] -> IO () +#if MIN_VERSION_ghc(9,3,0) + ,ideNc :: NameCache +#else ,ideNc :: IORef NameCache +#endif -- | A mapping of module name to known target (or candidate targets, if missing) ,knownTargetsVar :: TVar (Hashed KnownTargets) -- | A mapping of exported identifiers for local modules. Updated on kick @@ -572,8 +580,12 @@ shakeOpen recorder lspEnv defaultConfig idePlugins logger debouncer let log :: Logger.Priority -> Log -> IO () log = logWith recorder +#if MIN_VERSION_ghc(9,3,0) + ideNc <- initNameCache 'r' knownKeyNames +#else us <- mkSplitUniqSupply 'r' ideNc <- newIORef (initNameCache us knownKeyNames) +#endif shakeExtras <- do globals <- newTVarIO HMap.empty state <- STM.newIO @@ -959,8 +971,14 @@ runIdeAction _herald s i = runReaderT (runIdeActionT i) s askShake :: IdeAction ShakeExtras askShake = ask + +#if MIN_VERSION_ghc(9,3,0) +mkUpdater :: NameCache -> NameCacheUpdater +mkUpdater = id +#else mkUpdater :: IORef NameCache -> NameCacheUpdater mkUpdater ref = NCU (upNameCache ref) +#endif -- | A (maybe) stale result now, and an up to date one later data FastResult a = FastResult { stale :: Maybe (a,PositionMapping), uptoDate :: IO (Maybe a) } diff --git a/ghcide/src/Development/IDE/GHC/CPP.hs b/ghcide/src/Development/IDE/GHC/CPP.hs index 788e93ea8d..fc18450292 100644 --- a/ghcide/src/Development/IDE/GHC/CPP.hs +++ b/ghcide/src/Development/IDE/GHC/CPP.hs @@ -34,6 +34,9 @@ import ToolSettings import DynFlags #endif #endif +#if MIN_VERSION_ghc(9,3,0) +import qualified GHC.Driver.Pipeline.Execute as Pipeline +#endif addOptP :: String -> DynFlags -> DynFlags #if MIN_VERSION_ghc (8,10,0) diff --git a/ghcide/src/Development/IDE/GHC/Compat.hs b/ghcide/src/Development/IDE/GHC/Compat.hs index 43cb524256..157ddcde4c 100644 --- a/ghcide/src/Development/IDE/GHC/Compat.hs +++ b/ghcide/src/Development/IDE/GHC/Compat.hs @@ -9,12 +9,19 @@ -- | Attempt at hiding the GHC version differences we can. module Development.IDE.GHC.Compat( - NameCacheUpdater(..), + mkHomeModLocation, hPutStringBuffer, addIncludePathsQuote, getModuleHash, setUpTypedHoles, + NameCacheUpdater(..), +#if MIN_VERSION_ghc(9,3,0) + getMessages, + diagnosticMessage, + nameEnvElts, +#else upNameCache, +#endif disableWarningsAsErrors, reLoc, reLocA, @@ -27,8 +34,10 @@ module Development.IDE.GHC.Compat( #endif #if MIN_VERSION_ghc(9,2,0) +#if !MIN_VERSION_ghc(9,3,0) extendModSummaryNoDeps, emsModSummary, +#endif myCoreToStgExpr, #endif @@ -87,7 +96,11 @@ module Development.IDE.GHC.Compat( icInteractiveModule, HomePackageTable, lookupHpt, +#if MIN_VERSION_ghc(9,3,0) + Dependencies(dep_direct_mods), +#else Dependencies(dep_mods), +#endif bcoFreeNames, ModIfaceAnnotation, pattern Annotation, @@ -116,7 +129,7 @@ module Development.IDE.GHC.Compat( ) where import Data.Bifunctor -import Development.IDE.GHC.Compat.Core +import Development.IDE.GHC.Compat.Core hiding (moduleUnitId) import Development.IDE.GHC.Compat.Env import Development.IDE.GHC.Compat.Iface import Development.IDE.GHC.Compat.Logger @@ -147,7 +160,11 @@ import GHC.Linker.Types (isObjectLinkable) import GHC.Runtime.Context (icInteractiveModule) import GHC.Unit.Home.ModInfo (HomePackageTable, lookupHpt) -import GHC.Unit.Module.Deps (Dependencies (dep_mods)) +#if MIN_VERSION_ghc(9,3,0) +import GHC.Unit.Module.Deps (Dependencies(dep_direct_mods)) +#else +import GHC.Unit.Module.Deps (Dependencies(dep_mods)) +#endif #else import GHC.CoreToByteCode (coreExprToBCOs) import GHC.Driver.Types (Dependencies (dep_mods), @@ -255,16 +272,37 @@ import GHC.Types.CostCentre import GHC.Types.IPE #endif +#if MIN_VERSION_ghc(9,3,0) +import GHC.Types.Error +import GHC.Driver.Config.Stg.Pipeline +#endif + type ModIfaceAnnotation = Annotation +#if MIN_VERSION_ghc(9,3,0) +nameEnvElts :: NameEnv a -> [a] +nameEnvElts = nonDetNameEnvElts +#endif + #if MIN_VERSION_ghc(9,2,0) myCoreToStgExpr :: Logger -> DynFlags -> InteractiveContext +#if MIN_VERSION_ghc(9,3,0) + -> Bool +#endif -> Module -> ModLocation -> CoreExpr -> IO ( Id - , [StgTopBinding] +#if MIN_VERSION_ghc(9,3,0) + ,[CgStgTopBinding] -- output program +#else + ,[StgTopBinding] -- output program +#endif , InfoTableProvMap , CollectedCCs ) -myCoreToStgExpr logger dflags ictxt this_mod ml prepd_expr = do +myCoreToStgExpr logger dflags ictxt +#if MIN_VERSION_ghc(9,3,0) + for_bytecode +#endif + this_mod ml prepd_expr = do {- Create a temporary binding (just because myCoreToStg needs a binding for the stg2stg step) -} let bco_tmp_id = mkSysLocal (fsLit "BCO_toplevel") @@ -275,24 +313,46 @@ myCoreToStgExpr logger dflags ictxt this_mod ml prepd_expr = do myCoreToStg logger dflags ictxt +#if MIN_VERSION_ghc(9,3,0) + for_bytecode +#endif this_mod ml [NonRec bco_tmp_id prepd_expr] return (bco_tmp_id, stg_binds, prov_map, collected_ccs) myCoreToStg :: Logger -> DynFlags -> InteractiveContext +#if MIN_VERSION_ghc(9,3,0) + -> Bool +#endif -> Module -> ModLocation -> CoreProgram +#if MIN_VERSION_ghc(9,3,0) + -> IO ( [CgStgTopBinding] -- output program +#else -> IO ( [StgTopBinding] -- output program +#endif , InfoTableProvMap , CollectedCCs ) -- CAF cost centre info (declared and used) -myCoreToStg logger dflags ictxt this_mod ml prepd_binds = do +myCoreToStg logger dflags ictxt +#if MIN_VERSION_ghc(9,3,0) + for_bytecode +#endif + this_mod ml prepd_binds = do let (stg_binds, denv, cost_centre_info) = {-# SCC "Core2Stg" #-} coreToStg dflags this_mod ml prepd_binds +#if MIN_VERSION_ghc(9,4,2) + (stg_binds2,_) +#else stg_binds2 +#endif <- {-# SCC "Stg2Stg" #-} +#if MIN_VERSION_ghc(9,3,0) + stg2stg logger ictxt (initStgPipelineOpts dflags for_bytecode) this_mod stg_binds +#else stg2stg logger dflags ictxt this_mod stg_binds +#endif return (stg_binds2, denv, cost_centre_info) #endif @@ -307,7 +367,9 @@ reLocA = id #endif getDependentMods :: ModIface -> [ModuleName] -#if MIN_VERSION_ghc(9,0,0) +#if MIN_VERSION_ghc(9,3,0) +getDependentMods = map (gwib_mod . snd) . S.toList . dep_direct_mods . mi_deps +#elif MIN_VERSION_ghc(9,0,0) getDependentMods = map gwib_mod . dep_mods . mi_deps #else getDependentMods = map fst . dep_mods . mi_deps @@ -333,9 +395,15 @@ hPutStringBuffer hdl (StringBuffer buf len cur) #if MIN_VERSION_ghc(9,2,0) type ErrMsg = MsgEnvelope DecoratedSDoc #endif +#if MIN_VERSION_ghc(9,3,0) +type WarnMsg = MsgEnvelope DecoratedSDoc +#endif getMessages' :: PState -> DynFlags -> (Bag WarnMsg, Bag ErrMsg) getMessages' pst dflags = +#if MIN_VERSION_ghc(9,3,0) + bimap (fmap (fmap diagnosticMessage) . getMessages) (fmap (fmap diagnosticMessage) . getMessages) $ getPsMessages pst +#else #if MIN_VERSION_ghc(9,2,0) bimap (fmap pprWarning) (fmap pprError) $ #endif @@ -343,11 +411,16 @@ getMessages' pst dflags = #if !MIN_VERSION_ghc(9,2,0) dflags #endif +#endif #if MIN_VERSION_ghc(9,2,0) pattern PFailedWithErrorMessages :: forall a b. (b -> Bag (MsgEnvelope DecoratedSDoc)) -> ParseResult a pattern PFailedWithErrorMessages msgs +#if MIN_VERSION_ghc(9,3,0) + <- PFailed (const . fmap (fmap diagnosticMessage) . getMessages . getPsErrorMessages -> msgs) +#else <- PFailed (const . fmap pprError . getErrorMessages -> msgs) +#endif #elif MIN_VERSION_ghc(8,10,0) pattern PFailedWithErrorMessages :: (DynFlags -> ErrorMessages) -> ParseResult a pattern PFailedWithErrorMessages msgs @@ -360,7 +433,7 @@ pattern PFailedWithErrorMessages msgs mkPlainErrMsgIfPFailed (PFailed _ pst err) = Just (\dflags -> mkPlainErrMsg dflags pst err) mkPlainErrMsgIfPFailed _ = Nothing #endif -{-# COMPLETE PFailedWithErrorMessages #-} +{-# COMPLETE POk, PFailedWithErrorMessages #-} supportsHieFiles :: Bool supportsHieFiles = True @@ -368,7 +441,9 @@ supportsHieFiles = True hieExportNames :: HieFile -> [(SrcSpan, Name)] hieExportNames = nameListFromAvails . hie_exports - +#if MIN_VERSION_ghc(9,3,0) +type NameCacheUpdater = NameCache +#else upNameCache :: IORef NameCache -> (NameCache -> (NameCache, c)) -> IO c #if MIN_VERSION_ghc(8,8,0) upNameCache = updNameCache @@ -376,6 +451,7 @@ upNameCache = updNameCache upNameCache ref upd_fn = atomicModifyIORef' ref upd_fn #endif +#endif #if !MIN_VERSION_ghc(9,0,1) type RefMap a = Map.Map Identifier [(Span, IdentifierDetails a)] @@ -535,13 +611,16 @@ data GhcVersion | GHC810 | GHC90 | GHC92 + | GHC94 deriving (Eq, Ord, Show) ghcVersionStr :: String ghcVersionStr = VERSION_ghc ghcVersion :: GhcVersion -#if MIN_VERSION_GLASGOW_HASKELL(9,2,0,0) +#if MIN_VERSION_GLASGOW_HASKELL(9,4,0,0) +ghcVersion = GHC94 +#elif MIN_VERSION_GLASGOW_HASKELL(9,2,0,0) ghcVersion = GHC92 #elif MIN_VERSION_GLASGOW_HASKELL(9,0,0,0) ghcVersion = GHC90 diff --git a/ghcide/src/Development/IDE/GHC/Compat/Core.hs b/ghcide/src/Development/IDE/GHC/Compat/Core.hs index 222be572e6..173759a5f8 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Core.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Core.hs @@ -2,6 +2,7 @@ {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE ViewPatterns #-} -- TODO: remove {-# OPTIONS -Wno-dodgy-imports -Wno-unused-imports #-} @@ -61,7 +62,9 @@ module Development.IDE.GHC.Compat.Core ( pattern ExposePackage, parseDynamicFlagsCmdLine, parseDynamicFilePragma, +#if !MIN_VERSION_ghc(9,3,0) WarnReason(..), +#endif wWarningFlags, updOptLevel, -- slightly unsafe @@ -84,7 +87,9 @@ module Development.IDE.GHC.Compat.Core ( HscSource(..), WhereFrom(..), loadInterface, +#if !MIN_VERSION_ghc(9,3,0) SourceModified(..), +#endif loadModuleInterface, RecompileRequired(..), #if MIN_VERSION_ghc(8,10,0) @@ -188,12 +193,17 @@ module Development.IDE.GHC.Compat.Core ( hscInteractive, hscSimplify, hscTypecheckRename, - makeSimpleDetails, + Development.IDE.GHC.Compat.Core.makeSimpleDetails, -- * Typecheck utils Development.IDE.GHC.Compat.Core.tcSplitForAllTyVars, Development.IDE.GHC.Compat.Core.tcSplitForAllTyVarBinder_maybe, typecheckIface, - mkIfaceTc, + Development.IDE.GHC.Compat.Core.mkIfaceTc, + Development.IDE.GHC.Compat.Core.mkBootModDetailsTc, + Development.IDE.GHC.Compat.Core.initTidyOpts, + hscUpdateHPT, + driverNoStop, + tidyProgram, ImportedModsVal(..), importedByUser, GHC.TypecheckedSource, @@ -297,7 +307,6 @@ module Development.IDE.GHC.Compat.Core ( Warn(..), -- * ModLocation GHC.ModLocation, - pattern ModLocation, Module.ml_hs_file, Module.ml_obj_file, Module.ml_hi_file, @@ -349,7 +358,6 @@ module Development.IDE.GHC.Compat.Core ( module GHC.HsToCore.Expr, module GHC.HsToCore.Monad, - module GHC.Iface.Tidy, module GHC.Iface.Syntax, #if MIN_VERSION_ghc(9,2,0) @@ -430,7 +438,6 @@ module Development.IDE.GHC.Compat.Core ( module TcRnTypes, module TcRnDriver, module TcRnMonad, - module TidyPgm, module TyCon, module TysPrim, module TysWiredIn, @@ -466,11 +473,46 @@ module Development.IDE.GHC.Compat.Core ( module ExtractDocs, module Parser, module Lexer, +#endif +#if MIN_VERSION_ghc(9,3,0) + CompileReason(..), + hsc_type_env_vars, + hscUpdateHUG, hscUpdateHPT, hsc_HUG, + GhcMessage(..), + getKey, + module GHC.Driver.Env.KnotVars, + module GHC.Iface.Recomp, + module GHC.Linker.Types, + module GHC.Unit.Module.Graph, + module GHC.Types.Unique.Map, + module GHC.Utils.TmpFs, + module GHC.Utils.Panic, + module GHC.Unit.Finder.Types, + module GHC.Unit.Env, + module GHC.Driver.Phases, #endif ) where import qualified GHC +#if MIN_VERSION_ghc(9,3,0) +import GHC.Iface.Recomp (CompileReason(..)) +import GHC.Driver.Env.Types (hsc_type_env_vars) +import GHC.Driver.Env (hscUpdateHUG, hscUpdateHPT, hsc_HUG) +import GHC.Driver.Env.KnotVars +import GHC.Iface.Recomp +import GHC.Linker.Types +import GHC.Unit.Module.Graph +import GHC.Driver.Errors.Types +import GHC.Types.Unique.Map +import GHC.Types.Unique +import GHC.Utils.TmpFs +import GHC.Utils.Panic +import GHC.Unit.Finder.Types +import GHC.Unit.Env +import GHC.Driver.Phases +#endif + #if MIN_VERSION_ghc(9,0,0) import GHC.Builtin.Names hiding (Unique, printName) import GHC.Builtin.Types @@ -484,6 +526,10 @@ import qualified GHC.Core.DataCon as DataCon import GHC.Core.FamInstEnv hiding (pprFamInst) import GHC.Core.InstEnv import GHC.Types.Unique.FM +#if MIN_VERSION_ghc(9,3,0) +import qualified GHC.Driver.Config.Tidy as GHC +import qualified GHC.Data.Strict as Strict +#endif #if MIN_VERSION_ghc(9,2,0) import GHC.Data.Bag import GHC.Core.Multiplicity (scaledThing) @@ -505,13 +551,13 @@ import GHC.Core.Utils #if MIN_VERSION_ghc(9,2,0) import GHC.Driver.Env #else -import GHC.Driver.Finder +import GHC.Driver.Finder hiding (mkHomeModLocation) import GHC.Driver.Types import GHC.Driver.Ways #endif import GHC.Driver.CmdLine (Warn (..)) import GHC.Driver.Hooks -import GHC.Driver.Main +import GHC.Driver.Main as GHC import GHC.Driver.Monad import GHC.Driver.Phases import GHC.Driver.Pipeline @@ -537,11 +583,11 @@ import GHC.HsToCore.Docs import GHC.HsToCore.Expr import GHC.HsToCore.Monad import GHC.Iface.Load -import GHC.Iface.Make (mkFullIface, mkIfaceTc, - mkPartialIface) +import GHC.Iface.Make (mkFullIface, mkPartialIface) +import GHC.Iface.Make as GHC import GHC.Iface.Recomp import GHC.Iface.Syntax -import GHC.Iface.Tidy +import GHC.Iface.Tidy as GHC import GHC.IfaceToCore import GHC.Parser import GHC.Parser.Header hiding (getImports) @@ -588,7 +634,10 @@ import qualified GHC.Types.Name.Reader as RdrName #if MIN_VERSION_ghc(9,2,0) import GHC.Types.Name.Set import GHC.Types.SourceFile (HscSource (..), - SourceModified (..)) +#if !MIN_VERSION_ghc(9,3,0) + SourceModified(..) +#endif + ) import GHC.Types.SourceText import GHC.Types.Target (Target (..), TargetId (..)) import GHC.Types.TyThing @@ -604,7 +653,7 @@ import GHC.Types.Unique.Supply import GHC.Types.Var (Var (varName), setTyVarUnique, setVarUnique) #if MIN_VERSION_ghc(9,2,0) -import GHC.Unit.Finder +import GHC.Unit.Finder hiding (mkHomeModLocation) import GHC.Unit.Home.ModInfo #endif import GHC.Unit.Info (PackageName (..)) @@ -644,7 +693,7 @@ import ErrUtils hiding (logInfo, mkWarnMsg) import ExtractDocs import FamInst import FamInstEnv -import Finder +import Finder hiding (mkHomeModLocation) #if MIN_VERSION_ghc(8,10,0) import GHC.Hs hiding (HsLet, LetStmt) #endif @@ -652,7 +701,7 @@ import qualified GHCi import GhcMonad import HeaderInfo hiding (getImports) import Hooks -import HscMain +import HscMain as GHC import HscTypes #if !MIN_VERSION_ghc(8,10,0) -- Syntax imports @@ -674,7 +723,7 @@ import InstEnv import Lexer hiding (getSrcLoc) import qualified Linker import LoadIface -import MkIface +import MkIface as GHC import Module hiding (ModLocation (..), UnitId, addBootSuffixLocnOut, moduleUnitId) @@ -716,7 +765,7 @@ import TcRnMonad hiding (Applicative (..), IORef, import TcRnTypes import TcType hiding (mkVisFunTys) import qualified TcType -import TidyPgm +import TidyPgm as GHC import qualified TyCoRep import TyCon import Type hiding (mkVisFunTys) @@ -750,14 +799,48 @@ import System.FilePath #if MIN_VERSION_ghc(9,2,0) import Language.Haskell.Syntax hiding (FunDep) #endif +#if MIN_VERSION_ghc(9,3,0) +import GHC.Driver.Env as GHCi +#endif + +import Data.Foldable (toList) + +#if MIN_VERSION_ghc(9,3,0) +import qualified GHC.Unit.Finder as GHC +import qualified GHC.Driver.Config.Finder as GHC +#elif MIN_VERSION_ghc(9,2,0) +import qualified GHC.Unit.Finder as GHC +#elif MIN_VERSION_ghc(9,0,0) +import qualified GHC.Driver.Finder as GHC +#else +import qualified Finder as GHC +#endif + + +mkHomeModLocation :: DynFlags -> ModuleName -> FilePath -> IO Module.ModLocation +#if MIN_VERSION_ghc(9,3,0) +mkHomeModLocation df mn f = pure $ GHC.mkHomeModLocation (GHC.initFinderOpts df) mn f +#else +mkHomeModLocation = GHC.mkHomeModLocation +#endif + #if !MIN_VERSION_ghc(9,0,0) type BufSpan = () type BufPos = () #endif +#if MIN_VERSION_ghc(9,3,0) pattern RealSrcSpan :: SrcLoc.RealSrcSpan -> Maybe BufSpan -> SrcLoc.SrcSpan -#if MIN_VERSION_ghc(9,0,0) +#else +pattern RealSrcSpan :: SrcLoc.RealSrcSpan -> Maybe BufSpan -> SrcLoc.SrcSpan +#endif + +#if MIN_VERSION_ghc(9,3,0) +pattern RealSrcSpan x y <- SrcLoc.RealSrcSpan x ((\case Strict.Nothing -> Nothing; Strict.Just a -> Just a) -> y) where + RealSrcSpan x y = SrcLoc.RealSrcSpan x (case y of Nothing -> Strict.Nothing; Just a -> Strict.Just a) + +#elif MIN_VERSION_ghc(9,0,0) pattern RealSrcSpan x y = SrcLoc.RealSrcSpan x y #else pattern RealSrcSpan x y <- ((,Nothing) -> (SrcLoc.RealSrcSpan x, y)) where @@ -765,7 +848,11 @@ pattern RealSrcSpan x y <- ((,Nothing) -> (SrcLoc.RealSrcSpan x, y)) where #endif {-# COMPLETE RealSrcSpan, UnhelpfulSpan #-} +#if MIN_VERSION_ghc(9,3,0) +pattern RealSrcLoc :: SrcLoc.RealSrcLoc -> Strict.Maybe BufPos-> SrcLoc.SrcLoc +#else pattern RealSrcLoc :: SrcLoc.RealSrcLoc -> Maybe BufPos-> SrcLoc.SrcLoc +#endif #if MIN_VERSION_ghc(9,0,0) pattern RealSrcLoc x y = SrcLoc.RealSrcLoc x y #else @@ -936,14 +1023,6 @@ tcSplitForAllTyVarBinder_maybe = tcSplitForAllTy_maybe #endif -pattern ModLocation :: Maybe FilePath -> FilePath -> FilePath -> GHC.ModLocation -#if MIN_VERSION_ghc(8,8,0) -pattern ModLocation a b c <- - GHC.ModLocation a b c _ where ModLocation a b c = GHC.ModLocation a b c "" -#else -pattern ModLocation a b c <- - GHC.ModLocation a b c where ModLocation a b c = GHC.ModLocation a b c -#endif #if !MIN_VERSION_ghc(8,10,0) noExtField :: GHC.NoExt @@ -1015,6 +1094,7 @@ unload hsc_env linkables = #endif hsc_env linkables +#if !MIN_VERSION_ghc(9,3,0) setOutputFile :: FilePath -> DynFlags -> DynFlags setOutputFile f d = d { #if MIN_VERSION_ghc(9,2,0) @@ -1023,6 +1103,7 @@ setOutputFile f d = d { outputFile = Just f #endif } +#endif isSubspanOfA :: LocatedAn la a -> LocatedAn lb b -> Bool #if MIN_VERSION_ghc(9,2,0) @@ -1072,7 +1153,7 @@ pattern GRE :: Name -> Parent -> Bool -> [ImportSpec] -> RdrName.GlobalRdrElt #if MIN_VERSION_ghc(9,2,0) pattern GRE{gre_name, gre_par, gre_lcl, gre_imp} <- RdrName.GRE {gre_name = (greNamePrintableName -> gre_name) - ,gre_par, gre_lcl, gre_imp} + ,gre_par, gre_lcl, gre_imp = (toList -> gre_imp)} #else pattern GRE{gre_name, gre_par, gre_lcl, gre_imp} = RdrName.GRE{..} #endif @@ -1091,3 +1172,55 @@ pattern LetStmt xlet localBinds <- GHC.LetStmt xlet (SrcLoc.unLoc -> localBinds) rationalFromFractionalLit :: FractionalLit -> Rational rationalFromFractionalLit = fl_value #endif + +makeSimpleDetails :: HscEnv -> TcGblEnv -> IO ModDetails +makeSimpleDetails hsc_env = + GHC.makeSimpleDetails +#if MIN_VERSION_ghc(9,3,0) + (hsc_logger hsc_env) +#else + hsc_env +#endif + +mkIfaceTc hsc_env sf details ms tcGblEnv = +#if MIN_VERSION_ghc(8,10,0) + GHC.mkIfaceTc hsc_env sf details +#if MIN_VERSION_ghc(9,3,0) + ms +#endif + tcGblEnv +#else + fst <$> GHC.mkIfaceTc hsc_env Nothing sf details tcGblEnv +#endif + +mkBootModDetailsTc :: HscEnv -> TcGblEnv -> IO ModDetails +mkBootModDetailsTc session = GHC.mkBootModDetailsTc +#if MIN_VERSION_ghc(9,3,0) + (hsc_logger session) +#else + session +#endif + +#if !MIN_VERSION_ghc(9,3,0) +type TidyOpts = HscEnv +#endif + +initTidyOpts :: HscEnv -> IO TidyOpts +initTidyOpts = +#if MIN_VERSION_ghc(9,3,0) + GHC.initTidyOpts +#else + pure +#endif + +driverNoStop = +#if MIN_VERSION_ghc(9,3,0) + NoStop +#else + StopLn +#endif + +#if !MIN_VERSION_ghc(9,3,0) +hscUpdateHPT :: (HomePackageTable -> HomePackageTable) -> HscEnv -> HscEnv +hscUpdateHPT k session = session { hsc_HPT = k (hsc_HPT session) } +#endif diff --git a/ghcide/src/Development/IDE/GHC/Compat/Env.hs b/ghcide/src/Development/IDE/GHC/Compat/Env.hs index 7662587898..0909e78366 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Env.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Env.hs @@ -3,7 +3,14 @@ -- | Compat module for the main Driver types, such as 'HscEnv', -- 'UnitEnv' and some DynFlags compat functions. module Development.IDE.GHC.Compat.Env ( - Env.HscEnv(hsc_FC, hsc_NC, hsc_IC, hsc_mod_graph, hsc_HPT, hsc_type_env_var), + Env.HscEnv(hsc_FC, hsc_NC, hsc_IC, hsc_mod_graph +#if MIN_VERSION_ghc(9,3,0) + , hsc_type_env_vars +#else + , hsc_type_env_var +#endif + ), + Env.hsc_HPT, InteractiveContext(..), setInteractivePrintName, setInteractiveDynFlags, @@ -51,7 +58,11 @@ import GHC (setInteractiveDynFlags) #if MIN_VERSION_ghc(9,0,0) #if MIN_VERSION_ghc(9,2,0) import GHC.Driver.Backend as Backend +#if MIN_VERSION_ghc(9,3,0) +import GHC.Driver.Env (HscEnv) +#else import GHC.Driver.Env (HscEnv, hsc_EPS) +#endif import qualified GHC.Driver.Env as Env import qualified GHC.Driver.Session as Session import GHC.Platform.Ways hiding (hostFullWays) @@ -80,6 +91,11 @@ import HscTypes as Env import Module #endif +#if MIN_VERSION_ghc(9,3,0) +hsc_EPS :: HscEnv -> UnitEnv +hsc_EPS = hsc_unit_env +#endif + #if MIN_VERSION_ghc(9,0,0) #if !MIN_VERSION_ghc(9,2,0) import qualified Data.Set as Set diff --git a/ghcide/src/Development/IDE/GHC/Compat/Iface.hs b/ghcide/src/Development/IDE/GHC/Compat/Iface.hs index 36ac26a446..e0b36a13a9 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Iface.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Iface.hs @@ -7,6 +7,9 @@ module Development.IDE.GHC.Compat.Iface ( ) where import GHC +#if MIN_VERSION_ghc(9,3,0) +import GHC.Driver.Session (targetProfile) +#endif #if MIN_VERSION_ghc(9,2,0) import qualified GHC.Iface.Load as Iface import GHC.Unit.Finder.Types (FindResult) @@ -24,7 +27,9 @@ import Development.IDE.GHC.Compat.Env import Development.IDE.GHC.Compat.Outputable writeIfaceFile :: HscEnv -> FilePath -> ModIface -> IO () -#if MIN_VERSION_ghc(9,2,0) +#if MIN_VERSION_ghc(9,3,0) +writeIfaceFile env fp iface = Iface.writeIface (hsc_logger env) (targetProfile $ hsc_dflags env) fp iface +#elif MIN_VERSION_ghc(9,2,0) writeIfaceFile env fp iface = Iface.writeIface (hsc_logger env) (hsc_dflags env) fp iface #elif MIN_VERSION_ghc(9,0,0) writeIfaceFile env = Iface.writeIface (hsc_dflags env) diff --git a/ghcide/src/Development/IDE/GHC/Compat/Logger.hs b/ghcide/src/Development/IDE/GHC/Compat/Logger.hs index cb94532eb7..6e8c6dca52 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Logger.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Logger.hs @@ -24,6 +24,9 @@ import GHC.Utils.Logger as Logger import DynFlags import Outputable (queryQual) #endif +#if MIN_VERSION_ghc(9,3,0) +import GHC.Types.Error +#endif putLogHook :: Logger -> HscEnv -> HscEnv putLogHook logger env = @@ -41,6 +44,15 @@ pushLogHook f logger = logger { Env.log_action = f (Env.log_action logger) } #endif +#if MIN_VERSION_ghc(9,3,0) +type LogActionCompat = LogFlags -> Maybe DiagnosticReason -> Maybe Severity -> SrcSpan -> PrintUnqualified -> SDoc -> IO () + +-- alwaysQualify seems to still do the right thing here, according to the "unqualified warnings" test. +logActionCompat :: LogActionCompat -> LogAction +logActionCompat logAction logFlags (MCDiagnostic severity wr) loc = logAction logFlags (Just wr) (Just severity) loc alwaysQualify +logActionCompat logAction logFlags _cls loc = logAction logFlags Nothing Nothing loc alwaysQualify + +#else #if MIN_VERSION_ghc(9,0,0) type LogActionCompat = DynFlags -> WarnReason -> Severity -> SrcSpan -> PrintUnqualified -> SDoc -> IO () @@ -54,3 +66,4 @@ type LogActionCompat = DynFlags -> WarnReason -> Severity -> SrcSpan -> PrintUnq logActionCompat :: LogActionCompat -> LogAction logActionCompat logAction dynFlags wr severity loc style = logAction dynFlags wr severity loc (queryQual style) #endif +#endif diff --git a/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs b/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs index 88bd76934e..084a48a04b 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs @@ -17,8 +17,12 @@ module Development.IDE.GHC.Compat.Outputable ( -- * Parser errors PsWarning, PsError, +#if MIN_VERSION_ghc(9,3,0) + DiagnosticReason(..), +#else pprWarning, pprError, +#endif -- * Error infrastructure DecoratedSDoc, MsgEnvelope, @@ -35,7 +39,11 @@ module Development.IDE.GHC.Compat.Outputable ( import GHC.Driver.Env import GHC.Driver.Ppr import GHC.Driver.Session +#if !MIN_VERSION_ghc(9,3,0) import GHC.Parser.Errors +#else +import GHC.Parser.Errors.Types +#endif import qualified GHC.Parser.Errors.Ppr as Ppr import qualified GHC.Types.Error as Error import GHC.Types.Name.Ppr @@ -69,6 +77,11 @@ import Outputable as Out hiding import qualified Outputable as Out import SrcLoc #endif +#if MIN_VERSION_ghc(9,3,0) +import GHC.Utils.Logger +import GHC.Driver.Config.Diagnostic +import Data.Maybe +#endif -- | A compatible function to print `Outputable` instances -- without unique symbols. @@ -125,6 +138,7 @@ oldFormatErrDoc :: DynFlags -> Err.ErrDoc -> Out.SDoc oldFormatErrDoc = Err.formatErrDoc #endif +#if !MIN_VERSION_ghc(9,3,0) pprWarning :: PsWarning -> MsgEnvelope DecoratedSDoc pprWarning = #if MIN_VERSION_ghc(9,2,0) @@ -140,18 +154,27 @@ pprError = #else id #endif +#endif formatErrorWithQual :: DynFlags -> MsgEnvelope DecoratedSDoc -> String formatErrorWithQual dflags e = #if MIN_VERSION_ghc(9,2,0) showSDoc dflags (pprNoLocMsgEnvelope e) +#if MIN_VERSION_ghc(9,3,0) +pprNoLocMsgEnvelope :: MsgEnvelope DecoratedSDoc -> SDoc +#else pprNoLocMsgEnvelope :: Error.RenderableDiagnostic e => MsgEnvelope e -> SDoc +#endif pprNoLocMsgEnvelope (MsgEnvelope { errMsgDiagnostic = e , errMsgContext = unqual }) = sdocWithContext $ \ctx -> withErrStyle unqual $ +#if MIN_VERSION_ghc(9,3,0) + (formatBulleted ctx $ e) +#else (formatBulleted ctx $ Error.renderDiagnostic e) +#endif #else Out.showSDoc dflags @@ -178,13 +201,18 @@ mkPrintUnqualifiedDefault env = HscTypes.mkPrintUnqualified (hsc_dflags env) #endif -mkWarnMsg :: DynFlags -> SrcSpan -> PrintUnqualified -> SDoc -> MsgEnvelope DecoratedSDoc -mkWarnMsg = +#if MIN_VERSION_ghc(9,3,0) +mkWarnMsg :: DynFlags -> Maybe DiagnosticReason -> b -> SrcSpan -> PrintUnqualified -> SDoc -> MsgEnvelope DecoratedSDoc +mkWarnMsg df reason _logFlags l st doc = fmap diagnosticMessage $ mkMsgEnvelope (initDiagOpts df) l st (mkPlainDiagnostic (fromMaybe WarningWithoutFlag reason) [] doc) +#else +mkWarnMsg :: a -> b -> DynFlags -> SrcSpan -> PrintUnqualified -> SDoc -> MsgEnvelope DecoratedSDoc +mkWarnMsg _ _ = #if MIN_VERSION_ghc(9,2,0) const Error.mkWarnMsg #else Err.mkWarnMsg #endif +#endif defaultUserStyle :: PprStyle #if MIN_VERSION_ghc(9,0,0) diff --git a/ghcide/src/Development/IDE/GHC/Compat/Parser.hs b/ghcide/src/Development/IDE/GHC/Compat/Parser.hs index 91a925cb0b..391ca9fb82 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Parser.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Parser.hs @@ -62,7 +62,11 @@ import GHC (Anchor (anchor), pm_mod_summary, pm_parsed_source) import qualified GHC +#if MIN_VERSION_ghc(9,3,0) +import qualified GHC.Driver.Config.Parser as Config +#else import qualified GHC.Driver.Config as Config +#endif import GHC.Hs (LEpaComment, hpm_module, hpm_src_files) import GHC.Parser.Lexer hiding (initParserState) diff --git a/ghcide/src/Development/IDE/GHC/Compat/Plugins.hs b/ghcide/src/Development/IDE/GHC/Compat/Plugins.hs index 6fd5834f63..12cf035483 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Plugins.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Plugins.hs @@ -24,6 +24,11 @@ import qualified GHC.Driver.Env as Env import GHC.Driver.Plugins (Plugin (..), PluginWithArgs (..), StaticPlugin (..), +#if MIN_VERSION_ghc(9,3,0) + staticPlugins, + ParsedResult(..), + PsMessages(..), +#endif defaultPlugin, withPlugins) import qualified GHC.Runtime.Loader as Loader #elif MIN_VERSION_ghc(8,8,0) @@ -42,15 +47,25 @@ applyPluginsParsedResultAction :: HscEnv -> DynFlags -> ModSummary -> Parser.Api applyPluginsParsedResultAction env dflags ms hpm_annotations parsed = do -- Apply parsedResultAction of plugins let applyPluginAction p opts = parsedResultAction p opts ms +#if MIN_VERSION_ghc(9,3,0) + fmap (hpm_module . parsedResultModule) $ +#else fmap hpm_module $ +#endif runHsc env $ withPlugins -#if MIN_VERSION_ghc(9,2,0) +#if MIN_VERSION_ghc(9,3,0) + (Env.hsc_plugins env) +#elif MIN_VERSION_ghc(9,2,0) env #else dflags #endif applyPluginAction +#if MIN_VERSION_ghc(9,3,0) + (ParsedResult (HsParsedModule parsed [] hpm_annotations) (PsMessages mempty mempty)) +#else (HsParsedModule parsed [] hpm_annotations) +#endif initializePlugins :: HscEnv -> IO HscEnv initializePlugins env = do @@ -64,7 +79,9 @@ initializePlugins env = do #if MIN_VERSION_ghc(8,8,0) hsc_static_plugins :: HscEnv -> [StaticPlugin] -#if MIN_VERSION_ghc(9,2,0) +#if MIN_VERSION_ghc(9,3,0) +hsc_static_plugins = staticPlugins . Env.hsc_plugins +#elif MIN_VERSION_ghc(9,2,0) hsc_static_plugins = Env.hsc_static_plugins #else hsc_static_plugins = staticPlugins . hsc_dflags diff --git a/ghcide/src/Development/IDE/GHC/Compat/Units.hs b/ghcide/src/Development/IDE/GHC/Compat/Units.hs index 9077745aef..c4a56bec5f 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Units.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Units.hs @@ -5,7 +5,10 @@ module Development.IDE.GHC.Compat.Units ( -- * UnitState UnitState, +#if MIN_VERSION_ghc(9,3,0) initUnits, +#endif + oldInitUnits, unitState, getUnitName, explicitUnits, @@ -39,7 +42,7 @@ module Development.IDE.GHC.Compat.Units ( installedModule, -- * Module toUnitId, - moduleUnitId, + Development.IDE.GHC.Compat.Units.moduleUnitId, moduleUnit, -- * ExternalPackageState ExternalPackageState(..), @@ -49,10 +52,18 @@ module Development.IDE.GHC.Compat.Units ( showSDocForUser', ) where +import qualified Data.List.NonEmpty as NE +import qualified Data.Map.Strict as Map +import Control.Monad +#if MIN_VERSION_ghc(9,3,0) +import GHC.Unit.Home.ModInfo +#endif #if MIN_VERSION_ghc(9,0,0) #if MIN_VERSION_ghc(9,2,0) import qualified GHC.Data.ShortText as ST +#if !MIN_VERSION_ghc(9,3,0) import GHC.Driver.Env (hsc_unit_dbs) +#endif import GHC.Driver.Ppr import GHC.Unit.Env import GHC.Unit.External @@ -128,37 +139,69 @@ unitState = DynFlags.unitState . hsc_dflags unitState = DynFlags.pkgState . hsc_dflags #endif -initUnits :: HscEnv -> IO HscEnv -initUnits env = do -#if MIN_VERSION_ghc(9,2,0) - let dflags1 = hsc_dflags env - -- Copied from GHC.setSessionDynFlags - let cached_unit_dbs = hsc_unit_dbs env - (dbs,unit_state,home_unit,mconstants) <- State.initUnits (hsc_logger env) dflags1 cached_unit_dbs - - dflags <- DynFlags.updatePlatformConstants dflags1 mconstants - - +#if MIN_VERSION_ghc(9,3,0) +createUnitEnvFromFlags :: NE.NonEmpty DynFlags -> HomeUnitGraph +createUnitEnvFromFlags unitDflags = + let + newInternalUnitEnv dflags = mkHomeUnitEnv dflags emptyHomePackageTable Nothing + unitEnvList = NE.map (\dflags -> (homeUnitId_ dflags, newInternalUnitEnv dflags)) unitDflags + in + unitEnv_new (Map.fromList (NE.toList (unitEnvList))) + +initUnits :: [DynFlags] -> HscEnv -> IO HscEnv +initUnits unitDflags env = do + let dflags0 = hsc_dflags env + -- additionally, set checked dflags so we don't lose fixes + let initial_home_graph = createUnitEnvFromFlags (dflags0 NE.:| unitDflags) + home_units = unitEnv_keys initial_home_graph + home_unit_graph <- forM initial_home_graph $ \homeUnitEnv -> do + let cached_unit_dbs = homeUnitEnv_unit_dbs homeUnitEnv + dflags = homeUnitEnv_dflags homeUnitEnv + old_hpt = homeUnitEnv_hpt homeUnitEnv + + (dbs,unit_state,home_unit,mconstants) <- State.initUnits (hsc_logger env) dflags cached_unit_dbs home_units + + updated_dflags <- DynFlags.updatePlatformConstants dflags mconstants + pure HomeUnitEnv + { homeUnitEnv_units = unit_state + , homeUnitEnv_unit_dbs = Just dbs + , homeUnitEnv_dflags = updated_dflags + , homeUnitEnv_hpt = old_hpt + , homeUnitEnv_home_unit = Just home_unit + } + + let dflags1 = homeUnitEnv_dflags $ unitEnv_lookup (homeUnitId_ dflags0) home_unit_graph let unit_env = UnitEnv - { ue_platform = targetPlatform dflags - , ue_namever = DynFlags.ghcNameVersion dflags - , ue_home_unit = home_unit - , ue_units = unit_state + { ue_platform = targetPlatform dflags1 + , ue_namever = GHC.ghcNameVersion dflags1 + , ue_home_unit_graph = home_unit_graph + , ue_current_unit = homeUnitId_ dflags0 + , ue_eps = ue_eps (hsc_unit_env env) } - pure $ hscSetFlags dflags $ hscSetUnitEnv unit_env env - { hsc_unit_dbs = Just dbs - } + pure $ hscSetFlags dflags1 $ hscSetUnitEnv unit_env env +#endif + +-- | oldInitUnits only needs to modify DynFlags for GHC <9.2 +-- For GHC >= 9.2, we need to set the hsc_unit_env also, that is +-- done later by initUnits +oldInitUnits :: DynFlags -> IO DynFlags +#if MIN_VERSION_ghc(9,2,0) +oldInitUnits = pure #elif MIN_VERSION_ghc(9,0,0) - newFlags <- State.initUnits $ hsc_dflags env - pure $ hscSetFlags newFlags env +oldInitUnits dflags = do + newFlags <- State.initUnits dflags + pure newFlags #else - newFlags <- fmap fst . Packages.initPackages $ hsc_dflags env - pure $ hscSetFlags newFlags env +oldInitUnits dflags = do + newFlags <- fmap fst $ Packages.initPackages dflags + pure newFlags #endif explicitUnits :: UnitState -> [Unit] explicitUnits ue = -#if MIN_VERSION_ghc(9,0,0) +#if MIN_VERSION_ghc(9,3,0) + map fst $ State.explicitUnits ue +#elif MIN_VERSION_ghc(9,0,0) State.explicitUnits ue #else Packages.explicitPackages ue @@ -180,7 +223,15 @@ getUnitName env i = packageName <$> Packages.lookupPackage (hsc_dflags env) (definiteUnitId (defUnitId i)) #endif -lookupModuleWithSuggestions :: HscEnv -> ModuleName -> Maybe FastString -> LookupResult +lookupModuleWithSuggestions + :: HscEnv + -> ModuleName +#if MIN_VERSION_ghc(9,3,0) + -> GHC.PkgQual +#else + -> Maybe FastString +#endif + -> LookupResult lookupModuleWithSuggestions env modname mpkg = #if MIN_VERSION_ghc(9,0,0) State.lookupModuleWithSuggestions (unitState env) modname mpkg diff --git a/ghcide/src/Development/IDE/GHC/Compat/Util.hs b/ghcide/src/Development/IDE/GHC/Compat/Util.hs index 17799ea31d..7c521e88e8 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Util.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Util.hs @@ -24,7 +24,9 @@ module Development.IDE.GHC.Compat.Util ( LBooleanFormula, BooleanFormula(..), -- * OverridingBool +#if !MIN_VERSION_ghc(9,3,0) OverridingBool(..), +#endif -- * Maybes MaybeErr(..), orElse, diff --git a/ghcide/src/Development/IDE/GHC/Error.hs b/ghcide/src/Development/IDE/GHC/Error.hs index 74a72148c8..89c527e404 100644 --- a/ghcide/src/Development/IDE/GHC/Error.hs +++ b/ghcide/src/Development/IDE/GHC/Error.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE CPP #-} -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 module Development.IDE.GHC.Error @@ -121,13 +122,17 @@ p `isInsideSrcSpan` r = case srcSpanToRange r of -- | Convert a GHC severity to a DAML compiler Severity. Severities below -- "Warning" level are dropped (returning Nothing). toDSeverity :: GHC.Severity -> Maybe D.DiagnosticSeverity +#if !MIN_VERSION_ghc(9,3,0) toDSeverity SevOutput = Nothing toDSeverity SevInteractive = Nothing toDSeverity SevDump = Nothing toDSeverity SevInfo = Just DsInfo +toDSeverity SevFatal = Just DsError +#else +toDSeverity SevIgnore = Nothing +#endif toDSeverity SevWarning = Just DsWarning toDSeverity SevError = Just DsError -toDSeverity SevFatal = Just DsError -- | Produce a bag of GHC-style errors (@ErrorMessages@) from the given @@ -167,7 +172,11 @@ catchSrcErrors dflags fromWhere ghcM = do Right <$> ghcM where ghcExceptionToDiagnostics dflags = return . Left . diagFromGhcException fromWhere dflags - sourceErrorToDiagnostics dflags = return . Left . diagFromErrMsgs fromWhere dflags . srcErrorMessages + sourceErrorToDiagnostics dflags = return . Left . diagFromErrMsgs fromWhere dflags +#if MIN_VERSION_ghc(9,3,0) + . fmap (fmap Compat.diagnosticMessage) . Compat.getMessages +#endif + . srcErrorMessages diagFromGhcException :: T.Text -> DynFlags -> GhcException -> [FileDiagnostic] diff --git a/ghcide/src/Development/IDE/GHC/Orphans.hs b/ghcide/src/Development/IDE/GHC/Orphans.hs index 3d506fbe4a..4e7ff4463e 100644 --- a/ghcide/src/Development/IDE/GHC/Orphans.hs +++ b/ghcide/src/Development/IDE/GHC/Orphans.hs @@ -43,6 +43,9 @@ import GHC.ByteCode.Types #else import ByteCodeTypes #endif +#if MIN_VERSION_ghc(9,3,0) +import GHC.Types.PkgQual +#endif -- Orphan instances for types from the GHC API. instance Show CoreModule where show = unpack . printOutputable @@ -85,7 +88,9 @@ instance NFData SB.StringBuffer where rnf = rwhnf instance Show Module where show = moduleNameString . moduleName +#if !MIN_VERSION_ghc(9,3,0) instance Outputable a => Show (GenLocated SrcSpan a) where show = unpack . printOutputable +#endif instance (NFData l, NFData e) => NFData (GenLocated l e) where rnf (L l e) = rnf l `seq` rnf e @@ -126,10 +131,12 @@ instance Show HieFile where instance NFData HieFile where rnf = rwhnf +#if !MIN_VERSION_ghc(9,3,0) deriving instance Eq SourceModified deriving instance Show SourceModified instance NFData SourceModified where rnf = rwhnf +#endif #if !MIN_VERSION_ghc(9,2,0) instance Show ModuleName where @@ -207,3 +214,13 @@ instance Show HomeModInfo where show = show . mi_module . hm_iface instance NFData HomeModInfo where rnf (HomeModInfo iface dets link) = rwhnf iface `seq` rnf dets `seq` rnf link + +#if MIN_VERSION_ghc(9,3,0) +instance NFData PkgQual where + rnf NoPkgQual = () + rnf (ThisPkg uid) = rnf uid + rnf (OtherPkg uid) = rnf uid + +instance NFData UnitId where + rnf = rwhnf +#endif diff --git a/ghcide/src/Development/IDE/GHC/Util.hs b/ghcide/src/Development/IDE/GHC/Util.hs index 4776626aa6..8dd99b8bde 100644 --- a/ghcide/src/Development/IDE/GHC/Util.hs +++ b/ghcide/src/Development/IDE/GHC/Util.hs @@ -32,7 +32,7 @@ module Development.IDE.GHC.Util( #if MIN_VERSION_ghc(9,2,0) import GHC.Data.FastString import GHC.Data.StringBuffer -import GHC.Driver.Env +import GHC.Driver.Env hiding (hscSetFlags) import GHC.Driver.Monad import GHC.Driver.Session hiding (ExposePackage) import GHC.Parser.Lexer diff --git a/ghcide/src/Development/IDE/GHC/Warnings.hs b/ghcide/src/Development/IDE/GHC/Warnings.hs index 720828fef3..fa30373ce8 100644 --- a/ghcide/src/Development/IDE/GHC/Warnings.hs +++ b/ghcide/src/Development/IDE/GHC/Warnings.hs @@ -1,6 +1,7 @@ -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 {-# LANGUAGE ExplicitNamespaces #-} +{-# LANGUAGE CPP #-} module Development.IDE.GHC.Warnings(withWarnings) where @@ -23,14 +24,18 @@ import Language.LSP.Types (type (|?) (..)) -- https://github.com/ghc/ghc/blob/5f1d949ab9e09b8d95319633854b7959df06eb58/compiler/main/GHC.hs#L623-L640 -- which basically says that log_action is taken from the ModSummary when GHC feels like it. -- The given argument lets you refresh a ModSummary log_action +#if MIN_VERSION_ghc(9,3,0) +withWarnings :: T.Text -> ((HscEnv -> HscEnv) -> IO a) -> IO ([(Maybe DiagnosticReason, FileDiagnostic)], a) +#else withWarnings :: T.Text -> ((HscEnv -> HscEnv) -> IO a) -> IO ([(WarnReason, FileDiagnostic)], a) +#endif withWarnings diagSource action = do warnings <- newVar [] - let newAction :: LogActionCompat - newAction dynFlags wr _ loc prUnqual msg = do - let wr_d = map ((wr,) . third3 (attachReason wr)) $ diagFromErrMsg diagSource dynFlags $ mkWarnMsg dynFlags loc prUnqual msg + let newAction :: DynFlags -> LogActionCompat + newAction dynFlags logFlags wr _ loc prUnqual msg = do + let wr_d = map ((wr,) . third3 (attachReason wr)) $ diagFromErrMsg diagSource dynFlags $ mkWarnMsg dynFlags wr logFlags loc prUnqual msg modifyVar_ warnings $ return . (wr_d:) - newLogger env = pushLogHook (const (logActionCompat newAction)) (hsc_logger env) + newLogger env = pushLogHook (const (logActionCompat (newAction (hsc_dflags env)))) (hsc_logger env) res <- action $ \env -> putLogHook (newLogger env) env warns <- readVar warnings return (reverse $ concat warns, res) @@ -38,6 +43,15 @@ withWarnings diagSource action = do third3 :: (c -> d) -> (a, b, c) -> (a, b, d) third3 f (a, b, c) = (a, b, f c) +#if MIN_VERSION_ghc(9,3,0) +attachReason :: Maybe DiagnosticReason -> Diagnostic -> Diagnostic +attachReason Nothing d = d +attachReason (Just wr) d = d{_code = InR <$> showReason wr} + where + showReason = \case + WarningWithFlag flag -> showFlag flag + _ -> Nothing +#else attachReason :: WarnReason -> Diagnostic -> Diagnostic attachReason wr d = d{_code = InR <$> showReason wr} where @@ -45,6 +59,7 @@ attachReason wr d = d{_code = InR <$> showReason wr} NoReason -> Nothing Reason flag -> showFlag flag ErrReason flag -> showFlag =<< flag +#endif showFlag :: WarningFlag -> Maybe T.Text showFlag flag = ("-W" <>) . T.pack . flagSpecName <$> find ((== flag) . flagSpecFlag) wWarningFlags diff --git a/ghcide/src/Development/IDE/Import/FindImports.hs b/ghcide/src/Development/IDE/Import/FindImports.hs index 101e21fe32..2cc08b9f57 100644 --- a/ghcide/src/Development/IDE/Import/FindImports.hs +++ b/ghcide/src/Development/IDE/Import/FindImports.hs @@ -27,6 +27,9 @@ import Control.Monad.IO.Class import Data.List (isSuffixOf) import Data.Maybe import System.FilePath +#if MIN_VERSION_ghc(9,3,0) +import GHC.Types.PkgQual +#endif data Import = FileImport !ArtifactsLocation @@ -37,11 +40,11 @@ data ArtifactsLocation = ArtifactsLocation { artifactFilePath :: !NormalizedFilePath , artifactModLocation :: !(Maybe ModLocation) , artifactIsSource :: !Bool -- ^ True if a module is a source input - } - deriving (Show) + , artifactModule :: !(Maybe Module) + } deriving Show instance NFData ArtifactsLocation where - rnf ArtifactsLocation{..} = rnf artifactFilePath `seq` rwhnf artifactModLocation `seq` rnf artifactIsSource + rnf ArtifactsLocation{..} = rnf artifactFilePath `seq` rwhnf artifactModLocation `seq` rnf artifactIsSource `seq` rnf artifactModule isBootLocation :: ArtifactsLocation -> Bool isBootLocation = not . artifactIsSource @@ -51,28 +54,30 @@ instance NFData Import where rnf PackageImport = () modSummaryToArtifactsLocation :: NormalizedFilePath -> Maybe ModSummary -> ArtifactsLocation -modSummaryToArtifactsLocation nfp ms = ArtifactsLocation nfp (ms_location <$> ms) source +modSummaryToArtifactsLocation nfp ms = ArtifactsLocation nfp (ms_location <$> ms) source mod where isSource HsSrcFile = True isSource _ = False source = case ms of Nothing -> "-boot" `isSuffixOf` fromNormalizedFilePath nfp Just ms -> isSource (ms_hsc_src ms) + mod = ms_mod <$> ms -- | locate a module in the file system. Where we go from *daml to Haskell locateModuleFile :: MonadIO m - => [[FilePath]] + => [(UnitId, [FilePath])] -> [String] -> (ModuleName -> NormalizedFilePath -> m (Maybe NormalizedFilePath)) -> Bool -> ModuleName - -> m (Maybe NormalizedFilePath) + -> m (Maybe (UnitId, NormalizedFilePath)) locateModuleFile import_dirss exts targetFor isSource modName = do let candidates import_dirs = [ toNormalizedFilePath' (prefix moduleNameSlashes modName <.> maybeBoot ext) | prefix <- import_dirs , ext <- exts] - firstJustM (targetFor modName) (concatMap candidates import_dirss) + firstJustM go (concat [map (uid,) (candidates dirs) | (uid, dirs) <- import_dirss]) where + go (uid, candidate) = fmap ((uid,) <$>) $ targetFor modName candidate maybeBoot ext | isSource = ext ++ "-boot" | otherwise = ext @@ -81,8 +86,13 @@ locateModuleFile import_dirss exts targetFor isSource modName = do -- It only returns Just for unit-ids which are possible to import into the -- current module. In particular, it will return Nothing for 'main' components -- as they can never be imported into another package. -mkImportDirs :: HscEnv -> (UnitId, DynFlags) -> Maybe (PackageName, [FilePath]) -mkImportDirs env (i, flags) = (, importPaths flags) <$> getUnitName env i +#if MIN_VERSION_ghc(9,3,0) +mkImportDirs :: HscEnv -> (UnitId, DynFlags) -> Maybe (UnitId, [FilePath]) +mkImportDirs env (i, flags) = Just (i, importPaths flags) +#else +mkImportDirs :: HscEnv -> (UnitId, DynFlags) -> Maybe (PackageName, (UnitId, [FilePath])) +mkImportDirs env (i, flags) = (, (i, importPaths flags)) <$> getUnitName env i +#endif -- | locate a module in either the file system or the package database. Where we go from *daml to -- Haskell @@ -93,43 +103,72 @@ locateModule -> [String] -- ^ File extensions -> (ModuleName -> NormalizedFilePath -> m (Maybe NormalizedFilePath)) -- ^ does file exist predicate -> Located ModuleName -- ^ Module name +#if MIN_VERSION_ghc(9,3,0) + -> PkgQual -- ^ Package name +#else -> Maybe FastString -- ^ Package name +#endif -> Bool -- ^ Is boot module -> m (Either [FileDiagnostic] Import) locateModule env comp_info exts targetFor modName mbPkgName isSource = do case mbPkgName of -- "this" means that we should only look in the current package +#if MIN_VERSION_ghc(9,3,0) + ThisPkg _ -> do +#else Just "this" -> do - lookupLocal [importPaths dflags] +#endif + lookupLocal (homeUnitId_ dflags) (importPaths dflags) -- if a package name is given we only go look for a package +#if MIN_VERSION_ghc(9,3,0) + OtherPkg uid + | Just dirs <- lookup uid import_paths +#else Just pkgName - | Just dirs <- lookup (PackageName pkgName) import_paths - -> lookupLocal [dirs] + | Just (uid, dirs) <- lookup (PackageName pkgName) import_paths +#endif + -> lookupLocal uid dirs | otherwise -> lookupInPackageDB env +#if MIN_VERSION_ghc(9,3,0) + NoPkgQual -> do +#else Nothing -> do +#endif -- first try to find the module as a file. If we can't find it try to find it in the package -- database. -- Here the importPaths for the current modules are added to the front of the import paths from the other components. -- This is particularly important for Paths_* modules which get generated for every component but unless you use it in -- each component will end up being found in the wrong place and cause a multi-cradle match failure. - mbFile <- locateModuleFile (importPaths dflags : map snd import_paths) exts targetFor isSource $ unLoc modName + let import_paths' = +#if MIN_VERSION_ghc(9,3,0) + import_paths +#else + map snd import_paths +#endif + + mbFile <- locateModuleFile ((homeUnitId_ dflags, importPaths dflags) : import_paths') exts targetFor isSource $ unLoc modName case mbFile of Nothing -> lookupInPackageDB env - Just file -> toModLocation file + Just (uid, file) -> toModLocation uid file where dflags = hsc_dflags env import_paths = mapMaybe (mkImportDirs env) comp_info - toModLocation file = liftIO $ do + toModLocation uid file = liftIO $ do loc <- mkHomeModLocation dflags (unLoc modName) (fromNormalizedFilePath file) - return $ Right $ FileImport $ ArtifactsLocation file (Just loc) (not isSource) - - lookupLocal dirs = do - mbFile <- locateModuleFile dirs exts targetFor isSource $ unLoc modName +#if MIN_VERSION_ghc(9,0,0) + let mod = mkModule (RealUnit $ Definite uid) (unLoc modName) -- TODO support backpack holes +#else + let mod = mkModule uid (unLoc modName) +#endif + return $ Right $ FileImport $ ArtifactsLocation file (Just loc) (not isSource) (Just mod) + + lookupLocal uid dirs = do + mbFile <- locateModuleFile [(uid, dirs)] exts targetFor isSource $ unLoc modName case mbFile of Nothing -> return $ Left $ notFoundErr env modName $ LookupNotFound [] - Just file -> toModLocation file + Just (uid, file) -> toModLocation uid file - lookupInPackageDB env = + lookupInPackageDB env = do case Compat.lookupModuleWithSuggestions env (unLoc modName) mbPkgName of LookupFound _m _pkgConfig -> return $ Right PackageImport reason -> return $ Left $ notFoundErr env modName reason diff --git a/ghcide/src/Development/IDE/LSP/Outline.hs b/ghcide/src/Development/IDE/LSP/Outline.hs index 0a45688fef..2ad518d588 100644 --- a/ghcide/src/Development/IDE/LSP/Outline.hs +++ b/ghcide/src/Development/IDE/LSP/Outline.hs @@ -11,7 +11,7 @@ where import Control.Monad.IO.Class import Data.Functor -import Data.Generics +import Data.Generics hiding (Prefix) import Data.Maybe import qualified Data.Text as T import Development.IDE.Core.Rules @@ -122,8 +122,16 @@ documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (TyClD _ DataDecl { tcdLNam } where cvtFld :: LFieldOcc GhcPs -> Maybe DocumentSymbol +#if MIN_VERSION_ghc(9,3,0) + cvtFld (L (locA -> RealSrcSpan l _) n) = Just $ (defDocumentSymbol l :: DocumentSymbol) +#else cvtFld (L (RealSrcSpan l _) n) = Just $ (defDocumentSymbol l :: DocumentSymbol) +#endif +#if MIN_VERSION_ghc(9,3,0) + { _name = printOutputable (unLoc (foLabel n)) +#else { _name = printOutputable (unLoc (rdrNameFieldOcc n)) +#endif , _kind = SkField } cvtFld _ = Nothing @@ -161,8 +169,13 @@ documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (InstD _ DataFamInstD { dfi documentSymbolForDecl (L (RealSrcSpan l _) (InstD _ DataFamInstD { dfid_inst = DataFamInstDecl HsIB { hsib_body = FamEqn { feqn_tycon, feqn_pats } } })) #endif = Just (defDocumentSymbol l :: DocumentSymbol) - { _name = printOutputable (unLoc feqn_tycon) <> " " <> T.unwords + { _name = +#if MIN_VERSION_ghc(9,3,0) + printOutputable $ pprHsArgsApp (unLoc feqn_tycon) Prefix (feqn_pats) +#else + printOutputable (unLoc feqn_tycon) <> " " <> T.unwords (map printOutputable feqn_pats) +#endif , _kind = SkInterface } #if MIN_VERSION_ghc(9,2,0) @@ -171,8 +184,13 @@ documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (InstD _ TyFamInstD { tfid_ documentSymbolForDecl (L (RealSrcSpan l _) (InstD _ TyFamInstD { tfid_inst = TyFamInstDecl HsIB { hsib_body = FamEqn { feqn_tycon, feqn_pats } } })) #endif = Just (defDocumentSymbol l :: DocumentSymbol) - { _name = printOutputable (unLoc feqn_tycon) <> " " <> T.unwords + { _name = +#if MIN_VERSION_ghc(9,3,0) + printOutputable $ pprHsArgsApp (unLoc feqn_tycon) Prefix (feqn_pats) +#else + printOutputable (unLoc feqn_tycon) <> " " <> T.unwords (map printOutputable feqn_pats) +#endif , _kind = SkInterface } documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (DerivD _ DerivDecl { deriv_type })) = @@ -217,7 +235,7 @@ documentSymbolForImportSummary importSymbols = let -- safe because if we have no ranges then we don't take this branch mergeRanges xs = Range (minimum $ map _start xs) (maximum $ map _end xs) - importRange = mergeRanges $ map (_range :: DocumentSymbol -> Range) importSymbols + importRange = mergeRanges $ map (\DocumentSymbol{_range} -> _range) importSymbols in Just (defDocumentSymbol (rangeToRealSrcSpan "" importRange)) { _name = "imports" @@ -293,7 +311,11 @@ hsConDeclsBinders cons get_flds_gadt :: HsConDeclGADTDetails GhcPs -> ([LFieldOcc GhcPs]) +#if MIN_VERSION_ghc(9,3,0) + get_flds_gadt (RecConGADT flds _) = get_flds (reLoc flds) +#else get_flds_gadt (RecConGADT flds) = get_flds (reLoc flds) +#endif get_flds_gadt _ = [] get_flds :: Located [LConDeclField GhcPs] diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index c703ec7a66..415743d082 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -526,7 +526,11 @@ findRecordCompl uri pmod mn DataDecl {tcdLName, tcdDataDefn} = result -- -- is encoded as @[[arg1, arg2], [arg3], [arg4]]@ -- Hence, we must concat nested arguments into one to get all the fields. +#if MIN_VERSION_ghc(9,3,0) + = map (foLabel . unLoc) cd_fld_names +#else = map (rdrNameFieldOcc . unLoc) cd_fld_names +#endif -- XConDeclField extract _ = [] findRecordCompl _ _ _ _ = [] diff --git a/ghcide/src/Development/IDE/Spans/Common.hs b/ghcide/src/Development/IDE/Spans/Common.hs index dd241e7fc9..701074b3ac 100644 --- a/ghcide/src/Development/IDE/Spans/Common.hs +++ b/ghcide/src/Development/IDE/Spans/Common.hs @@ -50,7 +50,11 @@ safeTyThingId _ = Nothing -- Possible documentation for an element in the code data SpanDoc +#if MIN_VERSION_ghc(9,3,0) + = SpanDocString [HsDocString] SpanDocUris +#else = SpanDocString HsDocString SpanDocUris +#endif | SpanDocText [T.Text] SpanDocUris deriving stock (Eq, Show, Generic) deriving anyclass NFData @@ -86,7 +90,12 @@ emptySpanDoc = SpanDocText [] (SpanDocUris Nothing Nothing) spanDocToMarkdown :: SpanDoc -> [T.Text] spanDocToMarkdown = \case (SpanDocString docs uris) -> - let doc = T.pack $ haddockToMarkdown $ H.toRegular $ H._doc $ H.parseParas Nothing $ unpackHDS docs + let doc = T.pack $ haddockToMarkdown $ H.toRegular $ H._doc $ H.parseParas Nothing $ +#if MIN_VERSION_ghc(9,3,0) + renderHsDocStrings docs +#else + unpackHDS docs +#endif in go [doc] uris (SpanDocText txt uris) -> go txt uris where diff --git a/ghcide/src/Development/IDE/Spans/Documentation.hs b/ghcide/src/Development/IDE/Spans/Documentation.hs index c5a9190652..08ad918bc4 100644 --- a/ghcide/src/Development/IDE/Spans/Documentation.hs +++ b/ghcide/src/Development/IDE/Spans/Documentation.hs @@ -33,6 +33,9 @@ import System.Directory import System.FilePath import Language.LSP.Types (filePathToUri, getUri) +#if MIN_VERSION_ghc(9,3,0) +import GHC.Types.Unique.Map +#endif mkDocMap :: HscEnv @@ -41,12 +44,18 @@ mkDocMap -> IO DocAndKindMap mkDocMap env rm this_mod = do -#if MIN_VERSION_ghc(9,2,0) +#if MIN_VERSION_ghc(9,3,0) + (Just Docs{docs_decls = UniqMap this_docs}) <- extractDocs (hsc_dflags env) this_mod +#elif MIN_VERSION_ghc(9,2,0) (_ , DeclDocMap this_docs, _) <- extractDocs this_mod #else let (_ , DeclDocMap this_docs, _) = extractDocs this_mod #endif +#if MIN_VERSION_ghc(9,3,0) + d <- foldrM getDocs (fmap (\(_, x) -> (map hsDocString x) `SpanDocString` SpanDocUris Nothing Nothing) this_docs) names +#else d <- foldrM getDocs (mkNameEnv $ M.toList $ fmap (`SpanDocString` SpanDocUris Nothing Nothing) this_docs) names +#endif k <- foldrM getType (tcg_type_env this_mod) names pure $ DKMap d k where @@ -69,7 +78,7 @@ lookupKind env mod = fmap (fromRight Nothing) . catchSrcErrors (hsc_dflags env) "span" . lookupName env mod getDocumentationTryGhc :: HscEnv -> Module -> Name -> IO SpanDoc -getDocumentationTryGhc env mod n = head <$> getDocumentationsTryGhc env mod [n] +getDocumentationTryGhc env mod n = fromMaybe emptySpanDoc . listToMaybe <$> getDocumentationsTryGhc env mod [n] getDocumentationsTryGhc :: HscEnv -> Module -> [Name] -> IO [SpanDoc] getDocumentationsTryGhc env mod names = do @@ -78,7 +87,11 @@ getDocumentationsTryGhc env mod names = do Left _ -> return [] Right res -> zipWithM unwrap res names where +#if MIN_VERSION_ghc(9,3,0) + unwrap (Right (Just docs, _)) n = SpanDocString (map hsDocString docs) <$> getUris n +#else unwrap (Right (Just docs, _)) n = SpanDocString docs <$> getUris n +#endif unwrap _ n = mkSpanDocText n mkSpanDocText name = diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 12fce8ce67..523a7474f8 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -259,11 +259,11 @@ initializeResponseTests = withResource acquire release tests where _documentOnTypeFormattingProvider Nothing , chk "NO renaming" _renameProvider (Just $ InL False) , chk "NO doc link" _documentLinkProvider Nothing - , chk "NO color" _colorProvider (Just $ InL False) + , chk "NO color" (^. L.colorProvider) (Just $ InL False) , chk "NO folding range" _foldingRangeProvider (Just $ InL False) , che " execute command" _executeCommandProvider [typeLensCommandId, blockCommandId] - , chk " workspace" _workspace (Just $ WorkspaceServerCapabilities (Just WorkspaceFoldersServerCapabilities{_supported = Just True, _changeNotifications = Just ( InR True )})) - , chk "NO experimental" _experimental Nothing + , chk " workspace" (^. L.workspace) (Just $ WorkspaceServerCapabilities (Just WorkspaceFoldersServerCapabilities{_supported = Just True, _changeNotifications = Just ( InR True )})) + , chk "NO experimental" (^. L.experimental) Nothing ] where tds = Just (InL (TextDocumentSyncOptions @@ -564,13 +564,20 @@ diagnosticTests = testGroup "diagnostics" , "useBase = BaseList.map" , "wrong1 = ThisList.map" , "wrong2 = BaseList.x" + , "main = pure ()" ] _ <- createDoc "Data/List.hs" "haskell" thisDataListContent _ <- createDoc "Main.hs" "haskell" mainContent expectDiagnostics [ ( "Main.hs" - , [(DsError, (6, 9), "Not in scope: \8216ThisList.map\8217") - ,(DsError, (7, 9), "Not in scope: \8216BaseList.x\8217") + , [(DsError, (6, 9), + if ghcVersion >= GHC94 + then "Variable not in scope: map" -- See https://gitlab.haskell.org/ghc/ghc/-/issues/22130 + else "Not in scope: \8216ThisList.map\8217") + ,(DsError, (7, 9), + if ghcVersion >= GHC94 + then "Variable not in scope: x" -- See https://gitlab.haskell.org/ghc/ghc/-/issues/22130 + else "Not in scope: \8216BaseList.x\8217") ] ) ] @@ -588,7 +595,7 @@ diagnosticTests = testGroup "diagnostics" -- where appropriate. The warning should use an unqualified name 'Ord', not -- sometihng like 'GHC.Classes.Ord'. The choice of redundant-constraints to -- test this is fairly arbitrary. - , [(DsWarning, (2, 0), "Redundant constraint: Ord a") + , [(DsWarning, (2, if ghcVersion >= GHC94 then 7 else 0), "Redundant constraint: Ord a") ] ) ] @@ -621,7 +628,7 @@ diagnosticTests = testGroup "diagnostics" -- Check that if we put a lower-case drive in for A.A -- the diagnostics for A.B will also be lower-case. liftIO $ fileUri @?= uriB - let msg = _message (head (toList diags) :: Diagnostic) + let msg = head (toList diags) ^. L.message liftIO $ unless ("redundant" `T.isInfixOf` msg) $ assertFailure ("Expected redundant import but got " <> T.unpack msg) closeDoc a @@ -1096,7 +1103,7 @@ findDefinitionAndHoverTests = let holeL65 = Position 65 8 ; hleInfo2 = [ExpectHoverText ["_ :: a -> Maybe a"]] cccL17 = Position 17 16 ; docLink = [ExpectHoverTextRegex "\\*Defined in 'GHC.Types'\\* \\*\\(ghc-prim-[0-9.]+\\)\\*\n\n"] imported = Position 56 13 ; importedSig = getDocUri "Foo.hs" >>= \foo -> return [ExpectHoverText ["foo", "Foo", "Haddock"], mkL foo 5 0 5 3] - reexported = Position 55 14 ; reexportedSig = getDocUri "Bar.hs" >>= \bar -> return [ExpectHoverText ["Bar", "Bar", "Haddock"], mkL bar 3 0 3 14] + reexported = Position 55 14 ; reexportedSig = getDocUri "Bar.hs" >>= \bar -> return [ExpectHoverText ["Bar", "Bar", "Haddock"], mkL bar 3 (if ghcVersion >= GHC94 then 5 else 0) 3 (if ghcVersion >= GHC94 then 8 else 14)] thLocL57 = Position 59 10 ; thLoc = [ExpectHoverText ["Identity"]] in mkFindTests @@ -1180,7 +1187,7 @@ checkFileCompiles fp diag = pluginSimpleTests :: TestTree pluginSimpleTests = ignoreInWindowsForGHC88And810 $ - ignoreForGHC92 "blocked on ghc-typelits-natnormalise" $ + ignoreForGHC92Plus "blocked on ghc-typelits-natnormalise" $ testSessionWithExtraFiles "plugin-knownnat" "simple plugin" $ \dir -> do _ <- openDoc (dir "KnownNat.hs") "haskell" liftIO $ writeFile (dir"hie.yaml") @@ -1195,7 +1202,7 @@ pluginSimpleTests = pluginParsedResultTests :: TestTree pluginParsedResultTests = ignoreInWindowsForGHC88And810 $ - ignoreForGHC92 "No need for this plugin anymore!" $ + ignoreForGHC92Plus "No need for this plugin anymore!" $ testSessionWithExtraFiles "plugin-recorddot" "parsedResultAction plugin" $ \dir -> do _ <- openDoc (dir "RecordDot.hs") "haskell" expectNoMoreDiagnostics 2 @@ -1778,10 +1785,10 @@ packageCompletionTests = , _label == "fromList" ] liftIO $ take 3 (sort compls') @?= - map ("Defined in "<>) + map ("Defined in "<>) ( [ "'Data.List.NonEmpty" , "'GHC.Exts" - ] + ] ++ if ghcVersion >= GHC94 then [ "'GHC.IsList" ] else []) , testSessionWait "Map" $ do doc <- createDoc "A.hs" "haskell" $ T.unlines @@ -1994,10 +2001,10 @@ completionDocTests = test doc (Position 1 7) "id" (Just $ T.length expected) [expected] ] where - brokenForGhc9 = knownBrokenFor (BrokenForGHC [GHC90, GHC92]) "Completion doc doesn't support ghc9" - brokenForWinGhc9 = knownBrokenFor (BrokenSpecific Windows [GHC90, GHC92]) "Extern doc doesn't support Windows for ghc9.2" + brokenForGhc9 = knownBrokenFor (BrokenForGHC [GHC90, GHC92, GHC94]) "Completion doc doesn't support ghc9" + brokenForWinGhc9 = knownBrokenFor (BrokenSpecific Windows [GHC90, GHC92, GHC94]) "Extern doc doesn't support Windows for ghc9.2" -- https://gitlab.haskell.org/ghc/ghc/-/issues/20903 - brokenForMacGhc9 = knownBrokenFor (BrokenSpecific MacOS [GHC90, GHC92]) "Extern doc doesn't support MacOS for ghc9" + brokenForMacGhc9 = knownBrokenFor (BrokenSpecific MacOS [GHC90, GHC92, GHC94]) "Extern doc doesn't support MacOS for ghc9" test doc pos label mn expected = do _ <- waitForDiagnostics compls <- getCompletions doc pos @@ -2040,7 +2047,7 @@ highlightTests = testGroup "highlight" , DocumentHighlight (R 6 10 6 13) (Just HkRead) , DocumentHighlight (R 7 12 7 15) (Just HkRead) ] - , knownBrokenForGhcVersions [GHC90, GHC92] "Ghc9 highlights the constructor and not just this field" $ + , knownBrokenForGhcVersions [GHC90, GHC92, GHC94] "Ghc9 highlights the constructor and not just this field" $ testSessionWait "record" $ do doc <- createDoc "A.hs" "haskell" recsource _ <- waitForDiagnostics @@ -2048,8 +2055,8 @@ highlightTests = testGroup "highlight" liftIO $ highlights @?= List -- Span is just the .. on 8.10, but Rec{..} before [ if ghcVersion >= GHC810 - then DocumentHighlight (R 4 8 4 10) (Just HkWrite) - else DocumentHighlight (R 4 4 4 11) (Just HkWrite) + then DocumentHighlight (R 4 8 4 10) (Just HkWrite) + else DocumentHighlight (R 4 4 4 11) (Just HkWrite) , DocumentHighlight (R 4 14 4 20) (Just HkRead) ] highlights <- getHighlights doc (Position 3 17) @@ -2270,8 +2277,8 @@ ignoreInWindowsForGHC88And810 :: TestTree -> TestTree ignoreInWindowsForGHC88And810 = ignoreFor (BrokenSpecific Windows [GHC88, GHC810]) "tests are unreliable in windows for ghc 8.8 and 8.10" -ignoreForGHC92 :: String -> TestTree -> TestTree -ignoreForGHC92 = ignoreFor (BrokenForGHC [GHC92]) +ignoreForGHC92Plus :: String -> TestTree -> TestTree +ignoreForGHC92Plus = ignoreFor (BrokenForGHC [GHC92, GHC94]) ignoreInWindowsForGHC88 :: TestTree -> TestTree ignoreInWindowsForGHC88 = diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 6ea7ad476e..bfdd321674 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -239,12 +239,12 @@ flag dynamic manual: True common class - if flag(class) + if flag(class) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-class-plugin ^>= 1.0 cpp-options: -Dhls_class common callHierarchy - if flag(callHierarchy) + if flag(callHierarchy) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-call-hierarchy-plugin ^>= 1.0 cpp-options: -Dhls_callHierarchy @@ -254,22 +254,22 @@ common haddockComments cpp-options: -Dhls_haddockComments common eval - if flag(eval) + if flag(eval) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-eval-plugin ^>= 1.2 cpp-options: -Dhls_eval common importLens - if flag(importLens) + if flag(importLens) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-explicit-imports-plugin ^>= 1.1 cpp-options: -Dhls_importLens common refineImports - if flag(refineImports) + if flag(refineImports) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-refine-imports-plugin ^>=1.0 cpp-options: -Dhls_refineImports common rename - if flag(rename) + if flag(rename) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-rename-plugin ^>= 1.0 cpp-options: -Dhls_rename @@ -284,7 +284,7 @@ common tactic cpp-options: -Dhls_tactic common hlint - if flag(hlint) + if flag(hlint) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-hlint-plugin ^>= 1.0 cpp-options: -Dhls_hlint @@ -294,12 +294,12 @@ common stan cpp-options: -Dhls_stan common moduleName - if flag(moduleName) + if flag(moduleName) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-module-name-plugin ^>= 1.0 cpp-options: -Dhls_moduleName common pragmas - if flag(pragmas) + if flag(pragmas) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-pragmas-plugin ^>= 1.0 cpp-options: -Dhls_pragmas @@ -309,54 +309,54 @@ common splice cpp-options: -Dhls_splice common alternateNumberFormat - if flag(alternateNumberFormat) + if flag(alternateNumberFormat) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-alternate-number-format-plugin ^>= 1.1 cpp-options: -Dhls_alternateNumberFormat common qualifyImportedNames - if flag(qualifyImportedNames) + if flag(qualifyImportedNames) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-qualify-imported-names-plugin ^>=1.0 cpp-options: -Dhls_qualifyImportedNames common codeRange - if flag(codeRange) + if flag(codeRange) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-code-range-plugin ^>= 1.0 cpp-options: -Dhls_codeRange common changeTypeSignature - if flag(changeTypeSignature) + if flag(changeTypeSignature) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-change-type-signature-plugin ^>= 1.0 cpp-options: -Dhls_changeTypeSignature common gadt - if flag(gadt) + if flag(gadt) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-gadt-plugin ^>= 1.0 cpp-options: -Dhls_gadt common explicitFixity - if flag(explicitFixity) + if flag(explicitFixity) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-explicit-fixity-plugin ^>= 1.0 cpp-options: -DexplicitFixity -- formatters common floskell - if flag(floskell) + if flag(floskell) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-floskell-plugin ^>= 1.0 cpp-options: -Dhls_floskell common fourmolu - if flag(fourmolu) + if flag(fourmolu) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-fourmolu-plugin ^>= 1.0 cpp-options: -Dhls_fourmolu common ormolu - if flag(ormolu) + if flag(ormolu) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-ormolu-plugin ^>= 1.0 cpp-options: -Dhls_ormolu common stylishHaskell - if flag(stylishHaskell) + if flag(stylishHaskell) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-stylish-haskell-plugin ^>= 1.0 cpp-options: -Dhls_stylishHaskell @@ -366,7 +366,7 @@ common brittany cpp-options: -Dhls_brittany common refactor - if flag(refactor) + if flag(refactor) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-refactor-plugin ^>= 1.0 cpp-options: -Dhls_refactor diff --git a/hie-compat/hie-compat.cabal b/hie-compat/hie-compat.cabal index 8166d2fcd2..7e3aee55a6 100644 --- a/hie-compat/hie-compat.cabal +++ b/hie-compat/hie-compat.cabal @@ -52,3 +52,5 @@ library hs-source-dirs: src-ghc90 src-reexport-ghc9 if (impl(ghc >= 9.2) && impl(ghc < 9.3)) hs-source-dirs: src-ghc92 src-reexport-ghc9 + if (impl(ghc >= 9.4) && impl(ghc < 9.5)) + hs-source-dirs: src-reexport-ghc92 diff --git a/hie-compat/src-reexport-ghc92/Compat/HieAst.hs b/hie-compat/src-reexport-ghc92/Compat/HieAst.hs new file mode 100644 index 0000000000..240dc4da49 --- /dev/null +++ b/hie-compat/src-reexport-ghc92/Compat/HieAst.hs @@ -0,0 +1,3 @@ +module Compat.HieAst + ( module GHC.Iface.Ext.Ast ) where +import GHC.Iface.Ext.Ast diff --git a/hie-compat/src-reexport-ghc92/Compat/HieBin.hs b/hie-compat/src-reexport-ghc92/Compat/HieBin.hs new file mode 100644 index 0000000000..254e1db6d3 --- /dev/null +++ b/hie-compat/src-reexport-ghc92/Compat/HieBin.hs @@ -0,0 +1,8 @@ +{- +Binary serialization for .hie files. +-} + +module Compat.HieBin ( module GHC.Iface.Ext.Binary) +where + +import GHC.Iface.Ext.Binary diff --git a/hie-compat/src-reexport-ghc92/Compat/HieDebug.hs b/hie-compat/src-reexport-ghc92/Compat/HieDebug.hs new file mode 100644 index 0000000000..872da67c2b --- /dev/null +++ b/hie-compat/src-reexport-ghc92/Compat/HieDebug.hs @@ -0,0 +1,10 @@ +module Compat.HieDebug + ( module GHC.Iface.Ext.Debug + , ppHie ) where +import GHC.Iface.Ext.Debug + +import GHC.Iface.Ext.Types (HieAST) +import GHC.Utils.Outputable (Outputable(ppr), SDoc) + +ppHie :: Outputable a => HieAST a -> SDoc +ppHie = ppr diff --git a/hie-compat/src-reexport-ghc92/Compat/HieTypes.hs b/hie-compat/src-reexport-ghc92/Compat/HieTypes.hs new file mode 100644 index 0000000000..36bb86abeb --- /dev/null +++ b/hie-compat/src-reexport-ghc92/Compat/HieTypes.hs @@ -0,0 +1,3 @@ +module Compat.HieTypes + ( module GHC.Iface.Ext.Types ) where +import GHC.Iface.Ext.Types diff --git a/hie-compat/src-reexport-ghc92/Compat/HieUtils.hs b/hie-compat/src-reexport-ghc92/Compat/HieUtils.hs new file mode 100644 index 0000000000..204a312039 --- /dev/null +++ b/hie-compat/src-reexport-ghc92/Compat/HieUtils.hs @@ -0,0 +1,3 @@ +module Compat.HieUtils + ( module GHC.Iface.Ext.Utils ) where +import GHC.Iface.Ext.Utils diff --git a/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal b/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal index 372fd8c3d2..b7eee39ce0 100644 --- a/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal +++ b/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal @@ -18,6 +18,10 @@ extra-source-files: test/testdata/*.yaml library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.AlternateNumberFormat, Ide.Plugin.Conversion other-modules: Ide.Plugin.Literals hs-source-dirs: src @@ -47,6 +51,10 @@ library RecordWildCards test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal b/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal index 8865f07951..dc5cd8e398 100644 --- a/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal +++ b/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal @@ -16,6 +16,10 @@ extra-source-files: test/testdata/*.hs library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.CallHierarchy other-modules: Ide.Plugin.CallHierarchy.Internal @@ -43,6 +47,10 @@ library default-extensions: DataKinds test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-change-type-signature-plugin/hls-change-type-signature-plugin.cabal b/plugins/hls-change-type-signature-plugin/hls-change-type-signature-plugin.cabal index 5f9812c30e..f93f303788 100644 --- a/plugins/hls-change-type-signature-plugin/hls-change-type-signature-plugin.cabal +++ b/plugins/hls-change-type-signature-plugin/hls-change-type-signature-plugin.cabal @@ -19,6 +19,10 @@ extra-source-files: test/testdata/*.yaml library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.ChangeTypeSignature hs-source-dirs: src build-depends: @@ -46,6 +50,10 @@ library test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-class-plugin/hls-class-plugin.cabal b/plugins/hls-class-plugin/hls-class-plugin.cabal index c0ad09f305..245522a9cd 100644 --- a/plugins/hls-class-plugin/hls-class-plugin.cabal +++ b/plugins/hls-class-plugin/hls-class-plugin.cabal @@ -21,6 +21,10 @@ extra-source-files: test/testdata/*.hs library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.Class other-modules: Ide.Plugin.Class.CodeAction , Ide.Plugin.Class.CodeLens @@ -58,6 +62,10 @@ library ghc-options: -Wall -Wno-unticked-promoted-constructors -Wno-name-shadowing test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal b/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal index d726b48ee8..3d50d0c764 100644 --- a/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal +++ b/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal @@ -21,6 +21,10 @@ extra-source-files: test/testdata/selection-range/*.txt library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.CodeRange Ide.Plugin.CodeRange.Rules @@ -48,6 +52,10 @@ library , vector test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-eval-plugin/hls-eval-plugin.cabal b/plugins/hls-eval-plugin/hls-eval-plugin.cabal index 676e0bf732..a016aade1e 100644 --- a/plugins/hls-eval-plugin/hls-eval-plugin.cabal +++ b/plugins/hls-eval-plugin/hls-eval-plugin.cabal @@ -37,6 +37,10 @@ source-repository head location: https://github.com/haskell/haskell-language-server library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.Eval Ide.Plugin.Eval.Types @@ -97,6 +101,10 @@ library TypeOperators test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal b/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal index 087c1466b6..dc865f2c12 100644 --- a/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal +++ b/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal @@ -16,6 +16,10 @@ extra-source-files: test/testdata/*.hs library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.ExplicitFixity hs-source-dirs: src @@ -39,6 +43,10 @@ library default-extensions: DataKinds test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-explicit-imports-plugin/hls-explicit-imports-plugin.cabal b/plugins/hls-explicit-imports-plugin/hls-explicit-imports-plugin.cabal index f94922fd15..fb73ff894f 100644 --- a/plugins/hls-explicit-imports-plugin/hls-explicit-imports-plugin.cabal +++ b/plugins/hls-explicit-imports-plugin/hls-explicit-imports-plugin.cabal @@ -16,6 +16,10 @@ extra-source-files: test/testdata/*.yaml library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.ExplicitImports hs-source-dirs: src build-depends: @@ -37,6 +41,10 @@ library TypeOperators test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-floskell-plugin/hls-floskell-plugin.cabal b/plugins/hls-floskell-plugin/hls-floskell-plugin.cabal index 8416ccbc77..e13fa98e4a 100644 --- a/plugins/hls-floskell-plugin/hls-floskell-plugin.cabal +++ b/plugins/hls-floskell-plugin/hls-floskell-plugin.cabal @@ -17,6 +17,10 @@ extra-source-files: test/testdata/**/*.hs library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.Floskell hs-source-dirs: src build-depends: @@ -31,6 +35,10 @@ library default-language: Haskell2010 test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal b/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal index bb4e0b687a..e84cdccf7a 100644 --- a/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal +++ b/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal @@ -23,6 +23,10 @@ source-repository head location: git://github.com/haskell/haskell-language-server.git library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.Fourmolu , Ide.Plugin.Fourmolu.Shim @@ -44,6 +48,10 @@ library default-language: Haskell2010 test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-gadt-plugin/hls-gadt-plugin.cabal b/plugins/hls-gadt-plugin/hls-gadt-plugin.cabal index f109ea05d3..47ff630eed 100644 --- a/plugins/hls-gadt-plugin/hls-gadt-plugin.cabal +++ b/plugins/hls-gadt-plugin/hls-gadt-plugin.cabal @@ -16,6 +16,10 @@ extra-source-files: test/testdata/*.hs library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.GADT other-modules: Ide.Plugin.GHC @@ -46,6 +50,10 @@ library default-extensions: DataKinds test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-hlint-plugin/hls-hlint-plugin.cabal b/plugins/hls-hlint-plugin/hls-hlint-plugin.cabal index 82f60839e6..5fca22d6e0 100644 --- a/plugins/hls-hlint-plugin/hls-hlint-plugin.cabal +++ b/plugins/hls-hlint-plugin/hls-hlint-plugin.cabal @@ -26,6 +26,10 @@ flag pedantic manual: True library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.Hlint hs-source-dirs: src build-depends: @@ -73,6 +77,10 @@ library TypeOperators test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal b/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal index 1290ab75bf..08c17e2349 100644 --- a/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal +++ b/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal @@ -20,6 +20,10 @@ extra-source-files: test/testdata/**/*.project library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.ModuleName hs-source-dirs: src build-depends: @@ -37,6 +41,10 @@ library default-language: Haskell2010 test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-ormolu-plugin/hls-ormolu-plugin.cabal b/plugins/hls-ormolu-plugin/hls-ormolu-plugin.cabal index 67abe1c090..49bfb4959b 100644 --- a/plugins/hls-ormolu-plugin/hls-ormolu-plugin.cabal +++ b/plugins/hls-ormolu-plugin/hls-ormolu-plugin.cabal @@ -17,6 +17,10 @@ extra-source-files: test/testdata/**/*.hs library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.Ormolu hs-source-dirs: src build-depends: @@ -34,6 +38,10 @@ library default-language: Haskell2010 test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal b/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal index ba29d7a1cc..76f64083bd 100644 --- a/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal +++ b/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal @@ -18,6 +18,10 @@ extra-source-files: test/testdata/*.yaml library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.Pragmas hs-source-dirs: src build-depends: @@ -37,6 +41,10 @@ library default-language: Haskell2010 test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-qualify-imported-names-plugin/hls-qualify-imported-names-plugin.cabal b/plugins/hls-qualify-imported-names-plugin/hls-qualify-imported-names-plugin.cabal index 2ba2a6da91..8990e05f09 100644 --- a/plugins/hls-qualify-imported-names-plugin/hls-qualify-imported-names-plugin.cabal +++ b/plugins/hls-qualify-imported-names-plugin/hls-qualify-imported-names-plugin.cabal @@ -18,6 +18,10 @@ extra-source-files: test/data/*.yaml library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.QualifyImportedNames hs-source-dirs: src build-depends: @@ -41,6 +45,10 @@ library TypeOperators test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal b/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal index da2d1683c1..d848e77bbb 100644 --- a/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal +++ b/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal @@ -18,6 +18,10 @@ extra-source-files: test/data/**/*.yaml library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Development.IDE.GHC.ExactPrint Development.IDE.GHC.Compat.ExactPrint Development.IDE.Plugin.CodeAction @@ -81,6 +85,10 @@ library default-language: Haskell2010 test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Compat/ExactPrint.hs b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Compat/ExactPrint.hs index a80f251998..28e34ba379 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Compat/ExactPrint.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Compat/ExactPrint.hs @@ -2,6 +2,9 @@ -- multiple ghc-exactprint versions, accepting that anything more ambitious is -- pretty much impossible with the GHC 9.2 redesign of ghc-exactprint module Development.IDE.GHC.Compat.ExactPrint +#if MIN_VERSION_ghc(9,3,0) + ( ) where +#else ( ExactPrint , exactPrint , makeDeltaAst @@ -31,3 +34,5 @@ pattern Annotated {astA, annsA} <- (Retrie.astA &&& Retrie.annsA -> (astA, annsA pattern Annotated :: ast -> ApiAnns -> Retrie.Annotated ast pattern Annotated {astA, annsA} <- ((,()) . Retrie.astA -> (astA, annsA)) #endif + +#endif diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Dump.hs b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Dump.hs index abaaa81cfb..cde3f79c48 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Dump.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Dump.hs @@ -36,7 +36,11 @@ showAstDataHtml a0 = html $ li (showAstDataHtml' a0), li (nested "Raw" $ pre $ showAstData NoBlankSrcSpan NoBlankEpAnnotations a0) #else - li (nested "Raw" $ pre $ showAstData NoBlankSrcSpan a0) + li (nested "Raw" $ pre $ showAstData NoBlankSrcSpan +#if MIN_VERSION_ghc(9,3,0) + NoBlankEpAnnotations +#endif + a0) #endif ]) where @@ -49,7 +53,7 @@ showAstDataHtml a0 = html $ li = tag "li" caret x = tag' [("class", text "caret")] "span" "" <+> x nested foo cts -#if MIN_VERSION_ghc(9,2,1) +#if MIN_VERSION_ghc(9,2,1) && !MIN_VERSION_ghc(9,3,0) | cts == empty = foo #endif | otherwise = foo $$ (caret $ ul cts) diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs index a0d8ce135e..d56b513a79 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs @@ -3,6 +3,9 @@ -- | This module hosts various abstractions and utility functions to work with ghc-exactprint. module Development.IDE.GHC.ExactPrint +#if MIN_VERSION_ghc(9,3,0) + ( ) where +#else ( Graft(..), graftDecls, graftDeclsWithM, @@ -665,3 +668,5 @@ isCommaAnn :: TrailingAnn -> Bool isCommaAnn AddCommaAnn{} = True isCommaAnn _ = False #endif + +#endif diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index d883e84e89..01c3b555c1 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -132,14 +132,16 @@ iePluginDescriptor :: Recorder (WithPriority E.Log) -> PluginId -> PluginDescrip iePluginDescriptor recorder plId = let old = mkGhcideCAsPlugin [ - wrap suggestExtendImport - , wrap suggestImportDisambiguation - , wrap suggestNewOrExtendImportForClassMethod - , wrap suggestNewImport + wrap suggestExportUnusedTopBinding , wrap suggestModuleTypo , wrap suggestFixConstructorImport + , wrap suggestNewImport +#if !MIN_VERSION_ghc(9,3,0) + , wrap suggestExtendImport + , wrap suggestImportDisambiguation + , wrap suggestNewOrExtendImportForClassMethod , wrap suggestHideShadow - , wrap suggestExportUnusedTopBinding +#endif ] plId in mkExactprintPluginDescriptor recorder $ old {pluginHandlers = pluginHandlers old <> mkPluginHandler STextDocumentCodeAction codeAction } @@ -149,9 +151,11 @@ typeSigsPluginDescriptor recorder plId = mkExactprintPluginDescriptor recorder $ mkGhcideCAsPlugin [ wrap $ suggestSignature True , wrap suggestFillTypeWildcard - , wrap removeRedundantConstraints , wrap suggestAddTypeAnnotationToSatisfyContraints +#if !MIN_VERSION_ghc(9,3,0) + , wrap removeRedundantConstraints , wrap suggestConstraint +#endif ] plId @@ -159,7 +163,9 @@ bindingsPluginDescriptor :: Recorder (WithPriority E.Log) -> PluginId -> Plugin bindingsPluginDescriptor recorder plId = mkExactprintPluginDescriptor recorder $ mkGhcideCAsPlugin [ wrap suggestReplaceIdentifier +#if !MIN_VERSION_ghc(9,3,0) , wrap suggestImplicitParameter +#endif , wrap suggestNewDefinition , wrap suggestDeleteUnusedBinding ] @@ -296,7 +302,11 @@ findSigOfBind range bind = msum [findSigOfBinds range (grhssLocalBinds grhs) -- where clause , do +#if MIN_VERSION_ghc(9,3,0) + grhs <- findDeclContainingLoc (_start range) (grhssGRHSs grhs) +#else grhs <- findDeclContainingLoc (_start range) (map reLocA $ grhssGRHSs grhs) +#endif case unLoc grhs of GRHS _ _ bd -> findSigOfExpr (unLoc bd) ] @@ -305,7 +315,11 @@ findSigOfBind range bind = findSigOfExpr :: HsExpr p -> Maybe (Sig p) findSigOfExpr = go where +#if MIN_VERSION_ghc(9,3,0) + go (HsLet _ _ binds _ _) = findSigOfBinds range binds +#else go (HsLet _ binds _) = findSigOfBinds range binds +#endif go (HsDo _ _ stmts) = do stmtlr <- unLoc <$> findDeclContainingLoc (_start range) (unLoc stmts) case stmtlr of @@ -355,6 +369,7 @@ findDeclContainingLoc loc = find (\(L l _) -> loc `isInsideSrcSpan` locA l) -- imported from ‘Data.ByteString’ at B.hs:6:1-22 -- imported from ‘Data.ByteString.Lazy’ at B.hs:8:1-27 -- imported from ‘Data.Text’ at B.hs:7:1-16 +#if !MIN_VERSION_ghc(9,3,0) suggestHideShadow :: Annotated ParsedSource -> T.Text -> Maybe TcModuleResult -> Maybe HieAstResult -> Diagnostic -> [(T.Text, [Either TextEdit Rewrite])] suggestHideShadow ps fileContents mTcM mHar Diagnostic {_message, _range} | Just [identifier, modName, s] <- @@ -386,6 +401,7 @@ suggestHideShadow ps fileContents mTcM mHar Diagnostic {_message, _range} then maybeToList $ (\(_, te) -> (title, [Left te])) <$> newImportToEdit (hideImplicitPreludeSymbol identifier) ps fileContents else maybeToList $ (title,) . pure . pure . hideSymbol (T.unpack identifier) <$> mDecl | otherwise = [] +#endif findImportDeclByModuleName :: [LImportDecl GhcPs] -> String -> Maybe (LImportDecl GhcPs) findImportDeclByModuleName decls modName = flip find decls $ \case @@ -978,6 +994,7 @@ getIndentedGroupsBy pred inp = case dropWhile (not.pred) inp of indentation :: T.Text -> Int indentation = T.length . T.takeWhile isSpace +#if !MIN_VERSION_ghc(9,3,0) suggestExtendImport :: ExportsMap -> ParsedSource -> Diagnostic -> [(T.Text, CodeActionKind, Rewrite)] suggestExtendImport exportsMap (L _ HsModule {hsmodImports}) Diagnostic{_range=_range,..} | Just [binding, mod, srcspan] <- @@ -1025,6 +1042,7 @@ suggestExtendImport exportsMap (L _ HsModule {hsmodImports}) Diagnostic{_range=_ , parent = Nothing , isDatacon = False , moduleNameText = mod} +#endif data HidingMode = HideOthers [ModuleTarget] @@ -1050,6 +1068,7 @@ oneAndOthers = go isPreludeImplicit :: DynFlags -> Bool isPreludeImplicit = xopt Lang.ImplicitPrelude +#if !MIN_VERSION_ghc(9,3,0) -- | Suggests disambiguation for ambiguous symbols. suggestImportDisambiguation :: DynFlags -> @@ -1141,6 +1160,7 @@ suggestImportDisambiguation df (Just txt) ps fileContents diag@Diagnostic {..} <> "." <> symbol suggestImportDisambiguation _ _ _ _ _ = [] +#endif occursUnqualified :: T.Text -> ImportDecl GhcPs -> Bool occursUnqualified symbol ImportDecl{..} @@ -1163,6 +1183,7 @@ targetModuleName (ExistingImp (L _ ImportDecl{..} :| _)) = targetModuleName (ExistingImp _) = error "Cannot happen!" +#if !MIN_VERSION_ghc(9,3,0) disambiguateSymbol :: Annotated ParsedSource -> T.Text -> @@ -1195,6 +1216,8 @@ disambiguateSymbol ps fileContents Diagnostic {..} (T.unpack -> symbol) = \case liftParseAST @RdrName df $ T.unpack $ printOutputable $ L (mkGeneralSrcSpan "") rdr ] +#endif + findImportDeclByRange :: [LImportDecl GhcPs] -> Range -> Maybe (LImportDecl GhcPs) findImportDeclByRange xs range = find (\(L (locA -> l) _)-> srcSpanToRange l == Just range) xs @@ -1212,6 +1235,7 @@ suggestFixConstructorImport Diagnostic{_range=_range,..} in [("Fix import of " <> fixedImport, TextEdit _range fixedImport)] | otherwise = [] +#if !MIN_VERSION_ghc(9,3,0) -- | Suggests a constraint for a declaration for which a constraint is missing. suggestConstraint :: DynFlags -> ParsedSource -> Diagnostic -> [(T.Text, Rewrite)] suggestConstraint df (makeDeltaAst -> parsedModule) diag@Diagnostic {..} @@ -1293,10 +1317,12 @@ suggestImplicitParameter (L _ HsModule {hsmodDecls}) Diagnostic {_message, _rang [( "Add " <> implicitT <> " to the context of " <> T.pack (printRdrName funId) , appendConstraint (T.unpack implicitT) hsib_body)] | otherwise = [] +#endif findTypeSignatureName :: T.Text -> Maybe T.Text findTypeSignatureName t = matchRegexUnifySpaces t "([^ ]+) :: " <&> head +#if !MIN_VERSION_ghc(9,3,0) -- | Suggests a constraint for a type signature with any number of existing constraints. suggestFunctionConstraint :: DynFlags -> ParsedSource -> Diagnostic -> T.Text -> [(T.Text, Rewrite)] @@ -1443,6 +1469,7 @@ suggestNewOrExtendImportForClassMethod packageExportsMap ps fileContents Diagnos ] <> [(quickFixImportKind "new.all", newImportAll moduleNameText)] | otherwise -> [] +#endif suggestNewImport :: ExportsMap -> Annotated ParsedSource -> T.Text -> Diagnostic -> [(T.Text, CodeActionKind, TextEdit)] suggestNewImport packageExportsMap ps fileContents Diagnostic{_message} diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs index 59c0ac868f..ef5c7b623a 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs @@ -26,8 +26,10 @@ import Development.IDE.Core.Shake import Development.IDE.GHC.Compat import Development.IDE.GHC.Compat.ExactPrint import Development.IDE.GHC.ExactPrint +#if !MIN_VERSION_ghc(9,3,0) import Development.IDE.Plugin.CodeAction.ExactPrint (Rewrite, rewriteToEdit) +#endif import Development.IDE.Plugin.TypeLenses (GetGlobalBindingTypeSigs (GetGlobalBindingTypeSigs), GlobalBindingTypeSigsResult) import Development.IDE.Spans.LocalBindings (Bindings) @@ -70,7 +72,9 @@ runGhcideCodeAction state (CodeActionParams _ _ (TextDocumentIdentifier uri) _ra Just (_, txt) -> pure txt _ -> pure Nothing caaDf <- onceIO $ fmap (ms_hspp_opts . pm_mod_summary) <$> caaParsedModule +#if !MIN_VERSION_ghc(9,3,0) caaAnnSource <- onceIO $ runRule GetAnnotatedParsedSource +#endif caaTmr <- onceIO $ runRule TypeCheck caaHar <- onceIO $ runRule GetHieAst caaBindings <- onceIO $ runRule GetBindings @@ -113,6 +117,7 @@ class ToTextEdit a where instance ToTextEdit TextEdit where toTextEdit _ = pure . pure +#if !MIN_VERSION_ghc(9,3,0) instance ToTextEdit Rewrite where toTextEdit CodeActionArgs {..} rw = fmap (fromMaybe []) $ runMaybeT $ do @@ -124,6 +129,7 @@ instance ToTextEdit Rewrite where let r = rewriteToEdit df rw #endif pure $ fromRight [] r +#endif instance ToTextEdit a => ToTextEdit [a] where toTextEdit caa = foldMap (toTextEdit caa) @@ -143,7 +149,11 @@ data CodeActionArgs = CodeActionArgs caaParsedModule :: IO (Maybe ParsedModule), caaContents :: IO (Maybe T.Text), caaDf :: IO (Maybe DynFlags), +#if MIN_VERSION_ghc(9,3,0) + caaAnnSource :: IO (Maybe ParsedSource), +#else caaAnnSource :: IO (Maybe (Annotated ParsedSource)), +#endif caaTmr :: IO (Maybe TcModuleResult), caaHar :: IO (Maybe HieAstResult), caaBindings :: IO (Maybe Bindings), @@ -212,10 +222,17 @@ toCodeAction3 get f = ReaderT $ \caa -> get caa >>= flip runReaderT caa . toCode -- | this instance returns a delta AST, useful for exactprint transforms instance ToCodeAction r => ToCodeAction (ParsedSource -> r) where +#if !MIN_VERSION_ghc(9,3,0) toCodeAction f = ReaderT $ \caa@CodeActionArgs {caaAnnSource = x} -> x >>= \case Just s -> flip runReaderT caa . toCodeAction . f . astA $ s _ -> pure [] +#else + toCodeAction f = ReaderT $ \caa@CodeActionArgs {caaParsedModule = x} -> + x >>= \case + Just s -> flip runReaderT caa . toCodeAction . f . pm_parsed_source $ s + _ -> pure [] +#endif instance ToCodeAction r => ToCodeAction (ExportsMap -> r) where toCodeAction = toCodeAction3 caaExportsMap @@ -244,11 +261,13 @@ instance ToCodeAction r => ToCodeAction (Maybe DynFlags -> r) where instance ToCodeAction r => ToCodeAction (DynFlags -> r) where toCodeAction = toCodeAction2 caaDf +#if !MIN_VERSION_ghc(9,3,0) instance ToCodeAction r => ToCodeAction (Maybe (Annotated ParsedSource) -> r) where toCodeAction = toCodeAction1 caaAnnSource instance ToCodeAction r => ToCodeAction (Annotated ParsedSource -> r) where toCodeAction = toCodeAction2 caaAnnSource +#endif instance ToCodeAction r => ToCodeAction (Maybe TcModuleResult -> r) where toCodeAction = toCodeAction1 caaTmr diff --git a/plugins/hls-refine-imports-plugin/hls-refine-imports-plugin.cabal b/plugins/hls-refine-imports-plugin/hls-refine-imports-plugin.cabal index 98d36465e4..ebbe0b271c 100644 --- a/plugins/hls-refine-imports-plugin/hls-refine-imports-plugin.cabal +++ b/plugins/hls-refine-imports-plugin/hls-refine-imports-plugin.cabal @@ -16,6 +16,10 @@ extra-source-files: test/testdata/*.yaml library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.RefineImports hs-source-dirs: src build-depends: @@ -38,6 +42,10 @@ library TypeOperators test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-rename-plugin/hls-rename-plugin.cabal b/plugins/hls-rename-plugin/hls-rename-plugin.cabal index e0c295dc12..43f8397fbe 100644 --- a/plugins/hls-rename-plugin/hls-rename-plugin.cabal +++ b/plugins/hls-rename-plugin/hls-rename-plugin.cabal @@ -17,6 +17,10 @@ extra-source-files: test/testdata/*.yaml library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.Rename hs-source-dirs: src build-depends: @@ -41,6 +45,10 @@ library default-language: Haskell2010 test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-retrie-plugin/hls-retrie-plugin.cabal b/plugins/hls-retrie-plugin/hls-retrie-plugin.cabal index 3313ffe610..c86bacbb20 100644 --- a/plugins/hls-retrie-plugin/hls-retrie-plugin.cabal +++ b/plugins/hls-retrie-plugin/hls-retrie-plugin.cabal @@ -13,6 +13,10 @@ build-type: Simple extra-source-files: LICENSE library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.Retrie hs-source-dirs: src build-depends: diff --git a/plugins/hls-splice-plugin/hls-splice-plugin.cabal b/plugins/hls-splice-plugin/hls-splice-plugin.cabal index 4dc8f7fdd9..0ea73506de 100644 --- a/plugins/hls-splice-plugin/hls-splice-plugin.cabal +++ b/plugins/hls-splice-plugin/hls-splice-plugin.cabal @@ -23,6 +23,10 @@ extra-source-files: test/testdata/*.yaml library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.Splice Ide.Plugin.Splice.Types @@ -56,6 +60,10 @@ library TypeOperators test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-stan-plugin/hls-stan-plugin.cabal b/plugins/hls-stan-plugin/hls-stan-plugin.cabal index 0f63a01a77..5c149e18b4 100644 --- a/plugins/hls-stan-plugin/hls-stan-plugin.cabal +++ b/plugins/hls-stan-plugin/hls-stan-plugin.cabal @@ -76,4 +76,4 @@ test-suite test , text default-extensions: NamedFieldPuns - OverloadedStrings \ No newline at end of file + OverloadedStrings diff --git a/plugins/hls-stylish-haskell-plugin/hls-stylish-haskell-plugin.cabal b/plugins/hls-stylish-haskell-plugin/hls-stylish-haskell-plugin.cabal index c161f888bf..f7b229b4e7 100644 --- a/plugins/hls-stylish-haskell-plugin/hls-stylish-haskell-plugin.cabal +++ b/plugins/hls-stylish-haskell-plugin/hls-stylish-haskell-plugin.cabal @@ -16,6 +16,10 @@ extra-source-files: test/testdata/*.hs library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True exposed-modules: Ide.Plugin.StylishHaskell hs-source-dirs: src build-depends: @@ -33,6 +37,10 @@ library default-language: Haskell2010 test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal b/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal index 14faae448e..bbb7e9e104 100644 --- a/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal +++ b/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal @@ -25,6 +25,10 @@ flag pedantic manual: True library + if impl(ghc >= 9.3) + buildable: False + else + buildable: True hs-source-dirs: src exposed-modules: Ide.Plugin.Tactic @@ -127,6 +131,10 @@ library ViewPatterns test-suite tests + if impl(ghc >= 9.3) + buildable: False + else + buildable: True type: exitcode-stdio-1.0 main-is: Main.hs other-modules: From 75cfc3aa1e8b7284d0518130fca6bb6354747dff Mon Sep 17 00:00:00 2001 From: Guillaume Bouchard Date: Tue, 16 Aug 2022 17:51:21 +0200 Subject: [PATCH 077/213] chore: add the nix stuffs for GHC 9.4 I've just added naively the different nix lines. The configuration file comes from a copy of the one for 9.2. With that, we can open a shell with `nix develop .\#haskell-language-server-941-dev` and type `cabal build`. (cherry picked from commit 48084ab95729d48d470888d8fde807a1d7865860) --- configuration-ghc-94.nix | 42 ++++++++++++++++++++++++++++++++++++++++ flake.nix | 6 ++++++ 2 files changed, 48 insertions(+) create mode 100644 configuration-ghc-94.nix diff --git a/configuration-ghc-94.nix b/configuration-ghc-94.nix new file mode 100644 index 0000000000..3790e182a7 --- /dev/null +++ b/configuration-ghc-94.nix @@ -0,0 +1,42 @@ +{ pkgs, inputs }: + +let + disabledPlugins = [ + "hls-hlint-plugin" + # That one is not technically a plugin, but by putting it in this list, we + # get it removed from the top level list of requirement and it is not pull + # in the nix shell. + "shake-bench" + ]; + + hpkgsOverride = hself: hsuper: + with pkgs.haskell.lib; + { + hlsDisabledPlugins = disabledPlugins; + # YOLO + mkDerivation = args: + hsuper.mkDerivation (args // { + jailbreak = true; + doCheck = false; + }); + } // (builtins.mapAttrs (_: drv: disableLibraryProfiling drv) { + # ptr-poker breaks on MacOS without SSE2 optimizations + # https://github.com/nikita-volkov/ptr-poker/issues/11 + ptr-poker = hself.callCabal2nix "ptr-poker" inputs.ptr-poker { }; + + ghc-exactprint = + hself.callCabal2nix "ghc-exactprint" inputs.ghc-exactprint-150 { }; + # Hlint is still broken + hlint = doJailbreak (hself.callCabal2nix "hlint" inputs.hlint { }); + + stylish-haskell = appendConfigureFlag hsuper.stylish-haskell "-fghc-lib"; + + # Re-generate HLS drv excluding some plugins + haskell-language-server = + hself.callCabal2nixWithOptions "haskell-language-server" ./. + (pkgs.lib.concatStringsSep " " [ "-fpedantic" "-f-hlint" ]) { }; + }); +in { + inherit disabledPlugins; + tweakHpkgs = hpkgs: hpkgs.extend hpkgsOverride; +} diff --git a/flake.nix b/flake.nix index 47dcc62798..5257bda8f8 100644 --- a/flake.nix +++ b/flake.nix @@ -215,6 +215,7 @@ ghc902Config = (import ./configuration-ghc-90.nix) { inherit pkgs inputs; }; ghc924Config = (import ./configuration-ghc-92.nix) { inherit pkgs inputs; }; + ghc941Config = (import ./configuration-ghc-94.nix) { inherit pkgs inputs; }; # GHC versions # While HLS still works fine with 8.10 GHCs, we only support the versions that are cached @@ -224,11 +225,13 @@ cases = { ghc902 = ghc902Config.tweakHpkgs (pkgs.hlsHpkgs "ghc902"); ghc924 = ghc924Config.tweakHpkgs (pkgs.hlsHpkgs "ghc924"); + ghc941 = ghc941Config.tweakHpkgs (pkgs.hlsHpkgs "ghc941"); }; in { default = cases."${ghcVersion}"; } // cases; ghc902 = supportedGHCs.ghc902; ghc924 = supportedGHCs.ghc924; + ghc941 = supportedGHCs.ghc941; ghcDefault = supportedGHCs.default; # For markdown support @@ -361,6 +364,7 @@ haskell-language-server-dev = mkDevShell ghcDefault "cabal.project"; haskell-language-server-902-dev = mkDevShell ghc902 "cabal.project"; haskell-language-server-924-dev = mkDevShell ghc924 "cabal.project"; + haskell-language-server-941-dev = mkDevShell ghc941 "cabal.project"; }; # Developement shell, haskell packages are also provided by nix @@ -368,12 +372,14 @@ haskell-language-server-dev-nix = mkDevShellWithNixDeps ghcDefault "cabal.project"; haskell-language-server-902-dev-nix = mkDevShellWithNixDeps ghc902 "cabal.project"; haskell-language-server-924-dev-nix = mkDevShellWithNixDeps ghc924 "cabal.project"; + haskell-language-server-941-dev-nix = mkDevShellWithNixDeps ghc941 "cabal.project"; }; allPackages = { haskell-language-server = mkExe ghcDefault; haskell-language-server-902 = mkExe ghc902; haskell-language-server-924 = mkExe ghc924; + haskell-language-server-941 = mkExe ghc941; }; devShells = simpleDevShells // nixDevShells // { From feeaa3c5c7c8d11d02200302ab7dea988597b040 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Wed, 31 Aug 2022 19:22:51 +0530 Subject: [PATCH 078/213] drop windows for 9.4.2 --- .github/workflows/test.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 059a804125..d541d644cf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -89,9 +89,6 @@ jobs: - os: ubuntu-latest ghc: '8.6.5' test: true - - os: windows-latest - ghc: '9.4.2' - test: true - os: windows-latest ghc: '9.2.4' test: true From 87133b039474c9c3ca3d379217a902d532587e6c Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Wed, 31 Aug 2022 21:37:09 +0530 Subject: [PATCH 079/213] Fix func test --- haskell-language-server.cabal | 1 + test/functional/Format.hs | 11 +++++++---- test/functional/FunctionalCodeAction.hs | 2 ++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index bfdd321674..fb73813911 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -520,6 +520,7 @@ test-suite func-test import: common-deps , warnings , pedantic + , refactor type: exitcode-stdio-1.0 default-language: Haskell2010 build-tool-depends: diff --git a/test/functional/Format.hs b/test/functional/Format.hs index 9b853d527e..ec4087ec95 100644 --- a/test/functional/Format.hs +++ b/test/functional/Format.hs @@ -47,10 +47,13 @@ providerTests = testGroup "formatting provider" [ testCase "respects none" $ runSessionWithConfig (formatConfig "none") hlsCommand fullCaps "test/testdata/format" $ do doc <- openDoc "Format.hs" "haskell" resp <- request STextDocumentFormatting $ DocumentFormattingParams Nothing doc (FormattingOptions 2 True Nothing Nothing Nothing) - liftIO $ resp ^. LSP.result @?= Left (ResponseError InvalidRequest - ("No plugin enabled for STextDocumentFormatting, available:\n" - <> "PluginId \"floskell\"\nPluginId \"fourmolu\"\nPluginId \"stylish-haskell\"\nPluginId \"brittany\"\nPluginId \"ormolu\"\n") - Nothing) + case resp ^. LSP.result of + result@(Left (ResponseError reason message Nothing)) -> case reason of + MethodNotFound -> pure () -- No formatter + InvalidRequest | "No plugin enabled for STextDocumentFormatting" `isPrefixOf` message -> pure () + _ -> assertFailure $ "strange response from formatting provider:" ++ show result + result -> assertFailure $ "strange response from formatting provider:" ++ show result + , requiresOrmoluPlugin . requiresFloskellPlugin $ testCase "can change on the fly" $ runSession hlsCommand fullCaps "test/testdata/format" $ do formattedOrmolu <- liftIO $ T.readFile "test/testdata/format/Format.ormolu.formatted.hs" diff --git a/test/functional/FunctionalCodeAction.hs b/test/functional/FunctionalCodeAction.hs index 91cb322e20..aa7bf9253b 100644 --- a/test/functional/FunctionalCodeAction.hs +++ b/test/functional/FunctionalCodeAction.hs @@ -24,6 +24,7 @@ import Test.Hls.Command tests :: TestTree tests = testGroup "code actions" [ +#if hls_refactor importTests , packageTests , redundantImportTests @@ -31,6 +32,7 @@ tests = testGroup "code actions" [ , signatureTests , typedHoleTests , unusedTermTests +#endif ] renameTests :: TestTree From 661b336883b74986f4845e9d376e36b4c8dd9a1c Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Thu, 1 Sep 2022 00:27:57 +0530 Subject: [PATCH 080/213] Disable plugin tests for 9.4 --- .github/workflows/test.yml | 50 +++++++++++++++++++------------------- test/functional/Format.hs | 5 ++-- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d541d644cf..faf0529644 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -161,103 +161,103 @@ jobs: HLS_WRAPPER_TEST_EXE: hls-wrapper run: cabal test wrapper-test --test-options="$TEST_OPTS --rerun-log-file .tasty-rerun-log-wrapper" || cabal test wrapper-test --test-options="$TEST_OPTS --rerun-log-file .tasty-rerun-log-wrapper" || cabal test wrapper-test --test-options="$TEST_OPTS --rerun-log-file .tasty-rerun-log-wrapper" - - if: matrix.test && matrix.ghc != '9.2.4' + - if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2' name: Test hls-brittany-plugin run: cabal test hls-brittany-plugin --test-options="$TEST_OPTS" || cabal test hls-brittany-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-brittany-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-refactor-plugin run: cabal test hls-refactor-plugin --test-options="$TEST_OPTS" || cabal test hls-refactor-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-refactor-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-floskell-plugin run: cabal test hls-floskell-plugin --test-options="$TEST_OPTS" || cabal test hls-floskell-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-floskell-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-class-plugin run: cabal test hls-class-plugin --test-options="$TEST_OPTS" || cabal test hls-class-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-class-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-pragmas-plugin run: cabal test hls-pragmas-plugin --test-options="$TEST_OPTS" || cabal test hls-pragmas-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-pragmas-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-eval-plugin run: cabal test hls-eval-plugin --test-options="$TEST_OPTS" || cabal test hls-eval-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-eval-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.2.4' + - if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2' name: Test hls-haddock-comments-plugin run: cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS" || cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.2.4' + - if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2' name: Test hls-splice-plugin run: cabal test hls-splice-plugin --test-options="$TEST_OPTS" || cabal test hls-splice-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-splice-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-stylish-haskell-plugin run: cabal test hls-stylish-haskell-plugin --test-options="$TEST_OPTS" || cabal test hls-stylish-haskell-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-stylish-haskell-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-ormolu-plugin run: cabal test hls-ormolu-plugin --test-options="$TEST_OPTS" || cabal test hls-ormolu-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-ormolu-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-fourmolu-plugin run: cabal test hls-fourmolu-plugin --test-options="$TEST_OPTS" || cabal test hls-fourmolu-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-fourmolu-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.2.4' + - if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2' name: Test hls-tactics-plugin test suite run: cabal test hls-tactics-plugin --test-options="$TEST_OPTS" || cabal test hls-tactics-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-tactics-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-refine-imports-plugin test suite run: cabal test hls-refine-imports-plugin --test-options="$TEST_OPTS" || cabal test hls-refine-imports-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-refine-imports-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-explicit-imports-plugin test suite run: cabal test hls-explicit-imports-plugin --test-options="$TEST_OPTS" || cabal test hls-explicit-imports-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-explicit-imports-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-call-hierarchy-plugin test suite run: cabal test hls-call-hierarchy-plugin --test-options="$TEST_OPTS" || cabal test hls-call-hierarchy-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-call-hierarchy-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.os != 'windows-latest' + - if: matrix.test && matrix.os != 'windows-latest' && matrix.ghc != '9.4.2' name: Test hls-rename-plugin test suite run: cabal test hls-rename-plugin --test-options="$TEST_OPTS" || cabal test hls-rename-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-rename-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-hlint-plugin test suite run: cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-hlint-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.0.1' && matrix.ghc != '9.0.2' && matrix.ghc != '9.2.4' + - if: matrix.test && matrix.ghc != '9.0.1' && matrix.ghc != '9.0.2' && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2' name: Test hls-stan-plugin test suite run: cabal test hls-stan-plugin --test-options="$TEST_OPTS" || cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-stan-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-module-name-plugin test suite run: cabal test hls-module-name-plugin --test-options="$TEST_OPTS" || cabal test hls-module-name-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-module-name-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-alternate-number-format-plugin test suite run: cabal test hls-alternate-number-format-plugin --test-options="$TEST_OPTS" || cabal test hls-alternate-number-format-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-alternate-number-format-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-qualify-imported-names-plugin test suite run: cabal test hls-qualify-imported-names-plugin --test-options="$TEST_OPTS" || cabal test hls-qualify-imported-names-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-qualify-imported-names-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-code-range-plugin test suite run: cabal test hls-code-range-plugin --test-options="$TEST_OPTS" || cabal test hls-code-range-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-code-range-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-change-type-signature test suite run: cabal test hls-change-type-signature-plugin --test-options="$TEST_OPTS" || cabal test hls-change-type-signature-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-change-type-signature-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-gadt-plugin test suit run: cabal test hls-gadt-plugin --test-options="$TEST_OPTS" || cabal test hls-gadt-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-gadt-plugin --test-options="$TEST_OPTS" - - if: matrix.test + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-explicit-fixity-plugin test suite run: cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" || cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" diff --git a/test/functional/Format.hs b/test/functional/Format.hs index ec4087ec95..b3829c3a9f 100644 --- a/test/functional/Format.hs +++ b/test/functional/Format.hs @@ -8,6 +8,7 @@ import Data.Aeson import qualified Data.ByteString.Lazy as BS import qualified Data.Text.Encoding as T import qualified Data.Text.IO as T +import qualified Data.Text as T import Language.LSP.Test import Language.LSP.Types import qualified Language.LSP.Types.Lens as LSP @@ -47,10 +48,10 @@ providerTests = testGroup "formatting provider" [ testCase "respects none" $ runSessionWithConfig (formatConfig "none") hlsCommand fullCaps "test/testdata/format" $ do doc <- openDoc "Format.hs" "haskell" resp <- request STextDocumentFormatting $ DocumentFormattingParams Nothing doc (FormattingOptions 2 True Nothing Nothing Nothing) - case resp ^. LSP.result of + liftIO $ case resp ^. LSP.result of result@(Left (ResponseError reason message Nothing)) -> case reason of MethodNotFound -> pure () -- No formatter - InvalidRequest | "No plugin enabled for STextDocumentFormatting" `isPrefixOf` message -> pure () + InvalidRequest | "No plugin enabled for STextDocumentFormatting" `T.isPrefixOf` message -> pure () _ -> assertFailure $ "strange response from formatting provider:" ++ show result result -> assertFailure $ "strange response from formatting provider:" ++ show result From 108e9af7586be6d897c3cac5aef515d95137d7fc Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Thu, 1 Sep 2022 01:21:51 +0530 Subject: [PATCH 081/213] Improve GhcSessionDeps and lay out assumptions --- ghcide/src/Development/IDE/Core/Compile.hs | 39 ++++++++++----------- ghcide/src/Development/IDE/Core/Rules.hs | 40 ++++++++++++---------- ghcide/src/Development/IDE/GHC/Orphans.hs | 3 ++ 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/ghcide/src/Development/IDE/Core/Compile.hs b/ghcide/src/Development/IDE/Core/Compile.hs index 564078b513..fb0bd5b02a 100644 --- a/ghcide/src/Development/IDE/Core/Compile.hs +++ b/ghcide/src/Development/IDE/Core/Compile.hs @@ -1029,21 +1029,21 @@ loadModulesHome mod_infos e = #endif -- Merge the HPTs, module graphs and FinderCaches +-- See Note [GhcSessionDeps] in Development.IDE.Core.Rules +-- Add the current ModSummary to the graph, along with the +-- HomeModInfo's of all direct dependencies (by induction hypothesis all +-- transitive dependencies will be contained in envs) #if MIN_VERSION_ghc(9,3,0) -mergeEnvs :: HscEnv -> [ModuleGraphNode] -> [HomeModInfo] -> [HscEnv] -> IO HscEnv -mergeEnvs env extraNodes extraMods envs = do - let extraModSummaries = mapMaybe moduleGraphNodeModSum extraNodes - ims = map (\ms -> Compat.installedModule (toUnitId $ moduleUnit $ ms_mod ms) (moduleName (ms_mod ms))) extraModSummaries - ifrs = zipWith (\ms -> InstalledFound (ms_location ms)) extraModSummaries ims - curFinderCache = - foldl' - (\fc (im, ifr) -> Compat.extendInstalledModuleEnv fc im ifr) Compat.emptyInstalledModuleEnv - $ zip ims ifrs +mergeEnvs :: HscEnv -> (ModSummary, [NodeKey]) -> [HomeModInfo] -> [HscEnv] -> IO HscEnv +mergeEnvs env (ms, deps) extraMods envs = do + let im = Compat.installedModule (toUnitId $ moduleUnit $ ms_mod ms) (moduleName (ms_mod ms)) + ifr = InstalledFound (ms_location ms) im + curFinderCache = Compat.extendInstalledModuleEnv Compat.emptyInstalledModuleEnv im ifr -- Very important to force this as otherwise the hsc_mod_graph field is not -- forced and ends up retaining a reference to all the old hsc_envs we have merged to get -- this new one, which in turn leads to the EPS referencing the HPT. module_graph_nodes = - extraNodes ++ nubOrdOn mkNodeKey (concatMap (mgModSummaries' . hsc_mod_graph) envs) + nubOrdOn mkNodeKey (ModuleNode deps ms : concatMap (mgModSummaries' . hsc_mod_graph) envs) newFinderCache <- concatFC curFinderCache (map hsc_FC envs) liftRnf rwhnf module_graph_nodes `seq` (return $ loadModulesHome extraMods $ @@ -1065,16 +1065,16 @@ mergeEnvs env extraNodes extraMods envs = do concatFC cur xs = do fcModules <- mapM (readIORef . fcModuleCache) xs fcFiles <- mapM (readIORef . fcFileCache) xs - fcModules' <- newIORef (foldl' (plusInstalledModuleEnv const) cur fcModules) - fcFiles' <- newIORef (Map.unions fcFiles) + fcModules' <- newIORef $! foldl' (plusInstalledModuleEnv const) cur fcModules + fcFiles' <- newIORef $! Map.unions fcFiles pure $ FinderCache fcModules' fcFiles' #else -mergeEnvs :: HscEnv -> [ModSummary] -> [HomeModInfo] -> [HscEnv] -> IO HscEnv -mergeEnvs env extraModSummaries extraMods envs = do +mergeEnvs :: HscEnv -> ModSummary -> [HomeModInfo] -> [HscEnv] -> IO HscEnv +mergeEnvs env ms extraMods envs = do prevFinderCache <- concatFC <$> mapM (readIORef . hsc_FC) envs - let ims = map (\ms -> Compat.installedModule (toUnitId $ moduleUnit $ ms_mod ms) (moduleName (ms_mod ms))) extraModSummaries - ifrs = zipWith (\ms -> InstalledFound (ms_location ms)) extraModSummaries ims + let im = Compat.installedModule (toUnitId $ moduleUnit $ ms_mod ms) (moduleName (ms_mod ms)) + ifr = InstalledFound (ms_location ms) im -- Very important to force this as otherwise the hsc_mod_graph field is not -- forced and ends up retaining a reference to all the old hsc_envs we have merged to get -- this new one, which in turn leads to the EPS referencing the HPT. @@ -1085,12 +1085,9 @@ mergeEnvs env extraModSummaries extraMods envs = do -- This may have to change in the future. map extendModSummaryNoDeps $ #endif - extraModSummaries ++ nubOrdOn ms_mod (concatMap (mgModSummaries . hsc_mod_graph) envs) + nubOrdOn ms_mod (ms : concatMap (mgModSummaries . hsc_mod_graph) envs) - newFinderCache <- newIORef $ - foldl' - (\fc (im, ifr) -> Compat.extendInstalledModuleEnv fc im ifr) prevFinderCache - $ zip ims ifrs + newFinderCache <- newIORef $! Compat.extendInstalledModuleEnv prevFinderCache im ifr liftRnf rwhnf module_graph_nodes `seq` (return $ loadModulesHome extraMods $ env{ hsc_HPT = foldMapBy mergeUDFM emptyUDFM hsc_HPT envs, diff --git a/ghcide/src/Development/IDE/Core/Rules.hs b/ghcide/src/Development/IDE/Core/Rules.hs index 07cf081c21..72313a4661 100644 --- a/ghcide/src/Development/IDE/Core/Rules.hs +++ b/ghcide/src/Development/IDE/Core/Rules.hs @@ -67,6 +67,7 @@ import Control.Applicative (liftA2) #endif import Control.Concurrent.Async (concurrently) import Control.Concurrent.Strict +import Control.DeepSeq import Control.Exception.Safe import Control.Monad.Extra import Control.Monad.Reader @@ -668,7 +669,7 @@ typeCheckRule recorder = define (cmapWithPrio LogShake recorder) $ \TypeCheck fi -- very expensive. when (foi == NotFOI) $ logWith recorder Logger.Warning $ LogTypecheckedFOI file - typeCheckRuleDefinition hsc pm file + typeCheckRuleDefinition hsc pm knownFilesRule :: Recorder (WithPriority Log) -> Rules () knownFilesRule recorder = defineEarlyCutOffNoFile (cmapWithPrio LogShake recorder) $ \GetKnownTargets -> do @@ -689,9 +690,8 @@ getModuleGraphRule recorder = defineNoFile (cmapWithPrio LogShake recorder) $ \G typeCheckRuleDefinition :: HscEnv -> ParsedModule - -> NormalizedFilePath -> Action (IdeResult TcModuleResult) -typeCheckRuleDefinition hsc pm file = do +typeCheckRuleDefinition hsc pm = do setPriority priorityTypeCheck IdeOptions { optDefer = defer } <- getIdeOptions @@ -759,6 +759,11 @@ instance Default GhcSessionDepsConfig where { checkForImportCycles = True } +-- | Note [GhcSessionDeps] +-- For a file 'Foo', GhcSessionDeps "Foo.hs" results in an HscEnv which includes +-- 1. HomeModInfo's (in the HUG/HPT) for all modules in the transitive closure of "Foo", **NOT** including "Foo" itself. +-- 2. ModSummary's (in the ModuleGraph) for all modules in the transitive closure of "Foo", including "Foo" itself. +-- 3. ModLocation's (in the FinderCache) all modules in the transitive closure of "Foo", including "Foo" itself. ghcSessionDepsDefinition :: -- | full mod summary Bool -> @@ -771,27 +776,26 @@ ghcSessionDepsDefinition fullModSummary GhcSessionDepsConfig{..} env file = do Nothing -> return Nothing Just deps -> do when checkForImportCycles $ void $ uses_ ReportImportCycles deps - mss <- map msrModSummary <$> if fullModSummary - then uses_ GetModSummary deps - else uses_ GetModSummaryWithoutTimestamps deps + ms <- msrModSummary <$> if fullModSummary + then use_ GetModSummary file + else use_ GetModSummaryWithoutTimestamps file depSessions <- map hscEnv <$> uses_ (GhcSessionDeps_ fullModSummary) deps ifaces <- uses_ GetModIface deps let inLoadOrder = map (\HiFileResult{..} -> HomeModInfo hirModIface hirModDetails Nothing) ifaces #if MIN_VERSION_ghc(9,3,0) - mss_imports <- uses_ GetLocatedImports (file : deps) - final_deps <- forM mss_imports $ \imports -> do - let fs = mapMaybe (fmap artifactFilePath . snd) imports - dep_mss <- map msrModSummary <$> if fullModSummary - then uses_ GetModSummary fs - else uses_ GetModSummaryWithoutTimestamps fs - return (map (NodeKey_Module . msKey) dep_mss) - ms <- msrModSummary <$> use_ GetModSummary file - let moduleNodes = zipWith ModuleNode final_deps (ms : mss) + -- On GHC 9.4+, the module graph contains not only ModSummary's but each `ModuleNode` in the graph + -- also points to all the direct descendents of the current module. To get the keys for the descendents + -- we must get their `ModSummary`s + !final_deps <- do + dep_mss <- map msrModSummary <$> uses_ GetModSummaryWithoutTimestamps deps + -- Don't want to retain references to the entire ModSummary when just the key will do + return $!! map (NodeKey_Module . msKey) dep_mss + let moduleNode = (ms, final_deps) #else - let moduleNodes = mss + let moduleNode = ms #endif - session' <- liftIO $ mergeEnvs hsc moduleNodes inLoadOrder depSessions + session' <- liftIO $ mergeEnvs hsc moduleNode inLoadOrder depSessions Just <$> liftIO (newHscEnvEqWithImportPaths (envImportPaths env) session' []) @@ -996,7 +1000,7 @@ regenerateHiFile sess f ms compNeeded = do Just pm -> do -- Invoke typechecking directly to update it without incurring a dependency -- on the parsed module and the typecheck rules - (diags', mtmr) <- typeCheckRuleDefinition hsc pm f + (diags', mtmr) <- typeCheckRuleDefinition hsc pm case mtmr of Nothing -> pure (diags', Nothing) Just tmr -> do diff --git a/ghcide/src/Development/IDE/GHC/Orphans.hs b/ghcide/src/Development/IDE/GHC/Orphans.hs index 4e7ff4463e..d4f5c51972 100644 --- a/ghcide/src/Development/IDE/GHC/Orphans.hs +++ b/ghcide/src/Development/IDE/GHC/Orphans.hs @@ -223,4 +223,7 @@ instance NFData PkgQual where instance NFData UnitId where rnf = rwhnf + +instance NFData NodeKey where + rnf = rwhnf #endif From b15a6e0b6eb6b855b4ece264e7b6ef3486ef43ae Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Thu, 1 Sep 2022 01:33:44 +0530 Subject: [PATCH 082/213] Reduce nesting of ifdefs in hscCompileCoreHook --- ghcide/src/Development/IDE/Core/Compile.hs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ghcide/src/Development/IDE/Core/Compile.hs b/ghcide/src/Development/IDE/Core/Compile.hs index fb0bd5b02a..e6094a470d 100644 --- a/ghcide/src/Development/IDE/Core/Compile.hs +++ b/ghcide/src/Development/IDE/Core/Compile.hs @@ -306,19 +306,14 @@ captureSplicesAndDeps TypecheckHelpers{..} env k = do [homeUnitId_ dflags] #endif mods_transitive = getTransitiveMods hsc_env needed_mods + -- If we don't support multiple home units, ModuleNames are sufficient because all the units will be the same mods_transitive_list = #if MIN_VERSION_ghc(9,3,0) mapMaybe nodeKeyToInstalledModule $ Set.toList mods_transitive #else - map -#if MIN_VERSION_ghc(9,0,0) - (mkModule (homeUnitId_ dflags)) -#else - (InstalledModule (toInstalledUnitId $ homeUnitId_ dflags)) -#endif -- Non det OK as we will put it into maps later anyway - $ nonDetEltsUniqSet mods_transitive + map (Compat.installedModule (homeUnitId_ dflags)) $ nonDetEltsUniqSet mods_transitive #endif #if MIN_VERSION_ghc(9,3,0) From 830596ee212d4f2fbbc81bcf5d08574ae96947d3 Mon Sep 17 00:00:00 2001 From: Guillaume Bouchard Date: Thu, 1 Sep 2022 10:56:10 +0200 Subject: [PATCH 083/213] chore: add missing sub-project in flake.nix --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index 5257bda8f8..5c661fe66e 100644 --- a/flake.nix +++ b/flake.nix @@ -144,6 +144,7 @@ hie-compat = ./hie-compat; hls-plugin-api = ./hls-plugin-api; hls-test-utils = ./hls-test-utils; + ghcide-test-utils = ./ghcide/test; } // pluginSourceDirs; # Tweak our packages From 9f7a23b600ef6dabea0e674ded78a5ea9ab36520 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Thu, 1 Sep 2022 16:29:15 +0530 Subject: [PATCH 084/213] Revert "drop windows for 9.4.2" This reverts commit feeaa3c5c7c8d11d02200302ab7dea988597b040. --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index faf0529644..849dd9c7f3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -89,6 +89,9 @@ jobs: - os: ubuntu-latest ghc: '8.6.5' test: true + - os: windows-latest + ghc: '9.4.2' + test: true - os: windows-latest ghc: '9.2.4' test: true From 34ac6ead3627d55ac020e249acb7cd90f6961e3b Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Thu, 1 Sep 2022 16:29:47 +0530 Subject: [PATCH 085/213] Fix entropy to 0.4.1.10 to fix build on windows --- cabal.project | 1 + 1 file changed, 1 insertion(+) diff --git a/cabal.project b/cabal.project index eb2a58c05c..95d0b85672 100644 --- a/cabal.project +++ b/cabal.project @@ -51,6 +51,7 @@ write-ghc-environment-files: never index-state: 2022-08-15T06:53:13Z constraints: + entropy == 0.4.1.10, hyphenation +embed, -- remove this when hlint sets ghc-lib to true by default -- https://github.com/ndmitchell/hlint/issues/1376 From 738e4f636d86de1ba40d40d5346e9321330c42c9 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Thu, 1 Sep 2022 17:01:04 +0530 Subject: [PATCH 086/213] Use scoped allow-newer --- cabal.project | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/cabal.project b/cabal.project index 95d0b85672..10934aa7e7 100644 --- a/cabal.project +++ b/cabal.project @@ -48,10 +48,11 @@ package * write-ghc-environment-files: never -index-state: 2022-08-15T06:53:13Z +index-state: 2022-08-29T06:53:13Z constraints: - entropy == 0.4.1.10, + entropy >= 0.4.1.10, + basement >= 0.0.15, hyphenation +embed, -- remove this when hlint sets ghc-lib to true by default -- https://github.com/ndmitchell/hlint/issues/1376 @@ -87,8 +88,28 @@ source-repository-package -- https://github.com/haskell/lsp/pull/450 allow-newer: - base, ghc-prim, ghc-bignum, ghc, Cabal, binary, bytestring, unix, time, template-haskell, + -- ghc-9.4 + Chart-diagrams:lens, + Chart:lens, + co-log-core:base, + constraints-extras:base, + constraints-extras:template-haskell, + dependent-sum:some, + diagrams-contrib:base, + diagrams-contrib:lens, + diagrams-postscript:base, + diagrams-postscript:lens, + diagrams-svg:base, + diagrams-svg:lens, ghc-paths:Cabal, + haddock-library:base, + hie-bios:aeson, + hie-bios:ghc, + monoid-extras:base, + monoid-subclasses:vector, + svg-builder:base, + uuid:time, + vector-space:base, -- ghc-9.2 ---------- From 079d3f08e770d2113cd7ff0230baf3b0c2a3cf68 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Thu, 1 Sep 2022 17:04:09 +0530 Subject: [PATCH 087/213] Use scoped allow-newer --- cabal.project | 1 + 1 file changed, 1 insertion(+) diff --git a/cabal.project b/cabal.project index 10934aa7e7..37fbe414d2 100644 --- a/cabal.project +++ b/cabal.project @@ -101,6 +101,7 @@ allow-newer: diagrams-postscript:lens, diagrams-svg:base, diagrams-svg:lens, + ekg-json:base, ghc-paths:Cabal, haddock-library:base, hie-bios:aeson, From de66b16c9894a9039744219bab9856ab4257fb2b Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Thu, 1 Sep 2022 17:07:36 +0530 Subject: [PATCH 088/213] Bump base version for hie-compat --- hie-compat/hie-compat.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hie-compat/hie-compat.cabal b/hie-compat/hie-compat.cabal index 7e3aee55a6..ab94fb7a77 100644 --- a/hie-compat/hie-compat.cabal +++ b/hie-compat/hie-compat.cabal @@ -25,7 +25,7 @@ flag ghc-lib library default-language: Haskell2010 build-depends: - base < 4.17, array, bytestring, containers, directory, filepath, transformers + base < 4.18, array, bytestring, containers, directory, filepath, transformers if flag(ghc-lib) && impl(ghc < 9) build-depends: ghc-lib < 9.0 else From 66a719c50aebc6d07ed2f11abf07c31d4c81d423 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Thu, 1 Sep 2022 17:20:15 +0530 Subject: [PATCH 089/213] Add comments --- cabal.project | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cabal.project b/cabal.project index 37fbe414d2..3c1b731e0a 100644 --- a/cabal.project +++ b/cabal.project @@ -51,7 +51,9 @@ write-ghc-environment-files: never index-state: 2022-08-29T06:53:13Z constraints: + -- For GHC 9.4, older versions of entropy fail to build on Windows entropy >= 0.4.1.10, + -- For GHC 9.4 basement >= 0.0.15, hyphenation +embed, -- remove this when hlint sets ghc-lib to true by default From e1ef7abf1b2cb8747c544e90062cda3cd1e18e86 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Fri, 2 Sep 2022 17:22:01 +0530 Subject: [PATCH 090/213] remove ghcide-test-utils-internal --- ghcide/ghcide.cabal | 39 +-------------------------------------- 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index 810d8a47f1..0329130c88 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -349,7 +349,6 @@ test-suite ghcide-tests ghc, -------------------------------------------------------------- ghcide, - ghcide-test-utils-internal, lsp, lsp-types, hls-plugin-api, @@ -379,7 +378,7 @@ test-suite ghcide-tests record-hasfield if impl(ghc < 9.3) build-depends: ghc-typelits-knownnat - hs-source-dirs: test/cabal test/exe bench/lib + hs-source-dirs: test/cabal test/exe test/src bench/lib ghc-options: -threaded -Wall -Wno-name-shadowing -O0 -Wno-unticked-promoted-constructors main-is: Main.hs other-modules: @@ -387,42 +386,6 @@ test-suite ghcide-tests FuzzySearch Progress HieDbRetry - default-extensions: - BangPatterns - DeriveFunctor - DeriveGeneric - FlexibleContexts - GeneralizedNewtypeDeriving - LambdaCase - NamedFieldPuns - OverloadedStrings - RecordWildCards - ScopedTypeVariables - StandaloneDeriving - TupleSections - TypeApplications - ViewPatterns - -library ghcide-test-utils-internal - default-language: Haskell2010 - build-depends: - aeson, - base, - containers, - data-default, - directory, - extra, - filepath, - ghcide, - lsp-types, - hls-plugin-api, - lens, - lsp-test ^>= 0.14, - tasty-hunit >= 0.10, - text, - hs-source-dirs: test/src - ghc-options: -Wunused-packages - exposed-modules: Development.IDE.Test Development.IDE.Test.Diagnostic default-extensions: From 5b229b631fbc3b53fdacd1092aacb55f8f2de4a3 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Fri, 2 Sep 2022 18:21:43 +0530 Subject: [PATCH 091/213] Fix windows tests --- ghcide/test/exe/Main.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 523a7474f8..384efce985 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -2002,7 +2002,7 @@ completionDocTests = ] where brokenForGhc9 = knownBrokenFor (BrokenForGHC [GHC90, GHC92, GHC94]) "Completion doc doesn't support ghc9" - brokenForWinGhc9 = knownBrokenFor (BrokenSpecific Windows [GHC90, GHC92, GHC94]) "Extern doc doesn't support Windows for ghc9.2" + brokenForWinGhc9 = knownBrokenFor (BrokenSpecific Windows [GHC90, GHC92]) "Extern doc doesn't support Windows for ghc9.2" -- https://gitlab.haskell.org/ghc/ghc/-/issues/20903 brokenForMacGhc9 = knownBrokenFor (BrokenSpecific MacOS [GHC90, GHC92, GHC94]) "Extern doc doesn't support MacOS for ghc9" test doc pos label mn expected = do From 608cfd99b400045771942aa46422f1b727182f08 Mon Sep 17 00:00:00 2001 From: Nick Suchecki Date: Sun, 4 Sep 2022 16:24:44 -0400 Subject: [PATCH 092/213] Alternate Number Format Plugin buildable with GHC 9.4 --- .github/workflows/test.yml | 2 +- haskell-language-server.cabal | 2 +- .../hls-alternate-number-format-plugin.cabal | 12 +++--------- .../src/Ide/Plugin/Literals.hs | 5 ++--- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 849dd9c7f3..f811cd50bd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -240,7 +240,7 @@ jobs: name: Test hls-module-name-plugin test suite run: cabal test hls-module-name-plugin --test-options="$TEST_OPTS" || cabal test hls-module-name-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-module-name-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.4.2' + - if: matrix.test name: Test hls-alternate-number-format-plugin test suite run: cabal test hls-alternate-number-format-plugin --test-options="$TEST_OPTS" || cabal test hls-alternate-number-format-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-alternate-number-format-plugin --test-options="$TEST_OPTS" diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index fb73813911..26387f85b0 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -309,7 +309,7 @@ common splice cpp-options: -Dhls_splice common alternateNumberFormat - if flag(alternateNumberFormat) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) + if flag(alternateNumberFormat) build-depends: hls-alternate-number-format-plugin ^>= 1.1 cpp-options: -Dhls_alternateNumberFormat diff --git a/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal b/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal index b7eee39ce0..49f827774d 100644 --- a/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal +++ b/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-alternate-number-format-plugin -version: 1.1.0.0 +version: 1.1.0.1 synopsis: Provide Alternate Number Formats plugin for Haskell Language Server description: Please see the README on GitHub at @@ -18,10 +18,7 @@ extra-source-files: test/testdata/*.yaml library - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True exposed-modules: Ide.Plugin.AlternateNumberFormat, Ide.Plugin.Conversion other-modules: Ide.Plugin.Literals hs-source-dirs: src @@ -51,10 +48,7 @@ library RecordWildCards test-suite tests - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Literals.hs b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Literals.hs index 980d25685a..98a3551018 100644 --- a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Literals.hs +++ b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Literals.hs @@ -78,9 +78,8 @@ getPattern (L (locA -> (RealSrcSpan patSpan _)) pat) = case pat of HsInt _ val -> fromIntegralLit patSpan val HsRat _ val _ -> fromFractionalLit patSpan val _ -> Nothing - -- a located HsOverLit is (GenLocated SrcSpan HsOverLit) NOT (GenLocated SrcSpanAnn' a HsOverLit) - NPat _ (L (RealSrcSpan sSpan _) overLit) _ _ -> fromOverLit overLit sSpan - NPlusKPat _ _ (L (RealSrcSpan sSpan _) overLit1) _ _ _ -> fromOverLit overLit1 sSpan + NPat _ (L (locA -> (RealSrcSpan sSpan _)) overLit) _ _ -> fromOverLit overLit sSpan + NPlusKPat _ _ (L (locA -> (RealSrcSpan sSpan _)) overLit1) _ _ _ -> fromOverLit overLit1 sSpan _ -> Nothing getPattern _ = Nothing From 26a6ca607930b85bb1a2491b8deaf1a363c2e3df Mon Sep 17 00:00:00 2001 From: Nick Suchecki Date: Sun, 4 Sep 2022 22:45:55 -0400 Subject: [PATCH 093/213] Special handling of 9.2 --- .../src/Ide/Plugin/Literals.hs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Literals.hs b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Literals.hs index 98a3551018..a3935b92e9 100644 --- a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Literals.hs +++ b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Literals.hs @@ -78,8 +78,13 @@ getPattern (L (locA -> (RealSrcSpan patSpan _)) pat) = case pat of HsInt _ val -> fromIntegralLit patSpan val HsRat _ val _ -> fromFractionalLit patSpan val _ -> Nothing +#if __GLASGOW_HASKELL__ == 902 + NPat _ (L (RealSrcSpan sSpan _) overLit) _ _ -> fromOverLit overLit sSpan + NPlusKPat _ _ (L (RealSrcSpan sSpan _) overLit1) _ _ _ -> fromOverLit overLit1 sSpan +#else NPat _ (L (locA -> (RealSrcSpan sSpan _)) overLit) _ _ -> fromOverLit overLit sSpan NPlusKPat _ _ (L (locA -> (RealSrcSpan sSpan _)) overLit1) _ _ _ -> fromOverLit overLit1 sSpan +#endif _ -> Nothing getPattern _ = Nothing From 08cfc7839482f59d5fd046c1c53aae65de8bcc5e Mon Sep 17 00:00:00 2001 From: Nick Suchecki Date: Sun, 4 Sep 2022 22:22:26 -0400 Subject: [PATCH 094/213] Change Type Signature Plugin buildable with 9.4 --- .github/workflows/test.yml | 2 +- haskell-language-server.cabal | 2 +- .../hls-change-type-signature-plugin.cabal | 12 +++--------- .../hls-change-type-signature-plugin/test/Main.hs | 5 +++-- .../test/testdata/TRigidType2.expected.hs | 4 ++++ .../test/testdata/TRigidType2.hs | 4 ++++ 6 files changed, 16 insertions(+), 13 deletions(-) create mode 100644 plugins/hls-change-type-signature-plugin/test/testdata/TRigidType2.expected.hs create mode 100644 plugins/hls-change-type-signature-plugin/test/testdata/TRigidType2.hs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f811cd50bd..7d6c87b752 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -252,7 +252,7 @@ jobs: name: Test hls-code-range-plugin test suite run: cabal test hls-code-range-plugin --test-options="$TEST_OPTS" || cabal test hls-code-range-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-code-range-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.4.2' + - if: matrix.test name: Test hls-change-type-signature test suite run: cabal test hls-change-type-signature-plugin --test-options="$TEST_OPTS" || cabal test hls-change-type-signature-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-change-type-signature-plugin --test-options="$TEST_OPTS" diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 26387f85b0..38a08c9332 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -324,7 +324,7 @@ common codeRange cpp-options: -Dhls_codeRange common changeTypeSignature - if flag(changeTypeSignature) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) + if flag(changeTypeSignature) build-depends: hls-change-type-signature-plugin ^>= 1.0 cpp-options: -Dhls_changeTypeSignature diff --git a/plugins/hls-change-type-signature-plugin/hls-change-type-signature-plugin.cabal b/plugins/hls-change-type-signature-plugin/hls-change-type-signature-plugin.cabal index f93f303788..36449f006e 100644 --- a/plugins/hls-change-type-signature-plugin/hls-change-type-signature-plugin.cabal +++ b/plugins/hls-change-type-signature-plugin/hls-change-type-signature-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-change-type-signature-plugin -version: 1.0.1.0 +version: 1.0.1.1 synopsis: Change a declarations type signature with a Code Action description: Please see the README on GitHub at @@ -19,10 +19,7 @@ extra-source-files: test/testdata/*.yaml library - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True exposed-modules: Ide.Plugin.ChangeTypeSignature hs-source-dirs: src build-depends: @@ -50,10 +47,7 @@ library test-suite tests - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-change-type-signature-plugin/test/Main.hs b/plugins/hls-change-type-signature-plugin/test/Main.hs index ba5d917754..3aba829522 100644 --- a/plugins/hls-change-type-signature-plugin/test/Main.hs +++ b/plugins/hls-change-type-signature-plugin/test/Main.hs @@ -9,7 +9,7 @@ import Ide.Plugin.ChangeTypeSignature (errorMessageRegexes) import qualified Ide.Plugin.ChangeTypeSignature as ChangeTypeSignature import System.FilePath ((<.>), ()) import Test.Hls (CodeAction (..), Command, - GhcVersion (GHC92), IdeState, + GhcVersion (..), IdeState, PluginDescriptor, Position (Position), Range (Range), Session, @@ -38,7 +38,8 @@ test :: TestTree test = testGroup "changeTypeSignature" [ testRegexes , codeActionTest "TExpectedActual" 4 11 - , knownBrokenForGhcVersions [GHC92] "Error Message in 9.2 does not provide enough info" $ codeActionTest "TRigidType" 4 14 + , knownBrokenForGhcVersions [GHC92, GHC94] "Error Message in 9.2/9.4 does not provide enough info" $ codeActionTest "TRigidType" 4 14 + , codeActionTest "TRigidType2" 4 6 , codeActionTest "TLocalBinding" 7 22 , codeActionTest "TLocalBindingShadow1" 11 8 , codeActionTest "TLocalBindingShadow2" 7 22 diff --git a/plugins/hls-change-type-signature-plugin/test/testdata/TRigidType2.expected.hs b/plugins/hls-change-type-signature-plugin/test/testdata/TRigidType2.expected.hs new file mode 100644 index 0000000000..bbfb96cb81 --- /dev/null +++ b/plugins/hls-change-type-signature-plugin/test/testdata/TRigidType2.expected.hs @@ -0,0 +1,4 @@ +module TRigidType2 where + +test :: [Int] -> Int +test = head diff --git a/plugins/hls-change-type-signature-plugin/test/testdata/TRigidType2.hs b/plugins/hls-change-type-signature-plugin/test/testdata/TRigidType2.hs new file mode 100644 index 0000000000..9a6c25807c --- /dev/null +++ b/plugins/hls-change-type-signature-plugin/test/testdata/TRigidType2.hs @@ -0,0 +1,4 @@ +module TRigidType2 where + +test :: a -> Int +test = head From 9297c98c540fc8d9a0d8e304641f7ff3ff4082c7 Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Fri, 2 Sep 2022 21:11:43 +0200 Subject: [PATCH 095/213] enable a bunch of plugins that build with ghc 9.4 --- .github/workflows/test.yml | 14 +++++++------- haskell-language-server.cabal | 14 +++++++------- .../hls-call-hierarchy-plugin.cabal | 10 ++-------- .../hls-explicit-fixity-plugin.cabal | 10 ++-------- .../hls-explicit-imports-plugin.cabal | 10 ++-------- .../hls-module-name-plugin.cabal | 10 ++-------- .../hls-pragmas-plugin/hls-pragmas-plugin.cabal | 10 ++-------- .../hls-qualify-imported-names-plugin.cabal | 10 ++-------- .../hls-refine-imports-plugin.cabal | 10 ++-------- 9 files changed, 28 insertions(+), 70 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7d6c87b752..8199477be7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -180,7 +180,7 @@ jobs: name: Test hls-class-plugin run: cabal test hls-class-plugin --test-options="$TEST_OPTS" || cabal test hls-class-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-class-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.4.2' + - if: matrix.test name: Test hls-pragmas-plugin run: cabal test hls-pragmas-plugin --test-options="$TEST_OPTS" || cabal test hls-pragmas-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-pragmas-plugin --test-options="$TEST_OPTS" @@ -212,15 +212,15 @@ jobs: name: Test hls-tactics-plugin test suite run: cabal test hls-tactics-plugin --test-options="$TEST_OPTS" || cabal test hls-tactics-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-tactics-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.4.2' + - if: matrix.test name: Test hls-refine-imports-plugin test suite run: cabal test hls-refine-imports-plugin --test-options="$TEST_OPTS" || cabal test hls-refine-imports-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-refine-imports-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.4.2' + - if: matrix.test name: Test hls-explicit-imports-plugin test suite run: cabal test hls-explicit-imports-plugin --test-options="$TEST_OPTS" || cabal test hls-explicit-imports-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-explicit-imports-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.4.2' + - if: matrix.test name: Test hls-call-hierarchy-plugin test suite run: cabal test hls-call-hierarchy-plugin --test-options="$TEST_OPTS" || cabal test hls-call-hierarchy-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-call-hierarchy-plugin --test-options="$TEST_OPTS" @@ -236,7 +236,7 @@ jobs: name: Test hls-stan-plugin test suite run: cabal test hls-stan-plugin --test-options="$TEST_OPTS" || cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-stan-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.4.2' + - if: matrix.test name: Test hls-module-name-plugin test suite run: cabal test hls-module-name-plugin --test-options="$TEST_OPTS" || cabal test hls-module-name-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-module-name-plugin --test-options="$TEST_OPTS" @@ -244,7 +244,7 @@ jobs: name: Test hls-alternate-number-format-plugin test suite run: cabal test hls-alternate-number-format-plugin --test-options="$TEST_OPTS" || cabal test hls-alternate-number-format-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-alternate-number-format-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.4.2' + - if: matrix.test name: Test hls-qualify-imported-names-plugin test suite run: cabal test hls-qualify-imported-names-plugin --test-options="$TEST_OPTS" || cabal test hls-qualify-imported-names-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-qualify-imported-names-plugin --test-options="$TEST_OPTS" @@ -260,7 +260,7 @@ jobs: name: Test hls-gadt-plugin test suit run: cabal test hls-gadt-plugin --test-options="$TEST_OPTS" || cabal test hls-gadt-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-gadt-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.4.2' + - if: matrix.test name: Test hls-explicit-fixity-plugin test suite run: cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" || cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 38a08c9332..1040496490 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -244,7 +244,7 @@ common class cpp-options: -Dhls_class common callHierarchy - if flag(callHierarchy) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) + if flag(callHierarchy) build-depends: hls-call-hierarchy-plugin ^>= 1.0 cpp-options: -Dhls_callHierarchy @@ -259,12 +259,12 @@ common eval cpp-options: -Dhls_eval common importLens - if flag(importLens) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) + if flag(importLens) build-depends: hls-explicit-imports-plugin ^>= 1.1 cpp-options: -Dhls_importLens common refineImports - if flag(refineImports) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) + if flag(refineImports) build-depends: hls-refine-imports-plugin ^>=1.0 cpp-options: -Dhls_refineImports @@ -294,12 +294,12 @@ common stan cpp-options: -Dhls_stan common moduleName - if flag(moduleName) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) + if flag(moduleName) build-depends: hls-module-name-plugin ^>= 1.0 cpp-options: -Dhls_moduleName common pragmas - if flag(pragmas) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) + if flag(pragmas) build-depends: hls-pragmas-plugin ^>= 1.0 cpp-options: -Dhls_pragmas @@ -314,7 +314,7 @@ common alternateNumberFormat cpp-options: -Dhls_alternateNumberFormat common qualifyImportedNames - if flag(qualifyImportedNames) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) + if flag(qualifyImportedNames) build-depends: hls-qualify-imported-names-plugin ^>=1.0 cpp-options: -Dhls_qualifyImportedNames @@ -334,7 +334,7 @@ common gadt cpp-options: -Dhls_gadt common explicitFixity - if flag(explicitFixity) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) + if flag(explicitFixity) build-depends: hls-explicit-fixity-plugin ^>= 1.0 cpp-options: -DexplicitFixity diff --git a/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal b/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal index dc5cd8e398..ce2fb87f72 100644 --- a/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal +++ b/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal @@ -16,10 +16,7 @@ extra-source-files: test/testdata/*.hs library - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True exposed-modules: Ide.Plugin.CallHierarchy other-modules: Ide.Plugin.CallHierarchy.Internal @@ -47,10 +44,7 @@ library default-extensions: DataKinds test-suite tests - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal b/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal index dc865f2c12..1b185c28e8 100644 --- a/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal +++ b/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal @@ -16,10 +16,7 @@ extra-source-files: test/testdata/*.hs library - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True exposed-modules: Ide.Plugin.ExplicitFixity hs-source-dirs: src @@ -43,10 +40,7 @@ library default-extensions: DataKinds test-suite tests - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-explicit-imports-plugin/hls-explicit-imports-plugin.cabal b/plugins/hls-explicit-imports-plugin/hls-explicit-imports-plugin.cabal index fb73ff894f..3918ee5ac0 100644 --- a/plugins/hls-explicit-imports-plugin/hls-explicit-imports-plugin.cabal +++ b/plugins/hls-explicit-imports-plugin/hls-explicit-imports-plugin.cabal @@ -16,10 +16,7 @@ extra-source-files: test/testdata/*.yaml library - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True exposed-modules: Ide.Plugin.ExplicitImports hs-source-dirs: src build-depends: @@ -41,10 +38,7 @@ library TypeOperators test-suite tests - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal b/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal index 08c17e2349..e9dc57f577 100644 --- a/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal +++ b/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal @@ -20,10 +20,7 @@ extra-source-files: test/testdata/**/*.project library - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True exposed-modules: Ide.Plugin.ModuleName hs-source-dirs: src build-depends: @@ -41,10 +38,7 @@ library default-language: Haskell2010 test-suite tests - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal b/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal index 76f64083bd..02f256a4db 100644 --- a/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal +++ b/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal @@ -18,10 +18,7 @@ extra-source-files: test/testdata/*.yaml library - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True exposed-modules: Ide.Plugin.Pragmas hs-source-dirs: src build-depends: @@ -41,10 +38,7 @@ library default-language: Haskell2010 test-suite tests - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-qualify-imported-names-plugin/hls-qualify-imported-names-plugin.cabal b/plugins/hls-qualify-imported-names-plugin/hls-qualify-imported-names-plugin.cabal index 8990e05f09..0425eff7df 100644 --- a/plugins/hls-qualify-imported-names-plugin/hls-qualify-imported-names-plugin.cabal +++ b/plugins/hls-qualify-imported-names-plugin/hls-qualify-imported-names-plugin.cabal @@ -18,10 +18,7 @@ extra-source-files: test/data/*.yaml library - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True exposed-modules: Ide.Plugin.QualifyImportedNames hs-source-dirs: src build-depends: @@ -45,10 +42,7 @@ library TypeOperators test-suite tests - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-refine-imports-plugin/hls-refine-imports-plugin.cabal b/plugins/hls-refine-imports-plugin/hls-refine-imports-plugin.cabal index ebbe0b271c..7d9a656e69 100644 --- a/plugins/hls-refine-imports-plugin/hls-refine-imports-plugin.cabal +++ b/plugins/hls-refine-imports-plugin/hls-refine-imports-plugin.cabal @@ -16,10 +16,7 @@ extra-source-files: test/testdata/*.yaml library - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True exposed-modules: Ide.Plugin.RefineImports hs-source-dirs: src build-depends: @@ -42,10 +39,7 @@ library TypeOperators test-suite tests - if impl(ghc >= 9.3) - buildable: False - else - buildable: True + buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test From d3d4df51c2b1346935b544dc30459d2b2d8091ca Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Sat, 3 Sep 2022 11:12:07 +0200 Subject: [PATCH 096/213] refactor onlyWorkForGhcVersions --- hls-test-utils/src/Test/Hls/Util.hs | 6 +++--- plugins/hls-gadt-plugin/test/Main.hs | 8 ++++---- plugins/hls-pragmas-plugin/test/Main.hs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hls-test-utils/src/Test/Hls/Util.hs b/hls-test-utils/src/Test/Hls/Util.hs index 322a2bf867..7a608e6392 100644 --- a/hls-test-utils/src/Test/Hls/Util.hs +++ b/hls-test-utils/src/Test/Hls/Util.hs @@ -153,9 +153,9 @@ ignoreForGhcVersions :: [GhcVersion] -> String -> TestTree -> TestTree ignoreForGhcVersions vers = ignoreInEnv (map GhcVer vers) -- | Mark as broken if GHC does not match only work versions. -onlyWorkForGhcVersions :: [GhcVersion] -> String -> TestTree -> TestTree -onlyWorkForGhcVersions vers reason = - if ghcVersion `elem` vers +onlyWorkForGhcVersions :: (GhcVersion -> Bool) -> String -> TestTree -> TestTree +onlyWorkForGhcVersions pred reason = + if pred ghcVersion then id else expectFailBecause reason diff --git a/plugins/hls-gadt-plugin/test/Main.hs b/plugins/hls-gadt-plugin/test/Main.hs index 58c23422a8..5d266cd54d 100644 --- a/plugins/hls-gadt-plugin/test/Main.hs +++ b/plugins/hls-gadt-plugin/test/Main.hs @@ -35,13 +35,13 @@ tests = testGroup "GADT" , runTest "ConstuctorContext" "ConstructorContext" 2 0 2 38 , runTest "Context" "Context" 2 0 4 41 , runTest "Pragma" "Pragma" 2 0 3 29 - , onlyWorkForGhcVersions [GHC92] "Single deriving has different output on ghc9.2" $ + , onlyWorkForGhcVersions (==GHC92) "Single deriving has different output on ghc9.2" $ runTest "SingleDerivingGHC92" "SingleDerivingGHC92" 2 0 3 14 - , knownBrokenForGhcVersions [GHC92] "Single deriving has different output on ghc9.2" $ + , knownBrokenForGhcVersions (==GHC92) "Single deriving has different output on ghc9.2" $ runTest "SingleDeriving" "SingleDeriving" 2 0 3 14 - , onlyWorkForGhcVersions [GHC92] "only ghc-9.2 enabled GADTs pragma implicitly" $ + , onlyWorkForGhcVersions (==GHC92) "only ghc-9.2 enabled GADTs pragma implicitly" $ gadtPragmaTest "ghc-9.2 don't need to insert GADTs pragma" False - , knownBrokenForGhcVersions [GHC92] "ghc-9.2 has enabled GADTs pragma implicitly" $ + , knownBrokenForGhcVersions (==GHC92) "ghc-9.2 has enabled GADTs pragma implicitly" $ gadtPragmaTest "insert pragma" True ] diff --git a/plugins/hls-pragmas-plugin/test/Main.hs b/plugins/hls-pragmas-plugin/test/Main.hs index ec146ef2ef..47cbc0b25c 100644 --- a/plugins/hls-pragmas-plugin/test/Main.hs +++ b/plugins/hls-pragmas-plugin/test/Main.hs @@ -116,7 +116,7 @@ completionTests = , completionTest "completes language extensions case insensitive" "Completion.hs" "lAnGuaGe Overloaded" "OverloadedStrings" Nothing Nothing Nothing [0, 4, 0, 34, 0, 24] , completionTest "completes the Strict language extension" "Completion.hs" "Str" "Strict" Nothing Nothing Nothing [0, 13, 0, 31, 0, 16] , completionTest "completes No- language extensions" "Completion.hs" "NoOverload" "NoOverloadedStrings" Nothing Nothing Nothing [0, 13, 0, 31, 0, 23] - , onlyWorkForGhcVersions [GHC92] "GHC2021 flag introduced since ghc9.2" $ + , onlyWorkForGhcVersions (==GHC92) "GHC2021 flag introduced since ghc9.2" $ completionTest "completes GHC2021 extensions" "Completion.hs" "ghc" "GHC2021" Nothing Nothing Nothing [0, 13, 0, 31, 0, 16] ] From 28cbee9c01f6fd1bb303de8005c88eaa3d24f2e1 Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Sat, 3 Sep 2022 22:02:16 +0200 Subject: [PATCH 097/213] Tag 9.4 regressions in HLS pragmas test suite --- plugins/hls-pragmas-plugin/test/Main.hs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/plugins/hls-pragmas-plugin/test/Main.hs b/plugins/hls-pragmas-plugin/test/Main.hs index 47cbc0b25c..eb3f4e7da4 100644 --- a/plugins/hls-pragmas-plugin/test/Main.hs +++ b/plugins/hls-pragmas-plugin/test/Main.hs @@ -47,14 +47,14 @@ codeActionTests = , codeActionTest "Pragma then line haddock then newline line comment splits line" "PragmaThenLineHaddockNewlineLineComment" [("Add \"TupleSections\"", "Contains TupleSections code action")] , codeActionTest "does not add pragma after OPTIONS_GHC pragma located after a declaration" "OptionsGhcAfterDecl" [("Add \"TupleSections\"", "Contains TupleSections code action")] , codeActionTest "adds LANGUAGE with no other pragmas at start ignoring later INLINE pragma" "AddPragmaIgnoreInline" [("Add \"TupleSections\"", "Contains TupleSections code action")] - , codeActionTest "adds LANGUAGE before Doc comments after interchanging pragmas" "BeforeDocInterchanging" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] + , onlyWorkForGhcVersions (>GHC94) ghc94regression $ codeActionTest "adds LANGUAGE before Doc comments after interchanging pragmas" "BeforeDocInterchanging" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] , codeActionTest "Add language after altering OPTIONS_GHC and Language" "AddLanguagePragmaAfterInterchaningOptsGhcAndLangs" [("Add \"TupleSections\"", "Contains TupleSections code action")] , codeActionTest "Add language after pragmas with non standard space between prefix and name" "AddPragmaWithNonStandardSpacingInPrecedingPragmas" [("Add \"TupleSections\"", "Contains TupleSections code action")] , codeActionTest "adds LANGUAGE after OptGHC at start ignoring later INLINE pragma" "AddPragmaAfterOptsGhcIgnoreInline" [("Add \"TupleSections\"", "Contains TupleSections code action")] - , codeActionTest "adds LANGUAGE ignore later Ann pragma" "AddPragmaIgnoreLaterAnnPragma" [("Add \"BangPatterns\"", "Contains BangPatterns code action")] - , codeActionTest "adds LANGUAGE after interchanging pragmas ignoring later Ann pragma" "AddLanguageAfterInterchaningIgnoringLaterAnn" [("Add \"BangPatterns\"", "Contains BangPatterns code action")] - , codeActionTest "adds LANGUAGE after OptGHC preceded by another language pragma" "AddLanguageAfterLanguageThenOptsGhc" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] - , codeActionTest "adds LANGUAGE pragma after shebang and last language pragma" "AfterShebangAndPragma" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] + , onlyWorkForGhcVersions (>GHC94) ghc94regression $ codeActionTest "adds LANGUAGE ignore later Ann pragma" "AddPragmaIgnoreLaterAnnPragma" [("Add \"BangPatterns\"", "Contains BangPatterns code action")] + , onlyWorkForGhcVersions (>GHC94) ghc94regression $ codeActionTest "adds LANGUAGE after interchanging pragmas ignoring later Ann pragma" "AddLanguageAfterInterchaningIgnoringLaterAnn" [("Add \"BangPatterns\"", "Contains BangPatterns code action")] + , onlyWorkForGhcVersions (>GHC94) ghc94regression $ codeActionTest "adds LANGUAGE after OptGHC preceded by another language pragma" "AddLanguageAfterLanguageThenOptsGhc" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] + , onlyWorkForGhcVersions (>GHC94) ghc94regression $ codeActionTest "adds LANGUAGE pragma after shebang and last language pragma" "AfterShebangAndPragma" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] , codeActionTest "adds above module keyword on first line" "ModuleOnFirstLine" [("Add \"TupleSections\"", "Contains TupleSections code action")] , codeActionTest "adds LANGUAGE pragma after GHC_OPTIONS" "AfterGhcOptions" [("Add \"TupleSections\"", "Contains TupleSections code action")] , codeActionTest "adds LANGUAGE pragma after shebang and GHC_OPTIONS" "AfterShebangAndOpts" [("Add \"TupleSections\"", "Contains TupleSections code action")] @@ -63,14 +63,17 @@ codeActionTests = , codeActionTest "adds LANGUAGE pragma after all others ignoring multiple later INLINE pragma" "AfterAllWithMultipleInlines" [("Add \"TupleSections\"", "Contains TupleSections code action")] , codeActionTest "adds LANGUAGE pragma correctly ignoring later INLINE pragma" "AddLanguagePragma" [("Add \"TupleSections\"", "Contains TupleSections code action")] , codeActionTest "adds TypeApplications pragma" "TypeApplications" [("Add \"TypeApplications\"", "Contains TypeApplications code action")] - , codeActionTest "after shebang" "AfterShebang" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] - , codeActionTest "append to existing pragmas" "AppendToExisting" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] - , codeActionTest "before doc comments" "BeforeDocComment" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] + , onlyWorkForGhcVersions (>GHC94) ghc94regression $ codeActionTest "after shebang" "AfterShebang" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] + , onlyWorkForGhcVersions (>GHC94) ghc94regression $ codeActionTest "append to existing pragmas" "AppendToExisting" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] + , onlyWorkForGhcVersions (>GHC94) ghc94regression $ codeActionTest "before doc comments" "BeforeDocComment" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] , codeActionTest "before doc comments" "MissingSignatures" [("Disable \"missing-signatures\" warnings", "Contains missing-signatures code action")] , codeActionTest "before doc comments" "UnusedImports" [("Disable \"unused-imports\" warnings", "Contains unused-imports code action")] , codeActionTest "adds TypeSynonymInstances pragma" "NeedsPragmas" [("Add \"TypeSynonymInstances\"", "Contains TypeSynonymInstances code action"), ("Add \"FlexibleInstances\"", "Contains FlexibleInstances code action")] ] +ghc94regression :: String +ghc94regression = "to be reported" + codeActionTest :: String -> FilePath -> [(T.Text, String)] -> TestTree codeActionTest testComment fp actions = goldenWithPragmas testComment fp $ \doc -> do @@ -88,7 +91,7 @@ codeActionTests' :: TestTree codeActionTests' = testGroup "additional code actions" [ - goldenWithPragmas "no duplication" "NamedFieldPuns" $ \doc -> do + onlyWorkForGhcVersions (>GHC94) ghc94regression $ goldenWithPragmas "no duplication" "NamedFieldPuns" $ \doc -> do _ <- waitForDiagnosticsFrom doc cas <- map fromAction <$> getCodeActions doc (Range (Position 8 9) (Position 8 9)) ca <- liftIO $ case cas of @@ -116,7 +119,7 @@ completionTests = , completionTest "completes language extensions case insensitive" "Completion.hs" "lAnGuaGe Overloaded" "OverloadedStrings" Nothing Nothing Nothing [0, 4, 0, 34, 0, 24] , completionTest "completes the Strict language extension" "Completion.hs" "Str" "Strict" Nothing Nothing Nothing [0, 13, 0, 31, 0, 16] , completionTest "completes No- language extensions" "Completion.hs" "NoOverload" "NoOverloadedStrings" Nothing Nothing Nothing [0, 13, 0, 31, 0, 23] - , onlyWorkForGhcVersions (==GHC92) "GHC2021 flag introduced since ghc9.2" $ + , onlyWorkForGhcVersions (>=GHC92) "GHC2021 flag introduced since ghc9.2" $ completionTest "completes GHC2021 extensions" "Completion.hs" "ghc" "GHC2021" Nothing Nothing Nothing [0, 13, 0, 31, 0, 16] ] From 2b29a7e120ba4d5ab0fa7ee9f46eb495648883f3 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Sun, 4 Sep 2022 01:39:11 +0530 Subject: [PATCH 098/213] Restore hints in diagnostic messages --- ghcide/src/Development/IDE/GHC/Compat.hs | 11 ++++++++--- ghcide/src/Development/IDE/GHC/Error.hs | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ghcide/src/Development/IDE/GHC/Compat.hs b/ghcide/src/Development/IDE/GHC/Compat.hs index 157ddcde4c..8b322f6c3b 100644 --- a/ghcide/src/Development/IDE/GHC/Compat.hs +++ b/ghcide/src/Development/IDE/GHC/Compat.hs @@ -17,7 +17,7 @@ module Development.IDE.GHC.Compat( NameCacheUpdater(..), #if MIN_VERSION_ghc(9,3,0) getMessages, - diagnosticMessage, + renderDiagnosticMessageWithHints, nameEnvElts, #else upNameCache, @@ -402,7 +402,7 @@ type WarnMsg = MsgEnvelope DecoratedSDoc getMessages' :: PState -> DynFlags -> (Bag WarnMsg, Bag ErrMsg) getMessages' pst dflags = #if MIN_VERSION_ghc(9,3,0) - bimap (fmap (fmap diagnosticMessage) . getMessages) (fmap (fmap diagnosticMessage) . getMessages) $ getPsMessages pst + bimap (fmap (fmap renderDiagnosticMessageWithHints) . getMessages) (fmap (fmap renderDiagnosticMessageWithHints) . getMessages) $ getPsMessages pst #else #if MIN_VERSION_ghc(9,2,0) bimap (fmap pprWarning) (fmap pprError) $ @@ -413,11 +413,16 @@ getMessages' pst dflags = #endif #endif +#if MIN_VERSION_ghc(9,3,0) +renderDiagnosticMessageWithHints :: Diagnostic a => a -> DecoratedSDoc +renderDiagnosticMessageWithHints a = unionDecoratedSDoc (diagnosticMessage a) (mkDecorated $ map ppr $ diagnosticHints a) +#endif + #if MIN_VERSION_ghc(9,2,0) pattern PFailedWithErrorMessages :: forall a b. (b -> Bag (MsgEnvelope DecoratedSDoc)) -> ParseResult a pattern PFailedWithErrorMessages msgs #if MIN_VERSION_ghc(9,3,0) - <- PFailed (const . fmap (fmap diagnosticMessage) . getMessages . getPsErrorMessages -> msgs) + <- PFailed (const . fmap (fmap renderDiagnosticMessageWithHints) . getMessages . getPsErrorMessages -> msgs) #else <- PFailed (const . fmap pprError . getErrorMessages -> msgs) #endif diff --git a/ghcide/src/Development/IDE/GHC/Error.hs b/ghcide/src/Development/IDE/GHC/Error.hs index 89c527e404..b16d908c58 100644 --- a/ghcide/src/Development/IDE/GHC/Error.hs +++ b/ghcide/src/Development/IDE/GHC/Error.hs @@ -174,7 +174,7 @@ catchSrcErrors dflags fromWhere ghcM = do ghcExceptionToDiagnostics dflags = return . Left . diagFromGhcException fromWhere dflags sourceErrorToDiagnostics dflags = return . Left . diagFromErrMsgs fromWhere dflags #if MIN_VERSION_ghc(9,3,0) - . fmap (fmap Compat.diagnosticMessage) . Compat.getMessages + . fmap (fmap Compat.renderDiagnosticMessageWithHints) . Compat.getMessages #endif . srcErrorMessages From d3282c6cb18a3d3e4f05ab034a211cdeb5e4f374 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Sun, 4 Sep 2022 01:47:31 +0530 Subject: [PATCH 099/213] Restore more hints in diagnostic messages --- ghcide/src/Development/IDE/GHC/Compat.hs | 5 ----- ghcide/src/Development/IDE/GHC/Compat/Outputable.hs | 8 +++++++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/ghcide/src/Development/IDE/GHC/Compat.hs b/ghcide/src/Development/IDE/GHC/Compat.hs index 8b322f6c3b..ae4d57e715 100644 --- a/ghcide/src/Development/IDE/GHC/Compat.hs +++ b/ghcide/src/Development/IDE/GHC/Compat.hs @@ -413,11 +413,6 @@ getMessages' pst dflags = #endif #endif -#if MIN_VERSION_ghc(9,3,0) -renderDiagnosticMessageWithHints :: Diagnostic a => a -> DecoratedSDoc -renderDiagnosticMessageWithHints a = unionDecoratedSDoc (diagnosticMessage a) (mkDecorated $ map ppr $ diagnosticHints a) -#endif - #if MIN_VERSION_ghc(9,2,0) pattern PFailedWithErrorMessages :: forall a b. (b -> Bag (MsgEnvelope DecoratedSDoc)) -> ParseResult a pattern PFailedWithErrorMessages msgs diff --git a/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs b/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs index 084a48a04b..7fb9264422 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs @@ -19,6 +19,7 @@ module Development.IDE.GHC.Compat.Outputable ( PsError, #if MIN_VERSION_ghc(9,3,0) DiagnosticReason(..), + renderDiagnosticMessageWithHints, #else pprWarning, pprError, @@ -201,9 +202,14 @@ mkPrintUnqualifiedDefault env = HscTypes.mkPrintUnqualified (hsc_dflags env) #endif +#if MIN_VERSION_ghc(9,3,0) +renderDiagnosticMessageWithHints :: Diagnostic a => a -> DecoratedSDoc +renderDiagnosticMessageWithHints a = Error.unionDecoratedSDoc (diagnosticMessage a) (mkDecorated $ map ppr $ diagnosticHints a) +#endif + #if MIN_VERSION_ghc(9,3,0) mkWarnMsg :: DynFlags -> Maybe DiagnosticReason -> b -> SrcSpan -> PrintUnqualified -> SDoc -> MsgEnvelope DecoratedSDoc -mkWarnMsg df reason _logFlags l st doc = fmap diagnosticMessage $ mkMsgEnvelope (initDiagOpts df) l st (mkPlainDiagnostic (fromMaybe WarningWithoutFlag reason) [] doc) +mkWarnMsg df reason _logFlags l st doc = fmap renderDiagnosticMessageWithHints $ mkMsgEnvelope (initDiagOpts df) l st (mkPlainDiagnostic (fromMaybe WarningWithoutFlag reason) [] doc) #else mkWarnMsg :: a -> b -> DynFlags -> SrcSpan -> PrintUnqualified -> SDoc -> MsgEnvelope DecoratedSDoc mkWarnMsg _ _ = From b60abea0e17f605907a75aae3abea406090a161c Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Sat, 3 Sep 2022 23:12:17 +0200 Subject: [PATCH 100/213] pragmas tests no longer regressing --- plugins/hls-pragmas-plugin/test/Main.hs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/hls-pragmas-plugin/test/Main.hs b/plugins/hls-pragmas-plugin/test/Main.hs index eb3f4e7da4..0b5941a88a 100644 --- a/plugins/hls-pragmas-plugin/test/Main.hs +++ b/plugins/hls-pragmas-plugin/test/Main.hs @@ -47,14 +47,14 @@ codeActionTests = , codeActionTest "Pragma then line haddock then newline line comment splits line" "PragmaThenLineHaddockNewlineLineComment" [("Add \"TupleSections\"", "Contains TupleSections code action")] , codeActionTest "does not add pragma after OPTIONS_GHC pragma located after a declaration" "OptionsGhcAfterDecl" [("Add \"TupleSections\"", "Contains TupleSections code action")] , codeActionTest "adds LANGUAGE with no other pragmas at start ignoring later INLINE pragma" "AddPragmaIgnoreInline" [("Add \"TupleSections\"", "Contains TupleSections code action")] - , onlyWorkForGhcVersions (>GHC94) ghc94regression $ codeActionTest "adds LANGUAGE before Doc comments after interchanging pragmas" "BeforeDocInterchanging" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] + , codeActionTest "adds LANGUAGE before Doc comments after interchanging pragmas" "BeforeDocInterchanging" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] , codeActionTest "Add language after altering OPTIONS_GHC and Language" "AddLanguagePragmaAfterInterchaningOptsGhcAndLangs" [("Add \"TupleSections\"", "Contains TupleSections code action")] , codeActionTest "Add language after pragmas with non standard space between prefix and name" "AddPragmaWithNonStandardSpacingInPrecedingPragmas" [("Add \"TupleSections\"", "Contains TupleSections code action")] , codeActionTest "adds LANGUAGE after OptGHC at start ignoring later INLINE pragma" "AddPragmaAfterOptsGhcIgnoreInline" [("Add \"TupleSections\"", "Contains TupleSections code action")] - , onlyWorkForGhcVersions (>GHC94) ghc94regression $ codeActionTest "adds LANGUAGE ignore later Ann pragma" "AddPragmaIgnoreLaterAnnPragma" [("Add \"BangPatterns\"", "Contains BangPatterns code action")] - , onlyWorkForGhcVersions (>GHC94) ghc94regression $ codeActionTest "adds LANGUAGE after interchanging pragmas ignoring later Ann pragma" "AddLanguageAfterInterchaningIgnoringLaterAnn" [("Add \"BangPatterns\"", "Contains BangPatterns code action")] - , onlyWorkForGhcVersions (>GHC94) ghc94regression $ codeActionTest "adds LANGUAGE after OptGHC preceded by another language pragma" "AddLanguageAfterLanguageThenOptsGhc" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] - , onlyWorkForGhcVersions (>GHC94) ghc94regression $ codeActionTest "adds LANGUAGE pragma after shebang and last language pragma" "AfterShebangAndPragma" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] + , codeActionTest "adds LANGUAGE ignore later Ann pragma" "AddPragmaIgnoreLaterAnnPragma" [("Add \"BangPatterns\"", "Contains BangPatterns code action")] + , codeActionTest "adds LANGUAGE after interchanging pragmas ignoring later Ann pragma" "AddLanguageAfterInterchaningIgnoringLaterAnn" [("Add \"BangPatterns\"", "Contains BangPatterns code action")] + , codeActionTest "adds LANGUAGE after OptGHC preceded by another language pragma" "AddLanguageAfterLanguageThenOptsGhc" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] + , codeActionTest "adds LANGUAGE pragma after shebang and last language pragma" "AfterShebangAndPragma" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] , codeActionTest "adds above module keyword on first line" "ModuleOnFirstLine" [("Add \"TupleSections\"", "Contains TupleSections code action")] , codeActionTest "adds LANGUAGE pragma after GHC_OPTIONS" "AfterGhcOptions" [("Add \"TupleSections\"", "Contains TupleSections code action")] , codeActionTest "adds LANGUAGE pragma after shebang and GHC_OPTIONS" "AfterShebangAndOpts" [("Add \"TupleSections\"", "Contains TupleSections code action")] @@ -63,9 +63,9 @@ codeActionTests = , codeActionTest "adds LANGUAGE pragma after all others ignoring multiple later INLINE pragma" "AfterAllWithMultipleInlines" [("Add \"TupleSections\"", "Contains TupleSections code action")] , codeActionTest "adds LANGUAGE pragma correctly ignoring later INLINE pragma" "AddLanguagePragma" [("Add \"TupleSections\"", "Contains TupleSections code action")] , codeActionTest "adds TypeApplications pragma" "TypeApplications" [("Add \"TypeApplications\"", "Contains TypeApplications code action")] - , onlyWorkForGhcVersions (>GHC94) ghc94regression $ codeActionTest "after shebang" "AfterShebang" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] - , onlyWorkForGhcVersions (>GHC94) ghc94regression $ codeActionTest "append to existing pragmas" "AppendToExisting" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] - , onlyWorkForGhcVersions (>GHC94) ghc94regression $ codeActionTest "before doc comments" "BeforeDocComment" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] + , codeActionTest "after shebang" "AfterShebang" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] + , codeActionTest "append to existing pragmas" "AppendToExisting" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] + , codeActionTest "before doc comments" "BeforeDocComment" [("Add \"NamedFieldPuns\"", "Contains NamedFieldPuns code action")] , codeActionTest "before doc comments" "MissingSignatures" [("Disable \"missing-signatures\" warnings", "Contains missing-signatures code action")] , codeActionTest "before doc comments" "UnusedImports" [("Disable \"unused-imports\" warnings", "Contains unused-imports code action")] , codeActionTest "adds TypeSynonymInstances pragma" "NeedsPragmas" [("Add \"TypeSynonymInstances\"", "Contains TypeSynonymInstances code action"), ("Add \"FlexibleInstances\"", "Contains FlexibleInstances code action")] @@ -91,7 +91,7 @@ codeActionTests' :: TestTree codeActionTests' = testGroup "additional code actions" [ - onlyWorkForGhcVersions (>GHC94) ghc94regression $ goldenWithPragmas "no duplication" "NamedFieldPuns" $ \doc -> do + goldenWithPragmas "no duplication" "NamedFieldPuns" $ \doc -> do _ <- waitForDiagnosticsFrom doc cas <- map fromAction <$> getCodeActions doc (Range (Position 8 9) (Position 8 9)) ca <- liftIO $ case cas of From 4117da7bc3008a0891e2746b5d301c10130ffab7 Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Mon, 5 Sep 2022 08:29:35 +0200 Subject: [PATCH 101/213] fixup refactor onlyWorkForGhcVersions --- plugins/hls-gadt-plugin/test/Main.hs | 4 ++-- plugins/hls-refactor-plugin/test/Main.hs | 26 ++++++++++++------------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/plugins/hls-gadt-plugin/test/Main.hs b/plugins/hls-gadt-plugin/test/Main.hs index 5d266cd54d..1f99e5f8cc 100644 --- a/plugins/hls-gadt-plugin/test/Main.hs +++ b/plugins/hls-gadt-plugin/test/Main.hs @@ -37,11 +37,11 @@ tests = testGroup "GADT" , runTest "Pragma" "Pragma" 2 0 3 29 , onlyWorkForGhcVersions (==GHC92) "Single deriving has different output on ghc9.2" $ runTest "SingleDerivingGHC92" "SingleDerivingGHC92" 2 0 3 14 - , knownBrokenForGhcVersions (==GHC92) "Single deriving has different output on ghc9.2" $ + , knownBrokenForGhcVersions [GHC92] "Single deriving has different output on ghc9.2" $ runTest "SingleDeriving" "SingleDeriving" 2 0 3 14 , onlyWorkForGhcVersions (==GHC92) "only ghc-9.2 enabled GADTs pragma implicitly" $ gadtPragmaTest "ghc-9.2 don't need to insert GADTs pragma" False - , knownBrokenForGhcVersions (==GHC92) "ghc-9.2 has enabled GADTs pragma implicitly" $ + , knownBrokenForGhcVersions [GHC92] "ghc-9.2 has enabled GADTs pragma implicitly" $ gadtPragmaTest "insert pragma" True ] diff --git a/plugins/hls-refactor-plugin/test/Main.hs b/plugins/hls-refactor-plugin/test/Main.hs index dafbd1e843..2a81b9085e 100644 --- a/plugins/hls-refactor-plugin/test/Main.hs +++ b/plugins/hls-refactor-plugin/test/Main.hs @@ -1,17 +1,17 @@ -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE ImplicitParams #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE PolyKinds #-} -{-# LANGUAGE TypeOperators #-} -{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TypeOperators #-} {-# OPTIONS_GHC -Wno-deprecations -Wno-unticked-promoted-constructors #-} module Main @@ -19,34 +19,34 @@ module Main ) where import Control.Applicative.Combinators +import Control.Lens ((^.)) import Control.Monad import Data.Default import Data.Foldable import Data.List.Extra import Data.Maybe import qualified Data.Text as T -import Development.IDE.Test +import Data.Tuple.Extra import Development.IDE.GHC.Util import Development.IDE.Plugin.Completions.Types (extendImportCommandId) +import Development.IDE.Test import Development.IDE.Types.Location import Development.Shake (getDirectoryFilesIO) +import Ide.Types import Language.LSP.Test import Language.LSP.Types hiding (SemanticTokenAbsolute (length, line), SemanticTokenRelative (length), SemanticTokensEdit (_start), mkRange) -import qualified Language.LSP.Types.Lens as L +import qualified Language.LSP.Types as LSP import Language.LSP.Types.Capabilities +import qualified Language.LSP.Types.Lens as L import System.Directory import System.FilePath import System.Info.Extra (isMac, isWindows) import qualified System.IO.Extra import System.IO.Extra hiding (withTempDir) -import Control.Lens ((^.)) -import Data.Tuple.Extra -import Ide.Types -import qualified Language.LSP.Types as LSP import System.Time.Extra import Test.Tasty import Test.Tasty.ExpectedFailure @@ -54,11 +54,11 @@ import Test.Tasty.HUnit import Text.Regex.TDFA ((=~)) -import Test.Hls import Development.IDE.Plugin.CodeAction (matchRegExMultipleImports) +import Test.Hls -import qualified Development.IDE.Plugin.CodeAction as Refactor -import qualified Development.IDE.Plugin.HLS.GhcIde as GhcIde +import qualified Development.IDE.Plugin.CodeAction as Refactor +import qualified Development.IDE.Plugin.HLS.GhcIde as GhcIde main :: IO () main = defaultTestRunner tests From cd93447f0bd0b5b46b5e3a2671af197d8d53fc26 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Tue, 6 Sep 2022 14:17:53 +0530 Subject: [PATCH 102/213] warnings --- hls-test-utils/src/Test/Hls/Util.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hls-test-utils/src/Test/Hls/Util.hs b/hls-test-utils/src/Test/Hls/Util.hs index 7a608e6392..5ab1e093dd 100644 --- a/hls-test-utils/src/Test/Hls/Util.hs +++ b/hls-test-utils/src/Test/Hls/Util.hs @@ -154,8 +154,8 @@ ignoreForGhcVersions vers = ignoreInEnv (map GhcVer vers) -- | Mark as broken if GHC does not match only work versions. onlyWorkForGhcVersions :: (GhcVersion -> Bool) -> String -> TestTree -> TestTree -onlyWorkForGhcVersions pred reason = - if pred ghcVersion +onlyWorkForGhcVersions p reason = + if p ghcVersion then id else expectFailBecause reason From d18743d470557648269e6cd0259bd43532bbe77c Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Tue, 6 Sep 2022 14:45:05 +0530 Subject: [PATCH 103/213] Accept broken call-hierarchy tests --- plugins/hls-call-hierarchy-plugin/test/Main.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/hls-call-hierarchy-plugin/test/Main.hs b/plugins/hls-call-hierarchy-plugin/test/Main.hs index bbd8c44b93..ca9550a9f3 100644 --- a/plugins/hls-call-hierarchy-plugin/test/Main.hs +++ b/plugins/hls-call-hierarchy-plugin/test/Main.hs @@ -166,13 +166,13 @@ prepareCallHierarchyTests = expected = mkCallHierarchyItemC "A" SkConstructor range selRange oneCaseWithCreate contents 1 13 expected , testGroup "type signature" - [ testCase "next line" $ do + [ knownBrokenForGhcVersions [GHC94] "type signature broken" $ testCase "next line" $ do let contents = T.unlines ["a::Int", "a=3"] range = mkRange 1 0 1 3 selRange = mkRange 1 0 1 1 expected = mkCallHierarchyItemV "a" SkFunction range selRange oneCaseWithCreate contents 0 0 expected - , testCase "multi functions" $ do + , knownBrokenForGhcVersions [GHC94] "type signature broken" $ testCase "multi functions" $ do let contents = T.unlines [ "a,b::Int", "a=3", "b=4"] range = mkRange 2 0 2 3 selRange = mkRange 2 0 2 1 From a2df91907826b8c1b85f821607f2a19c961f511d Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Tue, 6 Sep 2022 14:53:29 +0530 Subject: [PATCH 104/213] Make hls-code-range-plugin buildable on 9.4 by removing usused exactprint dependencies --- haskell-language-server.cabal | 2 +- .../hls-code-range-plugin.cabal | 9 --------- .../src/Ide/Plugin/CodeRange.hs | 6 +----- .../src/Ide/Plugin/CodeRange/Rules.hs | 12 ++++-------- 4 files changed, 6 insertions(+), 23 deletions(-) diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 1040496490..24a9fcdfe5 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -319,7 +319,7 @@ common qualifyImportedNames cpp-options: -Dhls_qualifyImportedNames common codeRange - if flag(codeRange) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) + if flag(codeRange) build-depends: hls-code-range-plugin ^>= 1.0 cpp-options: -Dhls_codeRange diff --git a/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal b/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal index 3d50d0c764..e51ad55268 100644 --- a/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal +++ b/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal @@ -21,10 +21,6 @@ extra-source-files: test/testdata/selection-range/*.txt library - if impl(ghc >= 9.3) - buildable: False - else - buildable: True exposed-modules: Ide.Plugin.CodeRange Ide.Plugin.CodeRange.Rules @@ -42,7 +38,6 @@ library , ghcide ^>=1.6 || ^>=1.7 , hashable , hls-plugin-api ^>=1.3 || ^>=1.4 - , hls-refactor-plugin , lens , lsp , mtl @@ -52,10 +47,6 @@ library , vector test-suite tests - if impl(ghc >= 9.3) - buildable: False - else - buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs index 23a02cfb60..0a48a3467b 100644 --- a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs +++ b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs @@ -31,8 +31,6 @@ import Development.IDE.Core.PositionMapping (PositionMapping, fromCurrentPosition, toCurrentRange) import Development.IDE.Types.Logger (Pretty (..)) -import qualified Development.IDE.GHC.ExactPrint as E -import Development.IDE.Plugin.CodeAction import Ide.Plugin.CodeRange.Rules (CodeRange (..), GetCodeRange (..), codeRangeRule) @@ -57,7 +55,7 @@ import Language.LSP.Types (List (List), import Prelude hiding (log, span) descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState -descriptor recorder plId = mkExactprintPluginDescriptor (cmapWithPrio LogExactPrint recorder) $ (defaultPluginDescriptor plId) +descriptor recorder plId = (defaultPluginDescriptor plId) { pluginHandlers = mkPluginHandler STextDocumentSelectionRange selectionRangeHandler -- TODO @sloorush add folding range -- <> mkPluginHandler STextDocumentFoldingRange foldingRangeHandler @@ -65,12 +63,10 @@ descriptor recorder plId = mkExactprintPluginDescriptor (cmapWithPrio LogExactPr } data Log = LogRules Rules.Log - | LogExactPrint E.Log instance Pretty Log where pretty log = case log of LogRules codeRangeLog -> pretty codeRangeLog - LogExactPrint exactPrintLog -> pretty exactPrintLog selectionRangeHandler :: IdeState -> PluginId -> SelectionRangeParams -> LspM c (Either ResponseError (List SelectionRange)) selectionRangeHandler ide _ SelectionRangeParams{..} = do diff --git a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs index fcd96ffea1..992bf6ca28 100644 --- a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs +++ b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs @@ -44,12 +44,10 @@ import qualified Data.Vector as V import Development.IDE import Development.IDE.Core.Rules (toIdeResult) import qualified Development.IDE.Core.Shake as Shake -import Development.IDE.GHC.Compat.ExactPrint (Annotated) import Development.IDE.GHC.Compat (HieAST (..), HieASTs (getAsts), - ParsedSource, RefMap) + RefMap) import Development.IDE.GHC.Compat.Util -import Development.IDE.GHC.ExactPrint (GetAnnotatedParsedSource (GetAnnotatedParsedSource)) import GHC.Generics (Generic) import Ide.Plugin.CodeRange.ASTPreProcess (CustomNodeType (..), PreProcessEnv (..), @@ -105,8 +103,8 @@ instance Ord CodeRange where -- | Construct a 'CodeRange'. A valid CodeRange will be returned in any case. If anything go wrong, -- a list of warnings will be returned as 'Log' -buildCodeRange :: HieAST a -> RefMap a -> Annotated ParsedSource -> Writer [Log] CodeRange -buildCodeRange ast refMap _ = do +buildCodeRange :: HieAST a -> RefMap a -> Writer [Log] CodeRange +buildCodeRange ast refMap = do -- We work on 'HieAST', then convert it to 'CodeRange', so that applications such as selection range and folding -- range don't need to care about 'HieAST' -- TODO @sloorush actually use 'Annotated ParsedSource' to handle structures not in 'HieAST' properly (for example comments) @@ -178,9 +176,7 @@ codeRangeRule recorder = HAR{hieAst, refMap} <- lift $ use_ GetHieAst file ast <- maybeToExceptT LogNoAST . MaybeT . pure $ getAsts hieAst Map.!? (coerce . mkFastString . fromNormalizedFilePath) file - annPS <- lift $ use_ GetAnnotatedParsedSource file - - let (codeRange, warnings) = runWriter (buildCodeRange ast refMap annPS) + let (codeRange, warnings) = runWriter (buildCodeRange ast refMap) traverse_ (logWith recorder Warning) warnings pure codeRange From 163ebfe9d02d5aa9f2c83ef8d9db2e91cde31032 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Tue, 6 Sep 2022 16:13:37 +0530 Subject: [PATCH 105/213] Fix hlint --- .hlint.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.hlint.yaml b/.hlint.yaml index 8caaebd8f6..324dbb2d55 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -108,6 +108,7 @@ - Ide.Plugin.Class.ExactPrint - TExpectedActual - TRigidType + - TRigidType2 - RightToLeftFixities - Typeclass - Wingman.Judgements From aad896cdc5b4869dc781e9b1eade8f3b14148f7c Mon Sep 17 00:00:00 2001 From: Nick Suchecki <40047416+drsooch@users.noreply.github.com> Date: Wed, 7 Sep 2022 13:58:05 -0400 Subject: [PATCH 106/213] Update hls-retrie-plugin to be usable with 9.2.4. (#3120) * Update hls-retrie-plugin to be usable with 9.2.4. This is the first pass at getting hls-retrie-plugin enabled. Much of the changes were updating to match the changes in the upstream `retrie` package. * Replace GHC.Paths.libdir by querying a ModSummary for the LibDir * Looks like formatting was missed * Revert "Looks like formatting was missed" This reverts commit 4f6eee5a6fd0239066f5d711f43c555040d48c72. * Don't build retrie for 9.4 Co-authored-by: Pepe Iborra --- ghcide/src/Development/IDE/GHC/Compat/Core.hs | 8 ++ haskell-language-server.cabal | 2 +- .../hls-retrie-plugin/hls-retrie-plugin.cabal | 2 +- .../src/Ide/Plugin/Retrie.hs | 95 +++++++++++-------- 4 files changed, 68 insertions(+), 39 deletions(-) diff --git a/ghcide/src/Development/IDE/GHC/Compat/Core.hs b/ghcide/src/Development/IDE/GHC/Compat/Core.hs index 173759a5f8..afeace0acf 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Core.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Core.hs @@ -215,6 +215,7 @@ module Development.IDE.GHC.Compat.Core ( getLocA, locA, noLocA, + unLocA, LocatedAn, #if MIN_VERSION_ghc(9,2,0) GHC.AnnListItem(..), @@ -1125,6 +1126,13 @@ locA = GHC.locA locA = id #endif +#if MIN_VERSION_ghc(9,2,0) +unLocA :: forall pass a. XRec (GhcPass pass) a -> a +unLocA = unXRec @(GhcPass pass) +#else +unLocA = id +#endif + #if MIN_VERSION_ghc(9,2,0) getLocA :: SrcLoc.GenLocated (SrcSpanAnn' a) e -> SrcSpan getLocA = GHC.getLocA diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 24a9fcdfe5..45776a3482 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -274,7 +274,7 @@ common rename cpp-options: -Dhls_rename common retrie - if flag(retrie) && (impl(ghc < 9.2.1) || flag(ignore-plugins-ghc-bounds)) + if flag(retrie) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-retrie-plugin ^>= 1.0 cpp-options: -Dhls_retrie diff --git a/plugins/hls-retrie-plugin/hls-retrie-plugin.cabal b/plugins/hls-retrie-plugin/hls-retrie-plugin.cabal index c86bacbb20..887af8665d 100644 --- a/plugins/hls-retrie-plugin/hls-retrie-plugin.cabal +++ b/plugins/hls-retrie-plugin/hls-retrie-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.2 name: hls-retrie-plugin -version: 1.0.2.1 +version: 1.0.2.2 synopsis: Retrie integration plugin for Haskell Language Server description: Please see the README on GitHub at diff --git a/plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs b/plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs index 91ac93d59f..4c3c51f43c 100644 --- a/plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs +++ b/plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs @@ -11,12 +11,12 @@ {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE ViewPatterns #-} {-# OPTIONS -Wno-orphans #-} module Ide.Plugin.Retrie (descriptor) where -import Control.Concurrent.Extra (readVar) import Control.Concurrent.STM (readTVarIO) import Control.Exception.Safe (Exception (..), SomeException, catch, @@ -29,11 +29,8 @@ import Control.Monad.Trans.Except (ExceptT (ExceptT), import Control.Monad.Trans.Maybe import Data.Aeson (FromJSON (..), ToJSON (..), - Value (Null), - genericParseJSON) -import qualified Data.Aeson as Aeson -import Data.Bifunctor (Bifunctor (first), - second) + Value (Null)) +import Data.Bifunctor (second) import qualified Data.ByteString as BS import Data.Coerce import Data.Either (partitionEithers) @@ -43,7 +40,7 @@ import qualified Data.HashSet as Set import Data.IORef.Extra (atomicModifyIORef'_, newIORef, readIORef) import Data.List.Extra (find, nubOrdOn) -import Data.String (IsString (fromString)) +import Data.String (IsString) import qualified Data.Text as T import qualified Data.Text.Encoding as T import Data.Typeable (Typeable) @@ -51,36 +48,39 @@ import Development.IDE hiding (pluginHandlers) import Development.IDE.Core.PositionMapping import Development.IDE.Core.Shake (ShakeExtras (knownTargetsVar), toKnownFiles) -import Development.IDE.GHC.Compat (GenLocated (L), GhcRn, +import Development.IDE.GHC.Compat (GenLocated (L), GhcPs, + GhcRn, GhcTc, HsBindLR (FunBind), HsGroup (..), HsValBindsLR (..), HscEnv, IdP, LRuleDecls, ModSummary (ModSummary, ms_hspp_buf, ms_mod), - NHsValBindsLR (..), Outputable, ParsedModule (..), RuleDecl (HsRule), RuleDecls (HsRules), SourceText (..), - SrcSpan (..), TyClDecl (SynDecl), TyClGroup (..), fun_id, hm_iface, isQual, - isQual_maybe, + isQual_maybe, locA, mi_fixities, moduleNameString, + ms_hspp_opts, nameModule_maybe, - nameRdrName, occNameFS, - occNameString, - parseModule, + nameRdrName, noLocA, + occNameFS, occNameString, pattern IsBoot, pattern NotBoot, pattern RealSrcSpan, + pm_parsed_source, rdrNameOcc, rds_rules, - srcSpanFile) + srcSpanFile, topDir, + unLocA) import Development.IDE.GHC.Compat.Util hiding (catch, try) -import qualified GHC (parseModule) +import qualified GHC (Module, + ParsedModule (..), + moduleName, parseModule) import GHC.Generics (Generic) import Ide.PluginUtils import Ide.Types @@ -94,8 +94,13 @@ import Language.LSP.Types as J hiding SemanticTokenRelative (length), SemanticTokensEdit (_start)) import Retrie.CPP (CPP (NoCPP), parseCPP) -import Retrie.ExactPrint (fix, relativiseApiAnns, +import Retrie.ExactPrint (Annotated, fix, transformA, unsafeMkA) +#if MIN_VERSION_ghc(9,2,0) +import Retrie.ExactPrint (makeDeltaAst) +#else +import Retrie.ExactPrint (relativiseApiAnns) +#endif import Retrie.Fixity (mkFixityEnv) import qualified Retrie.GHC as GHC import Retrie.Monad (addImports, apply, @@ -202,7 +207,7 @@ provider state plId (CodeActionParams _ _ (TextDocumentIdentifier uri) range ca) ++ concatMap (suggestRuleRewrites uri pos ms_mod) hs_ruleds ++ [ r | TyClGroup {group_tyclds} <- hs_tyclds, - L l g <- group_tyclds, + L (locA -> l) g <- group_tyclds, pos `isInsideSrcSpan` l, r <- suggestTypeRewrites uri ms_mod g @@ -225,7 +230,7 @@ getBinds nfp = runMaybeT $ do ( HsGroup { hs_valds = XValBindsLR - (NValBinds binds _sigs :: NHsValBindsLR GHC.GhcRn), + (GHC.NValBinds binds _sigs :: GHC.NHsValBindsLR GhcRn), hs_ruleds, hs_tyclds }, @@ -247,7 +252,7 @@ suggestBindRewrites :: GHC.Module -> HsBindLR GhcRn GhcRn -> [(T.Text, CodeActionKind, RunRetrieParams)] -suggestBindRewrites originatingFile pos ms_mod FunBind {fun_id = L l' rdrName} +suggestBindRewrites originatingFile pos ms_mod FunBind {fun_id = L (locA -> l') rdrName} | pos `isInsideSrcSpan` l' = let pprNameText = printOutputable rdrName pprName = T.unpack pprNameText @@ -267,13 +272,13 @@ describeRestriction restrictToOriginatingFile = if restrictToOriginatingFile then " in current file" else "" suggestTypeRewrites :: - (Outputable (IdP pass)) => + (Outputable (IdP GhcRn)) => Uri -> GHC.Module -> - TyClDecl pass -> + TyClDecl GhcRn -> [(T.Text, CodeActionKind, RunRetrieParams)] -suggestTypeRewrites originatingFile ms_mod SynDecl {tcdLName = L _ rdrName} = - let pprNameText = printOutputable rdrName +suggestTypeRewrites originatingFile ms_mod SynDecl {tcdLName} = + let pprNameText = printOutputable (unLocA tcdLName) pprName = T.unpack pprNameText unfoldRewrite restrictToOriginatingFile = let rewrites = [TypeForward (qualify ms_mod pprName)] @@ -290,7 +295,7 @@ suggestRuleRewrites :: Uri -> Position -> GHC.Module -> - LRuleDecls pass -> + LRuleDecls GhcRn -> [(T.Text, CodeActionKind, RunRetrieParams)] suggestRuleRewrites originatingFile pos ms_mod (L _ HsRules {rds_rules}) = concat @@ -299,7 +304,7 @@ suggestRuleRewrites originatingFile pos ms_mod (L _ HsRules {rds_rules}) = , backwardsRewrite ruleName True , backwardsRewrite ruleName False ] - | L l r <- rds_rules, + | L (locA -> l) r <- rds_rules, pos `isInsideSrcSpan` l, #if MIN_VERSION_ghc(8,8,0) let HsRule {rd_name = L _ (_, rn)} = r, @@ -326,7 +331,6 @@ suggestRuleRewrites originatingFile pos ms_mod (L _ HsRules {rds_rules}) = CodeActionRefactor, RunRetrieParams {..} ) - suggestRuleRewrites _ _ _ _ = [] qualify :: GHC.Module -> String -> String @@ -359,24 +363,26 @@ callRetrie :: IO ([CallRetrieError], WorkspaceEdit) callRetrie state session rewrites origin restrictToOriginatingFile = do knownFiles <- toKnownFiles . unhashed <$> readTVarIO (knownTargetsVar $ shakeExtras state) +#if MIN_VERSION_ghc(9,2,0) + -- retrie needs the libdir for `parseRewriteSpecs` + libdir <- topDir . ms_hspp_opts . msrModSummary <$> useOrFail "Retrie.GetModSummary" (CallRetrieInternalError "file not found") GetModSummary origin +#endif let reuseParsedModule f = do - pm <- - useOrFail "GetParsedModule" NoParse GetParsedModule f - (fixities, pm) <- fixFixities f (fixAnns pm) - return (fixities, pm) + pm <- useOrFail "Retrie.GetParsedModule" NoParse GetParsedModule f + (fixities, pm') <- fixFixities f (fixAnns pm) + return (fixities, pm') getCPPmodule t = do nt <- toNormalizedFilePath' <$> makeAbsolute t let getParsedModule f contents = do modSummary <- msrModSummary <$> - useOrFail "GetModSummary" (CallRetrieInternalError "file not found") GetModSummary nt + useOrFail "Retrie.GetModSummary" (CallRetrieInternalError "file not found") GetModSummary nt let ms' = modSummary { ms_hspp_buf = Just (stringToStringBuffer contents) } logPriority (ideLogger state) Info $ T.pack $ "Parsing module: " <> t - parsed <- - evalGhcEnv session (GHC.parseModule ms') + parsed <- evalGhcEnv session (GHC.parseModule ms') `catch` \e -> throwIO (GHCParseError nt (show @SomeException e)) (fixities, parsed) <- fixFixities f (fixAnns parsed) return (fixities, parsed) @@ -416,12 +422,19 @@ callRetrie state session rewrites origin restrictToOriginatingFile = do (theImports, theRewrites) = partitionEithers rewrites annotatedImports = - unsafeMkA (map (GHC.noLoc . toImportDecl) theImports) mempty 0 +#if MIN_VERSION_ghc(9,2,0) + unsafeMkA (map (noLocA . toImportDecl) theImports) 0 +#else + unsafeMkA (map (noLocA . toImportDecl) theImports) mempty 0 +#endif (originFixities, originParsedModule) <- reuseParsedModule origin retrie <- (\specs -> apply specs >> addImports annotatedImports) <$> parseRewriteSpecs +#if MIN_VERSION_ghc(9,2,0) + libdir +#endif (\_f -> return $ NoCPP originParsedModule) originFixities theRewrites @@ -463,9 +476,13 @@ callRetrie state session rewrites origin restrictToOriginatingFile = do let fixities = fixityEnvFromModIface hirModIface res <- transformA pm (fix fixities) return (fixities, res) - fixAnns ParsedModule {..} = +#if MIN_VERSION_ghc(9,2,0) + fixAnns GHC.ParsedModule{pm_parsed_source} = unsafeMkA (makeDeltaAst pm_parsed_source) 0 +#else + fixAnns GHC.ParsedModule {..} = let ranns = relativiseApiAnns pm_parsed_source pm_annotations in unsafeMkA pm_parsed_source ranns 0 +#endif asEditMap :: [[(Uri, TextEdit)]] -> WorkspaceEditMap asEditMap = coerce . HM.fromListWith (++) . concatMap (map (second pure)) @@ -533,14 +550,18 @@ toImportDecl :: ImportSpec -> GHC.ImportDecl GHC.GhcPs toImportDecl AddImport {..} = GHC.ImportDecl {ideclSource = ideclSource', ..} where ideclSource' = if ideclSource then IsBoot else NotBoot - toMod = GHC.noLoc . GHC.mkModuleName + toMod = noLocA . GHC.mkModuleName ideclName = toMod ideclNameString ideclPkgQual = Nothing ideclSafe = False ideclImplicit = False ideclHiding = Nothing ideclSourceSrc = NoSourceText +#if MIN_VERSION_ghc(9,2,0) + ideclExt = GHC.EpAnnNotUsed +#else ideclExt = GHC.noExtField +#endif ideclAs = toMod <$> ideclAsString #if MIN_VERSION_ghc(8,10,0) ideclQualified = if ideclQualifiedBool then GHC.QualifiedPre else GHC.NotQualified From 3fb4082f8038d573d7636a2d74aa1be70d317cc2 Mon Sep 17 00:00:00 2001 From: Kobayashi Date: Fri, 9 Sep 2022 03:54:24 +0800 Subject: [PATCH 107/213] Improve performance of NormalizedFilePath (#3067) * upgrade lsp * modify default benchmark config * upgrade lsp * use lsp master * temp: compare benchmark with previous commit * use text in NormalizedFilePath * upgrade to lsp master * fix stack config * remove obsolete dir form ghcide.cabal * run pre-commit without args * Revert "run pre-commit without args" This reverts commit 1c2a11df9d234fd3bd919c10dfadc9a50cb168db. * remove unnecessary tests --- bench/config.yaml | 110 ++++++++++--------- cabal.project | 17 ++- ghcide/ghcide.cabal | 2 +- ghcide/src/Development/IDE/Types/Location.hs | 6 +- hls-plugin-api/src/Ide/PluginUtils.hs | 5 +- hls-plugin-api/test/Ide/PluginUtilsTest.hs | 18 +-- stack-lts16.yaml | 9 +- stack-lts19.yaml | 9 +- stack.yaml | 9 +- 9 files changed, 92 insertions(+), 93 deletions(-) diff --git a/bench/config.yaml b/bench/config.yaml index 4e99f1c8db..9e49ed6360 100644 --- a/bench/config.yaml +++ b/bench/config.yaml @@ -76,7 +76,7 @@ versions: # - 1.8.0.0 -# - upstream: origin/master +- upstream: origin/master # - HEAD~1 - HEAD @@ -95,26 +95,28 @@ configurations: # The implicitly included plugins are: # - ghcide-core # - ghcide-hover-and-symbols -- None: [] -- Core: - - callHierarchy - - codeRange - - eval - - ghcide-code-actions-bindings - - ghcide-code-actions-fill-holes - - ghcide-code-actions-imports-exports - - ghcide-code-actions-type-signatures - - ghcide-completions - - ghcide-type-lenses - - pragmas -- Ghcide: - - ghcide-completions - - ghcide-type-lenses -- Refactor: - - ghcide-code-actions-bindings - - ghcide-code-actions-fill-holes - - ghcide-code-actions-imports-exports - - ghcide-code-actions-type-signatures + +# Uncomment below sections if needed +# - None: [] +# - Core: +# - callHierarchy +# - codeRange +# - eval +# - ghcide-code-actions-bindings +# - ghcide-code-actions-fill-holes +# - ghcide-code-actions-imports-exports +# - ghcide-code-actions-type-signatures +# - ghcide-completions +# - ghcide-type-lenses +# - pragmas +# - Ghcide: +# - ghcide-completions +# - ghcide-type-lenses +# - Refactor: +# - ghcide-code-actions-bindings +# - ghcide-code-actions-fill-holes +# - ghcide-code-actions-imports-exports +# - ghcide-code-actions-type-signatures - All: - alternateNumberFormat - callHierarchy @@ -141,36 +143,36 @@ configurations: - refineImports - rename - stylish-haskell -- alternateNumberFormat -# - brittany -- callHierarchy -- changeTypeSignature -- class -- codeRange -- eval -- explicitFixity -# - floskell -# - fourmolu -- gadt -- ghcide-code-actions-bindings -- ghcide-code-actions-fill-holes -- ghcide-code-actions-imports-exports -- ghcide-code-actions-type-signatures -- ghcide-completions -# - ghcide-core # implicitly included in all configurations -# - ghcide-hover-and-symbols # implicitly included in all configurations -- ghcide-type-lenses -- haddockComments -- hlint -- importLens -- moduleName -# - ormolu -- pragmas -- qualifyImportedNames -- refineImports -- rename -- retrie -- splice -- stan -# - stylish-haskell -- tactics +# - alternateNumberFormat +# # - brittany +# - callHierarchy +# - changeTypeSignature +# - class +# - codeRange +# - eval +# - explicitFixity +# # - floskell +# # - fourmolu +# - gadt +# - ghcide-code-actions-bindings +# - ghcide-code-actions-fill-holes +# - ghcide-code-actions-imports-exports +# - ghcide-code-actions-type-signatures +# - ghcide-completions +# # - ghcide-core # implicitly included in all configurations +# # - ghcide-hover-and-symbols # implicitly included in all configurations +# - ghcide-type-lenses +# - haddockComments +# - hlint +# - importLens +# - moduleName +# # - ormolu +# - pragmas +# - qualifyImportedNames +# - refineImports +# - rename +# - retrie +# - splice +# - stan +# # - stylish-haskell +# - tactics diff --git a/cabal.project b/cabal.project index 3c1b731e0a..610f77689a 100644 --- a/cabal.project +++ b/cabal.project @@ -81,13 +81,24 @@ source-repository-package location: https://github.com/wz1000/hie-bios tag: aa73d3d2eb89df0003d2468a105e326d71b62cc7 --- Needed for ghcide-bench until a new release of lsp-test is out +-- Remove me when a new version of lsp is released +source-repository-package + type:git + location: https://github.com/haskell/lsp + subdir: lsp + tag: b0f8596887088b8ab65fc1015c773f45b47234ae + +source-repository-package + type:git + location: https://github.com/haskell/lsp + subdir: lsp-types + tag: b0f8596887088b8ab65fc1015c773f45b47234ae + source-repository-package type:git location: https://github.com/haskell/lsp subdir: lsp-test - tag: c95eb06c70c35f1e13c37ed11a7d9e5b36bfa2e8 - -- https://github.com/haskell/lsp/pull/450 + tag: b0f8596887088b8ab65fc1015c773f45b47234ae allow-newer: -- ghc-9.4 diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index 0329130c88..d4fab31b3c 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -378,7 +378,7 @@ test-suite ghcide-tests record-hasfield if impl(ghc < 9.3) build-depends: ghc-typelits-knownnat - hs-source-dirs: test/cabal test/exe test/src bench/lib + hs-source-dirs: test/cabal test/exe test/src ghc-options: -threaded -Wall -Wno-name-shadowing -O0 -Wno-unticked-promoted-constructors main-is: Main.hs other-modules: diff --git a/ghcide/src/Development/IDE/Types/Location.hs b/ghcide/src/Development/IDE/Types/Location.hs index 24a61d8a27..9891606947 100644 --- a/ghcide/src/Development/IDE/Types/Location.hs +++ b/ghcide/src/Development/IDE/Types/Location.hs @@ -50,11 +50,7 @@ toNormalizedFilePath' "" = emptyFilePath toNormalizedFilePath' fp = LSP.toNormalizedFilePath fp emptyFilePath :: LSP.NormalizedFilePath -#if MIN_VERSION_lsp_types(1,3,0) -emptyFilePath = LSP.normalizedFilePath emptyPathUri "" -#else -emptyFilePath = LSP.NormalizedFilePath emptyPathUri "" -#endif +emptyFilePath = LSP.emptyNormalizedFilePath -- | We use an empty string as a filepath when we don’t have a file. -- However, haskell-lsp doesn’t support that in uriToFilePath and given diff --git a/hls-plugin-api/src/Ide/PluginUtils.hs b/hls-plugin-api/src/Ide/PluginUtils.hs index 617b898fcb..7f8b1c2a7f 100644 --- a/hls-plugin-api/src/Ide/PluginUtils.hs +++ b/hls-plugin-api/src/Ide/PluginUtils.hs @@ -218,10 +218,7 @@ fullRange s = Range startPos endPos lastLine = fromIntegral $ length $ T.lines s subRange :: Range -> Range -> Bool -subRange smallRange range = _start smallRange >= _start range && _end smallRange <= _end range - -positionInRange :: Position -> Range -> Bool -positionInRange p (Range sp ep) = sp <= p && p < ep -- Range's end position is exclusive, see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#range +subRange = isSubrangeOf -- --------------------------------------------------------------------- diff --git a/hls-plugin-api/test/Ide/PluginUtilsTest.hs b/hls-plugin-api/test/Ide/PluginUtilsTest.hs index c6bedfdf28..f868a067d1 100644 --- a/hls-plugin-api/test/Ide/PluginUtilsTest.hs +++ b/hls-plugin-api/test/Ide/PluginUtilsTest.hs @@ -9,21 +9,5 @@ import Test.Tasty.HUnit tests :: TestTree tests = testGroup "PluginUtils" - [ positionInRangeTest - ] - -positionInRangeTest :: TestTree -positionInRangeTest = testGroup "positionInRange" - [ testCase "single line, after the end" $ - positionInRange (Position 1 10) (Range (Position 1 1) (Position 1 3)) @?= False - , testCase "single line, before the begining" $ - positionInRange (Position 1 0) (Range (Position 1 1) (Position 1 6)) @?= False - , testCase "single line, in range" $ - positionInRange (Position 1 5) (Range (Position 1 1) (Position 1 6)) @?= True - , testCase "single line, at the end" $ - positionInRange (Position 1 5) (Range (Position 1 1) (Position 1 5)) @?= False - , testCase "multiline, in range" $ - positionInRange (Position 3 5) (Range (Position 1 1) (Position 5 6)) @?= True - , testCase "multiline, out of range" $ - positionInRange (Position 3 5) (Range (Position 3 6) (Position 4 10)) @?= False + [ ] diff --git a/stack-lts16.yaml b/stack-lts16.yaml index 50120b1841..666089e558 100644 --- a/stack-lts16.yaml +++ b/stack-lts16.yaml @@ -87,9 +87,12 @@ extra-deps: - constraints-extras-0.3.0.2@sha256:013b8d0392582c6ca068e226718a4fe8be8e22321cc0634f6115505bf377ad26,1853 - some-1.0.1@sha256:26e5bab7276f48b25ea8660d3fd1166c0f20fd497dac879a40f408e23211f93e,2055 - unliftio-core-0.2.0.1@sha256:9b3e44ea9aacacbfc35b3b54015af450091916ac3618a41868ebf6546977659a,1082 - - lsp-1.5.0.0 - - lsp-types-1.5.0.0 - - lsp-test-0.14.0.3 + - git: git@github.com:haskell/lsp + commit: b0f8596887088b8ab65fc1015c773f45b47234ae + subdirs: + - lsp + - lsp-types + - lsp-test - stm-containers-1.1.0.4 - stm-hamt-1.2.0.6@sha256:fba86ccb4b45c5706c19b0e1315ba63dcac3b5d71de945ec001ba921fae80061,3972 - primitive-extras-0.10.1 diff --git a/stack-lts19.yaml b/stack-lts19.yaml index c13433ac13..d73ec71973 100644 --- a/stack-lts19.yaml +++ b/stack-lts19.yaml @@ -55,9 +55,12 @@ extra-deps: - refinery-0.4.0.0@sha256:fe3a43add8ff1db5cfffee7e7694c86128b1dfe62c541f26e25a8eadf9585610,1663 - retrie-1.1.0.0 - stylish-haskell-0.14.2.0@sha256:fffe1c13ad4c2678cf28a7470cac5d3bf20c71c36f09969e3e5f186787cceb7c,4321 -- lsp-1.5.0.0 -- lsp-types-1.5.0.0 -- lsp-test-0.14.0.3 +- git: git@github.com:haskell/lsp + commit: b0f8596887088b8ab65fc1015c773f45b47234ae + subdirs: + - lsp + - lsp-types + - lsp-test - co-log-core-0.3.1.0 configure-options: diff --git a/stack.yaml b/stack.yaml index 5a0649322a..75e17da1a3 100644 --- a/stack.yaml +++ b/stack.yaml @@ -41,9 +41,12 @@ extra-deps: - hiedb-0.4.1.0@sha256:fb20c657d9ecc91701b00dffcf4bbd77cb83720a1f9d867badd77ea227973135,2875 - implicit-hie-0.1.2.7@sha256:82bbbb1a8c05f99c8af3c16ac53e80c8648d8bf047b25ed5ce45a135bd736907,3122 - implicit-hie-cradle-0.5.0.0@sha256:4276f60f3a59bc22df03fd918f73bca9f777de9568f85e3a8be8bd7566234a59,2368 -- lsp-1.5.0.0 -- lsp-test-0.14.0.3 -- lsp-types-1.5.0.0 +- git: git@github.com:haskell/lsp + commit: b0f8596887088b8ab65fc1015c773f45b47234ae + subdirs: + - lsp + - lsp-types + - lsp-test - monad-dijkstra-0.1.1.3@sha256:d2fc098d7c122555e726830a12ae0423ac187f89de9228f32e56e2f6fc2238e1,1900 - retrie-1.2.0.1 - co-log-core-0.3.1.0 From 816cd6301e43cda96da44ac8490449b99084961e Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Mon, 12 Sep 2022 15:29:35 +0530 Subject: [PATCH 108/213] Prepare 1.8.0.0 Use hiedb from hackage gitlab-ci: remove workaround for windows 9.2.2 gitlab-ci: remove workaround for windows 9.2.2 hie-bios update hie bios update hie bios bounds hie-bios fixes hie-bios fixes lsp 1.6 cabal.project fixes hie-bios fixes lsp and hie-bios from hackage Mark hie-bios error as expectFail hie-bios fixes hie-bios fixes Stack Stack stack fixes --- .gitlab-ci.yml | 2 +- .gitlab/ci.sh | 14 +- .gitlab/test.sh | 22 +- ChangeLog.md | 262 ++++++++++++++++++ cabal.project | 40 +-- exe/Wrapper.hs | 11 +- ghcide/ghcide.cabal | 16 +- .../session-loader/Development/IDE/Session.hs | 11 +- ghcide/test/ghcide-test-utils.cabal | 2 +- haskell-language-server.cabal | 26 +- hie-compat/hie-compat.cabal | 2 +- hls-graph/hls-graph.cabal | 2 +- hls-plugin-api/hls-plugin-api.cabal | 6 +- hls-test-utils/hls-test-utils.cabal | 10 +- .../hls-alternate-number-format-plugin.cabal | 10 +- .../hls-brittany-plugin.cabal | 8 +- .../hls-call-hierarchy-plugin.cabal | 8 +- .../hls-change-type-signature-plugin.cabal | 6 +- .../hls-class-plugin/hls-class-plugin.cabal | 8 +- .../hls-code-range-plugin.cabal | 8 +- plugins/hls-eval-plugin/hls-eval-plugin.cabal | 8 +- .../hls-explicit-fixity-plugin.cabal | 6 +- .../hls-explicit-imports-plugin.cabal | 6 +- .../hls-floskell-plugin.cabal | 10 +- .../hls-fourmolu-plugin.cabal | 8 +- plugins/hls-gadt-plugin/hls-gadt-plugin.cabal | 6 +- .../hls-haddock-comments-plugin.cabal | 8 +- .../hls-hlint-plugin/hls-hlint-plugin.cabal | 8 +- .../hls-module-name-plugin.cabal | 8 +- .../hls-ormolu-plugin/hls-ormolu-plugin.cabal | 8 +- .../hls-pragmas-plugin.cabal | 8 +- .../hls-qualify-imported-names-plugin.cabal | 8 +- .../hls-refactor-plugin.cabal | 6 +- .../hls-refine-imports-plugin.cabal | 6 +- .../hls-rename-plugin/hls-rename-plugin.cabal | 8 +- .../hls-retrie-plugin/hls-retrie-plugin.cabal | 4 +- .../hls-splice-plugin/hls-splice-plugin.cabal | 8 +- plugins/hls-stan-plugin/hls-stan-plugin.cabal | 2 +- .../hls-stylish-haskell-plugin.cabal | 6 +- .../hls-tactics-plugin.cabal | 8 +- src/Ide/Main.hs | 2 +- stack-lts16.yaml | 14 +- stack-lts19.yaml | 12 +- stack.yaml | 12 +- test/functional/HieBios.hs | 2 +- 45 files changed, 435 insertions(+), 221 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1152d09d36..3d01eb4d7e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,7 +7,7 @@ stages: # https://gitlab.haskell.org/haskell/haskell-language-server/-/pipelines variables: # Commit of ghc/ci-images repository from which to pull Docker images - DOCKER_REV: "4ed1a4f27828ba96a34662dc954335e29b470cd2" + DOCKER_REV: "9e4c540d9e4972a36291dfdf81f079f37d748890" CABAL_INSTALL_VERSION: 3.8.1.0 diff --git a/.gitlab/ci.sh b/.gitlab/ci.sh index 579f2aff15..d5b1026709 100755 --- a/.gitlab/ci.sh +++ b/.gitlab/ci.sh @@ -6,7 +6,7 @@ source "$CI_PROJECT_DIR/.gitlab/common.sh" export GHCUP_INSTALL_BASE_PREFIX="$CI_PROJECT_DIR/toolchain" export CABAL_DIR="$CI_PROJECT_DIR/cabal" -EXE_EXTENSION = "" +EXE_EXTENSION="" case "$(uname)" in MSYS_*|MINGW*) @@ -50,10 +50,10 @@ esac case "$(uname)" in MSYS_*|MINGW*) # workaround for https://gitlab.haskell.org/ghc/ghc/-/issues/21196 - export PATH="${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/bin:${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/usr/bin:$PATH" - ls ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/bin - cp ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/bin/libgcc_s_seh-1.dll ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/bin - cp ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/bin/libwinpthread-1.dll ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/bin + # export PATH="${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/bin:${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/usr/bin:$PATH" + # ls ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/bin + # cp ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/bin/libgcc_s_seh-1.dll ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/bin + # cp ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/bin/libwinpthread-1.dll ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/bin ghc --info # Shorten binary names sed -i.bak -e 's/haskell-language-server/hls/g' \ @@ -76,8 +76,8 @@ case "$(uname)" in mkdir "$CI_PROJECT_DIR/out" - cp "$(cabal list-bin ${args[@]} exe:hls)" "$CI_PROJECT_DIR/out/haskell-language-server-${GHC_VERSION}"$EXE_EXTENSION - cp "$(cabal list-bin ${args[@]} exe:hls-wrapper)" "$CI_PROJECT_DIR/out/haskell-language-server-wrapper"$EXE_EXTENSION + cp "$(cabal list-bin -v0 ${args[@]} exe:hls)" "$CI_PROJECT_DIR/out/haskell-language-server-${GHC_VERSION}"$EXE_EXTENSION + cp "$(cabal list-bin -v0 ${args[@]} exe:hls-wrapper)" "$CI_PROJECT_DIR/out/haskell-language-server-wrapper"$EXE_EXTENSION ;; *) emake --version diff --git a/.gitlab/test.sh b/.gitlab/test.sh index 4260ca78b9..6c6ef0a51f 100644 --- a/.gitlab/test.sh +++ b/.gitlab/test.sh @@ -45,17 +45,17 @@ case "$(uname -s)" in ;; esac -case "$(uname)" in - MSYS_*|MINGW*) - # workaround for https://gitlab.haskell.org/ghc/ghc/-/issues/21196 - export PATH="${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/bin:${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/usr/bin:$PATH" - ls ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/bin - cp ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/bin/libgcc_s_seh-1.dll ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/bin - cp ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/bin/libwinpthread-1.dll ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/bin - ghc --info - ;; - *) ;; -esac +# case "$(uname)" in +# MSYS_*|MINGW*) +# # workaround for https://gitlab.haskell.org/ghc/ghc/-/issues/21196 +# export PATH="${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/bin:${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/usr/bin:$PATH" +# ls ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/bin +# cp ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/bin/libgcc_s_seh-1.dll ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/bin +# cp ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/mingw/bin/libwinpthread-1.dll ${GHCUP_INSTALL_BASE_PREFIX}/ghcup/ghc/${GHC_VERSION}/bin +# ghc --info +# ;; +# *) ;; +# esac # make sure out/ dir is gone, so build host rpaths don't # kick in (TODO: we should probably remove those) diff --git a/ChangeLog.md b/ChangeLog.md index 874cfd7809..39cd922ba3 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,267 @@ # Changelog for haskell-language-server +## 1.8.0.0 + +- Binaries for GHC 9.2.3 and GHC 9.2.4 +- Initial support for GHC 9.4 with binaries for GHC 9.4.1 and GHC 9.4.2 +- Startup time and performance improvements on projects using Template Haskell by serializing intermediate core (#2813) +- Memory usage improvements due to using a packed representation for filepaths (#3067, @kokobd) +- Improvements for hls-class-plugin (#2920, @July541) +- The new hls-gadt-plugin (#2899, @July541) +- Moving code actions from ghcide to the new hls-refactor-plugin (#3091, @wz1000) +- Many more improvements and bug fixes thanks to our contributors! + +### Pull requests merged for 1.8.0.0 + +- Alternate Number Format Plugin buildable with GHC 9.4 +([#3138](https://github.com/haskell/haskell-language-server/pull/3138)) by @drsooch +- Enable a bunch of plugins that build with ghc 9.4 +([#3136](https://github.com/haskell/haskell-language-server/pull/3136)) by @pepeiborra +- Enable support for 9.4 on windows +([#3132](https://github.com/haskell/haskell-language-server/pull/3132)) by @wz1000 +- flake.nix Add ghcide-bench to sourceDirs +([#3125](https://github.com/haskell/haskell-language-server/pull/3125)) by @akshaymankar +- Update hls-retrie-plugin to be usable with 9.2.4. +([#3120](https://github.com/haskell/haskell-language-server/pull/3120)) by @drsooch +- Add link to homepage and issues for `hie-compat` +([#3119](https://github.com/haskell/haskell-language-server/pull/3119)) by @parsonsmatt +- Remove pluginId from getNormalizedFilePath error message +([#3118](https://github.com/haskell/haskell-language-server/pull/3118)) by @drsooch +- HLS benchmarks +([#3117](https://github.com/haskell/haskell-language-server/pull/3117)) by @pepeiborra +- Fix --testing +([#3113](https://github.com/haskell/haskell-language-server/pull/3113)) by @pepeiborra +- Deduplicate HLS plugins +([#3112](https://github.com/haskell/haskell-language-server/pull/3112)) by @pepeiborra +- Do not send Heap Stats to the LSP log +([#3111](https://github.com/haskell/haskell-language-server/pull/3111)) by @pepeiborra +- Send begin progress message synchronously +([#3110](https://github.com/haskell/haskell-language-server/pull/3110)) by @pepeiborra +- Remove unused config in hls-class-plugin +([#3107](https://github.com/haskell/haskell-language-server/pull/3107)) by @July541 +- Support fourmolu-0.8.1.0 +([#3103](https://github.com/haskell/haskell-language-server/pull/3103)) by @brandonchinn178 +- Probe-tools: Print stack ghc version +([#3093](https://github.com/haskell/haskell-language-server/pull/3093)) by @andys8 +- Fix #3047 +([#3092](https://github.com/haskell/haskell-language-server/pull/3092)) by @July541 +- Remove exactprint dependencies from ghcide by introducing hls-refactor-plugin. +([#3091](https://github.com/haskell/haskell-language-server/pull/3091)) by @wz1000 +- Stan: Avoid terminal colors in messages +([#3090](https://github.com/haskell/haskell-language-server/pull/3090)) by @andys8 +- Support ghc-9.2.4 +([#3085](https://github.com/haskell/haskell-language-server/pull/3085)) by @July541 +- Bump Nix flake GHC 9.2.3 to 9.2.4 +([#3081](https://github.com/haskell/haskell-language-server/pull/3081)) by @cydparser +- fix lsp-types benchmark +([#3079](https://github.com/haskell/haskell-language-server/pull/3079)) by @pepeiborra +- Add support for Fourmolu 0.8 +([#3078](https://github.com/haskell/haskell-language-server/pull/3078)) by @brandonchinn178 +- upgrade lsp to 1.5 +([#3072](https://github.com/haskell/haskell-language-server/pull/3072)) by @kokobd +- Bump actions/cache from 2 to 3 +([#3071](https://github.com/haskell/haskell-language-server/pull/3071)) by @dependabot[bot] +- Bump actions/setup-python from 3 to 4 +([#3070](https://github.com/haskell/haskell-language-server/pull/3070)) by @dependabot[bot] +- Run the benchmark suite on GHC 9.2.3 +([#3069](https://github.com/haskell/haskell-language-server/pull/3069)) by @pepeiborra +- Simplify instructions about 'ghcup compile hls' +([#3068](https://github.com/haskell/haskell-language-server/pull/3068)) by @hasufell +- Improve performance of NormalizedFilePath +([#3067](https://github.com/haskell/haskell-language-server/pull/3067)) by @kokobd +- add a prefix to plugin CPP definitions +([#3065](https://github.com/haskell/haskell-language-server/pull/3065)) by @kokobd +- Add Github precommit workflow +([#3060](https://github.com/haskell/haskell-language-server/pull/3060)) by @lunaticare +- Run pre-commit hooks +([#3059](https://github.com/haskell/haskell-language-server/pull/3059)) by @lunaticare +- Fix grammar and spelling errors in configuration.md +([#3056](https://github.com/haskell/haskell-language-server/pull/3056)) by @arsenkhy +- Remove redundant WARNING prefix +([#3055](https://github.com/haskell/haskell-language-server/pull/3055)) by @michaelpj +- fix a typo in src/Ide/Plugin/Class/CodeLens.hs +([#3053](https://github.com/haskell/haskell-language-server/pull/3053)) by @tensorknower69 +- fix record-dot-syntax test +([#3051](https://github.com/haskell/haskell-language-server/pull/3051)) by @coltenwebb +- build(nix): ghc922 -> ghc923 +([#3049](https://github.com/haskell/haskell-language-server/pull/3049)) by @teto +- build(nix): bumped gitignore dependency +([#3048](https://github.com/haskell/haskell-language-server/pull/3048)) by @teto +- Update issue templates +([#3044](https://github.com/haskell/haskell-language-server/pull/3044)) by @michaelpj +- Simplify hlint config +([#3038](https://github.com/haskell/haskell-language-server/pull/3038)) by @michaelpj +- handle trailing comma in import list properly +([#3035](https://github.com/haskell/haskell-language-server/pull/3035)) by @kokobd +- upgrade ghc-check to fix #3002 +([#3034](https://github.com/haskell/haskell-language-server/pull/3034)) by @kokobd +- Fix Stack build with Nix on macOS +([#3031](https://github.com/haskell/haskell-language-server/pull/3031)) by @lunaticare +- haskell-language-server: add lower bound for githash +([#3030](https://github.com/haskell/haskell-language-server/pull/3030)) by @Bodigrim +- hls-eval-plugin: add lower bound for parser-combinators +([#3029](https://github.com/haskell/haskell-language-server/pull/3029)) by @Bodigrim +- hls-fourmolu-plugin: add lower bound for process-extras +([#3028](https://github.com/haskell/haskell-language-server/pull/3028)) by @Bodigrim +- ghcide: lower bounds +([#3025](https://github.com/haskell/haskell-language-server/pull/3025)) by @Bodigrim +- remove all usages of pre-commit-check in nix +([#3024](https://github.com/haskell/haskell-language-server/pull/3024)) by @kokobd +- hls-plugin-api: add lower bounds +([#3022](https://github.com/haskell/haskell-language-server/pull/3022)) by @Bodigrim +- hls-graph: add lower bound for async +([#3021](https://github.com/haskell/haskell-language-server/pull/3021)) by @Bodigrim +- Hlint: CodeAction with isPreferred +([#3018](https://github.com/haskell/haskell-language-server/pull/3018)) by @andys8 +- Record Dot Hover Types +([#3016](https://github.com/haskell/haskell-language-server/pull/3016)) by @coltenwebb +- re-enable haddock +([#3015](https://github.com/haskell/haskell-language-server/pull/3015)) by @kokobd +- add Helix to configuration.md +([#3014](https://github.com/haskell/haskell-language-server/pull/3014)) by @0rphee +- Renaming of indirect references (RecordFieldPuns) +([#3013](https://github.com/haskell/haskell-language-server/pull/3013)) by @OliverMadine +- Revert back to Warning not Error in Logging `ResponseErrors` +([#3009](https://github.com/haskell/haskell-language-server/pull/3009)) by @drsooch +- Disable flaky test on Windows +([#3008](https://github.com/haskell/haskell-language-server/pull/3008)) by @michaelpj +- Improve troubleshooting and installation docs a bit +([#3004](https://github.com/haskell/haskell-language-server/pull/3004)) by @michaelpj +- refactor selection range plugin +([#3003](https://github.com/haskell/haskell-language-server/pull/3003)) by @kokobd +- Hlint more partial functions, and Debug.Trace +([#3000](https://github.com/haskell/haskell-language-server/pull/3000)) by @michaelpj +- Don't use typecheck rule for non FOIs in refine imports plugin +([#2995](https://github.com/haskell/haskell-language-server/pull/2995)) by @wz1000 +- GHC 9.4 compatibility + Multiple Home Units +([#2994](https://github.com/haskell/haskell-language-server/pull/2994)) by @wz1000 +- unify pre-commit hook & update Gitpod config +([#2991](https://github.com/haskell/haskell-language-server/pull/2991)) by @kokobd +- Log response errors returned from Plugins +([#2988](https://github.com/haskell/haskell-language-server/pull/2988)) by @drsooch +- Add associated type families to local completions +([#2987](https://github.com/haskell/haskell-language-server/pull/2987)) by @gasparattila +- Remove some partial functions from Shake.hs +([#2986](https://github.com/haskell/haskell-language-server/pull/2986)) by @michaelpj +- Clean up ghc-9.0 partial support contents +([#2983](https://github.com/haskell/haskell-language-server/pull/2983)) by @July541 +- fix new import position +([#2981](https://github.com/haskell/haskell-language-server/pull/2981)) by @kokobd +- Implement PluginMethod for hard-wired in handlers +([#2977](https://github.com/haskell/haskell-language-server/pull/2977)) by @fendor +- Set up partial functions ratchet +([#2974](https://github.com/haskell/haskell-language-server/pull/2974)) by @michaelpj +- Turn HLS-wrapper into an LSP Server +([#2960](https://github.com/haskell/haskell-language-server/pull/2960)) by @smatting +- More Fourmolu improvements +([#2959](https://github.com/haskell/haskell-language-server/pull/2959)) by @georgefst +- hls-class-plugin: Only create placeholders for unimplemented methods +([#2956](https://github.com/haskell/haskell-language-server/pull/2956)) by @akshaymankar +- Fix Fourmolu 0.7 support +([#2950](https://github.com/haskell/haskell-language-server/pull/2950)) by @georgefst +- Teach HLS about different file extensions +([#2945](https://github.com/haskell/haskell-language-server/pull/2945)) by @fendor +- Support `fourmolu ^>= 0.7` +([#2944](https://github.com/haskell/haskell-language-server/pull/2944)) by @parsonsmatt +- hls-explicit-fixity-plugin +([#2941](https://github.com/haskell/haskell-language-server/pull/2941)) by @July541 +- chore(nix): bump nixpkgs to prevent glibc issues +([#2937](https://github.com/haskell/haskell-language-server/pull/2937)) by @teto +- Support ghc-9.2.3 +([#2936](https://github.com/haskell/haskell-language-server/pull/2936)) by @July541 +- Typo fix, dependecies -> dependencies +([#2934](https://github.com/haskell/haskell-language-server/pull/2934)) by @vikrem +- Update Archlinux installation section +([#2933](https://github.com/haskell/haskell-language-server/pull/2933)) by @marcin-rzeznicki +- docs/installation: Remove unused clone with submodule command +([#2930](https://github.com/haskell/haskell-language-server/pull/2930)) by @sloorush +- Omit more parens for wildcard type signature +([#2929](https://github.com/haskell/haskell-language-server/pull/2929)) by @sergv +- Add `throwPluginError` to Plugin Utilities +([#2924](https://github.com/haskell/haskell-language-server/pull/2924)) by @drsooch +- hls-class-plugin enhancement +([#2920](https://github.com/haskell/haskell-language-server/pull/2920)) by @July541 +- Bump documentation requirements +([#2918](https://github.com/haskell/haskell-language-server/pull/2918)) by @xsebek +- Document eval plugin limitations +([#2917](https://github.com/haskell/haskell-language-server/pull/2917)) by @xsebek +- Replace TextDocumentIdentifier with Uri in getNormalizedFilePath +([#2912](https://github.com/haskell/haskell-language-server/pull/2912)) by @July541 +- Fix hover format +([#2911](https://github.com/haskell/haskell-language-server/pull/2911)) by @July541 +- Fix multiline eval plugin padding +([#2910](https://github.com/haskell/haskell-language-server/pull/2910)) by @xsebek +- Stan integration #258 +([#2908](https://github.com/haskell/haskell-language-server/pull/2908)) by @uhbif19 +- A plugin for GADT syntax converter +([#2899](https://github.com/haskell/haskell-language-server/pull/2899)) by @July541 +- Fix DisplayTHWarning error +([#2895](https://github.com/haskell/haskell-language-server/pull/2895)) by @pepeiborra +- Enable hls-eval-plugin test on ghc-9.2.2 +([#2893](https://github.com/haskell/haskell-language-server/pull/2893)) by @July541 +- nix update +([#2892](https://github.com/haskell/haskell-language-server/pull/2892)) by @michaelpj +- Build hls-alternate-number-format-plugin with stack.yaml +([#2891](https://github.com/haskell/haskell-language-server/pull/2891)) by @July541 +- Modify ghcide requirements of hls-change-type-signature-plugin +([#2889](https://github.com/haskell/haskell-language-server/pull/2889)) by @July541 +- Fix hls-call-hierarchy-plugin tests +([#2888](https://github.com/haskell/haskell-language-server/pull/2888)) by @July541 +- Add .txt files as extra-source-files for hls-change-type-signature-plugin +([#2887](https://github.com/haskell/haskell-language-server/pull/2887)) by @cdepillabout +- Prefer Data.HashSet.member to Data.Foldable.elem +([#2886](https://github.com/haskell/haskell-language-server/pull/2886)) by @sergv +- no longer disable -dynamic in CI +([#2885](https://github.com/haskell/haskell-language-server/pull/2885)) by @pepeiborra +- hls-pragmas-plugin requires ghcide >= 1.7 +([#2884](https://github.com/haskell/haskell-language-server/pull/2884)) by @Bodigrim +- Make iface-error-test-1 less flaky +([#2882](https://github.com/haskell/haskell-language-server/pull/2882)) by @pepeiborra +- hls-haddock-comments does not support ghc-exactprint >= 1.0 +([#2878](https://github.com/haskell/haskell-language-server/pull/2878)) by @Bodigrim +- Restore compat. with prettyprinter 1.6 +([#2877](https://github.com/haskell/haskell-language-server/pull/2877)) by @pepeiborra +- ghcide requires ghc-exactprint >= 1.4 +([#2876](https://github.com/haskell/haskell-language-server/pull/2876)) by @Bodigrim +- ghcide needs prettyprinter-1.7 to build +([#2875](https://github.com/haskell/haskell-language-server/pull/2875)) by @juhp +- Review project stack descriptors according to #2533 +([#2874](https://github.com/haskell/haskell-language-server/pull/2874)) by @pepeiborra +- hls-call-hierarchy-plugin Patch release +([#2873](https://github.com/haskell/haskell-language-server/pull/2873)) by @pepeiborra +- Expand input to pragma if available +([#2871](https://github.com/haskell/haskell-language-server/pull/2871)) by @July541 +- Fix hanging redundant import on Unicode function +([#2870](https://github.com/haskell/haskell-language-server/pull/2870)) by @drsooch +- Compatibility with older aeson releases +([#2868](https://github.com/haskell/haskell-language-server/pull/2868)) by @pepeiborra +- simplify hlint plugin Cabal descriptor +([#2867](https://github.com/haskell/haskell-language-server/pull/2867)) by @pepeiborra +- Consolidate all cabal.project files +([#2866](https://github.com/haskell/haskell-language-server/pull/2866)) by @pepeiborra +- release script fixes +([#2861](https://github.com/haskell/haskell-language-server/pull/2861)) by @wz1000 +- Support hls-hlint-plugin and hls-stylish-plugin for ghc9.0 and ghc9.2 +([#2854](https://github.com/haskell/haskell-language-server/pull/2854)) by @July541 +- Bump haskell/actions from 1 to 2 +([#2852](https://github.com/haskell/haskell-language-server/pull/2852)) by @dependabot[bot] +- Add scripts for releases and final 1.7 tweaks +([#2850](https://github.com/haskell/haskell-language-server/pull/2850)) by @wz1000 +- Fix Completion document format +([#2848](https://github.com/haskell/haskell-language-server/pull/2848)) by @July541 +- Improve name export code action +([#2847](https://github.com/haskell/haskell-language-server/pull/2847)) by @sergv +- Update plugin support table +([#2840](https://github.com/haskell/haskell-language-server/pull/2840)) by @michaelpj +- Unify showSDocUnsafe +([#2830](https://github.com/haskell/haskell-language-server/pull/2830)) by @July541 +- ghcide: remove redundant env NIX_GHC_LIBDIR +([#2819](https://github.com/haskell/haskell-language-server/pull/2819)) by @sloorush +- Serialize Core +([#2813](https://github.com/haskell/haskell-language-server/pull/2813)) by @wz1000 +- Expose runtime metrics via EKG +([#2267](https://github.com/haskell/haskell-language-server/pull/2267)) by @pepeiborra + ## 1.7.0.0 - Distribute dynamically linked binaries for HLS to avoid statically linking against GLIBC diff --git a/cabal.project b/cabal.project index 610f77689a..625fcc827d 100644 --- a/cabal.project +++ b/cabal.project @@ -48,7 +48,7 @@ package * write-ghc-environment-files: never -index-state: 2022-08-29T06:53:13Z +index-state: 2022-09-14T16:53:13Z constraints: -- For GHC 9.4, older versions of entropy fail to build on Windows @@ -71,35 +71,6 @@ source-repository-package tag: 7a0af7a8fd38045fd15fb13445bdcc7085325460 -- https://github.com/tibbe/ekg-json/pull/12 -source-repository-package - type:git - location: https://github.com/wz1000/hiedb - tag: 67b92df2359558091df9102db5b701327308b930 - -source-repository-package - type:git - location: https://github.com/wz1000/hie-bios - tag: aa73d3d2eb89df0003d2468a105e326d71b62cc7 - --- Remove me when a new version of lsp is released -source-repository-package - type:git - location: https://github.com/haskell/lsp - subdir: lsp - tag: b0f8596887088b8ab65fc1015c773f45b47234ae - -source-repository-package - type:git - location: https://github.com/haskell/lsp - subdir: lsp-types - tag: b0f8596887088b8ab65fc1015c773f45b47234ae - -source-repository-package - type:git - location: https://github.com/haskell/lsp - subdir: lsp-test - tag: b0f8596887088b8ab65fc1015c773f45b47234ae - allow-newer: -- ghc-9.4 Chart-diagrams:lens, @@ -117,16 +88,9 @@ allow-newer: ekg-json:base, ghc-paths:Cabal, haddock-library:base, - hie-bios:aeson, - hie-bios:ghc, monoid-extras:base, monoid-subclasses:vector, svg-builder:base, uuid:time, vector-space:base, - - -- ghc-9.2 - ---------- - hiedb:base, - - ekg-wai:time + ekg-wai:time, diff --git a/exe/Wrapper.hs b/exe/Wrapper.hs index e4f50f2ad4..b0a6484c85 100644 --- a/exe/Wrapper.hs +++ b/exe/Wrapper.hs @@ -47,13 +47,12 @@ import Development.IDE.LSP.LanguageServer (runLanguageServer) import qualified Development.IDE.Main as Main import Development.IDE.Types.Logger (Logger (Logger), Pretty (pretty), - Priority (Debug, Error, Info, Warning), + Priority (Info), Recorder (logger_), WithPriority (WithPriority), cmapWithPrio, makeDefaultStderrRecorder) import GHC.Stack.Types (emptyCallStack) -import HIE.Bios.Internal.Log import Ide.Plugin.Config (Config) import Language.LSP.Server (LspM) import qualified Language.LSP.Server as LSP @@ -316,11 +315,3 @@ launchErrorLSP errorMsg = do exitHandler :: IO () -> LSP.Handlers (ErrorLSPM c) exitHandler exit = LSP.notificationHandler SExit $ const $ liftIO exit - -hlsWrapperLogger :: Logger -hlsWrapperLogger = Logger $ \pri txt -> - case pri of - Debug -> debugm (T.unpack txt) - Info -> logm (T.unpack txt) - Warning -> warningm (T.unpack txt) - Error -> errorm (T.unpack txt) diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index d4fab31b3c..ad9584e68b 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -2,7 +2,7 @@ cabal-version: 3.0 build-type: Simple category: Development name: ghcide -version: 1.7.0.1 +version: 1.8.0.0 license: Apache-2.0 license-file: LICENSE author: Digital Asset and Ghcide contributors @@ -64,13 +64,13 @@ library Glob, haddock-library >= 1.8 && < 1.11, hashable, - hie-compat ^>= 0.2.0.0, - hls-plugin-api ^>= 1.4, + hie-compat ^>= 0.3.0.0, + hls-plugin-api ^>= 1.5, lens, list-t, - hiedb == 0.4.1.*, - lsp-types ^>= 1.5.0.0, - lsp ^>= 1.5.0.0 , + hiedb == 0.4.2.*, + lsp-types ^>= 1.6.0.0, + lsp ^>= 1.6.0.0 , monoid-subclasses, mtl, optparse-applicative, @@ -81,7 +81,7 @@ library regex-tdfa >= 1.3.1.0, text-rope, safe-exceptions, - hls-graph ^>= 1.7, + hls-graph ^>= 1.8, sorted-list, sqlite-simple, stm, @@ -105,7 +105,7 @@ library ghc-check >=0.5.0.8, ghc-paths, cryptohash-sha1 >=0.11.100 && <0.12, - hie-bios ^>= 0.9.1, + hie-bios ^>= 0.11.0, implicit-hie-cradle ^>= 0.3.0.5 || ^>= 0.5, base16-bytestring >=0.1.1 && <1.1 if os(windows) diff --git a/ghcide/session-loader/Development/IDE/Session.hs b/ghcide/session-loader/Development/IDE/Session.hs index da0a7dd5e3..9da4b5a88d 100644 --- a/ghcide/session-loader/Development/IDE/Session.hs +++ b/ghcide/session-loader/Development/IDE/Session.hs @@ -68,12 +68,14 @@ import Development.IDE.Types.Logger (Pretty (pretty), Priority (Debug, Error, Info, Warning), Recorder, WithPriority, logWith, nest, vcat, - viaShow, (<+>)) + viaShow, (<+>), + toCologActionWithPrio, cmapWithPrio) import Development.IDE.Types.Options import GHC.Check import qualified HIE.Bios as HieBios +import qualified HIE.Bios.Types as HieBios import HIE.Bios.Environment hiding (getCacheDir) -import HIE.Bios.Types +import HIE.Bios.Types hiding (Log) import Hie.Implicit.Cradle (loadImplicitHieCradle) import Language.LSP.Server import Language.LSP.Types @@ -123,6 +125,7 @@ data Log | LogCradle !(Cradle Void) | LogNoneCradleFound FilePath | LogNewComponentCache !(([FileDiagnostic], Maybe HscEnvEq), DependencyInfo) + | LogHieBios HieBios.Log deriving instance Show Log instance Pretty Log where @@ -192,6 +195,7 @@ instance Pretty Log where "Cradle:" <+> viaShow cradle LogNewComponentCache componentCache -> "New component cache HscEnvEq:" <+> viaShow componentCache + LogHieBios log -> pretty log -- | Bump this version number when making changes to the format of the data stored in hiedb hiedbDataVersion :: String @@ -716,7 +720,8 @@ cradleToOptsAndLibDir recorder cradle file = do -- noneCradleFoundMessage f = T.pack $ "none cradle found for " <> f <> ", ignoring the file" -- Start off by getting the session options logWith recorder Debug $ LogCradle cradle - cradleRes <- HieBios.getCompilerOptions file cradle + let logger = toCologActionWithPrio $ cmapWithPrio LogHieBios recorder + cradleRes <- HieBios.getCompilerOptions logger file cradle case cradleRes of CradleSuccess r -> do -- Now get the GHC lib dir diff --git a/ghcide/test/ghcide-test-utils.cabal b/ghcide/test/ghcide-test-utils.cabal index 3bdac32071..6910923f38 100644 --- a/ghcide/test/ghcide-test-utils.cabal +++ b/ghcide/test/ghcide-test-utils.cabal @@ -3,7 +3,7 @@ cabal-version: 3.0 build-type: Simple category: Development name: ghcide-test-utils -version: 1.7.0.1 +version: 1.8.0.0 license: Apache-2.0 license-file: LICENSE author: Digital Asset and Ghcide contributors diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 45776a3482..5b281496bf 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -1,7 +1,7 @@ cabal-version: 3.0 category: Development name: haskell-language-server -version: 1.7.0.0 +version: 1.8.0.0 synopsis: LSP server for GHC description: Please see the README on GitHub at @@ -71,12 +71,12 @@ library , cryptohash-sha1 , data-default , ghc - , ghcide ^>=1.7 + , ghcide ^>=1.8 , githash >=0.1.6.1 , lsp , hie-bios , hiedb - , hls-plugin-api ^>=1.4 + , hls-plugin-api ^>=1.5 , optparse-applicative , optparse-simple , process @@ -240,22 +240,22 @@ flag dynamic common class if flag(class) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) - build-depends: hls-class-plugin ^>= 1.0 + build-depends: hls-class-plugin ^>= 1.1 cpp-options: -Dhls_class common callHierarchy if flag(callHierarchy) - build-depends: hls-call-hierarchy-plugin ^>= 1.0 + build-depends: hls-call-hierarchy-plugin ^>= 1.1 cpp-options: -Dhls_callHierarchy common haddockComments if flag(haddockComments) && (impl(ghc < 9.2.1) || flag(ignore-plugins-ghc-bounds)) - build-depends: hls-haddock-comments-plugin ^>= 1.0 + build-depends: hls-haddock-comments-plugin ^>= 1.1 cpp-options: -Dhls_haddockComments common eval if flag(eval) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) - build-depends: hls-eval-plugin ^>= 1.2 + build-depends: hls-eval-plugin ^>= 1.3 cpp-options: -Dhls_eval common importLens @@ -280,12 +280,12 @@ common retrie common tactic if flag(tactic) && (impl(ghc < 9.2.1) || flag(ignore-plugins-ghc-bounds)) - build-depends: hls-tactics-plugin >=1.2.0.0 && <1.7 + build-depends: hls-tactics-plugin ^>= 1.7 cpp-options: -Dhls_tactic common hlint if flag(hlint) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) - build-depends: hls-hlint-plugin ^>= 1.0 + build-depends: hls-hlint-plugin ^>= 1.1 cpp-options: -Dhls_hlint common stan @@ -295,7 +295,7 @@ common stan common moduleName if flag(moduleName) - build-depends: hls-module-name-plugin ^>= 1.0 + build-depends: hls-module-name-plugin ^>= 1.1 cpp-options: -Dhls_moduleName common pragmas @@ -310,7 +310,7 @@ common splice common alternateNumberFormat if flag(alternateNumberFormat) - build-depends: hls-alternate-number-format-plugin ^>= 1.1 + build-depends: hls-alternate-number-format-plugin ^>= 1.2 cpp-options: -Dhls_alternateNumberFormat common qualifyImportedNames @@ -347,7 +347,7 @@ common floskell common fourmolu if flag(fourmolu) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) - build-depends: hls-fourmolu-plugin ^>= 1.0 + build-depends: hls-fourmolu-plugin ^>= 1.1 cpp-options: -Dhls_fourmolu common ormolu @@ -534,7 +534,7 @@ test-suite func-test , lens , lens-aeson , ghcide - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.4 , lsp-types , aeson , hls-plugin-api diff --git a/hie-compat/hie-compat.cabal b/hie-compat/hie-compat.cabal index ab94fb7a77..4573c410f2 100644 --- a/hie-compat/hie-compat.cabal +++ b/hie-compat/hie-compat.cabal @@ -1,6 +1,6 @@ cabal-version: 1.22 name: hie-compat -version: 0.2.1.1 +version: 0.3.0.0 synopsis: HIE files for GHC 8.6 and other HIE file backports license: Apache-2.0 description: diff --git a/hls-graph/hls-graph.cabal b/hls-graph/hls-graph.cabal index 1cb975dc51..b2f90f20f4 100644 --- a/hls-graph/hls-graph.cabal +++ b/hls-graph/hls-graph.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-graph -version: 1.7.0.0 +version: 1.8.0.0 synopsis: Haskell Language Server internal graph API description: Please see the README on GitHub at diff --git a/hls-plugin-api/hls-plugin-api.cabal b/hls-plugin-api/hls-plugin-api.cabal index 67e57b578b..1e65328e77 100644 --- a/hls-plugin-api/hls-plugin-api.cabal +++ b/hls-plugin-api/hls-plugin-api.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-plugin-api -version: 1.4.0.0 +version: 1.5.0.0 synopsis: Haskell Language Server API for plugin communication description: Please see the README on GitHub at @@ -46,10 +46,10 @@ library , filepath , ghc , hashable - , hls-graph ^>= 1.7 + , hls-graph ^>= 1.8 , lens , lens-aeson - , lsp ^>=1.5.0.0 + , lsp ^>=1.6.0.0 , opentelemetry >=0.4 , optparse-applicative , process diff --git a/hls-test-utils/hls-test-utils.cabal b/hls-test-utils/hls-test-utils.cabal index 3335f5d8d3..aac00b6fab 100644 --- a/hls-test-utils/hls-test-utils.cabal +++ b/hls-test-utils/hls-test-utils.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-test-utils -version: 1.3.0.0 +version: 1.4.0.0 synopsis: Utilities used in the tests of Haskell Language Server description: Please see the README on GitHub at @@ -41,13 +41,13 @@ library , directory , extra , filepath - , ghcide ^>=1.6 || ^>=1.7 + , ghcide ^>=1.8 , hls-graph - , hls-plugin-api ^>=1.3 || ^>=1.4 + , hls-plugin-api ^>=1.5 , lens - , lsp ^>=1.5.0.0 + , lsp ^>=1.6.0.0 , lsp-test ^>=0.14 - , lsp-types ^>=1.5.0.0 + , lsp-types ^>=1.6.0.0 , tasty , tasty-expected-failure , tasty-golden diff --git a/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal b/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal index 49f827774d..4d6487e274 100644 --- a/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal +++ b/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-alternate-number-format-plugin -version: 1.1.0.1 +version: 1.2.0.0 synopsis: Provide Alternate Number Formats plugin for Haskell Language Server description: Please see the README on GitHub at @@ -27,13 +27,13 @@ library aeson , base >=4.12 && < 5 , containers - , ghcide ^>=1.6 || ^>=1.7 + , ghcide ^>= 1.8 , ghc-boot-th , hls-graph - , hls-plugin-api ^>=1.4 + , hls-plugin-api ^>= 1.5 , hie-compat , lens - , lsp + , lsp ^>=1.6 , mtl , regex-tdfa , syb @@ -59,7 +59,7 @@ test-suite tests , base >=4.12 && < 5 , filepath , hls-alternate-number-format-plugin - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.3 || ^>= 1.4 , lsp , QuickCheck , regex-tdfa diff --git a/plugins/hls-brittany-plugin/hls-brittany-plugin.cabal b/plugins/hls-brittany-plugin/hls-brittany-plugin.cabal index 9928d1f74b..872f3c1c12 100644 --- a/plugins/hls-brittany-plugin/hls-brittany-plugin.cabal +++ b/plugins/hls-brittany-plugin/hls-brittany-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-brittany-plugin -version: 1.0.2.1 +version: 1.0.2.2 synopsis: Integration with the Brittany code formatter description: Please see the README on GitHub at @@ -29,8 +29,8 @@ library , brittany >=0.13.1.0 && < 0.14.0.1 || > 0.14.0.1 , filepath , ghc-boot-th - , ghcide ^>= 1.6 || ^>= 1.7 - , hls-plugin-api ^>= 1.3 || ^>= 1.4 + , ghcide ^>= 1.6 || ^>= 1.7 || ^>= 1.8 + , hls-plugin-api ^>= 1.3 || ^>= 1.4 || ^>= 1.5 , lens , lsp-types , text @@ -56,4 +56,4 @@ test-suite tests , base , filepath , hls-brittany-plugin - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.3 || ^>= 1.4 diff --git a/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal b/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal index ce2fb87f72..f0ecdc0ab2 100644 --- a/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal +++ b/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-call-hierarchy-plugin -version: 1.0.3.1 +version: 1.1.0.0 synopsis: Call hierarchy plugin for Haskell Language Server description: Please see the README on GitHub at @@ -31,9 +31,9 @@ library , containers , extra , ghc - , ghcide ^>= 1.6 || ^>= 1.7 + , ghcide ^>= 1.8 , hiedb - , hls-plugin-api ^>= 1.2 || ^>= 1.3 || ^>= 1.4 + , hls-plugin-api ^>= 1.5 , lens , lsp >=1.2.0.1 , sqlite-simple @@ -57,7 +57,7 @@ test-suite tests , extra , filepath , hls-call-hierarchy-plugin - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.4 , lens , lsp , lsp-test diff --git a/plugins/hls-change-type-signature-plugin/hls-change-type-signature-plugin.cabal b/plugins/hls-change-type-signature-plugin/hls-change-type-signature-plugin.cabal index 36449f006e..6c453854b7 100644 --- a/plugins/hls-change-type-signature-plugin/hls-change-type-signature-plugin.cabal +++ b/plugins/hls-change-type-signature-plugin/hls-change-type-signature-plugin.cabal @@ -24,8 +24,8 @@ library hs-source-dirs: src build-depends: , base >=4.12 && < 5 - , ghcide ^>=1.7 - , hls-plugin-api ^>=1.4 + , ghcide ^>=1.8 + , hls-plugin-api ^>=1.5 , lsp-types , regex-tdfa , syb @@ -57,7 +57,7 @@ test-suite tests , base >=4.12 && < 5 , filepath , hls-change-type-signature-plugin - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.4 , lsp , QuickCheck , regex-tdfa diff --git a/plugins/hls-class-plugin/hls-class-plugin.cabal b/plugins/hls-class-plugin/hls-class-plugin.cabal index 245522a9cd..d388c3a7a8 100644 --- a/plugins/hls-class-plugin/hls-class-plugin.cabal +++ b/plugins/hls-class-plugin/hls-class-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-class-plugin -version: 1.0.3.0 +version: 1.1.0.0 synopsis: Class/instance management plugin for Haskell Language Server @@ -39,10 +39,10 @@ library , deepseq , extra , ghc - , ghcide ^>=1.7 + , ghcide ^>=1.8 , ghc-boot-th , hls-graph - , hls-plugin-api ^>=1.4 + , hls-plugin-api ^>=1.5 , lens , lsp , text @@ -78,6 +78,6 @@ test-suite tests , ghcide , hls-class-plugin , hls-plugin-api - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.4 , lens , lsp-types diff --git a/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal b/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal index e51ad55268..50849b35e2 100644 --- a/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal +++ b/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal @@ -35,9 +35,9 @@ library , containers , deepseq , extra - , ghcide ^>=1.6 || ^>=1.7 + , ghcide ^>=1.8 , hashable - , hls-plugin-api ^>=1.3 || ^>=1.4 + , hls-plugin-api ^>=1.5 , lens , lsp , mtl @@ -60,9 +60,9 @@ test-suite tests , bytestring , containers , filepath - , ghcide ^>=1.6 || ^>=1.7 + , ghcide ^>=1.8 , hls-code-range-plugin - , hls-test-utils ^>=1.2 || ^>=1.3 + , hls-test-utils ^>=1.4 , lens , lsp , lsp-test diff --git a/plugins/hls-eval-plugin/hls-eval-plugin.cabal b/plugins/hls-eval-plugin/hls-eval-plugin.cabal index a016aade1e..b923cf6517 100644 --- a/plugins/hls-eval-plugin/hls-eval-plugin.cabal +++ b/plugins/hls-eval-plugin/hls-eval-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-eval-plugin -version: 1.2.2.0 +version: 1.3.0.0 synopsis: Eval plugin for Haskell Language Server description: Please see the README on GitHub at @@ -70,10 +70,10 @@ library , ghc , ghc-boot-th , ghc-paths - , ghcide ^>=1.7 + , ghcide ^>=1.8 , hashable , hls-graph - , hls-plugin-api ^>=1.4 + , hls-plugin-api ^>=1.5 , lens , lsp , lsp-types @@ -119,7 +119,7 @@ test-suite tests , filepath , hls-eval-plugin , hls-plugin-api - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.4 , lens , lsp-types , text diff --git a/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal b/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal index 1b185c28e8..9bbab0d8c7 100644 --- a/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal +++ b/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal @@ -26,9 +26,9 @@ library , deepseq , extra , ghc - , ghcide ^>=1.7 + , ghcide ^>=1.8 , hashable - , hls-plugin-api ^>=1.4 + , hls-plugin-api ^>=1.5 , lsp >=1.2.0.1 , text @@ -50,5 +50,5 @@ test-suite tests , base , filepath , hls-explicit-fixity-plugin - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.4 , text diff --git a/plugins/hls-explicit-imports-plugin/hls-explicit-imports-plugin.cabal b/plugins/hls-explicit-imports-plugin/hls-explicit-imports-plugin.cabal index 3918ee5ac0..c7b8e65fbe 100644 --- a/plugins/hls-explicit-imports-plugin/hls-explicit-imports-plugin.cabal +++ b/plugins/hls-explicit-imports-plugin/hls-explicit-imports-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.2 name: hls-explicit-imports-plugin -version: 1.1.0.0 +version: 1.1.0.1 synopsis: Explicit imports plugin for Haskell Language Server description: Please see the README on GitHub at @@ -25,9 +25,9 @@ library , containers , deepseq , ghc - , ghcide ^>=1.7 + , ghcide ^>=1.7 || ^>=1.8 , hls-graph - , hls-plugin-api ^>=1.4 + , hls-plugin-api ^>=1.4 || ^>=1.5 , lsp , text , unordered-containers diff --git a/plugins/hls-floskell-plugin/hls-floskell-plugin.cabal b/plugins/hls-floskell-plugin/hls-floskell-plugin.cabal index e13fa98e4a..c27181926e 100644 --- a/plugins/hls-floskell-plugin/hls-floskell-plugin.cabal +++ b/plugins/hls-floskell-plugin/hls-floskell-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-floskell-plugin -version: 1.0.1.1 +version: 1.0.1.2 synopsis: Integration with the Floskell code formatter description: Please see the README on GitHub at @@ -26,9 +26,9 @@ library build-depends: , base >=4.12 && <5 , floskell ^>=0.10 - , ghcide ^>=1.6 || ^>=1.7 - , hls-plugin-api ^>=1.3 || ^>=1.4 - , lsp-types + , ghcide ^>=1.6 || ^>=1.7 || ^>= 1.8 + , hls-plugin-api ^>=1.3 || ^>=1.4 || ^>= 1.5 + , lsp-types ^>=1.6 , text , transformers @@ -48,4 +48,4 @@ test-suite tests , base , filepath , hls-floskell-plugin - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.3 || ^>= 1.4 diff --git a/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal b/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal index e84cdccf7a..039ee1615f 100644 --- a/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal +++ b/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-fourmolu-plugin -version: 1.0.3.0 +version: 1.1.0.0 synopsis: Integration with the Fourmolu code formatter description: Please see the README on GitHub at @@ -38,8 +38,8 @@ library , fourmolu ^>=0.3 || ^>=0.4 || ^>= 0.6 || ^>= 0.7 || ^>= 0.8 , ghc , ghc-boot-th - , ghcide ^>=1.7 - , hls-plugin-api ^>=1.4 + , ghcide ^>=1.8 + , hls-plugin-api ^>=1.5 , lens , lsp , process-extras >= 0.7.1 @@ -66,5 +66,5 @@ test-suite tests , filepath , hls-fourmolu-plugin , hls-plugin-api - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.4 , lsp-test diff --git a/plugins/hls-gadt-plugin/hls-gadt-plugin.cabal b/plugins/hls-gadt-plugin/hls-gadt-plugin.cabal index 47ff630eed..3db304c269 100644 --- a/plugins/hls-gadt-plugin/hls-gadt-plugin.cabal +++ b/plugins/hls-gadt-plugin/hls-gadt-plugin.cabal @@ -30,10 +30,10 @@ library , containers , extra , ghc - , ghcide ^>= 1.7 + , ghcide ^>= 1.8 , ghc-boot-th , ghc-exactprint - , hls-plugin-api ^>= 1.4 + , hls-plugin-api ^>= 1.5 , hls-refactor-plugin , lens , lsp >=1.2.0.1 @@ -63,7 +63,7 @@ test-suite tests , base , filepath , hls-gadt-plugin - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.4 , lens , lsp , lsp-test diff --git a/plugins/hls-haddock-comments-plugin/hls-haddock-comments-plugin.cabal b/plugins/hls-haddock-comments-plugin/hls-haddock-comments-plugin.cabal index 6de352816b..5b97c917b5 100644 --- a/plugins/hls-haddock-comments-plugin/hls-haddock-comments-plugin.cabal +++ b/plugins/hls-haddock-comments-plugin/hls-haddock-comments-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-haddock-comments-plugin -version: 1.0.1.0 +version: 1.1.0.0 synopsis: Haddock comments plugin for Haskell Language Server description: Please see the README on GitHub at @@ -33,8 +33,8 @@ library , containers , ghc , ghc-exactprint < 1 - , ghcide ^>=1.6 || ^>=1.7 - , hls-plugin-api ^>=1.3 || ^>=1.4 + , ghcide ^>=1.8 + , hls-plugin-api ^>=1.5 , hls-refactor-plugin , lsp-types , text @@ -59,5 +59,5 @@ test-suite tests , base , filepath , hls-haddock-comments-plugin - , hls-test-utils ^>=1.2 || ^>=1.3 + , hls-test-utils ^>=1.2 || ^>=1.3 || ^>= 1.4 , text diff --git a/plugins/hls-hlint-plugin/hls-hlint-plugin.cabal b/plugins/hls-hlint-plugin/hls-hlint-plugin.cabal index 5fca22d6e0..4de8669b9f 100644 --- a/plugins/hls-hlint-plugin/hls-hlint-plugin.cabal +++ b/plugins/hls-hlint-plugin/hls-hlint-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-hlint-plugin -version: 1.0.4.0 +version: 1.1.0.0 synopsis: Hlint integration plugin with Haskell Language Server description: Please see the README on GitHub at @@ -45,10 +45,10 @@ library , extra , filepath , ghc-exactprint >=0.6.3.4 - , ghcide ^>=1.7 + , ghcide ^>=1.8 , hashable , hlint < 3.5 - , hls-plugin-api ^>=1.4 + , hls-plugin-api ^>=1.5 , hslogger , lens , lsp @@ -93,7 +93,7 @@ test-suite tests , filepath , hls-hlint-plugin , hls-plugin-api - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.4 , lens , lsp-types , text diff --git a/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal b/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal index e9dc57f577..223fb76370 100644 --- a/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal +++ b/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-module-name-plugin -version: 1.0.2.0 +version: 1.1.0.0 synopsis: Module name plugin for Haskell Language Server description: Please see the README on GitHub at @@ -28,8 +28,8 @@ library , base >=4.12 && <5 , directory , filepath - , ghcide ^>=1.7 - , hls-plugin-api ^>=1.4 + , ghcide ^>=1.8 + , hls-plugin-api ^>=1.5 , lsp , text , transformers @@ -48,4 +48,4 @@ test-suite tests , base , filepath , hls-module-name-plugin - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.4 diff --git a/plugins/hls-ormolu-plugin/hls-ormolu-plugin.cabal b/plugins/hls-ormolu-plugin/hls-ormolu-plugin.cabal index 49bfb4959b..6d436087ad 100644 --- a/plugins/hls-ormolu-plugin/hls-ormolu-plugin.cabal +++ b/plugins/hls-ormolu-plugin/hls-ormolu-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-ormolu-plugin -version: 1.0.2.1 +version: 1.0.2.2 synopsis: Integration with the Ormolu code formatter description: Please see the README on GitHub at @@ -28,8 +28,8 @@ library , filepath , ghc , ghc-boot-th - , ghcide ^>=1.6 || ^>=1.7 - , hls-plugin-api ^>=1.3 || ^>=1.4 + , ghcide ^>=1.6 || ^>=1.7 || ^>= 1.8 + , hls-plugin-api ^>=1.3 || ^>=1.4 || ^>= 1.5 , lens , lsp , ormolu ^>=0.1.2 || ^>= 0.2 || ^>= 0.3 || ^>= 0.5 @@ -51,5 +51,5 @@ test-suite tests , base , filepath , hls-ormolu-plugin - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.3 || ^>=1.4 , lsp-types diff --git a/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal b/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal index 02f256a4db..af2f13d639 100644 --- a/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal +++ b/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-pragmas-plugin -version: 1.0.2.1 +version: 1.0.3.0 synopsis: Pragmas plugin for Haskell Language Server description: Please see the README on GitHub at @@ -26,8 +26,8 @@ library , extra , fuzzy , ghc - , ghcide ^>=1.7 - , hls-plugin-api ^>=1.3 || ^>=1.4 + , ghcide ^>=1.8 + , hls-plugin-api ^>=1.5 , lens , lsp , text @@ -48,7 +48,7 @@ test-suite tests , base , filepath , hls-pragmas-plugin - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.4 , lens , lsp-types , text diff --git a/plugins/hls-qualify-imported-names-plugin/hls-qualify-imported-names-plugin.cabal b/plugins/hls-qualify-imported-names-plugin/hls-qualify-imported-names-plugin.cabal index 0425eff7df..4c00fb91f4 100644 --- a/plugins/hls-qualify-imported-names-plugin/hls-qualify-imported-names-plugin.cabal +++ b/plugins/hls-qualify-imported-names-plugin/hls-qualify-imported-names-plugin.cabal @@ -18,7 +18,6 @@ extra-source-files: test/data/*.yaml library - buildable: True exposed-modules: Ide.Plugin.QualifyImportedNames hs-source-dirs: src build-depends: @@ -27,9 +26,9 @@ library , containers , deepseq , ghc - , ghcide ^>=1.6 || ^>=1.7 + , ghcide ^>=1.6 || ^>=1.7 || ^>= 1.8 , hls-graph - , hls-plugin-api ^>=1.3 || ^>=1.4 + , hls-plugin-api ^>=1.3 || ^>=1.4 || ^>= 1.5 , lsp , text , unordered-containers @@ -42,7 +41,6 @@ library TypeOperators test-suite tests - buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test @@ -53,4 +51,4 @@ test-suite tests , text , filepath , hls-qualify-imported-names-plugin - , hls-test-utils ^>= 1.2 || ^>=1.3 + , hls-test-utils ^>= 1.2 || ^>=1.3 || ^>= 1.4 diff --git a/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal b/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal index d848e77bbb..d61d1593ee 100644 --- a/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal +++ b/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal @@ -63,8 +63,8 @@ library , ghc-boot , regex-tdfa , text-rope - , ghcide ^>=1.7 - , hls-plugin-api ^>=1.3 || ^>=1.4 + , ghcide ^>=1.8 + , hls-plugin-api ^>=1.3 || ^>=1.4 || ^>= 1.5 , lsp , text , transformers @@ -98,7 +98,7 @@ test-suite tests , base , filepath , hls-refactor-plugin - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.4 , lens , lsp-types , text diff --git a/plugins/hls-refine-imports-plugin/hls-refine-imports-plugin.cabal b/plugins/hls-refine-imports-plugin/hls-refine-imports-plugin.cabal index 7d9a656e69..47abdac196 100644 --- a/plugins/hls-refine-imports-plugin/hls-refine-imports-plugin.cabal +++ b/plugins/hls-refine-imports-plugin/hls-refine-imports-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-refine-imports-plugin -version: 1.0.2.0 +version: 1.0.3.0 synopsis: Refine imports plugin for Haskell Language Server description: Please see the README on GitHub at @@ -25,10 +25,10 @@ library , containers , deepseq , ghc - , ghcide ^>=1.7 + , ghcide ^>=1.8 , hls-explicit-imports-plugin ^>=1.1 , hls-graph - , hls-plugin-api ^>=1.4 + , hls-plugin-api ^>=1.5 , lsp , text , unordered-containers diff --git a/plugins/hls-rename-plugin/hls-rename-plugin.cabal b/plugins/hls-rename-plugin/hls-rename-plugin.cabal index 43f8397fbe..c3f063b332 100644 --- a/plugins/hls-rename-plugin/hls-rename-plugin.cabal +++ b/plugins/hls-rename-plugin/hls-rename-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-rename-plugin -version: 1.0.0.2 +version: 1.0.1.0 synopsis: Rename plugin for Haskell Language Server description: Please see the README on GitHub at @@ -29,10 +29,10 @@ library , extra , ghc , ghc-exactprint - , ghcide ^>= 1.6 || ^>=1.7 + , ghcide ^>=1.8 , hashable , hiedb - , hls-plugin-api ^>= 1.3 || ^>=1.4 + , hls-plugin-api ^>= 1.3 || ^>=1.4 || ^>= 1.5 , hls-refactor-plugin , lsp , lsp-types @@ -61,4 +61,4 @@ test-suite tests , filepath , hls-plugin-api , hls-rename-plugin - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.4 diff --git a/plugins/hls-retrie-plugin/hls-retrie-plugin.cabal b/plugins/hls-retrie-plugin/hls-retrie-plugin.cabal index 887af8665d..d484973769 100644 --- a/plugins/hls-retrie-plugin/hls-retrie-plugin.cabal +++ b/plugins/hls-retrie-plugin/hls-retrie-plugin.cabal @@ -28,9 +28,9 @@ library , directory , extra , ghc - , ghcide ^>=1.7 + , ghcide ^>=1.8 , hashable - , hls-plugin-api ^>=1.4 + , hls-plugin-api ^>=1.5 , lsp , lsp-types , retrie >=0.1.1.0 diff --git a/plugins/hls-splice-plugin/hls-splice-plugin.cabal b/plugins/hls-splice-plugin/hls-splice-plugin.cabal index 0ea73506de..fa6d14a3a7 100644 --- a/plugins/hls-splice-plugin/hls-splice-plugin.cabal +++ b/plugins/hls-splice-plugin/hls-splice-plugin.cabal @@ -1,6 +1,6 @@ cabal-version: 2.4 name: hls-splice-plugin -version: 1.0.1.0 +version: 1.0.2.0 synopsis: HLS Plugin to expand TemplateHaskell Splices and QuasiQuotes @@ -42,8 +42,8 @@ library , foldl , ghc , ghc-exactprint - , ghcide ^>=1.6 || ^>=1.7 - , hls-plugin-api ^>=1.3 || ^>=1.4 + , ghcide ^>=1.8 + , hls-plugin-api ^>= 1.5 , hls-refactor-plugin , lens , lsp @@ -73,5 +73,5 @@ test-suite tests , base , filepath , hls-splice-plugin - , hls-test-utils ^>=1.2 || ^>=1.3 + , hls-test-utils ^>= 1.4 , text diff --git a/plugins/hls-stan-plugin/hls-stan-plugin.cabal b/plugins/hls-stan-plugin/hls-stan-plugin.cabal index 5c149e18b4..fbc0e882bb 100644 --- a/plugins/hls-stan-plugin/hls-stan-plugin.cabal +++ b/plugins/hls-stan-plugin/hls-stan-plugin.cabal @@ -70,7 +70,7 @@ test-suite test , filepath , hls-stan-plugin , hls-plugin-api - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.3 || ^>= 1.4 , lens , lsp-types , text diff --git a/plugins/hls-stylish-haskell-plugin/hls-stylish-haskell-plugin.cabal b/plugins/hls-stylish-haskell-plugin/hls-stylish-haskell-plugin.cabal index f7b229b4e7..9f28cd4bb9 100644 --- a/plugins/hls-stylish-haskell-plugin/hls-stylish-haskell-plugin.cabal +++ b/plugins/hls-stylish-haskell-plugin/hls-stylish-haskell-plugin.cabal @@ -28,8 +28,8 @@ library , filepath , ghc , ghc-boot-th - , ghcide ^>=1.7 - , hls-plugin-api ^>=1.4 + , ghcide ^>=1.7 || ^>= 1.8 + , hls-plugin-api ^>=1.4 || ^>= 1.5 , lsp-types , stylish-haskell ^>=0.12 || ^>=0.13 || ^>=0.14.2 , text @@ -50,4 +50,4 @@ test-suite tests , base , filepath , hls-stylish-haskell-plugin - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.3 || ^>=1.4 diff --git a/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal b/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal index bbb7e9e104..f62dbc5786 100644 --- a/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal +++ b/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal @@ -1,7 +1,7 @@ cabal-version: 2.4 category: Development name: hls-tactics-plugin -version: 1.6.2.0 +version: 1.7.0.0 synopsis: Wingman plugin for Haskell Language Server description: Please see the README on GitHub at @@ -86,9 +86,9 @@ library , ghc-boot-th , ghc-exactprint , ghc-source-gen ^>=0.4.1 - , ghcide ^>=1.7 + , ghcide ^>= 1.8 , hls-graph - , hls-plugin-api ^>=1.4 + , hls-plugin-api ^>=1.5 , hls-refactor-plugin , hyphenation , lens @@ -169,7 +169,7 @@ test-suite tests , ghcide , hls-plugin-api , hls-tactics-plugin - , hls-test-utils ^>=1.3 + , hls-test-utils ^>=1.4 , hspec , hspec-expectations , lens diff --git a/src/Ide/Main.hs b/src/Ide/Main.hs index 92bedba4fa..7e03357418 100644 --- a/src/Ide/Main.hs +++ b/src/Ide/Main.hs @@ -27,7 +27,7 @@ import Development.IDE.Types.Logger as G import qualified Development.IDE.Types.Options as Ghcide import GHC.Stack (emptyCallStack) import qualified HIE.Bios.Environment as HieBios -import HIE.Bios.Types +import HIE.Bios.Types hiding (Log) import Ide.Arguments import Ide.Plugin.ConfigUtils (pluginsToDefaultConfig, pluginsToVSCodeExtensionSchema) diff --git a/stack-lts16.yaml b/stack-lts16.yaml index 666089e558..e947f3244f 100644 --- a/stack-lts16.yaml +++ b/stack-lts16.yaml @@ -58,7 +58,6 @@ extra-deps: - ghc-trace-events-0.1.2.1 - haskell-src-exts-1.21.1 - heapsize-0.3.0 - - hie-bios-0.9.1 - hlint-3.2.8 - HsYAML-aeson-0.2.0.0@rev:2 - hoogle-5.0.17.11 @@ -78,7 +77,7 @@ extra-deps: - temporary-1.2.1.1 - th-compat-0.1.2@sha256:3d55de1adc542c1a870c9ada90da2fbbe5f4e8bcd3eed545a55c3df9311b32a8,2854 - bytestring-encoding-0.1.0.0@sha256:460b49779fbf0112e8e2f1753c1ed9131eb18827600c298f4d6bb51c4e8c1c0d,1727 - - hiedb-0.4.1.0 + - hiedb-0.4.2.0 - sqlite-simple-0.4.18.0@sha256:3ceea56375c0a3590c814e411a4eb86943f8d31b93b110ca159c90689b6b39e5,3002 - direct-sqlite-2.3.26@sha256:04e835402f1508abca383182023e4e2b9b86297b8533afbd4e57d1a5652e0c23,3718 - dependent-map-0.4.0.0@sha256:ca2b131046f4340a1c35d138c5a003fe4a5be96b14efc26291ed35fd08c62221,1657 @@ -87,12 +86,6 @@ extra-deps: - constraints-extras-0.3.0.2@sha256:013b8d0392582c6ca068e226718a4fe8be8e22321cc0634f6115505bf377ad26,1853 - some-1.0.1@sha256:26e5bab7276f48b25ea8660d3fd1166c0f20fd497dac879a40f408e23211f93e,2055 - unliftio-core-0.2.0.1@sha256:9b3e44ea9aacacbfc35b3b54015af450091916ac3618a41868ebf6546977659a,1082 - - git: git@github.com:haskell/lsp - commit: b0f8596887088b8ab65fc1015c773f45b47234ae - subdirs: - - lsp - - lsp-types - - lsp-test - stm-containers-1.1.0.4 - stm-hamt-1.2.0.6@sha256:fba86ccb4b45c5706c19b0e1315ba63dcac3b5d71de945ec001ba921fae80061,3972 - primitive-extras-0.10.1 @@ -107,6 +100,11 @@ extra-deps: - trial-tomland-0.0.0.0@sha256:743a9baaa36891ed3a44618fdfd5bc4ed9afc39cf9b9fa23ea1b96f3787f5ec0,2526 - text-rope-0.2 - co-log-core-0.3.1.0 + - lsp-1.6.0.0 + - lsp-types-1.6.0.0 + - lsp-test-0.14.1.0 + - hie-bios-0.11.0 + - prettyprinter-1.7.1@sha256:9c43c9d8c3cd9f445596e5a2379574bba87f935a4d1fa41b5407ee3cf4edc743,6987 configure-options: ghcide: diff --git a/stack-lts19.yaml b/stack-lts19.yaml index d73ec71973..320cc5c6c6 100644 --- a/stack-lts19.yaml +++ b/stack-lts19.yaml @@ -46,7 +46,7 @@ extra-deps: - ghc-lib-parser-9.2.4.20220729 - ghc-lib-parser-ex-9.2.0.4 - heapsize-0.3.0.1@sha256:0b69aa97a46d819b700ac7b145f3b5493c3565cf2c5b8298682238d405d0326e,1417 -- hiedb-0.4.1.0@sha256:fb20c657d9ecc91701b00dffcf4bbd77cb83720a1f9d867badd77ea227973135,2875 +- hiedb-0.4.2.0 - hlint-3.4 - implicit-hie-0.1.2.7@sha256:82bbbb1a8c05f99c8af3c16ac53e80c8648d8bf047b25ed5ce45a135bd736907,3122 - implicit-hie-cradle-0.5.0.0@sha256:4276f60f3a59bc22df03fd918f73bca9f777de9568f85e3a8be8bd7566234a59,2368 @@ -55,13 +55,11 @@ extra-deps: - refinery-0.4.0.0@sha256:fe3a43add8ff1db5cfffee7e7694c86128b1dfe62c541f26e25a8eadf9585610,1663 - retrie-1.1.0.0 - stylish-haskell-0.14.2.0@sha256:fffe1c13ad4c2678cf28a7470cac5d3bf20c71c36f09969e3e5f186787cceb7c,4321 -- git: git@github.com:haskell/lsp - commit: b0f8596887088b8ab65fc1015c773f45b47234ae - subdirs: - - lsp - - lsp-types - - lsp-test - co-log-core-0.3.1.0 +- lsp-1.6.0.0 +- lsp-types-1.6.0.0 +- lsp-test-0.14.1.0 +- hie-bios-0.11.0 configure-options: ghcide: diff --git a/stack.yaml b/stack.yaml index 75e17da1a3..33c545e3bb 100644 --- a/stack.yaml +++ b/stack.yaml @@ -38,18 +38,16 @@ packages: extra-deps: - floskell-0.10.6@sha256:e77d194189e8540abe2ace2c7cb8efafc747ca35881a2fefcbd2d40a1292e036,3819 - heapsize-0.3.0.1@sha256:0b69aa97a46d819b700ac7b145f3b5493c3565cf2c5b8298682238d405d0326e,1417 -- hiedb-0.4.1.0@sha256:fb20c657d9ecc91701b00dffcf4bbd77cb83720a1f9d867badd77ea227973135,2875 +- hiedb-0.4.2.0 - implicit-hie-0.1.2.7@sha256:82bbbb1a8c05f99c8af3c16ac53e80c8648d8bf047b25ed5ce45a135bd736907,3122 - implicit-hie-cradle-0.5.0.0@sha256:4276f60f3a59bc22df03fd918f73bca9f777de9568f85e3a8be8bd7566234a59,2368 -- git: git@github.com:haskell/lsp - commit: b0f8596887088b8ab65fc1015c773f45b47234ae - subdirs: - - lsp - - lsp-types - - lsp-test - monad-dijkstra-0.1.1.3@sha256:d2fc098d7c122555e726830a12ae0423ac187f89de9228f32e56e2f6fc2238e1,1900 - retrie-1.2.0.1 - co-log-core-0.3.1.0 +- lsp-1.6.0.0 +- lsp-types-1.6.0.0 +- lsp-test-0.14.1.0 +- hie-bios-0.11.0 # currently needed for ghcide>extra, etc. allow-newer: true diff --git a/test/functional/HieBios.hs b/test/functional/HieBios.hs index df742abcdb..ea4d2515bf 100644 --- a/test/functional/HieBios.hs +++ b/test/functional/HieBios.hs @@ -24,7 +24,7 @@ tests = testGroup "hie-bios" [ @? "found hover text for main" _ -> error $ "Unexpected hover contents: " ++ show hoverContents - , testCase "reports errors in hie.yaml" $ do + , expectFailBecause "hie-bios 0.11 has poor error messages" $ testCase "reports errors in hie.yaml" $ do writeFile (hieBiosErrorPath "hie.yaml") "" runSession hlsCommand fullCaps hieBiosErrorPath $ do _ <- openDoc "Foo.hs" "haskell" From 276fb623d4c31ce930d441b59a69595301e0a03d Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Tue, 13 Sep 2022 23:28:19 +0530 Subject: [PATCH 109/213] gitlab-ci: workaround centos failure --- .gitlab/ci.sh | 1 + cabal.project | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/.gitlab/ci.sh b/.gitlab/ci.sh index d5b1026709..751b09c7ea 100755 --- a/.gitlab/ci.sh +++ b/.gitlab/ci.sh @@ -80,6 +80,7 @@ case "$(uname)" in cp "$(cabal list-bin -v0 ${args[@]} exe:hls-wrapper)" "$CI_PROJECT_DIR/out/haskell-language-server-wrapper"$EXE_EXTENSION ;; *) + sed -i.bak -e '/DELETE MARKER FOR CI/,/END DELETE/d' cabal.project # see comment in cabal.project emake --version emake GHCUP=ghcup hls emake GHCUP=ghcup bindist diff --git a/cabal.project b/cabal.project index 625fcc827d..2a2ee425a6 100644 --- a/cabal.project +++ b/cabal.project @@ -65,11 +65,16 @@ constraints: -- This is benign and won't affect our ability to release to Hackage, -- because we only depend on `ekg-json` when a non-default flag -- is turned on. +-- DELETE MARKER FOR CI +-- centos7 has an old version of git which cabal doesn't +-- support. We delete these lines in gitlab ci to workaround +-- this issue, as this is not necessary to build our binaries. source-repository-package type:git location: https://github.com/pepeiborra/ekg-json tag: 7a0af7a8fd38045fd15fb13445bdcc7085325460 -- https://github.com/tibbe/ekg-json/pull/12 +-- END DELETE allow-newer: -- ghc-9.4 From 5a734dfb7f2ae12a1dc85a3bc0d0ef0a16125862 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Tue, 13 Sep 2022 23:36:38 +0530 Subject: [PATCH 110/213] gitlab-ci: upgrade to fedora33 from fedora27 --- .gitlab-ci.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3d01eb4d7e..73a9d50c18 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -80,10 +80,10 @@ workflow: - x86_64-linux image: "registry.gitlab.haskell.org/ghc/ci-images/x86_64-linux-centos7:$DOCKER_REV" -.x86_64-linux-fedora27: +.x86_64-linux-fedora33: tags: - x86_64-linux - image: "registry.gitlab.haskell.org/ghc/ci-images/x86_64-linux-fedora27:$DOCKER_REV" + image: "registry.gitlab.haskell.org/ghc/ci-images/x86_64-linux-fedora33:$DOCKER_REV" .x86_64-linux-alpine: tags: @@ -268,35 +268,35 @@ test-x86_64-linux-centos7: - sudo yum install -y tree ###################### -# x86_64 linux fedora27 +# x86_64 linux fedora33 ###################### -build-x86_64-linux-fedora27: +build-x86_64-linux-fedora33: extends: - .build - - .x86_64-linux-fedora27 + - .x86_64-linux-fedora33 before_script: - sudo dnf install -y patchelf tree variables: ADD_CABAL_ARGS: "--enable-split-sections" -tar-x86_64-linux-fedora27: +tar-x86_64-linux-fedora33: extends: - .artifacts - - .x86_64-linux-fedora27 + - .x86_64-linux-fedora33 stage: tar - needs: ["build-x86_64-linux-fedora27"] + needs: ["build-x86_64-linux-fedora33"] script: - ./.gitlab/tar.sh variables: - TARBALL_ARCHIVE_SUFFIX: x86_64-fedora27-linux + TARBALL_ARCHIVE_SUFFIX: x86_64-fedora33-linux TARBALL_EXT: tar.xz -test-x86_64-linux-fedora27: +test-x86_64-linux-fedora33: extends: - .test - - .x86_64-linux-fedora27 - needs: ["tar-x86_64-linux-fedora27"] + - .x86_64-linux-fedora33 + needs: ["tar-x86_64-linux-fedora33"] before_script: - sudo dnf install -y tree From 0ebfeb574c64898de6443f88f2b765c30ff61b89 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Wed, 14 Sep 2022 02:32:26 +0530 Subject: [PATCH 111/213] Add base bound for ghcide-test-utils --- ghcide/test/ghcide-test-utils.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/test/ghcide-test-utils.cabal b/ghcide/test/ghcide-test-utils.cabal index 6910923f38..f1f38818bb 100644 --- a/ghcide/test/ghcide-test-utils.cabal +++ b/ghcide/test/ghcide-test-utils.cabal @@ -25,7 +25,7 @@ library default-language: Haskell2010 build-depends: aeson, - base, + base > 4.9 && < 5, containers, data-default, directory, From ddb21a0c8d4e657c4b81ce250239bccf28fc9524 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Wed, 14 Sep 2022 02:44:59 +0530 Subject: [PATCH 112/213] Remove internal library haskell-language-server:plugins --- haskell-language-server.cabal | 139 ++++++++++++++++------------------ 1 file changed, 64 insertions(+), 75 deletions(-) diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 5b281496bf..917770c777 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -50,45 +50,6 @@ common pedantic if flag(pedantic) ghc-options: -Werror -library - import: common-deps - -- configuration - , warnings - , pedantic - exposed-modules: - Ide.Arguments - Ide.Main - Ide.Version - - other-modules: Paths_haskell_language_server - autogen-modules: Paths_haskell_language_server - hs-source-dirs: src - build-depends: - , async - , base16-bytestring - , bytestring - , containers - , cryptohash-sha1 - , data-default - , ghc - , ghcide ^>=1.8 - , githash >=0.1.6.1 - , lsp - , hie-bios - , hiedb - , hls-plugin-api ^>=1.5 - , optparse-applicative - , optparse-simple - , process - , hls-graph - , safe-exceptions - , sqlite-simple - , unordered-containers - , aeson-pretty - - default-language: Haskell2010 - default-extensions: DataKinds, TypeOperators - -- Plugin flags are designed for 'cabal install haskell-language-server': -- - Bulk flags should be default:False -- - Individual flags should be default:True @@ -370,42 +331,71 @@ common refactor build-depends: hls-refactor-plugin ^>= 1.0 cpp-options: -Dhls_refactor -library plugins - import: common-deps +library + import: common-deps -- configuration - , warnings - , pedantic - -- plugins - , callHierarchy - , changeTypeSignature - , class - , haddockComments - , eval - , importLens - , refineImports - , rename - , retrie - , tactic - , hlint - , stan - , moduleName - , pragmas - , splice - , alternateNumberFormat - , qualifyImportedNames - , codeRange - , gadt - , explicitFixity - , floskell - , fourmolu - , ormolu - , stylishHaskell - , brittany - , refactor - exposed-modules: HlsPlugins - hs-source-dirs: src + , warnings + , pedantic + -- plugins + , callHierarchy + , changeTypeSignature + , class + , haddockComments + , eval + , importLens + , refineImports + , rename + , retrie + , tactic + , hlint + , stan + , moduleName + , pragmas + , splice + , alternateNumberFormat + , qualifyImportedNames + , codeRange + , gadt + , explicitFixity + , floskell + , fourmolu + , ormolu + , stylishHaskell + , brittany + , refactor + + exposed-modules: + Ide.Arguments + Ide.Main + Ide.Version + HlsPlugins + + other-modules: Paths_haskell_language_server + autogen-modules: Paths_haskell_language_server + hs-source-dirs: src + build-depends: + , async + , base16-bytestring + , bytestring + , containers + , cryptohash-sha1 + , data-default + , ghc + , ghcide ^>=1.8 + , githash >=0.1.6.1 + , lsp + , hie-bios + , hiedb + , hls-plugin-api ^>=1.5 + , optparse-applicative + , optparse-simple + , process + , hls-graph + , safe-exceptions + , sqlite-simple + , unordered-containers + , aeson-pretty - build-depends: ghcide, hls-plugin-api default-language: Haskell2010 default-extensions: DataKinds, TypeOperators @@ -452,7 +442,6 @@ executable haskell-language-server , ghcide , hashable , haskell-language-server - , haskell-language-server:plugins , lsp , hie-bios , hiedb @@ -630,7 +619,7 @@ benchmark benchmark extra, filepath, ghcide-bench, - haskell-language-server:plugins, + haskell-language-server, hls-plugin-api, lens, lens-aeson, From 5d35a740b2fd70b225aaa94541ff22c3ed3e060a Mon Sep 17 00:00:00 2001 From: wz1000 Date: Wed, 14 Sep 2022 13:44:32 +0530 Subject: [PATCH 113/213] Release script fixes (#3154) --- release/fetch_gitlab.py | 5 +++-- release/upload.sh | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/release/fetch_gitlab.py b/release/fetch_gitlab.py index 1232e26d6d..7aa18c9527 100644 --- a/release/fetch_gitlab.py +++ b/release/fetch_gitlab.py @@ -41,13 +41,14 @@ def fetch_artifacts(release: str, pipeline_id: int, logging.info(f'artifact archive for job {job.name} (job {job.id}) is empty') continue - dest = dest_dir / f'haskell-language-server-{release}-{platform}.tar.xz' + extension = 'zip' if job.name.endswith('windows') else 'tar.xz' + dest = dest_dir / f'haskell-language-server-{release}-{platform}.{extension}' if dest.exists(): logging.info(f'bindist {dest} already exists') continue subprocess.run(['unzip', '-bo', zip_name, '-d', destdir]) - bindist_files = list(destdir.glob('*/haskell-language-server*.tar.xz')) + bindist_files = list(destdir.glob(f'*/haskell-language-server*.{extension}')) if len(bindist_files) == 0: logging.warn(f'Bindist does not exist') continue diff --git a/release/upload.sh b/release/upload.sh index c83eaaa950..29f6849757 100755 --- a/release/upload.sh +++ b/release/upload.sh @@ -35,7 +35,7 @@ fi echo HLS version $ver -host="webhost.haskell.org" +host="gitlab-storage.haskell.org" usage() { echo "Usage: [rel_name=] SIGNING_KEY= $0 " From 4ce8f99bcde8fee46878b4650874538fd19721ea Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Thu, 15 Sep 2022 11:44:02 +0200 Subject: [PATCH 114/213] Remove unused build-depends and install warnings (#3155) * Remove unused build-depends and install warnings * Remove -Werror --- ghcide/ghcide.cabal | 22 ++++++++-------------- hls-graph/hls-graph.cabal | 6 ++++-- hls-plugin-api/hls-plugin-api.cabal | 3 ++- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index ad9584e68b..54f417055e 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -71,7 +71,6 @@ library hiedb == 0.4.2.*, lsp-types ^>= 1.6.0.0, lsp ^>= 1.6.0.0 , - monoid-subclasses, mtl, optparse-applicative, parallel, @@ -230,6 +229,9 @@ library exposed-modules: Development.IDE.GHC.Compat.CPP + if impl(ghc >= 9) + ghc-options: -Wunused-packages + if flag(ekg) build-depends: ekg-wai, @@ -271,28 +273,15 @@ executable ghcide "-with-rtsopts=-I0 -A128M -T" main-is: Main.hs build-depends: - hiedb, - aeson, base == 4.*, data-default, - directory, extra, - filepath, gitrev, - safe-exceptions, - ghc, - hashable, lsp, lsp-types, - heapsize, - hie-bios, hls-plugin-api, ghcide, - lens, optparse-applicative, - hls-graph, - text, - unordered-containers, other-modules: Arguments Paths_ghcide @@ -322,6 +311,9 @@ executable ghcide ekg-wai, ekg-core, cpp-options: -DMONITORING_EKG + if impl(ghc >= 9) + ghc-options: -Wunused-packages + test-suite ghcide-tests type: exitcode-stdio-1.0 @@ -378,6 +370,8 @@ test-suite ghcide-tests record-hasfield if impl(ghc < 9.3) build-depends: ghc-typelits-knownnat + if impl(ghc >= 9) + ghc-options: -Wunused-packages hs-source-dirs: test/cabal test/exe test/src ghc-options: -threaded -Wall -Wno-name-shadowing -O0 -Wno-unticked-promoted-constructors main-is: Main.hs diff --git a/hls-graph/hls-graph.cabal b/hls-graph/hls-graph.cabal index b2f90f20f4..10c0cc4b32 100644 --- a/hls-graph/hls-graph.cabal +++ b/hls-graph/hls-graph.cabal @@ -76,7 +76,6 @@ library , js-flot , js-jquery , list-t - , primitive , stm , stm-containers , time @@ -91,7 +90,8 @@ library template-haskell if flag(stm-stats) cpp-options: -DSTM_STATS - + if impl(ghc >= 9) + ghc-options: -Wunused-packages ghc-options: -Wall -Wredundant-constraints -Wno-name-shadowing @@ -136,3 +136,5 @@ test-suite tests , text , unordered-containers build-tool-depends: hspec-discover:hspec-discover -any + if impl(ghc >= 9) + ghc-options: -Wunused-packages diff --git a/hls-plugin-api/hls-plugin-api.cabal b/hls-plugin-api/hls-plugin-api.cabal index 1e65328e77..4b451bda42 100644 --- a/hls-plugin-api/hls-plugin-api.cabal +++ b/hls-plugin-api/hls-plugin-api.cabal @@ -52,7 +52,6 @@ library , lsp ^>=1.6.0.0 , opentelemetry >=0.4 , optparse-applicative - , process , regex-tdfa >=1.3.1.0 , text , transformers @@ -70,6 +69,8 @@ library if flag(pedantic) ghc-options: -Werror + if impl(ghc >= 9) + ghc-options: -Wunused-packages default-language: Haskell2010 default-extensions: From 1568ce7b0adfbd55fbef286c66ca931019ad36ab Mon Sep 17 00:00:00 2001 From: Matt Russell Date: Thu, 15 Sep 2022 04:37:15 -0700 Subject: [PATCH 115/213] Fixes the flake deps to align with cabal (#3163) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- flake.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index 5c661fe66e..5d0ec5955d 100644 --- a/flake.nix +++ b/flake.nix @@ -21,11 +21,11 @@ # List of hackage dependencies lsp = { - url = "https://hackage.haskell.org/package/lsp-1.5.0.0/lsp-1.5.0.0.tar.gz"; + url = "https://hackage.haskell.org/package/lsp-1.6.0.0/lsp-1.6.0.0.tar.gz"; flake = false; }; lsp-types = { - url = "https://hackage.haskell.org/package/lsp-types-1.5.0.0/lsp-types-1.5.0.0.tar.gz"; + url = "https://hackage.haskell.org/package/lsp-types-1.6.0.0/lsp-types-1.6.0.0.tar.gz"; flake = false; }; lsp-test = { @@ -85,7 +85,7 @@ flake = false; }; hie-bios = { - url = "https://hackage.haskell.org/package/hie-bios-0.9.1/hie-bios-0.9.1.tar.gz"; + url = "https://hackage.haskell.org/package/hie-bios-0.11.0/hie-bios-0.11.0.tar.gz"; flake = false; }; myst-parser = { From 2bd1863eea528044fec0b0d9b1067214d9041e6d Mon Sep 17 00:00:00 2001 From: Kobayashi Date: Thu, 15 Sep 2022 23:12:51 +0800 Subject: [PATCH 116/213] unescape printable characters (#3140) * unescape printable characters * add comments * add tests * improve the parser * simplify code & add more docs --- .../Development/IDE/GHC/Compat/Outputable.hs | 6 +-- ghcide/src/Development/IDE/GHC/Util.hs | 14 +++++-- hls-plugin-api/hls-plugin-api.cabal | 2 + hls-plugin-api/src/Ide/PluginUtils.hs | 39 ++++++++++++++++++- hls-plugin-api/test/Ide/PluginUtilsTest.hs | 26 ++++++++++++- 5 files changed, 78 insertions(+), 9 deletions(-) diff --git a/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs b/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs index 7fb9264422..0dd10fc9a3 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Outputable.hs @@ -79,9 +79,9 @@ import qualified Outputable as Out import SrcLoc #endif #if MIN_VERSION_ghc(9,3,0) -import GHC.Utils.Logger -import GHC.Driver.Config.Diagnostic -import Data.Maybe +import Data.Maybe +import GHC.Driver.Config.Diagnostic +import GHC.Utils.Logger #endif -- | A compatible function to print `Outputable` instances diff --git a/ghcide/src/Development/IDE/GHC/Util.hs b/ghcide/src/Development/IDE/GHC/Util.hs index 8dd99b8bde..69cc2adf77 100644 --- a/ghcide/src/Development/IDE/GHC/Util.hs +++ b/ghcide/src/Development/IDE/GHC/Util.hs @@ -81,6 +81,7 @@ import GHC.IO.Exception import GHC.IO.Handle.Internals import GHC.IO.Handle.Types import GHC.Stack +import Ide.PluginUtils (unescape) import System.Environment.Blank (getEnvDefault) import System.FilePath import System.IO.Unsafe @@ -287,10 +288,17 @@ instance Outputable SDoc where #endif -- | Print a GHC value in `defaultUserStyle` without unique symbols. +-- It uses `showSDocUnsafe` with `unsafeGlobalDynFlags` internally. -- --- This is the most common print utility, will print with a user-friendly style like: `a_a4ME` as `a`. +-- This is the most common print utility. +-- It will do something additionally compared to what the 'Outputable' instance does. -- --- It internal using `showSDocUnsafe` with `unsafeGlobalDynFlags`. +-- 1. print with a user-friendly style: `a_a4ME` as `a`. +-- 2. unescape escape sequences of printable unicode characters within a pair of double quotes printOutputable :: Outputable a => a -> T.Text -printOutputable = T.pack . printWithoutUniques +printOutputable = + -- IfaceTyLit from GHC.Iface.Type implements Outputable with 'show'. + -- Showing a String escapes non-ascii printable characters. We unescape it here. + -- More discussion at https://github.com/haskell/haskell-language-server/issues/3115. + unescape . T.pack . printWithoutUniques {-# INLINE printOutputable #-} diff --git a/hls-plugin-api/hls-plugin-api.cabal b/hls-plugin-api/hls-plugin-api.cabal index 4b451bda42..217e7ae30f 100644 --- a/hls-plugin-api/hls-plugin-api.cabal +++ b/hls-plugin-api/hls-plugin-api.cabal @@ -56,6 +56,7 @@ library , text , transformers , unordered-containers + , megaparsec > 9 if os(windows) build-depends: Win32 @@ -91,4 +92,5 @@ test-suite tests , tasty , tasty-hunit , tasty-rerun + , text , lsp-types diff --git a/hls-plugin-api/src/Ide/PluginUtils.hs b/hls-plugin-api/src/Ide/PluginUtils.hs index 7f8b1c2a7f..5c93407974 100644 --- a/hls-plugin-api/src/Ide/PluginUtils.hs +++ b/hls-plugin-api/src/Ide/PluginUtils.hs @@ -32,6 +32,7 @@ module Ide.PluginUtils handleMaybe, handleMaybeM, throwPluginError, + unescape, ) where @@ -43,10 +44,12 @@ import Control.Monad.Trans.Except (ExceptT, runExceptT, throwE) import Data.Algorithm.Diff import Data.Algorithm.DiffOutput import Data.Bifunctor (Bifunctor (first)) +import Data.Char (isPrint, showLitChar) +import Data.Functor (void) import qualified Data.HashMap.Strict as H -import Data.List (find) import Data.String (IsString (fromString)) import qualified Data.Text as T +import Data.Void (Void) import Ide.Plugin.Config import Ide.Plugin.Properties import Ide.Types @@ -57,6 +60,9 @@ import Language.LSP.Types hiding SemanticTokensEdit (_start)) import qualified Language.LSP.Types as J import Language.LSP.Types.Capabilities +import qualified Text.Megaparsec as P +import qualified Text.Megaparsec.Char as P +import qualified Text.Megaparsec.Char.Lexer as P -- --------------------------------------------------------------------- @@ -255,3 +261,34 @@ pluginResponse :: Monad m => ExceptT String m a -> m (Either ResponseError a) pluginResponse = fmap (first (\msg -> ResponseError InternalError (fromString msg) Nothing)) . runExceptT + +-- --------------------------------------------------------------------- + +type TextParser = P.Parsec Void T.Text + +-- | Unescape printable escape sequences within double quotes. +-- This is useful if you have to call 'show' indirectly, and it escapes some characters which you would prefer to +-- display as is. +unescape :: T.Text -> T.Text +unescape input = + case P.runParser escapedTextParser "inline" input of + Left _ -> input + Right strs -> T.pack strs + +-- | Parser for a string that contains double quotes. Returns unescaped string. +escapedTextParser :: TextParser String +escapedTextParser = concat <$> P.many (outsideStringLiteral P.<|> stringLiteral) + where + outsideStringLiteral :: TextParser String + outsideStringLiteral = P.someTill (P.anySingleBut '"') (P.lookAhead (void (P.char '"') P.<|> P.eof)) + + stringLiteral :: TextParser String + stringLiteral = do + inside <- P.char '"' >> P.manyTill P.charLiteral (P.char '"') + let f '"' = "\\\"" -- double quote should still be escaped + -- Despite the docs, 'showLitChar' and 'showLitString' from 'Data.Char' DOES ESCAPE unicode printable + -- characters. So we need to call 'isPrint' from 'Data.Char' manually. + f ch = if isPrint ch then [ch] else showLitChar ch "" + inside' = concatMap f inside + + pure $ "\"" <> inside' <> "\"" diff --git a/hls-plugin-api/test/Ide/PluginUtilsTest.hs b/hls-plugin-api/test/Ide/PluginUtilsTest.hs index f868a067d1..bad3c1dfbc 100644 --- a/hls-plugin-api/test/Ide/PluginUtilsTest.hs +++ b/hls-plugin-api/test/Ide/PluginUtilsTest.hs @@ -1,13 +1,35 @@ +{-# LANGUAGE OverloadedStrings #-} + module Ide.PluginUtilsTest ( tests ) where -import Ide.PluginUtils (positionInRange) +import Data.Char (isPrint) +import qualified Data.Text as T +import Ide.PluginUtils (positionInRange, unescape) import Language.LSP.Types (Position (Position), Range (Range)) import Test.Tasty import Test.Tasty.HUnit tests :: TestTree tests = testGroup "PluginUtils" - [ + [ unescapeTest + ] + +unescapeTest :: TestTree +unescapeTest = testGroup "unescape" + [ testCase "no double quote" $ + unescape "hello世界" @?= "hello世界" + , testCase "whole string quoted" $ + unescape "\"hello\\19990\\30028\"" @?= "\"hello世界\"" + , testCase "text before quotes should not be unescaped" $ + unescape "\\19990a\"hello\\30028\"" @?= "\\19990a\"hello界\"" + , testCase "some text after quotes" $ + unescape "\"hello\\19990\\30028\"abc" @?= "\"hello世界\"abc" + , testCase "many pairs of quote" $ + unescape "oo\"hello\\19990\\30028\"abc\"\1087\1088\1080\1074\1077\1090\"hh" @?= "oo\"hello世界\"abc\"привет\"hh" + , testCase "double quote itself should not be unescaped" $ + unescape "\"\\\"\\19990o\"" @?= "\"\\\"世o\"" + , testCase "control characters should not be escaped" $ + unescape "\"\\n\\t\"" @?= "\"\\n\\t\"" ] From 88a1dbbbfb3eec8898af8e87efacd0fa711ab032 Mon Sep 17 00:00:00 2001 From: Guillaume Bouchard Date: Thu, 1 Sep 2022 22:59:59 +0200 Subject: [PATCH 117/213] nix: fix nix environment - Bumped nixpkgs, so that's a true GHC 9.4.2 and not 9.4.0, which was failing in cabal which because it was detecting <=9.4.1. - Fixed GHC 9.4 overrides for dependencies - Use the "default" GHC environment for tooling in the shell. Hence it rebuilds less. - I introduced a flake for cabal-hashes, hence it can be updated automatically (with `nix flake update`) and hence we can use `callHackage` for any hackage package. --- configuration-ghc-90.nix | 2 +- configuration-ghc-94.nix | 13 ++++++ flake.lock | 88 ++++++++++++++++++++++------------------ flake.nix | 47 +++++++++++++-------- 4 files changed, 92 insertions(+), 58 deletions(-) diff --git a/configuration-ghc-90.nix b/configuration-ghc-90.nix index a31673afed..f82e03ce03 100644 --- a/configuration-ghc-90.nix +++ b/configuration-ghc-90.nix @@ -23,7 +23,7 @@ let ghc-lib-parser-ex = hself.ghc-lib-parser-ex_9_2_0_4; Cabal = hself.Cabal_3_6_3_0; - ormolu = hself.ormolu_0_5_0_0; + ormolu = hself.ormolu_0_5_0_1; fourmolu = hself.fourmolu_0_6_0_0; # Hlint is still broken hlint = doJailbreak (hself.callCabal2nix "hlint" inputs.hlint-34 { }); diff --git a/configuration-ghc-94.nix b/configuration-ghc-94.nix index 3790e182a7..23c0d5883d 100644 --- a/configuration-ghc-94.nix +++ b/configuration-ghc-94.nix @@ -31,6 +31,19 @@ let stylish-haskell = appendConfigureFlag hsuper.stylish-haskell "-fghc-lib"; + cereal = hsuper.callHackage "cereal" "0.5.8.3" {}; + base-compat = hsuper.callHackage "base-compat" "0.12.2" {}; + base-compat-batteries = hsuper.callHackage "base-compat-batteries" "0.12.2" {}; + hashable = hsuper.callHackage "hashable" "1.4.1.0" {}; + primitive = hsuper.callHackage "primitive" "0.7.4.0" {}; + ghc-check = hsuper.callHackage "ghc-check" "0.5.0.8" {}; + lens = hsuper.callHackage "lens" "5.2" {}; + integer-logarithms = hsuper.callHackage "integer-logarithms" "1.0.3.1" {}; + hiedb = hsuper.callHackage "hiedb" "0.4.2.0" {}; + hie-bios = hsuper.callHackage "hie-bios" "0.11.0" {}; + lsp = hsuper.callCabal2nix "lsp" "${inputs.lsp}/lsp" {}; + lsp-types = hsuper.callCabal2nix "lsp-types" "${inputs.lsp}/lsp-types" {}; + # Re-generate HLS drv excluding some plugins haskell-language-server = hself.callCabal2nixWithOptions "haskell-language-server" ./. diff --git a/flake.lock b/flake.lock index e01237f392..5d54006241 100644 --- a/flake.lock +++ b/flake.lock @@ -12,6 +12,23 @@ "url": "https://hackage.haskell.org/package/aeson-1.5.2.0/aeson-1.5.2.0.tar.gz" } }, + "all-cabal-hashes-unpacked": { + "flake": false, + "locked": { + "lastModified": 1663164766, + "narHash": "sha256-7ypfdEzJwfaQMQx9HV8B+r9BV7bN6iIO0lWhDy+8+0k=", + "owner": "commercialhaskell", + "repo": "all-cabal-hashes", + "rev": "ed701f5e64ece3e57efa4227243f9fb64f935253", + "type": "github" + }, + "original": { + "owner": "commercialhaskell", + "ref": "current-hackage", + "repo": "all-cabal-hashes", + "type": "github" + } + }, "brittany-01312": { "flake": false, "locked": { @@ -54,11 +71,11 @@ }, "flake-utils": { "locked": { - "lastModified": 1656928814, - "narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=", + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", "owner": "numtide", "repo": "flake-utils", - "rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", "type": "github" }, "original": { @@ -69,11 +86,11 @@ }, "flake-utils_2": { "locked": { - "lastModified": 1656928814, - "narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=", + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", "owner": "numtide", "repo": "flake-utils", - "rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", "type": "github" }, "original": { @@ -133,11 +150,11 @@ "gitignore": { "flake": false, "locked": { - "lastModified": 1657706534, - "narHash": "sha256-5jIzNHKtDu06mA325K/5CshUVb5r7sSmnRiula6Gr7o=", + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "f840a659d57e53fa751a9248b17149fd0cf2a221", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", "type": "github" }, "original": { @@ -149,13 +166,13 @@ "hie-bios": { "flake": false, "locked": { - "narHash": "sha256-5RqspT27rb/tMBwrKr4VfSSbq0+c0LMNuaKlTun0Kkk=", + "narHash": "sha256-KLAg++tO9lCOn7R/cSN2wLbrhpeBOOmeTEh7auIbUNk=", "type": "tarball", - "url": "https://hackage.haskell.org/package/hie-bios-0.9.1/hie-bios-0.9.1.tar.gz" + "url": "https://hackage.haskell.org/package/hie-bios-0.11.0/hie-bios-0.11.0.tar.gz" }, "original": { "type": "tarball", - "url": "https://hackage.haskell.org/package/hie-bios-0.9.1/hie-bios-0.9.1.tar.gz" + "url": "https://hackage.haskell.org/package/hie-bios-0.11.0/hie-bios-0.11.0.tar.gz" } }, "hlint": { @@ -197,13 +214,18 @@ "lsp": { "flake": false, "locked": { - "narHash": "sha256-+rkFYvSAI1hyFxPkgWZReyM2P6irVDpGVUGK8mcfEJE=", - "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-1.5.0.0/lsp-1.5.0.0.tar.gz" + "lastModified": 1662291729, + "narHash": "sha256-KlL38v/75G9zrW7+IiUeiCxFfLJGm/EdFeWQRUikab8=", + "owner": "haskell", + "repo": "lsp", + "rev": "b0f8596887088b8ab65fc1015c773f45b47234ae", + "type": "github" }, "original": { - "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-1.5.0.0/lsp-1.5.0.0.tar.gz" + "owner": "haskell", + "repo": "lsp", + "rev": "b0f8596887088b8ab65fc1015c773f45b47234ae", + "type": "github" } }, "lsp-test": { @@ -218,18 +240,6 @@ "url": "https://hackage.haskell.org/package/lsp-test-0.14.0.3/lsp-test-0.14.0.3.tar.gz" } }, - "lsp-types": { - "flake": false, - "locked": { - "narHash": "sha256-q4XTvIvsLvISjgedpRktJbWsWHSRIQbOx2Z/2u+3s50=", - "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-types-1.5.0.0/lsp-types-1.5.0.0.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-types-1.5.0.0/lsp-types-1.5.0.0.tar.gz" - } - }, "myst-parser": { "flake": false, "locked": { @@ -249,11 +259,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1659782844, - "narHash": "sha256-tM/qhHFE61puBxh9ebP3BIG1fkRAT4rHqD3jCM0HXGY=", + "lastModified": 1663112159, + "narHash": "sha256-31rjPhB6Hj1QoqLvFSULFf9Z/6z05vR0KrLGYvr9w0M=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c85e56bb060291eac3fb3c75d4e0e64f6836fcfe", + "rev": "78bce1608960b994405f3696ba086ba1d63654e9", "type": "github" }, "original": { @@ -265,11 +275,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1657888067, - "narHash": "sha256-GnwJoFBTPfW3+mz7QEeJEEQ9OMHZOiIJ/qDhZxrlKh8=", + "lastModified": 1663165252, + "narHash": "sha256-H9OiflDQy1vLu1w5yz46qWfGdcFBpS8VnPRLP0bsiZE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "65fae659e31098ca4ac825a6fef26d890aaf3f4e", + "rev": "bde85b0815144b77763732e0f32918017480e6b5", "type": "github" }, "original": { @@ -284,11 +294,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1657626303, - "narHash": "sha256-O/JJ0hSBCmlx0oP8QGAlRrWn0BvlC5cj7/EZ0CCWHTU=", + "lastModified": 1662879385, + "narHash": "sha256-ZmiyHn0uPH4xHYcOhY0e0sPtfyM6jCF/shmVq2aTOLc=", "owner": "nix-community", "repo": "poetry2nix", - "rev": "920ba682377d5c0d87945c5eb6141ab8447ca509", + "rev": "4f8d61cd936f853242a4ce1fd476f5488c288c26", "type": "github" }, "original": { @@ -325,6 +335,7 @@ "root": { "inputs": { "aeson-1520": "aeson-1520", + "all-cabal-hashes-unpacked": "all-cabal-hashes-unpacked", "brittany-01312": "brittany-01312", "constraints-extras": "constraints-extras", "flake-compat": "flake-compat", @@ -340,7 +351,6 @@ "implicit-hie-cradle": "implicit-hie-cradle", "lsp": "lsp", "lsp-test": "lsp-test", - "lsp-types": "lsp-types", "myst-parser": "myst-parser", "nixpkgs": "nixpkgs", "poetry2nix": "poetry2nix", diff --git a/flake.nix b/flake.nix index 5d0ec5955d..b9774bdf14 100644 --- a/flake.nix +++ b/flake.nix @@ -19,13 +19,16 @@ flake = false; }; - # List of hackage dependencies - lsp = { - url = "https://hackage.haskell.org/package/lsp-1.6.0.0/lsp-1.6.0.0.tar.gz"; + # cabal hashes contains all the version for different haskell packages, to update: + # nix flake lock --update-input all-cabal-hashes-unpacked + all-cabal-hashes-unpacked = { + url = "github:commercialhaskell/all-cabal-hashes/current-hackage"; flake = false; }; - lsp-types = { - url = "https://hackage.haskell.org/package/lsp-types-1.6.0.0/lsp-types-1.6.0.0.tar.gz"; + + # List of hackage dependencies + lsp = { + url = "github:haskell/lsp/b0f8596887088b8ab65fc1015c773f45b47234ae"; flake = false; }; lsp-test = { @@ -100,7 +103,7 @@ poetry2nix.url = "github:nix-community/poetry2nix/master"; }; outputs = - inputs@{ self, nixpkgs, flake-compat, flake-utils, gitignore, ... }: + inputs@{ self, nixpkgs, flake-compat, flake-utils, gitignore, all-cabal-hashes-unpacked, ... }: { overlays.default = final: prev: with prev; @@ -156,8 +159,8 @@ # GHCIDE requires hie-bios ^>=0.9.1 hie-bios = hself.callCabal2nix "hie-bios" inputs.hie-bios {}; - lsp = hsuper.callCabal2nix "lsp" inputs.lsp {}; - lsp-types = hsuper.callCabal2nix "lsp-types" inputs.lsp-types {}; + lsp = hsuper.callCabal2nix "lsp" "${inputs.lsp}/lsp" {}; + lsp-types = hsuper.callCabal2nix "lsp-types" "${inputs.lsp}/lsp-types" {}; lsp-test = hsuper.callCabal2nix "lsp-test" inputs.lsp-test {}; implicit-hie-cradle = hself.callCabal2nix "implicit-hie-cradle" inputs.implicit-hie-cradle {}; @@ -186,6 +189,14 @@ in { inherit hlsSources; + all-cabal-hashes = prev.runCommand "all-cabal-hashes.tar.gz" + { } + '' + cd ${all-cabal-hashes-unpacked} + cd .. + tar czf $out $(basename ${all-cabal-hashes-unpacked}) + ''; + # Haskell packages extended with our packages hlsHpkgs = compiler: extended haskell.packages.${compiler}; @@ -216,7 +227,7 @@ ghc902Config = (import ./configuration-ghc-90.nix) { inherit pkgs inputs; }; ghc924Config = (import ./configuration-ghc-92.nix) { inherit pkgs inputs; }; - ghc941Config = (import ./configuration-ghc-94.nix) { inherit pkgs inputs; }; + ghc942Config = (import ./configuration-ghc-94.nix) { inherit pkgs inputs; }; # GHC versions # While HLS still works fine with 8.10 GHCs, we only support the versions that are cached @@ -226,13 +237,13 @@ cases = { ghc902 = ghc902Config.tweakHpkgs (pkgs.hlsHpkgs "ghc902"); ghc924 = ghc924Config.tweakHpkgs (pkgs.hlsHpkgs "ghc924"); - ghc941 = ghc941Config.tweakHpkgs (pkgs.hlsHpkgs "ghc941"); + ghc942 = ghc942Config.tweakHpkgs (pkgs.hlsHpkgs "ghc942"); }; in { default = cases."${ghcVersion}"; } // cases; ghc902 = supportedGHCs.ghc902; ghc924 = supportedGHCs.ghc924; - ghc941 = supportedGHCs.ghc941; + ghc942 = supportedGHCs.ghc942; ghcDefault = supportedGHCs.default; # For markdown support @@ -282,16 +293,16 @@ hpkgs.ghc pkgs.cabal-install # @guibou: I'm not sure hie-bios is needed - ghcDefault.hie-bios + pkgs.haskellPackages.hie-bios # Dependencies needed to build some parts of hackage gmp zlib ncurses # Changelog tooling - (gen-hls-changelogs ghcDefault) + (gen-hls-changelogs pkgs.haskellPackages) # For the documentation pythonWithPackages # @guibou: I'm not sure this is needed. hlint - ghcDefault.opentelemetry-extra + pkgs.haskellPackages.opentelemetry-extra capstone tracy # ormolu # stylish-haskell @@ -345,7 +356,7 @@ src = null; }; # Create a hls executable - # Copied from https://github.com/NixOS/nixpkgs/blob/210784b7c8f3d926b7db73bdad085f4dc5d79418/pkgs/development/tools/haskell/haskell-language-server/withWrapper.nix#L16 + # Copied from https://github.com/NixOS/nixpkgs/blob/210784b7c8f3d926b7db73bdad085f4dc5d79428/pkgs/development/tools/haskell/haskell-language-server/withWrapper.nix#L16 mkExe = hpkgs: with pkgs.haskell.lib; (enableSharedExecutables (overrideCabal hpkgs.haskell-language-server @@ -365,7 +376,7 @@ haskell-language-server-dev = mkDevShell ghcDefault "cabal.project"; haskell-language-server-902-dev = mkDevShell ghc902 "cabal.project"; haskell-language-server-924-dev = mkDevShell ghc924 "cabal.project"; - haskell-language-server-941-dev = mkDevShell ghc941 "cabal.project"; + haskell-language-server-942-dev = mkDevShell ghc942 "cabal.project"; }; # Developement shell, haskell packages are also provided by nix @@ -373,14 +384,14 @@ haskell-language-server-dev-nix = mkDevShellWithNixDeps ghcDefault "cabal.project"; haskell-language-server-902-dev-nix = mkDevShellWithNixDeps ghc902 "cabal.project"; haskell-language-server-924-dev-nix = mkDevShellWithNixDeps ghc924 "cabal.project"; - haskell-language-server-941-dev-nix = mkDevShellWithNixDeps ghc941 "cabal.project"; + haskell-language-server-942-dev-nix = mkDevShellWithNixDeps ghc942 "cabal.project"; }; allPackages = { haskell-language-server = mkExe ghcDefault; haskell-language-server-902 = mkExe ghc902; haskell-language-server-924 = mkExe ghc924; - haskell-language-server-941 = mkExe ghc941; + haskell-language-server-942 = mkExe ghc942; }; devShells = simpleDevShells // nixDevShells // { From 5d6a6889b2909e0aa777960d00665c378136ebe5 Mon Sep 17 00:00:00 2001 From: Andy Date: Sat, 17 Sep 2022 08:38:39 +0200 Subject: [PATCH 118/213] Docs: List stan plugin (#3180) --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 73ab3f39fd..8105521d6d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -50,7 +50,7 @@ Here is a list of the additional settings currently supported by `haskell-langua Plugins have a generic config to control their behaviour. The schema of such config is: - `haskell.plugin.${pluginName}.globalOn`: usually with default true. Whether the plugin is enabled at runtime or it is not. That is the option you might use if you want to disable completely a plugin. - - Actual plugin names are: `ghcide-code-actions-fill-holes`, `ghcide-completions`, `ghcide-hover-and-symbols`, `ghcide-type-lenses`, `ghcide-code-actions-type-signatures`, `ghcide-code-actions-bindings`, `ghcide-code-actions-imports-exports`, `eval`, `moduleName`, `pragmas`, `refineImports`, `importLens`, `class`, `tactics` (aka wingman), `hlint`, `haddockComments`, `retrie`, `rename`, `splice`. + - Actual plugin names are: `ghcide-code-actions-fill-holes`, `ghcide-completions`, `ghcide-hover-and-symbols`, `ghcide-type-lenses`, `ghcide-code-actions-type-signatures`, `ghcide-code-actions-bindings`, `ghcide-code-actions-imports-exports`, `eval`, `moduleName`, `pragmas`, `refineImports`, `importLens`, `class`, `tactics` (aka wingman), `hlint`, `haddockComments`, `retrie`, `rename`, `splice`, `stan`. - So to disable the import lens with an explicit list of module definitions you could set `haskell.plugin.importLens.globalOn: false` - `haskell.plugin.${pluginName}.${lspCapability}On`: usually with default true. Whether a concrete plugin capability is enabled. - Capabilities are the different ways a lsp server can interact with the editor. The current available capabilities of the server are: `callHierarchy`, `codeActions`, `codeLens`, `diagnostics`, `hover`, `symbols`, `completion`, `rename`. From 855a88238279b795634fa6144a4c0e8acc7e9644 Mon Sep 17 00:00:00 2001 From: Andy Date: Sat, 17 Sep 2022 10:08:49 +0200 Subject: [PATCH 119/213] Allows disabling stan plugin (#3179) Respects globalOn in stan plugin configuration and won't show hints anymore if the plugin is disabled. Co-authored-by: Pepe Iborra --- plugins/hls-stan-plugin/hls-stan-plugin.cabal | 1 + .../hls-stan-plugin/src/Ide/Plugin/Stan.hs | 53 +++++++++---------- 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/plugins/hls-stan-plugin/hls-stan-plugin.cabal b/plugins/hls-stan-plugin/hls-stan-plugin.cabal index fbc0e882bb..855314adc0 100644 --- a/plugins/hls-stan-plugin/hls-stan-plugin.cabal +++ b/plugins/hls-stan-plugin/hls-stan-plugin.cabal @@ -32,6 +32,7 @@ library build-depends: base , containers + , data-default , deepseq , hashable , hls-plugin-api diff --git a/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs b/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs index 8ea671e9ee..4104ac910a 100644 --- a/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs +++ b/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs @@ -5,28 +5,17 @@ import Control.Monad (void) import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Maybe (MaybeT (MaybeT), runMaybeT) +import Data.Default import Data.Foldable (toList) -import Data.Hashable (Hashable) import qualified Data.HashMap.Strict as HM +import Data.Hashable (Hashable) import qualified Data.Map as Map import Data.Maybe (fromJust, mapMaybe) import qualified Data.Text as T -import Development.IDE (Action, FileDiagnostic, - GetHieAst (..), - GetModSummaryWithoutTimestamps (..), - GhcSession (..), IdeState, - NormalizedFilePath, - Pretty (..), Recorder, - RuleResult, Rules, - ShowDiagnostic (..), - TypeCheck (..), WithPriority, - action, cmapWithPrio, define, - getFilesOfInterestUntracked, - hscEnv, msrModSummary, - tmrTypechecked, use, uses) +import Development.IDE +import Development.IDE.Core.RuleTypes (HieAstResult (..)) import Development.IDE.Core.Rules (getHieFile, getSourceFileSource) -import Development.IDE.Core.RuleTypes (HieAstResult (..)) import qualified Development.IDE.Core.Shake as Shake import Development.IDE.GHC.Compat (HieASTs (HieASTs), RealSrcSpan (..), mkHieFile', @@ -38,9 +27,11 @@ import Development.IDE.GHC.Compat (HieASTs (HieASTs), import Development.IDE.GHC.Error (realSrcSpanToRange) import GHC.Generics (Generic) import HieTypes (HieASTs, HieFile) +import Ide.Plugin.Config import Ide.Types (PluginDescriptor (..), PluginId, - defaultPluginDescriptor) + defaultPluginDescriptor, + pluginEnabledConfig) import qualified Language.LSP.Types as LSP import Stan.Analysis (Analysis (..), runAnalysis) import Stan.Category (Category (..)) @@ -50,7 +41,8 @@ import Stan.Inspection.All (inspectionsIds, inspectionsMap) import Stan.Observation (Observation (..)) descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState -descriptor recorder plId = (defaultPluginDescriptor plId) {pluginRules = rules recorder} +descriptor recorder plId = (defaultPluginDescriptor plId) + {pluginRules = rules recorder plId} newtype Log = LogShake Shake.Log deriving (Show) @@ -67,18 +59,21 @@ instance NFData GetStanDiagnostics type instance RuleResult GetStanDiagnostics = () -rules :: Recorder (WithPriority Log) -> Rules () -rules recorder = do +rules :: Recorder (WithPriority Log) -> PluginId -> Rules () +rules recorder plId = do define (cmapWithPrio LogShake recorder) $ \GetStanDiagnostics file -> do - maybeHie <- getHieFile file - case maybeHie of - Nothing -> return ([], Nothing) - Just hie -> do - let enabledInspections = HM.fromList [(LSP.fromNormalizedFilePath file, inspectionsIds)] - -- This should use Cabal config for extensions and Stan config for inspection preferences is the future - let analysis = runAnalysis Map.empty enabledInspections [] [hie] - return (analysisToDiagnostics file analysis, Just ()) + config <- getClientConfigAction def + if pluginEnabledConfig plcDiagnosticsOn plId config then do + maybeHie <- getHieFile file + case maybeHie of + Nothing -> return ([], Nothing) + Just hie -> do + let enabledInspections = HM.fromList [(LSP.fromNormalizedFilePath file, inspectionsIds)] + -- This should use Cabal config for extensions and Stan config for inspection preferences is the future + let analysis = runAnalysis Map.empty enabledInspections [] [hie] + return (analysisToDiagnostics file analysis, Just ()) + else return ([], Nothing) action $ do files <- getFilesOfInterestUntracked @@ -87,7 +82,7 @@ rules recorder = do analysisToDiagnostics :: NormalizedFilePath -> Analysis -> [FileDiagnostic] analysisToDiagnostics file = mapMaybe (observationToDianostic file) . toList . analysisObservations observationToDianostic :: NormalizedFilePath -> Observation -> Maybe FileDiagnostic - observationToDianostic file (Observation {observationSrcSpan, observationInspectionId}) = + observationToDianostic file Observation {observationSrcSpan, observationInspectionId} = do inspection <- HM.lookup observationInspectionId inspectionsMap let @@ -109,7 +104,7 @@ rules recorder = do return ( file, ShowDiag, LSP.Diagnostic - { _range = realSrcSpanToRange $ observationSrcSpan, + { _range = realSrcSpanToRange observationSrcSpan, _severity = Just LSP.DsHint, _code = Just (LSP.InR $ unId (inspectionId inspection)), _source = Just "stan", From d7bdd0c6cdec3bdf30d8320ced93e19625918623 Mon Sep 17 00:00:00 2001 From: Andy Date: Sat, 17 Sep 2022 14:00:32 +0200 Subject: [PATCH 120/213] Docs: Fix checkParents documentation (#3184) * Check on close modes were removed * Moving docs to configuration and away from ghcide README --- docs/configuration.md | 4 ++-- ghcide/README.md | 18 ------------------ 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 8105521d6d..c88f5b981d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -42,8 +42,8 @@ Here is a list of the additional settings currently supported by `haskell-langua - Formatting provider (`haskell.formattingProvider`, default `ormolu`): what formatter to use; one of `floskell`, `ormolu`, `fourmolu`, `stylish-haskell`, or `brittany` (if compiled with the brittany plugin). - Max completions (`haskell.maxCompletions`, default 40): maximum number of completions sent to the LSP client. -- Check project (`haskell.checkProject`, default true): whether to typecheck the entire project on load. As it is activated by default could drive to bad performance in large projects. -- Check parents (`haskell.checkParents`, default `CheckOnSaveAndClose`): when to typecheck reverse dependencies of a file; one of `NeverCheck`, `CheckOnClose`, `CheckOnSaveAndClose`, or `AlwaysCheck`. +- Check project (`haskell.checkProject`, default true): whether to typecheck the entire project on initial load. As it is activated by default could drive to bad performance in large projects. +- Check parents (`haskell.checkParents`, default `CheckOnSave`): when to typecheck reverse dependencies of a file; one of `NeverCheck`, `CheckOnSave` (means dependent/parent modules will only be checked when you save), or `AlwaysCheck` (means re-typechecking them on every change). #### Generic plugin configuration diff --git a/ghcide/README.md b/ghcide/README.md index 5ae04177e8..6ef82afe6e 100644 --- a/ghcide/README.md +++ b/ghcide/README.md @@ -100,24 +100,6 @@ If you can't get `ghcide` working outside the editor, see [this setup troublesho `ghcide` has been designed to handle projects with hundreds or thousands of modules. If `ghci` can handle it, then `ghcide` should be able to handle it. The only caveat is that this currently requires GHC >= 8.6, and that the first time a module is loaded in the editor will trigger generation of support files in the background if those do not already exist. -### Configuration - -`ghcide` accepts the following lsp configuration options: - -```typescript -{ - // When to check the dependents of a module - // AlwaysCheck means retypechecking them on every change - // CheckOnSave means dependent/parent modules will only be checked when you save - // "CheckOnSaveAndClose" by default - checkParents : "NeverCheck" | "CheckOnClose" | "CheckOnSaveAndClose" | "AlwaysCheck" | , - // Whether to check the entire project on initial load - // true by default - checkProject : boolean - -} -``` - ### Using with VS Code The [Haskell](https://marketplace.visualstudio.com/items?itemName=haskell.haskell) extension has a setting for ghcide. From 41c978dd661b1ee6e5c95b06de719e2c18c97638 Mon Sep 17 00:00:00 2001 From: Michael Peyton Jones Date: Sat, 17 Sep 2022 16:10:39 +0100 Subject: [PATCH 121/213] Update supported GHC versions doc (#3186) Fixes #3172. --- docs/supported-versions.md | 70 +++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/docs/supported-versions.md b/docs/supported-versions.md index 687ecccc94..c713823a2e 100644 --- a/docs/supported-versions.md +++ b/docs/supported-versions.md @@ -15,26 +15,25 @@ Support status (see the support policy below for more details): - "will be deprecated ...": this version of GHC has special deprecation conditions that deviate from the support policy - "partial": not all features and plugins work, see the plugin support table and any linked issues for more details -| GHC version | Last supporting HLS version | Support status | -| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -| 9.2.4 | next | supported([partial](https://github.com/haskell/haskell-language-server/issues/2982)) | -| 9.2.3 | next | supported([partial](https://github.com/haskell/haskell-language-server/issues/2982)) | -| 9.2.2 | [1.7.0.0](https://github.com/haskell/haskell-language-server/releases/tag/1.7.0.0) | supported, ([partial](https://github.com/haskell/haskell-language-server/issues/2982)) | -| 9.2.1 | [1.7.0.0](https://github.com/haskell/haskell-language-server/releases/tag/1.7.0.0) | deprecated | -| 9.0.2 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported | -| 9.0.1 | [1.6.1.0](https://github.com/haskell/haskell-language-server/releases/tag/1.6.1.0) | deprecated | -| 8.10.7 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported | -| 8.10.6 | [1.6.1.0](https://github.com/haskell/haskell-language-server/releases/tag/1.6.1.0) | deprecated | -| 8.10.5 | [1.5.1](https://github.com/haskell/haskell-language-server/releases/tag/1.5.1) | deprecated | -| 8.10.4 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | -| 8.10.3 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | -| 8.10.2 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | -| 8.10.1 | [0.9.0](https://github.com/haskell/haskell-language-server/releases/tag/0.9.0) | deprecated | -| 8.8.4 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | will be deprecated after LTS and HLS full support for ghc-9.2 | -| 8.8.3 | [1.5.1](https://github.com/haskell/haskell-language-server/releases/1.5.1) | deprecated | -| 8.8.2 | [1.2.0](https://github.com/haskell/haskell-language-server/releases/tag/1.2.0) | deprecated | -| 8.6.5 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | will be deprecated after LTS and HLS full suppot for ghc-9.2 | -| 8.6.4 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | +| GHC version | Last supporting HLS version | Support status | +| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | +| 9.4.2 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported (partial) | +| 9.4.1 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported (partial) | +| 9.2.4 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported ([partial](https://github.com/haskell/haskell-language-server/issues/2982)) | +| 9.2.3 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported ([partial](https://github.com/haskell/haskell-language-server/issues/2982)) | +| 9.2.(1,2) | [1.7.0.0](https://github.com/haskell/haskell-language-server/releases/tag/1.7.0.0) | deprecated | +| 9.0.2 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported | +| 9.0.1 | [1.6.1.0](https://github.com/haskell/haskell-language-server/releases/tag/1.6.1.0) | deprecated | +| 8.10.7 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported | +| 8.10.6 | [1.6.1.0](https://github.com/haskell/haskell-language-server/releases/tag/1.6.1.0) | deprecated | +| 8.10.5 | [1.5.1](https://github.com/haskell/haskell-language-server/releases/tag/1.5.1) | deprecated | +| 8.10.(4,3,2) | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | +| 8.10.1 | [0.9.0](https://github.com/haskell/haskell-language-server/releases/tag/0.9.0) | deprecated | +| 8.8.4 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported, will be deprecated after LTS and HLS full support for ghc-9.2 | +| 8.8.3 | [1.5.1](https://github.com/haskell/haskell-language-server/releases/1.5.1) | deprecated | +| 8.8.2 | [1.2.0](https://github.com/haskell/haskell-language-server/releases/tag/1.2.0) | deprecated | +| 8.6.5 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | supported, will be deprecated after LTS and HLS full suppot for ghc-9.2 | +| 8.6.4 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | GHC versions not in the list have never been supported by HLS. LTS stands for [Stackage](https://www.stackage.org/) Long Term Support. @@ -50,28 +49,29 @@ Sometimes a plugin will be supported in the pre-built binaries but not in a HLS | Plugin | Unsupported GHC versions | |-------------------------------------|--------------------------| | `hls-alternate-number-plugin` | | -| `hls-brittany-plugin` | 9.2 | +| `hls-brittany-plugin` | 9.2, 9.4 | +| `hls-code-ragne-plugin` | 9.4 | | `hls-call-hierarchy-plugin` | | -| `hls-class-plugin` | | -| `hls-eval-plugin` | | +| `hls-class-plugin` | 9.4 | +| `hls-eval-plugin` | 9.4 | | `hls-explicit-imports-plugin` | | -| `hls-floskell-plugin` | | -| `hls-fourmolu-plugin` | | -| `hls-haddock-comments-plugin` | 9.2 | -| `hls-hlint-plugin` | | -| `hls-stan-plugin` | 8.6, 9.0, 9.2 | +| `hls-floskell-plugin` | 9.4 | +| `hls-fourmolu-plugin` | 9.4 | +| `hls-gadt-plugin` | 9.4 | +| `hls-haddock-comments-plugin` | 9.2, 9.4 | +| `hls-hlint-plugin` | 9.4 | +| `hls-stan-plugin` | 8.6, 9.0, 9.2, 9.4 | | `hls-module-name-plugin` | | -| `hls-ormolu-plugin` | | +| `hls-ormolu-plugin` | 9.4 | | `hls-pragmas-plugin` | | | `hls-qualify-imported-names-plugin` | | | `hls-refine-imports-plugin` | | -| `hls-rename-plugin` | | +| `hls-rename-plugin` | 9.4 | +| `hls-refactor-plugin` | 9.4 | | `hls-retrie-plugin` | 9.2 | -| `hls-splice-plugin` | 9.2 | -| `hls-stylish-haskell-plugin` | | -| `hls-tactics-plugin` | 9.2 | -| `hls-code-range-plugin` | | -| `hls-gadt-plugin` | | +| `hls-splice-plugin` | 9.2, 9.4 | +| `hls-stylish-haskell-plugin` | 9.4 | +| `hls-tactics-plugin` | 9.2, 9.4 | ### Using deprecated GHC versions From aee737237c7f4542bddc7ab31a2bdd7ee0cab48d Mon Sep 17 00:00:00 2001 From: Andy Date: Sat, 17 Sep 2022 19:26:18 +0200 Subject: [PATCH 122/213] Configuration: more advanced Vim / Coc example (#3181) Adding an example with settings Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Pepe Iborra --- docs/configuration.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/configuration.md b/docs/configuration.md index c88f5b981d..5162e5f7f0 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -271,6 +271,8 @@ Coc is recommend since it is the only complete LSP implementation for Vim and Ne Follow Coc's [installation instructions](https://github.com/neoclide/coc.nvim). Then issue `:CocConfig` and add the following to your Coc config file. +##### Minimal Example + ```json { "languageserver": { @@ -284,6 +286,32 @@ Then issue `:CocConfig` and add the following to your Coc config file. } ``` +##### Example with Settings + +```json +{ + "languageserver": { + "haskell": { + "command": "haskell-language-server-wrapper", + "args": ["--lsp"], + "rootPatterns": [ "*.cabal", "stack.yaml", "cabal.project", "package.yaml", "hie.yaml" ], + "filetypes": ["haskell", "lhaskell"], + "settings": { + "haskell": { + "checkParents": "CheckOnSave", + "checkProject": true, + "maxCompletions": 40, + "formattingProvider": "ormolu", + "plugin": { + "stan": { "globalOn": true } + } + } + } + } + } +} +``` + #### LanguageClient-neovim ##### vim-plug From a13e1b3c8d081b64faa7f7ad05e03a01f9c20b55 Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Sun, 18 Sep 2022 09:03:46 +0200 Subject: [PATCH 123/213] Clean up heapsize bits (#3168) --- ghcide/exe/Main.hs | 1 - ghcide/ghcide.cabal | 1 - ghcide/src/Development/IDE/Core/Shake.hs | 9 +- ghcide/src/Development/IDE/Core/Tracing.hs | 162 +------------------- ghcide/src/Development/IDE/Main.hs | 18 --- ghcide/src/Development/IDE/Types/Options.hs | 4 - stack-lts16.yaml | 1 - stack-lts19.yaml | 3 - stack.yaml | 1 - 9 files changed, 7 insertions(+), 193 deletions(-) diff --git a/ghcide/exe/Main.hs b/ghcide/exe/Main.hs index ec5fe8518a..3a86bcd943 100644 --- a/ghcide/exe/Main.hs +++ b/ghcide/exe/Main.hs @@ -144,7 +144,6 @@ main = withTelemetryLogger $ \telemetryLogger -> do let defOptions = IDEMain.argsIdeOptions arguments config sessionLoader in defOptions { optShakeProfiling = argsShakeProfiling - , optOTMemoryProfiling = IdeOTMemoryProfiling argsOTMemoryProfiling , optCheckParents = pure $ checkParents config , optCheckProject = pure $ checkProject config , optRunSubset = not argsConservativeChangeTracking diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index 54f417055e..109cb252c5 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -95,7 +95,6 @@ library Diff ^>=0.4.0, vector, opentelemetry >=0.6.1, - heapsize ==0.3.*, unliftio >= 0.2.6, unliftio-core, ghc-boot-th, diff --git a/ghcide/src/Development/IDE/Core/Shake.hs b/ghcide/src/Development/IDE/Core/Shake.hs index 0b31b83ac7..9728fd0410 100644 --- a/ghcide/src/Development/IDE/Core/Shake.hs +++ b/ghcide/src/Development/IDE/Core/Shake.hs @@ -1,6 +1,7 @@ -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 +{-# LANGUAGE CPP #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE DuplicateRecordFields #-} @@ -10,7 +11,6 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecursiveDo #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE CPP #-} -- | A Shake implementation of the compiler service. -- @@ -162,7 +162,7 @@ import GHC.Stack (HasCallStack) import HieDb.Types import Ide.Plugin.Config import qualified Ide.PluginUtils as HLS -import Ide.Types (PluginId, IdePlugins) +import Ide.Types (IdePlugins, PluginId) import Language.LSP.Diagnostics import qualified Language.LSP.Server as LSP import Language.LSP.Types @@ -630,13 +630,10 @@ shakeOpen recorder lspEnv defaultConfig idePlugins logger debouncer shakeDatabaseProfile <- shakeDatabaseProfileIO shakeProfileDir IdeOptions - { optOTMemoryProfiling = IdeOTMemoryProfiling otProfilingEnabled - , optProgressStyle + { optProgressStyle , optCheckParents } <- getIdeOptionsIO shakeExtras - startProfilingTelemetry otProfilingEnabled logger $ state shakeExtras - checkParents <- optCheckParents -- monitoring diff --git a/ghcide/src/Development/IDE/Core/Tracing.hs b/ghcide/src/Development/IDE/Core/Tracing.hs index 60bdc9fec5..5aaaada98e 100644 --- a/ghcide/src/Development/IDE/Core/Tracing.hs +++ b/ghcide/src/Development/IDE/Core/Tracing.hs @@ -5,9 +5,6 @@ module Development.IDE.Core.Tracing ( otTracedHandler , otTracedAction - , startProfilingTelemetry - , measureMemory - , getInstrumentCached , otTracedProvider , otSetUri , otTracedGarbageCollection @@ -17,56 +14,28 @@ module Development.IDE.Core.Tracing ) where -import Control.Concurrent.Async (Async, async) -import Control.Concurrent.Extra (modifyVar_, newVar, readVar, - threadDelay) -import Control.Exception (evaluate) -import Control.Exception.Safe (SomeException, catch, - generalBracket) -import Control.Monad (forM_, forever, void, when, - (>=>)) +import Control.Exception.Safe (generalBracket) import Control.Monad.Catch (ExitCase (..), MonadMask) -import Control.Monad.Extra (whenJust) import Control.Monad.IO.Unlift -import Control.Monad.STM (atomically) -import Control.Seq (r0, seqList, seqTuple2, - using) import Data.ByteString (ByteString) import Data.ByteString.Char8 (pack) -import qualified Data.HashMap.Strict as HMap -import Data.IORef (modifyIORef', newIORef, - readIORef, writeIORef) import Data.String (IsString (fromString)) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) -import Data.Typeable (TypeRep, typeOf) import Data.Word (Word16) import Debug.Trace.Flags (userTracingEnabled) -import Development.IDE.Core.RuleTypes (GhcSession (GhcSession), - GhcSessionDeps (GhcSessionDeps), - GhcSessionIO (GhcSessionIO)) import Development.IDE.Graph (Action) import Development.IDE.Graph.Rule import Development.IDE.Types.Diagnostics (FileDiagnostic, showDiagnostics) import Development.IDE.Types.Location (Uri (..)) -import Development.IDE.Types.Logger (Logger (Logger), logDebug, - logInfo) -import Development.IDE.Types.Shake (ValueWithDiagnostics (..), - Values, fromKeyType) -import Foreign.Storable (Storable (sizeOf)) -import HeapSize (recursiveSize, runHeapsize) -import Ide.PluginUtils (installSigUsr1Handler) +import Development.IDE.Types.Logger (Logger (Logger)) import Ide.Types (PluginId (..)) import Language.LSP.Types (NormalizedFilePath, fromNormalizedFilePath) -import qualified "list-t" ListT -import Numeric.Natural (Natural) import OpenTelemetry.Eventlog (SpanInFlight (..), addEvent, - beginSpan, endSpan, - mkValueObserver, observe, - setTag, withSpan, withSpan_) -import qualified StmContainers.Map as STM + beginSpan, endSpan, setTag, + withSpan) #if MIN_VERSION_ghc(8,8,0) otTracedProvider :: MonadUnliftIO m => PluginId -> ByteString -> m a -> m a @@ -178,126 +147,3 @@ otTracedProvider (PluginId pluginName) provider act | otherwise = act -startProfilingTelemetry :: Bool -> Logger -> Values -> IO () -startProfilingTelemetry allTheTime logger state = do - instrumentFor <- getInstrumentCached - - installSigUsr1Handler $ do - logInfo logger "SIGUSR1 received: performing memory measurement" - performMeasurement logger state instrumentFor - - when allTheTime $ void $ regularly (1 * seconds) $ - performMeasurement logger state instrumentFor - where - seconds = 1000000 - - regularly :: Int -> IO () -> IO (Async ()) - regularly delay act = async $ forever (act >> threadDelay delay) - - -performMeasurement :: - Logger -> - Values -> - (Maybe String -> IO OurValueObserver) -> - IO () -performMeasurement logger values instrumentFor = do - contents <- atomically $ ListT.toList $ STM.listT values - let keys = typeOf GhcSession - : typeOf GhcSessionDeps - -- TODO restore - : [ kty - | (k,_) <- contents - , Just (kty,_) <- [fromKeyType k] - -- do GhcSessionIO last since it closes over stateRef itself - , kty /= typeOf GhcSession - , kty /= typeOf GhcSessionDeps - , kty /= typeOf GhcSessionIO - ] - ++ [typeOf GhcSessionIO] - groupedForSharing <- evaluate (keys `using` seqList r0) - measureMemory logger [groupedForSharing] instrumentFor values - `catch` \(e::SomeException) -> - logInfo logger ("MEMORY PROFILING ERROR: " <> fromString (show e)) - - -type OurValueObserver = Int -> IO () - -getInstrumentCached :: IO (Maybe String -> IO OurValueObserver) -getInstrumentCached = do - instrumentMap <- newVar HMap.empty - mapBytesInstrument <- mkValueObserver "value map size_bytes" - - let instrumentFor k = do - mb_inst <- HMap.lookup k <$> readVar instrumentMap - case mb_inst of - Nothing -> do - instrument <- mkValueObserver (fromString (show k ++ " size_bytes")) - modifyVar_ instrumentMap (return . HMap.insert k instrument) - return $ observe instrument - Just v -> return $ observe v - return $ maybe (return $ observe mapBytesInstrument) instrumentFor - -whenNothing :: IO () -> IO (Maybe a) -> IO () -whenNothing act mb = mb >>= f - where f Nothing = act - f Just{} = return () - -measureMemory - :: Logger - -> [[TypeRep]] -- ^ Grouping of keys for the sharing-aware analysis - -> (Maybe String -> IO OurValueObserver) - -> Values - -> IO () -measureMemory logger groups instrumentFor values = withSpan_ "Measure Memory" $ do - contents <- atomically $ ListT.toList $ STM.listT values - valuesSizeRef <- newIORef $ Just 0 - let !groupsOfGroupedValues = groupValues contents - logDebug logger "STARTING MEMORY PROFILING" - forM_ groupsOfGroupedValues $ \groupedValues -> do - keepGoing <- readIORef valuesSizeRef - whenJust keepGoing $ \_ -> - whenNothing (writeIORef valuesSizeRef Nothing) $ - repeatUntilJust 3 $ do - -- logDebug logger (fromString $ show $ map fst groupedValues) - runHeapsize 25000000 $ - forM_ groupedValues $ \(k,v) -> withSpan ("Measure " <> fromString k) $ \sp -> do - acc <- liftIO $ newIORef 0 - observe <- liftIO $ instrumentFor $ Just k - mapM_ (recursiveSize >=> \x -> liftIO (modifyIORef' acc (+ x))) v - size <- liftIO $ readIORef acc - let !byteSize = sizeOf (undefined :: Word) * size - setTag sp "size" (fromString (show byteSize ++ " bytes")) - () <- liftIO $ observe byteSize - liftIO $ modifyIORef' valuesSizeRef (fmap (+ byteSize)) - - mbValuesSize <- readIORef valuesSizeRef - case mbValuesSize of - Just valuesSize -> do - observe <- instrumentFor Nothing - observe valuesSize - logDebug logger "MEMORY PROFILING COMPLETED" - Nothing -> - logInfo logger "Memory profiling could not be completed: increase the size of your nursery (+RTS -Ax) and try again" - - where - -- groupValues :: Values -> [ [(String, [Value Dynamic])] ] - groupValues contents = - let !groupedValues = - [ [ (show ty, vv) - | ty <- groupKeys - , let vv = [ v | (fromKeyType -> Just (kty,_), ValueWithDiagnostics v _) <- contents - , kty == ty] - ] - | groupKeys <- groups - ] - -- force the spine of the nested lists - in groupedValues `using` seqList (seqList (seqTuple2 r0 (seqList r0))) - -repeatUntilJust :: Monad m => Natural -> m (Maybe a) -> m (Maybe a) -repeatUntilJust 0 _ = return Nothing -repeatUntilJust nattempts action = do - res <- action - case res of - Nothing -> repeatUntilJust (nattempts-1) action - Just{} -> return res - diff --git a/ghcide/src/Development/IDE/Main.hs b/ghcide/src/Development/IDE/Main.hs index d342b1bb5d..d746c2539d 100644 --- a/ghcide/src/Development/IDE/Main.hs +++ b/ghcide/src/Development/IDE/Main.hs @@ -64,7 +64,6 @@ import Development.IDE.Core.Shake (IdeState (shakeExtras shakeSessionInit, uses) import qualified Development.IDE.Core.Shake as Shake -import Development.IDE.Core.Tracing (measureMemory) import Development.IDE.Graph (action) import Development.IDE.LSP.LanguageServer (runLanguageServer, setupLSP) @@ -234,7 +233,6 @@ commandP plugins = data Arguments = Arguments { argsProjectRoot :: Maybe FilePath - , argsOTMemoryProfiling :: Bool , argCommand :: Command , argsLogger :: IO Logger , argsRules :: Rules () @@ -255,7 +253,6 @@ data Arguments = Arguments defaultArguments :: Recorder (WithPriority Log) -> Logger -> Arguments defaultArguments recorder logger = Arguments { argsProjectRoot = Nothing - , argsOTMemoryProfiling = False , argCommand = LSP , argsLogger = pure logger , argsRules = mainRule (cmapWithPrio LogRules recorder) def >> action kick @@ -439,21 +436,6 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re let nfiles xs = let n = length xs in if n == 1 then "1 file" else show n ++ " files" putStrLn $ "\nCompleted (" ++ nfiles worked ++ " worked, " ++ nfiles failed ++ " failed)" - when argsOTMemoryProfiling $ do - let values = state $ shakeExtras ide - let consoleObserver Nothing = return $ \size -> printf "Total: %.2fMB\n" (fromIntegral @Int @Double size / 1e6) - consoleObserver (Just k) = return $ \size -> printf " - %s: %.2fKB\n" (show k) (fromIntegral @Int @Double size / 1e3) - - stateContents <- atomically $ ListT.toList $ STM.listT values - printf "# Shake value store contents(%d):\n" (length stateContents) - let keys = - nub $ - typeOf GhcSession : - typeOf GhcSessionDeps : - [kty | (fromKeyType -> Just (kty,_), _) <- stateContents, kty /= typeOf GhcSessionIO] ++ - [typeOf GhcSessionIO] - measureMemory logger [keys] consoleObserver values - unless (null failed) (exitWith $ ExitFailure (length failed)) Db opts cmd -> do root <- maybe IO.getCurrentDirectory return argsProjectRoot diff --git a/ghcide/src/Development/IDE/Types/Options.hs b/ghcide/src/Development/IDE/Types/Options.hs index d01d9f3260..5b59bf0d3b 100644 --- a/ghcide/src/Development/IDE/Types/Options.hs +++ b/ghcide/src/Development/IDE/Types/Options.hs @@ -43,9 +43,6 @@ data IdeOptions = IdeOptions -- ^ File extensions to search for code, defaults to Haskell sources (including @.hs@) , optShakeProfiling :: Maybe FilePath -- ^ Set to 'Just' to create a directory of profiling reports. - , optOTMemoryProfiling :: IdeOTMemoryProfiling - -- ^ Whether to record profiling information with OpenTelemetry. You must - -- also enable the -l RTS flag for this to have any effect , optTesting :: IdeTesting -- ^ Whether to enable additional lsp messages used by the test suite for checking invariants , optReportProgress :: IdeReportProgress @@ -123,7 +120,6 @@ defaultIdeOptions session = IdeOptions ,optPkgLocationOpts = defaultIdePkgLocationOptions ,optShakeOptions = shakeOptions ,optShakeProfiling = Nothing - ,optOTMemoryProfiling = IdeOTMemoryProfiling False ,optReportProgress = IdeReportProgress False ,optLanguageSyntax = "haskell" ,optNewColonConvention = False diff --git a/stack-lts16.yaml b/stack-lts16.yaml index e947f3244f..40449ab4d5 100644 --- a/stack-lts16.yaml +++ b/stack-lts16.yaml @@ -57,7 +57,6 @@ extra-deps: - ghc-source-gen-0.4.1.0 - ghc-trace-events-0.1.2.1 - haskell-src-exts-1.21.1 - - heapsize-0.3.0 - hlint-3.2.8 - HsYAML-aeson-0.2.0.0@rev:2 - hoogle-5.0.17.11 diff --git a/stack-lts19.yaml b/stack-lts19.yaml index 320cc5c6c6..af599c2112 100644 --- a/stack-lts19.yaml +++ b/stack-lts19.yaml @@ -45,7 +45,6 @@ extra-deps: - ghc-lib-9.2.4.20220729 - ghc-lib-parser-9.2.4.20220729 - ghc-lib-parser-ex-9.2.0.4 -- heapsize-0.3.0.1@sha256:0b69aa97a46d819b700ac7b145f3b5493c3565cf2c5b8298682238d405d0326e,1417 - hiedb-0.4.2.0 - hlint-3.4 - implicit-hie-0.1.2.7@sha256:82bbbb1a8c05f99c8af3c16ac53e80c8648d8bf047b25ed5ce45a135bd736907,3122 @@ -66,8 +65,6 @@ configure-options: - --disable-library-for-ghci haskell-language-server: - --disable-library-for-ghci - heapsize: - - --disable-library-for-ghci flags: haskell-language-server: diff --git a/stack.yaml b/stack.yaml index 33c545e3bb..914b0a980b 100644 --- a/stack.yaml +++ b/stack.yaml @@ -37,7 +37,6 @@ packages: extra-deps: - floskell-0.10.6@sha256:e77d194189e8540abe2ace2c7cb8efafc747ca35881a2fefcbd2d40a1292e036,3819 -- heapsize-0.3.0.1@sha256:0b69aa97a46d819b700ac7b145f3b5493c3565cf2c5b8298682238d405d0326e,1417 - hiedb-0.4.2.0 - implicit-hie-0.1.2.7@sha256:82bbbb1a8c05f99c8af3c16ac53e80c8648d8bf047b25ed5ce45a135bd736907,3122 - implicit-hie-cradle-0.5.0.0@sha256:4276f60f3a59bc22df03fd918f73bca9f777de9568f85e3a8be8bd7566234a59,2368 From b378de2d42d1c78d0119d54e65646b475a9dc6c5 Mon Sep 17 00:00:00 2001 From: Andy Date: Sun, 18 Sep 2022 10:41:14 +0200 Subject: [PATCH 124/213] Solve formatting issues (stylish-haskell, pre-commit CI) (#3171) * Solve formatting issues * stylish-haskell parse errors solved (partially) * Env: Changing order (import) * Plugins: Dangling `$` Co-authored-by: Pepe Iborra --- .../src/Development/IDE/Core/Preprocessor.hs | 6 +-- ghcide/src/Development/IDE/Core/Shake.hs | 5 +- ghcide/src/Development/IDE/GHC/CPP.hs | 2 +- ghcide/src/Development/IDE/GHC/Compat/Env.hs | 10 ++-- .../src/Development/IDE/GHC/Compat/Logger.hs | 2 +- .../src/Development/IDE/GHC/Compat/Plugins.hs | 13 +++-- .../src/Development/IDE/GHC/Compat/Units.hs | 8 ++-- ghcide/src/Development/IDE/GHC/Orphans.hs | 6 +-- ghcide/src/Development/IDE/GHC/Warnings.hs | 6 +-- .../src/Development/IDE/Plugin/Completions.hs | 47 +++++++++---------- ghcide/src/Development/IDE/Spans/Common.hs | 3 +- .../Development/IDE/Spans/Documentation.hs | 2 +- hls-plugin-api/src/Ide/Types.hs | 6 +-- .../src/Ide/Plugin/CodeRange/Rules.hs | 3 +- .../src/Development/IDE/GHC/Dump.hs | 10 ++-- .../Development/IDE/Plugin/CodeAction/Util.hs | 14 +++--- .../src/Ide/Plugin/Rename.hs | 6 +-- test/functional/Format.hs | 2 +- 18 files changed, 74 insertions(+), 77 deletions(-) diff --git a/ghcide/src/Development/IDE/Core/Preprocessor.hs b/ghcide/src/Development/IDE/Core/Preprocessor.hs index 08a41b0ed4..d41a7f9795 100644 --- a/ghcide/src/Development/IDE/Core/Preprocessor.hs +++ b/ghcide/src/Development/IDE/Core/Preprocessor.hs @@ -1,6 +1,6 @@ -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE CPP #-} +{-# LANGUAGE CPP #-} module Development.IDE.Core.Preprocessor ( preprocessor @@ -30,8 +30,8 @@ import qualified GHC.LanguageExtensions as LangExt import System.FilePath import System.IO.Extra #if MIN_VERSION_ghc(9,3,0) -import GHC.Utils.Logger (LogFlags(..)) -import GHC.Utils.Outputable (renderWithContext) +import GHC.Utils.Logger (LogFlags (..)) +import GHC.Utils.Outputable (renderWithContext) #endif -- | Given a file and some contents, apply any necessary preprocessors, diff --git a/ghcide/src/Development/IDE/Core/Shake.hs b/ghcide/src/Development/IDE/Core/Shake.hs index 9728fd0410..6d43d6e43f 100644 --- a/ghcide/src/Development/IDE/Core/Shake.hs +++ b/ghcide/src/Development/IDE/Core/Shake.hs @@ -130,11 +130,10 @@ import Development.IDE.GHC.Compat (NameCache, NameCacheUpdater (..), initNameCache, knownKeyNames, + mkSplitUniqSupply) #if !MIN_VERSION_ghc(9,3,0) - upNameCache, +import Development.IDE.GHC.Compat (upNameCache) #endif - mkSplitUniqSupply - ) import Development.IDE.GHC.Orphans () import Development.IDE.Graph hiding (ShakeValue) import qualified Development.IDE.Graph as Shake diff --git a/ghcide/src/Development/IDE/GHC/CPP.hs b/ghcide/src/Development/IDE/GHC/CPP.hs index fc18450292..d0aaec5e95 100644 --- a/ghcide/src/Development/IDE/GHC/CPP.hs +++ b/ghcide/src/Development/IDE/GHC/CPP.hs @@ -35,7 +35,7 @@ import DynFlags #endif #endif #if MIN_VERSION_ghc(9,3,0) -import qualified GHC.Driver.Pipeline.Execute as Pipeline +import qualified GHC.Driver.Pipeline.Execute as Pipeline #endif addOptP :: String -> DynFlags -> DynFlags diff --git a/ghcide/src/Development/IDE/GHC/Compat/Env.hs b/ghcide/src/Development/IDE/GHC/Compat/Env.hs index 0909e78366..596593376d 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Env.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Env.hs @@ -91,11 +91,6 @@ import HscTypes as Env import Module #endif -#if MIN_VERSION_ghc(9,3,0) -hsc_EPS :: HscEnv -> UnitEnv -hsc_EPS = hsc_unit_env -#endif - #if MIN_VERSION_ghc(9,0,0) #if !MIN_VERSION_ghc(9,2,0) import qualified Data.Set as Set @@ -105,6 +100,11 @@ import qualified Data.Set as Set import Data.IORef #endif +#if MIN_VERSION_ghc(9,3,0) +hsc_EPS :: HscEnv -> UnitEnv +hsc_EPS = hsc_unit_env +#endif + #if !MIN_VERSION_ghc(9,2,0) type UnitEnv = () newtype Logger = Logger { log_action :: LogAction } diff --git a/ghcide/src/Development/IDE/GHC/Compat/Logger.hs b/ghcide/src/Development/IDE/GHC/Compat/Logger.hs index 6e8c6dca52..cffac134ab 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Logger.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Logger.hs @@ -25,7 +25,7 @@ import DynFlags import Outputable (queryQual) #endif #if MIN_VERSION_ghc(9,3,0) -import GHC.Types.Error +import GHC.Types.Error #endif putLogHook :: Logger -> HscEnv -> HscEnv diff --git a/ghcide/src/Development/IDE/GHC/Compat/Plugins.hs b/ghcide/src/Development/IDE/GHC/Compat/Plugins.hs index 12cf035483..b241c150c6 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Plugins.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Plugins.hs @@ -24,12 +24,12 @@ import qualified GHC.Driver.Env as Env import GHC.Driver.Plugins (Plugin (..), PluginWithArgs (..), StaticPlugin (..), + defaultPlugin, withPlugins) #if MIN_VERSION_ghc(9,3,0) - staticPlugins, - ParsedResult(..), - PsMessages(..), +import GHC.Driver.Plugins (ParsedResult (..), + PsMessages (..), + staticPlugins) #endif - defaultPlugin, withPlugins) import qualified GHC.Runtime.Loader as Loader #elif MIN_VERSION_ghc(8,8,0) import qualified DynamicLoading as Loader @@ -48,11 +48,10 @@ applyPluginsParsedResultAction env dflags ms hpm_annotations parsed = do -- Apply parsedResultAction of plugins let applyPluginAction p opts = parsedResultAction p opts ms #if MIN_VERSION_ghc(9,3,0) - fmap (hpm_module . parsedResultModule) $ + fmap (hpm_module . parsedResultModule) $ runHsc env $ withPlugins #else - fmap hpm_module $ + fmap hpm_module $ runHsc env $ withPlugins #endif - runHsc env $ withPlugins #if MIN_VERSION_ghc(9,3,0) (Env.hsc_plugins env) #elif MIN_VERSION_ghc(9,2,0) diff --git a/ghcide/src/Development/IDE/GHC/Compat/Units.hs b/ghcide/src/Development/IDE/GHC/Compat/Units.hs index c4a56bec5f..a96c8be564 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Units.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Units.hs @@ -52,11 +52,11 @@ module Development.IDE.GHC.Compat.Units ( showSDocForUser', ) where -import qualified Data.List.NonEmpty as NE -import qualified Data.Map.Strict as Map -import Control.Monad +import Control.Monad +import qualified Data.List.NonEmpty as NE +import qualified Data.Map.Strict as Map #if MIN_VERSION_ghc(9,3,0) -import GHC.Unit.Home.ModInfo +import GHC.Unit.Home.ModInfo #endif #if MIN_VERSION_ghc(9,0,0) #if MIN_VERSION_ghc(9,2,0) diff --git a/ghcide/src/Development/IDE/GHC/Orphans.hs b/ghcide/src/Development/IDE/GHC/Orphans.hs index d4f5c51972..6a2ddc7586 100644 --- a/ghcide/src/Development/IDE/GHC/Orphans.hs +++ b/ghcide/src/Development/IDE/GHC/Orphans.hs @@ -44,7 +44,7 @@ import GHC.ByteCode.Types import ByteCodeTypes #endif #if MIN_VERSION_ghc(9,3,0) -import GHC.Types.PkgQual +import GHC.Types.PkgQual #endif -- Orphan instances for types from the GHC API. @@ -217,8 +217,8 @@ instance NFData HomeModInfo where #if MIN_VERSION_ghc(9,3,0) instance NFData PkgQual where - rnf NoPkgQual = () - rnf (ThisPkg uid) = rnf uid + rnf NoPkgQual = () + rnf (ThisPkg uid) = rnf uid rnf (OtherPkg uid) = rnf uid instance NFData UnitId where diff --git a/ghcide/src/Development/IDE/GHC/Warnings.hs b/ghcide/src/Development/IDE/GHC/Warnings.hs index fa30373ce8..9ddda656c9 100644 --- a/ghcide/src/Development/IDE/GHC/Warnings.hs +++ b/ghcide/src/Development/IDE/GHC/Warnings.hs @@ -1,7 +1,7 @@ -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 +{-# LANGUAGE CPP #-} {-# LANGUAGE ExplicitNamespaces #-} -{-# LANGUAGE CPP #-} module Development.IDE.GHC.Warnings(withWarnings) where @@ -49,8 +49,8 @@ attachReason Nothing d = d attachReason (Just wr) d = d{_code = InR <$> showReason wr} where showReason = \case - WarningWithFlag flag -> showFlag flag - _ -> Nothing + WarningWithFlag flag -> showFlag flag + _ -> Nothing #else attachReason :: WarnReason -> Diagnostic -> Diagnostic attachReason wr d = d{_code = InR <$> showReason wr} diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index 8e95614a27..4a02d94bf9 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -8,46 +8,45 @@ module Development.IDE.Plugin.Completions , ghcideCompletionsPluginPriority ) where -import Control.Concurrent.Async (concurrently) -import Control.Concurrent.STM.Stats (readTVarIO) +import Control.Concurrent.Async (concurrently) +import Control.Concurrent.STM.Stats (readTVarIO) import Control.Monad.Extra import Control.Monad.IO.Class import Control.Monad.Trans.Maybe import Data.Aeson -import qualified Data.HashMap.Strict as Map -import qualified Data.HashSet as Set -import Data.List (find) +import qualified Data.HashMap.Strict as Map +import qualified Data.HashSet as Set +import Data.List (find) import Data.Maybe -import qualified Data.Text as T +import qualified Data.Text as T import Development.IDE.Core.PositionMapping import Development.IDE.Core.RuleTypes -import Development.IDE.Core.Service hiding (Log, - LogShake) -import Development.IDE.Core.Shake hiding (Log) -import qualified Development.IDE.Core.Shake as Shake +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.GHC.Compat -import Development.IDE.GHC.Error (rangeToSrcSpan) -import Development.IDE.GHC.Util (printOutputable) +import Development.IDE.GHC.Error (rangeToSrcSpan) +import Development.IDE.GHC.Util (printOutputable) import Development.IDE.Graph import Development.IDE.Plugin.Completions.Logic import Development.IDE.Plugin.Completions.Types import Development.IDE.Types.Exports -import Development.IDE.Types.HscEnvEq (HscEnvEq (envPackageExports), - hscEnv) -import qualified Development.IDE.Types.KnownTargets as KT +import Development.IDE.Types.HscEnvEq (HscEnvEq (envPackageExports), + hscEnv) +import qualified Development.IDE.Types.KnownTargets as KT import Development.IDE.Types.Location -import Development.IDE.Types.Logger (Pretty (pretty), - Recorder, - WithPriority, - cmapWithPrio) -import GHC.Exts (fromList, toList) -import Ide.Plugin.Config (Config) +import Development.IDE.Types.Logger (Pretty (pretty), + Recorder, + WithPriority, + cmapWithPrio) +import GHC.Exts (fromList, toList) +import Ide.Plugin.Config (Config) import Ide.Types -import qualified Language.LSP.Server as LSP +import qualified Language.LSP.Server as LSP import Language.LSP.Types -import qualified Language.LSP.VFS as VFS +import qualified Language.LSP.VFS as VFS import Numeric.Natural -import Text.Fuzzy.Parallel (Scored (..)) +import Text.Fuzzy.Parallel (Scored (..)) data Log = LogShake Shake.Log deriving Show diff --git a/ghcide/src/Development/IDE/Spans/Common.hs b/ghcide/src/Development/IDE/Spans/Common.hs index 701074b3ac..00b98ded2a 100644 --- a/ghcide/src/Development/IDE/Spans/Common.hs +++ b/ghcide/src/Development/IDE/Spans/Common.hs @@ -49,10 +49,11 @@ safeTyThingId (AConLike (RealDataCon dataCon)) = Just (dataConWrapId dataCon) safeTyThingId _ = Nothing -- Possible documentation for an element in the code -data SpanDoc #if MIN_VERSION_ghc(9,3,0) +data SpanDoc = SpanDocString [HsDocString] SpanDocUris #else +data SpanDoc = SpanDocString HsDocString SpanDocUris #endif | SpanDocText [T.Text] SpanDocUris diff --git a/ghcide/src/Development/IDE/Spans/Documentation.hs b/ghcide/src/Development/IDE/Spans/Documentation.hs index 08ad918bc4..63f84966a3 100644 --- a/ghcide/src/Development/IDE/Spans/Documentation.hs +++ b/ghcide/src/Development/IDE/Spans/Documentation.hs @@ -34,7 +34,7 @@ import System.FilePath import Language.LSP.Types (filePathToUri, getUri) #if MIN_VERSION_ghc(9,3,0) -import GHC.Types.Unique.Map +import GHC.Types.Unique.Map #endif mkDocMap diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index e9fbe8a28d..997300900a 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -56,6 +56,7 @@ import Control.Monad (void) import qualified System.Posix.Process as P (getProcessID) import System.Posix.Signals #endif +import Control.Applicative ((<|>)) import Control.Arrow ((&&&)) import Control.Lens ((^.)) import Data.Aeson hiding (defaultOptions) @@ -67,7 +68,7 @@ import Data.GADT.Compare import Data.Hashable (Hashable) import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HashMap -import Data.List.Extra (sortOn, find) +import Data.List.Extra (find, sortOn) import Data.List.NonEmpty (NonEmpty (..), toList) import qualified Data.Map as Map import Data.Maybe @@ -107,12 +108,11 @@ import Options.Applicative (ParserInfo) import System.FilePath import System.IO.Unsafe import Text.Regex.TDFA.Text () -import Control.Applicative ((<|>)) -- --------------------------------------------------------------------- data IdePlugins ideState = IdePlugins_ - { ipMap_ :: HashMap PluginId (PluginDescriptor ideState) + { ipMap_ :: HashMap PluginId (PluginDescriptor ideState) , lookupCommandProvider :: CommandId -> Maybe PluginId } diff --git a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs index 992bf6ca28..05515bb8e8 100644 --- a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs +++ b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs @@ -45,8 +45,7 @@ import Development.IDE import Development.IDE.Core.Rules (toIdeResult) import qualified Development.IDE.Core.Shake as Shake import Development.IDE.GHC.Compat (HieAST (..), - HieASTs (getAsts), - RefMap) + HieASTs (getAsts), RefMap) import Development.IDE.GHC.Compat.Util import GHC.Generics (Generic) import Ide.Plugin.CodeRange.ASTPreProcess (CustomNodeType (..), diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Dump.hs b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Dump.hs index cde3f79c48..19e7efe6e6 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Dump.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Dump.hs @@ -1,7 +1,7 @@ {-# LANGUAGE CPP #-} module Development.IDE.GHC.Dump(showAstDataHtml) where -import Data.Data hiding (Fixity) -import Development.IDE.GHC.Compat hiding (NameAnn) +import Data.Data hiding (Fixity) +import Development.IDE.GHC.Compat hiding (NameAnn) import Development.IDE.GHC.Compat.ExactPrint #if MIN_VERSION_ghc(8,10,1) import GHC.Hs.Dump @@ -9,9 +9,9 @@ import GHC.Hs.Dump import HsDumpAst #endif #if MIN_VERSION_ghc(9,2,1) -import qualified Data.ByteString as B +import qualified Data.ByteString as B import Development.IDE.GHC.Compat.Util -import Generics.SYB (ext1Q, ext2Q, extQ) +import Generics.SYB (ext1Q, ext2Q, extQ) import GHC.Hs #endif #if MIN_VERSION_ghc(9,0,1) @@ -19,7 +19,7 @@ import GHC.Plugins #else import GhcPlugins #endif -import Prelude hiding ((<>)) +import Prelude hiding ((<>)) -- | Show a GHC syntax tree in HTML. #if MIN_VERSION_ghc(9,2,1) diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Util.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Util.hs index bfcb0d7a37..6f51131bd6 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Util.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Util.hs @@ -1,12 +1,5 @@ module Development.IDE.Plugin.CodeAction.Util where -#if MIN_VERSION_ghc(9,2,0) -import GHC.Utils.Outputable -#else -import Development.IDE.GHC.Util -import Development.IDE.GHC.Compat.Util -import Development.IDE.GHC.Compat -#endif import Data.Data (Data) import qualified Data.Unique as U import Debug.Trace @@ -18,6 +11,13 @@ import Text.Printf import Development.IDE.GHC.Dump (showAstDataHtml) import Data.Time.Clock.POSIX (POSIXTime, getCurrentTime, utcTimeToPOSIXSeconds) +#if MIN_VERSION_ghc(9,2,0) +import GHC.Utils.Outputable +#else +import Development.IDE.GHC.Util +import Development.IDE.GHC.Compat.Util +import Development.IDE.GHC.Compat +#endif -------------------------------------------------------------------------------- -- Tracing exactprint terms diff --git a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs index c6c1238b61..8a22172a67 100644 --- a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs +++ b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs @@ -29,7 +29,7 @@ import qualified Data.Map as M import Data.Maybe import Data.Mod.Word import qualified Data.Text as T -import Development.IDE (Recorder, WithPriority) +import Development.IDE (Recorder, WithPriority) import Development.IDE.Core.PositionMapping import Development.IDE.Core.RuleTypes import Development.IDE.Core.Service @@ -40,10 +40,10 @@ import Development.IDE.GHC.Compat.Parser import Development.IDE.GHC.Compat.Units import Development.IDE.GHC.Error import Development.IDE.GHC.ExactPrint -import qualified Development.IDE.GHC.ExactPrint as E +import qualified Development.IDE.GHC.ExactPrint as E +import Development.IDE.Plugin.CodeAction import Development.IDE.Spans.AtPoint import Development.IDE.Types.Location -import Development.IDE.Plugin.CodeAction import HieDb.Query import Ide.Plugin.Properties import Ide.PluginUtils diff --git a/test/functional/Format.hs b/test/functional/Format.hs index b3829c3a9f..e08809a8ec 100644 --- a/test/functional/Format.hs +++ b/test/functional/Format.hs @@ -6,9 +6,9 @@ import Control.Lens ((^.)) import Control.Monad.IO.Class import Data.Aeson import qualified Data.ByteString.Lazy as BS +import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.Text.IO as T -import qualified Data.Text as T import Language.LSP.Test import Language.LSP.Types import qualified Language.LSP.Types.Lens as LSP From 362ca34488899454f2070f098a5a2ebf25e52c5e Mon Sep 17 00:00:00 2001 From: Andy Date: Sun, 18 Sep 2022 18:58:11 +0200 Subject: [PATCH 125/213] Refactor plugin: Prefer code action (#3167) `isPreferred` can influence the client side order of code actions. The idea is that an unused import is likely to be removed and less likely the warning will be disabled. Therefore actions to remove a single or all redundant imports should be preferred, so that the client can prioritize them higher. Followup of --- .../src/Development/IDE/Plugin/CodeAction.hs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index 01c3b555c1..32ea6bf8ce 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -389,7 +389,7 @@ suggestHideShadow ps fileContents mTcM mHar Diagnostic {_message, _range} | otherwise = [] where L _ HsModule {hsmodImports} = astA ps - + suggests identifier modName s | Just tcM <- mTcM, Just har <- mHar, @@ -458,6 +458,12 @@ suggestRemoveRedundantImport ParsedModule{pm_parsed_source = L _ HsModule{hsmod = [("Remove import", [TextEdit (extendToWholeLineIfPossible contents _range) ""])] | otherwise = [] + +-- Note [Removing imports is preferred] +-- It's good to prefer the remove imports code action because an unused import +-- is likely to be removed and less likely the warning will be disabled. +-- Therefore actions to remove a single or all redundant imports should be +-- preferred, so that the client can prioritize them higher. caRemoveRedundantImports :: Maybe ParsedModule -> Maybe T.Text -> [Diagnostic] -> [Diagnostic] -> Uri -> [Command |? CodeAction] caRemoveRedundantImports m contents digs ctxDigs uri | Just pm <- m, @@ -481,7 +487,8 @@ caRemoveRedundantImports m contents digs ctxDigs uri _diagnostics = Nothing _documentChanges = Nothing _edit = Just WorkspaceEdit{..} - _isPreferred = Nothing + -- See Note [Removing imports is preferred] + _isPreferred = Just True _command = Nothing _disabled = Nothing _xdata = Nothing @@ -520,7 +527,8 @@ caRemoveInvalidExports m contents digs ctxDigs uri _documentChanges = Nothing _edit = Just WorkspaceEdit{..} _command = Nothing - _isPreferred = Nothing + -- See Note [Removing imports is preferred] + _isPreferred = Just True _disabled = Nothing _xdata = Nothing _changeAnnotations = Nothing @@ -534,7 +542,8 @@ caRemoveInvalidExports m contents digs ctxDigs uri _documentChanges = Nothing _edit = Just WorkspaceEdit{..} _command = Nothing - _isPreferred = Nothing + -- See Note [Removing imports is preferred] + _isPreferred = Just True _disabled = Nothing _xdata = Nothing _changeAnnotations = Nothing From ff4f29f859244f6e828ecf74fab6e8a6b745c020 Mon Sep 17 00:00:00 2001 From: Nick Suchecki <40047416+drsooch@users.noreply.github.com> Date: Sun, 18 Sep 2022 15:21:22 -0400 Subject: [PATCH 126/213] Only run the pre-commit hook on changed files (#3145) * Only run the pre-commit hook on changed files * Let's change a haskell file * Didn't use the PATTERN field correctly * Move pre-commit block before building stylish-haskell * Try to evaluate GIT_DIFF value to pass to pre-commit * Try to evaluate GIT_DIFF value to pass to pre-commit * Move pre-commit action back to original spot * Introduce bad formatting in a file * Conditionally run the pre-commit job only when there are haskell source changes * Restore haskell files we used for testing * workflow fixes * Try changing haskell source files with new workflow structure * Revert all changes to files during testing Co-authored-by: Pepe Iborra --- .github/workflows/pre-commit.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index af9f4981af..c385ab2727 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -6,11 +6,24 @@ on: branches: [master] jobs: + file-diff: + runs-on: ubuntu-latest + outputs: + git-diff: ${{ steps.git-diff.outputs.diff }} + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Find changed files + uses: technote-space/get-diff-action@v4.0.1 + id: git-diff + with: + PATTERNS: | + +(src|exe|test|ghcide|plugins|hls-plugin-api|hie-compat|hls-graph|hls-test-utils)/**/*.hs pre-commit: runs-on: ubuntu-latest + needs: file-diff steps: - uses: actions/checkout@v3 - - uses: ./.github/actions/setup-build with: os: ${{ runner.os }} @@ -40,3 +53,5 @@ jobs: - uses: actions/setup-python@v4 - uses: pre-commit/action@v3.0.0 + with: + extra_args: --files ${{ needs.file-diff.outputs.git-diff }} From 0d214482763bb9354bd7a92f2f4af7a27b7f8fb6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Sep 2022 07:35:49 +0000 Subject: [PATCH 127/213] Bump technote-space/get-diff-action from 4.0.1 to 6.1.0 (#3198) Bumps [technote-space/get-diff-action](https://github.com/technote-space/get-diff-action) from 4.0.1 to 6.1.0. - [Release notes](https://github.com/technote-space/get-diff-action/releases) - [Changelog](https://github.com/technote-space/get-diff-action/blob/main/.releasegarc) - [Commits](https://github.com/technote-space/get-diff-action/compare/v4.0.1...v6.1.0) --- updated-dependencies: - dependency-name: technote-space/get-diff-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pre-commit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index c385ab2727..43122276fa 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -14,7 +14,7 @@ jobs: - name: Checkout code uses: actions/checkout@v3 - name: Find changed files - uses: technote-space/get-diff-action@v4.0.1 + uses: technote-space/get-diff-action@v6.1.0 id: git-diff with: PATTERNS: | From 00469199cf35eef1f8f5a30d8205f9cce7b520dd Mon Sep 17 00:00:00 2001 From: maralorn Date: Mon, 19 Sep 2022 18:45:35 +0200 Subject: [PATCH 128/213] docs/supported-versions: Fix typo and make brittany versions more precise (#3201) --- docs/supported-versions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/supported-versions.md b/docs/supported-versions.md index c713823a2e..647634240d 100644 --- a/docs/supported-versions.md +++ b/docs/supported-versions.md @@ -49,8 +49,8 @@ Sometimes a plugin will be supported in the pre-built binaries but not in a HLS | Plugin | Unsupported GHC versions | |-------------------------------------|--------------------------| | `hls-alternate-number-plugin` | | -| `hls-brittany-plugin` | 9.2, 9.4 | -| `hls-code-ragne-plugin` | 9.4 | +| `hls-brittany-plugin` | 9.0.2, 9.2, 9.4 | +| `hls-code-range-plugin` | 9.4 | | `hls-call-hierarchy-plugin` | | | `hls-class-plugin` | 9.4 | | `hls-eval-plugin` | 9.4 | From bd1d0a1675fb8c35692f6f847e2f7e9a10dc1476 Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 19 Sep 2022 22:31:37 +0200 Subject: [PATCH 129/213] Stylish Haskell: CPP parse issues (#3199) * Stylish Haskell: Parse issues * CPP language extension * Manually fix some files with duplication * Pre-commit: Fix ignored files It looks like `ExactPrint` was ignored before, but since absolute paths are used and weren't adapted it wasn't ignored afterwards. --- .pre-commit-config.yaml | 2 +- .stylish-haskell.yaml | 1 + .../session-loader/Development/IDE/Session.hs | 16 +++++++-------- .../src/Development/IDE/GHC/Compat/Iface.hs | 2 +- .../src/Development/IDE/Import/FindImports.hs | 7 ++++--- .../IDE/Plugin/Completions/Logic.hs | 6 ++++-- .../src/Ide/Plugin/HaddockComments.hs | 4 ++-- .../src/Development/IDE/Plugin/CodeAction.hs | 20 ++++++++++--------- .../Development/IDE/Plugin/CodeAction/Util.hs | 17 ++++++++-------- .../hls-stan-plugin/src/Ide/Plugin/Stan.hs | 4 ++-- 10 files changed, 43 insertions(+), 36 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ba541e83e3..9ef5013bd1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ "hooks": [ { "entry": "stylish-haskell --inplace", - "exclude": "(^Setup.hs$|test/testdata/.*$|test/data/.*$|test/manual/lhs/.*$|^hie-compat/.*$|^plugins/hls-tactics-plugin/.*$|^ghcide/src/Development/IDE/GHC/Compat.hs$|^ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs$|^ghcide/src/Development/IDE/GHC/Compat/Core.hs$|^ghcide/src/Development/IDE/Spans/Pragmas.hs$|^ghcide/src/Development/IDE/LSP/Outline.hs$|^plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs$|^ghcide/test/exe/Main.hs$|ghcide/src/Development/IDE/Core/Rules.hs|^hls-test-utils/src/Test/Hls/Util.hs$)", + "exclude": "(^Setup.hs$|test/testdata/.*$|test/data/.*$|test/manual/lhs/.*$|^hie-compat/.*$|^plugins/hls-tactics-plugin/.*$|^ghcide/src/Development/IDE/GHC/Compat.hs$|^plugins/hls-refactor-plugin/src/Development/IDE/GHC/Compat/ExactPrint.hs$|^ghcide/src/Development/IDE/GHC/Compat/Core.hs$|^ghcide/src/Development/IDE/Spans/Pragmas.hs$|^ghcide/src/Development/IDE/LSP/Outline.hs$|^plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs$|^ghcide/test/exe/Main.hs$|^ghcide/src/Development/IDE/Core/Rules.hs$|^hls-test-utils/src/Test/Hls/Util.hs$|^ghcide/src/Development/IDE/Core/Compile.hs$|^plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs$|^plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs$)", "files": "\\.l?hs$", "id": "stylish-haskell", "language": "system", diff --git a/.stylish-haskell.yaml b/.stylish-haskell.yaml index f64e341e96..76840c7497 100644 --- a/.stylish-haskell.yaml +++ b/.stylish-haskell.yaml @@ -57,6 +57,7 @@ newline: lf language_extensions: - BangPatterns + - CPP - DataKinds - DeriveFunctor - DeriveGeneric diff --git a/ghcide/session-loader/Development/IDE/Session.hs b/ghcide/session-loader/Development/IDE/Session.hs index 9da4b5a88d..886b035bb7 100644 --- a/ghcide/session-loader/Development/IDE/Session.hs +++ b/ghcide/session-loader/Development/IDE/Session.hs @@ -1,7 +1,7 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE CPP #-} {-| The logic for setting up a ghcide session by tapping into hie-bios. @@ -67,15 +67,16 @@ import Development.IDE.Types.Location import Development.IDE.Types.Logger (Pretty (pretty), Priority (Debug, Error, Info, Warning), Recorder, WithPriority, - logWith, nest, vcat, - viaShow, (<+>), - toCologActionWithPrio, cmapWithPrio) + cmapWithPrio, logWith, + nest, + toCologActionWithPrio, + vcat, viaShow, (<+>)) import Development.IDE.Types.Options import GHC.Check import qualified HIE.Bios as HieBios -import qualified HIE.Bios.Types as HieBios import HIE.Bios.Environment hiding (getCacheDir) import HIE.Bios.Types hiding (Log) +import qualified HIE.Bios.Types as HieBios import Hie.Implicit.Cradle (loadImplicitHieCradle) import Language.LSP.Server import Language.LSP.Types @@ -90,6 +91,8 @@ import Data.Void import Control.Concurrent.STM.Stats (atomically, modifyTVar', readTVar, writeTVar) import Control.Concurrent.STM.TQueue +import Control.DeepSeq +import Control.Exception (evaluate) import Control.Monad.IO.Unlift (MonadUnliftIO) import Data.Foldable (for_) import Data.HashMap.Strict (HashMap) @@ -103,9 +106,6 @@ import HieDb.Types import HieDb.Utils import qualified System.Random as Random import System.Random (RandomGen) -import Control.Monad.IO.Unlift (MonadUnliftIO) -import Control.Exception (evaluate) -import Control.DeepSeq data Log = LogSettingInitialDynFlags diff --git a/ghcide/src/Development/IDE/GHC/Compat/Iface.hs b/ghcide/src/Development/IDE/GHC/Compat/Iface.hs index e0b36a13a9..5df7eeff2d 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Iface.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Iface.hs @@ -8,7 +8,7 @@ module Development.IDE.GHC.Compat.Iface ( import GHC #if MIN_VERSION_ghc(9,3,0) -import GHC.Driver.Session (targetProfile) +import GHC.Driver.Session (targetProfile) #endif #if MIN_VERSION_ghc(9,2,0) import qualified GHC.Iface.Load as Iface diff --git a/ghcide/src/Development/IDE/Import/FindImports.hs b/ghcide/src/Development/IDE/Import/FindImports.hs index 2cc08b9f57..b0efe64858 100644 --- a/ghcide/src/Development/IDE/Import/FindImports.hs +++ b/ghcide/src/Development/IDE/Import/FindImports.hs @@ -28,7 +28,7 @@ import Data.List (isSuffixOf) import Data.Maybe import System.FilePath #if MIN_VERSION_ghc(9,3,0) -import GHC.Types.PkgQual +import GHC.Types.PkgQual #endif data Import @@ -123,11 +123,12 @@ locateModule env comp_info exts targetFor modName mbPkgName isSource = do #if MIN_VERSION_ghc(9,3,0) OtherPkg uid | Just dirs <- lookup uid import_paths + -> lookupLocal uid dirs #else Just pkgName | Just (uid, dirs) <- lookup (PackageName pkgName) import_paths -#endif -> lookupLocal uid dirs +#endif | otherwise -> lookupInPackageDB env #if MIN_VERSION_ghc(9,3,0) NoPkgQual -> do @@ -148,7 +149,7 @@ locateModule env comp_info exts targetFor modName mbPkgName isSource = do mbFile <- locateModuleFile ((homeUnitId_ dflags, importPaths dflags) : import_paths') exts targetFor isSource $ unLoc modName case mbFile of - Nothing -> lookupInPackageDB env + Nothing -> lookupInPackageDB env Just (uid, file) -> toModLocation uid file where dflags = hsc_dflags env diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index 415743d082..6e03a61a22 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -59,7 +59,8 @@ import GHC.Plugins (Depth (AllTheWay), #endif import Ide.PluginUtils (mkLspCommand) import Ide.Types (CommandId (..), - IdePlugins(..), PluginId) + IdePlugins (..), + PluginId) import Language.LSP.Types import Language.LSP.Types.Capabilities import qualified Language.LSP.VFS as VFS @@ -516,7 +517,6 @@ findRecordCompl uri pmod mn DataDecl {tcdLName, tcdDataDefn} = result PrefixCon{} -> Just [] _ -> Nothing - extract ConDeclField{..} -- NOTE: 'cd_fld_names' is grouped so that the fields -- sharing the same type declaration to fit in the same group; e.g. -- @@ -527,8 +527,10 @@ findRecordCompl uri pmod mn DataDecl {tcdLName, tcdDataDefn} = result -- is encoded as @[[arg1, arg2], [arg3], [arg4]]@ -- Hence, we must concat nested arguments into one to get all the fields. #if MIN_VERSION_ghc(9,3,0) + extract ConDeclField{..} = map (foLabel . unLoc) cd_fld_names #else + extract ConDeclField{..} = map (rdrNameFieldOcc . unLoc) cd_fld_names #endif -- XConDeclField diff --git a/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments.hs b/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments.hs index 2993219893..79acf7b072 100644 --- a/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments.hs +++ b/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments.hs @@ -15,10 +15,10 @@ import qualified Data.Map as Map import qualified Data.Text as T import Development.IDE hiding (pluginHandlers) import Development.IDE.GHC.Compat -import Development.IDE.Plugin.CodeAction import Development.IDE.GHC.Compat.ExactPrint import Development.IDE.GHC.ExactPrint (GetAnnotatedParsedSource (..)) -import qualified Development.IDE.GHC.ExactPrint as E +import qualified Development.IDE.GHC.ExactPrint as E +import Development.IDE.Plugin.CodeAction import Ide.Types import Language.Haskell.GHC.ExactPrint import Language.Haskell.GHC.ExactPrint.Types hiding (GhcPs) diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index 32ea6bf8ce..9775809e63 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -19,9 +19,9 @@ import Control.Arrow (second, (&&&), (>>>)) import Control.Concurrent.STM.Stats (atomically) +import Control.Monad.Extra import Control.Monad.IO.Class import Control.Monad.Trans.Maybe -import Control.Monad.Extra import Data.Aeson import Data.Char import qualified Data.DList as DL @@ -39,50 +39,52 @@ import qualified Data.Set as S import qualified Data.Text as T import qualified Data.Text.Utf16.Rope as Rope import Data.Tuple.Extra (fst3) -import Development.IDE.Types.Logger hiding (group) import Development.IDE.Core.Rules import Development.IDE.Core.RuleTypes import Development.IDE.Core.Service +import Development.IDE.Core.Shake hiding (Log) import Development.IDE.GHC.Compat import Development.IDE.GHC.Compat.ExactPrint import Development.IDE.GHC.Compat.Util import Development.IDE.GHC.Error import Development.IDE.GHC.ExactPrint -import qualified Development.IDE.GHC.ExactPrint as E +import qualified Development.IDE.GHC.ExactPrint as E import Development.IDE.GHC.Util (printOutputable, printRdrName) -import Development.IDE.Core.Shake hiding (Log) import Development.IDE.Plugin.CodeAction.Args import Development.IDE.Plugin.CodeAction.ExactPrint -import Development.IDE.Plugin.CodeAction.Util import Development.IDE.Plugin.CodeAction.PositionIndexed +import Development.IDE.Plugin.CodeAction.Util import Development.IDE.Plugin.Completions.Types import Development.IDE.Plugin.TypeLenses (suggestSignature) import Development.IDE.Types.Exports import Development.IDE.Types.Location +import Development.IDE.Types.Logger hiding + (group) import Development.IDE.Types.Options +import GHC.Exts (fromList) import qualified GHC.LanguageExtensions as Lang import Ide.PluginUtils (subRange) import Ide.Types import qualified Language.LSP.Server as LSP -import Language.LSP.Types (ApplyWorkspaceEditParams(..), CodeAction (..), +import Language.LSP.Types (ApplyWorkspaceEditParams (..), + CodeAction (..), CodeActionContext (CodeActionContext, _diagnostics), CodeActionKind (CodeActionQuickFix, CodeActionUnknown), CodeActionParams (CodeActionParams), Command, Diagnostic (..), - MessageType (..), - ShowMessageParams (..), List (..), + MessageType (..), ResponseError, SMethod (..), + ShowMessageParams (..), TextDocumentIdentifier (TextDocumentIdentifier), TextEdit (TextEdit, _range), UInt, WorkspaceEdit (WorkspaceEdit, _changeAnnotations, _changes, _documentChanges), type (|?) (InR), uriToFilePath) -import GHC.Exts (fromList) import Language.LSP.VFS (VirtualFile, _file_text) import Text.Regex.TDFA (mrAfter, diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Util.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Util.hs index 6f51131bd6..0b33d5112f 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Util.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Util.hs @@ -1,22 +1,23 @@ module Development.IDE.Plugin.CodeAction.Util where -import Data.Data (Data) -import qualified Data.Unique as U +import Data.Data (Data) +import Data.Time.Clock.POSIX (POSIXTime, + getCurrentTime, + utcTimeToPOSIXSeconds) +import qualified Data.Unique as U import Debug.Trace import Development.IDE.GHC.Compat.ExactPrint as GHC +import Development.IDE.GHC.Dump (showAstDataHtml) import GHC.Stack -import System.Environment.Blank (getEnvDefault) +import System.Environment.Blank (getEnvDefault) import System.IO.Unsafe import Text.Printf -import Development.IDE.GHC.Dump (showAstDataHtml) -import Data.Time.Clock.POSIX (POSIXTime, getCurrentTime, - utcTimeToPOSIXSeconds) #if MIN_VERSION_ghc(9,2,0) import GHC.Utils.Outputable #else -import Development.IDE.GHC.Util -import Development.IDE.GHC.Compat.Util import Development.IDE.GHC.Compat +import Development.IDE.GHC.Compat.Util +import Development.IDE.GHC.Util #endif -------------------------------------------------------------------------------- -- Tracing exactprint terms diff --git a/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs b/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs index 4104ac910a..73f7f25c18 100644 --- a/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs +++ b/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs @@ -7,15 +7,15 @@ import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Maybe (MaybeT (MaybeT), runMaybeT) import Data.Default import Data.Foldable (toList) -import qualified Data.HashMap.Strict as HM import Data.Hashable (Hashable) +import qualified Data.HashMap.Strict as HM import qualified Data.Map as Map import Data.Maybe (fromJust, mapMaybe) import qualified Data.Text as T import Development.IDE -import Development.IDE.Core.RuleTypes (HieAstResult (..)) import Development.IDE.Core.Rules (getHieFile, getSourceFileSource) +import Development.IDE.Core.RuleTypes (HieAstResult (..)) import qualified Development.IDE.Core.Shake as Shake import Development.IDE.GHC.Compat (HieASTs (HieASTs), RealSrcSpan (..), mkHieFile', From 42bcf9229a966aaba0dfe35be678d6a765b48985 Mon Sep 17 00:00:00 2001 From: Aarush Bhat Date: Wed, 21 Sep 2022 17:29:57 +0530 Subject: [PATCH 130/213] Feat: Folding Ranges (#3058) * save some progress: add basic starter code for folding ranges * save some progress: add function to traverse through coderange and form folding ranges * save some progress: add parsing of folding ranges * fix: maybe issue with foldingRanges * add: generate folding ranges from coderange * add: plugin request method instance for folding ranges * ref: alter function and var names * post review: cleanup crk to frk & fix typo * fix: find folding ranges function * format: run formatter and add comments * fix: return all response results of folding range request * Revert "format: run formatter and add comments" This reverts commit e6a2b5ca3e8232bc082ce84d22545a5bd462a2ff. * add: removed comments after revert * fix: formatting * docs: add folding range to features section and cabal file * refactor: use destructuring for createFoldingRange function and use characters * test: add basic unit test for findFoldingRanges function * test: add tests for children and code kind * test: add more test cases * test: add test for createFoldingRange * test: add integration test for folding ranges * fix: duplicate start line foldingranges and remove single line foldingranges * refactor: duplicate folding range functionality * fix: formatting in code range plugin * added more descriptive comments and encorporate code review suggestions * revert: automatic formatting for selection range test case file * fix: ignoring children if root fails to provide folding ranges * remove: redundant match on crkToFrk * revert: filtering same line foldings and multiple foldings on the same line as it can be handled by clients * revert: formatting change to selection range test file * fix: entire file folding because of root node Co-authored-by: Kobayashi --- docs/features.md | 38 ++++++---- hls-plugin-api/src/Ide/Plugin/Config.hs | 6 +- hls-plugin-api/src/Ide/Types.hs | 10 +++ .../hls-code-range-plugin.cabal | 2 +- .../src/Ide/Plugin/CodeRange.hs | 70 +++++++++++++++++-- .../src/Ide/Plugin/CodeRange/Rules.hs | 11 ++- .../test/Ide/Plugin/CodeRangeTest.hs | 69 +++++++++++++++++- plugins/hls-code-range-plugin/test/Main.hs | 28 +++++++- .../folding-range/Function.golden.txt | 41 +++++++++++ .../test/testdata/folding-range/Function.hs | 13 ++++ .../test/testdata/folding-range/hie.yaml | 4 ++ 11 files changed, 265 insertions(+), 27 deletions(-) create mode 100644 plugins/hls-code-range-plugin/test/testdata/folding-range/Function.golden.txt create mode 100644 plugins/hls-code-range-plugin/test/testdata/folding-range/Function.hs create mode 100644 plugins/hls-code-range-plugin/test/testdata/folding-range/hie.yaml diff --git a/docs/features.md b/docs/features.md index 793b66a61e..4154b72b53 100644 --- a/docs/features.md +++ b/docs/features.md @@ -4,7 +4,7 @@ This table gives a summary of the features that HLS supports. Many of these are standard LSP features, but a lot of special features are provided as [code actions](#code-actions) and [code lenses](#code-lenses). | Feature | [LSP method](./what-is-hls.md#lsp-terminology) | -|-----------------------------------------------------|---------------------------------------------------------------------------------------------------| +| --------------------------------------------------- | ------------------------------------------------------------------------------------------------- | | [Diagnostics](#diagnostics) | `textDocument/publishDiagnostics` | | [Hovers](#hovers) | `textDocument/hover` | | [Jump to definition](#jump-to-definition) | `textDocument/definition` | @@ -100,7 +100,7 @@ Completions for language pragmas. Format your code with various Haskell code formatters. | Formatter | Provided by | -|-----------------|------------------------------| +| --------------- | ---------------------------- | | Brittany | `hls-brittany-plugin` | | Floskell | `hls-floskell-plugin` | | Fourmolu | `hls-fourmolu-plugin` | @@ -261,6 +261,7 @@ Change/Update a type signature to match implementation. Status: Until GHC 9.4, the implementation is ad-hoc and relies on GHC error messages to create a new signature. Not all GHC error messages are supported. Known Limitations: + - Not all GHC error messages are supported - Top-level and Function-local bindings with the same names can cause issues, such as incorrect signature changes or no code actions available. @@ -337,6 +338,16 @@ support. ![Selection range demo](https://user-images.githubusercontent.com/16440269/177240833-7dc8fe39-b446-477e-b5b1-7fc303608d4f.gif) +## Folding range + +Provided by: `hls-code-range-plugin` + +Provides haskell specific +[Folding](https://code.visualstudio.com/docs/editor/codebasics#_folding) +support. + +![Folding range demo](https://user-images.githubusercontent.com/54478821/184468510-7c0d5182-c684-48ef-9b39-3866dc2309df.gif) + ## Rename Provided by: `hls-rename-plugin` @@ -354,15 +365,14 @@ Known limitations: The following features are supported by the LSP specification but not implemented in HLS. Contributions welcome! -| Feature | Status | [LSP method](./what-is-hls.md#lsp-terminology) | -|------------------------|------------------------------------------------------------------------------------------|-----------------------------------------------------| -| Signature help | Unimplemented | `textDocument/signatureHelp` | -| Jump to declaration | Unclear if useful | `textDocument/declaration` | -| Jump to implementation | Unclear if useful | `textDocument/implementation` | -| Folding | Unimplemented | `textDocument/foldingRange` | -| Semantic tokens | Unimplemented | `textDocument/semanticTokens` | -| Linked editing | Unimplemented | `textDocument/linkedEditingRange` | -| Document links | Unimplemented | `textDocument/documentLink` | -| Document color | Unclear if useful | `textDocument/documentColor` | -| Color presentation | Unclear if useful | `textDocument/colorPresentation` | -| Monikers | Unclear if useful | `textDocument/moniker` | +| Feature | Status | [LSP method](./what-is-hls.md#lsp-terminology) | +| ---------------------- | ----------------- | ---------------------------------------------- | +| Signature help | Unimplemented | `textDocument/signatureHelp` | +| Jump to declaration | Unclear if useful | `textDocument/declaration` | +| Jump to implementation | Unclear if useful | `textDocument/implementation` | +| Semantic tokens | Unimplemented | `textDocument/semanticTokens` | +| Linked editing | Unimplemented | `textDocument/linkedEditingRange` | +| Document links | Unimplemented | `textDocument/documentLink` | +| Document color | Unclear if useful | `textDocument/documentColor` | +| Color presentation | Unclear if useful | `textDocument/colorPresentation` | +| Monikers | Unclear if useful | `textDocument/moniker` | diff --git a/hls-plugin-api/src/Ide/Plugin/Config.hs b/hls-plugin-api/src/Ide/Plugin/Config.hs index 8aaafd9849..13f33278a8 100644 --- a/hls-plugin-api/src/Ide/Plugin/Config.hs +++ b/hls-plugin-api/src/Ide/Plugin/Config.hs @@ -110,6 +110,7 @@ data PluginConfig = , plcCompletionOn :: !Bool , plcRenameOn :: !Bool , plcSelectionRangeOn :: !Bool + , plcFoldingRangeOn :: !Bool , plcConfig :: !A.Object } deriving (Show,Eq) @@ -125,11 +126,12 @@ instance Default PluginConfig where , plcCompletionOn = True , plcRenameOn = True , plcSelectionRangeOn = True + , plcFoldingRangeOn = True , plcConfig = mempty } instance A.ToJSON PluginConfig where - toJSON (PluginConfig g ch ca cl d h s c rn sr cfg) = r + toJSON (PluginConfig g ch ca cl d h s c rn sr fr cfg) = r where r = object [ "globalOn" .= g , "callHierarchyOn" .= ch @@ -141,6 +143,7 @@ instance A.ToJSON PluginConfig where , "completionOn" .= c , "renameOn" .= rn , "selectionRangeOn" .= sr + , "foldingRangeOn" .= fr , "config" .= cfg ] @@ -156,6 +159,7 @@ instance A.FromJSON PluginConfig where <*> o .:? "completionOn" .!= plcCompletionOn def <*> o .:? "renameOn" .!= plcRenameOn def <*> o .:? "selectionRangeOn" .!= plcSelectionRangeOn def + <*> o .:? "foldingRangeOn" .!= plcFoldingRangeOn def <*> o .:? "config" .!= plcConfig def -- --------------------------------------------------------------------- diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index 997300900a..f41c17286b 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -429,6 +429,13 @@ instance PluginMethod Request TextDocumentSelectionRange where uri = msgParams ^. J.textDocument . J.uri pid = pluginId pluginDesc +instance PluginMethod Request TextDocumentFoldingRange where + pluginEnabled _ msgParams pluginDesc conf = pluginResponsible uri pluginDesc + && pluginEnabledConfig plcFoldingRangeOn pid conf + where + uri = msgParams ^. J.textDocument . J.uri + pid = pluginId pluginDesc + instance PluginMethod Request CallHierarchyIncomingCalls where -- This method has no URI parameter, thus no call to 'pluginResponsible' pluginEnabled _ _ pluginDesc conf = pluginEnabledConfig plcCallHierarchyOn pid conf @@ -529,6 +536,9 @@ instance PluginRequestMethod TextDocumentPrepareCallHierarchy where instance PluginRequestMethod TextDocumentSelectionRange where combineResponses _ _ _ _ (x :| _) = x +instance PluginRequestMethod TextDocumentFoldingRange where + combineResponses _ _ _ _ x = sconcat x + instance PluginRequestMethod CallHierarchyIncomingCalls where instance PluginRequestMethod CallHierarchyOutgoingCalls where diff --git a/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal b/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal index 50849b35e2..a9000a0158 100644 --- a/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal +++ b/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal @@ -2,7 +2,7 @@ cabal-version: 2.4 name: hls-code-range-plugin version: 1.0.0.0 synopsis: - HLS Plugin to support smart selection range + HLS Plugin to support smart selection range and Folding range description: Please see the README on GitHub at diff --git a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs index 0a48a3467b..75fb1eca53 100644 --- a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs +++ b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs @@ -1,5 +1,6 @@ -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} module Ide.Plugin.CodeRange ( descriptor @@ -7,6 +8,8 @@ module Ide.Plugin.CodeRange ( -- * Internal , findPosition + , findFoldingRanges + , createFoldingRange ) where import Control.Monad.Except (ExceptT (ExceptT), @@ -33,7 +36,7 @@ import Development.IDE.Core.PositionMapping (PositionMapping, import Development.IDE.Types.Logger (Pretty (..)) import Ide.Plugin.CodeRange.Rules (CodeRange (..), GetCodeRange (..), - codeRangeRule) + codeRangeRule, crkToFrk) import qualified Ide.Plugin.CodeRange.Rules as Rules (Log) import Ide.PluginUtils (pluginResponse, positionInRange) @@ -42,12 +45,14 @@ import Ide.Types (PluginDescriptor (pluginH defaultPluginDescriptor, mkPluginHandler) import Language.LSP.Server (LspM) -import Language.LSP.Types (List (List), +import Language.LSP.Types (FoldingRange (..), + FoldingRangeParams (..), + List (List), NormalizedFilePath, Position (..), Range (_start), ResponseError, - SMethod (STextDocumentSelectionRange), + SMethod (STextDocumentFoldingRange, STextDocumentSelectionRange), SelectionRange (..), SelectionRangeParams (..), TextDocumentIdentifier (TextDocumentIdentifier), @@ -57,8 +62,7 @@ import Prelude hiding (log, span) descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState descriptor recorder plId = (defaultPluginDescriptor plId) { pluginHandlers = mkPluginHandler STextDocumentSelectionRange selectionRangeHandler - -- TODO @sloorush add folding range - -- <> mkPluginHandler STextDocumentFoldingRange foldingRangeHandler + <> mkPluginHandler STextDocumentFoldingRange foldingRangeHandler , pluginRules = codeRangeRule (cmapWithPrio LogRules recorder) } @@ -68,6 +72,25 @@ instance Pretty Log where pretty log = case log of LogRules codeRangeLog -> pretty codeRangeLog +foldingRangeHandler :: IdeState -> PluginId -> FoldingRangeParams -> LspM c (Either ResponseError (List FoldingRange)) +foldingRangeHandler ide _ FoldingRangeParams{..} = do + pluginResponse $ do + filePath <- ExceptT . pure . maybeToEither "fail to convert uri to file path" $ + toNormalizedFilePath' <$> uriToFilePath' uri + foldingRanges <- ExceptT . liftIO . runIdeAction "FoldingRange" (shakeExtras ide) . runExceptT $ + getFoldingRanges filePath + pure . List $ foldingRanges + where + uri :: Uri + TextDocumentIdentifier uri = _textDocument + +getFoldingRanges :: NormalizedFilePath -> ExceptT String IdeAction [FoldingRange] +getFoldingRanges file = do + (codeRange, _) <- maybeToExceptT "fail to get code range" $ useE GetCodeRange file + + -- removing first node because it folds the entire file + pure $ drop 1 $ findFoldingRanges codeRange + selectionRangeHandler :: IdeState -> PluginId -> SelectionRangeParams -> LspM c (Either ResponseError (List SelectionRange)) selectionRangeHandler ide _ SelectionRangeParams{..} = do pluginResponse $ do @@ -126,6 +149,39 @@ findPosition pos root = go Nothing root startOfRight <- _start . _codeRange_range <$> V.headM right if pos < startOfRight then binarySearchPos left else binarySearchPos right +-- | Traverses through the code range and it children to a folding ranges. +-- +-- It starts with the root node, converts that into a folding range then moves towards the children. +-- It converts each child of each root node and parses it to folding range and moves to its children. +-- +-- Two cases to that are assumed to be taken care on the client side are: +-- +-- 1. When a folding range starts and ends on the same line, it is upto the client if it wants to +-- fold a single line folding or not. +-- +-- 2. As we are converting nodes of the ast into folding ranges, there are multiple nodes starting from a single line. +-- A single line of code doesn't mean a single node in AST, so this function removes all the nodes that have a duplicate +-- start line, ie. they start from the same line. +-- Eg. A multi-line function that also has a multi-line if statement starting from the same line should have the folding +-- according to the function. +-- +-- We think the client can handle this, if not we could change to remove these in future +-- +-- Discussion reference: https://github.com/haskell/haskell-language-server/pull/3058#discussion_r973737211 +findFoldingRanges :: CodeRange -> [FoldingRange] +findFoldingRanges r@(CodeRange _ children _) = + let frChildren :: [FoldingRange] = concat $ V.toList $ fmap findFoldingRanges children + in case createFoldingRange r of + Just x -> x:frChildren + Nothing -> frChildren + +-- | Parses code range to folding range +createFoldingRange :: CodeRange -> Maybe FoldingRange +createFoldingRange (CodeRange (Range (Position lineStart charStart) (Position lineEnd charEnd)) _ ck) = do + -- Type conversion of codeRangeKind to FoldingRangeKind + let frk = crkToFrk ck + Just (FoldingRange lineStart (Just charStart) lineEnd (Just charEnd) (Just frk)) + -- | Likes 'toCurrentPosition', but works on 'SelectionRange' toCurrentSelectionRange :: PositionMapping -> SelectionRange -> Maybe SelectionRange toCurrentSelectionRange positionMapping SelectionRange{..} = do diff --git a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs index 05515bb8e8..13a2dd3847 100644 --- a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs +++ b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs @@ -21,6 +21,7 @@ module Ide.Plugin.CodeRange.Rules -- * Internal , removeInterleaving , simplify + , crkToFrk ) where import Control.DeepSeq (NFData) @@ -52,6 +53,7 @@ import Ide.Plugin.CodeRange.ASTPreProcess (CustomNodeType (..), PreProcessEnv (..), isCustomNode, preProcessAST) +import Language.LSP.Types (FoldingRangeKind (FoldingRangeComment, FoldingRangeImports, FoldingRangeRegion)) import Language.LSP.Types.Lens (HasEnd (end), HasStart (start)) import Prelude hiding (log) @@ -89,7 +91,7 @@ data CodeRangeKind = | CodeKindImports -- | a comment | CodeKindComment - deriving (Show, Generic, NFData) + deriving (Show, Eq, Generic, NFData) Lens.makeLenses ''CodeRange @@ -189,3 +191,10 @@ handleError recorder action' = do logWith recorder Error msg pure $ toIdeResult (Left []) Right value -> pure $ toIdeResult (Right value) + +-- | Maps type CodeRangeKind to FoldingRangeKind +crkToFrk :: CodeRangeKind -> FoldingRangeKind +crkToFrk crk = case crk of + CodeKindComment -> FoldingRangeComment + CodeKindImports -> FoldingRangeImports + CodeKindRegion -> FoldingRangeRegion diff --git a/plugins/hls-code-range-plugin/test/Ide/Plugin/CodeRangeTest.hs b/plugins/hls-code-range-plugin/test/Ide/Plugin/CodeRangeTest.hs index 73bebf3a2a..8495b1ee4d 100644 --- a/plugins/hls-code-range-plugin/test/Ide/Plugin/CodeRangeTest.hs +++ b/plugins/hls-code-range-plugin/test/Ide/Plugin/CodeRangeTest.hs @@ -17,7 +17,7 @@ testTree = mkCodeRange :: Position -> Position -> V.Vector CodeRange -> CodeRange mkCodeRange start end children = CodeRange (Range start end) children CodeKindRegion - in [ + in [ testCase "not in range" $ check (Position 10 1) (mkCodeRange (Position 1 1) (Position 5 10) []) @@ -50,5 +50,70 @@ testTree = ( SelectionRange (Range (Position 1 1) (Position 5 10)) Nothing ) ) - ] + ], + + -- TODO: Some more tests can be added on strange cases like + -- 1. lots of blank lines in between type signature and the body + -- 2. lots of blank lines in the function itself + -- etc. + testGroup "findFoldingRanges" $ + let check :: CodeRange -> [FoldingRange] -> Assertion + check codeRange = (findFoldingRanges codeRange @?=) + + mkCodeRange :: Position -> Position -> V.Vector CodeRange -> CodeRangeKind -> CodeRange + mkCodeRange start end children crk = CodeRange (Range start end) children crk + in [ + -- General test + testCase "Test General Code Block" $ check + (mkCodeRange (Position 1 1) (Position 5 10) [] CodeKindRegion) + [FoldingRange 1 (Just 1) 5 (Just 10) (Just FoldingRangeRegion)], + + -- Tests for code kind + testCase "Test Code Kind Region" $ check + (mkCodeRange (Position 1 1) (Position 5 10) [] CodeKindRegion) + [FoldingRange 1 (Just 1) 5 (Just 10) (Just FoldingRangeRegion)], + testCase "Test Code Kind Comment" $ check + (mkCodeRange (Position 1 1) (Position 5 10) [] CodeKindComment) + [FoldingRange 1 (Just 1) 5 (Just 10) (Just FoldingRangeComment)], + testCase "Test Code Kind Import" $ check + (mkCodeRange (Position 1 1) (Position 5 10) [] CodeKindImports) + [FoldingRange 1 (Just 1) 5 (Just 10) (Just FoldingRangeImports)], + + -- Test for Code Portions with children + testCase "Test Children" $ check + (mkCodeRange (Position 1 1) (Position 5 10) [ + mkCodeRange (Position 1 2) (Position 3 6) [] CodeKindRegion, + mkCodeRange (Position 3 7) (Position 5 10) [] CodeKindRegion + ] CodeKindRegion) + [FoldingRange 1 (Just 1) 5 (Just 10) (Just FoldingRangeRegion), + FoldingRange 1 (Just 2) 3 (Just 6) (Just FoldingRangeRegion), + FoldingRange 3 (Just 7) 5 (Just 10) (Just FoldingRangeRegion)], + + -- Single line returns [] because single line ranges need not be folded + testCase "Test Single Line" $ check + (mkCodeRange (Position 1 0) (Position 1 15) [] CodeKindRegion) + [FoldingRange 1 (Just 0) 1 (Just 15) (Just FoldingRangeRegion)], + + -- MultiLine imports + testCase "MultiLine Imports" $ check + (mkCodeRange (Position 1 0) (Position 5 15) [] CodeKindImports) + [FoldingRange 1 (Just 0) 5 (Just 15) (Just FoldingRangeImports)] + ], + + testGroup "createFoldingRange" $ + let check :: CodeRange -> Maybe FoldingRange -> Assertion + check codeRange = (createFoldingRange codeRange @?=) + + mkCodeRange :: Position -> Position -> V.Vector CodeRange -> CodeRangeKind -> CodeRange + mkCodeRange start end children crk = CodeRange (Range start end) children crk + in [ + -- General tests + testCase "Test General Code Block" $ check + (mkCodeRange (Position 1 1) (Position 5 10) [] CodeKindRegion) + (Just (FoldingRange 1 (Just 1) 5 (Just 10) (Just FoldingRangeRegion))), + -- If a range has the same start and end line it need not be folded so Nothing is expected + testCase "Test Same Start Line" $ check + (mkCodeRange (Position 1 1) (Position 1 10) [] CodeKindRegion) + (Just (FoldingRange 1 (Just 1) 1 (Just 10) (Just FoldingRangeRegion))) + ] ] diff --git a/plugins/hls-code-range-plugin/test/Main.hs b/plugins/hls-code-range-plugin/test/Main.hs index bffc3f716e..1738c41fbe 100644 --- a/plugins/hls-code-range-plugin/test/Main.hs +++ b/plugins/hls-code-range-plugin/test/Main.hs @@ -28,7 +28,8 @@ main = do testGroup "Code Range" [ testGroup "Integration Tests" [ makeSelectionRangeGoldenTest recorder "Import" [(4, 36), (1, 8)], - makeSelectionRangeGoldenTest recorder "Function" [(5, 19), (5, 12), (4, 4), (3, 5)] + makeSelectionRangeGoldenTest recorder "Function" [(5, 19), (5, 12), (4, 4), (3, 5)], + foldingRangeGoldenTest recorder "Function" ], testGroup "Unit Tests" [ Ide.Plugin.CodeRangeTest.testTree, @@ -64,3 +65,28 @@ makeSelectionRangeGoldenTest recorder testName positions = goldenGitDiff testNam showPosition :: Position -> ByteString showPosition (Position line col) = "(" <> showLBS (line + 1) <> "," <> showLBS (col + 1) <> ")" showLBS = fromString . show + +foldingRangeGoldenTest :: Recorder (WithPriority Log) -> TestName -> TestTree +foldingRangeGoldenTest recorder testName = goldenGitDiff testName (testDataDir testName <.> "golden" <.> "txt") $ do + res <- runSessionWithServer (plugin recorder) testDataDir $ do + doc <- openDoc (testName <.> "hs") "haskell" + resp <- request STextDocumentFoldingRange $ FoldingRangeParams Nothing Nothing doc + let res = resp ^. result + pure $ fmap showFoldingRangesForTest res + + case res of + Left err -> assertFailure (show err) + Right golden -> pure golden + + where + testDataDir :: FilePath + testDataDir = "test" "testdata" "folding-range" + + showFoldingRangesForTest :: List FoldingRange -> ByteString + showFoldingRangesForTest (List foldingRanges) = LBSChar8.intercalate "\n" $ fmap showFoldingRangeForTest foldingRanges + + showFoldingRangeForTest :: FoldingRange -> ByteString + showFoldingRangeForTest f@(FoldingRange sl (Just sc) el (Just ec) (Just frk)) = "((" <> showLBS sl <>", "<> showLBS sc <> ")" <> " : " <> "(" <> showLBS el <>", "<> showLBS ec<> ")) : " <> showFRK frk + + showLBS = fromString . show + showFRK = fromString . show diff --git a/plugins/hls-code-range-plugin/test/testdata/folding-range/Function.golden.txt b/plugins/hls-code-range-plugin/test/testdata/folding-range/Function.golden.txt new file mode 100644 index 0000000000..b7af2a60a0 --- /dev/null +++ b/plugins/hls-code-range-plugin/test/testdata/folding-range/Function.golden.txt @@ -0,0 +1,41 @@ +((2, 16) : (2, 22)) : FoldingRangeRegion +((4, 0) : (7, 21)) : FoldingRangeRegion +((4, 0) : (4, 25)) : FoldingRangeRegion +((4, 0) : (4, 6)) : FoldingRangeRegion +((4, 10) : (4, 25)) : FoldingRangeRegion +((4, 10) : (4, 17)) : FoldingRangeRegion +((4, 21) : (4, 25)) : FoldingRangeRegion +((5, 0) : (7, 21)) : FoldingRangeRegion +((5, 0) : (5, 6)) : FoldingRangeRegion +((5, 7) : (5, 8)) : FoldingRangeRegion +((5, 9) : (7, 21)) : FoldingRangeRegion +((5, 11) : (7, 21)) : FoldingRangeRegion +((5, 14) : (5, 28)) : FoldingRangeRegion +((5, 14) : (5, 23)) : FoldingRangeRegion +((5, 14) : (5, 15)) : FoldingRangeRegion +((5, 16) : (5, 21)) : FoldingRangeRegion +((5, 22) : (5, 23)) : FoldingRangeRegion +((5, 24) : (5, 26)) : FoldingRangeRegion +((5, 27) : (5, 28)) : FoldingRangeRegion +((6, 16) : (6, 20)) : FoldingRangeRegion +((7, 16) : (7, 21)) : FoldingRangeRegion +((9, 0) : (12, 20)) : FoldingRangeRegion +((9, 0) : (9, 24)) : FoldingRangeRegion +((9, 0) : (9, 5)) : FoldingRangeRegion +((9, 9) : (9, 24)) : FoldingRangeRegion +((9, 9) : (9, 16)) : FoldingRangeRegion +((9, 20) : (9, 24)) : FoldingRangeRegion +((10, 0) : (12, 20)) : FoldingRangeRegion +((10, 0) : (10, 5)) : FoldingRangeRegion +((10, 6) : (10, 7)) : FoldingRangeRegion +((10, 8) : (12, 20)) : FoldingRangeRegion +((10, 10) : (12, 20)) : FoldingRangeRegion +((10, 13) : (10, 27)) : FoldingRangeRegion +((10, 13) : (10, 22)) : FoldingRangeRegion +((10, 13) : (10, 14)) : FoldingRangeRegion +((10, 15) : (10, 20)) : FoldingRangeRegion +((10, 21) : (10, 22)) : FoldingRangeRegion +((10, 23) : (10, 25)) : FoldingRangeRegion +((10, 26) : (10, 27)) : FoldingRangeRegion +((11, 16) : (11, 21)) : FoldingRangeRegion +((12, 16) : (12, 20)) : FoldingRangeRegion \ No newline at end of file diff --git a/plugins/hls-code-range-plugin/test/testdata/folding-range/Function.hs b/plugins/hls-code-range-plugin/test/testdata/folding-range/Function.hs new file mode 100644 index 0000000000..b73bece14f --- /dev/null +++ b/plugins/hls-code-range-plugin/test/testdata/folding-range/Function.hs @@ -0,0 +1,13 @@ +{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} +{-# HLINT ignore "Use module export list" #-} +module Function(isEven) where + +isEven :: Integer -> Bool +isEven n = if n `mod` 2 == 0 + then True + else False + +isOdd :: Integer -> Bool +isOdd n = if n `mod` 2 == 0 + then False + else True diff --git a/plugins/hls-code-range-plugin/test/testdata/folding-range/hie.yaml b/plugins/hls-code-range-plugin/test/testdata/folding-range/hie.yaml new file mode 100644 index 0000000000..22a5941a9b --- /dev/null +++ b/plugins/hls-code-range-plugin/test/testdata/folding-range/hie.yaml @@ -0,0 +1,4 @@ +cradle: + direct: + arguments: + - "Function" From b547d4e9acf2e4b70695483bf34f24541b1c27dc Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Wed, 21 Sep 2022 22:43:53 +0800 Subject: [PATCH 131/213] Fix broken call-hierarchy-plugin-tests for type signatures (#3188) * Fix broken tests for signatures * Remove unused Maybe * Refactir prepare * Refactor incoming and outgoing calls * Fix doc format Co-authored-by: Pepe Iborra Co-authored-by: Michael Peyton Jones --- plugins/hls-call-hierarchy-plugin/README.md | 3 + .../hls-call-hierarchy-plugin.cabal | 2 - .../src/Ide/Plugin/CallHierarchy.hs | 9 +- .../src/Ide/Plugin/CallHierarchy/Internal.hs | 378 +++++++++--------- .../src/Ide/Plugin/CallHierarchy/Types.hs | 38 +- .../hls-call-hierarchy-plugin/test/Main.hs | 4 +- 6 files changed, 207 insertions(+), 227 deletions(-) diff --git a/plugins/hls-call-hierarchy-plugin/README.md b/plugins/hls-call-hierarchy-plugin/README.md index 011c5585f6..ae2d3fdf95 100644 --- a/plugins/hls-call-hierarchy-plugin/README.md +++ b/plugins/hls-call-hierarchy-plugin/README.md @@ -23,6 +23,9 @@ Enabled by default. You can disable it in your editor settings whenever you like ``` ## Change log +### 1.1.0.0 +- Support ghc-9.4. +- Refactor code base and force four space indent. ### 1.0.3.0 Remove force update `HieDb` logic in queries. ### 1.0.1.0 diff --git a/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal b/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal index f0ecdc0ab2..771de409c6 100644 --- a/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal +++ b/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal @@ -27,10 +27,8 @@ library build-depends: , aeson , base >=4.12 && <5 - , bytestring , containers , extra - , ghc , ghcide ^>= 1.8 , hiedb , hls-plugin-api ^>= 1.5 diff --git a/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy.hs b/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy.hs index 0a0242376d..cf7e042986 100644 --- a/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy.hs +++ b/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy.hs @@ -7,7 +7,8 @@ import Language.LSP.Types descriptor :: PluginDescriptor IdeState descriptor = (defaultPluginDescriptor X.callHierarchyId) - { Ide.Types.pluginHandlers = mkPluginHandler STextDocumentPrepareCallHierarchy X.prepareCallHierarchy - <> mkPluginHandler SCallHierarchyIncomingCalls X.incomingCalls - <> mkPluginHandler SCallHierarchyOutgoingCalls X.outgoingCalls - } + { Ide.Types.pluginHandlers = + mkPluginHandler STextDocumentPrepareCallHierarchy X.prepareCallHierarchy + <> mkPluginHandler SCallHierarchyIncomingCalls X.incomingCalls + <> mkPluginHandler SCallHierarchyOutgoingCalls X.outgoingCalls + } diff --git a/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Internal.hs b/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Internal.hs index 8219862cc7..db148733ec 100644 --- a/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Internal.hs +++ b/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Internal.hs @@ -15,24 +15,18 @@ module Ide.Plugin.CallHierarchy.Internal ( ) where import Control.Lens ((^.)) -import Control.Monad.Extra import Control.Monad.IO.Class import Data.Aeson as A -import qualified Data.ByteString as BS -import qualified Data.HashMap.Strict as HM import Data.List (groupBy, sortBy) import qualified Data.Map as M import Data.Maybe import qualified Data.Set as S import qualified Data.Text as T -import qualified Data.Text.Encoding as T import Data.Tuple.Extra import Development.IDE -import Development.IDE.Core.Compile import Development.IDE.Core.Shake import Development.IDE.GHC.Compat as Compat import Development.IDE.Spans.AtPoint -import GHC.Conc.Sync import HieDb (Symbol (Symbol)) import qualified Ide.Plugin.CallHierarchy.Query as Q import Ide.Plugin.CallHierarchy.Types @@ -51,37 +45,29 @@ callHierarchyId = PluginId "callHierarchy" prepareCallHierarchy :: PluginMethodHandler IdeState TextDocumentPrepareCallHierarchy prepareCallHierarchy state _ param = pluginResponse $ do nfp <- getNormalizedFilePath (param ^. L.textDocument ^. L.uri) - items <- liftIO (runAction "CallHierarchy.prepareHierarchy" state (prepareCallHierarchyItem nfp (param ^. L.position))) - pure (List <$> items) - -prepareCallHierarchyItem :: NormalizedFilePath -> Position -> Action (Maybe [CallHierarchyItem]) -prepareCallHierarchyItem = constructFromAst - -constructFromAst :: NormalizedFilePath -> Position -> Action (Maybe [CallHierarchyItem]) -constructFromAst nfp pos = - use GetHieAst nfp >>= - \case - Nothing -> pure Nothing - Just (HAR _ hf _ _ _) -> do - resolveIntoCallHierarchy hf pos nfp - -resolveIntoCallHierarchy :: Applicative f => HieASTs a -> Position -> NormalizedFilePath -> f (Maybe [CallHierarchyItem]) -resolveIntoCallHierarchy hf pos nfp = - case listToMaybe $ pointCommand hf pos extract of - Nothing -> pure Nothing - Just infos -> - case mapMaybe (construct nfp hf) infos of - [] -> pure Nothing - res -> pure $ Just res - -extract :: HieAST a -> [(Identifier, S.Set ContextInfo, Span)] + items <- liftIO + $ runAction "CallHierarchy.prepareHierarchy" state + $ prepareCallHierarchyItem nfp (param ^. L.position) + pure $ List <$> pure items + +prepareCallHierarchyItem :: NormalizedFilePath -> Position -> Action [CallHierarchyItem] +prepareCallHierarchyItem nfp pos = use GetHieAst nfp >>= \case + Nothing -> pure mempty + Just (HAR _ hf _ _ _) -> pure $ prepareByAst hf pos nfp + +prepareByAst :: HieASTs a -> Position -> NormalizedFilePath -> [CallHierarchyItem] +prepareByAst hf pos nfp = + case listToMaybe $ pointCommand hf pos extract of + Nothing -> mempty + Just infos -> mapMaybe (construct nfp hf) infos + +extract :: HieAST a -> [(Identifier, [ContextInfo], Span)] extract ast = let span = nodeSpan ast - infos = M.toList $ M.map identInfo (Compat.getNodeIds ast) - in [ (ident, contexts, span) | (ident, contexts) <- infos ] + infos = M.toList $ M.map (S.toList . identInfo) (Compat.getNodeIds ast) + in [(ident, contexts, span) | (ident, contexts) <- infos] recFieldInfo, declInfo, valBindInfo, classTyDeclInfo, - useInfo, patternBindInfo, tyDeclInfo, matchBindInfo - :: [ContextInfo] -> Maybe ContextInfo + useInfo, patternBindInfo, tyDeclInfo, matchBindInfo :: [ContextInfo] -> Maybe ContextInfo recFieldInfo ctxs = listToMaybe [ctx | ctx@RecField{} <- ctxs] declInfo ctxs = listToMaybe [ctx | ctx@Decl{} <- ctxs] valBindInfo ctxs = listToMaybe [ctx | ctx@ValBind{} <- ctxs] @@ -91,98 +77,93 @@ patternBindInfo ctxs = listToMaybe [ctx | ctx@PatternBind{} <- ctxs] tyDeclInfo ctxs = listToMaybe [TyDecl | TyDecl <- ctxs] matchBindInfo ctxs = listToMaybe [MatchBind | MatchBind <- ctxs] -construct :: NormalizedFilePath -> HieASTs a -> (Identifier, S.Set ContextInfo, Span) -> Maybe CallHierarchyItem +construct :: NormalizedFilePath -> HieASTs a -> (Identifier, [ContextInfo], Span) -> Maybe CallHierarchyItem construct nfp hf (ident, contexts, ssp) - | isInternalIdentifier ident = Nothing + | isInternalIdentifier ident = Nothing - | Just (RecField RecFieldDecl _) <- recFieldInfo ctxList - -- ignored type span - = Just $ mkCallHierarchyItem' ident SkField ssp ssp + | Just (RecField RecFieldDecl _) <- recFieldInfo contexts + -- ignored type span + = Just $ mkCallHierarchyItem' ident SkField ssp ssp - | isJust (matchBindInfo ctxList) && isNothing (valBindInfo ctxList) - = Just $ mkCallHierarchyItem' ident SkFunction ssp ssp + | isJust (matchBindInfo contexts) && isNothing (valBindInfo contexts) + = Just $ mkCallHierarchyItem' ident SkFunction ssp ssp - | Just ctx <- valBindInfo ctxList - = Just $ case ctx of - ValBind _ _ span -> mkCallHierarchyItem' ident SkFunction (renderSpan span) ssp - _ -> mkCallHierarchyItem' ident skUnknown ssp ssp + | Just ctx <- valBindInfo contexts + = Just $ case ctx of + ValBind _ _ span -> mkCallHierarchyItem' ident SkFunction (renderSpan span) ssp + _ -> mkCallHierarchyItem' ident skUnknown ssp ssp - | Just ctx <- declInfo ctxList - = Just $ case ctx of - Decl ClassDec span -> mkCallHierarchyItem' ident SkInterface (renderSpan span) ssp - Decl ConDec span -> mkCallHierarchyItem' ident SkConstructor (renderSpan span) ssp - Decl DataDec span -> mkCallHierarchyItem' ident SkStruct (renderSpan span) ssp - Decl FamDec span -> mkCallHierarchyItem' ident SkFunction (renderSpan span) ssp - Decl InstDec span -> mkCallHierarchyItem' ident SkInterface (renderSpan span) ssp - Decl SynDec span -> mkCallHierarchyItem' ident SkTypeParameter (renderSpan span) ssp - _ -> mkCallHierarchyItem' ident skUnknown ssp ssp + | Just ctx <- declInfo contexts + = Just $ case ctx of + Decl ClassDec span -> mkCallHierarchyItem' ident SkInterface (renderSpan span) ssp + Decl ConDec span -> mkCallHierarchyItem' ident SkConstructor (renderSpan span) ssp + Decl DataDec span -> mkCallHierarchyItem' ident SkStruct (renderSpan span) ssp + Decl FamDec span -> mkCallHierarchyItem' ident SkFunction (renderSpan span) ssp + Decl InstDec span -> mkCallHierarchyItem' ident SkInterface (renderSpan span) ssp + Decl SynDec span -> mkCallHierarchyItem' ident SkTypeParameter (renderSpan span) ssp + _ -> mkCallHierarchyItem' ident skUnknown ssp ssp - | Just (ClassTyDecl span) <- classTyDeclInfo ctxList - = Just $ mkCallHierarchyItem' ident SkMethod (renderSpan span) ssp + | Just (ClassTyDecl span) <- classTyDeclInfo contexts + = Just $ mkCallHierarchyItem' ident SkMethod (renderSpan span) ssp - | Just (PatternBind _ _ span) <- patternBindInfo ctxList - = Just $ mkCallHierarchyItem' ident SkFunction (renderSpan span) ssp + | Just (PatternBind _ _ span) <- patternBindInfo contexts + = Just $ mkCallHierarchyItem' ident SkFunction (renderSpan span) ssp - | Just Use <- useInfo ctxList - = Just $ mkCallHierarchyItem' ident SkInterface ssp ssp + | Just _ <- useInfo contexts = Just $ mkCallHierarchyItem' ident SkInterface ssp ssp - | Just _ <- tyDeclInfo ctxList - = renderTyDecl + | Just _ <- tyDeclInfo contexts = renderTyDecl - | otherwise = Nothing - where - renderSpan = \case Just span -> span - _ -> ssp + | otherwise = Nothing + where + renderSpan (Just span) = span + renderSpan _ = ssp - skUnknown = SkUnknown 27 + -- https://github.com/haskell/lsp/blob/e11b7c09658610f6d815d04db08a64e7cf6b4467/lsp-types/src/Language/LSP/Types/DocumentSymbol.hs#L97 + skUnknown = SkUnknown 27 -- 27 is the first unused number while ToJSON - mkCallHierarchyItem' = mkCallHierarchyItem nfp + mkCallHierarchyItem' = mkCallHierarchyItem nfp - isInternalIdentifier = \case - Left _ -> False - Right name -> isInternalName name + isInternalIdentifier = \case + Left _ -> False + Right name -> isInternalName name - ctxList = S.toList contexts - - renderTyDecl = case ident of - Left _ -> Nothing - Right name -> case getNameBindingInClass name ssp (getAsts hf) of - Nothing -> Nothing - Just sp -> case resolveIntoCallHierarchy hf (realSrcSpanToRange sp ^. L.start) nfp of - Just (Just items) -> listToMaybe items - _ -> Nothing + renderTyDecl = case ident of + Left _ -> Nothing + Right name -> case getNameBinding name (getAsts hf) of + Nothing -> Nothing + Just sp -> listToMaybe $ prepareByAst hf (realSrcSpanToRange sp ^. L.start) nfp mkCallHierarchyItem :: NormalizedFilePath -> Identifier -> SymbolKind -> Span -> Span -> CallHierarchyItem mkCallHierarchyItem nfp ident kind span selSpan = - CallHierarchyItem - (T.pack $ optimize $ identifierName ident) - kind - Nothing - (Just $ T.pack $ identifierToDetail ident) - (fromNormalizedUri $ normalizedFilePathToUri nfp) - (realSrcSpanToRange span) - (realSrcSpanToRange selSpan) - (toJSON . show <$> mkSymbol ident) - where - identifierToDetail :: Identifier -> String - identifierToDetail = \case - Left modName -> moduleNameString modName - Right name -> (moduleNameString . moduleName . nameModule) name - - identifierName :: Identifier -> String - identifierName = \case - Left modName -> moduleNameString modName - Right name -> occNameString $ nameOccName name - - optimize :: String -> String - optimize name -- optimize display for DuplicateRecordFields - | "$sel:" == take 5 name = drop 5 name - | otherwise = name + CallHierarchyItem + (T.pack $ optimizeDisplay $ identifierName ident) + kind + Nothing + (Just $ T.pack $ identifierToDetail ident) + (fromNormalizedUri $ normalizedFilePathToUri nfp) + (realSrcSpanToRange span) + (realSrcSpanToRange selSpan) + (toJSON . show <$> mkSymbol ident) + where + identifierToDetail :: Identifier -> String + identifierToDetail = \case + Left modName -> moduleNameString modName + Right name -> (moduleNameString . moduleName . nameModule) name + + identifierName :: Identifier -> String + identifierName = \case + Left modName -> moduleNameString modName + Right name -> occNameString $ nameOccName name + + optimizeDisplay :: String -> String + optimizeDisplay name -- Optimize display for DuplicateRecordFields + | "$sel:" == take 5 name = drop 5 name + | otherwise = name mkSymbol :: Identifier -> Maybe Symbol mkSymbol = \case - Left _ -> Nothing - Right name -> Just $ Symbol (occName name) (nameModule name) + Left _ -> Nothing + Right name -> Just $ Symbol (occName name) (nameModule name) ---------------------------------------------------------------------- -------------- Incoming calls and outgoing calls --------------------- @@ -198,106 +179,103 @@ deriving instance Ord Value -- | Render incoming calls request. incomingCalls :: PluginMethodHandler IdeState CallHierarchyIncomingCalls incomingCalls state pluginId param = pluginResponse $ do - calls <- liftIO $ runAction "CallHierarchy.incomingCalls" state $ - queryCalls (param ^. L.item) Q.incomingCalls mkCallHierarchyIncomingCall - mergeIncomingCalls - case calls of - Just x -> pure $ Just $ List x - Nothing -> throwPluginError "incomingCalls - Internal Error" - where - mkCallHierarchyIncomingCall :: Vertex -> Action (Maybe CallHierarchyIncomingCall) - mkCallHierarchyIncomingCall = mkCallHierarchyCall CallHierarchyIncomingCall - - mergeIncomingCalls :: [CallHierarchyIncomingCall] -> [CallHierarchyIncomingCall] - mergeIncomingCalls = map merge - . groupBy (\a b -> a ^. L.from == b ^. L.from) - . sortBy (\a b -> (a ^. L.from) `compare` (b ^. L.from)) - where - merge calls = let ranges = concatMap ((\(List x) -> x) . (^. L.fromRanges)) calls - in CallHierarchyIncomingCall (head calls ^. L.from) (List ranges) - --- Render outgoing calls request. + calls <- liftIO + $ runAction "CallHierarchy.incomingCalls" state + $ queryCalls + (param ^. L.item) + Q.incomingCalls + mkCallHierarchyIncomingCall + (mergeCalls CallHierarchyIncomingCall L.from) + pure $ Just $ List calls + where + mkCallHierarchyIncomingCall :: Vertex -> Action (Maybe CallHierarchyIncomingCall) + mkCallHierarchyIncomingCall = mkCallHierarchyCall CallHierarchyIncomingCall + +-- | Render outgoing calls request. outgoingCalls :: PluginMethodHandler IdeState CallHierarchyOutgoingCalls outgoingCalls state pluginId param = pluginResponse $ do - calls <- liftIO $ runAction "CallHierarchy.outgoingCalls" state $ - queryCalls (param ^. L.item) Q.outgoingCalls mkCallHierarchyOutgoingCall - mergeOutgoingCalls - case calls of - Just x -> pure $ Just $ List x - Nothing -> throwPluginError "outgoingCalls - Internal Error" - where - mkCallHierarchyOutgoingCall :: Vertex -> Action (Maybe CallHierarchyOutgoingCall) - mkCallHierarchyOutgoingCall = mkCallHierarchyCall CallHierarchyOutgoingCall - - mergeOutgoingCalls :: [CallHierarchyOutgoingCall] -> [CallHierarchyOutgoingCall] - mergeOutgoingCalls = map merge - . groupBy (\a b -> a ^. L.to == b ^. L.to) - . sortBy (\a b -> (a ^. L.to) `compare` (b ^. L.to)) - where - merge calls = let ranges = concatMap ((\(List x) -> x) . (^. L.fromRanges)) calls - in CallHierarchyOutgoingCall (head calls ^. L.to) (List ranges) + calls <- liftIO + $ runAction "CallHierarchy.outgoingCalls" state + $ queryCalls + (param ^. L.item) + Q.outgoingCalls + mkCallHierarchyOutgoingCall + (mergeCalls CallHierarchyOutgoingCall L.to) + pure $ Just $ List calls + where + mkCallHierarchyOutgoingCall :: Vertex -> Action (Maybe CallHierarchyOutgoingCall) + mkCallHierarchyOutgoingCall = mkCallHierarchyCall CallHierarchyOutgoingCall + +-- | Merge calls from the same place +mergeCalls constructor target = + concatMap merge + . groupBy (\a b -> a ^. target == b ^. target) + . sortBy (\a b -> (a ^. target) `compare` (b ^. target)) + where + merge [] = [] + merge calls@(call:_) = + let ranges = concatMap ((\(List x) -> x) . (^. L.fromRanges)) calls + in [constructor (call ^. target) (List ranges)] mkCallHierarchyCall :: (CallHierarchyItem -> List Range -> a) -> Vertex -> Action (Maybe a) mkCallHierarchyCall mk v@Vertex{..} = do - let pos = Position (fromIntegral $ sl - 1) (fromIntegral $ sc - 1) - nfp = toNormalizedFilePath' hieSrc - range = mkRange (fromIntegral $ casl - 1) (fromIntegral $ casc - 1) (fromIntegral $ cael - 1) (fromIntegral $ caec - 1) - - prepareCallHierarchyItem nfp pos >>= - \case - Just [item] -> pure $ Just $ mk item (List [range]) - _ -> do - ShakeExtras{withHieDb} <- getShakeExtras - liftIO (withHieDb (`Q.getSymbolPosition` v)) >>= - \case - (x:_) -> - prepareCallHierarchyItem nfp (Position (fromIntegral $ psl x - 1) (fromIntegral $ psc x - 1)) >>= - \case - Just [item] -> pure $ Just $ mk item (List [range]) - _ -> pure Nothing - _ -> pure Nothing + let pos = Position (fromIntegral $ sl - 1) (fromIntegral $ sc - 1) + nfp = toNormalizedFilePath' hieSrc + range = mkRange + (fromIntegral $ casl - 1) + (fromIntegral $ casc - 1) + (fromIntegral $ cael - 1) + (fromIntegral $ caec - 1) + + prepareCallHierarchyItem nfp pos >>= + \case + [item] -> pure $ Just $ mk item (List [range]) + _ -> do + ShakeExtras{withHieDb} <- getShakeExtras + sps <- liftIO (withHieDb (`Q.getSymbolPosition` v)) + case sps of + (x:_) -> do + items <- prepareCallHierarchyItem + nfp + (Position (fromIntegral $ psl x - 1) (fromIntegral $ psc x - 1)) + case items of + [item] -> pure $ Just $ mk item (List [range]) + _ -> pure Nothing + _ -> pure Nothing -- | Unified queries include incoming calls and outgoing calls. queryCalls :: (Show a) - => CallHierarchyItem - -> (HieDb -> Symbol -> IO [Vertex]) - -> (Vertex -> Action (Maybe a)) - -> ([a] -> [a]) - -> Action (Maybe [a]) + => CallHierarchyItem + -> (HieDb -> Symbol -> IO [Vertex]) + -> (Vertex -> Action (Maybe a)) + -> ([a] -> [a]) + -> Action [a] queryCalls item queryFunc makeFunc merge - | Just nfp <- uriToNormalizedFilePath $ toNormalizedUri uri = do - ShakeExtras{withHieDb} <- getShakeExtras - maySymbol <- getSymbol nfp - case maySymbol of - Nothing -> error "CallHierarchy.Impossible" - Just symbol -> do - vs <- liftIO $ withHieDb (`queryFunc` symbol) - items <- Just . catMaybes <$> mapM makeFunc vs - pure $ merge <$> items - | otherwise = pure Nothing - where - uri = item ^. L.uri - xdata = item ^. L.xdata - pos = item ^. (L.selectionRange . L.start) - - getSymbol nfp = - case item ^. L.xdata of - Just xdata -> case fromJSON xdata of - A.Success (symbolStr :: String) -> - case readMaybe symbolStr of - Just symbol -> pure $ Just symbol - Nothing -> getSymbolFromAst nfp pos - A.Error _ -> getSymbolFromAst nfp pos - Nothing -> getSymbolFromAst nfp pos - - getSymbolFromAst :: NormalizedFilePath -> Position -> Action (Maybe Symbol) - getSymbolFromAst nfp pos = - use GetHieAst nfp >>= - \case - Nothing -> pure Nothing - Just (HAR _ hf _ _ _) -> do - case listToMaybe $ pointCommand hf pos extract of - Just infos -> case mkSymbol . fst3 <$> listToMaybe infos of - Nothing -> pure Nothing - Just res -> pure res - Nothing -> pure Nothing + | Just nfp <- uriToNormalizedFilePath $ toNormalizedUri uri = do + ShakeExtras{withHieDb} <- getShakeExtras + maySymbol <- getSymbol nfp + case maySymbol of + Nothing -> pure mempty + Just symbol -> do + vs <- liftIO $ withHieDb (`queryFunc` symbol) + items <- catMaybes <$> mapM makeFunc vs + pure $ merge items + | otherwise = pure mempty + where + uri = item ^. L.uri + xdata = item ^. L.xdata + pos = item ^. (L.selectionRange . L.start) + + getSymbol nfp = case item ^. L.xdata of + Just xdata -> case fromJSON xdata of + A.Success (symbolStr :: String) -> maybe (getSymbolFromAst nfp pos) (pure . pure) $ readMaybe symbolStr + A.Error _ -> getSymbolFromAst nfp pos + Nothing -> getSymbolFromAst nfp pos -- Fallback if xdata lost, some editor(VSCode) will drop it + + getSymbolFromAst :: NormalizedFilePath -> Position -> Action (Maybe Symbol) + getSymbolFromAst nfp pos = use GetHieAst nfp >>= \case + Nothing -> pure Nothing + Just (HAR _ hf _ _ _) -> do + case listToMaybe $ pointCommand hf pos extract of + Just infos -> maybe (pure Nothing) pure $ mkSymbol . fst3 <$> listToMaybe infos + Nothing -> pure Nothing diff --git a/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Types.hs b/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Types.hs index c279cebbe3..d71b60e292 100644 --- a/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Types.hs +++ b/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Types.hs @@ -13,28 +13,28 @@ data Vertex = Vertex { mod :: String , occ :: String , hieSrc :: FilePath -, sl :: Int -, sc :: Int -, el :: Int -, ec :: Int -, casl :: Int -- sl for call appear -, casc :: Int -- sc for call appear -, cael :: Int -- el for call appear -, caec :: Int -- ec for call appear +, sl :: Int -- ^ start line +, sc :: Int -- ^ start character +, el :: Int -- ^ end line +, ec :: Int -- ^ end character +, casl :: Int -- ^ sl for call appear +, casc :: Int -- ^ sc for call appear +, cael :: Int -- ^ el for call appear +, caec :: Int -- ^ ec for call appear } deriving (Eq, Show, Generic, FromJSON, ToJSON) instance ToRow Vertex where - toRow (Vertex a b c d e f g h i j k) = - [ toField a, toField b, toField c, toField d - , toField e, toField f, toField g, toField h - , toField i, toField j, toField k - ] + toRow (Vertex a b c d e f g h i j k) = + [ toField a, toField b, toField c, toField d + , toField e, toField f, toField g, toField h + , toField i, toField j, toField k + ] instance FromRow Vertex where - fromRow = Vertex <$> field <*> field <*> field - <*> field <*> field <*> field - <*> field <*> field <*> field - <*> field <*> field + fromRow = Vertex <$> field <*> field <*> field + <*> field <*> field <*> field + <*> field <*> field <*> field + <*> field <*> field data SymbolPosition = SymbolPosition { psl :: Int @@ -42,7 +42,7 @@ data SymbolPosition = SymbolPosition { } deriving (Eq, Show, Generic, FromJSON, ToJSON) instance ToRow SymbolPosition where - toRow (SymbolPosition a b) = toRow (a, b) + toRow (SymbolPosition a b) = toRow (a, b) instance FromRow SymbolPosition where - fromRow = SymbolPosition <$> field <*> field + fromRow = SymbolPosition <$> field <*> field diff --git a/plugins/hls-call-hierarchy-plugin/test/Main.hs b/plugins/hls-call-hierarchy-plugin/test/Main.hs index ca9550a9f3..bbd8c44b93 100644 --- a/plugins/hls-call-hierarchy-plugin/test/Main.hs +++ b/plugins/hls-call-hierarchy-plugin/test/Main.hs @@ -166,13 +166,13 @@ prepareCallHierarchyTests = expected = mkCallHierarchyItemC "A" SkConstructor range selRange oneCaseWithCreate contents 1 13 expected , testGroup "type signature" - [ knownBrokenForGhcVersions [GHC94] "type signature broken" $ testCase "next line" $ do + [ testCase "next line" $ do let contents = T.unlines ["a::Int", "a=3"] range = mkRange 1 0 1 3 selRange = mkRange 1 0 1 1 expected = mkCallHierarchyItemV "a" SkFunction range selRange oneCaseWithCreate contents 0 0 expected - , knownBrokenForGhcVersions [GHC94] "type signature broken" $ testCase "multi functions" $ do + , testCase "multi functions" $ do let contents = T.unlines [ "a,b::Int", "a=3", "b=4"] range = mkRange 2 0 2 3 selRange = mkRange 2 0 2 1 From dca5cc36c7b7579dfc9e74ffcf379d49f5a20324 Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Wed, 21 Sep 2022 19:36:19 +0200 Subject: [PATCH 132/213] Log plugin name and attribute errors to plugins (#3194) * Log plugin name * redundant import * Attribute response error logs to plugins * remove redundant plugin names from error messages * improve pretty printing * Avoid show * simplify test messages * Fix --- ghcide/src/Development/IDE/Main.hs | 1 - ghcide/src/Development/IDE/Plugin/HLS.hs | 55 +++++++++++++------ haskell-language-server.cabal | 1 + .../src/Ide/Plugin/AlternateNumberFormat.hs | 30 +++++----- .../test/Main.hs | 2 +- .../src/Ide/Plugin/ChangeTypeSignature.hs | 2 +- .../src/Ide/Plugin/Class/CodeAction.hs | 2 +- .../src/Ide/Plugin/ExplicitFixity.hs | 9 +-- .../hls-explicit-fixity-plugin/test/Main.hs | 2 +- .../src/Ide/Plugin/ModuleName.hs | 4 +- src/HlsPlugins.hs | 51 ++++++++--------- test/functional/FunctionalCodeAction.hs | 2 + 12 files changed, 88 insertions(+), 73 deletions(-) diff --git a/ghcide/src/Development/IDE/Main.hs b/ghcide/src/Development/IDE/Main.hs index d746c2539d..e6c9d9373e 100644 --- a/ghcide/src/Development/IDE/Main.hs +++ b/ghcide/src/Development/IDE/Main.hs @@ -143,7 +143,6 @@ import System.IO (BufferMode (LineBuffe import System.Random (newStdGen) import System.Time.Extra (Seconds, offsetTime, showDuration) -import Text.Printf (printf) data Log = LogHeapStats !HeapStats.Log diff --git a/ghcide/src/Development/IDE/Plugin/HLS.hs b/ghcide/src/Development/IDE/Plugin/HLS.hs index 8749a9efda..dd8c0d60b0 100644 --- a/ghcide/src/Development/IDE/Plugin/HLS.hs +++ b/ghcide/src/Development/IDE/Plugin/HLS.hs @@ -13,13 +13,16 @@ import Control.Exception (SomeException) import Control.Lens ((^.)) import Control.Monad import qualified Data.Aeson as J +import Data.Bifunctor (first) import Data.Dependent.Map (DMap) import qualified Data.Dependent.Map as DMap import Data.Dependent.Sum import Data.Either import qualified Data.List as List import Data.List.NonEmpty (NonEmpty, nonEmpty, toList) +import qualified Data.List.NonEmpty as NE import qualified Data.Map as Map +import Data.Some import Data.String import Data.Text (Text) import qualified Data.Text as T @@ -38,6 +41,7 @@ import Language.LSP.Types import qualified Language.LSP.Types as J import qualified Language.LSP.Types.Lens as LSP import Language.LSP.VFS +import Prettyprinter.Render.String (renderString) import Text.Regex.TDFA.Text () import UnliftIO (MonadUnliftIO) import UnliftIO.Async (forConcurrently) @@ -46,12 +50,18 @@ import UnliftIO.Exception (catchAny) -- --------------------------------------------------------------------- -- -data Log = LogPluginError ResponseError - deriving Show - +data Log + = LogPluginError PluginId ResponseError + | LogNoPluginForMethod (Some SMethod) + | LogInvalidCommandIdentifier instance Pretty Log where pretty = \case - LogPluginError err -> prettyResponseError err + LogPluginError (PluginId pId) err -> pretty pId <> ":" <+> prettyResponseError err + LogNoPluginForMethod (Some method) -> + "No plugin enabled for " <> pretty (show method) + LogInvalidCommandIdentifier-> "Invalid command identifier" + +instance Show Log where show = renderString . layoutCompact . pretty -- various error message specific builders prettyResponseError :: ResponseError -> Doc a @@ -77,10 +87,10 @@ failedToParseArgs :: CommandId -- ^ command that failed to parse failedToParseArgs (CommandId com) (PluginId pid) err arg = "Error while parsing args for " <> com <> " in plugin " <> pid <> ": " <> T.pack err <> "\narg = " <> T.pack (show arg) -- | Build a ResponseError and log it before returning to the caller -logAndReturnError :: Recorder (WithPriority Log) -> ErrorCode -> Text -> LSP.LspT Config IO (Either ResponseError a) -logAndReturnError recorder errCode msg = do +logAndReturnError :: Recorder (WithPriority Log) -> PluginId -> ErrorCode -> Text -> LSP.LspT Config IO (Either ResponseError a) +logAndReturnError recorder p errCode msg = do let err = ResponseError errCode msg Nothing - logWith recorder Warning $ LogPluginError err + logWith recorder Warning $ LogPluginError p err pure $ Left err -- | Map a set of plugins to the underlying ghcide engine. @@ -164,15 +174,17 @@ executeCommandHandlers recorder ecs = requestHandler SWorkspaceExecuteCommand ex Just (plugin, cmd) -> runPluginCommand ide plugin cmd cmdParams -- Couldn't parse the command identifier - _ -> logAndReturnError recorder InvalidParams "Invalid command Identifier" + _ -> do + logWith recorder Warning LogInvalidCommandIdentifier + return $ Left $ ResponseError InvalidParams "Invalid command identifier" Nothing runPluginCommand ide p com arg = case Map.lookup p pluginMap of - Nothing -> logAndReturnError recorder InvalidRequest (pluginDoesntExist p) + Nothing -> logAndReturnError recorder p InvalidRequest (pluginDoesntExist p) Just xs -> case List.find ((com ==) . commandId) xs of - Nothing -> logAndReturnError recorder InvalidRequest (commandDoesntExist com p xs) + Nothing -> logAndReturnError recorder p InvalidRequest (commandDoesntExist com p xs) Just (PluginCommand _ _ f) -> case J.fromJSON arg of - J.Error err -> logAndReturnError recorder InvalidParams (failedToParseArgs com p err arg) + J.Error err -> logAndReturnError recorder p InvalidParams (failedToParseArgs com p err arg) J.Success a -> f ide a -- --------------------------------------------------------------------- @@ -195,15 +207,21 @@ extensiblePlugins recorder xs = mempty { P.pluginHandlers = handlers } let fs = filter (\(_, desc, _) -> pluginEnabled m params desc config) fs' -- Clients generally don't display ResponseErrors so instead we log any that we come across case nonEmpty fs of - Nothing -> logAndReturnError recorder InvalidRequest (pluginNotEnabled m fs') + Nothing -> do + logWith recorder Warning (LogNoPluginForMethod $ Some m) + let err = ResponseError InvalidRequest msg Nothing + msg = pluginNotEnabled m fs' + return $ Left err Just fs -> do let msg e pid = "Exception in plugin " <> T.pack (show pid) <> " while processing " <> T.pack (show m) <> ": " <> T.pack (show e) handlers = fmap (\(plid,_,handler) -> (plid,handler)) fs es <- runConcurrently msg (show m) handlers ide params - let (errs,succs) = partitionEithers $ toList es - unless (null errs) $ forM_ errs $ \err -> logWith recorder Warning $ LogPluginError err + + let (errs,succs) = partitionEithers $ toList $ join $ NE.zipWith (\(pId,_) -> fmap (first (pId,))) handlers es + unless (null errs) $ forM_ errs $ \(pId, err) -> + logWith recorder Warning $ LogPluginError pId err case nonEmpty succs of - Nothing -> pure $ Left $ combineErrors errs + Nothing -> pure $ Left $ combineErrors $ map snd errs Just xs -> do caps <- LSP.getClientCapabilities pure $ Right $ combineResponses m config caps params xs @@ -226,7 +244,8 @@ extensibleNotificationPlugins recorder xs = mempty { P.pluginHandlers = handlers -- Only run plugins that are allowed to run on this request let fs = filter (\(_, desc, _) -> pluginEnabled m params desc config) fs' case nonEmpty fs of - Nothing -> void $ logAndReturnError recorder InvalidRequest (pluginNotEnabled m fs') + Nothing -> do + logWith recorder Warning (LogNoPluginForMethod $ Some m) Just fs -> do -- We run the notifications in order, so the core ghcide provider -- (which restarts the shake process) hopefully comes last @@ -242,8 +261,8 @@ runConcurrently -- ^ Enabled plugin actions that we are allowed to run -> a -> b - -> m (NonEmpty (Either ResponseError d)) -runConcurrently msg method fs a b = fmap join $ forConcurrently fs $ \(pid,f) -> otTracedProvider pid (fromString method) $ do + -> m (NonEmpty(NonEmpty (Either ResponseError d))) +runConcurrently msg method fs a b = forConcurrently fs $ \(pid,f) -> otTracedProvider pid (fromString method) $ do f a b `catchAny` (\e -> pure $ pure $ Left $ ResponseError InternalError (msg e pid) Nothing) diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 917770c777..bf38eefa48 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -523,6 +523,7 @@ test-suite func-test , lens , lens-aeson , ghcide + , ghcide-test-utils , hls-test-utils ^>=1.4 , lsp-types , aeson diff --git a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs index c7ae1bae19..e73afdbd65 100644 --- a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs +++ b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs @@ -8,8 +8,7 @@ module Ide.Plugin.AlternateNumberFormat (descriptor, Log(..)) where import Control.Lens ((^.)) import Control.Monad.Except (ExceptT, MonadIO, liftIO) import qualified Data.HashMap.Strict as HashMap -import Data.String (IsString) -import Data.Text (Text) +import Data.Text (Text, unpack) import qualified Data.Text as T import Development.IDE (GetParsedModule (GetParsedModule), GhcSession (GhcSession), @@ -43,11 +42,8 @@ instance Pretty Log where pretty = \case LogShake log -> pretty log -alternateNumberFormatId :: IsString a => a -alternateNumberFormatId = "alternateNumberFormat" - -descriptor :: Recorder (WithPriority Log) -> PluginDescriptor IdeState -descriptor recorder = (defaultPluginDescriptor alternateNumberFormatId) +descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState +descriptor recorder pId = (defaultPluginDescriptor pId) { pluginHandlers = mkPluginHandler STextDocumentCodeAction codeActionHandler , pluginRules = collectLiteralsRule recorder } @@ -87,10 +83,10 @@ collectLiteralsRule recorder = define (cmapWithPrio LogShake recorder) $ \Collec getExtensions = map GhcExtension . toList . extensionFlags . ms_hspp_opts . pm_mod_summary codeActionHandler :: PluginMethodHandler IdeState 'TextDocumentCodeAction -codeActionHandler state _ (CodeActionParams _ _ docId currRange _) = pluginResponse $ do +codeActionHandler state pId (CodeActionParams _ _ docId currRange _) = pluginResponse $ do nfp <- getNormalizedFilePath (docId ^. L.uri) - CLR{..} <- requestLiterals state nfp - pragma <- getFirstPragma state nfp + CLR{..} <- requestLiterals pId state nfp + pragma <- getFirstPragma pId state nfp -- remove any invalid literals (see validTarget comment) let litsInRange = filter inCurrentRange literals -- generate alternateFormats and zip with the literal that generated the alternates @@ -145,16 +141,16 @@ contains Range {_start, _end} x = isInsideRealSrcSpan _start x || isInsideRealSr isInsideRealSrcSpan :: Position -> RealSrcSpan -> Bool p `isInsideRealSrcSpan` r = let (Range sp ep) = realSrcSpanToRange r in sp <= p && p <= ep -getFirstPragma :: MonadIO m => IdeState -> NormalizedFilePath -> ExceptT String m NextPragmaInfo -getFirstPragma state nfp = handleMaybeM "Error: Could not get NextPragmaInfo" $ do - ghcSession <- liftIO $ runAction (alternateNumberFormatId <> ".GhcSession") state $ useWithStale GhcSession nfp - (_, fileContents) <- liftIO $ runAction (alternateNumberFormatId <> ".GetFileContents") state $ getFileContents nfp +getFirstPragma :: MonadIO m => PluginId -> IdeState -> NormalizedFilePath -> ExceptT String m NextPragmaInfo +getFirstPragma (PluginId pId) state nfp = handleMaybeM "Could not get NextPragmaInfo" $ do + ghcSession <- liftIO $ runAction (unpack pId <> ".GhcSession") state $ useWithStale GhcSession nfp + (_, fileContents) <- liftIO $ runAction (unpack pId <> ".GetFileContents") state $ getFileContents nfp case ghcSession of Just (hscEnv -> hsc_dflags -> sessionDynFlags, _) -> pure $ Just $ getNextPragmaInfo sessionDynFlags fileContents Nothing -> pure Nothing -requestLiterals :: MonadIO m => IdeState -> NormalizedFilePath -> ExceptT String m CollectLiteralsResult -requestLiterals state = handleMaybeM "Error: Could not Collect Literals" +requestLiterals :: MonadIO m => PluginId -> IdeState -> NormalizedFilePath -> ExceptT String m CollectLiteralsResult +requestLiterals (PluginId pId) state = handleMaybeM "Could not Collect Literals" . liftIO - . runAction (alternateNumberFormatId <> ".CollectLiterals") state + . runAction (unpack pId <> ".CollectLiterals") state . use CollectLiterals diff --git a/plugins/hls-alternate-number-format-plugin/test/Main.hs b/plugins/hls-alternate-number-format-plugin/test/Main.hs index f3ea16bb15..c71fffb9e8 100644 --- a/plugins/hls-alternate-number-format-plugin/test/Main.hs +++ b/plugins/hls-alternate-number-format-plugin/test/Main.hs @@ -20,7 +20,7 @@ main :: IO () main = defaultTestRunner test alternateNumberFormatPlugin :: PluginDescriptor IdeState -alternateNumberFormatPlugin = AlternateNumberFormat.descriptor mempty +alternateNumberFormatPlugin = AlternateNumberFormat.descriptor mempty "alternateNumberFormat" -- NOTE: For whatever reason, this plugin does not play nice with creating Code Actions on time. -- As a result tests will mostly pass if `import Prelude` is added at the top. We (mostly fendor) surmise this has something diff --git a/plugins/hls-change-type-signature-plugin/src/Ide/Plugin/ChangeTypeSignature.hs b/plugins/hls-change-type-signature-plugin/src/Ide/Plugin/ChangeTypeSignature.hs index f3a39fd03c..e18a2dac36 100644 --- a/plugins/hls-change-type-signature-plugin/src/Ide/Plugin/ChangeTypeSignature.hs +++ b/plugins/hls-change-type-signature-plugin/src/Ide/Plugin/ChangeTypeSignature.hs @@ -45,7 +45,7 @@ codeActionHandler ideState _ CodeActionParams {_textDocument = TextDocumentIdent pure $ List actions getDecls :: MonadIO m => IdeState -> NormalizedFilePath -> ExceptT String m [LHsDecl GhcPs] -getDecls state = handleMaybeM "Error: Could not get Parsed Module" +getDecls state = handleMaybeM "Could not get Parsed Module" . liftIO . fmap (fmap (hsmodDecls . unLoc . pm_parsed_source)) . runAction (changeTypeSignatureId <> ".GetParsedModule") state diff --git a/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeAction.hs b/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeAction.hs index 05c73beda8..23cfaf6973 100644 --- a/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeAction.hs +++ b/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeAction.hs @@ -190,7 +190,7 @@ codeAction recorder state plId (CodeActionParams _ _ docId _ context) = pluginRe . liftIO . runAction "classplugin.findClassFromIdentifier.TypeCheck" state $ useWithStale TypeCheck docPath - handleMaybeM "Error in TcEnv" + handleMaybeM "TcEnv" . liftIO . fmap snd . initTcWithGbl hscenv thisMod ghostSpan $ do diff --git a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs index 1fb1c5aa11..58899a460f 100644 --- a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs +++ b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs @@ -36,11 +36,8 @@ import Ide.PluginUtils (getNormalizedFilePath, import Ide.Types hiding (pluginId) import Language.LSP.Types -pluginId :: PluginId -pluginId = "explicitFixity" - -descriptor :: Recorder (WithPriority Log) -> PluginDescriptor IdeState -descriptor recorder = (defaultPluginDescriptor pluginId) +descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState +descriptor recorder pluginId = (defaultPluginDescriptor pluginId) { pluginRules = fixityRule recorder , pluginHandlers = mkPluginHandler STextDocumentHover hover -- Make this plugin has a lower priority than ghcide's plugin to ensure @@ -51,7 +48,7 @@ descriptor recorder = (defaultPluginDescriptor pluginId) hover :: PluginMethodHandler IdeState TextDocumentHover hover state _ (HoverParams (TextDocumentIdentifier uri) pos _) = pluginResponse $ do nfp <- getNormalizedFilePath uri - fixityTrees <- handleMaybeM "ExplicitFixity: Unable to get fixity" + fixityTrees <- handleMaybeM "Unable to get fixity" $ liftIO $ runAction "ExplicitFixity.GetFixity" state $ use GetFixity nfp diff --git a/plugins/hls-explicit-fixity-plugin/test/Main.hs b/plugins/hls-explicit-fixity-plugin/test/Main.hs index 52367e215c..82d374029f 100644 --- a/plugins/hls-explicit-fixity-plugin/test/Main.hs +++ b/plugins/hls-explicit-fixity-plugin/test/Main.hs @@ -8,7 +8,7 @@ import System.FilePath import Test.Hls plugin :: PluginDescriptor IdeState -plugin = descriptor mempty +plugin = descriptor mempty "explicit-fixity" main :: IO () main = defaultTestRunner tests diff --git a/plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs b/plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs index 05dd03ab56..e2083b2114 100644 --- a/plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs +++ b/plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs @@ -33,7 +33,7 @@ import qualified Data.Text as T import Development.IDE (GetParsedModule (GetParsedModule), GhcSession (GhcSession), IdeState, Pretty, - Priority (Debug, Info), Recorder, + Priority (Debug), Recorder, WithPriority, colon, evalGhcEnv, hscEnvWithImportPaths, logWith, realSrcSpanToRange, runAction, @@ -112,7 +112,7 @@ action recorder state uri = correctNames <- liftIO $ pathModuleNames recorder state nfp fp logWith recorder Debug (CorrectNames correctNames) bestName <- minimumBy (comparing T.length) <$> (MaybeT . pure $ NE.nonEmpty correctNames) - logWith recorder Info (BestName bestName) + logWith recorder Debug (BestName bestName) statedNameMaybe <- liftIO $ codeModuleName state nfp logWith recorder Debug (ModuleName $ snd <$> statedNameMaybe) diff --git a/src/HlsPlugins.hs b/src/HlsPlugins.hs index 2adef201dc..5095a637c0 100644 --- a/src/HlsPlugins.hs +++ b/src/HlsPlugins.hs @@ -6,7 +6,8 @@ module HlsPlugins where import Development.IDE.Types.Logger (Pretty (pretty), Recorder, WithPriority, cmapWithPrio) import Ide.PluginUtils (pluginDescToIdePlugins) -import Ide.Types (IdePlugins) +import Ide.Types (IdePlugins, + PluginId (PluginId)) -- fixed plugins import Development.IDE (IdeState) @@ -119,10 +120,10 @@ import qualified Ide.Plugin.Brittany as Brittany import qualified Development.IDE.Plugin.CodeAction as Refactor #endif -data Log = forall a. (Pretty a) => Log a +data Log = forall a. (Pretty a) => Log PluginId a instance Pretty Log where - pretty (Log a) = pretty a + pretty (Log (PluginId pId) a) = pretty pId <> ": " <> pretty a -- --------------------------------------------------------------------- @@ -134,8 +135,8 @@ instance Pretty Log where idePlugins :: Recorder (WithPriority Log) -> IdePlugins IdeState idePlugins recorder = pluginDescToIdePlugins allPlugins where - pluginRecorder :: forall log. (Pretty log) => Recorder (WithPriority log) - pluginRecorder = cmapWithPrio Log recorder + pluginRecorder :: forall log. (Pretty log) => PluginId -> Recorder (WithPriority log) + pluginRecorder pluginId = cmapWithPrio (Log pluginId) recorder allPlugins = #if hls_pragmas Pragmas.descriptor "pragmas" : @@ -144,10 +145,10 @@ idePlugins recorder = pluginDescToIdePlugins allPlugins Floskell.descriptor "floskell" : #endif #if hls_fourmolu - Fourmolu.descriptor pluginRecorder "fourmolu" : + let pId = "fourmolu" in Fourmolu.descriptor (pluginRecorder pId) pId: #endif #if hls_tactic - Tactic.descriptor pluginRecorder "tactics" : + let pId = "tactics" in Tactic.descriptor (pluginRecorder pId) pId: #endif #if hls_ormolu Ormolu.descriptor "ormolu" : @@ -156,7 +157,7 @@ idePlugins recorder = pluginDescToIdePlugins allPlugins StylishHaskell.descriptor "stylish-haskell" : #endif #if hls_rename - Rename.descriptor pluginRecorder "rename" : + let pId = "rename" in Rename.descriptor (pluginRecorder pId) pId: #endif #if hls_retrie Retrie.descriptor "retrie" : @@ -168,40 +169,40 @@ idePlugins recorder = pluginDescToIdePlugins allPlugins CallHierarchy.descriptor : #endif #if hls_class - Class.descriptor pluginRecorder "class" : + let pId = "class" in Class.descriptor (pluginRecorder pId) pId: #endif #if hls_haddockComments - HaddockComments.descriptor pluginRecorder "haddockComments" : + let pId = "haddockComments" in HaddockComments.descriptor (pluginRecorder pId) pId: #endif #if hls_eval - Eval.descriptor pluginRecorder "eval" : + let pId = "eval" in Eval.descriptor (pluginRecorder pId) pId: #endif #if hls_importLens - ExplicitImports.descriptor pluginRecorder "importLens" : + let pId = "importLens" in ExplicitImports.descriptor (pluginRecorder pId) pId: #endif #if hls_qualifyImportedNames QualifyImportedNames.descriptor "qualifyImportedNames" : #endif #if hls_refineImports - RefineImports.descriptor pluginRecorder "refineImports" : + let pId = "refineImports" in RefineImports.descriptor (pluginRecorder pId) pId: #endif #if hls_moduleName - ModuleName.descriptor pluginRecorder "moduleName" : + let pId = "moduleName" in ModuleName.descriptor (pluginRecorder pId) pId: #endif #if hls_hlint - Hlint.descriptor pluginRecorder "hlint" : + let pId = "hlint" in Hlint.descriptor (pluginRecorder pId) pId: #endif #if hls_stan - Stan.descriptor pluginRecorder "stan" : + let pId = "stan" in Stan.descriptor (pluginRecorder pId) pId : #endif #if hls_splice Splice.descriptor "splice" : #endif #if hls_alternateNumberFormat - AlternateNumberFormat.descriptor pluginRecorder : + let pId = "alternateNumberFormat" in AlternateNumberFormat.descriptor (pluginRecorder pId) pId : #endif #if hls_codeRange - CodeRange.descriptor pluginRecorder "codeRange" : + let pId = "codeRange" in CodeRange.descriptor (pluginRecorder pId) pId: #endif #if hls_changeTypeSignature ChangeTypeSignature.descriptor : @@ -210,14 +211,14 @@ idePlugins recorder = pluginDescToIdePlugins allPlugins GADT.descriptor "gadt" : #endif #if hls_refactor - Refactor.iePluginDescriptor pluginRecorder "ghcide-code-actions-imports-exports" : - Refactor.typeSigsPluginDescriptor pluginRecorder "ghcide-code-actions-type-signatures" : - Refactor.bindingsPluginDescriptor pluginRecorder "ghcide-code-actions-bindings" : - Refactor.fillHolePluginDescriptor pluginRecorder "ghcide-code-actions-fill-holes" : - Refactor.extendImportPluginDescriptor pluginRecorder "ghcide-extend-import-action" : + let pId = "ghcide-code-actions-imports-exports" in Refactor.iePluginDescriptor (pluginRecorder pId) pId : + let pId = "ghcide-code-actions-type-signatures" in Refactor.typeSigsPluginDescriptor (pluginRecorder pId) pId : + let pId = "ghcide-code-actions-bindings" in Refactor.bindingsPluginDescriptor (pluginRecorder pId) pId : + let pId = "ghcide-code-actions-fill-holes" in Refactor.fillHolePluginDescriptor (pluginRecorder pId) pId : + let pId = "ghcide-extend-import-action" in Refactor.extendImportPluginDescriptor (pluginRecorder pId) pId : #endif - GhcIde.descriptors pluginRecorder + GhcIde.descriptors (pluginRecorder "ghcide") #if explicitFixity - ++ [ExplicitFixity.descriptor pluginRecorder] + ++ [let pId = "explicit-fixity" in ExplicitFixity.descriptor (pluginRecorder pId) pId] #endif diff --git a/test/functional/FunctionalCodeAction.hs b/test/functional/FunctionalCodeAction.hs index aa7bf9253b..b28038cc5b 100644 --- a/test/functional/FunctionalCodeAction.hs +++ b/test/functional/FunctionalCodeAction.hs @@ -18,6 +18,7 @@ import qualified Language.LSP.Types.Lens as L import Test.Hls import Test.Hspec.Expectations +import Development.IDE.Test (configureCheckProject) import Test.Hls.Command {-# ANN module ("HLint: ignore Reduce duplication"::String) #-} @@ -52,6 +53,7 @@ renameTests = testGroup "rename suggestions" [ , testCase "doesn't give both documentChanges and changes" $ runSession hlsCommand noLiteralCaps "test/testdata" $ do + configureCheckProject False doc <- openDoc "CodeActionRename.hs" "haskell" _ <- waitForDiagnosticsFromSource doc "typecheck" From b289e480642684e48f1c710b04e259dd69a59da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Berk=20=C3=96zk=C3=BCt=C3=BCk?= Date: Thu, 22 Sep 2022 15:03:33 +0300 Subject: [PATCH 133/213] Add a note regarding the `stack` requirement in wrapper tests (#3212) --- docs/contributing/contributing.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index 4d7aae78a5..544c278a6c 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -71,6 +71,7 @@ GHC 8.6.5 is not supported here because `nixpkgs-unstable` no longer maintains t The tests make use of the [Tasty](https://github.com/feuerbach/tasty) test framework. There are two test suites in the main haskell-language-server package, functional tests, and wrapper tests. +Some of the wrapper tests expect `stack` to be present on the system, or else they fail. Other project packages, like the core library or plugins, can have their own test suite. ### Testing with Cabal From fd1a7d1923aa0e09d718eef14d21c1b350ae680d Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Fri, 23 Sep 2022 08:01:22 +0200 Subject: [PATCH 134/213] Add diagnostics to Stan descriptor (#3213) --- plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs b/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs index 73f7f25c18..db2f18b9ef 100644 --- a/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs +++ b/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs @@ -29,7 +29,8 @@ import GHC.Generics (Generic) import HieTypes (HieASTs, HieFile) import Ide.Plugin.Config import Ide.Types (PluginDescriptor (..), - PluginId, + PluginId, configHasDiagnostics, + defaultConfigDescriptor, defaultPluginDescriptor, pluginEnabledConfig) import qualified Language.LSP.Types as LSP @@ -42,7 +43,11 @@ import Stan.Observation (Observation (..)) descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState descriptor recorder plId = (defaultPluginDescriptor plId) - {pluginRules = rules recorder plId} + { pluginRules = rules recorder plId + , pluginConfigDescriptor = defaultConfigDescriptor + { configHasDiagnostics = True + } + } newtype Log = LogShake Shake.Log deriving (Show) From c422cf373a1d9065c63f94ccfe2caa99ec699c80 Mon Sep 17 00:00:00 2001 From: hololeap Date: Fri, 23 Sep 2022 20:50:25 -0600 Subject: [PATCH 135/213] Add source-repository to all cabal files (#3219) Add a source-repository stanza to any .cabal files in the project that are missing it. This metadata can be useful for package maintainers. See: https://cabal.readthedocs.io/en/stable/cabal-package.html#source-repositories Signed-off-by: hololeap Signed-off-by: hololeap --- ghcide-bench/ghcide-bench.cabal | 4 ++++ hie-compat/hie-compat.cabal | 4 ++++ .../hls-alternate-number-format-plugin.cabal | 4 ++++ plugins/hls-brittany-plugin/hls-brittany-plugin.cabal | 4 ++++ .../hls-call-hierarchy-plugin.cabal | 4 ++++ .../hls-change-type-signature-plugin.cabal | 4 ++++ plugins/hls-class-plugin/hls-class-plugin.cabal | 4 ++++ plugins/hls-code-range-plugin/hls-code-range-plugin.cabal | 4 ++++ .../hls-explicit-fixity-plugin.cabal | 4 ++++ .../hls-explicit-imports-plugin.cabal | 4 ++++ plugins/hls-floskell-plugin/hls-floskell-plugin.cabal | 4 ++++ plugins/hls-gadt-plugin/hls-gadt-plugin.cabal | 4 ++++ .../hls-haddock-comments-plugin.cabal | 4 ++++ plugins/hls-hlint-plugin/hls-hlint-plugin.cabal | 4 ++++ plugins/hls-module-name-plugin/hls-module-name-plugin.cabal | 4 ++++ plugins/hls-ormolu-plugin/hls-ormolu-plugin.cabal | 4 ++++ plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal | 4 ++++ .../hls-qualify-imported-names-plugin.cabal | 4 ++++ plugins/hls-refactor-plugin/hls-refactor-plugin.cabal | 4 ++++ .../hls-refine-imports-plugin.cabal | 4 ++++ plugins/hls-rename-plugin/hls-rename-plugin.cabal | 4 ++++ plugins/hls-retrie-plugin/hls-retrie-plugin.cabal | 4 ++++ plugins/hls-splice-plugin/hls-splice-plugin.cabal | 4 ++++ plugins/hls-stan-plugin/hls-stan-plugin.cabal | 5 ++++- .../hls-stylish-haskell-plugin.cabal | 4 ++++ plugins/hls-tactics-plugin/hls-tactics-plugin.cabal | 4 ++++ shake-bench/shake-bench.cabal | 4 ++++ 27 files changed, 108 insertions(+), 1 deletion(-) diff --git a/ghcide-bench/ghcide-bench.cabal b/ghcide-bench/ghcide-bench.cabal index 450aadba50..2a87563121 100644 --- a/ghcide-bench/ghcide-bench.cabal +++ b/ghcide-bench/ghcide-bench.cabal @@ -14,6 +14,10 @@ homepage: https://github.com/haskell/haskell-language-server/tree/mast bug-reports: https://github.com/haskell/haskell-language-server/issues tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + executable ghcide-bench default-language: Haskell2010 build-depends: diff --git a/hie-compat/hie-compat.cabal b/hie-compat/hie-compat.cabal index 4573c410f2..8dd3f899b9 100644 --- a/hie-compat/hie-compat.cabal +++ b/hie-compat/hie-compat.cabal @@ -17,6 +17,10 @@ category: Development homepage: https://github.com/haskell/haskell-language-server/tree/master/hie-compat#readme bug-reports: https://github.com/haskell/haskell-language-server/issues +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + flag ghc-lib description: build against ghc-lib instead of the ghc package default: False diff --git a/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal b/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal index 4d6487e274..041998756d 100644 --- a/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal +++ b/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal @@ -17,6 +17,10 @@ extra-source-files: test/testdata/*.hs test/testdata/*.yaml +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library buildable: True exposed-modules: Ide.Plugin.AlternateNumberFormat, Ide.Plugin.Conversion diff --git a/plugins/hls-brittany-plugin/hls-brittany-plugin.cabal b/plugins/hls-brittany-plugin/hls-brittany-plugin.cabal index 872f3c1c12..23c7853f23 100644 --- a/plugins/hls-brittany-plugin/hls-brittany-plugin.cabal +++ b/plugins/hls-brittany-plugin/hls-brittany-plugin.cabal @@ -16,6 +16,10 @@ extra-source-files: LICENSE test/testdata/**/*.hs +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library if impl(ghc >= 9.2) buildable: False diff --git a/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal b/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal index 771de409c6..abd5b17d33 100644 --- a/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal +++ b/plugins/hls-call-hierarchy-plugin/hls-call-hierarchy-plugin.cabal @@ -15,6 +15,10 @@ extra-source-files: LICENSE test/testdata/*.hs +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library buildable: True exposed-modules: Ide.Plugin.CallHierarchy diff --git a/plugins/hls-change-type-signature-plugin/hls-change-type-signature-plugin.cabal b/plugins/hls-change-type-signature-plugin/hls-change-type-signature-plugin.cabal index 6c453854b7..e348255a2a 100644 --- a/plugins/hls-change-type-signature-plugin/hls-change-type-signature-plugin.cabal +++ b/plugins/hls-change-type-signature-plugin/hls-change-type-signature-plugin.cabal @@ -18,6 +18,10 @@ extra-source-files: test/testdata/*.txt test/testdata/*.yaml +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library buildable: True exposed-modules: Ide.Plugin.ChangeTypeSignature diff --git a/plugins/hls-class-plugin/hls-class-plugin.cabal b/plugins/hls-class-plugin/hls-class-plugin.cabal index d388c3a7a8..7fd5483ed5 100644 --- a/plugins/hls-class-plugin/hls-class-plugin.cabal +++ b/plugins/hls-class-plugin/hls-class-plugin.cabal @@ -20,6 +20,10 @@ extra-source-files: LICENSE test/testdata/*.hs +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library if impl(ghc >= 9.3) buildable: False diff --git a/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal b/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal index a9000a0158..4b228dcde0 100644 --- a/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal +++ b/plugins/hls-code-range-plugin/hls-code-range-plugin.cabal @@ -20,6 +20,10 @@ extra-source-files: test/testdata/selection-range/*.yaml test/testdata/selection-range/*.txt +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library exposed-modules: Ide.Plugin.CodeRange diff --git a/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal b/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal index 9bbab0d8c7..066d53737d 100644 --- a/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal +++ b/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal @@ -15,6 +15,10 @@ extra-source-files: LICENSE test/testdata/*.hs +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library buildable: True exposed-modules: Ide.Plugin.ExplicitFixity diff --git a/plugins/hls-explicit-imports-plugin/hls-explicit-imports-plugin.cabal b/plugins/hls-explicit-imports-plugin/hls-explicit-imports-plugin.cabal index c7b8e65fbe..15f12c7f60 100644 --- a/plugins/hls-explicit-imports-plugin/hls-explicit-imports-plugin.cabal +++ b/plugins/hls-explicit-imports-plugin/hls-explicit-imports-plugin.cabal @@ -15,6 +15,10 @@ extra-source-files: test/testdata/*.hs test/testdata/*.yaml +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library buildable: True exposed-modules: Ide.Plugin.ExplicitImports diff --git a/plugins/hls-floskell-plugin/hls-floskell-plugin.cabal b/plugins/hls-floskell-plugin/hls-floskell-plugin.cabal index c27181926e..3e4811da3b 100644 --- a/plugins/hls-floskell-plugin/hls-floskell-plugin.cabal +++ b/plugins/hls-floskell-plugin/hls-floskell-plugin.cabal @@ -16,6 +16,10 @@ extra-source-files: LICENSE test/testdata/**/*.hs +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library if impl(ghc >= 9.3) buildable: False diff --git a/plugins/hls-gadt-plugin/hls-gadt-plugin.cabal b/plugins/hls-gadt-plugin/hls-gadt-plugin.cabal index 3db304c269..a4b1568436 100644 --- a/plugins/hls-gadt-plugin/hls-gadt-plugin.cabal +++ b/plugins/hls-gadt-plugin/hls-gadt-plugin.cabal @@ -15,6 +15,10 @@ extra-source-files: LICENSE test/testdata/*.hs +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library if impl(ghc >= 9.3) buildable: False diff --git a/plugins/hls-haddock-comments-plugin/hls-haddock-comments-plugin.cabal b/plugins/hls-haddock-comments-plugin/hls-haddock-comments-plugin.cabal index 5b97c917b5..d3cb390791 100644 --- a/plugins/hls-haddock-comments-plugin/hls-haddock-comments-plugin.cabal +++ b/plugins/hls-haddock-comments-plugin/hls-haddock-comments-plugin.cabal @@ -17,6 +17,10 @@ extra-source-files: LICENSE test/testdata/*.hs +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library if impl(ghc >= 9.2) buildable: False diff --git a/plugins/hls-hlint-plugin/hls-hlint-plugin.cabal b/plugins/hls-hlint-plugin/hls-hlint-plugin.cabal index 4de8669b9f..a29c40e033 100644 --- a/plugins/hls-hlint-plugin/hls-hlint-plugin.cabal +++ b/plugins/hls-hlint-plugin/hls-hlint-plugin.cabal @@ -20,6 +20,10 @@ extra-source-files: test/testdata/**/*.hs test/testdata/**/*.h +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + flag pedantic description: Enable -Werror default: False diff --git a/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal b/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal index 223fb76370..811ba7105b 100644 --- a/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal +++ b/plugins/hls-module-name-plugin/hls-module-name-plugin.cabal @@ -19,6 +19,10 @@ extra-source-files: test/testdata/**/*.cabal test/testdata/**/*.project +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library buildable: True exposed-modules: Ide.Plugin.ModuleName diff --git a/plugins/hls-ormolu-plugin/hls-ormolu-plugin.cabal b/plugins/hls-ormolu-plugin/hls-ormolu-plugin.cabal index 6d436087ad..b17ccbf741 100644 --- a/plugins/hls-ormolu-plugin/hls-ormolu-plugin.cabal +++ b/plugins/hls-ormolu-plugin/hls-ormolu-plugin.cabal @@ -16,6 +16,10 @@ extra-source-files: LICENSE test/testdata/**/*.hs +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library if impl(ghc >= 9.3) buildable: False diff --git a/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal b/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal index af2f13d639..20abe18fa4 100644 --- a/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal +++ b/plugins/hls-pragmas-plugin/hls-pragmas-plugin.cabal @@ -17,6 +17,10 @@ extra-source-files: test/testdata/*.hs test/testdata/*.yaml +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library buildable: True exposed-modules: Ide.Plugin.Pragmas diff --git a/plugins/hls-qualify-imported-names-plugin/hls-qualify-imported-names-plugin.cabal b/plugins/hls-qualify-imported-names-plugin/hls-qualify-imported-names-plugin.cabal index 4c00fb91f4..f60416b3cc 100644 --- a/plugins/hls-qualify-imported-names-plugin/hls-qualify-imported-names-plugin.cabal +++ b/plugins/hls-qualify-imported-names-plugin/hls-qualify-imported-names-plugin.cabal @@ -17,6 +17,10 @@ extra-source-files: test/data/*.hs test/data/*.yaml +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library exposed-modules: Ide.Plugin.QualifyImportedNames hs-source-dirs: src diff --git a/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal b/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal index d61d1593ee..7dfa8a020c 100644 --- a/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal +++ b/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal @@ -17,6 +17,10 @@ extra-source-files: test/data/**/*.hs test/data/**/*.yaml +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library if impl(ghc >= 9.3) buildable: False diff --git a/plugins/hls-refine-imports-plugin/hls-refine-imports-plugin.cabal b/plugins/hls-refine-imports-plugin/hls-refine-imports-plugin.cabal index 47abdac196..f47053736f 100644 --- a/plugins/hls-refine-imports-plugin/hls-refine-imports-plugin.cabal +++ b/plugins/hls-refine-imports-plugin/hls-refine-imports-plugin.cabal @@ -15,6 +15,10 @@ extra-source-files: test/testdata/*.hs test/testdata/*.yaml +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library buildable: True exposed-modules: Ide.Plugin.RefineImports diff --git a/plugins/hls-rename-plugin/hls-rename-plugin.cabal b/plugins/hls-rename-plugin/hls-rename-plugin.cabal index c3f063b332..8092a27d9e 100644 --- a/plugins/hls-rename-plugin/hls-rename-plugin.cabal +++ b/plugins/hls-rename-plugin/hls-rename-plugin.cabal @@ -16,6 +16,10 @@ extra-source-files: test/testdata/*.hs test/testdata/*.yaml +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library if impl(ghc >= 9.3) buildable: False diff --git a/plugins/hls-retrie-plugin/hls-retrie-plugin.cabal b/plugins/hls-retrie-plugin/hls-retrie-plugin.cabal index d484973769..9b1347b4d0 100644 --- a/plugins/hls-retrie-plugin/hls-retrie-plugin.cabal +++ b/plugins/hls-retrie-plugin/hls-retrie-plugin.cabal @@ -12,6 +12,10 @@ category: Development build-type: Simple extra-source-files: LICENSE +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library if impl(ghc >= 9.3) buildable: False diff --git a/plugins/hls-splice-plugin/hls-splice-plugin.cabal b/plugins/hls-splice-plugin/hls-splice-plugin.cabal index fa6d14a3a7..8e045230c8 100644 --- a/plugins/hls-splice-plugin/hls-splice-plugin.cabal +++ b/plugins/hls-splice-plugin/hls-splice-plugin.cabal @@ -22,6 +22,10 @@ extra-source-files: test/testdata/*.hs test/testdata/*.yaml +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library if impl(ghc >= 9.3) buildable: False diff --git a/plugins/hls-stan-plugin/hls-stan-plugin.cabal b/plugins/hls-stan-plugin/hls-stan-plugin.cabal index 855314adc0..370d206f81 100644 --- a/plugins/hls-stan-plugin/hls-stan-plugin.cabal +++ b/plugins/hls-stan-plugin/hls-stan-plugin.cabal @@ -16,12 +16,15 @@ extra-source-files: LICENSE test/testdata/*.hs +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + flag pedantic description: Enable -Werror default: False manual: True - library if impl(ghc < 8.8) || impl(ghc >= 9.0) buildable: False diff --git a/plugins/hls-stylish-haskell-plugin/hls-stylish-haskell-plugin.cabal b/plugins/hls-stylish-haskell-plugin/hls-stylish-haskell-plugin.cabal index 9f28cd4bb9..e8e7690326 100644 --- a/plugins/hls-stylish-haskell-plugin/hls-stylish-haskell-plugin.cabal +++ b/plugins/hls-stylish-haskell-plugin/hls-stylish-haskell-plugin.cabal @@ -15,6 +15,10 @@ extra-source-files: LICENSE test/testdata/*.hs +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library if impl(ghc >= 9.3) buildable: False diff --git a/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal b/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal index f62dbc5786..d06fe4297c 100644 --- a/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal +++ b/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal @@ -19,6 +19,10 @@ extra-source-files: test/golden/*.hs test/golden/*.yaml +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + flag pedantic description: Enable -Werror default: False diff --git a/shake-bench/shake-bench.cabal b/shake-bench/shake-bench.cabal index ec1649ccd5..365ef99820 100644 --- a/shake-bench/shake-bench.cabal +++ b/shake-bench/shake-bench.cabal @@ -11,6 +11,10 @@ build-type: Simple -- description is a single line so that implicit-hie can parse it description: A library Shake rules to build and run benchmarks for multiple revisions of a project. An example of usage can be found in the ghcide benchmark suite +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server.git + library exposed-modules: Development.Benchmark.Rules hs-source-dirs: src From 6f9b43533164252327ba14ff86fa7935d6ec1d4f Mon Sep 17 00:00:00 2001 From: fendor Date: Sun, 25 Sep 2022 17:34:55 +0200 Subject: [PATCH 136/213] Sort vscode extension schema json by keys (#3203) Makes it easier to copy and paste configurations into VSCode and reviewing what options have been added and removed. Remove code-duplication, namely ghcide exe loses some capabilities, as it is destined to be removed sooner or later. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Michael Peyton Jones --- ghcide/src/Development/IDE/Main.hs | 14 -------------- src/Ide/Arguments.hs | 11 +++++++++++ src/Ide/Main.hs | 12 ++++++++---- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/ghcide/src/Development/IDE/Main.hs b/ghcide/src/Development/IDE/Main.hs index e6c9d9373e..a344a46dd5 100644 --- a/ghcide/src/Development/IDE/Main.hs +++ b/ghcide/src/Development/IDE/Main.hs @@ -190,8 +190,6 @@ data Command | Db {hieOptions :: HieDb.Options, hieCommand :: HieDb.Command} -- ^ Run a command in the hiedb | LSP -- ^ Run the LSP server - | PrintExtensionSchema - | PrintDefaultConfig | Custom {ideCommand :: IdeCommand IdeState} -- ^ User defined deriving Show @@ -208,8 +206,6 @@ commandP plugins = hsubparser(command "typecheck" (info (Check <$> fileCmd) fileInfo) <> command "hiedb" (info (Db <$> HieDb.optParser "" True <*> HieDb.cmdParser) hieInfo) <> command "lsp" (info (pure LSP) lspInfo) - <> command "vscode-extension-schema" extensionSchemaCommand - <> command "generate-default-config" generateDefaultConfigCommand <> pluginCommands ) where @@ -217,12 +213,6 @@ commandP plugins = lspInfo = fullDesc <> progDesc "Start talking to an LSP client" fileInfo = fullDesc <> progDesc "Used as a test bed to check your IDE will work" hieInfo = fullDesc <> progDesc "Query .hie files" - extensionSchemaCommand = - info (pure PrintExtensionSchema) - (fullDesc <> progDesc "Print generic config schema for plugins (used in the package.json of haskell vscode extension)") - generateDefaultConfigCommand = - info (pure PrintDefaultConfig) - (fullDesc <> progDesc "Print config supported by the server with default values") pluginCommands = mconcat [ command (T.unpack pId) (Custom <$> p) @@ -330,10 +320,6 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re numProcessors <- getNumProcessors case argCommand of - PrintExtensionSchema -> - LT.putStrLn $ decodeUtf8 $ A.encodePretty $ pluginsToVSCodeExtensionSchema argsHlsPlugins - PrintDefaultConfig -> - LT.putStrLn $ decodeUtf8 $ A.encodePretty $ pluginsToDefaultConfig argsHlsPlugins LSP -> withNumCapabilities (maybe (numProcessors `div` 2) fromIntegral argsThreads) $ do t <- offsetTime log Info $ LogLspStart (pluginId <$> ipMap argsHlsPlugins) diff --git a/src/Ide/Arguments.hs b/src/Ide/Arguments.hs index eb87bee30c..43826dbd93 100644 --- a/src/Ide/Arguments.hs +++ b/src/Ide/Arguments.hs @@ -66,6 +66,10 @@ getArguments exeName plugins = execParser opts opts = info (( VersionMode <$> printVersionParser exeName <|> probeToolsParser exeName + <|> hsubparser + ( command "vscode-extension-schema" extensionSchemaCommand + <> command "generate-default-config" generateDefaultConfigCommand + ) <|> listPluginsParser <|> BiosMode <$> biosParser <|> Ghcide <$> arguments plugins @@ -76,6 +80,13 @@ getArguments exeName plugins = execParser opts <> progDesc "Used as a test bed to check your IDE Client will work" <> header (exeName ++ " - GHC Haskell LSP server")) + extensionSchemaCommand = + info (pure VSCodeExtensionSchemaMode) + (fullDesc <> progDesc "Print generic config schema for plugins (used in the package.json of haskell vscode extension)") + generateDefaultConfigCommand = + info (pure DefaultConfigurationMode) + (fullDesc <> progDesc "Print config supported by the server with default values") + printVersionParser :: String -> Parser PrintVersion printVersionParser exeName = flag' PrintVersion diff --git a/src/Ide/Main.hs b/src/Ide/Main.hs index 7e03357418..eaf15d6440 100644 --- a/src/Ide/Main.hs +++ b/src/Ide/Main.hs @@ -12,12 +12,13 @@ module Ide.Main(defaultMain, runLspMode, Log(..)) where import Control.Monad.Extra import qualified Data.Aeson.Encode.Pretty as A -import qualified Data.ByteString.Lazy.Char8 as LBS import Data.Coerce (coerce) import Data.Default import Data.List (sort) import Data.Text (Text) import qualified Data.Text as T +import Data.Text.Lazy.Encoding (decodeUtf8) +import qualified Data.Text.Lazy.IO as LT import Development.IDE.Core.Rules hiding (Log, logToPriority) import Development.IDE.Core.Tracing (withTelemetryLogger) import Development.IDE.Main (isLSP) @@ -96,10 +97,9 @@ defaultMain recorder args idePlugins = do runLspMode recorder ghcideArgs idePlugins VSCodeExtensionSchemaMode -> do - LBS.putStrLn $ A.encodePretty $ pluginsToVSCodeExtensionSchema idePlugins - + LT.putStrLn $ decodeUtf8 $ encodePrettySorted $ pluginsToVSCodeExtensionSchema idePlugins DefaultConfigurationMode -> do - LBS.putStrLn $ A.encodePretty $ pluginsToDefaultConfig idePlugins + LT.putStrLn $ decodeUtf8 $ encodePrettySorted $ pluginsToDefaultConfig idePlugins PrintLibDir -> do d <- getCurrentDirectory let initialFp = d "a" @@ -107,6 +107,10 @@ defaultMain recorder args idePlugins = do cradle <- Session.loadCradle def hieYaml d (CradleSuccess libdir) <- HieBios.getRuntimeGhcLibDir cradle putStr libdir + where + encodePrettySorted = A.encodePretty' A.defConfig + { A.confCompare = compare + } -- --------------------------------------------------------------------- From 217573f4a13e0a222739d4e2c3adccf2eaa0aab8 Mon Sep 17 00:00:00 2001 From: Kobayashi Date: Mon, 26 Sep 2022 08:05:19 +0800 Subject: [PATCH 137/213] Improve haddock comments (#3207) * remove 'buildable: False' in cabal * remove constraint on ghc-exactprint * wip * revert HaddockComments.hs * generate haddock comments for constructors * fix tests * restore constraints * make it compatible with ghc 9.0 * add more tests * add comments & fix dp calculation for inline case * add kokobd to codeowners of haddock-comments plugin * fix a comment * rephrase some comments to make them clearer Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- CODEOWNERS | 2 +- .../hls-haddock-comments-plugin.cabal | 7 +- .../src/Ide/Plugin/HaddockComments.hs | 87 ++++----- .../src/Ide/Plugin/HaddockComments/Data.hs | 168 ++++++++++++++++++ .../src/Ide/Plugin/HaddockComments/Prelude.hs | 29 +++ .../hls-haddock-comments-plugin/test/Main.hs | 6 +- .../test/testdata/InlineRecord.expected.hs | 11 ++ .../test/testdata/InlineRecord.hs | 4 + .../test/testdata/Record.expected.hs | 30 ++-- .../test/testdata/Record2.expected.hs | 10 ++ .../test/testdata/Record2.hs | 7 + .../test/testdata/StaleRecord.hs | 7 +- 12 files changed, 294 insertions(+), 74 deletions(-) create mode 100644 plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments/Data.hs create mode 100644 plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments/Prelude.hs create mode 100644 plugins/hls-haddock-comments-plugin/test/testdata/InlineRecord.expected.hs create mode 100644 plugins/hls-haddock-comments-plugin/test/testdata/InlineRecord.hs create mode 100644 plugins/hls-haddock-comments-plugin/test/testdata/Record2.expected.hs create mode 100644 plugins/hls-haddock-comments-plugin/test/testdata/Record2.hs diff --git a/CODEOWNERS b/CODEOWNERS index 27cc7e20d4..61dfd9a206 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -16,7 +16,7 @@ /plugins/hls-floskell-plugin @Ailrun /plugins/hls-fourmolu-plugin @georgefst /plugins/hls-gadt-plugin @July541 -/plugins/hls-haddock-comments-plugin @berberman +/plugins/hls-haddock-comments-plugin @berberman @kokobd /plugins/hls-hlint-plugin @jneira @eddiemundo /plugins/hls-module-name-plugin /plugins/hls-ormolu-plugin @georgefst diff --git a/plugins/hls-haddock-comments-plugin/hls-haddock-comments-plugin.cabal b/plugins/hls-haddock-comments-plugin/hls-haddock-comments-plugin.cabal index d3cb390791..a815423f08 100644 --- a/plugins/hls-haddock-comments-plugin/hls-haddock-comments-plugin.cabal +++ b/plugins/hls-haddock-comments-plugin/hls-haddock-comments-plugin.cabal @@ -26,7 +26,10 @@ library buildable: False else buildable: True - exposed-modules: Ide.Plugin.HaddockComments + exposed-modules: + Ide.Plugin.HaddockComments + Ide.Plugin.HaddockComments.Data + Ide.Plugin.HaddockComments.Prelude hs-source-dirs: src ghc-options: -Wall -Wno-name-shadowing -Wredundant-constraints @@ -43,6 +46,8 @@ library , lsp-types , text , unordered-containers + , transformers + , mtl default-language: Haskell2010 default-extensions: diff --git a/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments.hs b/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments.hs index 79acf7b072..66ea479416 100644 --- a/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments.hs +++ b/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments.hs @@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-} +{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} @@ -8,8 +9,9 @@ module Ide.Plugin.HaddockComments (descriptor) where -import Control.Monad (join) +import Control.Monad (join, when) import Control.Monad.IO.Class +import Control.Monad.Trans.Class (lift) import qualified Data.HashMap.Strict as HashMap import qualified Data.Map as Map import qualified Data.Text as T @@ -19,6 +21,8 @@ import Development.IDE.GHC.Compat.ExactPrint import Development.IDE.GHC.ExactPrint (GetAnnotatedParsedSource (..)) import qualified Development.IDE.GHC.ExactPrint as E import Development.IDE.Plugin.CodeAction +import Ide.Plugin.HaddockComments.Data (genForDataDecl) +import Ide.Plugin.HaddockComments.Prelude import Ide.Types import Language.Haskell.GHC.ExactPrint import Language.Haskell.GHC.ExactPrint.Types hiding (GhcPs) @@ -40,47 +44,49 @@ codeActionProvider ideState _pId (CodeActionParams _ _ (TextDocumentIdentifier u (join -> pm) <- liftIO $ runAction "HaddockComments.GetAnnotatedParsedSource" ideState $ use GetAnnotatedParsedSource `traverse` nfp let locDecls = hsmodDecls . unLoc . astA <$> pm anns = annsA <$> pm - edits = [runGenComments gen locDecls anns range | noErr, gen <- genList] + edits = [gen locDecls anns range | noErr, gen <- genList] return $ Right $ List [InR $ toAction title uri edit | (Just (title, edit)) <- edits] -genList :: [GenComments] +genList :: [Maybe [LHsDecl GhcPs] -> Maybe Anns -> Range -> Maybe (T.Text, TextEdit)] genList = - [ genForSig, - genForRecord + [ runGenCommentsSimple genForSig, + runGenComments genForDataDecl ] ----------------------------------------------------------------------------- --- | Defines how to generate haddock comments by tweaking annotations of AST -data GenComments = forall a. - GenComments - { title :: T.Text, - fromDecl :: HsDecl GhcPs -> Maybe a, - collectKeys :: a -> [AnnKey], - isFresh :: Annotation -> Bool, - updateAnn :: Annotation -> Annotation, - updateDeclAnn :: Annotation -> Annotation - } - runGenComments :: GenComments -> Maybe [LHsDecl GhcPs] -> Maybe Anns -> Range -> Maybe (T.Text, TextEdit) -runGenComments GenComments {..} mLocDecls mAnns range +runGenComments GenComments{..} mLocDecls mAnns range | Just locDecls <- mLocDecls, Just anns <- mAnns, - [(locDecl, src, x)] <- [(locDecl, l, x) | locDecl@(L l (fromDecl -> Just x)) <- locDecls, range `isIntersectWith` l], - annKeys <- collectKeys x, - not $ null annKeys, - and $ maybe False isFresh . flip Map.lookup anns <$> annKeys, - declKey <- mkAnnKey locDecl, - anns' <- Map.adjust updateDeclAnn declKey $ foldr (Map.adjust updateAnn) anns annKeys, + [(locDecl, src)] <- [(locDecl, l) | locDecl@(L l _) <- locDecls, range `isIntersectWith` l], Just range' <- toRange src, - result <- T.strip . T.pack $ exactPrint locDecl anns' = - Just (title, TextEdit range' result) + Just (_, (anns', _), _) <- runTransformT anns (updateAnns locDecl), + result <- T.strip . T.pack $ exactPrint locDecl anns' + = Just (title, TextEdit range' result) | otherwise = Nothing +runGenCommentsSimple :: GenCommentsSimple -> Maybe [LHsDecl GhcPs] -> Maybe Anns -> Range -> Maybe (T.Text, TextEdit) +runGenCommentsSimple GenCommentsSimple {..} = runGenComments GenComments { + title = title, + updateAnns = updateAnns + } + where + updateAnns :: LHsDecl GhcPs -> TransformT Maybe () + updateAnns locDecl@(L _ decl) = do + x <- lift $ fromDecl decl + let annKeys = collectKeys x + anns <- getAnnsT + when (null annKeys || not (and $ maybe False isFresh . flip Map.lookup anns <$> annKeys)) $ + lift Nothing + let declKey = mkAnnKey locDecl + anns' = Map.adjust updateDeclAnn declKey $ foldr (Map.adjust updateAnn) anns annKeys + putAnnsT anns' + ----------------------------------------------------------------------------- -genForSig :: GenComments -genForSig = GenComments {..} +genForSig :: GenCommentsSimple +genForSig = GenCommentsSimple {..} where title = "Generate signature comments" @@ -102,30 +108,6 @@ genForSig = GenComments {..} #endif dp = [(AnnComment comment, DP (0, 1)), (G AnnRarrow, DP (1, 2))] -genForRecord :: GenComments -genForRecord = GenComments {..} - where - title = "Generate fields comments" - - fromDecl (TyClD _ DataDecl {tcdDataDefn = HsDataDefn {dd_cons = cons}}) = - Just [x | (L _ ConDeclH98 {con_args = x}) <- cons] - fromDecl _ = Nothing - - updateAnn x = x {annEntryDelta = DP (1, 2), annPriorComments = [(comment, DP (1, 2))]} - updateDeclAnn = cleanPriorComments - - isFresh Ann {annPriorComments} = null annPriorComments - - collectKeys = keyFromCon - -#if MIN_VERSION_ghc(9,2,0) - comment = mkComment "-- | " (spanAsAnchor noSrcSpan) -#elif MIN_VERSION_ghc(9,0,0) - comment = mkComment "-- | " badRealSrcSpan -#else - comment = mkComment "-- | " noSrcSpan -#endif - ----------------------------------------------------------------------------- toAction :: T.Text -> Uri -> TextEdit -> CodeAction @@ -176,7 +158,4 @@ keyFromTyVar dep (L _ (HsParTy _ x)) = keyFromTyVar (succ dep) x keyFromTyVar dep (L _ (HsBangTy _ _ x)) = keyFromTyVar dep x keyFromTyVar _ _ = [] -keyFromCon :: [HsConDeclDetails GhcPs] -> [AnnKey] -keyFromCon cons = mconcat [mkAnnKey <$> xs | (RecCon (L _ xs)) <- cons] - ----------------------------------------------------------------------------- diff --git a/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments/Data.hs b/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments/Data.hs new file mode 100644 index 0000000000..3c37556841 --- /dev/null +++ b/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments/Data.hs @@ -0,0 +1,168 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} + +module Ide.Plugin.HaddockComments.Data + ( genForDataDecl + ) where + +import Control.Monad (unless, when) +import Control.Monad.Trans.Class (lift) +import Data.Data (Data) +import Data.Foldable (for_) +import Data.List (isPrefixOf) +import qualified Data.Map.Strict as Map +import Data.Maybe (fromMaybe, isJust) +import Development.IDE (realSpan) +import Development.IDE.GHC.Compat +import Development.IDE.GHC.ExactPrint +import Ide.Plugin.HaddockComments.Prelude +import Language.Haskell.GHC.ExactPrint +import Language.Haskell.GHC.ExactPrint.Types hiding (GhcPs) +import Language.Haskell.GHC.ExactPrint.Utils (mkComment) + +genForDataDecl :: GenComments +genForDataDecl = + GenComments { + title = "Generate haddock comments", + updateAnns = updateDataAnns + } + +updateDataAnns :: LHsDecl GhcPs -> TransformT Maybe () +updateDataAnns decl@(L declLoc (TyClD _ DataDecl {tcdDataDefn = HsDataDefn { dd_cons = cons }})) = do + -- skip if all constructors and fields already have a haddock comment + getAnnsT >>= (\anns -> unless (missingSomeHaddock anns cons) (lift Nothing)) + + -- visit each constructor and field + addHaddockCommentsToList True declLoc (G AnnVbar) cons + for_ cons $ \case + L conLoc ConDeclH98 { con_args = RecCon (L _ fields) } -> addHaddockCommentsToList False conLoc (G AnnComma) fields + _ -> pure () + modifyAnnsT $ Map.adjust (\ann -> ann {annPriorComments = []}) (mkAnnKey decl) +updateDataAnns _ = lift Nothing + +-- | Add haddock comments to a list of nodes +addHaddockCommentsToList + :: (Data a, Monad m) + => Bool -- ^ If true, for each node, use previous node in the list as the anchor. Otherwise, use the outer node + -> SrcSpan -- ^ The outer node + -> KeywordId -- ^ The seperator between adjacent nodes + -> [Located a] -- ^ The list of nodes. Haddock comments will be added to each of them + -> TransformT m () +addHaddockCommentsToList usePrevNodeAsAnchor outerLoc seperator nodes = + -- If you want to understand this function, please first read this page carefully: + -- https://hackage.haskell.org/package/ghc-exactprint-0.6.4/docs/Language-Haskell-GHC-ExactPrint-Delta.html + -- The important part is that for DP(r,c), if r is zero, c is the offset start from the end of the previous node. + -- However, if r is greater than zero, c is the offset start from the 'anchor'. + -- Generally speaking, the 'anchor' is the node that "enclose" the current node. But it's not always the case. + -- Sometimes 'anchor' is just the previous node. It depends on the the syntactic structure. + -- For constructors, the anchor is the previous node (if there is any). + -- For record fields, the anchor is always the constructor they belong to. + for_ (zip nodes (Nothing: fmap Just nodes)) $ \(node, prevNode) -> do + addHaddockCommentToCurrentNode <- fmap (not . fromMaybe True . flip hasHaddock node) getAnnsT + -- We don't add new haddock comments to nodes with existing ones. + when addHaddockCommentToCurrentNode $ do + -- 'sameLineAsPrev' is a flag to determine the inline case, for example: + -- data T = A { a :: Int, b :: String } | B { b :: Double } + -- Note that it's a 'Maybe (Located a)', containing the previous node if the current node + -- and the previous node are on the same line. + -- + -- For the multiline case (which is the most common), we keep the original indentation of each constructor + -- and field. + -- + -- For the inline case, we use the first construcotr/field as the base, and align all following items + -- to them. + let sameLineAsPrev = prevNode >>= ( + \prevNode' -> if notSeperatedByLineEnding prevNode' node + then pure prevNode' + else Nothing + ) + -- For the inline case, we need to move the seperator to the next line. + -- For constructors, it's vertical bar; for fields, it's comma. + -- The seperator is passed in as function argument. + when (isJust sameLineAsPrev) $ modifyAnnsT $ \anns -> + let newSepCol :: Annotation -> Int + newSepCol ann = + if usePrevNodeAsAnchor then 0 else deltaColumn (annEntryDelta ann) + updateSepAnn :: Annotation -> Annotation + updateSepAnn ann = ann {annsDP = + Map.toList . Map.adjust (const $ DP (1, newSepCol ann)) seperator . Map.fromList $ annsDP ann} + in flip (maybe anns) prevNode $ \prevNode' -> Map.adjust updateSepAnn (mkAnnKey prevNode') anns + -- Calculate the real column of the anchor + let anchorCol = maybe 0 srcSpanStartCol . realSpan . maybe outerLoc getLoc $ + if usePrevNodeAsAnchor then prevNode else Nothing + -- 'dpCol' is what we will use for the current node's entry delta's column + dpCol <- flip fmap getAnnsT $ \anns -> + case sameLineAsPrev of + Just prevNode' -> + -- If the previous node is the anchor, using 0 as column will make current code align with + -- the previous one. + -- Otherwise, use the column of entry delta of the previous node. + -- The map lookup should not fail. '2' is used as a fallback value to make sure the syntax + -- is correct after the changes. + if usePrevNodeAsAnchor then 0 else maybe 2 (deltaColumn . annEntryDelta) + $ anns Map.!? mkAnnKey prevNode' + -- We subtract the real column to get dp column. + Nothing -> (maybe 2 srcSpanStartCol . realSpan $ getLoc node) - anchorCol + -- Modify the current node + modifyAnnsT $ + let updateCurrent :: Annotation -> Annotation + updateCurrent ann = ann { + -- If there exist non-haddock comments, we simply inherit the first one's delta pos, + -- and move them two lines below, to seperate them from our newly added haddock comments + -- Otherwise, inherit the node's entry delta pos. + annPriorComments = case annPriorComments ann of + (c, dp) : rem -> (emptyPriorHaddockComment, dp) : (c, DP (2,0)) : rem + _ -> [(emptyPriorHaddockComment, annEntryDelta ann)], + annEntryDelta = DP (1, dpCol) + } + in Map.adjust updateCurrent (mkAnnKey node) + +-- | Determine if a list of constructor declarations is missing some haddock comments. +missingSomeHaddock :: Anns -> [LConDecl GhcPs] -> Bool +missingSomeHaddock anns = any $ \lcon@(L _ conDecl) -> case conDecl of + ConDeclH98 { con_args = RecCon (L _ fields) } -> + elem (Just False) $ hasHaddock anns lcon : fmap (hasHaddock anns) fields + _ -> False -- GADT is not supported yet + +-- | Returns 'True' if the end of the first node and the start of the second node are on the same line. +notSeperatedByLineEnding :: Located a + -> Located a + -> Bool +notSeperatedByLineEnding (L (RealSrcSpan x _) _) (L (RealSrcSpan y _) _) = + srcLocLine (realSrcSpanEnd x) == srcLocLine (realSrcSpanStart y) +notSeperatedByLineEnding _ _ = False + +-- | Empty haddock, suitable for being added to 'annPriorComments' +emptyPriorHaddockComment :: Comment +emptyPriorHaddockComment = mkComment "-- |" +#if MIN_VERSION_ghc(9,0,0) + badRealSrcSpan +#else + noSrcSpan +#endif + +-- | Determines the given node has haddock comments attached to it. +hasHaddock :: Data a => Anns -> Located a -> Maybe Bool +hasHaddock anns node = fmap annHasHaddock (anns Map.!? key) + where + key = mkAnnKey node + annHasHaddock ann = + any (matchCommentPrefix priorCommentPrefix . fst) (annPriorComments ann) + || any (matchCommentPrefix followingCommentPrefix . fst) (annFollowingComments ann) + || any (keywordIdIsHaddockComment . fst) (annsDP ann) + +-- | Checks if the given 'KeywordId' is a comment, and specifically, a haddock comment. +keywordIdIsHaddockComment :: KeywordId -> Bool +keywordIdIsHaddockComment (AnnComment comment) = any (`isPrefixOf` commentContents comment) (priorCommentPrefix ++ followingCommentPrefix) +keywordIdIsHaddockComment _ = False + +priorCommentPrefix :: [String] +priorCommentPrefix = ["-- |", "{-|", "{- |"] + +followingCommentPrefix :: [String] +followingCommentPrefix = ["-- ^", "{-^", "{- ^"] + +matchCommentPrefix :: [String] -> Comment -> Bool +matchCommentPrefix prefix comment = any (`isPrefixOf` commentContents comment) prefix diff --git a/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments/Prelude.hs b/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments/Prelude.hs new file mode 100644 index 0000000000..3bf56e2b61 --- /dev/null +++ b/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments/Prelude.hs @@ -0,0 +1,29 @@ +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE ExistentialQuantification #-} + +module Ide.Plugin.HaddockComments.Prelude where +import qualified Data.Text as T +import Development.IDE.GHC.Compat +import Development.IDE.GHC.ExactPrint +import Language.Haskell.GHC.ExactPrint (AnnKey, Annotation) + +-- | A more generic comments generator +data GenComments = GenComments + { title :: T.Text, + -- | Use 'Maybe' monad to exit early. 'Nothing' means a code action for haddock comments + -- in the given context is not possible. + updateAnns :: LHsDecl GhcPs -> TransformT Maybe () + } + +-- | Defines how to generate haddock comments by tweaking annotations of AST +-- +-- This is left here for compatibility reason, so that we don't break the existing code. +data GenCommentsSimple = forall a. + GenCommentsSimple + { title :: T.Text, + fromDecl :: HsDecl GhcPs -> Maybe a, + collectKeys :: a -> [AnnKey], + isFresh :: Annotation -> Bool, + updateAnn :: Annotation -> Annotation, + updateDeclAnn :: Annotation -> Annotation + } diff --git a/plugins/hls-haddock-comments-plugin/test/Main.hs b/plugins/hls-haddock-comments-plugin/test/Main.hs index 22189c2590..eaf10903a0 100644 --- a/plugins/hls-haddock-comments-plugin/test/Main.hs +++ b/plugins/hls-haddock-comments-plugin/test/Main.hs @@ -30,9 +30,11 @@ tests = goldenWithHaddockComments "MultivariateFunction" Signature 4 8, goldenWithHaddockComments "QualFunction" Signature 2 10, goldenWithHaddockComments "Record" Record 7 2, + goldenWithHaddockComments "Record2" Record 3 6, + goldenWithHaddockComments "InlineRecord" Record 3 20, expectedNothing "ConstFunction" Signature 2 2, expectedNothing "StaleFunction" Signature 3 3, - expectedNothing "StaleRecord" Record 3 12 + expectedNothing "StaleRecord" Record 4 9 ] goldenWithHaddockComments :: FilePath -> GenCommentsType -> UInt -> UInt -> TestTree @@ -54,7 +56,7 @@ data GenCommentsType = Signature | Record toTitle :: GenCommentsType -> Text toTitle Signature = "Generate signature comments" -toTitle Record = "Generate fields comments" +toTitle Record = "Generate haddock comments" caTitle :: (Command |? CodeAction) -> Maybe Text caTitle (InR CodeAction {_title}) = Just _title diff --git a/plugins/hls-haddock-comments-plugin/test/testdata/InlineRecord.expected.hs b/plugins/hls-haddock-comments-plugin/test/testdata/InlineRecord.expected.hs new file mode 100644 index 0000000000..cff893ddcb --- /dev/null +++ b/plugins/hls-haddock-comments-plugin/test/testdata/InlineRecord.expected.hs @@ -0,0 +1,11 @@ +module Record2 where + +-- | A record +data Record = -- | + A { -- | + a :: Int + , -- | + b :: String } + | -- | + B { -- | + bb :: Double } diff --git a/plugins/hls-haddock-comments-plugin/test/testdata/InlineRecord.hs b/plugins/hls-haddock-comments-plugin/test/testdata/InlineRecord.hs new file mode 100644 index 0000000000..c2f48dd98e --- /dev/null +++ b/plugins/hls-haddock-comments-plugin/test/testdata/InlineRecord.hs @@ -0,0 +1,4 @@ +module Record2 where + +-- | A record +data Record = A { a :: Int , b :: String } | B { bb :: Double } diff --git a/plugins/hls-haddock-comments-plugin/test/testdata/Record.expected.hs b/plugins/hls-haddock-comments-plugin/test/testdata/Record.expected.hs index a6ded3780b..9ac5afcf73 100644 --- a/plugins/hls-haddock-comments-plugin/test/testdata/Record.expected.hs +++ b/plugins/hls-haddock-comments-plugin/test/testdata/Record.expected.hs @@ -2,19 +2,21 @@ module Record where -- | A record data Record a b c d e f - = RecordA - { - -- | - a :: a, - -- | - b :: b + = -- | + RecordA + { -- | + a :: a, + -- | + b :: b } - | Pair c d - | RecordB - { - -- | - c :: e, - -- | - d :: f + | -- | + Pair c d + | -- | + RecordB + { -- | + c :: e, + -- | + d :: f } - | Void + | -- | + Void diff --git a/plugins/hls-haddock-comments-plugin/test/testdata/Record2.expected.hs b/plugins/hls-haddock-comments-plugin/test/testdata/Record2.expected.hs new file mode 100644 index 0000000000..c5968e5353 --- /dev/null +++ b/plugins/hls-haddock-comments-plugin/test/testdata/Record2.expected.hs @@ -0,0 +1,10 @@ +module Record2 where + +-- | A record +data Record = -- | + RecordA + { -- | + a :: Int + , -- | + b :: String + } diff --git a/plugins/hls-haddock-comments-plugin/test/testdata/Record2.hs b/plugins/hls-haddock-comments-plugin/test/testdata/Record2.hs new file mode 100644 index 0000000000..49ee7ba383 --- /dev/null +++ b/plugins/hls-haddock-comments-plugin/test/testdata/Record2.hs @@ -0,0 +1,7 @@ +module Record2 where + +-- | A record +data Record = RecordA + { a :: Int + , b :: String + } diff --git a/plugins/hls-haddock-comments-plugin/test/testdata/StaleRecord.hs b/plugins/hls-haddock-comments-plugin/test/testdata/StaleRecord.hs index 466db4c136..3b639bafae 100644 --- a/plugins/hls-haddock-comments-plugin/test/testdata/StaleRecord.hs +++ b/plugins/hls-haddock-comments-plugin/test/testdata/StaleRecord.hs @@ -1,6 +1,9 @@ module StaleRecord where -data Record = Record - { a :: Int, -- ^ ... +data Record = + -- | ... + Record + { a :: Int -- ^ aaa + , -- | bbb b :: String } From 30b2a4716e1438ec416bdc9cd0f1d671f40daaab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Sep 2022 09:13:27 +0100 Subject: [PATCH 138/213] Bump fkirc/skip-duplicate-actions from 4.0.0 to 5.1.0 (#3226) Bumps [fkirc/skip-duplicate-actions](https://github.com/fkirc/skip-duplicate-actions) from 4.0.0 to 5.1.0. - [Release notes](https://github.com/fkirc/skip-duplicate-actions/releases) - [Commits](https://github.com/fkirc/skip-duplicate-actions/compare/v4.0.0...v5.1.0) --- updated-dependencies: - dependency-name: fkirc/skip-duplicate-actions dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/bench.yml | 2 +- .github/workflows/caching.yml | 2 +- .github/workflows/flags.yml | 2 +- .github/workflows/nix.yml | 4 ++-- .github/workflows/test.yml | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index a9d7bdfae4..37ddcd4f9d 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -21,7 +21,7 @@ jobs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check - uses: fkirc/skip-duplicate-actions@v4.0.0 + uses: fkirc/skip-duplicate-actions@v5.1.0 with: cancel_others: false paths_ignore: '[ "**/docs/**" diff --git a/.github/workflows/caching.yml b/.github/workflows/caching.yml index ada3e69ba6..1245229505 100644 --- a/.github/workflows/caching.yml +++ b/.github/workflows/caching.yml @@ -57,7 +57,7 @@ jobs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check - uses: fkirc/skip-duplicate-actions@v4.0.0 + uses: fkirc/skip-duplicate-actions@v5.1.0 with: cancel_others: false paths_ignore: '["**/docs/**" diff --git a/.github/workflows/flags.yml b/.github/workflows/flags.yml index 849d68bc1b..352d3f9296 100644 --- a/.github/workflows/flags.yml +++ b/.github/workflows/flags.yml @@ -21,7 +21,7 @@ jobs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check - uses: fkirc/skip-duplicate-actions@v4.0.0 + uses: fkirc/skip-duplicate-actions@v5.1.0 with: cancel_others: false paths_ignore: '[ "**/docs/**" diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index eff8fa2123..64b1924575 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -21,7 +21,7 @@ jobs: should_skip_build: ${{ steps.skip_check_no_nix.outputs.should_skip }} steps: - id: skip_check - uses: fkirc/skip-duplicate-actions@v4.0.0 + uses: fkirc/skip-duplicate-actions@v5.1.0 with: cancel_others: false paths_ignore: '[ "**/docs/**" @@ -36,7 +36,7 @@ jobs: , ".gitlab/**" ]' - id: skip_check_no_nix - uses: fkirc/skip-duplicate-actions@v4.0.0 + uses: fkirc/skip-duplicate-actions@v5.1.0 with: cancel_others: false paths: '[ "**.nix" ]' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8199477be7..bf0b04403b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ jobs: should_skip_ghcide: ${{ steps.skip_ghcide_check.outputs.should_skip }} steps: - id: skip_check - uses: fkirc/skip-duplicate-actions@v4.0.0 + uses: fkirc/skip-duplicate-actions@v5.1.0 with: cancel_others: false paths_ignore: '[ "**/docs/**" @@ -39,7 +39,7 @@ jobs: ]' # If we only change ghcide downstream packages we have not test ghcide itself - id: skip_ghcide_check - uses: fkirc/skip-duplicate-actions@v4.0.0 + uses: fkirc/skip-duplicate-actions@v5.1.0 with: cancel_others: false paths_ignore: '[ "hls-test-utils/**" From e09c00588b447f66832c4fa0825c0eeffbebabd9 Mon Sep 17 00:00:00 2001 From: hololeap Date: Mon, 26 Sep 2022 06:37:47 -0600 Subject: [PATCH 139/213] hls-hlint-plugin: Update README.md (#3216) Remove warning about hlint restrictions, as #1340 has been merged. Co-authored-by: Michael Peyton Jones --- plugins/hls-hlint-plugin/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/hls-hlint-plugin/README.md b/plugins/hls-hlint-plugin/README.md index 77b33c0769..4fc4ebbf78 100644 --- a/plugins/hls-hlint-plugin/README.md +++ b/plugins/hls-hlint-plugin/README.md @@ -9,5 +9,3 @@ if your configuration is in a non-standard location or you want to change settin ## Known Differences from the `hlint` executable - The `hlint` executable by default turns on many extensions when parsing a file because it is not certain about the exact extensions that apply to the file (they may come from project files). This differs from HLS which uses only the extensions the file needs to parse the file. Hence it is possible for the `hlint` executable to report a parse error on a file, but the `hlint` plugin to work just fine on the same file. This does mean that the turning on/off of extensions in the hlint config may be ignored by the `hlint` plugin. -- Hlint restrictions do not work (yet). This [PR](https://github.com/ndmitchell/hlint/pull/1340) should enable that functionality, but this requires a newer version of hlint to be used in HLS. - From cdbef3e5cb79f5df0b2a3954f3eef24e8e91b40d Mon Sep 17 00:00:00 2001 From: Colten Webb <8738145+coltenwebb@users.noreply.github.com> Date: Mon, 26 Sep 2022 11:30:29 -0400 Subject: [PATCH 140/213] Feat: basic record dot completions (#3080) * baseline for record completions * address feedback * gate ghc version * add test * refactor * fix rope import * fix plugins from rebase * gate test by ghc version * comments, fixes * fix ghc90 test --- .../src/Development/IDE/Plugin/Completions.hs | 28 ++++- .../IDE/Plugin/Completions/Logic.hs | 109 +++++++++++++++--- .../IDE/Plugin/Completions/Types.hs | 22 ++++ test/functional/Completion.hs | 29 +++++ test/testdata/completion/RecordDotSyntax.hs | 28 +++++ 5 files changed, 195 insertions(+), 21 deletions(-) create mode 100644 test/testdata/completion/RecordDotSyntax.hs diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index 4a02d94bf9..a7fea1a075 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -48,6 +48,9 @@ import qualified Language.LSP.VFS as VFS import Numeric.Natural import Text.Fuzzy.Parallel (Scored (..)) +import qualified GHC.LanguageExtensions as LangExt +import Language.LSP.Types + data Log = LogShake Shake.Log deriving Show instance Pretty Log where @@ -120,7 +123,7 @@ getCompletionsLSP ide plId fmap Right $ case (contents, uriToFilePath' uri) of (Just cnts, Just path) -> do let npath = toNormalizedFilePath' path - (ideOpts, compls, moduleExports) <- liftIO $ runIdeAction "Completion" (shakeExtras ide) $ do + (ideOpts, compls, moduleExports, astres) <- liftIO $ runIdeAction "Completion" (shakeExtras ide) $ do opts <- liftIO $ getIdeOptionsIO $ shakeExtras ide localCompls <- useWithStaleFast LocalCompletions npath nonLocalCompls <- useWithStaleFast NonLocalCompletions npath @@ -140,18 +143,31 @@ getCompletionsLSP ide plId exportsCompls = mempty{anyQualCompls = exportsCompItems} let compls = (fst <$> localCompls) <> (fst <$> nonLocalCompls) <> Just exportsCompls <> Just lModules - pure (opts, fmap (,pm,binds) compls, moduleExports) + -- get HieAst if OverloadedRecordDot is enabled +#if MIN_VERSION_ghc(9,2,0) + let uses_overloaded_record_dot (ms_hspp_opts . msrModSummary -> dflags) = xopt LangExt.OverloadedRecordDot dflags +#else + let uses_overloaded_record_dot _ = False +#endif + ms <- fmap fst <$> useWithStaleFast GetModSummaryWithoutTimestamps npath + astres <- case ms of + Just ms' | uses_overloaded_record_dot ms' + -> useWithStaleFast GetHieAst npath + _ -> return Nothing + + pure (opts, fmap (,pm,binds) compls, moduleExports, astres) case compls of Just (cci', parsedMod, bindMap) -> do - pfix <- VFS.getCompletionPrefix position cnts + let pfix = getCompletionPrefix position cnts case (pfix, completionContext) of - (Just (VFS.PosPrefixInfo _ "" _ _), Just CompletionContext { _triggerCharacter = Just "."}) + ((PosPrefixInfo _ "" _ _), Just CompletionContext { _triggerCharacter = Just "."}) -> return (InL $ List []) - (Just pfix', _) -> do + (_, _) -> do let clientCaps = clientCapabilities $ shakeExtras ide plugins = idePlugins $ shakeExtras ide config <- getCompletionsConfig plId - allCompletions <- liftIO $ getCompletions plugins ideOpts cci' parsedMod bindMap pfix' clientCaps config moduleExports + + allCompletions <- liftIO $ getCompletions plugins ideOpts cci' parsedMod astres bindMap pfix clientCaps config moduleExports pure $ InL (List $ orderedCompletions allCompletions) _ -> return (InL $ List []) _ -> return (InL $ List []) diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index 6e03a61a22..78a921bec4 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -10,16 +10,18 @@ module Development.IDE.Plugin.Completions.Logic ( , localCompletionsForParsedModule , getCompletions , fromIdentInfo +, getCompletionPrefix ) where import Control.Applicative -import Data.Char (isUpper) +import Data.Char (isAlphaNum, isUpper) import Data.Generics import Data.List.Extra as List hiding (stripPrefix) import qualified Data.Map as Map -import Data.Maybe (fromMaybe, isJust, +import Data.Maybe (catMaybes, fromMaybe, + isJust, listToMaybe, mapMaybe) import qualified Data.Text as T import qualified Text.Fuzzy.Parallel as Fuzzy @@ -30,6 +32,7 @@ import Data.Either (fromRight) import Data.Function (on) import Data.Functor import qualified Data.HashMap.Strict as HM + import qualified Data.HashSet as HashSet import Data.Monoid (First (..)) import Data.Ord (Down (Down)) @@ -67,6 +70,11 @@ import qualified Language.LSP.VFS as VFS import Text.Fuzzy.Parallel (Scored (score), original) +import qualified Data.Text.Utf16.Rope as Rope +import Development.IDE + +import Development.IDE.Spans.AtPoint (pointCommand) + -- Chunk size used for parallelizing fuzzy matching chunkSize :: Int chunkSize = 1000 @@ -564,20 +572,21 @@ getCompletions -> IdeOptions -> CachedCompletions -> Maybe (ParsedModule, PositionMapping) + -> Maybe (HieAstResult, PositionMapping) -> (Bindings, PositionMapping) - -> VFS.PosPrefixInfo + -> PosPrefixInfo -> ClientCapabilities -> CompletionsConfig -> HM.HashMap T.Text (HashSet.HashSet IdentInfo) -> IO [Scored CompletionItem] getCompletions plugins ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qualCompls, importableModules} - maybe_parsed (localBindings, bmapping) prefixInfo caps config moduleExportsMap = do - let VFS.PosPrefixInfo { fullLine, prefixModule, prefixText } = prefixInfo - enteredQual = if T.null prefixModule then "" else prefixModule <> "." + maybe_parsed maybe_ast_res (localBindings, bmapping) prefixInfo caps config moduleExportsMap = do + let PosPrefixInfo { fullLine, prefixScope, prefixText } = prefixInfo + enteredQual = if T.null prefixScope then "" else prefixScope <> "." fullPrefix = enteredQual <> prefixText -- Boolean labels to tag suggestions as qualified (or not) - qual = not(T.null prefixModule) + qual = not(T.null prefixScope) notQual = False {- correct the position by moving 'foo :: Int -> String -> ' @@ -585,7 +594,7 @@ getCompletions plugins ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, to 'foo :: Int -> String -> ' ^ -} - pos = VFS.cursorPos prefixInfo + pos = cursorPos prefixInfo maxC = maxCompletions config @@ -608,6 +617,42 @@ getCompletions plugins ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, hpos = upperRange position' in getCContext lpos pm <|> getCContext hpos pm + + -- We need the hieast to be "fresh". We can't get types from "stale" hie files, so hasfield won't work, + -- since it gets the record fields from the types. + -- Perhaps this could be fixed with a refactor to GHC's IfaceTyCon, to have it also contain record fields. + -- Requiring fresh hieast is fine for normal workflows, because it is generated while the user edits. + recordDotSyntaxCompls :: [(Bool, CompItem)] + recordDotSyntaxCompls = case maybe_ast_res of + Just (HAR {hieAst = hieast, hieKind = HieFresh},_) -> concat $ pointCommand hieast (completionPrefixPos prefixInfo) nodeCompletions + _ -> [] + where + nodeCompletions :: HieAST Type -> [(Bool, CompItem)] + nodeCompletions node = concatMap g (nodeType $ nodeInfo node) + g :: Type -> [(Bool, CompItem)] + g (TyConApp theTyCon _) = map (dotFieldSelectorToCompl (printOutputable $ GHC.tyConName theTyCon)) $ getSels theTyCon + g _ = [] + getSels :: GHC.TyCon -> [T.Text] + getSels tycon = let f fieldLabel = printOutputable fieldLabel + in map f $ tyConFieldLabels tycon + -- Completions can return more information that just the completion itself, but it will + -- require more than what GHC currently gives us in the HieAST, since it only gives the Type + -- of the fields, not where they are defined, etc. So for now the extra fields remain empty. + -- Also: additionalTextEdits is a todo, since we may want to import the record. It requires a way + -- to get the record's module, which isn't included in the type information used to get the fields. + dotFieldSelectorToCompl :: T.Text -> T.Text -> (Bool, CompItem) + dotFieldSelectorToCompl recname label = (True, CI + { compKind = CiField + , insertText = label + , provenance = DefinedIn recname + , typeText = Nothing + , label = label + , isInfix = Nothing + , docs = emptySpanDoc + , isTypeCompl = False + , additionalTextEdits = Nothing + }) + -- completions specific to the current context ctxCompls' = case mcc of Nothing -> compls @@ -618,10 +663,10 @@ getCompletions plugins ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, ctxCompls = (fmap.fmap) (\comp -> toggleAutoExtend config $ comp { isInfix = infixCompls }) ctxCompls' infixCompls :: Maybe Backtick - infixCompls = isUsedAsInfix fullLine prefixModule prefixText pos + infixCompls = isUsedAsInfix fullLine prefixScope prefixText pos PositionMapping bDelta = bmapping - oldPos = fromDelta bDelta $ VFS.cursorPos prefixInfo + oldPos = fromDelta bDelta $ cursorPos prefixInfo startLoc = lowerRange oldPos endLoc = upperRange oldPos localCompls = map (uncurry localBindsToCompItem) $ getFuzzyScope localBindings startLoc endLoc @@ -634,10 +679,14 @@ getCompletions plugins ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, ty = showForSnippet <$> typ thisModName = Local $ nameSrcSpan name - compls = if T.null prefixModule - then map (notQual,) localCompls ++ map (qual,) unqualCompls ++ ((notQual,) . ($Nothing) <$> anyQualCompls) - else ((qual,) <$> Map.findWithDefault [] prefixModule (getQualCompls qualCompls)) - ++ ((notQual,) . ($ Just prefixModule) <$> anyQualCompls) + -- When record-dot-syntax completions are available, we return them exclusively. + -- They are only available when we write i.e. `myrecord.` with OverloadedRecordDot enabled. + -- Anything that isn't a field is invalid, so those completion don't make sense. + compls + | T.null prefixScope = map (notQual,) localCompls ++ map (qual,) unqualCompls ++ map (\compl -> (notQual, compl Nothing)) anyQualCompls + | not $ null recordDotSyntaxCompls = recordDotSyntaxCompls + | otherwise = ((qual,) <$> Map.findWithDefault [] prefixScope (getQualCompls qualCompls)) + ++ map (\compl -> (notQual, compl (Just prefixScope))) anyQualCompls filtListWith f list = [ fmap f label @@ -648,7 +697,7 @@ getCompletions plugins ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, filtImportCompls = filtListWith (mkImportCompl enteredQual) importableModules filterModuleExports moduleName = filtListWith $ mkModuleFunctionImport moduleName filtKeywordCompls - | T.null prefixModule = filtListWith mkExtCompl (optKeywords ideOpts) + | T.null prefixScope = filtListWith mkExtCompl (optKeywords ideOpts) | otherwise = [] if @@ -696,6 +745,7 @@ getCompletions plugins ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, + uniqueCompl :: CompItem -> CompItem -> Ordering uniqueCompl candidate unique = case compare (label candidate, compKind candidate) @@ -892,3 +942,32 @@ mergeListsBy cmp all_lists = merge_lists all_lists [] -> [] [xs] -> xs lists' -> merge_lists lists' + +-- |From the given cursor position, gets the prefix module or record for autocompletion +getCompletionPrefix :: Position -> VFS.VirtualFile -> PosPrefixInfo +getCompletionPrefix pos@(Position l c) (VFS.VirtualFile _ _ ropetext) = + fromMaybe (PosPrefixInfo "" "" "" pos) $ do -- Maybe monad + let headMaybe = listToMaybe + lastMaybe = headMaybe . reverse + + -- grab the entire line the cursor is at + curLine <- headMaybe $ T.lines $ Rope.toText + $ fst $ Rope.splitAtLine 1 $ snd $ Rope.splitAtLine (fromIntegral l) ropetext + let beforePos = T.take (fromIntegral c) curLine + -- the word getting typed, after previous space and before cursor + curWord <- + if | T.null beforePos -> Just "" + | T.last beforePos == ' ' -> Just "" -- don't count abc as the curword in 'abc ' + | otherwise -> lastMaybe (T.words beforePos) + + let parts = T.split (=='.') + $ T.takeWhileEnd (\x -> isAlphaNum x || x `elem` ("._'"::String)) curWord + case reverse parts of + [] -> Nothing + (x:xs) -> do + let modParts = reverse $ filter (not .T.null) xs + modName = T.intercalate "." modParts + return $ PosPrefixInfo { fullLine = curLine, prefixScope = modName, prefixText = x, cursorPos = pos } + +completionPrefixPos :: PosPrefixInfo -> Position +completionPrefixPos PosPrefixInfo { cursorPos = Position ln co, prefixText = str} = Position ln (co - (fromInteger . toInteger . T.length $ str) - 1) diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Types.hs b/ghcide/src/Development/IDE/Plugin/Completions/Types.hs index 127ba369b3..46129e78ad 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Types.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Types.hs @@ -26,6 +26,7 @@ import Ide.PluginUtils (getClientConfig, usePropertyLsp) import Ide.Types (PluginId) import Language.LSP.Server (MonadLsp) import Language.LSP.Types (CompletionItemKind (..), Uri) +import qualified Language.LSP.Types as J -- | Produce completions info for a file type instance RuleResult LocalCompletions = CachedCompletions @@ -136,3 +137,24 @@ instance Monoid CachedCompletions where instance Semigroup CachedCompletions where CC a b c d e <> CC a' b' c' d' e' = CC (a<>a') (b<>b') (c<>c') (d<>d') (e<>e') + + +-- | Describes the line at the current cursor position +data PosPrefixInfo = PosPrefixInfo + { fullLine :: !T.Text + -- ^ The full contents of the line the cursor is at + + , prefixScope :: !T.Text + -- ^ If any, the module name that was typed right before the cursor position. + -- For example, if the user has typed "Data.Maybe.from", then this property + -- will be "Data.Maybe" + -- If OverloadedRecordDot is enabled, "Shape.rect.width" will be + -- "Shape.rect" + + , prefixText :: !T.Text + -- ^ The word right before the cursor position, after removing the module part. + -- For example if the user has typed "Data.Maybe.from", + -- then this property will be "from" + , cursorPos :: !J.Position + -- ^ The cursor position + } deriving (Show,Eq) diff --git a/test/functional/Completion.hs b/test/functional/Completion.hs index 820f25ce95..8516051c51 100644 --- a/test/functional/Completion.hs +++ b/test/functional/Completion.hs @@ -84,6 +84,32 @@ tests = testGroup "completions" [ compls <- getCompletions doc (Position 5 7) liftIO $ assertBool "Expected completions" $ not $ null compls + , expectFailIfBeforeGhc92 "record dot syntax is introduced in GHC 9.2" + $ testGroup "recorddotsyntax" + [ testCase "shows field selectors" $ runSession hlsCommand fullCaps "test/testdata/completion" $ do + doc <- openDoc "RecordDotSyntax.hs" "haskell" + + let te = TextEdit (Range (Position 25 0) (Position 25 5)) "z = x.a" + _ <- applyEdit doc te + + compls <- getCompletions doc (Position 25 6) + item <- getCompletionByLabel "a" compls + + liftIO $ do + item ^. label @?= "a" + , testCase "shows field selectors for nested field" $ runSession hlsCommand fullCaps "test/testdata/completion" $ do + doc <- openDoc "RecordDotSyntax.hs" "haskell" + + let te = TextEdit (Range (Position 27 0) (Position 27 8)) "z2 = x.c.z" + _ <- applyEdit doc te + + compls <- getCompletions doc (Position 27 9) + item <- getCompletionByLabel "z" compls + + liftIO $ do + item ^. label @?= "z" + ] + -- See https://github.com/haskell/haskell-ide-engine/issues/903 , testCase "strips compiler generated stuff from completions" $ runSession hlsCommand fullCaps "test/testdata/completion" $ do doc <- openDoc "DupRecFields.hs" "haskell" @@ -348,3 +374,6 @@ shouldNotContainCompl :: [CompletionItem] -> T.Text -> Assertion compls `shouldNotContainCompl` lbl = all ((/= lbl) . (^. label)) compls @? "Should not contain completion: " ++ show lbl + +expectFailIfBeforeGhc92 :: String -> TestTree -> TestTree +expectFailIfBeforeGhc92 = knownBrokenForGhcVersions [GHC810, GHC88, GHC86, GHC90] diff --git a/test/testdata/completion/RecordDotSyntax.hs b/test/testdata/completion/RecordDotSyntax.hs new file mode 100644 index 0000000000..4ea2f6994b --- /dev/null +++ b/test/testdata/completion/RecordDotSyntax.hs @@ -0,0 +1,28 @@ +{-# LANGUAGE OverloadedRecordDot #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE NoFieldSelectors #-} + +module Test where + +import qualified Data.Maybe as M + +data MyRecord = MyRecord1 + { a :: String + , b :: Integer + , c :: MyChild + } + | MyRecord2 { a2 :: String + , b2 :: Integer + , c2 :: MyChild + } deriving (Eq, Show) + +newtype MyChild = MyChild + { z :: String + } deriving (Eq, Show) + +x = MyRecord1 { a = "Hello", b = 12, c = MyChild { z = "there" } } + +y = x.a ++ show x.b + +y2 = x.c.z + From 468db6f2aad207040b7eda2546499999945b71fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Berk=20=C3=96zk=C3=BCt=C3=BCk?= Date: Tue, 27 Sep 2022 12:16:14 +0300 Subject: [PATCH 141/213] Use nixpkgs variants of Sphinx packages (#3227) This commit replaces manually specified `myst-parser` and `sphinx_rtd_theme` packages with their counterparts already packaged on nixpkgs. With this change, `poetry2nix` is no more used, therefore that is removed as well. --- flake.lock | 87 ------------------------------------------------------ flake.nix | 33 ++------------------- 2 files changed, 2 insertions(+), 118 deletions(-) diff --git a/flake.lock b/flake.lock index 5d54006241..a26965d76a 100644 --- a/flake.lock +++ b/flake.lock @@ -84,21 +84,6 @@ "type": "github" } }, - "flake-utils_2": { - "locked": { - "lastModified": 1659877975, - "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "fourmolu": { "flake": false, "locked": { @@ -240,23 +225,6 @@ "url": "https://hackage.haskell.org/package/lsp-test-0.14.0.3/lsp-test-0.14.0.3.tar.gz" } }, - "myst-parser": { - "flake": false, - "locked": { - "lastModified": 1650933355, - "narHash": "sha256-Osd3urvH1bn9w/6sAMyLKHO7gxuYePpGJ9y8ReBfp4E=", - "owner": "smunix", - "repo": "MyST-Parser", - "rev": "57d0d78169a0e157406c35df951bdffdf94b4f9b", - "type": "github" - }, - "original": { - "owner": "smunix", - "ref": "fix.hls-docutils", - "repo": "MyST-Parser", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1663112159, @@ -273,41 +241,6 @@ "type": "github" } }, - "nixpkgs_2": { - "locked": { - "lastModified": 1663165252, - "narHash": "sha256-H9OiflDQy1vLu1w5yz46qWfGdcFBpS8VnPRLP0bsiZE=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "bde85b0815144b77763732e0f32918017480e6b5", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "nixpkgs", - "type": "github" - } - }, - "poetry2nix": { - "inputs": { - "flake-utils": "flake-utils_2", - "nixpkgs": "nixpkgs_2" - }, - "locked": { - "lastModified": 1662879385, - "narHash": "sha256-ZmiyHn0uPH4xHYcOhY0e0sPtfyM6jCF/shmVq2aTOLc=", - "owner": "nix-community", - "repo": "poetry2nix", - "rev": "4f8d61cd936f853242a4ce1fd476f5488c288c26", - "type": "github" - }, - "original": { - "owner": "nix-community", - "ref": "master", - "repo": "poetry2nix", - "type": "github" - } - }, "ptr-poker": { "flake": false, "locked": { @@ -351,32 +284,12 @@ "implicit-hie-cradle": "implicit-hie-cradle", "lsp": "lsp", "lsp-test": "lsp-test", - "myst-parser": "myst-parser", "nixpkgs": "nixpkgs", - "poetry2nix": "poetry2nix", "ptr-poker": "ptr-poker", "retrie": "retrie", - "sphinx_rtd_theme": "sphinx_rtd_theme", "stylish-haskell": "stylish-haskell" } }, - "sphinx_rtd_theme": { - "flake": false, - "locked": { - "lastModified": 1628276861, - "narHash": "sha256-fzqi0QfDSiN8YXtAiWXIoOlyHZQI4V32uOKZjPfWeWY=", - "owner": "readthedocs", - "repo": "sphinx_rtd_theme", - "rev": "34f81daaf52466366c80003db293d50075c1b896", - "type": "github" - }, - "original": { - "owner": "readthedocs", - "repo": "sphinx_rtd_theme", - "rev": "34f81daaf52466366c80003db293d50075c1b896", - "type": "github" - } - }, "stylish-haskell": { "flake": false, "locked": { diff --git a/flake.nix b/flake.nix index b9774bdf14..d417f444db 100644 --- a/flake.nix +++ b/flake.nix @@ -91,16 +91,6 @@ url = "https://hackage.haskell.org/package/hie-bios-0.11.0/hie-bios-0.11.0.tar.gz"; flake = false; }; - myst-parser = { - url = "github:smunix/MyST-Parser?ref=fix.hls-docutils"; - flake = false; - }; - # For https://github.com/readthedocs/sphinx_rtd_theme/pull/1185, otherwise lists are broken locally - sphinx_rtd_theme = { - url = "github:readthedocs/sphinx_rtd_theme?rev=34f81daaf52466366c80003db293d50075c1b896"; - flake = false; - }; - poetry2nix.url = "github:nix-community/poetry2nix/master"; }; outputs = inputs@{ self, nixpkgs, flake-compat, flake-utils, gitignore, all-cabal-hashes-unpacked, ... }: @@ -221,7 +211,7 @@ let pkgs = import nixpkgs { inherit system; - overlays = [ self.overlays.default inputs.poetry2nix.overlay ]; + overlays = [ self.overlays.default ]; config = { allowBroken = true; }; }; @@ -246,26 +236,7 @@ ghc942 = supportedGHCs.ghc942; ghcDefault = supportedGHCs.default; - # For markdown support - myst-parser = pkgs.poetry2nix.mkPoetryEnv { - projectDir = inputs.myst-parser; - python = pkgs.python39; - overrides = [ - pkgs.poetry2nix.defaultPoetryOverrides - ]; - }; - sphinx_rtd_theme = pkgs.poetry2nix.mkPoetryEnv { - projectDir = inputs.sphinx_rtd_theme; - python = pkgs.python39; - overrides = [ - pkgs.poetry2nix.defaultPoetryOverrides - (self: super: { - # The RTD theme doesn't work with newer docutils - docutils = pkgs.python3Packages.callPackage ./docutils.nix {}; - }) - ]; - }; - pythonWithPackages = pkgs.python3.withPackages (ps: [ps.sphinx myst-parser sphinx_rtd_theme ps.pip]); + pythonWithPackages = pkgs.python3.withPackages (ps: [ps.sphinx ps.myst-parser ps.sphinx_rtd_theme ps.pip]); docs = pkgs.stdenv.mkDerivation { name = "hls-docs"; From f6dc2064d1b99fe3edf95dbffec96084ce96961a Mon Sep 17 00:00:00 2001 From: maralorn Date: Wed, 28 Sep 2022 15:22:21 +0200 Subject: [PATCH 142/213] wrapper.in: Require runtime ghc-pkgs to be an abi compatible superset of bootpkgs (#3214) This still makes sure that ghc has been compiled with the same core libraries as hls while it allows runtime environments where other packages have been added to the ghc-pkg database. This commit also adds that file to the sdist, so that distro packagers can use it. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- GNUmakefile | 1 + bindist/wrapper.in | 8 ++++++-- haskell-language-server.cabal | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index b9c848798c..f26c9b4b20 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -106,6 +106,7 @@ bindist-ghc: $(SED) \ -e "s/@@EXE_NAME@@/haskell-language-server-$(GHC_VERSION)/" \ -e "s/@@GHC_VERSION@@/$(GHC_VERSION)/" \ + -e "s/@@BOOT_PKGS@@/$(shell ghc-pkg-$(GHC_VERSION) --global list --simple-output)/" \ -e "s/@@ABI_HASHES@@/$(shell for dep in `ghc-pkg-$(GHC_VERSION) --global list --simple-output` ; do printf "%s:" "$$dep" && ghc-pkg-$(GHC_VERSION) field $$dep abi --simple-output ; done | tr '\n' ' ' | xargs)/" \ bindist/wrapper.in > "$(BINDIST_OUT_DIR)/haskell-language-server-$(GHC_VERSION).in" $(CHMOD_X) "$(BINDIST_OUT_DIR)/haskell-language-server-$(GHC_VERSION).in" diff --git a/bindist/wrapper.in b/bindist/wrapper.in index 71804d40ec..bb2affcf42 100644 --- a/bindist/wrapper.in +++ b/bindist/wrapper.in @@ -3,6 +3,10 @@ exedir="@@EXE_DIR@@" executablename="@@EXE_NAME@@" GHC_VERSION="@@GHC_VERSION@@" + +# This space separated list contains the names and versions of the boot libraries used to compile hls. +BOOT_PKGS="@@BOOT_PKGS@@" +# This space separated list contains the ABI hashes of the pkgs in BOOT_PKGS at compiletime. ABI_HASHES="@@ABI_HASHES@@" debug_msg() { @@ -62,7 +66,7 @@ check_ghc() { # check version if [ "${check_ghc_ver}" = "${GHC_VERSION}" ] ; then - # check ABI + # check for all packages listed in BOOT_PKGS that they are present with the same ABI hash as at hls-compiletime to prevent linking issues. if "${GHC_PKG}" --version >/dev/null ; then : elif "${GHC_PKG}-${GHC_VERSION}" --version >/dev/null ; then @@ -73,7 +77,7 @@ check_ghc() { return 1 fi PKGCONF="${check_ghc_libdir}/package.conf.d" - MY_ABI_HASHES="$(for dep in $("${GHC_PKG}" --global --global-package-db "$PKGCONF" list --simple-output) ; do printf "%s:" "${dep}" && "${GHC_PKG}" --global --global-package-db "$PKGCONF" field "${dep}" abi --simple-output ; done | tr '\n' ' ' | xargs)" + MY_ABI_HASHES="$(for dep in ${BOOT_PKGS} ; do printf "%s:" "${dep}" && "${GHC_PKG}" --global --global-package-db "$PKGCONF" field "${dep}" abi --simple-output ; done | tr '\n' ' ' | xargs)" if [ "${ABI_HASHES}" != "${MY_ABI_HASHES}" ] ; then err_abi "${MY_ABI_HASHES}" return 3 diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index bf38eefa48..d6a63b16a1 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -22,6 +22,7 @@ extra-source-files: test/testdata/**/*.cabal test/testdata/**/*.yaml test/testdata/**/*.hs + bindist/wrapper.in flag pedantic description: Enable -Werror From 011d110b4c6b57f243846ce3f417905b921b2d43 Mon Sep 17 00:00:00 2001 From: Kobayashi Date: Thu, 29 Sep 2022 12:54:06 +0800 Subject: [PATCH 143/213] Fix error in code range (#3229) * add test case * handle error more properly * add an error type * fix tests * log the bad dependency case --- .../src/Ide/Plugin/CodeRange.hs | 110 ++++++++++++------ .../src/Ide/Plugin/CodeRange/Rules.hs | 2 +- .../test/Ide/Plugin/CodeRangeTest.hs | 41 ++++--- plugins/hls-code-range-plugin/test/Main.hs | 9 +- .../testdata/folding-range/Empty.golden.txt | 0 .../test/testdata/folding-range/Empty.hs | 1 + .../test/testdata/folding-range/hie.yaml | 1 + .../testdata/selection-range/Empty.golden.txt | 0 .../test/testdata/selection-range/Empty.hs | 1 + .../test/testdata/selection-range/hie.yaml | 1 + 10 files changed, 105 insertions(+), 61 deletions(-) create mode 100644 plugins/hls-code-range-plugin/test/testdata/folding-range/Empty.golden.txt create mode 100644 plugins/hls-code-range-plugin/test/testdata/folding-range/Empty.hs create mode 100644 plugins/hls-code-range-plugin/test/testdata/selection-range/Empty.golden.txt create mode 100644 plugins/hls-code-range-plugin/test/testdata/selection-range/Empty.hs diff --git a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs index 75fb1eca53..1fef9060b1 100644 --- a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs +++ b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs @@ -1,6 +1,7 @@ -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} module Ide.Plugin.CodeRange ( descriptor @@ -13,7 +14,7 @@ module Ide.Plugin.CodeRange ( ) where import Control.Monad.Except (ExceptT (ExceptT), - runExceptT) + mapExceptT) import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans.Maybe (MaybeT (MaybeT), maybeToExceptT) @@ -21,19 +22,21 @@ import Data.Either.Extra (maybeToEither) import Data.Maybe (fromMaybe) import Data.Vector (Vector) import qualified Data.Vector as V -import Development.IDE (IdeAction, +import Development.IDE (Action, IdeAction, IdeState (shakeExtras), Range (Range), Recorder, WithPriority, - cmapWithPrio, + cmapWithPrio, runAction, runIdeAction, toNormalizedFilePath', - uriToFilePath') -import Development.IDE.Core.Actions (useE) + uriToFilePath', use, + useWithStaleFast) import Development.IDE.Core.PositionMapping (PositionMapping, fromCurrentPosition, toCurrentRange) -import Development.IDE.Types.Logger (Pretty (..)) +import Development.IDE.Types.Logger (Pretty (..), + Priority (Warning), + logWith) import Ide.Plugin.CodeRange.Rules (CodeRange (..), GetCodeRange (..), codeRangeRule, crkToFrk) @@ -44,7 +47,7 @@ import Ide.Types (PluginDescriptor (pluginH PluginId, defaultPluginDescriptor, mkPluginHandler) -import Language.LSP.Server (LspM) +import Language.LSP.Server (LspM, LspT) import Language.LSP.Types (FoldingRange (..), FoldingRangeParams (..), List (List), @@ -61,44 +64,54 @@ import Prelude hiding (log, span) descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState descriptor recorder plId = (defaultPluginDescriptor plId) - { pluginHandlers = mkPluginHandler STextDocumentSelectionRange selectionRangeHandler - <> mkPluginHandler STextDocumentFoldingRange foldingRangeHandler + { pluginHandlers = mkPluginHandler STextDocumentSelectionRange (selectionRangeHandler recorder) + <> mkPluginHandler STextDocumentFoldingRange (foldingRangeHandler recorder) , pluginRules = codeRangeRule (cmapWithPrio LogRules recorder) } data Log = LogRules Rules.Log + | forall rule. Show rule => LogBadDependency rule instance Pretty Log where pretty log = case log of LogRules codeRangeLog -> pretty codeRangeLog + LogBadDependency rule -> pretty $ "bad dependency: " <> show rule -foldingRangeHandler :: IdeState -> PluginId -> FoldingRangeParams -> LspM c (Either ResponseError (List FoldingRange)) -foldingRangeHandler ide _ FoldingRangeParams{..} = do +foldingRangeHandler :: Recorder (WithPriority Log) -> IdeState -> PluginId -> FoldingRangeParams -> LspM c (Either ResponseError (List FoldingRange)) +foldingRangeHandler recorder ide _ FoldingRangeParams{..} = do pluginResponse $ do filePath <- ExceptT . pure . maybeToEither "fail to convert uri to file path" $ toNormalizedFilePath' <$> uriToFilePath' uri - foldingRanges <- ExceptT . liftIO . runIdeAction "FoldingRange" (shakeExtras ide) . runExceptT $ + foldingRanges <- mapExceptT runAction' $ getFoldingRanges filePath pure . List $ foldingRanges - where - uri :: Uri - TextDocumentIdentifier uri = _textDocument + where + uri :: Uri + TextDocumentIdentifier uri = _textDocument -getFoldingRanges :: NormalizedFilePath -> ExceptT String IdeAction [FoldingRange] -getFoldingRanges file = do - (codeRange, _) <- maybeToExceptT "fail to get code range" $ useE GetCodeRange file + runAction' :: Action (Either FoldingRangeError [FoldingRange]) -> LspT c IO (Either String [FoldingRange]) + runAction' action = do + result <- liftIO $ runAction "FoldingRange" ide action + case result of + Left err -> case err of + FoldingRangeBadDependency rule -> do + logWith recorder Warning $ LogBadDependency rule + pure $ Right [] + Right list -> pure $ Right list - -- removing first node because it folds the entire file - pure $ drop 1 $ findFoldingRanges codeRange +data FoldingRangeError = forall rule. Show rule => FoldingRangeBadDependency rule -selectionRangeHandler :: IdeState -> PluginId -> SelectionRangeParams -> LspM c (Either ResponseError (List SelectionRange)) -selectionRangeHandler ide _ SelectionRangeParams{..} = do +getFoldingRanges :: NormalizedFilePath -> ExceptT FoldingRangeError Action [FoldingRange] +getFoldingRanges file = do + codeRange <- maybeToExceptT (FoldingRangeBadDependency GetCodeRange) . MaybeT $ use GetCodeRange file + pure $ findFoldingRanges codeRange + +selectionRangeHandler :: Recorder (WithPriority Log) -> IdeState -> PluginId -> SelectionRangeParams -> LspM c (Either ResponseError (List SelectionRange)) +selectionRangeHandler recorder ide _ SelectionRangeParams{..} = do pluginResponse $ do filePath <- ExceptT . pure . maybeToEither "fail to convert uri to file path" $ toNormalizedFilePath' <$> uriToFilePath' uri - selectionRanges <- ExceptT . liftIO . runIdeAction "SelectionRange" (shakeExtras ide) . runExceptT $ - getSelectionRanges filePath positions - pure . List $ selectionRanges + fmap List . mapExceptT runIdeAction' . getSelectionRanges filePath $ positions where uri :: Uri TextDocumentIdentifier uri = _textDocument @@ -106,20 +119,42 @@ selectionRangeHandler ide _ SelectionRangeParams{..} = do positions :: [Position] List positions = _positions -getSelectionRanges :: NormalizedFilePath -> [Position] -> ExceptT String IdeAction [SelectionRange] + runIdeAction' :: IdeAction (Either SelectionRangeError [SelectionRange]) -> LspT c IO (Either String [SelectionRange]) + runIdeAction' action = do + result <- liftIO $ runIdeAction "SelectionRange" (shakeExtras ide) action + case result of + Left err -> case err of + SelectionRangeBadDependency rule -> do + logWith recorder Warning $ LogBadDependency rule + -- This might happen if the HieAst is not ready, + -- so we give it a default value instead of throwing an error + pure $ Right [] + SelectionRangeInputPositionMappingFailure -> pure $ + Left "failed to apply position mapping to input positions" + SelectionRangeOutputPositionMappingFailure -> pure $ + Left "failed to apply position mapping to output positions" + Right list -> pure $ Right list + +data SelectionRangeError = forall rule. Show rule => SelectionRangeBadDependency rule + | SelectionRangeInputPositionMappingFailure + | SelectionRangeOutputPositionMappingFailure + +getSelectionRanges :: NormalizedFilePath -> [Position] -> ExceptT SelectionRangeError IdeAction [SelectionRange] getSelectionRanges file positions = do - (codeRange, positionMapping) <- maybeToExceptT "fail to get code range" $ useE GetCodeRange file + (codeRange, positionMapping) <- maybeToExceptT (SelectionRangeBadDependency GetCodeRange) . MaybeT $ + useWithStaleFast GetCodeRange file -- 'positionMapping' should be appied to the input before using them - positions' <- maybeToExceptT "fail to apply position mapping to input positions" . MaybeT . pure $ + positions' <- maybeToExceptT SelectionRangeInputPositionMappingFailure . MaybeT . pure $ traverse (fromCurrentPosition positionMapping) positions let selectionRanges = flip fmap positions' $ \pos -> - -- We need a default selection range if the lookup fails, so that other positions can still have valid results. + -- We need a default selection range if the lookup fails, + -- so that other positions can still have valid results. let defaultSelectionRange = SelectionRange (Range pos pos) Nothing in fromMaybe defaultSelectionRange . findPosition pos $ codeRange -- 'positionMapping' should be applied to the output ranges before returning them - maybeToExceptT "fail to apply position mapping to output positions" . MaybeT . pure $ + maybeToExceptT SelectionRangeOutputPositionMappingFailure . MaybeT . pure $ traverse (toCurrentSelectionRange positionMapping) selectionRanges -- | Find 'Position' in 'CodeRange'. This can fail, if the given position is not covered by the 'CodeRange'. @@ -169,8 +204,13 @@ findPosition pos root = go Nothing root -- -- Discussion reference: https://github.com/haskell/haskell-language-server/pull/3058#discussion_r973737211 findFoldingRanges :: CodeRange -> [FoldingRange] -findFoldingRanges r@(CodeRange _ children _) = - let frChildren :: [FoldingRange] = concat $ V.toList $ fmap findFoldingRanges children +findFoldingRanges codeRange = + -- removing the first node because it folds the entire file + drop 1 $ findFoldingRangesRec codeRange + +findFoldingRangesRec :: CodeRange -> [FoldingRange] +findFoldingRangesRec r@(CodeRange _ children _) = + let frChildren :: [FoldingRange] = concat $ V.toList $ fmap findFoldingRangesRec children in case createFoldingRange r of Just x -> x:frChildren Nothing -> frChildren diff --git a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs index 13a2dd3847..311984a403 100644 --- a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs +++ b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs @@ -188,7 +188,7 @@ handleError recorder action' = do valueEither <- runExceptT action' case valueEither of Left msg -> do - logWith recorder Error msg + logWith recorder Warning msg pure $ toIdeResult (Left []) Right value -> pure $ toIdeResult (Right value) diff --git a/plugins/hls-code-range-plugin/test/Ide/Plugin/CodeRangeTest.hs b/plugins/hls-code-range-plugin/test/Ide/Plugin/CodeRangeTest.hs index 8495b1ee4d..1157b03930 100644 --- a/plugins/hls-code-range-plugin/test/Ide/Plugin/CodeRangeTest.hs +++ b/plugins/hls-code-range-plugin/test/Ide/Plugin/CodeRangeTest.hs @@ -66,38 +66,37 @@ testTree = -- General test testCase "Test General Code Block" $ check (mkCodeRange (Position 1 1) (Position 5 10) [] CodeKindRegion) - [FoldingRange 1 (Just 1) 5 (Just 10) (Just FoldingRangeRegion)], + [], -- Tests for code kind testCase "Test Code Kind Region" $ check - (mkCodeRange (Position 1 1) (Position 5 10) [] CodeKindRegion) - [FoldingRange 1 (Just 1) 5 (Just 10) (Just FoldingRangeRegion)], + (mkCodeRange (Position 1 1) (Position 5 10) [ + mkCodeRange (Position 1 2) (Position 3 6) [] CodeKindRegion + ] CodeKindRegion) + [FoldingRange 1 (Just 2) 3 (Just 6) (Just FoldingRangeRegion)], testCase "Test Code Kind Comment" $ check - (mkCodeRange (Position 1 1) (Position 5 10) [] CodeKindComment) - [FoldingRange 1 (Just 1) 5 (Just 10) (Just FoldingRangeComment)], + (mkCodeRange (Position 1 1) (Position 5 10) [ + mkCodeRange (Position 1 2) (Position 3 6) [] CodeKindComment + ] CodeKindRegion) + [FoldingRange 1 (Just 2) 3 (Just 6) (Just FoldingRangeComment)], testCase "Test Code Kind Import" $ check - (mkCodeRange (Position 1 1) (Position 5 10) [] CodeKindImports) - [FoldingRange 1 (Just 1) 5 (Just 10) (Just FoldingRangeImports)], + (mkCodeRange (Position 1 1) (Position 5 10) [ + mkCodeRange (Position 1 2) (Position 3 6) [] CodeKindImports + ] CodeKindRegion) + [FoldingRange 1 (Just 2) 3 (Just 6) (Just FoldingRangeImports)], -- Test for Code Portions with children testCase "Test Children" $ check (mkCodeRange (Position 1 1) (Position 5 10) [ - mkCodeRange (Position 1 2) (Position 3 6) [] CodeKindRegion, + mkCodeRange (Position 1 2) (Position 3 6) [ + mkCodeRange (Position 1 3) (Position 1 5) [] CodeKindRegion + ] CodeKindRegion, mkCodeRange (Position 3 7) (Position 5 10) [] CodeKindRegion ] CodeKindRegion) - [FoldingRange 1 (Just 1) 5 (Just 10) (Just FoldingRangeRegion), - FoldingRange 1 (Just 2) 3 (Just 6) (Just FoldingRangeRegion), - FoldingRange 3 (Just 7) 5 (Just 10) (Just FoldingRangeRegion)], - - -- Single line returns [] because single line ranges need not be folded - testCase "Test Single Line" $ check - (mkCodeRange (Position 1 0) (Position 1 15) [] CodeKindRegion) - [FoldingRange 1 (Just 0) 1 (Just 15) (Just FoldingRangeRegion)], - - -- MultiLine imports - testCase "MultiLine Imports" $ check - (mkCodeRange (Position 1 0) (Position 5 15) [] CodeKindImports) - [FoldingRange 1 (Just 0) 5 (Just 15) (Just FoldingRangeImports)] + [ FoldingRange 1 (Just 2) 3 (Just 6) (Just FoldingRangeRegion), + FoldingRange 1 (Just 3) 1 (Just 5) (Just FoldingRangeRegion), + FoldingRange 3 (Just 7) 5 (Just 10) (Just FoldingRangeRegion) + ] ], testGroup "createFoldingRange" $ diff --git a/plugins/hls-code-range-plugin/test/Main.hs b/plugins/hls-code-range-plugin/test/Main.hs index 1738c41fbe..5ad43de5f2 100644 --- a/plugins/hls-code-range-plugin/test/Main.hs +++ b/plugins/hls-code-range-plugin/test/Main.hs @@ -27,8 +27,9 @@ main = do defaultTestRunner $ testGroup "Code Range" [ testGroup "Integration Tests" [ - makeSelectionRangeGoldenTest recorder "Import" [(4, 36), (1, 8)], - makeSelectionRangeGoldenTest recorder "Function" [(5, 19), (5, 12), (4, 4), (3, 5)], + selectionRangeGoldenTest recorder "Import" [(4, 36), (1, 8)], + selectionRangeGoldenTest recorder "Function" [(5, 19), (5, 12), (4, 4), (3, 5)], + selectionRangeGoldenTest recorder "Empty" [(1, 5)], foldingRangeGoldenTest recorder "Function" ], testGroup "Unit Tests" [ @@ -37,8 +38,8 @@ main = do ] ] -makeSelectionRangeGoldenTest :: Recorder (WithPriority Log) -> TestName -> [(UInt, UInt)] -> TestTree -makeSelectionRangeGoldenTest recorder testName positions = goldenGitDiff testName (testDataDir testName <.> "golden" <.> "txt") $ do +selectionRangeGoldenTest :: Recorder (WithPriority Log) -> TestName -> [(UInt, UInt)] -> TestTree +selectionRangeGoldenTest recorder testName positions = goldenGitDiff testName (testDataDir testName <.> "golden" <.> "txt") $ do res <- runSessionWithServer (plugin recorder) testDataDir $ do doc <- openDoc (testName <.> "hs") "haskell" resp <- request STextDocumentSelectionRange $ SelectionRangeParams Nothing Nothing doc diff --git a/plugins/hls-code-range-plugin/test/testdata/folding-range/Empty.golden.txt b/plugins/hls-code-range-plugin/test/testdata/folding-range/Empty.golden.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/hls-code-range-plugin/test/testdata/folding-range/Empty.hs b/plugins/hls-code-range-plugin/test/testdata/folding-range/Empty.hs new file mode 100644 index 0000000000..444d0ce37b --- /dev/null +++ b/plugins/hls-code-range-plugin/test/testdata/folding-range/Empty.hs @@ -0,0 +1 @@ +module Empty where diff --git a/plugins/hls-code-range-plugin/test/testdata/folding-range/hie.yaml b/plugins/hls-code-range-plugin/test/testdata/folding-range/hie.yaml index 22a5941a9b..1a62ad9a94 100644 --- a/plugins/hls-code-range-plugin/test/testdata/folding-range/hie.yaml +++ b/plugins/hls-code-range-plugin/test/testdata/folding-range/hie.yaml @@ -2,3 +2,4 @@ cradle: direct: arguments: - "Function" + - "Empty" diff --git a/plugins/hls-code-range-plugin/test/testdata/selection-range/Empty.golden.txt b/plugins/hls-code-range-plugin/test/testdata/selection-range/Empty.golden.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/hls-code-range-plugin/test/testdata/selection-range/Empty.hs b/plugins/hls-code-range-plugin/test/testdata/selection-range/Empty.hs new file mode 100644 index 0000000000..444d0ce37b --- /dev/null +++ b/plugins/hls-code-range-plugin/test/testdata/selection-range/Empty.hs @@ -0,0 +1 @@ +module Empty where diff --git a/plugins/hls-code-range-plugin/test/testdata/selection-range/hie.yaml b/plugins/hls-code-range-plugin/test/testdata/selection-range/hie.yaml index bf7a576fe2..dd72f7881e 100644 --- a/plugins/hls-code-range-plugin/test/testdata/selection-range/hie.yaml +++ b/plugins/hls-code-range-plugin/test/testdata/selection-range/hie.yaml @@ -3,3 +3,4 @@ cradle: arguments: - "Import" - "Function" + - "Empty" From c1d8f7816011bb32a034713eea1bfeffd7492b23 Mon Sep 17 00:00:00 2001 From: Javier Neira Date: Thu, 29 Sep 2022 17:59:20 +0200 Subject: [PATCH 144/213] Remove myself from codeowners (#3230) Cause i am getting lot of notifications i dont have time to attend Co-authored-by: Michael Peyton Jones --- CODEOWNERS | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 61dfd9a206..627b4f1361 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -2,8 +2,8 @@ /ghcide @pepeiborra /ghcide/session-loader @pepeiborra @fendor /hls-graph @pepeiborra -/hls-plugin-api @jneira @berberman -/hls-test-utils @jneira +/hls-plugin-api @berberman +/hls-test-utils /hie-compat # Plugins @@ -17,7 +17,7 @@ /plugins/hls-fourmolu-plugin @georgefst /plugins/hls-gadt-plugin @July541 /plugins/hls-haddock-comments-plugin @berberman @kokobd -/plugins/hls-hlint-plugin @jneira @eddiemundo +/plugins/hls-hlint-plugin @eddiemundo /plugins/hls-module-name-plugin /plugins/hls-ormolu-plugin @georgefst /plugins/hls-pragmas-plugin @berberman @Ailrun @eddiemundo @@ -35,14 +35,14 @@ /shake-bench @pepeiborra # Docs -/docs @michaelpj @jneira +/docs @michaelpj # CI -/.circleci @jneira @Anton-Latukha -/.github @jneira @Anton-Latukha @Ailrun -/.gitlab @jneira @hasufell +/.circleci @Anton-Latukha +/.github @Anton-Latukha @Ailrun +/.gitlab @hasufell # Build *.nix @berberman @michaelpj @guibou -*.project @jneira +*.project .gitpod.* @kokobd From b4abea79a04652574173dee9d9fc381783578df4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 09:07:41 +0100 Subject: [PATCH 145/213] Bump fkirc/skip-duplicate-actions from 5.1.0 to 5.2.0 (#3239) Bumps [fkirc/skip-duplicate-actions](https://github.com/fkirc/skip-duplicate-actions) from 5.1.0 to 5.2.0. - [Release notes](https://github.com/fkirc/skip-duplicate-actions/releases) - [Commits](https://github.com/fkirc/skip-duplicate-actions/compare/v5.1.0...v5.2.0) --- updated-dependencies: - dependency-name: fkirc/skip-duplicate-actions dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/bench.yml | 2 +- .github/workflows/caching.yml | 2 +- .github/workflows/flags.yml | 2 +- .github/workflows/nix.yml | 4 ++-- .github/workflows/test.yml | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 37ddcd4f9d..e70be7ba13 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -21,7 +21,7 @@ jobs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check - uses: fkirc/skip-duplicate-actions@v5.1.0 + uses: fkirc/skip-duplicate-actions@v5.2.0 with: cancel_others: false paths_ignore: '[ "**/docs/**" diff --git a/.github/workflows/caching.yml b/.github/workflows/caching.yml index 1245229505..8a31da2873 100644 --- a/.github/workflows/caching.yml +++ b/.github/workflows/caching.yml @@ -57,7 +57,7 @@ jobs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check - uses: fkirc/skip-duplicate-actions@v5.1.0 + uses: fkirc/skip-duplicate-actions@v5.2.0 with: cancel_others: false paths_ignore: '["**/docs/**" diff --git a/.github/workflows/flags.yml b/.github/workflows/flags.yml index 352d3f9296..024ec01015 100644 --- a/.github/workflows/flags.yml +++ b/.github/workflows/flags.yml @@ -21,7 +21,7 @@ jobs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check - uses: fkirc/skip-duplicate-actions@v5.1.0 + uses: fkirc/skip-duplicate-actions@v5.2.0 with: cancel_others: false paths_ignore: '[ "**/docs/**" diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 64b1924575..749256be57 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -21,7 +21,7 @@ jobs: should_skip_build: ${{ steps.skip_check_no_nix.outputs.should_skip }} steps: - id: skip_check - uses: fkirc/skip-duplicate-actions@v5.1.0 + uses: fkirc/skip-duplicate-actions@v5.2.0 with: cancel_others: false paths_ignore: '[ "**/docs/**" @@ -36,7 +36,7 @@ jobs: , ".gitlab/**" ]' - id: skip_check_no_nix - uses: fkirc/skip-duplicate-actions@v5.1.0 + uses: fkirc/skip-duplicate-actions@v5.2.0 with: cancel_others: false paths: '[ "**.nix" ]' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bf0b04403b..e600185839 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ jobs: should_skip_ghcide: ${{ steps.skip_ghcide_check.outputs.should_skip }} steps: - id: skip_check - uses: fkirc/skip-duplicate-actions@v5.1.0 + uses: fkirc/skip-duplicate-actions@v5.2.0 with: cancel_others: false paths_ignore: '[ "**/docs/**" @@ -39,7 +39,7 @@ jobs: ]' # If we only change ghcide downstream packages we have not test ghcide itself - id: skip_ghcide_check - uses: fkirc/skip-duplicate-actions@v5.1.0 + uses: fkirc/skip-duplicate-actions@v5.2.0 with: cancel_others: false paths_ignore: '[ "hls-test-utils/**" From f143cf1520ad742220b01654cbccf1e7a95910e2 Mon Sep 17 00:00:00 2001 From: Michael Peyton Jones Date: Mon, 3 Oct 2022 11:00:06 +0100 Subject: [PATCH 146/213] Add policy on plugin support tiers (#3189) Following discussion in https://github.com/haskell/haskell-language-server/discussions/3123. This aims to make it more obvious what it means when we say we support a version of GHC. --- docs/contributing/releases.md | 3 +- docs/features.md | 2 +- docs/index.rst | 2 +- docs/support/ghc-version-support.md | 104 +++++++++++++++++++++ docs/support/index.rst | 8 ++ docs/support/plugin-support.md | 68 ++++++++++++++ docs/supported-versions.md | 136 ---------------------------- docs/troubleshooting.md | 2 +- 8 files changed, 185 insertions(+), 140 deletions(-) create mode 100644 docs/support/ghc-version-support.md create mode 100644 docs/support/index.rst create mode 100644 docs/support/plugin-support.md delete mode 100644 docs/supported-versions.md diff --git a/docs/contributing/releases.md b/docs/contributing/releases.md index ff5dfb6353..b7c47c6d4d 100644 --- a/docs/contributing/releases.md +++ b/docs/contributing/releases.md @@ -18,7 +18,8 @@ and it is being used in nix environments. ### prerelease sanity checks -- [ ] set the supported GHC versions and their corresponding cabal project-files in `bindist/ghcs` according to the [GHC version deprecation policy](../supported-versions.md#ghc-version-deprecation-policy) +- [ ] check that all plugins work according to their [support tiers](../support/plugin-support.md) +- [ ] set the supported GHC versions and their corresponding cabal project-files in `bindist/ghcs` according to the [GHC version deprecation policy](../support/ghc-version-support.md#ghc-version-deprecation-policy) - [ ] [trigger manually](https://docs.github.com/es/actions/managing-workflow-runs/manually-running-a-workflow) the hackage workflow *without* uploading the packages - [ ] trigger manually the build workflow - [ ] create a prerelease tag `${version}-check-gitlab` and push it to the [project repo in gitlab](https://gitlab.haskell.org/haskell/haskell-language-server) to check the build is fine diff --git a/docs/features.md b/docs/features.md index 4154b72b53..54c03d1b86 100644 --- a/docs/features.md +++ b/docs/features.md @@ -22,7 +22,7 @@ Many of these are standard LSP features, but a lot of special features are provi | [Rename](#rename) | `textDocument/rename` | The individual sections below also identify which [HLS plugin](./what-is-hls.md#hls-plugins) is responsible for providing the given functionality, which is useful if you want to raise an issue report or contribute! -Additionally, not all plugins are supported on all versions of GHC, see the [GHC version support page](supported-versions.md) for details. +Additionally, not all plugins are supported on all versions of GHC, see the [plugin support page](./support/plugin-support.md) for details. ## Diagnostics diff --git a/docs/index.rst b/docs/index.rst index 7a5ce0df59..0cf743688c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,7 +9,7 @@ Official Haskell Language Server implementation. :ref:`Read more Date: Wed, 5 Oct 2022 11:07:18 -0400 Subject: [PATCH 147/213] update link to supported versions (#3242) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a718927e33..d06d9f0fdd 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ The official Haskell language server (LSP) implementation. Consult the [project - [Features](https://haskell-language-server.readthedocs.io/en/latest/features.html) - [Installation](https://haskell-language-server.readthedocs.io/en/latest/installation.html) -- [Supported GHC Versions](https://haskell-language-server.readthedocs.io/en/latest/supported-versions.html) +- [Supported GHC Versions](https://haskell-language-server.readthedocs.io/en/latest/support/ghc-version-support.html) - [Configuration](https://haskell-language-server.readthedocs.io/en/latest/configuration.html) - [Troubleshooting](https://haskell-language-server.readthedocs.io/en/latest/troubleshooting.html) - [Contributing](https://haskell-language-server.readthedocs.io/en/latest/contributing/index.html) From 5bf8fe228b7566f9836c71a1d2e14454d740ef15 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 6 Oct 2022 10:29:45 +0200 Subject: [PATCH 148/213] Docs: Plugin Support hls-explicit-fixity-plugin (#3251) --- docs/support/plugin-support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/support/plugin-support.md b/docs/support/plugin-support.md index 35c69e6edc..e6963ff2b3 100644 --- a/docs/support/plugin-support.md +++ b/docs/support/plugin-support.md @@ -49,7 +49,7 @@ For example, a plugin to provide a formatter which has itself been abandoned has | `hls-class-plugin` | 2 | 9.4 | | `hls-change-type-signature-plugin` | 2 | | | `hls-eval-plugin` | 2 | 9.4 | -| `hls-expliit-fixity-plugin` | 2 | | +| `hls-explicit-fixity-plugin` | 2 | | | `hls-floskell-plugin` | 2 | 9.4 | | `hls-fourmolu-plugin` | 2 | 9.4 | | `hls-gadt-plugin` | 2 | 9.4 | From b7e59d02e0b534927dc7069b52a4b0fe6524a98e Mon Sep 17 00:00:00 2001 From: Michael Peyton Jones Date: Thu, 6 Oct 2022 09:45:37 +0100 Subject: [PATCH 149/213] GCH -> GHC (#3252) --- docs/support/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/support/index.rst b/docs/support/index.rst index db48f91789..1005e51b24 100644 --- a/docs/support/index.rst +++ b/docs/support/index.rst @@ -1,4 +1,4 @@ -GCH and Plugin Support +GHC and Plugin Support ====================== .. toctree:: From 8fd2b89a018dc58cd0b95e204f8be9ecf6f4b67d Mon Sep 17 00:00:00 2001 From: Vilem <17603372+buggymcbugfix@users.noreply.github.com> Date: Thu, 6 Oct 2022 11:36:25 +0200 Subject: [PATCH 150/213] Fix dead link to supported GHC versions (#3244) Co-authored-by: Michael Peyton Jones From 73b02ef3f76f41ab85ea02893e10c38894f89521 Mon Sep 17 00:00:00 2001 From: Akshay Mankar Date: Fri, 7 Oct 2022 15:19:53 +0200 Subject: [PATCH 151/213] Fix nix developement shell (#3257) Upgrade a few packages which needed upgrade and avoid building hiedb in nix as it has a dependency on a package in this repository (hie-compat). --- flake.lock | 73 ++++++++++++++++++++++++++++++++++++++++++++---------- flake.nix | 36 +++++++++++++++++++++++---- 2 files changed, 91 insertions(+), 18 deletions(-) diff --git a/flake.lock b/flake.lock index a26965d76a..aa439d1b7e 100644 --- a/flake.lock +++ b/flake.lock @@ -53,6 +53,18 @@ "url": "https://hackage.haskell.org/package/constraints-extras-0.3.2.1/constraints-extras-0.3.2.1.tar.gz" } }, + "entropy": { + "flake": false, + "locked": { + "narHash": "sha256-Oj0vftbS7Pau7OzdMrzRPghqwEiimwQbt0w59cMcH98=", + "type": "tarball", + "url": "https://hackage.haskell.org/package/entropy-0.4.1.10/entropy-0.4.1.10.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://hackage.haskell.org/package/entropy-0.4.1.10/entropy-0.4.1.10.tar.gz" + } + }, "flake-compat": { "flake": false, "locked": { @@ -108,6 +120,18 @@ "url": "https://hackage.haskell.org/package/fourmolu-0.3.0.0/fourmolu-0.3.0.0.tar.gz" } }, + "ghc-check": { + "flake": false, + "locked": { + "narHash": "sha256-pmmQMrk6X00+zbsstV49w/Es9+V9gssrXzJoub2ReEs=", + "type": "tarball", + "url": "https://hackage.haskell.org/package/ghc-check-0.5.0.8/ghc-check-0.5.0.8.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://hackage.haskell.org/package/ghc-check-0.5.0.8/ghc-check-0.5.0.8.tar.gz" + } + }, "ghc-exactprint": { "flake": false, "locked": { @@ -160,6 +184,18 @@ "url": "https://hackage.haskell.org/package/hie-bios-0.11.0/hie-bios-0.11.0.tar.gz" } }, + "hiedb": { + "flake": false, + "locked": { + "narHash": "sha256-Ny9Ya7Y8GGdBh8r2cryQfK4XZj2dIrYQpaB8dTNQ3KI=", + "type": "tarball", + "url": "https://hackage.haskell.org/package/hiedb-0.4.2.0/hiedb-0.4.2.0.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://hackage.haskell.org/package/hiedb-0.4.2.0/hiedb-0.4.2.0.tar.gz" + } + }, "hlint": { "flake": false, "locked": { @@ -199,30 +235,37 @@ "lsp": { "flake": false, "locked": { - "lastModified": 1662291729, - "narHash": "sha256-KlL38v/75G9zrW7+IiUeiCxFfLJGm/EdFeWQRUikab8=", - "owner": "haskell", - "repo": "lsp", - "rev": "b0f8596887088b8ab65fc1015c773f45b47234ae", - "type": "github" + "narHash": "sha256-g5R34SVz0kRD5zpODNsaaaIJOHty10cTS6ZDPi4s8pc=", + "type": "tarball", + "url": "https://hackage.haskell.org/package/lsp-1.6.0.0/lsp-1.6.0.0.tar.gz" }, "original": { - "owner": "haskell", - "repo": "lsp", - "rev": "b0f8596887088b8ab65fc1015c773f45b47234ae", - "type": "github" + "type": "tarball", + "url": "https://hackage.haskell.org/package/lsp-1.6.0.0/lsp-1.6.0.0.tar.gz" } }, "lsp-test": { "flake": false, "locked": { - "narHash": "sha256-TXRy/VT94Cn0BPtiL65c7UqahyJZgUtBQQgESZacrdY=", + "narHash": "sha256-HhFAdNvmnnnCzsKfTWbqUyTFrCfq1n6pGKfk2R0fcUc=", + "type": "tarball", + "url": "https://hackage.haskell.org/package/lsp-test-0.14.1.0/lsp-test-0.14.1.0.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://hackage.haskell.org/package/lsp-test-0.14.1.0/lsp-test-0.14.1.0.tar.gz" + } + }, + "lsp-types": { + "flake": false, + "locked": { + "narHash": "sha256-QSixsrCvsWlckG/LLF1z8LsHhqaXxVAxOPIA1NxjVT4=", "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-test-0.14.0.3/lsp-test-0.14.0.3.tar.gz" + "url": "https://hackage.haskell.org/package/lsp-types-1.6.0.0/lsp-types-1.6.0.0.tar.gz" }, "original": { "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-test-0.14.0.3/lsp-test-0.14.0.3.tar.gz" + "url": "https://hackage.haskell.org/package/lsp-types-1.6.0.0/lsp-types-1.6.0.0.tar.gz" } }, "nixpkgs": { @@ -271,19 +314,23 @@ "all-cabal-hashes-unpacked": "all-cabal-hashes-unpacked", "brittany-01312": "brittany-01312", "constraints-extras": "constraints-extras", + "entropy": "entropy", "flake-compat": "flake-compat", "flake-utils": "flake-utils", "fourmolu": "fourmolu", "fourmolu-0300": "fourmolu-0300", + "ghc-check": "ghc-check", "ghc-exactprint": "ghc-exactprint", "ghc-exactprint-150": "ghc-exactprint-150", "gitignore": "gitignore", "hie-bios": "hie-bios", + "hiedb": "hiedb", "hlint": "hlint", "hlint-34": "hlint-34", "implicit-hie-cradle": "implicit-hie-cradle", "lsp": "lsp", "lsp-test": "lsp-test", + "lsp-types": "lsp-types", "nixpkgs": "nixpkgs", "ptr-poker": "ptr-poker", "retrie": "retrie", diff --git a/flake.nix b/flake.nix index d417f444db..0ad8731a9c 100644 --- a/flake.nix +++ b/flake.nix @@ -28,11 +28,15 @@ # List of hackage dependencies lsp = { - url = "github:haskell/lsp/b0f8596887088b8ab65fc1015c773f45b47234ae"; + url = "https://hackage.haskell.org/package/lsp-1.6.0.0/lsp-1.6.0.0.tar.gz"; + flake = false; + }; + lsp-types = { + url = "https://hackage.haskell.org/package/lsp-types-1.6.0.0/lsp-types-1.6.0.0.tar.gz"; flake = false; }; lsp-test = { - url = "https://hackage.haskell.org/package/lsp-test-0.14.0.3/lsp-test-0.14.0.3.tar.gz"; + url = "https://hackage.haskell.org/package/lsp-test-0.14.1.0/lsp-test-0.14.1.0.tar.gz"; flake = false; }; ghc-exactprint-150 = { @@ -43,6 +47,10 @@ url = "https://hackage.haskell.org/package/ghc-exactprint-1.4.1/ghc-exactprint-1.4.1.tar.gz"; flake = false; }; + ghc-check = { + url = "https://hackage.haskell.org/package/ghc-check-0.5.0.8/ghc-check-0.5.0.8.tar.gz"; + flake = false; + }; constraints-extras = { url = "https://hackage.haskell.org/package/constraints-extras-0.3.2.1/constraints-extras-0.3.2.1.tar.gz"; flake = false; @@ -91,6 +99,14 @@ url = "https://hackage.haskell.org/package/hie-bios-0.11.0/hie-bios-0.11.0.tar.gz"; flake = false; }; + entropy = { + url = "https://hackage.haskell.org/package/entropy-0.4.1.10/entropy-0.4.1.10.tar.gz"; + flake = false; + }; + hiedb = { + url = "https://hackage.haskell.org/package/hiedb-0.4.2.0/hiedb-0.4.2.0.tar.gz"; + flake = false; + }; }; outputs = inputs@{ self, nixpkgs, flake-compat, flake-utils, gitignore, all-cabal-hashes-unpacked, ... }: @@ -138,6 +154,13 @@ hls-plugin-api = ./hls-plugin-api; hls-test-utils = ./hls-test-utils; ghcide-test-utils = ./ghcide/test; + # hiedb depends on hie-compact, which is part of this repository. If + # cabal inside the nix development shell tries to use the hiedb + # compiled inside nix, it thinks that this package is broken and + # does nothing. Adding this here ensures that hiedb compiled in nix + # is not available to cabal and then cabal downloads hiedb from + # hackage and compiles it. + hiedb = inputs.hiedb; } // pluginSourceDirs; # Tweak our packages @@ -149,12 +172,15 @@ # GHCIDE requires hie-bios ^>=0.9.1 hie-bios = hself.callCabal2nix "hie-bios" inputs.hie-bios {}; - lsp = hsuper.callCabal2nix "lsp" "${inputs.lsp}/lsp" {}; - lsp-types = hsuper.callCabal2nix "lsp-types" "${inputs.lsp}/lsp-types" {}; + lsp = hsuper.callCabal2nix "lsp" inputs.lsp {}; + lsp-types = hsuper.callCabal2nix "lsp-types" inputs.lsp-types {}; lsp-test = hsuper.callCabal2nix "lsp-test" inputs.lsp-test {}; - implicit-hie-cradle = hself.callCabal2nix "implicit-hie-cradle" inputs.implicit-hie-cradle {}; + entropy = hsuper.callCabal2nix "entropy" inputs.entropy {}; + hiedb = hsuper.callCabal2nix "hiedb" inputs.hiedb {}; + implicit-hie-cradle = hself.callCabal2nix "implicit-hie-cradle" inputs.implicit-hie-cradle {}; + ghc-check = hself.callCabal2nix "ghc-check" inputs.ghc-check {}; # https://github.com/NixOS/nixpkgs/issues/140774 ormolu = if final.system == "aarch64-darwin" From d8e1e75241a6ad2408ab9ddabc4cda0884de1007 Mon Sep 17 00:00:00 2001 From: Kobayashi Date: Sat, 8 Oct 2022 14:10:57 +0800 Subject: [PATCH 152/213] Move new imports down the code action list (#3235) * move new imports to the tail * remove LANGUAGE CPP Co-authored-by: Pepe Iborra --- .../src/Development/IDE/Plugin/CodeAction.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index 9775809e63..3c9437719d 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -137,13 +137,13 @@ iePluginDescriptor recorder plId = wrap suggestExportUnusedTopBinding , wrap suggestModuleTypo , wrap suggestFixConstructorImport - , wrap suggestNewImport #if !MIN_VERSION_ghc(9,3,0) , wrap suggestExtendImport , wrap suggestImportDisambiguation , wrap suggestNewOrExtendImportForClassMethod , wrap suggestHideShadow #endif + , wrap suggestNewImport ] plId in mkExactprintPluginDescriptor recorder $ old {pluginHandlers = pluginHandlers old <> mkPluginHandler STextDocumentCodeAction codeAction } From 595c527e432b7b87afd2a9c2cf062b6767ce979d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Dybiec?= Date: Sat, 8 Oct 2022 14:45:45 +0200 Subject: [PATCH 153/213] Update plugin tutorial (#3266) --- docs/contributing/plugin-tutorial.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/contributing/plugin-tutorial.md b/docs/contributing/plugin-tutorial.md index a53ebe0a7d..e397d9fddc 100644 --- a/docs/contributing/plugin-tutorial.md +++ b/docs/contributing/plugin-tutorial.md @@ -44,7 +44,7 @@ cabal build ``` If you run into any issues trying to build the binaries, the #haskell-language-server IRC chat room in -Freenode is always a good place to ask for help. +[Libera Chat](https://libera.chat/) is always a good place to ask for help. Once cabal is done take a note of the location of the `haskell-language-server` binary and point your LSP client to it. In VSCode this is done by editing the "Haskell Server Executable Path" setting. This way you can simply test your changes by reloading your editor after rebuilding the binary. @@ -83,7 +83,7 @@ The HLS codebase includes several plugins under the namespace `Ide.Plugin.*`, th I would recommend looking at the existing plugins for inspiration and reference. -Plugins are "linked" in the `Main` module, so we will need to add our plugin there once we have defined it: +Plugins are "linked" in the `HlsPlugins` module, so we will need to add our plugin there once we have defined it: ```haskell idePlugins = pluginDescToIdePlugins allPlugins @@ -381,7 +381,7 @@ generateLens pId uri minImports (L src imp) ## Wrapping up -There's only one haskell code change left to do at this point: "link" the plugin in the `Main` HLS module. +There's only one haskell code change left to do at this point: "link" the plugin in the `HlsPlugins` HLS module. However integrating the plugin in haskell-language-server itself will need some changes in config files. The best way is looking for the id (f.e. `hls-tactics-plugin`) of an existing plugin: - `./cabal*.project` and `./stack*.yaml`: add the plugin package in the `packages` field - `./haskell-language-server.cabal`: add a conditional block with the plugin package dependency From 6d49b343ca27e6ab6e24f2e33f3d1258f1bcd366 Mon Sep 17 00:00:00 2001 From: Zdeno Osina Date: Sun, 9 Oct 2022 10:59:48 +0200 Subject: [PATCH 154/213] Fix typos in documentation (#3274) --- docs/contributing/contributing.md | 2 +- docs/contributing/plugin-tutorial.md | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index 544c278a6c..0903b1bf31 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -196,7 +196,7 @@ See the [tutorial](./plugin-tutorial.md) on writing a plugin in HLS. When ghcide is built with the `ekg` flag, HLS opens a metrics server on port 8999 exposing GC and ghcide metrics. The ghcide metrics currently exposed are: -- `ghcide.values_count`- count of build results in the store +- `ghcide.values_count` - count of build results in the store - `ghcide.database_count` - count of build keys in the store (these two would be the same in the absence of GC) - `ghcide.build_count` - build count. A key is GC'ed if it is dirty and older than 100 builds - `ghcide.dirty_keys_count` - non transitive count of dirty build keys diff --git a/docs/contributing/plugin-tutorial.md b/docs/contributing/plugin-tutorial.md index e397d9fddc..442bb47041 100644 --- a/docs/contributing/plugin-tutorial.md +++ b/docs/contributing/plugin-tutorial.md @@ -89,14 +89,14 @@ Plugins are "linked" in the `HlsPlugins` module, so we will need to add our plug idePlugins = pluginDescToIdePlugins allPlugins where allPlugins = - [ GhcIde.descriptor "ghcide" - , Pragmas.descriptor "pragmas" + [ GhcIde.descriptor "ghcide" + , Pragmas.descriptor "pragmas" , Floskell.descriptor "floskell" , Fourmolu.descriptor "fourmolu" - , Ormolu.descriptor "ormolu" + , Ormolu.descriptor "ormolu" , StylishHaskell.descriptor "stylish-haskell" , Retrie.descriptor "retrie" - , Brittany.descriptor "brittany" + , Brittany.descriptor "brittany" , Eval.descriptor "eval" ] ``` @@ -271,7 +271,7 @@ runImportCommand _lspFuncs _state (ImportCommandParams edit) = do The code lens provider implements all the steps of the algorithm described earlier: -> 1. Request the type checking artefacts from the ghcide subsystem +> 1. Request the type checking artefacts from the ghcide subsystem > 2. Extract the actual import lists from the type checked AST, > 3. Ask GHC to produce the minimal import lists for this AST, > 4. For every import statement without a explicit import list, find out what's the minimal import list, and produce a code lens to display it together with a diff to graft the import list in. @@ -392,5 +392,4 @@ However integrating the plugin in haskell-language-server itself will need some The full code as used in this tutorial, including imports, can be found in [this Gist](https://gist.github.com/pepeiborra/49b872b2e9ad112f61a3220cdb7db967) as well as in this [branch](https://github.com/pepeiborra/ide/blob/imports-lens/src/Ide/Plugin/ImportLens.hs) I hope this has given you a taste of how easy and joyful it is to write plugins for HLS. -If you are looking for ideas for contributing, here are some cool ones found in the HLS issue tracker: -- https://github.com/haskell/haskell-language-server/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+possible+new+plugin%22 +If you are looking for ideas for contributing, here are some cool ones found in the HLS [issue tracker](https://github.com/haskell/haskell-language-server/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+possible+new+plugin%22). From 4898f5b5683df4ab66ae147f0193273ecd56ede4 Mon Sep 17 00:00:00 2001 From: Akshay Mankar Date: Sun, 9 Oct 2022 11:14:50 +0200 Subject: [PATCH 155/213] configuration-ghc-94.nix: Fix references to lsp and lsp-types source (#3265) Co-authored-by: Michael Peyton Jones --- configuration-ghc-94.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configuration-ghc-94.nix b/configuration-ghc-94.nix index 23c0d5883d..df39a67833 100644 --- a/configuration-ghc-94.nix +++ b/configuration-ghc-94.nix @@ -41,8 +41,8 @@ let integer-logarithms = hsuper.callHackage "integer-logarithms" "1.0.3.1" {}; hiedb = hsuper.callHackage "hiedb" "0.4.2.0" {}; hie-bios = hsuper.callHackage "hie-bios" "0.11.0" {}; - lsp = hsuper.callCabal2nix "lsp" "${inputs.lsp}/lsp" {}; - lsp-types = hsuper.callCabal2nix "lsp-types" "${inputs.lsp}/lsp-types" {}; + lsp = hsuper.callCabal2nix "lsp" inputs.lsp {}; + lsp-types = hsuper.callCabal2nix "lsp-types" inputs.lsp-types {}; # Re-generate HLS drv excluding some plugins haskell-language-server = From 0eb79a4695ac84d915aa8367d376f56c81e81e8b Mon Sep 17 00:00:00 2001 From: Zdeno Osina Date: Sun, 9 Oct 2022 15:38:27 +0200 Subject: [PATCH 156/213] Fix action removes ticks from TemplateHaskellQuotes (#628) (#3260) --- .../src/Development/IDE/Plugin/CodeAction.hs | 42 +++++++++++++++---- plugins/hls-refactor-plugin/test/Main.hs | 22 ++++++++++ 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index 3c9437719d..937f95147a 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -839,8 +839,13 @@ suggestAddTypeAnnotationToSatisfyContraints sourceOpt Diagnostic{_range=_range,. in [( title, edits )] -suggestReplaceIdentifier :: Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])] -suggestReplaceIdentifier contents Diagnostic{_range=_range,..} +-- | GHC strips out backticks in case of infix functions as well as single quote +-- in case of quoted name when using TemplateHaskellQuotes. Which is not desired. +-- +-- For example: +-- 1. +-- +-- @ -- File.hs:52:41: error: -- * Variable not in scope: -- suggestAcion :: Maybe T.Text -> Range -> Range @@ -852,6 +857,27 @@ suggestReplaceIdentifier contents Diagnostic{_range=_range,..} -- ‘T.isInfixOf’ (imported from Data.Text), -- ‘T.isSuffixOf’ (imported from Data.Text) -- Module ‘Data.Text’ does not export ‘isPrfixOf’. +-- @ +-- +-- * action: \`suggestAcion\` will be renamed to \`suggestAction\` keeping back ticks around the function +-- +-- 2. +-- +-- @ +-- import Language.Haskell.TH (Name) +-- foo :: Name +-- foo = 'bread +-- +-- File.hs:8:7: error: +-- Not in scope: ‘bread’ +-- * Perhaps you meant one of these: +-- ‘break’ (imported from Prelude), ‘read’ (imported from Prelude) +-- * In the Template Haskell quotation 'bread +-- @ +-- +-- * action: 'bread will be renamed to 'break keeping single quote on beginning of name +suggestReplaceIdentifier :: Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])] +suggestReplaceIdentifier contents Diagnostic{_range=_range,..} | renameSuggestions@(_:_) <- extractRenamableTerms _message = [ ("Replace with ‘" <> name <> "’", [mkRenameEdit contents _range name]) | name <- renameSuggestions ] | otherwise = [] @@ -1771,15 +1797,17 @@ extractDoesNotExportModuleName x mkRenameEdit :: Maybe T.Text -> Range -> T.Text -> TextEdit -mkRenameEdit contents range name = - if maybeIsInfixFunction == Just True - then TextEdit range ("`" <> name <> "`") - else TextEdit range name +mkRenameEdit contents range name + | maybeIsInfixFunction == Just True = TextEdit range ("`" <> name <> "`") + | maybeIsTemplateFunction == Just True = TextEdit range ("'" <> name) + | otherwise = TextEdit range name where maybeIsInfixFunction = do curr <- textInRange range <$> contents pure $ "`" `T.isPrefixOf` curr && "`" `T.isSuffixOf` curr - + maybeIsTemplateFunction = do + curr <- textInRange range <$> contents + pure $ "'" `T.isPrefixOf` curr -- | Extract the type and surround it in parentheses except in obviously safe cases. -- diff --git a/plugins/hls-refactor-plugin/test/Main.hs b/plugins/hls-refactor-plugin/test/Main.hs index 2a81b9085e..599d4bde29 100644 --- a/plugins/hls-refactor-plugin/test/Main.hs +++ b/plugins/hls-refactor-plugin/test/Main.hs @@ -609,6 +609,28 @@ renameActionTests = testGroup "rename actions" , "foo x y = x `monus` y" ] liftIO $ expectedContentAfterAction @=? contentAfterAction + , testSession "change template function" $ do + let content = T.unlines + [ "{-# LANGUAGE TemplateHaskellQuotes #-}" + , "module Testing where" + , "import Language.Haskell.TH (Name)" + , "foo :: Name" + , "foo = 'bread" + ] + doc <- createDoc "Testing.hs" "haskell" content + diags <- waitForDiagnostics + actionsOrCommands <- getCodeActions doc (Range (Position 4 6) (Position 4 12)) + [fixTypo] <- pure [action | InR action@CodeAction{ _title = actionTitle } <- actionsOrCommands, "break" `T.isInfixOf` actionTitle ] + executeCodeAction fixTypo + contentAfterAction <- documentContents doc + let expectedContentAfterAction = T.unlines + [ "{-# LANGUAGE TemplateHaskellQuotes #-}" + , "module Testing where" + , "import Language.Haskell.TH (Name)" + , "foo :: Name" + , "foo = 'break" + ] + liftIO $ expectedContentAfterAction @=? contentAfterAction ] typeWildCardActionTests :: TestTree From 050497a19f0c40936169b39506fce7c007595b83 Mon Sep 17 00:00:00 2001 From: Chrizzl Date: Sun, 9 Oct 2022 19:32:17 +0200 Subject: [PATCH 157/213] Exclude the implicit prelude import (#2798) (#3277) --- .../src/Ide/Plugin/ExplicitImports.hs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/plugins/hls-explicit-imports-plugin/src/Ide/Plugin/ExplicitImports.hs b/plugins/hls-explicit-imports-plugin/src/Ide/Plugin/ExplicitImports.hs index 9c96678d5a..aedacd67c4 100644 --- a/plugins/hls-explicit-imports-plugin/src/Ide/Plugin/ExplicitImports.hs +++ b/plugins/hls-explicit-imports-plugin/src/Ide/Plugin/ExplicitImports.hs @@ -118,7 +118,7 @@ lensProvider -- haskell-lsp provides conversion functions | Just nfp <- uriToNormalizedFilePath $ toNormalizedUri _uri = liftIO $ do - mbMinImports <- runAction "" state $ useWithStale MinimalImports nfp + mbMinImports <- runAction "MinimalImports" state $ useWithStale MinimalImports nfp case mbMinImports of -- Implement the provider logic: -- for every import, if it's lacking a explicit list, generate a code lens @@ -212,6 +212,7 @@ minimalImportsRule recorder = define (cmapWithPrio LogShake recorder) $ \Minimal Map.fromList [ (realSrcSpanStart l, printOutputable i) | L (locA -> RealSrcSpan l _) i <- fromMaybe [] mbMinImports + , not (isImplicitPrelude i) ] res = [ (i, Map.lookup (realSrcSpanStart l) importsMap) @@ -219,6 +220,15 @@ minimalImportsRule recorder = define (cmapWithPrio LogShake recorder) $ \Minimal , RealSrcSpan l _ <- [getLoc i] ] return ([], MinimalImportsResult res <$ mbMinImports) + where + isImplicitPrelude :: (Outputable a) => a -> Bool + isImplicitPrelude importDecl = + T.isPrefixOf implicitPreludeImportPrefix (printOutputable importDecl) + +-- | This is the prefix of an implicit prelude import which should be ignored, +-- when considering the minimal imports rule +implicitPreludeImportPrefix :: T.Text +implicitPreludeImportPrefix = "import (implicit) Prelude" -------------------------------------------------------------------------------- From 21c8e9b83e620484cfc405a1e4d32eacc215b333 Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 10 Oct 2022 13:47:42 +0200 Subject: [PATCH 158/213] Hlint: A handful of fixes to hints (#3259) * CI: rwe/actions-hlint-run v2 * fmt script: Fix missing directories Otherwise will fail * Hlint: Automatically fix warnings via apply-refact * Hlint: Do not suggest fromMaybe Co-authored-by: Michael Peyton Jones --- .github/workflows/hlint.yml | 2 +- .hlint.yaml | 29 ++++++++++--------- bench/Main.hs | 1 - exe/Wrapper.hs | 4 +-- fmt.sh | 2 +- ghcide-bench/src/Experiments.hs | 2 +- ghcide/src/Development/IDE/Core/Compile.hs | 6 ++-- ghcide/src/Development/IDE/Core/Rules.hs | 2 +- ghcide/src/Development/IDE/Main.hs | 2 +- hie-compat/src-ghc86/Compat/HieTypes.hs | 2 +- hie-compat/src-ghc86/Compat/HieUtils.hs | 2 +- .../IDE/Graph/Internal/Database.hs | 2 +- hls-plugin-api/src/Ide/Plugin/ConfigUtils.hs | 6 ++-- .../hls-alternate-number-format-plugin.cabal | 1 + .../src/Ide/Plugin/Conversion.hs | 9 +++--- .../src/Ide/Plugin/CodeRange.hs | 3 +- .../src/Ide/Plugin/ExplicitImports.hs | 2 +- .../src/Development/Benchmark/Rules.hs | 2 +- 18 files changed, 41 insertions(+), 38 deletions(-) diff --git a/.github/workflows/hlint.yml b/.github/workflows/hlint.yml index 053c49a03d..600d9ff669 100644 --- a/.github/workflows/hlint.yml +++ b/.github/workflows/hlint.yml @@ -18,7 +18,7 @@ jobs: version: '3.4' - name: 'Checking code' - uses: rwe/actions-hlint-run@v1 + uses: rwe/actions-hlint-run@v2 with: hlint-bin: "hlint --with-group=extra" fail-on: error diff --git a/.hlint.yaml b/.hlint.yaml index 324dbb2d55..369bb797f2 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -23,6 +23,7 @@ - ignore: {name: "Use camelCase"} - ignore: {name: "Use uncurry"} - ignore: {name: "Avoid lambda using `infix`"} +- ignore: {name: "Replace case with fromMaybe"} # Gives at least one suggestion we don't like. - ignore: {name: "Use <=<"} @@ -65,11 +66,11 @@ - Wingman.Types - AutoTupleSpec - name: unsafeInterleaveIO - within: + within: - Development.IDE.LSP.LanguageServer - {name: unsafeDupablePerformIO, within: []} - name: unsafeCoerce - within: + within: - Ide.Plugin.Eval.Code - Development.IDE.Core.Compile - Development.IDE.Types.Shake @@ -85,12 +86,12 @@ - Compat.HieBin # Partial functions - + # We need to check fucntions which # are typically exported multiple ways under both names, # see https://github.com/ndmitchell/hlint/issues/1389 - name: [Prelude.head, Data.List.head] - within: + within: - Main - Experiments - Development.Benchmark.Rules @@ -116,7 +117,7 @@ - Wingman.Tactics - name: [Prelude.tail, Data.List.tail] - within: + within: - Main - Development.Benchmark.Rules - Development.IDE.Plugin.CodeAction @@ -125,7 +126,7 @@ - UnificationSpec - name: [Prelude.last, Data.List.last] - within: + within: - Main - Development.IDE.Plugin.CodeAction - Development.IDE.Plugin.CodeAction.ExactPrint @@ -136,7 +137,7 @@ - Ide.Plugin.Eval.CodeLens - name: [Prelude.init, Data.List.init] - within: + within: - Main - Development.IDE.Spans.Common - Ide.PluginUtils @@ -151,7 +152,7 @@ within: [] - name: ["Prelude.!!", "Data.List.!!"] - within: + within: - Main - Experiments - FunctionalCodeAction @@ -171,11 +172,11 @@ within: [] - name: Data.Foldable.foldr1 - within: + within: - Wingman.Tactics - - name: Data.Maybe.fromJust - within: + - name: Data.Maybe.fromJust + within: - Experiments - Main - MultipleImports @@ -193,7 +194,7 @@ - Ide.Plugin.Class - name: "Data.Map.!" - within: + within: - Wingman.LanguageServer - name: "Data.IntMap.!" @@ -210,7 +211,7 @@ # Debug.Trace, because that module also # includes the eventlog tracing functions, # which are legitimate to use. - - name: + - name: - Debug.Trace.trace - Debug.Trace.traceId - Debug.Trace.traceShow @@ -220,7 +221,7 @@ - Debug.Trace.traceM - Debug.Trace.traceShowM - Debug.Trace.putTraceMsg - within: + within: - Development.IDE.Core.Compile - Development.IDE.Graph.Internal.Database - Development.IDE.GHC.Util diff --git a/bench/Main.hs b/bench/Main.hs index 97d01f9537..2318d4a5e7 100644 --- a/bench/Main.hs +++ b/bench/Main.hs @@ -44,7 +44,6 @@ {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE TypeFamilies #-} {-# OPTIONS -Wno-orphans #-} -{-# LANGUAGE PackageImports #-} import Control.Lens (preview, (^.)) import Control.Monad.Extra diff --git a/exe/Wrapper.hs b/exe/Wrapper.hs index b0a6484c85..b64c743cb7 100644 --- a/exe/Wrapper.hs +++ b/exe/Wrapper.hs @@ -12,11 +12,11 @@ module Main where import Control.Monad.Extra -import Data.Char (isSpace) import Data.Default import Data.Either.Extra (eitherToMaybe) import Data.Foldable import Data.List +import Data.List.Extra (trimEnd) import Data.Void import qualified Development.IDE.Session as Session import qualified HIE.Bios.Environment as HieBios @@ -232,7 +232,7 @@ findProjectCradle' log = do trim :: String -> String trim s = case lines s of [] -> s - ls -> dropWhileEnd isSpace $ last ls + ls -> trimEnd $ last ls data WrapperSetupError = FailedToObtainGhcVersion (ActionName Void) CradleError diff --git a/fmt.sh b/fmt.sh index c469a6be2c..1884d57e57 100755 --- a/fmt.sh +++ b/fmt.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash set -eou pipefail -curl -sSL https://raw.github.com/ndmitchell/hlint/master/misc/run.sh | sh -s ghcide/src ghcide/exe ghcide/bench/lib ghcide/bench/exe ghcide/bench/hist shake-bench/src ghcide/test/exe --with-group=extra --hint=ghcide/.hlint.yaml +curl -sSL https://raw.github.com/ndmitchell/hlint/master/misc/run.sh | sh -s ghcide/src ghcide/exe ghcide-bench/exe shake-bench/src ghcide/test/exe --with-group=extra --hint=.hlint.yaml diff --git a/ghcide-bench/src/Experiments.hs b/ghcide-bench/src/Experiments.hs index 1d8d6f6c5b..b6ab82226d 100644 --- a/ghcide-bench/src/Experiments.hs +++ b/ghcide-bench/src/Experiments.hs @@ -538,7 +538,7 @@ runBench runSess b = handleAny (\e -> print e >> return badRun) output (showDuration t) -- Wait for the delayed actions to finish td <- waitForBuildQueue - loop' (timeForFirstResponse <|> (Just (t,td))) (userWaits+t) (delayedWork+td) (n -1) + loop' (timeForFirstResponse <|> Just (t,td)) (userWaits+t) (delayedWork+td) (n -1) loop = loop' Nothing (runExperiment, result) <- duration $ loop 0 0 samples diff --git a/ghcide/src/Development/IDE/Core/Compile.hs b/ghcide/src/Development/IDE/Core/Compile.hs index e6094a470d..5b04963917 100644 --- a/ghcide/src/Development/IDE/Core/Compile.hs +++ b/ghcide/src/Development/IDE/Core/Compile.hs @@ -308,7 +308,7 @@ captureSplicesAndDeps TypecheckHelpers{..} env k = do mods_transitive = getTransitiveMods hsc_env needed_mods -- If we don't support multiple home units, ModuleNames are sufficient because all the units will be the same - mods_transitive_list = + mods_transitive_list = #if MIN_VERSION_ghc(9,3,0) mapMaybe nodeKeyToInstalledModule $ Set.toList mods_transitive #else @@ -362,7 +362,7 @@ captureSplicesAndDeps TypecheckHelpers{..} env k = do #endif -- Compute the transitive set of linkables required - getTransitiveMods hsc_env needed_mods + getTransitiveMods hsc_env needed_mods #if MIN_VERSION_ghc(9,3,0) = Set.unions (Set.fromList (map moduleToNodeKey mods) : [ dep | m <- mods , Just dep <- [Map.lookup (moduleToNodeKey m) (mgTransDeps (hsc_mod_graph hsc_env))] @@ -561,7 +561,7 @@ mkHiFileResultCompile se session' tcm simplified_guts = catchErrs $ do when (not $ null diffs) $ - panicDoc "verify core failed!" (vcat $ punctuate (text "\n\n") (diffs )) -- ++ [ppr binds , ppr binds'])) + panicDoc "verify core failed!" (vcat $ punctuate (text "\n\n") diffs) -- ++ [ppr binds , ppr binds'])) _ -> pure () pure ([], Just $! mkHiFileResult ms final_iface details (tmrRuntimeModules tcm) core_file) diff --git a/ghcide/src/Development/IDE/Core/Rules.hs b/ghcide/src/Development/IDE/Core/Rules.hs index 72313a4661..5cb56379a8 100644 --- a/ghcide/src/Development/IDE/Core/Rules.hs +++ b/ghcide/src/Development/IDE/Core/Rules.hs @@ -697,7 +697,7 @@ typeCheckRuleDefinition hsc pm = do unlift <- askUnliftIO let dets = TypecheckHelpers - { getLinkablesToKeep = unliftIO unlift $ currentLinkables + { getLinkablesToKeep = unliftIO unlift currentLinkables , getLinkables = unliftIO unlift . uses_ GetLinkable } addUsageDependencies $ liftIO $ diff --git a/ghcide/src/Development/IDE/Main.hs b/ghcide/src/Development/IDE/Main.hs index a344a46dd5..3f27e395aa 100644 --- a/ghcide/src/Development/IDE/Main.hs +++ b/ghcide/src/Development/IDE/Main.hs @@ -283,7 +283,7 @@ testing recorder logger = hlsPlugins = pluginDescToIdePlugins $ idePluginsToPluginDesc argsHlsPlugins ++ [Test.blockCommandDescriptor "block-command", Test.plugin] - ideOptions = \config sessionLoader -> + ideOptions config sessionLoader = let defOptions = argsIdeOptions config sessionLoader in diff --git a/hie-compat/src-ghc86/Compat/HieTypes.hs b/hie-compat/src-ghc86/Compat/HieTypes.hs index d17ec9a521..d3ed1170fa 100644 --- a/hie-compat/src-ghc86/Compat/HieTypes.hs +++ b/hie-compat/src-ghc86/Compat/HieTypes.hs @@ -5,7 +5,7 @@ For more information see https://gitlab.haskell.org/ghc/ghc/wikis/hie-files -} {-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE TypeSynonymInstances #-} + {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -Wno-orphans #-} diff --git a/hie-compat/src-ghc86/Compat/HieUtils.hs b/hie-compat/src-ghc86/Compat/HieUtils.hs index 367e0adf2e..4eb5451913 100644 --- a/hie-compat/src-ghc86/Compat/HieUtils.hs +++ b/hie-compat/src-ghc86/Compat/HieUtils.hs @@ -127,7 +127,7 @@ recoverFullType i m = go i getTypeIndex :: Type -> State HieTypeState TypeIndex getTypeIndex t - | otherwise = do + = do tm <- gets tyMap case lookupTypeMap tm t of Just i -> return i diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Database.hs b/hls-graph/src/Development/IDE/Graph/Internal/Database.hs index f936687ebb..5bb4ed9ff4 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Database.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Database.hs @@ -1,7 +1,7 @@ -- We deliberately want to ensure the function we add to the rule database -- has the constraints we need on it when we get it out. {-# OPTIONS_GHC -Wno-redundant-constraints #-} -{-# LANGUAGE DeriveFunctor #-} + {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE RankNTypes #-} diff --git a/hls-plugin-api/src/Ide/Plugin/ConfigUtils.hs b/hls-plugin-api/src/Ide/Plugin/ConfigUtils.hs index 8e07af801d..60b700e0eb 100644 --- a/hls-plugin-api/src/Ide/Plugin/ConfigUtils.hs +++ b/hls-plugin-api/src/Ide/Plugin/ConfigUtils.hs @@ -12,7 +12,7 @@ import qualified Data.Aeson.Types as A import Data.Default (def) import qualified Data.Dependent.Map as DMap import qualified Data.Dependent.Sum as DSum -import Data.List (nub) +import Data.List.Extra (nubOrd) import Data.String (IsString (fromString)) import qualified Data.Text as T import Ide.Plugin.Config @@ -62,7 +62,7 @@ pluginsToDefaultConfig IdePlugins {..} = -- } -- genericDefaultConfig = - let x = ["diagnosticsOn" A..= True | configHasDiagnostics] <> nub (mconcat (handlersToGenericDefaultConfig <$> handlers)) + let x = ["diagnosticsOn" A..= True | configHasDiagnostics] <> nubOrd (mconcat (handlersToGenericDefaultConfig <$> handlers)) in case x of -- if the plugin has only one capability, we produce globalOn instead of the specific one; -- otherwise we don't produce globalOn at all @@ -106,7 +106,7 @@ pluginsToVSCodeExtensionSchema IdePlugins {..} = A.object $ mconcat $ singlePlug genericSchema = let x = [toKey' "diagnosticsOn" A..= schemaEntry "diagnostics" | configHasDiagnostics] - <> nub (mconcat (handlersToGenericSchema <$> handlers)) + <> nubOrd (mconcat (handlersToGenericSchema <$> handlers)) in case x of -- If the plugin has only one capability, we produce globalOn instead of the specific one; -- otherwise we don't produce globalOn at all diff --git a/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal b/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal index 041998756d..91198b3caf 100644 --- a/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal +++ b/plugins/hls-alternate-number-format-plugin/hls-alternate-number-format-plugin.cabal @@ -31,6 +31,7 @@ library aeson , base >=4.12 && < 5 , containers + , extra , ghcide ^>= 1.8 , ghc-boot-th , hls-graph diff --git a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Conversion.hs b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Conversion.hs index 523ae6d37d..a6872121af 100644 --- a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Conversion.hs +++ b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Conversion.hs @@ -23,6 +23,7 @@ module Ide.Plugin.Conversion ( import Data.Char (toUpper) import Data.List (delete) +import Data.List.Extra (enumerate, upper) import Data.Maybe (mapMaybe) import Data.Ratio (denominator, numerator) import Data.Text (Text) @@ -108,10 +109,10 @@ filterFracFormats = mapMaybe getFracFormat getFracFormat _ = Nothing intFormats :: [IntFormatType] -intFormats = [minBound .. maxBound] +intFormats = enumerate fracFormats :: [FracFormatType] -fracFormats = [minBound .. maxBound] +fracFormats = enumerate -- | Regex to match a Haskell Hex Literal hexRegex :: Text @@ -157,8 +158,8 @@ sourceToFormatType srcText toBase :: (Num a, Ord a) => (a -> ShowS) -> String -> a -> String toBase conv header n - | n < 0 = '-' : header <> map toUpper (conv (abs n) "") - | otherwise = header <> map toUpper (conv n "") + | n < 0 = '-' : header <> upper (conv (abs n) "") + | otherwise = header <> upper (conv n "") toOctal :: (Integral a, Show a) => a -> String toOctal = toBase showOct "0o" diff --git a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs index 1fef9060b1..699e51f1f7 100644 --- a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs +++ b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs @@ -19,6 +19,7 @@ import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans.Maybe (MaybeT (MaybeT), maybeToExceptT) import Data.Either.Extra (maybeToEither) +import Data.List.Extra (drop1) import Data.Maybe (fromMaybe) import Data.Vector (Vector) import qualified Data.Vector as V @@ -206,7 +207,7 @@ findPosition pos root = go Nothing root findFoldingRanges :: CodeRange -> [FoldingRange] findFoldingRanges codeRange = -- removing the first node because it folds the entire file - drop 1 $ findFoldingRangesRec codeRange + drop1 $ findFoldingRangesRec codeRange findFoldingRangesRec :: CodeRange -> [FoldingRange] findFoldingRangesRec r@(CodeRange _ children _) = diff --git a/plugins/hls-explicit-imports-plugin/src/Ide/Plugin/ExplicitImports.hs b/plugins/hls-explicit-imports-plugin/src/Ide/Plugin/ExplicitImports.hs index aedacd67c4..d3c7878629 100644 --- a/plugins/hls-explicit-imports-plugin/src/Ide/Plugin/ExplicitImports.hs +++ b/plugins/hls-explicit-imports-plugin/src/Ide/Plugin/ExplicitImports.hs @@ -316,7 +316,7 @@ abbreviateImportTitle input = oneLineText = T.unwords $ T.lines input -- Now, split at the max columns, leaving space for the summary text we're going to add -- (conservatively assuming we won't need to print a number larger than 100) - (prefix, suffix) = T.splitAt (maxColumns - (T.length (summaryText 100))) oneLineText + (prefix, suffix) = T.splitAt (maxColumns - T.length (summaryText 100)) oneLineText -- We also want to truncate the last item so we get a "clean" break, rather than half way through -- something. The conditional here is just because 'breakOnEnd' doesn't give us quite the right thing -- if there are actually no commas. diff --git a/shake-bench/src/Development/Benchmark/Rules.hs b/shake-bench/src/Development/Benchmark/Rules.hs index 7d5e4dcef9..4ffa6d22e6 100644 --- a/shake-bench/src/Development/Benchmark/Rules.hs +++ b/shake-bench/src/Development/Benchmark/Rules.hs @@ -690,7 +690,7 @@ plotDiagram includeFailed t@Diagram {traceMetric, runLogs} out = do ] ] return (lplot E.& E.plot_lines_style . E.line_width E.*~ 2) - case (runFirstReponse rl) of + case runFirstReponse rl of Just t -> E.plot $ pure $ E.vlinePlot ("First build: " ++ runVersion rl) (E.defaultPlotLineStyle E.& E.line_color E..~ c) t _ -> pure () From e176eca89776da738f729f8428c586d580ea71ef Mon Sep 17 00:00:00 2001 From: Chrizzl Date: Mon, 10 Oct 2022 15:29:28 +0200 Subject: [PATCH 159/213] Use an importance score to order the suggested import code action (#3234) (#3271) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../src/Development/IDE/Plugin/CodeAction.hs | 61 ++++++++++++++----- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index 937f95147a..7535b478ff 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -38,7 +38,6 @@ import Data.Ord (comparing) import qualified Data.Set as S import qualified Data.Text as T import qualified Data.Text.Utf16.Rope as Rope -import Data.Tuple.Extra (fst3) import Development.IDE.Core.Rules import Development.IDE.Core.RuleTypes import Development.IDE.Core.Service @@ -87,6 +86,7 @@ import Language.LSP.Types (ApplyWorkspa uriToFilePath) import Language.LSP.VFS (VirtualFile, _file_text) +import qualified Text.Fuzzy.Parallel as TFP import Text.Regex.TDFA (mrAfter, (=~), (=~~)) #if MIN_VERSION_ghc(9,2,0) @@ -99,7 +99,6 @@ import GHC (AddEpAnn (Ad EpaLocation (..), LEpaComment, LocatedA) - #else import Language.Haskell.GHC.ExactPrint.Types (Annotation (annsDP), DeltaPos, @@ -1521,35 +1520,65 @@ suggestNewImport packageExportsMap ps fileContents Diagnostic{_message} , Just (range, indent) <- newImportInsertRange ps fileContents , extendImportSuggestions <- matchRegexUnifySpaces msg "Perhaps you want to add ‘[^’]*’ to the import list in the import of ‘([^’]*)’" - = sortOn fst3 [(imp, kind, TextEdit range (imp <> "\n" <> T.replicate indent " ")) - | (kind, unNewImport -> imp) <- constructNewImportSuggestions packageExportsMap (qual <|> qual', thingMissing) extendImportSuggestions - ] + = let suggestions = nubSort + (constructNewImportSuggestions packageExportsMap (qual <|> qual', thingMissing) extendImportSuggestions) in + map (\(ImportSuggestion _ kind (unNewImport -> imp)) -> (imp, kind, TextEdit range (imp <> "\n" <> T.replicate indent " "))) suggestions where L _ HsModule {..} = astA ps suggestNewImport _ _ _ _ = [] constructNewImportSuggestions - :: ExportsMap -> (Maybe T.Text, NotInScope) -> Maybe [T.Text] -> [(CodeActionKind, NewImport)] -constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules = nubOrdOn snd + :: ExportsMap -> (Maybe T.Text, NotInScope) -> Maybe [T.Text] -> [ImportSuggestion] +constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules = nubOrd [ suggestion - | Just name <- [T.stripPrefix (maybe "" (<> ".") qual) $ notInScope thingMissing] - , identInfo <- maybe [] Set.toList $ Map.lookup name (getExportsMap exportsMap) - , canUseIdent thingMissing identInfo - , moduleNameText identInfo `notElem` fromMaybe [] notTheseModules - , suggestion <- renderNewImport identInfo + | Just name <- [T.stripPrefix (maybe "" (<> ".") qual) $ notInScope thingMissing] -- strip away qualified module names from the unknown name + , identInfo <- maybe [] Set.toList $ Map.lookup name (getExportsMap exportsMap) -- look up the modified unknown name in the export map + , canUseIdent thingMissing identInfo -- check if the identifier information retrieved can be used + , moduleNameText identInfo `notElem` fromMaybe [] notTheseModules -- check if the module of the identifier is allowed + , suggestion <- renderNewImport identInfo -- creates a list of import suggestions for the retrieved identifier information ] where - renderNewImport :: IdentInfo -> [(CodeActionKind, NewImport)] + renderNewImport :: IdentInfo -> [ImportSuggestion] renderNewImport identInfo | Just q <- qual - = [(quickFixImportKind "new.qualified", newQualImport m q)] + = [ImportSuggestion importanceScore (quickFixImportKind "new.qualified") (newQualImport m q)] | otherwise - = [(quickFixImportKind' "new" importStyle, newUnqualImport m (renderImportStyle importStyle) False) + = [ImportSuggestion importanceScore (quickFixImportKind' "new" importStyle) (newUnqualImport m (renderImportStyle importStyle) False) | importStyle <- NE.toList $ importStyles identInfo] ++ - [(quickFixImportKind "new.all", newImportAll m)] + [ImportSuggestion importanceScore (quickFixImportKind "new.all") (newImportAll m)] where + -- The importance score takes 2 metrics into account. The first being the similarity using + -- the Text.Fuzzy.Parallel.match function. The second is a factor of the relation between + -- the modules prefix import suggestion and the unknown identifier names. + importanceScore + | Just q <- qual + = let + similarityScore = fromIntegral $ unpackMatchScore (TFP.match (T.toLower q) (T.toLower m)) :: Double + (maxLength, minLength) = case (T.length q, T.length m) of + (la, lb) + | la >= lb -> (fromIntegral la, fromIntegral lb) + | otherwise -> (fromIntegral lb, fromIntegral la) + lengthPenaltyFactor = 100 * minLength / maxLength + in max 0 (floor (similarityScore * lengthPenaltyFactor)) + | otherwise + = 0 + where + unpackMatchScore pScore + | Just score <- pScore = score + | otherwise = 0 m = moduleNameText identInfo +-- | Implements a lexicographic order for import suggestions. +-- First compares the importance score in DESCENDING order. +-- If the scores are equal it compares the import names alphabetical order. +data ImportSuggestion = ImportSuggestion !Int !CodeActionKind !NewImport + deriving ( Eq ) + +instance Ord ImportSuggestion where + compare (ImportSuggestion s1 _ i1) (ImportSuggestion s2 _ i2) + | s1 == s2 = compare i1 i2 + | otherwise = flip compare s1 s2 + newtype NewImport = NewImport {unNewImport :: T.Text} deriving (Show, Eq, Ord) From 07f14e33b33039d64e243790974ef36b78f053e3 Mon Sep 17 00:00:00 2001 From: Chrizzl Date: Tue, 11 Oct 2022 10:54:10 +0200 Subject: [PATCH 160/213] Remove unlawful Ord instance and replace it by a compare function (#3271) (#3279) --- .../src/Development/IDE/Plugin/CodeAction.hs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index 7535b478ff..4901ccab05 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -1520,7 +1520,7 @@ suggestNewImport packageExportsMap ps fileContents Diagnostic{_message} , Just (range, indent) <- newImportInsertRange ps fileContents , extendImportSuggestions <- matchRegexUnifySpaces msg "Perhaps you want to add ‘[^’]*’ to the import list in the import of ‘([^’]*)’" - = let suggestions = nubSort + = let suggestions = nubSortBy simpleCompareImportSuggestion (constructNewImportSuggestions packageExportsMap (qual <|> qual', thingMissing) extendImportSuggestions) in map (\(ImportSuggestion _ kind (unNewImport -> imp)) -> (imp, kind, TextEdit range (imp <> "\n" <> T.replicate indent " "))) suggestions where @@ -1529,7 +1529,7 @@ suggestNewImport _ _ _ _ = [] constructNewImportSuggestions :: ExportsMap -> (Maybe T.Text, NotInScope) -> Maybe [T.Text] -> [ImportSuggestion] -constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules = nubOrd +constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules = nubOrdBy simpleCompareImportSuggestion [ suggestion | Just name <- [T.stripPrefix (maybe "" (<> ".") qual) $ notInScope thingMissing] -- strip away qualified module names from the unknown name , identInfo <- maybe [] Set.toList $ Map.lookup name (getExportsMap exportsMap) -- look up the modified unknown name in the export map @@ -1568,16 +1568,18 @@ constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules = | otherwise = 0 m = moduleNameText identInfo --- | Implements a lexicographic order for import suggestions. --- First compares the importance score in DESCENDING order. --- If the scores are equal it compares the import names alphabetical order. data ImportSuggestion = ImportSuggestion !Int !CodeActionKind !NewImport deriving ( Eq ) -instance Ord ImportSuggestion where - compare (ImportSuggestion s1 _ i1) (ImportSuggestion s2 _ i2) - | s1 == s2 = compare i1 i2 - | otherwise = flip compare s1 s2 +-- | Implements a lexicographic order for import suggestions that ignores the code action. +-- First it compares the importance score in DESCENDING order. +-- If the scores are equal it compares the import names alphabetical order. +-- +-- TODO: this should be a correct Ord instance but CodeActionKind does not implement a Ord +-- which would lead to an unlawful Ord instance. +simpleCompareImportSuggestion :: ImportSuggestion -> ImportSuggestion -> Ordering +simpleCompareImportSuggestion (ImportSuggestion s1 _ i1) (ImportSuggestion s2 _ i2) + = flip compare s1 s2 <> compare i1 i2 newtype NewImport = NewImport {unNewImport :: T.Text} deriving (Show, Eq, Ord) From 9b491f7bbf21eb407f38e9d56f0cddc9f004b41f Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Tue, 11 Oct 2022 14:18:49 +0200 Subject: [PATCH 161/213] Drop compatibility with GHC 8.6.5 (#3101) * Drop compatibility with GHC 8.6.5 * drop a few more bits * fixup merge Co-authored-by: Javier Neira --- .github/workflows/caching.yml | 1 - .github/workflows/flags.yml | 1 - .github/workflows/hackage.yml | 1 - .github/workflows/test.yml | 7 ------- .gitpod.Dockerfile | 1 - bindist/ghcs | 1 - docs/support/ghc-version-support.md | 2 +- ghcide/ghcide.cabal | 2 +- haskell-language-server.cabal | 2 +- hls-graph/src/Development/IDE/Graph/Internal/Types.hs | 2 -- 10 files changed, 3 insertions(+), 17 deletions(-) diff --git a/.github/workflows/caching.yml b/.github/workflows/caching.yml index 8a31da2873..ae4c9c07fe 100644 --- a/.github/workflows/caching.yml +++ b/.github/workflows/caching.yml @@ -85,7 +85,6 @@ jobs: , "9.0.2" , "8.10.7" , "8.8.4" - , "8.6.5" ] os: [ "ubuntu-latest" , "macOS-latest" diff --git a/.github/workflows/flags.yml b/.github/workflows/flags.yml index 024ec01015..a4f070a4da 100644 --- a/.github/workflows/flags.yml +++ b/.github/workflows/flags.yml @@ -48,7 +48,6 @@ jobs: , "9.0.2" , "8.10.7" , "8.8.4" - , "8.6.5" ] os: [ "ubuntu-latest" ] diff --git a/.github/workflows/hackage.yml b/.github/workflows/hackage.yml index ac939f9896..4378b8ffb7 100644 --- a/.github/workflows/hackage.yml +++ b/.github/workflows/hackage.yml @@ -41,7 +41,6 @@ jobs: ghc: [ "9.0.2" , "8.10.7" , "8.8.4" - , "8.6.5" ] exclude: - ghc: "9.0.2" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e600185839..78bcf83e15 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -64,7 +64,6 @@ jobs: , "9.0.2" , "8.10.7" , "8.8.4" - , "8.6.5" ] os: [ "ubuntu-latest" , "macOS-latest" @@ -86,9 +85,6 @@ jobs: - os: ubuntu-latest ghc: '8.8.4' test: true - - os: ubuntu-latest - ghc: '8.6.5' - test: true - os: windows-latest ghc: '9.4.2' test: true @@ -101,9 +97,6 @@ jobs: - os: windows-latest ghc: '8.10.7' test: true - - os: windows-latest - ghc: '8.6.5' - test: true # only build rest of supported ghc versions for windows - os: windows-latest ghc: '8.8.4' diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index 6deba14260..87dc2ff6a1 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -7,7 +7,6 @@ RUN sudo install-packages build-essential curl libffi-dev libffi7 libgmp-dev lib echo 'export PATH=$HOME/.cabal/bin:$HOME/.local/bin:$PATH' >> $HOME/.bashrc && \ . /home/gitpod/.ghcup/env && \ # Install all verions of GHC that HLS supports. Putting GHC into Docker image makes workspace start much faster. - ghcup install ghc 8.6.5 && \ ghcup install ghc 8.8.4 && \ ghcup install ghc 8.10.7 && \ ghcup install ghc 9.0.2 && \ diff --git a/bindist/ghcs b/bindist/ghcs index 151afa1251..d1c741c324 100644 --- a/bindist/ghcs +++ b/bindist/ghcs @@ -1,4 +1,3 @@ -8.6.5,cabal.project 8.8.4,cabal.project 8.10.7,cabal.project 9.0.2,cabal.project diff --git a/docs/support/ghc-version-support.md b/docs/support/ghc-version-support.md index 246d6036a2..da7887f1fc 100644 --- a/docs/support/ghc-version-support.md +++ b/docs/support/ghc-version-support.md @@ -32,7 +32,7 @@ Support status (see the support policy below for more details): | 8.8.4 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | full support, will be deprecated after LTS and HLS full support for ghc-9.2 | | 8.8.3 | [1.5.1](https://github.com/haskell/haskell-language-server/releases/1.5.1) | deprecated | | 8.8.2 | [1.2.0](https://github.com/haskell/haskell-language-server/releases/tag/1.2.0) | deprecated | -| 8.6.5 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | full support, will be deprecated after LTS and HLS full suppot for ghc-9.2 | +| 8.6.5 | [1.8.0.0](https://github.com/haskell/haskell-language-server/releases/tag/1.8.0.0) | deprecated | | 8.6.4 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | GHC versions not in the list have never been supported by HLS. diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index 109cb252c5..a3261e6e30 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -13,7 +13,7 @@ description: A library for building Haskell IDE's on top of the GHC API. homepage: https://github.com/haskell/haskell-language-server/tree/master/ghcide#readme bug-reports: https://github.com/haskell/haskell-language-server/issues -tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 +tested-with: GHC == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 extra-source-files: README.md CHANGELOG.md test/data/**/*.project test/data/**/*.cabal diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index d6a63b16a1..d44b072928 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -14,7 +14,7 @@ copyright: The Haskell IDE Team license: Apache-2.0 license-file: LICENSE build-type: Simple -tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 +tested-with: GHC == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 extra-source-files: README.md ChangeLog.md diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Types.hs b/hls-graph/src/Development/IDE/Graph/Internal/Types.hs index 56d2d48ac5..5bcaca0cf6 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Types.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Types.hs @@ -13,9 +13,7 @@ module Development.IDE.Graph.Internal.Types where import Control.Applicative import Control.Monad.Catch #if __GLASGOW_HASKELL__ < 808 --- Needed in GHC 8.6.5 import Control.Concurrent.STM.Stats (TVar, atomically) -import Control.Monad.Fail #else import GHC.Conc (TVar, atomically) #endif From 86e3fd6c650770f5ac6edc13389cdcdfe3284c70 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 12 Oct 2022 00:35:18 +0200 Subject: [PATCH 162/213] Cleanup GHC macros (because min version is 8.8.4) (#3281) * Drop min_version_ghc (8.8.4 is min supported) * Drop conditional glasgow_haskell cpp * Inline some imports (review feedback) * Drop hie-compat 8.6 (review feedback) * Dropping more ghc 8.6 related code and docs * Eval: Include tests that were broken for 8.6 --- docs/contributing/contributing.md | 2 - docs/contributing/plugin-tutorial.md | 2 +- docs/installation.md | 2 +- ghcide-bench/ghcide-bench.cabal | 2 +- ghcide/README.md | 2 +- ghcide/ghcide.cabal | 4 +- ghcide/src/Development/IDE/Core/Compile.hs | 2 - ghcide/src/Development/IDE/Core/Rules.hs | 3 - ghcide/src/Development/IDE/Core/Tracing.hs | 16 +- ghcide/src/Development/IDE/GHC/Compat.hs | 49 +- ghcide/src/Development/IDE/GHC/Compat/CPP.hs | 21 +- ghcide/src/Development/IDE/GHC/Compat/Core.hs | 73 +- .../src/Development/IDE/GHC/Compat/Plugins.hs | 12 +- ghcide/src/Development/IDE/GHC/CoreFile.hs | 2 +- ghcide/src/Development/IDE/Spans/AtPoint.hs | 4 - ghcide/src/Development/IDE/Spans/Pragmas.hs | 5 +- ghcide/test/ghcide-test-utils.cabal | 2 +- hie-compat/README.md | 2 +- hie-compat/hie-compat.cabal | 6 +- hie-compat/src-ghc86/Compat/HieAst.hs | 1753 ----------------- hie-compat/src-ghc86/Compat/HieBin.hs | 388 ---- hie-compat/src-ghc86/Compat/HieDebug.hs | 145 -- hie-compat/src-ghc86/Compat/HieTypes.hs | 534 ----- hie-compat/src-ghc86/Compat/HieUtils.hs | 451 ----- .../Development/IDE/Graph/Internal/Types.hs | 6 +- plugins/hls-eval-plugin/test/Main.hs | 9 +- .../src/Ide/Plugin/Retrie.hs | 4 - .../src/Wingman/LanguageServer.hs | 8 +- .../Wingman/LanguageServer/TacticProviders.hs | 12 - .../src/Wingman/StaticPlugin.hs | 8 - .../test/CodeAction/RunMetaprogramSpec.hs | 6 - test/functional/Completion.hs | 2 +- 32 files changed, 40 insertions(+), 3497 deletions(-) delete mode 100644 hie-compat/src-ghc86/Compat/HieAst.hs delete mode 100644 hie-compat/src-ghc86/Compat/HieBin.hs delete mode 100644 hie-compat/src-ghc86/Compat/HieDebug.hs delete mode 100644 hie-compat/src-ghc86/Compat/HieTypes.hs delete mode 100644 hie-compat/src-ghc86/Compat/HieUtils.hs diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index 0903b1bf31..f8f705da1c 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -64,8 +64,6 @@ To create binaries: * `nix build .#haskell-language-server-884` - GHC 8.8.4 * `nix build .#haskell-language-server-901` - GHC 9.0.1 -GHC 8.6.5 is not supported here because `nixpkgs-unstable` no longer maintains the corresponding packages set. - ## Testing The tests make use of the [Tasty](https://github.com/feuerbach/tasty) test framework. diff --git a/docs/contributing/plugin-tutorial.md b/docs/contributing/plugin-tutorial.md index 442bb47041..53bcfb1a4f 100644 --- a/docs/contributing/plugin-tutorial.md +++ b/docs/contributing/plugin-tutorial.md @@ -34,7 +34,7 @@ And here is the gist of the algorithm: ## Setup -To get started, let’s fetch the HLS repo and build it. You need at least GHC 8.6 for this: +To get started, let’s fetch the HLS repo and build it. You need at least GHC 8.8 for this: ``` git clone --recursive http://github.com/haskell/haskell-language-server hls diff --git a/docs/installation.md b/docs/installation.md index cc107a2315..7ebfbb432f 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -157,7 +157,7 @@ Homebrew users can install `haskell-language-server` using the following command brew install haskell-language-server ``` -This formula contains HLS binaries compiled with GHC versions available via Homebrew; at the moment those are: 8.6.5, 8.8.4, 8.10.7. +This formula contains HLS binaries compiled with GHC versions available via Homebrew; at the moment those are: 8.8.4, 8.10.7. You need to provide your own GHC/Cabal/Stack as required by your project, possibly via Homebrew. diff --git a/ghcide-bench/ghcide-bench.cabal b/ghcide-bench/ghcide-bench.cabal index 2a87563121..f48484de96 100644 --- a/ghcide-bench/ghcide-bench.cabal +++ b/ghcide-bench/ghcide-bench.cabal @@ -12,7 +12,7 @@ synopsis: An LSP client for running performance experiments on HLS description: An LSP client for running performance experiments on HLS homepage: https://github.com/haskell/haskell-language-server/tree/master/ghcide#readme bug-reports: https://github.com/haskell/haskell-language-server/issues -tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 +tested-with: GHC == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 source-repository head type: git diff --git a/ghcide/README.md b/ghcide/README.md index 6ef82afe6e..39f8bb8ee8 100644 --- a/ghcide/README.md +++ b/ghcide/README.md @@ -98,7 +98,7 @@ If you can't get `ghcide` working outside the editor, see [this setup troublesho ### Optimal project setup -`ghcide` has been designed to handle projects with hundreds or thousands of modules. If `ghci` can handle it, then `ghcide` should be able to handle it. The only caveat is that this currently requires GHC >= 8.6, and that the first time a module is loaded in the editor will trigger generation of support files in the background if those do not already exist. +`ghcide` has been designed to handle projects with hundreds or thousands of modules. If `ghci` can handle it, then `ghcide` should be able to handle it. The only caveat is that this currently requires GHC >= 8.8, and that the first time a module is loaded in the editor will trigger generation of support files in the background if those do not already exist. ### Using with VS Code diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index a3261e6e30..abf724fc64 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -99,7 +99,7 @@ library unliftio-core, ghc-boot-th, ghc-boot, - ghc >= 8.6, + ghc >= 8.8, ghc-check >=0.5.0.8, ghc-paths, cryptohash-sha1 >=0.11.100 && <0.12, @@ -363,7 +363,7 @@ test-suite ghcide-tests text, text-rope, unordered-containers, - if (impl(ghc >= 8.6) && impl(ghc < 9.2)) + if impl(ghc < 9.2) build-depends: record-dot-preprocessor, record-hasfield diff --git a/ghcide/src/Development/IDE/Core/Compile.hs b/ghcide/src/Development/IDE/Core/Compile.hs index 5b04963917..c8527d115d 100644 --- a/ghcide/src/Development/IDE/Core/Compile.hs +++ b/ghcide/src/Development/IDE/Core/Compile.hs @@ -1181,9 +1181,7 @@ getModSummaryFromImports env fp modTime contents = do msrModSummary = ModSummary { ms_mod = modl -#if MIN_VERSION_ghc(8,8,0) , ms_hie_date = Nothing -#endif #if MIN_VERSION_ghc(9,3,0) , ms_dyn_obj_date = Nothing , ms_ghc_prim_import = ghc_prim_import diff --git a/ghcide/src/Development/IDE/Core/Rules.hs b/ghcide/src/Development/IDE/Core/Rules.hs index 5cb56379a8..83a8e9bad8 100644 --- a/ghcide/src/Development/IDE/Core/Rules.hs +++ b/ghcide/src/Development/IDE/Core/Rules.hs @@ -62,9 +62,6 @@ module Development.IDE.Core.Rules( DisplayTHWarning(..), ) where -#if !MIN_VERSION_ghc(8,8,0) -import Control.Applicative (liftA2) -#endif import Control.Concurrent.Async (concurrently) import Control.Concurrent.Strict import Control.DeepSeq diff --git a/ghcide/src/Development/IDE/Core/Tracing.hs b/ghcide/src/Development/IDE/Core/Tracing.hs index 5aaaada98e..609134c5ab 100644 --- a/ghcide/src/Development/IDE/Core/Tracing.hs +++ b/ghcide/src/Development/IDE/Core/Tracing.hs @@ -37,18 +37,8 @@ import OpenTelemetry.Eventlog (SpanInFlight (..), addEvent, beginSpan, endSpan, setTag, withSpan) -#if MIN_VERSION_ghc(8,8,0) -otTracedProvider :: MonadUnliftIO m => PluginId -> ByteString -> m a -> m a -otTracedGarbageCollection :: (MonadMask f, MonadIO f, Show a) => ByteString -> f [a] -> f [a] -withEventTrace :: (MonadMask m, MonadIO m) => String -> ((ByteString -> m ()) -> m a) -> m a -#else -otTracedProvider :: MonadUnliftIO m => PluginId -> String -> m a -> m a -otTracedGarbageCollection :: (MonadMask f, MonadIO f, Show a) => String -> f [a] -> f [a] -withEventTrace :: (MonadMask m, MonadIO m) => String -> ((ByteString -> m ()) -> m a) -> m a -#endif -withTrace :: (MonadMask m, MonadIO m) => - String -> ((String -> String -> m ()) -> m a) -> m a +withTrace :: (MonadMask m, MonadIO m) => String -> ((String -> String -> m ()) -> m a) -> m a withTrace name act | userTracingEnabled = withSpan (fromString name) $ \sp -> do @@ -56,6 +46,7 @@ withTrace name act act setSpan' | otherwise = act (\_ _ -> pure ()) +withEventTrace :: (MonadMask m, MonadIO m) => String -> ((ByteString -> m ()) -> m a) -> m a withEventTrace name act | userTracingEnabled = withSpan (fromString name) $ \sp -> do @@ -125,6 +116,7 @@ otTracedAction key file mode result act (\sp -> act (liftIO . setTag sp "diagnostics" . encodeUtf8 . showDiagnostics )) | otherwise = act (\_ -> return ()) +otTracedGarbageCollection :: (MonadMask f, MonadIO f, Show a) => ByteString -> f [a] -> f [a] otTracedGarbageCollection label act | userTracingEnabled = fst <$> generalBracket @@ -138,6 +130,7 @@ otTracedGarbageCollection label act (const act) | otherwise = act +otTracedProvider :: MonadUnliftIO m => PluginId -> ByteString -> m a -> m a otTracedProvider (PluginId pluginName) provider act | userTracingEnabled = do runInIO <- askRunInIO @@ -146,4 +139,3 @@ otTracedProvider (PluginId pluginName) provider act runInIO act | otherwise = act - diff --git a/ghcide/src/Development/IDE/GHC/Compat.hs b/ghcide/src/Development/IDE/GHC/Compat.hs index ae4d57e715..b853fb0a25 100644 --- a/ghcide/src/Development/IDE/GHC/Compat.hs +++ b/ghcide/src/Development/IDE/GHC/Compat.hs @@ -228,18 +228,8 @@ import DynFlags hiding (ExposePackage) import HscTypes import MkIface hiding (writeIfaceFile) -#if MIN_VERSION_ghc(8,8,0) import StringBuffer (hPutStringBuffer) -#endif import qualified SysTools - -#if !MIN_VERSION_ghc(8,8,0) -import qualified EnumSet -import SrcLoc (RealLocated) - -import Foreign.ForeignPtr -import System.IO -#endif #endif import Compat.HieAst (enrichHie) @@ -385,13 +375,6 @@ corePrepExpr _ = GHC.corePrepExpr simplifyExpr df _ = GHC.simplifyExpr df #endif -#if !MIN_VERSION_ghc(8,8,0) -hPutStringBuffer :: Handle -> StringBuffer -> IO () -hPutStringBuffer hdl (StringBuffer buf len cur) - = withForeignPtr (plusForeignPtr buf cur) $ \ptr -> - hPutBuf hdl ptr len -#endif - #if MIN_VERSION_ghc(9,2,0) type ErrMsg = MsgEnvelope DecoratedSDoc #endif @@ -445,12 +428,7 @@ hieExportNames = nameListFromAvails . hie_exports type NameCacheUpdater = NameCache #else upNameCache :: IORef NameCache -> (NameCache -> (NameCache, c)) -> IO c -#if MIN_VERSION_ghc(8,8,0) upNameCache = updNameCache -#else -upNameCache ref upd_fn - = atomicModifyIORef' ref upd_fn -#endif #endif #if !MIN_VERSION_ghc(9,0,1) @@ -480,27 +458,15 @@ addIncludePathsQuote path x = x{includePaths = f $ includePaths x} where f i = i{includePathsQuote = path : includePathsQuote i} setHieDir :: FilePath -> DynFlags -> DynFlags -setHieDir _f d = -#if MIN_VERSION_ghc(8,8,0) - d { hieDir = Just _f} -#else - d -#endif +setHieDir _f d = d { hieDir = Just _f} dontWriteHieFiles :: DynFlags -> DynFlags -dontWriteHieFiles d = -#if MIN_VERSION_ghc(8,8,0) - gopt_unset d Opt_WriteHie -#else - d -#endif +dontWriteHieFiles d = gopt_unset d Opt_WriteHie setUpTypedHoles ::DynFlags -> DynFlags setUpTypedHoles df = flip gopt_unset Opt_AbstractRefHoleFits -- too spammy -#if MIN_VERSION_ghc(8,8,0) $ flip gopt_unset Opt_ShowDocsOfHoleFits -- not used -#endif $ flip gopt_unset Opt_ShowMatchesOfHoleFits -- nice but broken (forgets module qualifiers) $ flip gopt_unset Opt_ShowProvOfHoleFits -- not used $ flip gopt_unset Opt_ShowTypeAppOfHoleFits -- not used @@ -533,12 +499,6 @@ disableWarningsAsErrors :: DynFlags -> DynFlags disableWarningsAsErrors df = flip gopt_unset Opt_WarnIsError $ foldl' wopt_unset_fatal df [toEnum 0 ..] -#if !MIN_VERSION_ghc(8,8,0) -wopt_unset_fatal :: DynFlags -> WarningFlag -> DynFlags -wopt_unset_fatal dfs f - = dfs { fatalWarningFlags = EnumSet.delete f (fatalWarningFlags dfs) } -#endif - isQualifiedImport :: ImportDecl a -> Bool #if MIN_VERSION_ghc(8,10,0) isQualifiedImport ImportDecl{ideclQualified = NotQualified} = False @@ -606,8 +566,7 @@ generatedNodeInfo = sourceNodeInfo -- before ghc 9.0, we don't distinguish the s #endif data GhcVersion - = GHC86 - | GHC88 + = GHC88 | GHC810 | GHC90 | GHC92 @@ -628,8 +587,6 @@ ghcVersion = GHC90 ghcVersion = GHC810 #elif MIN_VERSION_GLASGOW_HASKELL(8,8,0,0) ghcVersion = GHC88 -#elif MIN_VERSION_GLASGOW_HASKELL(8,6,0,0) -ghcVersion = GHC86 #endif runUnlit :: Logger -> DynFlags -> [Option] -> IO () diff --git a/ghcide/src/Development/IDE/GHC/Compat/CPP.hs b/ghcide/src/Development/IDE/GHC/Compat/CPP.hs index b9063e8a92..831ecfa3cc 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/CPP.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/CPP.hs @@ -16,24 +16,20 @@ module Development.IDE.GHC.Compat.CPP ( doCpp ) where -import FileCleanup -import Packages -import Panic -import SysTools -#if MIN_VERSION_ghc(8,8,2) -import LlvmCodeGen (llvmVersionList) -#elif MIN_VERSION_ghc(8,8,0) -import LlvmCodeGen (LlvmVersion (..)) -#endif import Control.Monad import Data.List (intercalate) import Data.Maybe import Data.Version import DynFlags +import FileCleanup +import LlvmCodeGen (llvmVersionList) import Module (rtsUnitId, toInstalledUnitId) +import Packages +import Panic import System.Directory import System.FilePath import System.Info +import SysTools import Development.IDE.GHC.Compat as Compat @@ -136,16 +132,9 @@ getBackendDefs :: DynFlags -> IO [String] getBackendDefs dflags | hscTarget dflags == HscLlvm = do llvmVer <- figureLlvmVersion dflags return $ case llvmVer of -#if MIN_VERSION_ghc(8,8,2) Just v | [m] <- llvmVersionList v -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (m, 0) ] | m:n:_ <- llvmVersionList v -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (m, n) ] -#elif MIN_VERSION_ghc(8,8,0) - Just (LlvmVersion n) -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (n,0) ] - Just (LlvmVersionOld m n) -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (m,n) ] -#else - Just n -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format n ] -#endif _ -> [] where format (major, minor) diff --git a/ghcide/src/Development/IDE/GHC/Compat/Core.hs b/ghcide/src/Development/IDE/GHC/Compat/Core.hs index afeace0acf..88acf5cde4 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Core.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Core.hs @@ -36,11 +36,9 @@ module Development.IDE.GHC.Compat.Core ( maxRefHoleFits, maxValidHoleFits, setOutputFile, -#if MIN_VERSION_ghc(8,8,0) CommandLineOption, #if !MIN_VERSION_ghc(9,2,0) staticPlugins, -#endif #endif sPgm_F, settings, @@ -242,7 +240,7 @@ module Development.IDE.GHC.Compat.Core ( SrcLoc.mkGeneralSrcSpan, SrcLoc.mkRealSrcSpan, SrcLoc.mkRealSrcLoc, - getRealSrcSpan, + SrcLoc.getRealSrcSpan, SrcLoc.realSrcLocSpan, SrcLoc.realSrcSpanStart, SrcLoc.realSrcSpanEnd, @@ -263,7 +261,7 @@ module Development.IDE.GHC.Compat.Core ( SrcLoc.noSrcSpan, SrcLoc.noSrcLoc, SrcLoc.noLoc, -#if !MIN_VERSION_ghc(8,10,0) && MIN_VERSION_ghc(8,8,0) +#if !MIN_VERSION_ghc(8,10,0) SrcLoc.dL, #endif -- * Finder @@ -311,13 +309,13 @@ module Development.IDE.GHC.Compat.Core ( Module.ml_hs_file, Module.ml_obj_file, Module.ml_hi_file, - Development.IDE.GHC.Compat.Core.ml_hie_file, + Module.ml_hie_file, -- * DataCon - Development.IDE.GHC.Compat.Core.dataConExTyCoVars, + DataCon.dataConExTyCoVars, -- * Role Role(..), -- * Panic - PlainGhcException, + Plain.PlainGhcException, panic, panicDoc, -- * Other @@ -734,19 +732,12 @@ import NameCache import NameEnv import NameSet import Packages -#if MIN_VERSION_ghc(8,8,0) import Panic hiding (try) import qualified PlainPanic as Plain -#else -import Panic hiding (GhcException, try) -import qualified Panic as Plain -#endif import Parser import PatSyn import RnFixity -#if MIN_VERSION_ghc(8,8,0) import Plugins -#endif import PprTyThing hiding (pprFamInst) import PrelInfo import PrelNames hiding (Unique, printName) @@ -791,10 +782,8 @@ import SrcLoc (RealLocated, #endif -#if !MIN_VERSION_ghc(8,8,0) import Data.List (isSuffixOf) import System.FilePath -#endif #if MIN_VERSION_ghc(9,2,0) @@ -931,49 +920,16 @@ pattern L l a <- GHC.L (getLoc -> l) a {-# COMPLETE L #-} #endif -#elif MIN_VERSION_ghc(8,8,0) +#else type HasSrcSpan = SrcLoc.HasSrcSpan getLoc :: SrcLoc.HasSrcSpan a => a -> SrcLoc.SrcSpan getLoc = SrcLoc.getLoc - -#else - -class HasSrcSpan a where - getLoc :: a -> SrcSpan -instance HasSrcSpan Name where - getLoc = nameSrcSpan -instance HasSrcSpan (SrcLoc.GenLocated SrcSpan a) where - getLoc = SrcLoc.getLoc - #endif -getRealSrcSpan :: SrcLoc.RealLocated a -> SrcLoc.RealSrcSpan -#if !MIN_VERSION_ghc(8,8,0) -getRealSrcSpan = SrcLoc.getLoc -#else -getRealSrcSpan = SrcLoc.getRealSrcSpan -#endif - - -- | Add the @-boot@ suffix to all output file paths associated with the -- module, not including the input file itself addBootSuffixLocnOut :: GHC.ModLocation -> GHC.ModLocation -#if !MIN_VERSION_ghc(8,8,0) -addBootSuffixLocnOut locn - = locn { Module.ml_hi_file = Module.addBootSuffix (Module.ml_hi_file locn) - , Module.ml_obj_file = Module.addBootSuffix (Module.ml_obj_file locn) - } -#else addBootSuffixLocnOut = Module.addBootSuffixLocnOut -#endif - - -dataConExTyCoVars :: DataCon -> [TyCoVar] -#if __GLASGOW_HASKELL__ >= 808 -dataConExTyCoVars = DataCon.dataConExTyCoVars -#else -dataConExTyCoVars = DataCon.dataConExTyVars -#endif #if !MIN_VERSION_ghc(9,0,0) -- Linear Haskell @@ -987,7 +943,7 @@ unrestricted = id mkVisFunTys :: [Scaled Type] -> Type -> Type mkVisFunTys = -#if __GLASGOW_HASKELL__ <= 808 +#if __GLASGOW_HASKELL__ == 808 mkFunTys #else TcType.mkVisFunTys @@ -1030,27 +986,12 @@ noExtField :: GHC.NoExt noExtField = GHC.noExt #endif -ml_hie_file :: GHC.ModLocation -> FilePath -#if !MIN_VERSION_ghc(8,8,0) -ml_hie_file ml - | "boot" `isSuffixOf ` Module.ml_hi_file ml = Module.ml_hi_file ml -<.> ".hie-boot" - | otherwise = Module.ml_hi_file ml -<.> ".hie" -#else -ml_hie_file = Module.ml_hie_file -#endif - #if !MIN_VERSION_ghc(9,0,0) pattern NotBoot, IsBoot :: IsBootInterface pattern NotBoot = False pattern IsBoot = True #endif -#if MIN_VERSION_ghc(8,8,0) -type PlainGhcException = Plain.PlainGhcException -#else -type PlainGhcException = Plain.GhcException -#endif - #if MIN_VERSION_ghc(9,0,0) -- This is from the old api, but it still simplifies pattern ConPatIn :: SrcLoc.Located (ConLikeP GhcPs) -> HsConPatDetails GhcPs -> Pat GhcPs diff --git a/ghcide/src/Development/IDE/GHC/Compat/Plugins.hs b/ghcide/src/Development/IDE/GHC/Compat/Plugins.hs index b241c150c6..9af3d38162 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Plugins.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Plugins.hs @@ -4,17 +4,13 @@ module Development.IDE.GHC.Compat.Plugins ( Plugin(..), defaultPlugin, -#if __GLASGOW_HASKELL__ >= 808 PluginWithArgs(..), -#endif applyPluginsParsedResultAction, initializePlugins, -- * Static plugins -#if MIN_VERSION_ghc(8,8,0) StaticPlugin(..), hsc_static_plugins, -#endif ) where #if MIN_VERSION_ghc(9,0,0) @@ -31,13 +27,9 @@ import GHC.Driver.Plugins (ParsedResult (..), staticPlugins) #endif import qualified GHC.Runtime.Loader as Loader -#elif MIN_VERSION_ghc(8,8,0) -import qualified DynamicLoading as Loader -import Plugins #else import qualified DynamicLoading as Loader -import Plugins (Plugin (..), defaultPlugin, - withPlugins) +import Plugins #endif import Development.IDE.GHC.Compat.Core import Development.IDE.GHC.Compat.Env (hscSetFlags, hsc_dflags) @@ -76,7 +68,6 @@ initializePlugins env = do #endif -#if MIN_VERSION_ghc(8,8,0) hsc_static_plugins :: HscEnv -> [StaticPlugin] #if MIN_VERSION_ghc(9,3,0) hsc_static_plugins = staticPlugins . Env.hsc_plugins @@ -85,4 +76,3 @@ hsc_static_plugins = Env.hsc_static_plugins #else hsc_static_plugins = staticPlugins . hsc_dflags #endif -#endif diff --git a/ghcide/src/Development/IDE/GHC/CoreFile.hs b/ghcide/src/Development/IDE/GHC/CoreFile.hs index dde331c6ce..737441f9ef 100644 --- a/ghcide/src/Development/IDE/GHC/CoreFile.hs +++ b/ghcide/src/Development/IDE/GHC/CoreFile.hs @@ -38,7 +38,7 @@ import GHC.Types.TypeEnv import GHC.Driver.Types #endif -#elif MIN_VERSION_ghc(8,6,0) +#else import Binary import BinFingerprint (fingerprintBinMem) import BinIface diff --git a/ghcide/src/Development/IDE/Spans/AtPoint.hs b/ghcide/src/Development/IDE/Spans/AtPoint.hs index c729ec8e5d..a1ed871633 100644 --- a/ghcide/src/Development/IDE/Spans/AtPoint.hs +++ b/ghcide/src/Development/IDE/Spans/AtPoint.hs @@ -280,11 +280,7 @@ typeLocationsAtPoint withHieDb lookupModule _ideOptions pos (HAR _ ast _ _ hieKi where ni = nodeInfo' x getTypes ts = flip concatMap (unfold ts) $ \case HTyVarTy n -> [n] -#if MIN_VERSION_ghc(8,8,0) HAppTy a (HieArgs xs) -> getTypes (a : map snd xs) -#else - HAppTy a b -> getTypes [a,b] -#endif HTyConApp tc (HieArgs xs) -> ifaceTyConName tc : getTypes (map snd xs) HForAllTy _ a -> getTypes [a] #if MIN_VERSION_ghc(9,0,1) diff --git a/ghcide/src/Development/IDE/Spans/Pragmas.hs b/ghcide/src/Development/IDE/Spans/Pragmas.hs index 6663642c59..f22acf04c3 100644 --- a/ghcide/src/Development/IDE/Spans/Pragmas.hs +++ b/ghcide/src/Development/IDE/Spans/Pragmas.hs @@ -416,10 +416,7 @@ mkLexerPState dynFlags stringBuffer = startRealSrcLoc = mkRealSrcLoc "asdf" 1 1 updateDynFlags = flip gopt_unset Opt_Haddock . flip gopt_set Opt_KeepRawTokenStream finalDynFlags = updateDynFlags dynFlags -#if !MIN_VERSION_ghc(8,8,1) - pState = mkPState finalDynFlags stringBuffer startRealSrcLoc - finalPState = pState{ use_pos_prags = False } -#elif !MIN_VERSION_ghc(8,10,1) +#if !MIN_VERSION_ghc(8,10,1) mkLexerParserFlags = mkParserFlags' <$> warningFlags diff --git a/ghcide/test/ghcide-test-utils.cabal b/ghcide/test/ghcide-test-utils.cabal index f1f38818bb..cccc5e35ac 100644 --- a/ghcide/test/ghcide-test-utils.cabal +++ b/ghcide/test/ghcide-test-utils.cabal @@ -14,7 +14,7 @@ description: Test utils for ghcide homepage: https://github.com/haskell/haskell-language-server/tree/master/ghcide#readme bug-reports: https://github.com/haskell/haskell-language-server/issues -tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 +tested-with: GHC == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 source-repository head type: git diff --git a/hie-compat/README.md b/hie-compat/README.md index 6b5e101def..7ac08b305a 100644 --- a/hie-compat/README.md +++ b/hie-compat/README.md @@ -1,7 +1,7 @@ # hie-compat Mainly a backport of [HIE -Files](https://gitlab.haskell.org/ghc/ghc/-/wikis/hie-files) for ghc 8.6, along +Files](https://gitlab.haskell.org/ghc/ghc/-/wikis/hie-files) for ghc 8.8, along with a few other backports of fixes useful for `ghcide` Also includes backport of record-dot-syntax support to 9.2.x diff --git a/hie-compat/hie-compat.cabal b/hie-compat/hie-compat.cabal index 8dd3f899b9..2a7c2d65d8 100644 --- a/hie-compat/hie-compat.cabal +++ b/hie-compat/hie-compat.cabal @@ -1,10 +1,10 @@ cabal-version: 1.22 name: hie-compat version: 0.3.0.0 -synopsis: HIE files for GHC 8.6 and other HIE file backports +synopsis: HIE files for GHC 8.8 and other HIE file backports license: Apache-2.0 description: - Backports for HIE files to GHC 8.6, along with a few other backports + Backports for HIE files to GHC 8.8, along with a few other backports of HIE file related fixes for ghcide. THIS DOES NOT LET YOU READ HIE FILES WITH MISMATCHED VERSIONS OF GHC @@ -46,8 +46,6 @@ library Compat.HieDebug Compat.HieUtils - if (impl(ghc > 8.5) && impl(ghc < 8.7) && !flag(ghc-lib)) - hs-source-dirs: src-ghc86 if (impl(ghc > 8.7) && impl(ghc < 8.10)) hs-source-dirs: src-ghc88 src-reexport if (impl(ghc > 8.9) && impl(ghc < 8.11)) diff --git a/hie-compat/src-ghc86/Compat/HieAst.hs b/hie-compat/src-ghc86/Compat/HieAst.hs deleted file mode 100644 index 8fdae7ecb0..0000000000 --- a/hie-compat/src-ghc86/Compat/HieAst.hs +++ /dev/null @@ -1,1753 +0,0 @@ -{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} -{- -Forked from GHC v8.8.1 to work around the readFile side effect in mkHiefile - -Main functions for .hie file generation --} -{- HLINT ignore -} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE TypeSynonymInstances #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE AllowAmbiguousTypes #-} -{-# LANGUAGE ViewPatterns #-} -{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE DataKinds #-} -module Compat.HieAst ( enrichHie ) where - -import Avail ( Avails ) -import Bag ( Bag, bagToList ) -import BasicTypes -import BooleanFormula -import Class ( FunDep ) -import CoreUtils ( exprType ) -import ConLike ( conLikeName ) -import Desugar ( deSugarExpr ) -import FieldLabel -import HsSyn -import HscTypes -import Module ( ModuleName ) -import MonadUtils ( concatMapM, liftIO ) -import Name ( Name, nameSrcSpan ) -import SrcLoc -import TcHsSyn ( hsLitType, hsPatType ) -import Type ( mkFunTys, Type ) -import TysWiredIn ( mkListTy, mkSumTy ) -import Var ( Id, Var, setVarName, varName, varType ) - -import Compat.HieTypes -import Compat.HieUtils - -import qualified Data.Map as M -import qualified Data.Set as S -import Data.Data ( Data, Typeable ) -import Data.List (foldl', foldl1' ) -import Control.Monad.Trans.Reader -import Control.Monad.Trans.Class ( lift ) - --- These synonyms match those defined in main/GHC.hs -type RenamedSource = ( HsGroup GhcRn, [LImportDecl GhcRn] - , Maybe [(LIE GhcRn, Avails)] - , Maybe LHsDocString ) -type TypecheckedSource = LHsBinds GhcTc - --- | Marks that a field uses the GhcRn variant even when the pass --- parameter is GhcTc. Useful for storing HsTypes in HsExprs, say, because --- HsType GhcTc should never occur. -type family NoGhcTc (p :: *) where - -- this way, GHC can figure out that the result is a GhcPass - NoGhcTc (GhcPass pass) = GhcPass (NoGhcTcPass pass) - NoGhcTc other = other - -type family NoGhcTcPass (p :: Pass) :: Pass where - NoGhcTcPass 'Typechecked = 'Renamed - NoGhcTcPass other = other - -{- Note [Name Remapping] -The Typechecker introduces new names for mono names in AbsBinds. -We don't care about the distinction between mono and poly bindings, -so we replace all occurrences of the mono name with the poly name. --} -newtype HieState = HieState - { name_remapping :: M.Map Name Id - } - -initState :: HieState -initState = HieState M.empty - -class ModifyState a where -- See Note [Name Remapping] - addSubstitution :: a -> a -> HieState -> HieState - -instance ModifyState Name where - addSubstitution _ _ hs = hs - -instance ModifyState Id where - addSubstitution mono poly hs = - hs{name_remapping = M.insert (varName mono) poly (name_remapping hs)} - -modifyState :: ModifyState (IdP p) => [ABExport p] -> HieState -> HieState -modifyState = foldr go id - where - go ABE{abe_poly=poly,abe_mono=mono} f = addSubstitution mono poly . f - go _ f = f - -type HieM = ReaderT HieState Hsc - -enrichHie :: TypecheckedSource -> RenamedSource -> Hsc (HieASTs Type) -enrichHie ts (hsGrp, imports, exports, _) = flip runReaderT initState $ do - tasts <- toHie $ fmap (BC RegularBind ModuleScope) ts - rasts <- processGrp hsGrp - imps <- toHie $ filter (not . ideclImplicit . unLoc) imports - exps <- toHie $ fmap (map $ IEC Export . fst) exports - let spanFile children = case children of - [] -> mkRealSrcSpan (mkRealSrcLoc "" 1 1) (mkRealSrcLoc "" 1 1) - _ -> mkRealSrcSpan (realSrcSpanStart $ nodeSpan $ head children) - (realSrcSpanEnd $ nodeSpan $ last children) - - modulify xs = - Node (simpleNodeInfo "Module" "Module") (spanFile xs) xs - - asts = HieASTs - $ resolveTyVarScopes - $ M.map (modulify . mergeSortAsts) - $ M.fromListWith (++) - $ map (\x -> (srcSpanFile (nodeSpan x),[x])) flat_asts - - flat_asts = concat - [ tasts - , rasts - , imps - , exps - ] - return asts - where - processGrp grp = concatM - [ toHie $ fmap (RS ModuleScope ) hs_valds grp - , toHie $ hs_splcds grp - , toHie $ hs_tyclds grp - , toHie $ hs_derivds grp - , toHie $ hs_fixds grp - , toHie $ hs_defds grp - , toHie $ hs_fords grp - , toHie $ hs_warnds grp - , toHie $ hs_annds grp - , toHie $ hs_ruleds grp - ] - -getRealSpan :: SrcSpan -> Maybe Span -getRealSpan (RealSrcSpan sp) = Just sp -getRealSpan _ = Nothing - -grhss_span :: GRHSs p body -> SrcSpan -grhss_span (GRHSs _ xs bs) = foldl' combineSrcSpans (getLoc bs) (map getLoc xs) -grhss_span (XGRHSs _) = error "XGRHS has no span" - -bindingsOnly :: [Context Name] -> [HieAST a] -bindingsOnly [] = [] -bindingsOnly (C c n : xs) = case nameSrcSpan n of - RealSrcSpan span -> Node nodeinfo span [] : bindingsOnly xs - where nodeinfo = NodeInfo S.empty [] (M.singleton (Right n) info) - info = mempty{identInfo = S.singleton c} - _ -> bindingsOnly xs - -concatM :: Monad m => [m [a]] -> m [a] -concatM xs = concat <$> sequence xs - -{- Note [Capturing Scopes and other non local information] -toHie is a local tranformation, but scopes of bindings cannot be known locally, -hence we have to push the relevant info down into the binding nodes. -We use the following types (*Context and *Scoped) to wrap things and -carry the required info -(Maybe Span) always carries the span of the entire binding, including rhs --} -data Context a = C ContextInfo a -- Used for names and bindings - -data RContext a = RC RecFieldContext a -data RFContext a = RFC RecFieldContext (Maybe Span) a --- ^ context for record fields - -data IEContext a = IEC IEType a --- ^ context for imports/exports - -data BindContext a = BC BindType Scope a --- ^ context for imports/exports - -data PatSynFieldContext a = PSC (Maybe Span) a --- ^ context for pattern synonym fields. - -data SigContext a = SC SigInfo a --- ^ context for type signatures - -data SigInfo = SI SigType (Maybe Span) - -data SigType = BindSig | ClassSig | InstSig - -data RScoped a = RS Scope a --- ^ Scope spans over everything to the right of a, (mostly) not --- including a itself --- (Includes a in a few special cases like recursive do bindings) or --- let/where bindings - --- | Pattern scope -data PScoped a = PS (Maybe Span) - Scope -- ^ use site of the pattern - Scope -- ^ pattern to the right of a, not including a - a - deriving (Typeable, Data) -- Pattern Scope - -{- Note [TyVar Scopes] -Due to -XScopedTypeVariables, type variables can be in scope quite far from -their original binding. We resolve the scope of these type variables -in a separate pass --} -data TScoped a = TS TyVarScope a -- TyVarScope - -data TVScoped a = TVS TyVarScope Scope a -- TyVarScope --- ^ First scope remains constant --- Second scope is used to build up the scope of a tyvar over --- things to its right, ala RScoped - --- | Each element scopes over the elements to the right -listScopes :: Scope -> [Located a] -> [RScoped (Located a)] -listScopes _ [] = [] -listScopes rhsScope [pat] = [RS rhsScope pat] -listScopes rhsScope (pat : pats) = RS sc pat : pats' - where - pats'@((RS scope p):_) = listScopes rhsScope pats - sc = combineScopes scope $ mkScope $ getLoc p - --- | 'listScopes' specialised to 'PScoped' things -patScopes - :: Maybe Span - -> Scope - -> Scope - -> [LPat (GhcPass p)] - -> [PScoped (LPat (GhcPass p))] -patScopes rsp useScope patScope xs = - map (\(RS sc a) -> PS rsp useScope sc a) $ - listScopes patScope xs - --- | 'listScopes' specialised to 'TVScoped' things -tvScopes - :: TyVarScope - -> Scope - -> [LHsTyVarBndr a] - -> [TVScoped (LHsTyVarBndr a)] -tvScopes tvScope rhsScope xs = - map (\(RS sc a)-> TVS tvScope sc a) $ listScopes rhsScope xs - -{- Note [Scoping Rules for SigPat] -Explicitly quantified variables in pattern type signatures are not -brought into scope in the rhs, but implicitly quantified variables -are (HsWC and HsIB). -This is unlike other signatures, where explicitly quantified variables -are brought into the RHS Scope -For example -foo :: forall a. ...; -foo = ... -- a is in scope here - -bar (x :: forall a. a -> a) = ... -- a is not in scope here --- ^ a is in scope here (pattern body) - -bax (x :: a) = ... -- a is in scope here -Because of HsWC and HsIB pass on their scope to their children -we must wrap the LHsType in pattern signatures in a -Shielded explictly, so that the HsWC/HsIB scope is not passed -on the the LHsType --} - -data Shielded a = SH Scope a -- Ignores its TScope, uses its own scope instead - -type family ProtectedSig a where - ProtectedSig GhcRn = HsWildCardBndrs GhcRn (HsImplicitBndrs - GhcRn - (Shielded (LHsType GhcRn))) - ProtectedSig GhcTc = NoExt - -class ProtectSig a where - protectSig :: Scope -> XSigPat a -> ProtectedSig a - -instance (HasLoc a) => HasLoc (Shielded a) where - loc (SH _ a) = loc a - -instance (ToHie (TScoped a)) => ToHie (TScoped (Shielded a)) where - toHie (TS _ (SH sc a)) = toHie (TS (ResolvedScopes [sc]) a) - -instance ProtectSig GhcTc where - protectSig _ _ = NoExt - -instance ProtectSig GhcRn where - protectSig sc (HsWC a (HsIB b sig)) = - HsWC a (HsIB b (SH sc sig)) - protectSig _ _ = error "protectSig not given HsWC (HsIB)" - -class HasLoc a where - -- ^ defined so that HsImplicitBndrs and HsWildCardBndrs can - -- know what their implicit bindings are scoping over - loc :: a -> SrcSpan - -instance HasLoc thing => HasLoc (TScoped thing) where - loc (TS _ a) = loc a - -instance HasLoc thing => HasLoc (PScoped thing) where - loc (PS _ _ _ a) = loc a - -instance HasLoc (LHsQTyVars GhcRn) where - loc (HsQTvs _ vs) = loc vs - loc _ = noSrcSpan - -instance HasLoc thing => HasLoc (HsImplicitBndrs a thing) where - loc (HsIB _ a) = loc a - loc _ = noSrcSpan - -instance HasLoc thing => HasLoc (HsWildCardBndrs a thing) where - loc (HsWC _ a) = loc a - loc _ = noSrcSpan - -instance HasLoc (Located a) where - loc (L l _) = l - -instance HasLoc a => HasLoc [a] where - loc [] = noSrcSpan - loc xs = foldl1' combineSrcSpans $ map loc xs - -instance (HasLoc a, HasLoc b) => HasLoc (FamEqn s a b) where - loc (FamEqn _ a b _ c) = foldl1' combineSrcSpans [loc a, loc b, loc c] - loc _ = noSrcSpan -{- -instance (HasLoc tm, HasLoc ty) => HasLoc (HsArg tm ty) where - loc (HsValArg tm) = loc tm - loc (HsTypeArg _ ty) = loc ty - loc (HsArgPar sp) = sp --} - -instance HasLoc (HsDataDefn GhcRn) where - loc def@(HsDataDefn{}) = loc $ dd_cons def - -- Only used for data family instances, so we only need rhs - -- Most probably the rest will be unhelpful anyway - loc _ = noSrcSpan - --- | The main worker class -class ToHie a where - toHie :: a -> HieM [HieAST Type] - --- | Used to collect type info -class Data a => HasType a where - getTypeNode :: a -> HieM [HieAST Type] - -instance (ToHie a) => ToHie [a] where - toHie = concatMapM toHie - -instance (ToHie a) => ToHie (Bag a) where - toHie = toHie . bagToList - -instance (ToHie a) => ToHie (Maybe a) where - toHie = maybe (pure []) toHie - -instance ToHie (Context (Located NoExt)) where - toHie _ = pure [] - -instance ToHie (TScoped NoExt) where - toHie _ = pure [] - -instance ToHie (IEContext (Located ModuleName)) where - toHie (IEC c (L (RealSrcSpan span) mname)) = - pure $ [Node (NodeInfo S.empty [] idents) span []] - where details = mempty{identInfo = S.singleton (IEThing c)} - idents = M.singleton (Left mname) details - toHie _ = pure [] - -instance ToHie (Context (Located Var)) where - toHie c = case c of - C context (L (RealSrcSpan span) name') - -> do - m <- asks name_remapping - let name = M.findWithDefault name' (varName name') m - pure - [Node - (NodeInfo S.empty [] $ - M.singleton (Right $ varName name) - (IdentifierDetails (Just $ varType name') - (S.singleton context))) - span - []] - _ -> pure [] - -instance ToHie (Context (Located Name)) where - toHie c = case c of - C context (L (RealSrcSpan span) name') -> do - m <- asks name_remapping - let name = case M.lookup name' m of - Just var -> varName var - Nothing -> name' - pure - [Node - (NodeInfo S.empty [] $ - M.singleton (Right name) - (IdentifierDetails Nothing - (S.singleton context))) - span - []] - _ -> pure [] - --- | Dummy instances - never called -instance ToHie (TScoped (LHsSigWcType GhcTc)) where - toHie _ = pure [] -instance ToHie (TScoped (LHsWcType GhcTc)) where - toHie _ = pure [] -instance ToHie (SigContext (LSig GhcTc)) where - toHie _ = pure [] -instance ToHie (TScoped Type) where - toHie _ = pure [] - -instance HasType (LHsBind GhcRn) where - getTypeNode (L spn bind) = makeNode bind spn - -instance HasType (LHsBind GhcTc) where - getTypeNode (L spn bind) = case bind of - FunBind{fun_id = name} -> makeTypeNode bind spn (varType $ unLoc name) - _ -> makeNode bind spn - -instance HasType (LPat GhcRn) where - getTypeNode (L spn pat) = makeNode pat spn - -instance HasType (LPat GhcTc) where - getTypeNode (L spn opat) = makeTypeNode opat spn (hsPatType opat) - -instance HasType (LHsExpr GhcRn) where - getTypeNode (L spn e) = makeNode e spn - --- | This instance tries to construct 'HieAST' nodes which include the type of --- the expression. It is not yet possible to do this efficiently for all --- expression forms, so we skip filling in the type for those inputs. --- --- 'HsApp', for example, doesn't have any type information available directly on --- the node. Our next recourse would be to desugar it into a 'CoreExpr' then --- query the type of that. Yet both the desugaring call and the type query both --- involve recursive calls to the function and argument! This is particularly --- problematic when you realize that the HIE traversal will eventually visit --- those nodes too and ask for their types again. --- --- Since the above is quite costly, we just skip cases where computing the --- expression's type is going to be expensive. --- --- See #16233 -instance HasType (LHsExpr GhcTc) where - getTypeNode e@(L spn e') = lift $ - -- Some expression forms have their type immediately available - let tyOpt = case e' of - HsLit _ l -> Just (hsLitType l) - HsOverLit _ o -> Just (overLitType o) - - HsLam _ (MG { mg_ext = groupTy }) -> Just (matchGroupType groupTy) - HsLamCase _ (MG { mg_ext = groupTy }) -> Just (matchGroupType groupTy) - HsCase _ _ (MG { mg_ext = groupTy }) -> Just (mg_res_ty groupTy) - - ExplicitList ty _ _ -> Just (mkListTy ty) - ExplicitSum ty _ _ _ -> Just (mkSumTy ty) - HsDo ty _ _ -> Just ty - HsMultiIf ty _ -> Just ty - - _ -> Nothing - - in - case tyOpt of - _ | skipDesugaring e' -> fallback - | otherwise -> do - hs_env <- Hsc $ \e w -> return (e,w) - (_,mbe) <- liftIO $ deSugarExpr hs_env e - maybe fallback (makeTypeNode e' spn . exprType) mbe - where - fallback = makeNode e' spn - - matchGroupType :: MatchGroupTc -> Type - matchGroupType (MatchGroupTc args res) = mkFunTys args res - - -- | Skip desugaring of these expressions for performance reasons. - -- - -- See impact on Haddock output (esp. missing type annotations or links) - -- before marking more things here as 'False'. See impact on Haddock - -- performance before marking more things as 'True'. - skipDesugaring :: HsExpr a -> Bool - skipDesugaring e = case e of - HsVar{} -> False - HsUnboundVar{} -> False - HsConLikeOut{} -> False - HsRecFld{} -> False - HsOverLabel{} -> False - HsIPVar{} -> False - HsWrap{} -> False - _ -> True - -instance ( ToHie (Context (Located (IdP a))) - , ToHie (MatchGroup a (LHsExpr a)) - , ToHie (PScoped (LPat a)) - , ToHie (GRHSs a (LHsExpr a)) - , ToHie (LHsExpr a) - , ToHie (Located (PatSynBind a a)) - , HasType (LHsBind a) - , ModifyState (IdP a) - , Data (HsBind a) - ) => ToHie (BindContext (LHsBind a)) where - toHie (BC context scope b@(L span bind)) = - concatM $ getTypeNode b : case bind of - FunBind{fun_id = name, fun_matches = matches} -> - [ toHie $ C (ValBind context scope $ getRealSpan span) name - , toHie matches - ] - PatBind{pat_lhs = lhs, pat_rhs = rhs} -> - [ toHie $ PS (getRealSpan span) scope NoScope lhs - , toHie rhs - ] - VarBind{var_rhs = expr} -> - [ toHie expr - ] - AbsBinds{abs_exports = xs, abs_binds = binds} -> - [ local (modifyState xs) $ -- Note [Name Remapping] - toHie $ fmap (BC context scope) binds - ] - PatSynBind _ psb -> - [ toHie $ L span psb -- PatSynBinds only occur at the top level - ] - XHsBindsLR _ -> [] - -instance ( ToHie (LMatch a body) - ) => ToHie (MatchGroup a body) where - toHie mg = concatM $ case mg of - MG{ mg_alts = (L span alts) , mg_origin = FromSource } -> - [ pure $ locOnly span - , toHie alts - ] - MG{} -> [] - XMatchGroup _ -> [] - -instance ( ToHie (Context (Located (IdP a))) - , ToHie (PScoped (LPat a)) - , ToHie (HsPatSynDir a) - ) => ToHie (Located (PatSynBind a a)) where - toHie (L sp psb) = concatM $ case psb of - PSB{psb_id=var, psb_args=dets, psb_def=pat, psb_dir=dir} -> - [ toHie $ C (Decl PatSynDec $ getRealSpan sp) var - , toHie $ toBind dets - , toHie $ PS Nothing lhsScope NoScope pat - , toHie dir - ] - where - lhsScope = combineScopes varScope detScope - varScope = mkLScope var - detScope = case dets of - (PrefixCon args) -> foldr combineScopes NoScope $ map mkLScope args - (InfixCon a b) -> combineScopes (mkLScope a) (mkLScope b) - (RecCon r) -> foldr go NoScope r - go (RecordPatSynField a b) c = combineScopes c - $ combineScopes (mkLScope a) (mkLScope b) - detSpan = case detScope of - LocalScope a -> Just a - _ -> Nothing - toBind (PrefixCon args) = PrefixCon $ map (C Use) args - toBind (InfixCon a b) = InfixCon (C Use a) (C Use b) - toBind (RecCon r) = RecCon $ map (PSC detSpan) r - XPatSynBind _ -> [] - -instance ( ToHie (MatchGroup a (LHsExpr a)) - ) => ToHie (HsPatSynDir a) where - toHie dir = case dir of - ExplicitBidirectional mg -> toHie mg - _ -> pure [] - -instance ( a ~ GhcPass p - , ToHie body - , ToHie (HsMatchContext (NameOrRdrName (IdP a))) - , ToHie (PScoped (LPat a)) - , ToHie (GRHSs a body) - , Data (Match a body) - ) => ToHie (LMatch (GhcPass p) body) where - toHie (L span m ) = concatM $ makeNode m span : case m of - Match{m_ctxt=mctx, m_pats = pats, m_grhss = grhss } -> - [ toHie mctx - , let rhsScope = mkScope $ grhss_span grhss - in toHie $ patScopes Nothing rhsScope NoScope pats - , toHie grhss - ] - XMatch _ -> [] - -instance ( ToHie (Context (Located a)) - ) => ToHie (HsMatchContext a) where - toHie (FunRhs{mc_fun=name}) = toHie $ C MatchBind name - toHie (StmtCtxt a) = toHie a - toHie _ = pure [] - -instance ( ToHie (HsMatchContext a) - ) => ToHie (HsStmtContext a) where - toHie (PatGuard a) = toHie a - toHie (ParStmtCtxt a) = toHie a - toHie (TransStmtCtxt a) = toHie a - toHie _ = pure [] - -instance ( a ~ GhcPass p - , ToHie (Context (Located (IdP a))) - , ToHie (RContext (HsRecFields a (PScoped (LPat a)))) - , ToHie (LHsExpr a) - , ToHie (TScoped (LHsSigWcType a)) - , ProtectSig a - , ToHie (TScoped (ProtectedSig a)) - , HasType (LPat a) - , Data (HsSplice a) - ) => ToHie (PScoped (LPat (GhcPass p))) where - toHie (PS rsp scope pscope lpat@(L ospan opat)) = - concatM $ getTypeNode lpat : case opat of - WildPat _ -> - [] - VarPat _ lname -> - [ toHie $ C (PatternBind scope pscope rsp) lname - ] - LazyPat _ p -> - [ toHie $ PS rsp scope pscope p - ] - AsPat _ lname pat -> - [ toHie $ C (PatternBind scope - (combineScopes (mkLScope pat) pscope) - rsp) - lname - , toHie $ PS rsp scope pscope pat - ] - ParPat _ pat -> - [ toHie $ PS rsp scope pscope pat - ] - BangPat _ pat -> - [ toHie $ PS rsp scope pscope pat - ] - ListPat _ pats -> - [ toHie $ patScopes rsp scope pscope pats - ] - TuplePat _ pats _ -> - [ toHie $ patScopes rsp scope pscope pats - ] - SumPat _ pat _ _ -> - [ toHie $ PS rsp scope pscope pat - ] - ConPatIn c dets -> - [ toHie $ C Use c - , toHie $ contextify dets - ] - ConPatOut {pat_con = con, pat_args = dets}-> - [ toHie $ C Use $ fmap conLikeName con - , toHie $ contextify dets - ] - ViewPat _ expr pat -> - [ toHie expr - , toHie $ PS rsp scope pscope pat - ] - SplicePat _ sp -> - [ toHie $ L ospan sp - ] - LitPat _ _ -> - [] - NPat _ _ _ _ -> - [] - NPlusKPat _ n _ _ _ _ -> - [ toHie $ C (PatternBind scope pscope rsp) n - ] - SigPat sig pat -> - [ toHie $ PS rsp scope pscope pat - , let cscope = mkLScope pat in - toHie $ TS (ResolvedScopes [cscope, scope, pscope]) - (protectSig @a cscope sig) - -- See Note [Scoping Rules for SigPat] - ] - CoPat _ _ _ _ -> - [] - XPat _ -> [] - where - contextify (PrefixCon args) = PrefixCon $ patScopes rsp scope pscope args - contextify (InfixCon a b) = InfixCon a' b' - where [a', b'] = patScopes rsp scope pscope [a,b] - contextify (RecCon r) = RecCon $ RC RecFieldMatch $ contextify_rec r - contextify_rec (HsRecFields fds a) = HsRecFields (map go scoped_fds) a - where - go (RS fscope (L spn (HsRecField lbl pat pun))) = - L spn $ HsRecField lbl (PS rsp scope fscope pat) pun - scoped_fds = listScopes pscope fds - -instance ( ToHie body - , ToHie (LGRHS a body) - , ToHie (RScoped (LHsLocalBinds a)) - ) => ToHie (GRHSs a body) where - toHie grhs = concatM $ case grhs of - GRHSs _ grhss binds -> - [ toHie grhss - , toHie $ RS (mkScope $ grhss_span grhs) binds - ] - XGRHSs _ -> [] - -instance ( ToHie (Located body) - , ToHie (RScoped (GuardLStmt a)) - , Data (GRHS a (Located body)) - ) => ToHie (LGRHS a (Located body)) where - toHie (L span g) = concatM $ makeNode g span : case g of - GRHS _ guards body -> - [ toHie $ listScopes (mkLScope body) guards - , toHie body - ] - XGRHS _ -> [] - -instance ( a ~ GhcPass p - , ToHie (Context (Located (IdP a))) - , HasType (LHsExpr a) - , ToHie (PScoped (LPat a)) - , ToHie (MatchGroup a (LHsExpr a)) - , ToHie (LGRHS a (LHsExpr a)) - , ToHie (RContext (HsRecordBinds a)) - , ToHie (RFContext (Located (AmbiguousFieldOcc a))) - , ToHie (ArithSeqInfo a) - , ToHie (LHsCmdTop a) - , ToHie (RScoped (GuardLStmt a)) - , ToHie (RScoped (LHsLocalBinds a)) - , ToHie (TScoped (LHsWcType (NoGhcTc a))) - , ToHie (TScoped (LHsSigWcType (NoGhcTc a))) - , ToHie (TScoped (XExprWithTySig (GhcPass p))) - , ToHie (TScoped (XAppTypeE (GhcPass p))) - , Data (HsExpr a) - , Data (HsSplice a) - , Data (HsTupArg a) - , Data (AmbiguousFieldOcc a) - ) => ToHie (LHsExpr (GhcPass p)) where - toHie e@(L mspan oexpr) = concatM $ getTypeNode e : case oexpr of - HsVar _ (L _ var) -> - [ toHie $ C Use (L mspan var) - -- Patch up var location since typechecker removes it - ] - HsUnboundVar _ _ -> - [] - HsConLikeOut _ con -> - [ toHie $ C Use $ L mspan $ conLikeName con - ] - HsRecFld _ fld -> - [ toHie $ RFC RecFieldOcc Nothing (L mspan fld) - ] - HsOverLabel _ _ _ -> [] - HsIPVar _ _ -> [] - HsOverLit _ _ -> [] - HsLit _ _ -> [] - HsLam _ mg -> - [ toHie mg - ] - HsLamCase _ mg -> - [ toHie mg - ] - HsApp _ a b -> - [ toHie a - , toHie b - ] - HsAppType sig expr -> - [ toHie expr - , toHie $ TS (ResolvedScopes []) sig - ] - OpApp _ a b c -> - [ toHie a - , toHie b - , toHie c - ] - NegApp _ a _ -> - [ toHie a - ] - HsPar _ a -> - [ toHie a - ] - SectionL _ a b -> - [ toHie a - , toHie b - ] - SectionR _ a b -> - [ toHie a - , toHie b - ] - ExplicitTuple _ args _ -> - [ toHie args - ] - ExplicitSum _ _ _ expr -> - [ toHie expr - ] - HsCase _ expr matches -> - [ toHie expr - , toHie matches - ] - HsIf _ _ a b c -> - [ toHie a - , toHie b - , toHie c - ] - HsMultiIf _ grhss -> - [ toHie grhss - ] - HsLet _ binds expr -> - [ toHie $ RS (mkLScope expr) binds - , toHie expr - ] - HsDo _ _ (L ispan stmts) -> - [ pure $ locOnly ispan - , toHie $ listScopes NoScope stmts - ] - ExplicitList _ _ exprs -> - [ toHie exprs - ] - RecordCon {rcon_con_name = name, rcon_flds = binds}-> - [ toHie $ C Use name - , toHie $ RC RecFieldAssign $ binds - ] - RecordUpd {rupd_expr = expr, rupd_flds = upds}-> - [ toHie expr - , toHie $ map (RC RecFieldAssign) upds - ] - ExprWithTySig sig expr -> - [ toHie expr - , toHie $ TS (ResolvedScopes [mkLScope expr]) sig - ] - ArithSeq _ _ info -> - [ toHie info - ] - HsSCC _ _ _ expr -> - [ toHie expr - ] - HsCoreAnn _ _ _ expr -> - [ toHie expr - ] - HsProc _ pat cmdtop -> - [ toHie $ PS Nothing (mkLScope cmdtop) NoScope pat - , toHie cmdtop - ] - HsStatic _ expr -> - [ toHie expr - ] - HsArrApp _ a b _ _ -> - [ toHie a - , toHie b - ] - HsArrForm _ expr _ cmds -> - [ toHie expr - , toHie cmds - ] - HsTick _ _ expr -> - [ toHie expr - ] - HsBinTick _ _ _ expr -> - [ toHie expr - ] - HsTickPragma _ _ _ _ expr -> - [ toHie expr - ] - HsWrap _ _ a -> - [ toHie $ L mspan a - ] - HsBracket _ b -> - [ toHie b - ] - HsRnBracketOut _ b p -> - [ toHie b - , toHie p - ] - HsTcBracketOut _ b p -> - [ toHie b - , toHie p - ] - HsSpliceE _ x -> - [ toHie $ L mspan x - ] - EWildPat _ -> [] - EAsPat _ a b -> - [ toHie $ C Use a - , toHie b - ] - EViewPat _ a b -> - [ toHie a - , toHie b - ] - ELazyPat _ a -> - [ toHie a - ] - XExpr _ -> [] - -instance ( a ~ GhcPass p - , ToHie (LHsExpr a) - , Data (HsTupArg a) - ) => ToHie (LHsTupArg (GhcPass p)) where - toHie (L span arg) = concatM $ makeNode arg span : case arg of - Present _ expr -> - [ toHie expr - ] - Missing _ -> [] - XTupArg _ -> [] - -instance ( a ~ GhcPass p - , ToHie (PScoped (LPat a)) - , ToHie (LHsExpr a) - , ToHie (SigContext (LSig a)) - , ToHie (RScoped (LHsLocalBinds a)) - , ToHie (RScoped (ApplicativeArg a)) - , ToHie (Located body) - , Data (StmtLR a a (Located body)) - , Data (StmtLR a a (Located (HsExpr a))) - ) => ToHie (RScoped (LStmt (GhcPass p) (Located body))) where - toHie (RS scope (L span stmt)) = concatM $ makeNode stmt span : case stmt of - LastStmt _ body _ _ -> - [ toHie body - ] - BindStmt _ pat body _ _ -> - [ toHie $ PS (getRealSpan $ getLoc body) scope NoScope pat - , toHie body - ] - ApplicativeStmt _ stmts _ -> - [ concatMapM (toHie . RS scope . snd) stmts - ] - BodyStmt _ body _ _ -> - [ toHie body - ] - LetStmt _ binds -> - [ toHie $ RS scope binds - ] - ParStmt _ parstmts _ _ -> - [ concatMapM (\(ParStmtBlock _ stmts _ _) -> - toHie $ listScopes NoScope stmts) - parstmts - ] - TransStmt {trS_stmts = stmts, trS_using = using, trS_by = by} -> - [ toHie $ listScopes scope stmts - , toHie using - , toHie by - ] - RecStmt {recS_stmts = stmts} -> - [ toHie $ map (RS $ combineScopes scope (mkScope span)) stmts - ] - XStmtLR _ -> [] - -instance ( ToHie (LHsExpr a) - , ToHie (PScoped (LPat a)) - , ToHie (BindContext (LHsBind a)) - , ToHie (SigContext (LSig a)) - , ToHie (RScoped (HsValBindsLR a a)) - , Data (HsLocalBinds a) - ) => ToHie (RScoped (LHsLocalBinds a)) where - toHie (RS scope (L sp binds)) = concatM $ makeNode binds sp : case binds of - EmptyLocalBinds _ -> [] - HsIPBinds _ _ -> [] - HsValBinds _ valBinds -> - [ toHie $ RS (combineScopes scope $ mkScope sp) - valBinds - ] - XHsLocalBindsLR _ -> [] - -instance ( ToHie (BindContext (LHsBind a)) - , ToHie (SigContext (LSig a)) - , ToHie (RScoped (XXValBindsLR a a)) - ) => ToHie (RScoped (HsValBindsLR a a)) where - toHie (RS sc v) = concatM $ case v of - ValBinds _ binds sigs -> - [ toHie $ fmap (BC RegularBind sc) binds - , toHie $ fmap (SC (SI BindSig Nothing)) sigs - ] - XValBindsLR x -> [ toHie $ RS sc x ] - -instance ToHie (RScoped (NHsValBindsLR GhcTc)) where - toHie (RS sc (NValBinds binds sigs)) = concatM $ - [ toHie (concatMap (map (BC RegularBind sc) . bagToList . snd) binds) - , toHie $ fmap (SC (SI BindSig Nothing)) sigs - ] -instance ToHie (RScoped (NHsValBindsLR GhcRn)) where - toHie (RS sc (NValBinds binds sigs)) = concatM $ - [ toHie (concatMap (map (BC RegularBind sc) . bagToList . snd) binds) - , toHie $ fmap (SC (SI BindSig Nothing)) sigs - ] - -instance ( ToHie (RContext (LHsRecField a arg)) - ) => ToHie (RContext (HsRecFields a arg)) where - toHie (RC c (HsRecFields fields _)) = toHie $ map (RC c) fields - -instance ( ToHie (RFContext (Located label)) - , ToHie arg - , HasLoc arg - , Data label - , Data arg - ) => ToHie (RContext (LHsRecField' label arg)) where - toHie (RC c (L span recfld)) = concatM $ makeNode recfld span : case recfld of - HsRecField label expr _ -> - [ toHie $ RFC c (getRealSpan $ loc expr) label - , toHie expr - ] - -instance ToHie (RFContext (LFieldOcc GhcRn)) where - toHie (RFC c rhs (L nspan f)) = concatM $ case f of - FieldOcc name _ -> - [ toHie $ C (RecField c rhs) (L nspan name) - ] - XFieldOcc _ -> [] - -instance ToHie (RFContext (LFieldOcc GhcTc)) where - toHie (RFC c rhs (L nspan f)) = concatM $ case f of - FieldOcc var _ -> - let var' = setVarName var (varName var) - in [ toHie $ C (RecField c rhs) (L nspan var') - ] - XFieldOcc _ -> [] - -instance ToHie (RFContext (Located (AmbiguousFieldOcc GhcRn))) where - toHie (RFC c rhs (L nspan afo)) = concatM $ case afo of - Unambiguous name _ -> - [ toHie $ C (RecField c rhs) $ L nspan name - ] - Ambiguous _name _ -> - [ ] - XAmbiguousFieldOcc _ -> [] - -instance ToHie (RFContext (Located (AmbiguousFieldOcc GhcTc))) where - toHie (RFC c rhs (L nspan afo)) = concatM $ case afo of - Unambiguous var _ -> - let var' = setVarName var (varName var) - in [ toHie $ C (RecField c rhs) (L nspan var') - ] - Ambiguous var _ -> - let var' = setVarName var (varName var) - in [ toHie $ C (RecField c rhs) (L nspan var') - ] - XAmbiguousFieldOcc _ -> [] - -instance ( a ~ GhcPass p - , ToHie (PScoped (LPat a)) - , ToHie (BindContext (LHsBind a)) - , ToHie (LHsExpr a) - , ToHie (SigContext (LSig a)) - , ToHie (RScoped (HsValBindsLR a a)) - , Data (StmtLR a a (Located (HsExpr a))) - , Data (HsLocalBinds a) - ) => ToHie (RScoped (ApplicativeArg (GhcPass p))) where - toHie (RS sc (ApplicativeArgOne _ pat expr _)) = concatM - [ toHie $ PS Nothing sc NoScope pat - , toHie expr - ] - toHie (RS sc (ApplicativeArgMany _ stmts _ pat)) = concatM - [ toHie $ listScopes NoScope stmts - , toHie $ PS Nothing sc NoScope pat - ] - toHie (RS _ (XApplicativeArg _)) = pure [] - -instance (ToHie arg, ToHie rec) => ToHie (HsConDetails arg rec) where - toHie (PrefixCon args) = toHie args - toHie (RecCon rec) = toHie rec - toHie (InfixCon a b) = concatM [ toHie a, toHie b] - -instance ( ToHie (LHsCmd a) - , Data (HsCmdTop a) - ) => ToHie (LHsCmdTop a) where - toHie (L span top) = concatM $ makeNode top span : case top of - HsCmdTop _ cmd -> - [ toHie cmd - ] - XCmdTop _ -> [] - -instance ( a ~ GhcPass p - , ToHie (PScoped (LPat a)) - , ToHie (BindContext (LHsBind a)) - , ToHie (LHsExpr a) - , ToHie (MatchGroup a (LHsCmd a)) - , ToHie (SigContext (LSig a)) - , ToHie (RScoped (HsValBindsLR a a)) - , Data (HsCmd a) - , Data (HsCmdTop a) - , Data (StmtLR a a (Located (HsCmd a))) - , Data (HsLocalBinds a) - , Data (StmtLR a a (Located (HsExpr a))) - ) => ToHie (LHsCmd (GhcPass p)) where - toHie (L span cmd) = concatM $ makeNode cmd span : case cmd of - HsCmdArrApp _ a b _ _ -> - [ toHie a - , toHie b - ] - HsCmdArrForm _ a _ _ cmdtops -> - [ toHie a - , toHie cmdtops - ] - HsCmdApp _ a b -> - [ toHie a - , toHie b - ] - HsCmdLam _ mg -> - [ toHie mg - ] - HsCmdPar _ a -> - [ toHie a - ] - HsCmdCase _ expr alts -> - [ toHie expr - , toHie alts - ] - HsCmdIf _ _ a b c -> - [ toHie a - , toHie b - , toHie c - ] - HsCmdLet _ binds cmd' -> - [ toHie $ RS (mkLScope cmd') binds - , toHie cmd' - ] - HsCmdDo _ (L ispan stmts) -> - [ pure $ locOnly ispan - , toHie $ listScopes NoScope stmts - ] - HsCmdWrap _ _ _ -> [] - XCmd _ -> [] - -instance ToHie (TyClGroup GhcRn) where - toHie (TyClGroup _ classes roles instances) = concatM - [ toHie classes - , toHie roles - , toHie instances - ] - toHie (XTyClGroup _) = pure [] - -instance ToHie (LTyClDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - FamDecl {tcdFam = fdecl} -> - [ toHie (L span fdecl) - ] - SynDecl {tcdLName = name, tcdTyVars = vars, tcdRhs = typ} -> - [ toHie $ C (Decl SynDec $ getRealSpan span) name - , toHie $ TS (ResolvedScopes [mkScope $ getLoc typ]) vars - , toHie typ - ] - DataDecl {tcdLName = name, tcdTyVars = vars, tcdDataDefn = defn} -> - [ toHie $ C (Decl DataDec $ getRealSpan span) name - , toHie $ TS (ResolvedScopes [quant_scope, rhs_scope]) vars - , toHie defn - ] - where - quant_scope = mkLScope $ dd_ctxt defn - rhs_scope = sig_sc `combineScopes` con_sc `combineScopes` deriv_sc - sig_sc = maybe NoScope mkLScope $ dd_kindSig defn - con_sc = foldr combineScopes NoScope $ map mkLScope $ dd_cons defn - deriv_sc = mkLScope $ dd_derivs defn - ClassDecl { tcdCtxt = context - , tcdLName = name - , tcdTyVars = vars - , tcdFDs = deps - , tcdSigs = sigs - , tcdMeths = meths - , tcdATs = typs - , tcdATDefs = deftyps - } -> - [ toHie $ C (Decl ClassDec $ getRealSpan span) name - , toHie context - , toHie $ TS (ResolvedScopes [context_scope, rhs_scope]) vars - , toHie deps - , toHie $ map (SC $ SI ClassSig $ getRealSpan span) sigs - , toHie $ fmap (BC InstanceBind ModuleScope) meths - , toHie typs - , concatMapM (pure . locOnly . getLoc) deftyps - , toHie $ map (go . unLoc) deftyps - ] - where - context_scope = mkLScope context - rhs_scope = foldl1' combineScopes $ map mkScope - [ loc deps, loc sigs, loc (bagToList meths), loc typs, loc deftyps] - - go :: TyFamDefltEqn GhcRn - -> FamEqn GhcRn (TScoped (LHsQTyVars GhcRn)) (LHsType GhcRn) - go (FamEqn a var pat b rhs) = - FamEqn a var (TS (ResolvedScopes [mkLScope rhs]) pat) b rhs - go (XFamEqn NoExt) = XFamEqn NoExt - XTyClDecl _ -> [] - -instance ToHie (LFamilyDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - FamilyDecl _ info name vars _ sig inj -> - [ toHie $ C (Decl FamDec $ getRealSpan span) name - , toHie $ TS (ResolvedScopes [rhsSpan]) vars - , toHie info - , toHie $ RS injSpan sig - , toHie inj - ] - where - rhsSpan = sigSpan `combineScopes` injSpan - sigSpan = mkScope $ getLoc sig - injSpan = maybe NoScope (mkScope . getLoc) inj - XFamilyDecl _ -> [] - -instance ToHie (FamilyInfo GhcRn) where - toHie (ClosedTypeFamily (Just eqns)) = concatM $ - [ concatMapM (pure . locOnly . getLoc) eqns - , toHie $ map go eqns - ] - where - go (L l ib) = TS (ResolvedScopes [mkScope l]) ib - toHie _ = pure [] - -instance ToHie (RScoped (LFamilyResultSig GhcRn)) where - toHie (RS sc (L span sig)) = concatM $ makeNode sig span : case sig of - NoSig _ -> - [] - KindSig _ k -> - [ toHie k - ] - TyVarSig _ bndr -> - [ toHie $ TVS (ResolvedScopes [sc]) NoScope bndr - ] - XFamilyResultSig _ -> [] - -instance ToHie (Located (FunDep (Located Name))) where - toHie (L span fd@(lhs, rhs)) = concatM $ - [ makeNode fd span - , toHie $ map (C Use) lhs - , toHie $ map (C Use) rhs - ] - -instance (ToHie pats, ToHie rhs, HasLoc pats, HasLoc rhs) - => ToHie (TScoped (FamEqn GhcRn pats rhs)) where - toHie (TS _ f) = toHie f - -instance ( ToHie pats - , ToHie rhs - , HasLoc pats - , HasLoc rhs - ) => ToHie (FamEqn GhcRn pats rhs) where - toHie fe@(FamEqn _ var pats _ rhs) = concatM $ - [ toHie $ C (Decl InstDec $ getRealSpan $ loc fe) var - , toHie pats - , toHie rhs - ] - toHie (XFamEqn _) = pure [] - -instance ToHie (LInjectivityAnn GhcRn) where - toHie (L span ann) = concatM $ makeNode ann span : case ann of - InjectivityAnn lhs rhs -> - [ toHie $ C Use lhs - , toHie $ map (C Use) rhs - ] - -instance ToHie (HsDataDefn GhcRn) where - toHie (HsDataDefn _ _ ctx _ mkind cons derivs) = concatM - [ toHie ctx - , toHie mkind - , toHie cons - , toHie derivs - ] - toHie (XHsDataDefn _) = pure [] - -instance ToHie (HsDeriving GhcRn) where - toHie (L span clauses) = concatM - [ pure $ locOnly span - , toHie clauses - ] - -instance ToHie (LHsDerivingClause GhcRn) where - toHie (L span cl) = concatM $ makeNode cl span : case cl of - HsDerivingClause _ strat (L ispan tys) -> - [ toHie strat - , pure $ locOnly ispan - , toHie $ map (TS (ResolvedScopes [])) tys - ] - XHsDerivingClause _ -> [] - -instance ToHie (Located (DerivStrategy GhcRn)) where - toHie (L span strat) = concatM $ makeNode strat span : case strat of - StockStrategy -> [] - AnyclassStrategy -> [] - NewtypeStrategy -> [] - ViaStrategy s -> [ toHie $ TS (ResolvedScopes []) s ] - -instance ToHie (Located OverlapMode) where - toHie (L span _) = pure $ locOnly span - -instance ToHie (LConDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - ConDeclGADT { con_names = names, con_qvars = qvars - , con_mb_cxt = ctx, con_args = args, con_res_ty = typ } -> - [ toHie $ map (C (Decl ConDec $ getRealSpan span)) names - , toHie $ TS (ResolvedScopes [ctxScope, rhsScope]) qvars - , toHie ctx - , toHie args - , toHie typ - ] - where - rhsScope = combineScopes argsScope tyScope - ctxScope = maybe NoScope mkLScope ctx - argsScope = condecl_scope args - tyScope = mkLScope typ - ConDeclH98 { con_name = name, con_ex_tvs = qvars - , con_mb_cxt = ctx, con_args = dets } -> - [ toHie $ C (Decl ConDec $ getRealSpan span) name - , toHie $ tvScopes (ResolvedScopes []) rhsScope qvars - , toHie ctx - , toHie dets - ] - where - rhsScope = combineScopes ctxScope argsScope - ctxScope = maybe NoScope mkLScope ctx - argsScope = condecl_scope dets - XConDecl _ -> [] - where condecl_scope args = case args of - PrefixCon xs -> foldr combineScopes NoScope $ map mkLScope xs - InfixCon a b -> combineScopes (mkLScope a) (mkLScope b) - RecCon x -> mkLScope x - -instance ToHie (Located [LConDeclField GhcRn]) where - toHie (L span decls) = concatM $ - [ pure $ locOnly span - , toHie decls - ] - -instance ( HasLoc thing - , ToHie (TScoped thing) - ) => ToHie (TScoped (HsImplicitBndrs GhcRn thing)) where - toHie (TS sc (HsIB ibrn a)) = concatM $ - [ pure $ bindingsOnly $ map (C $ TyVarBind (mkScope span) sc) $ (hsib_vars ibrn) - , toHie $ TS sc a - ] - where span = loc a - toHie (TS _ (XHsImplicitBndrs _)) = pure [] - -instance ( HasLoc thing - , ToHie (TScoped thing) - ) => ToHie (TScoped (HsWildCardBndrs GhcRn thing)) where - toHie (TS sc (HsWC names a)) = concatM $ - [ pure $ bindingsOnly $ map (C $ TyVarBind (mkScope span) sc) names - , toHie $ TS sc a - ] - where span = loc a - toHie (TS _ (XHsWildCardBndrs _)) = pure [] - -instance ToHie (SigContext (LSig GhcRn)) where - toHie (SC (SI styp msp) (L sp sig)) = concatM $ makeNode sig sp : case sig of - TypeSig _ names typ -> - [ toHie $ map (C TyDecl) names - , toHie $ TS (UnresolvedScope (map unLoc names) Nothing) typ - ] - PatSynSig _ names typ -> - [ toHie $ map (C TyDecl) names - , toHie $ TS (UnresolvedScope (map unLoc names) Nothing) typ - ] - ClassOpSig _ _ names typ -> - [ case styp of - ClassSig -> toHie $ map (C $ ClassTyDecl $ getRealSpan sp) names - _ -> toHie $ map (C $ TyDecl) names - , toHie $ TS (UnresolvedScope (map unLoc names) msp) typ - ] - IdSig _ _ -> [] - FixSig _ fsig -> - [ toHie $ L sp fsig - ] - InlineSig _ name _ -> - [ toHie $ (C Use) name - ] - SpecSig _ name typs _ -> - [ toHie $ (C Use) name - , toHie $ map (TS (ResolvedScopes [])) typs - ] - SpecInstSig _ _ typ -> - [ toHie $ TS (ResolvedScopes []) typ - ] - MinimalSig _ _ form -> - [ toHie form - ] - SCCFunSig _ _ name mtxt -> - [ toHie $ (C Use) name - , pure $ maybe [] (locOnly . getLoc) mtxt - ] - CompleteMatchSig _ _ (L ispan names) typ -> - [ pure $ locOnly ispan - , toHie $ map (C Use) names - , toHie $ fmap (C Use) typ - ] - XSig _ -> [] - -instance ToHie (LHsType GhcRn) where - toHie x = toHie $ TS (ResolvedScopes []) x - -instance ToHie (TScoped (LHsType GhcRn)) where - toHie (TS tsc (L span t)) = concatM $ makeNode t span : case t of - HsForAllTy _ bndrs body -> - [ toHie $ tvScopes tsc (mkScope $ getLoc body) bndrs - , toHie body - ] - HsQualTy _ ctx body -> - [ toHie ctx - , toHie body - ] - HsTyVar _ _ var -> - [ toHie $ C Use var - ] - HsAppTy _ a b -> - [ toHie a - , toHie b - ] - HsFunTy _ a b -> - [ toHie a - , toHie b - ] - HsListTy _ a -> - [ toHie a - ] - HsTupleTy _ _ tys -> - [ toHie tys - ] - HsSumTy _ tys -> - [ toHie tys - ] - HsOpTy _ a op b -> - [ toHie a - , toHie $ C Use op - , toHie b - ] - HsParTy _ a -> - [ toHie a - ] - HsIParamTy _ ip ty -> - [ toHie ip - , toHie ty - ] - HsKindSig _ a b -> - [ toHie a - , toHie b - ] - HsSpliceTy _ a -> - [ toHie $ L span a - ] - HsDocTy _ a _ -> - [ toHie a - ] - HsBangTy _ _ ty -> - [ toHie ty - ] - HsRecTy _ fields -> - [ toHie fields - ] - HsExplicitListTy _ _ tys -> - [ toHie tys - ] - HsExplicitTupleTy _ tys -> - [ toHie tys - ] - HsTyLit _ _ -> [] - HsWildCardTy _ -> [] - HsStarTy _ _ -> [] - XHsType _ -> [] - -{- -instance (ToHie tm, ToHie ty) => ToHie (HsArg tm ty) where - toHie (HsValArg tm) = toHie tm - toHie (HsTypeArg _ ty) = toHie ty - toHie (HsArgPar sp) = pure $ locOnly sp --} - -instance ToHie (TVScoped (LHsTyVarBndr GhcRn)) where - toHie (TVS tsc sc (L span bndr)) = concatM $ makeNode bndr span : case bndr of - UserTyVar _ var -> - [ toHie $ C (TyVarBind sc tsc) var - ] - KindedTyVar _ var kind -> - [ toHie $ C (TyVarBind sc tsc) var - , toHie kind - ] - XTyVarBndr _ -> [] - -instance ToHie (TScoped (LHsQTyVars GhcRn)) where - toHie (TS sc (HsQTvs (HsQTvsRn implicits _) vars)) = concatM $ - [ pure $ bindingsOnly bindings - , toHie $ tvScopes sc NoScope vars - ] - where - varLoc = loc vars - bindings = map (C $ TyVarBind (mkScope varLoc) sc) implicits - toHie (TS _ (XLHsQTyVars _)) = pure [] - -instance ToHie (LHsContext GhcRn) where - toHie (L span tys) = concatM $ - [ pure $ locOnly span - , toHie tys - ] - -instance ToHie (LConDeclField GhcRn) where - toHie (L span field) = concatM $ makeNode field span : case field of - ConDeclField _ fields typ _ -> - [ toHie $ map (RFC RecFieldDecl (getRealSpan $ loc typ)) fields - , toHie typ - ] - XConDeclField _ -> [] - -instance ToHie (LHsExpr a) => ToHie (ArithSeqInfo a) where - toHie (From expr) = toHie expr - toHie (FromThen a b) = concatM $ - [ toHie a - , toHie b - ] - toHie (FromTo a b) = concatM $ - [ toHie a - , toHie b - ] - toHie (FromThenTo a b c) = concatM $ - [ toHie a - , toHie b - , toHie c - ] - -instance ToHie (LSpliceDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - SpliceDecl _ splice _ -> - [ toHie splice - ] - XSpliceDecl _ -> [] - -instance ToHie (HsBracket a) where - toHie _ = pure [] - -instance ToHie PendingRnSplice where - toHie _ = pure [] - -instance ToHie PendingTcSplice where - toHie _ = pure [] - -instance ToHie (LBooleanFormula (Located Name)) where - toHie (L span form) = concatM $ makeNode form span : case form of - Var a -> - [ toHie $ C Use a - ] - And forms -> - [ toHie forms - ] - Or forms -> - [ toHie forms - ] - Parens f -> - [ toHie f - ] - -instance ToHie (Located HsIPName) where - toHie (L span e) = makeNode e span - -instance ( ToHie (LHsExpr a) - , Data (HsSplice a) - ) => ToHie (Located (HsSplice a)) where - toHie (L span sp) = concatM $ makeNode sp span : case sp of - HsTypedSplice _ _ _ expr -> - [ toHie expr - ] - HsUntypedSplice _ _ _ expr -> - [ toHie expr - ] - HsQuasiQuote _ _ _ ispan _ -> - [ pure $ locOnly ispan - ] - HsSpliced _ _ _ -> - [] - XSplice _ -> [] - -instance ToHie (LRoleAnnotDecl GhcRn) where - toHie (L span annot) = concatM $ makeNode annot span : case annot of - RoleAnnotDecl _ var roles -> - [ toHie $ C Use var - , concatMapM (pure . locOnly . getLoc) roles - ] - XRoleAnnotDecl _ -> [] - -instance ToHie (LInstDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - ClsInstD _ d -> - [ toHie $ L span d - ] - DataFamInstD _ d -> - [ toHie $ L span d - ] - TyFamInstD _ d -> - [ toHie $ L span d - ] - XInstDecl _ -> [] - -instance ToHie (LClsInstDecl GhcRn) where - toHie (L span decl) = concatM - [ toHie $ TS (ResolvedScopes [mkScope span]) $ cid_poly_ty decl - , toHie $ fmap (BC InstanceBind ModuleScope) $ cid_binds decl - , toHie $ map (SC $ SI InstSig $ getRealSpan span) $ cid_sigs decl - , pure $ concatMap (locOnly . getLoc) $ cid_tyfam_insts decl - , toHie $ cid_tyfam_insts decl - , pure $ concatMap (locOnly . getLoc) $ cid_datafam_insts decl - , toHie $ cid_datafam_insts decl - , toHie $ cid_overlap_mode decl - ] - -instance ToHie (LDataFamInstDecl GhcRn) where - toHie (L sp (DataFamInstDecl d)) = toHie $ TS (ResolvedScopes [mkScope sp]) d - -instance ToHie (LTyFamInstDecl GhcRn) where - toHie (L sp (TyFamInstDecl d)) = toHie $ TS (ResolvedScopes [mkScope sp]) d - -instance ToHie (Context a) - => ToHie (PatSynFieldContext (RecordPatSynField a)) where - toHie (PSC sp (RecordPatSynField a b)) = concatM $ - [ toHie $ C (RecField RecFieldDecl sp) a - , toHie $ C Use b - ] - -instance ToHie (LDerivDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - DerivDecl _ typ strat overlap -> - [ toHie $ TS (ResolvedScopes []) typ - , toHie strat - , toHie overlap - ] - XDerivDecl _ -> [] - -instance ToHie (LFixitySig GhcRn) where - toHie (L span sig) = concatM $ makeNode sig span : case sig of - FixitySig _ vars _ -> - [ toHie $ map (C Use) vars - ] - XFixitySig _ -> [] - -instance ToHie (LDefaultDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - DefaultDecl _ typs -> - [ toHie typs - ] - XDefaultDecl _ -> [] - -instance ToHie (LForeignDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - ForeignImport {fd_name = name, fd_sig_ty = sig, fd_fi = fi} -> - [ toHie $ C (ValBind RegularBind ModuleScope $ getRealSpan span) name - , toHie $ TS (ResolvedScopes []) sig - , toHie fi - ] - ForeignExport {fd_name = name, fd_sig_ty = sig, fd_fe = fe} -> - [ toHie $ C Use name - , toHie $ TS (ResolvedScopes []) sig - , toHie fe - ] - XForeignDecl _ -> [] - -instance ToHie ForeignImport where - toHie (CImport (L a _) (L b _) _ _ (L c _)) = pure $ concat $ - [ locOnly a - , locOnly b - , locOnly c - ] - -instance ToHie ForeignExport where - toHie (CExport (L a _) (L b _)) = pure $ concat $ - [ locOnly a - , locOnly b - ] - -instance ToHie (LWarnDecls GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - Warnings _ _ warnings -> - [ toHie warnings - ] - XWarnDecls _ -> [] - -instance ToHie (LWarnDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - Warning _ vars _ -> - [ toHie $ map (C Use) vars - ] - XWarnDecl _ -> [] - -instance ToHie (LAnnDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - HsAnnotation _ _ prov expr -> - [ toHie prov - , toHie expr - ] - XAnnDecl _ -> [] - -instance ToHie (Context (Located a)) => ToHie (AnnProvenance a) where - toHie (ValueAnnProvenance a) = toHie $ C Use a - toHie (TypeAnnProvenance a) = toHie $ C Use a - toHie ModuleAnnProvenance = pure [] - -instance ToHie (LRuleDecls GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - HsRules _ _ rules -> - [ toHie rules - ] - XRuleDecls _ -> [] - -instance ToHie (LRuleDecl GhcRn) where - toHie (L _ (XRuleDecl _)) = pure [] - toHie (L span r@(HsRule _ rname _ bndrs exprA exprB)) = concatM - [ makeNode r span - , pure $ locOnly $ getLoc rname - , toHie $ map (RS $ mkScope span) bndrs - , toHie exprA - , toHie exprB - ] - -instance ToHie (RScoped (LRuleBndr GhcRn)) where - toHie (RS sc (L span bndr)) = concatM $ makeNode bndr span : case bndr of - RuleBndr _ var -> - [ toHie $ C (ValBind RegularBind sc Nothing) var - ] - RuleBndrSig _ var typ -> - [ toHie $ C (ValBind RegularBind sc Nothing) var - , toHie $ TS (ResolvedScopes [sc]) typ - ] - XRuleBndr _ -> [] - -instance ToHie (LImportDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - ImportDecl { ideclName = name, ideclAs = as, ideclHiding = hidden } -> - [ toHie $ IEC Import name - , toHie $ fmap (IEC ImportAs) as - , maybe (pure []) goIE hidden - ] - XImportDecl _ -> [] - where - goIE (hiding, (L sp liens)) = concatM $ - [ pure $ locOnly sp - , toHie $ map (IEC c) liens - ] - where - c = if hiding then ImportHiding else Import - -instance ToHie (IEContext (LIE GhcRn)) where - toHie (IEC c (L span ie)) = concatM $ makeNode ie span : case ie of - IEVar _ n -> - [ toHie $ IEC c n - ] - IEThingAbs _ n -> - [ toHie $ IEC c n - ] - IEThingAll _ n -> - [ toHie $ IEC c n - ] - IEThingWith _ n _ ns flds -> - [ toHie $ IEC c n - , toHie $ map (IEC c) ns - , toHie $ map (IEC c) flds - ] - IEModuleContents _ n -> - [ toHie $ IEC c n - ] - IEGroup _ _ _ -> [] - IEDoc _ _ -> [] - IEDocNamed _ _ -> [] - XIE _ -> [] - -instance ToHie (IEContext (LIEWrappedName Name)) where - toHie (IEC c (L span iewn)) = concatM $ makeNode iewn span : case iewn of - IEName n -> - [ toHie $ C (IEThing c) n - ] - IEPattern p -> - [ toHie $ C (IEThing c) p - ] - IEType n -> - [ toHie $ C (IEThing c) n - ] - -instance ToHie (IEContext (Located (FieldLbl Name))) where - toHie (IEC c (L span lbl)) = concatM $ makeNode lbl span : case lbl of - FieldLabel _ _ n -> - [ toHie $ C (IEThing c) $ L span n - ] - diff --git a/hie-compat/src-ghc86/Compat/HieBin.hs b/hie-compat/src-ghc86/Compat/HieBin.hs deleted file mode 100644 index b02fe63b2e..0000000000 --- a/hie-compat/src-ghc86/Compat/HieBin.hs +++ /dev/null @@ -1,388 +0,0 @@ -{- -Binary serialization for .hie files. --} -{-# LANGUAGE ScopedTypeVariables #-} -module Compat.HieBin ( readHieFile, readHieFileWithVersion, HieHeader, writeHieFile, HieName(..), toHieName, HieFileResult(..), hieMagic,NameCacheUpdater(..)) where - -import Config ( cProjectVersion ) -import Binary -import BinIface ( getDictFastString ) -import FastMutInt -import FastString ( FastString ) -import Module ( Module ) -import Name -import NameCache -import Outputable -import PrelInfo -import SrcLoc -import UniqSupply ( takeUniqFromSupply ) -import Util ( maybeRead ) -import Unique -import UniqFM -import IfaceEnv - -import qualified Data.Array as A -import Data.IORef -import Data.ByteString ( ByteString ) -import qualified Data.ByteString as BS -import qualified Data.ByteString.Char8 as BSC -import Data.List ( mapAccumR ) -import Data.Word ( Word8, Word32 ) -import Control.Monad ( replicateM, when ) -import System.Directory ( createDirectoryIfMissing ) -import System.FilePath ( takeDirectory ) - -import Compat.HieTypes - --- | `Name`'s get converted into `HieName`'s before being written into @.hie@ --- files. See 'toHieName' and 'fromHieName' for logic on how to convert between --- these two types. -data HieName - = ExternalName !Module !OccName !SrcSpan - | LocalName !OccName !SrcSpan - | KnownKeyName !Unique - deriving (Eq) - -instance Ord HieName where - compare (ExternalName a b c) (ExternalName d e f) = compare (a,b,c) (d,e,f) - compare (LocalName a b) (LocalName c d) = compare (a,b) (c,d) - compare (KnownKeyName a) (KnownKeyName b) = nonDetCmpUnique a b - -- Not actually non determinstic as it is a KnownKey - compare ExternalName{} _ = LT - compare LocalName{} ExternalName{} = GT - compare LocalName{} _ = LT - compare KnownKeyName{} _ = GT - -instance Outputable HieName where - ppr (ExternalName m n sp) = text "ExternalName" <+> ppr m <+> ppr n <+> ppr sp - ppr (LocalName n sp) = text "LocalName" <+> ppr n <+> ppr sp - ppr (KnownKeyName u) = text "KnownKeyName" <+> ppr u - - -data HieSymbolTable = HieSymbolTable - { hie_symtab_next :: !FastMutInt - , hie_symtab_map :: !(IORef (UniqFM (Int, HieName))) - } - -data HieDictionary = HieDictionary - { hie_dict_next :: !FastMutInt -- The next index to use - , hie_dict_map :: !(IORef (UniqFM (Int,FastString))) -- indexed by FastString - } - -initBinMemSize :: Int -initBinMemSize = 1024*1024 - --- | The header for HIE files - Capital ASCII letters "HIE". -hieMagic :: [Word8] -hieMagic = [72,73,69] - -hieMagicLen :: Int -hieMagicLen = length hieMagic - -ghcVersion :: ByteString -ghcVersion = BSC.pack cProjectVersion - -putBinLine :: BinHandle -> ByteString -> IO () -putBinLine bh xs = do - mapM_ (putByte bh) $ BS.unpack xs - putByte bh 10 -- newline char - --- | Write a `HieFile` to the given `FilePath`, with a proper header and --- symbol tables for `Name`s and `FastString`s -writeHieFile :: FilePath -> HieFile -> IO () -writeHieFile hie_file_path hiefile = do - bh0 <- openBinMem initBinMemSize - - -- Write the header: hieHeader followed by the - -- hieVersion and the GHC version used to generate this file - mapM_ (putByte bh0) hieMagic - putBinLine bh0 $ BSC.pack $ show hieVersion - putBinLine bh0 ghcVersion - - -- remember where the dictionary pointer will go - dict_p_p <- tellBin bh0 - put_ bh0 dict_p_p - - -- remember where the symbol table pointer will go - symtab_p_p <- tellBin bh0 - put_ bh0 symtab_p_p - - -- Make some intial state - symtab_next <- newFastMutInt - writeFastMutInt symtab_next 0 - symtab_map <- newIORef emptyUFM - let hie_symtab = HieSymbolTable { - hie_symtab_next = symtab_next, - hie_symtab_map = symtab_map } - dict_next_ref <- newFastMutInt - writeFastMutInt dict_next_ref 0 - dict_map_ref <- newIORef emptyUFM - let hie_dict = HieDictionary { - hie_dict_next = dict_next_ref, - hie_dict_map = dict_map_ref } - - -- put the main thing - let bh = setUserData bh0 $ newWriteState (putName hie_symtab) - (putName hie_symtab) - (putFastString hie_dict) - put_ bh hiefile - - -- write the symtab pointer at the front of the file - symtab_p <- tellBin bh - putAt bh symtab_p_p symtab_p - seekBin bh symtab_p - - -- write the symbol table itself - symtab_next' <- readFastMutInt symtab_next - symtab_map' <- readIORef symtab_map - putSymbolTable bh symtab_next' symtab_map' - - -- write the dictionary pointer at the front of the file - dict_p <- tellBin bh - putAt bh dict_p_p dict_p - seekBin bh dict_p - - -- write the dictionary itself - dict_next <- readFastMutInt dict_next_ref - dict_map <- readIORef dict_map_ref - putDictionary bh dict_next dict_map - - -- and send the result to the file - createDirectoryIfMissing True (takeDirectory hie_file_path) - writeBinMem bh hie_file_path - return () - -data HieFileResult - = HieFileResult - { hie_file_result_version :: Integer - , hie_file_result_ghc_version :: ByteString - , hie_file_result :: HieFile - } - -type HieHeader = (Integer, ByteString) - --- | Read a `HieFile` from a `FilePath`. Can use --- an existing `NameCache`. Allows you to specify --- which versions of hieFile to attempt to read. --- `Left` case returns the failing header versions. -readHieFileWithVersion :: (HieHeader -> Bool) -> NameCacheUpdater -> FilePath -> IO (Either HieHeader HieFileResult) -readHieFileWithVersion readVersion ncu file = do - bh0 <- readBinMem file - - (hieVersion, ghcVersion) <- readHieFileHeader file bh0 - - if readVersion (hieVersion, ghcVersion) - then do - hieFile <- readHieFileContents bh0 ncu - return $ Right (HieFileResult hieVersion ghcVersion hieFile) - else return $ Left (hieVersion, ghcVersion) - - --- | Read a `HieFile` from a `FilePath`. Can use --- an existing `NameCache`. -readHieFile :: NameCacheUpdater -> FilePath -> IO HieFileResult -readHieFile ncu file = do - - bh0 <- readBinMem file - - (readHieVersion, ghcVersion) <- readHieFileHeader file bh0 - - -- Check if the versions match - when (readHieVersion /= hieVersion) $ - panic $ unwords ["readHieFile: hie file versions don't match for file:" - , file - , "Expected" - , show hieVersion - , "but got", show readHieVersion - ] - hieFile <- readHieFileContents bh0 ncu - return $ HieFileResult hieVersion ghcVersion hieFile - -readBinLine :: BinHandle -> IO ByteString -readBinLine bh = BS.pack . reverse <$> loop [] - where - loop acc = do - char <- get bh :: IO Word8 - if char == 10 -- ASCII newline '\n' - then return acc - else loop (char : acc) - -readHieFileHeader :: FilePath -> BinHandle -> IO HieHeader -readHieFileHeader file bh0 = do - -- Read the header - magic <- replicateM hieMagicLen (get bh0) - version <- BSC.unpack <$> readBinLine bh0 - case maybeRead version of - Nothing -> - panic $ unwords ["readHieFileHeader: hieVersion isn't an Integer:" - , show version - ] - Just readHieVersion -> do - ghcVersion <- readBinLine bh0 - - -- Check if the header is valid - when (magic /= hieMagic) $ - panic $ unwords ["readHieFileHeader: headers don't match for file:" - , file - , "Expected" - , show hieMagic - , "but got", show magic - ] - return (readHieVersion, ghcVersion) - -readHieFileContents :: BinHandle -> NameCacheUpdater -> IO HieFile -readHieFileContents bh0 ncu = do - - dict <- get_dictionary bh0 - - -- read the symbol table so we are capable of reading the actual data - bh1 <- do - let bh1 = setUserData bh0 $ newReadState (error "getSymtabName") - (getDictFastString dict) - symtab <- get_symbol_table bh1 - let bh1' = setUserData bh1 - $ newReadState (getSymTabName symtab) - (getDictFastString dict) - return bh1' - - -- load the actual data - hiefile <- get bh1 - return hiefile - where - get_dictionary bin_handle = do - dict_p <- get bin_handle - data_p <- tellBin bin_handle - seekBin bin_handle dict_p - dict <- getDictionary bin_handle - seekBin bin_handle data_p - return dict - - get_symbol_table bh1 = do - symtab_p <- get bh1 - data_p' <- tellBin bh1 - seekBin bh1 symtab_p - symtab <- getSymbolTable bh1 ncu - seekBin bh1 data_p' - return symtab - -putFastString :: HieDictionary -> BinHandle -> FastString -> IO () -putFastString HieDictionary { hie_dict_next = j_r, - hie_dict_map = out_r} bh f - = do - out <- readIORef out_r - let unique = getUnique f - case lookupUFM out unique of - Just (j, _) -> put_ bh (fromIntegral j :: Word32) - Nothing -> do - j <- readFastMutInt j_r - put_ bh (fromIntegral j :: Word32) - writeFastMutInt j_r (j + 1) - writeIORef out_r $! addToUFM out unique (j, f) - -putSymbolTable :: BinHandle -> Int -> UniqFM (Int,HieName) -> IO () -putSymbolTable bh next_off symtab = do - put_ bh next_off - let names = A.elems (A.array (0,next_off-1) (nonDetEltsUFM symtab)) - mapM_ (putHieName bh) names - -getSymbolTable :: BinHandle -> NameCacheUpdater -> IO SymbolTable -getSymbolTable bh ncu = do - sz <- get bh - od_names <- replicateM sz (getHieName bh) - updateNameCache ncu $ \nc -> - let arr = A.listArray (0,sz-1) names - (nc', names) = mapAccumR fromHieName nc od_names - in (nc',arr) - -getSymTabName :: SymbolTable -> BinHandle -> IO Name -getSymTabName st bh = do - i :: Word32 <- get bh - return $ st A.! fromIntegral i - -putName :: HieSymbolTable -> BinHandle -> Name -> IO () -putName (HieSymbolTable next ref) bh name = do - symmap <- readIORef ref - case lookupUFM symmap name of - Just (off, ExternalName mod occ (UnhelpfulSpan _)) - | isGoodSrcSpan (nameSrcSpan name) -> do - let hieName = ExternalName mod occ (nameSrcSpan name) - writeIORef ref $! addToUFM symmap name (off, hieName) - put_ bh (fromIntegral off :: Word32) - Just (off, LocalName _occ span) - | notLocal (toHieName name) || nameSrcSpan name /= span -> do - writeIORef ref $! addToUFM symmap name (off, toHieName name) - put_ bh (fromIntegral off :: Word32) - Just (off, _) -> put_ bh (fromIntegral off :: Word32) - Nothing -> do - off <- readFastMutInt next - writeFastMutInt next (off+1) - writeIORef ref $! addToUFM symmap name (off, toHieName name) - put_ bh (fromIntegral off :: Word32) - - where - notLocal :: HieName -> Bool - notLocal LocalName{} = False - notLocal _ = True - - --- ** Converting to and from `HieName`'s - -toHieName :: Name -> HieName -toHieName name - | isKnownKeyName name = KnownKeyName (nameUnique name) - | isExternalName name = ExternalName (nameModule name) - (nameOccName name) - (nameSrcSpan name) - | otherwise = LocalName (nameOccName name) (nameSrcSpan name) - -fromHieName :: NameCache -> HieName -> (NameCache, Name) -fromHieName nc (ExternalName mod occ span) = - let cache = nsNames nc - in case lookupOrigNameCache cache mod occ of - Just name - | nameSrcSpan name == span -> (nc, name) - | otherwise -> - let name' = setNameLoc name span - new_cache = extendNameCache cache mod occ name' - in ( nc{ nsNames = new_cache }, name' ) - Nothing -> - let (uniq, us) = takeUniqFromSupply (nsUniqs nc) - name = mkExternalName uniq mod occ span - new_cache = extendNameCache cache mod occ name - in ( nc{ nsUniqs = us, nsNames = new_cache }, name ) -fromHieName nc (LocalName occ span) = - let (uniq, us) = takeUniqFromSupply (nsUniqs nc) - name = mkInternalName uniq occ span - in ( nc{ nsUniqs = us }, name ) -fromHieName nc (KnownKeyName u) = case lookupKnownKeyName u of - Nothing -> pprPanic "fromHieName:unknown known-key unique" - (ppr (unpkUnique u)) - Just n -> (nc, n) - --- ** Reading and writing `HieName`'s - -putHieName :: BinHandle -> HieName -> IO () -putHieName bh (ExternalName mod occ span) = do - putByte bh 0 - put_ bh (mod, occ, span) -putHieName bh (LocalName occName span) = do - putByte bh 1 - put_ bh (occName, span) -putHieName bh (KnownKeyName uniq) = do - putByte bh 2 - put_ bh $ unpkUnique uniq - -getHieName :: BinHandle -> IO HieName -getHieName bh = do - t <- getByte bh - case t of - 0 -> do - (modu, occ, span) <- get bh - return $ ExternalName modu occ span - 1 -> do - (occ, span) <- get bh - return $ LocalName occ span - 2 -> do - (c,i) <- get bh - return $ KnownKeyName $ mkUnique c i - _ -> panic "HieBin.getHieName: invalid tag" diff --git a/hie-compat/src-ghc86/Compat/HieDebug.hs b/hie-compat/src-ghc86/Compat/HieDebug.hs deleted file mode 100644 index c3df58f2f8..0000000000 --- a/hie-compat/src-ghc86/Compat/HieDebug.hs +++ /dev/null @@ -1,145 +0,0 @@ -{- -Functions to validate and check .hie file ASTs generated by GHC. --} -{-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE FlexibleContexts #-} -module Compat.HieDebug where - -import Prelude hiding ((<>)) -import SrcLoc -import Module -import FastString -import Outputable - -import Compat.HieTypes -import Compat.HieBin -import Compat.HieUtils - -import qualified Data.Map as M -import qualified Data.Set as S -import Data.Function ( on ) -import Data.List ( sortOn ) -import Data.Foldable ( toList ) - -ppHies :: Outputable a => HieASTs a -> SDoc -ppHies (HieASTs asts) = M.foldrWithKey go "" asts - where - go k a rest = vcat - [ "File: " <> ppr k - , ppHie a - , rest - ] - -ppHie :: Outputable a => HieAST a -> SDoc -ppHie = go 0 - where - go n (Node inf sp children) = hang header n rest - where - rest = vcat $ map (go (n+2)) children - header = hsep - [ "Node" - , ppr sp - , ppInfo inf - ] - -ppInfo :: Outputable a => NodeInfo a -> SDoc -ppInfo ni = hsep - [ ppr $ toList $ nodeAnnotations ni - , ppr $ nodeType ni - , ppr $ M.toList $ nodeIdentifiers ni - ] - -type Diff a = a -> a -> [SDoc] - -diffFile :: Diff HieFile -diffFile = diffAsts eqDiff `on` (getAsts . hie_asts) - -diffAsts :: (Outputable a, Eq a) => Diff a -> Diff (M.Map FastString (HieAST a)) -diffAsts f = diffList (diffAst f) `on` M.elems - -diffAst :: (Outputable a, Eq a) => Diff a -> Diff (HieAST a) -diffAst diffType (Node info1 span1 xs1) (Node info2 span2 xs2) = - infoDiff ++ spanDiff ++ diffList (diffAst diffType) xs1 xs2 - where - spanDiff - | span1 /= span2 = [hsep ["Spans", ppr span1, "and", ppr span2, "differ"]] - | otherwise = [] - infoDiff - = (diffList eqDiff `on` (S.toAscList . nodeAnnotations)) info1 info2 - ++ (diffList diffType `on` nodeType) info1 info2 - ++ (diffIdents `on` nodeIdentifiers) info1 info2 - diffIdents a b = (diffList diffIdent `on` normalizeIdents) a b - diffIdent (a,b) (c,d) = diffName a c - ++ eqDiff b d - diffName (Right a) (Right b) = case (a,b) of - (ExternalName m o _, ExternalName m' o' _) -> eqDiff (m,o) (m',o') - (LocalName o _, ExternalName _ o' _) -> eqDiff o o' - _ -> eqDiff a b - diffName a b = eqDiff a b - -type DiffIdent = Either ModuleName HieName - -normalizeIdents :: NodeIdentifiers a -> [(DiffIdent,IdentifierDetails a)] -normalizeIdents = sortOn fst . map (first toHieName) . M.toList - where - first f (a,b) = (fmap f a, b) - -diffList :: Diff a -> Diff [a] -diffList f xs ys - | length xs == length ys = concat $ zipWith f xs ys - | otherwise = ["length of lists doesn't match"] - -eqDiff :: (Outputable a, Eq a) => Diff a -eqDiff a b - | a == b = [] - | otherwise = [hsep [ppr a, "and", ppr b, "do not match"]] - -validAst :: HieAST a -> Either SDoc () -validAst (Node _ span children) = do - checkContainment children - checkSorted children - mapM_ validAst children - where - checkSorted [] = return () - checkSorted [_] = return () - checkSorted (x:y:xs) - | nodeSpan x `leftOf` nodeSpan y = checkSorted (y:xs) - | otherwise = Left $ hsep - [ ppr $ nodeSpan x - , "is not to the left of" - , ppr $ nodeSpan y - ] - checkContainment [] = return () - checkContainment (x:xs) - | span `containsSpan` nodeSpan x = checkContainment xs - | otherwise = Left $ hsep - [ ppr span - , "does not contain" - , ppr $ nodeSpan x - ] - --- | Look for any identifiers which occur outside of their supposed scopes. --- Returns a list of error messages. -validateScopes :: M.Map FastString (HieAST a) -> [SDoc] -validateScopes asts = M.foldrWithKey (\k a b -> valid k a ++ b) [] refMap - where - refMap = generateReferencesMap asts - valid (Left _) _ = [] - valid (Right n) refs = concatMap inScope refs - where - mapRef = foldMap getScopeFromContext . identInfo . snd - scopes = case foldMap mapRef refs of - Just xs -> xs - Nothing -> [] - inScope (sp, dets) - | definedInAsts asts n - && any isOccurrence (identInfo dets) - = case scopes of - [] -> [] - _ -> if any (`scopeContainsSpan` sp) scopes - then [] - else return $ hsep - [ "Name", ppr n, "at position", ppr sp - , "doesn't occur in calculated scope", ppr scopes] - | otherwise = [] diff --git a/hie-compat/src-ghc86/Compat/HieTypes.hs b/hie-compat/src-ghc86/Compat/HieTypes.hs deleted file mode 100644 index d3ed1170fa..0000000000 --- a/hie-compat/src-ghc86/Compat/HieTypes.hs +++ /dev/null @@ -1,534 +0,0 @@ -{- -Types for the .hie file format are defined here. - -For more information see https://gitlab.haskell.org/ghc/ghc/wikis/hie-files --} -{-# LANGUAGE DeriveTraversable #-} -{-# LANGUAGE DeriveDataTypeable #-} - -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# OPTIONS_GHC -Wno-orphans #-} -module Compat.HieTypes where - -import Config -import Binary -import FastString ( FastString ) -import IfaceType -import Module ( ModuleName, Module ) -import Name ( Name ) -import Outputable hiding ( (<>) ) -import SrcLoc -import Avail - -import qualified Data.Array as A -import qualified Data.Map as M -import qualified Data.Set as S -import Data.ByteString ( ByteString ) -import Data.Data ( Typeable, Data ) -import Data.Semigroup ( Semigroup(..) ) -import Data.Word ( Word8 ) -import Control.Applicative ( (<|>) ) - -type Span = RealSrcSpan - -instance Binary RealSrcSpan where - put_ bh ss = do - put_ bh (srcSpanFile ss) - put_ bh (srcSpanStartLine ss) - put_ bh (srcSpanStartCol ss) - put_ bh (srcSpanEndLine ss) - put_ bh (srcSpanEndCol ss) - - get bh = do - f <- get bh - sl <- get bh - sc <- get bh - el <- get bh - ec <- get bh - return (mkRealSrcSpan (mkRealSrcLoc f sl sc) - (mkRealSrcLoc f el ec)) - -instance (A.Ix a, Binary a, Binary b) => Binary (A.Array a b) where - put_ bh arr = do - put_ bh $ A.bounds arr - put_ bh $ A.elems arr - get bh = do - bounds <- get bh - xs <- get bh - return $ A.listArray bounds xs - --- | Current version of @.hie@ files -hieVersion :: Integer -hieVersion = read (cProjectVersionInt ++ cProjectPatchLevel) :: Integer - -{- | -GHC builds up a wealth of information about Haskell source as it compiles it. -@.hie@ files are a way of persisting some of this information to disk so that -external tools that need to work with haskell source don't need to parse, -typecheck, and rename all over again. These files contain: - - * a simplified AST - - * nodes are annotated with source positions and types - * identifiers are annotated with scope information - - * the raw bytes of the initial Haskell source - -Besides saving compilation cycles, @.hie@ files also offer a more stable -interface than the GHC API. --} -data HieFile = HieFile - { hie_hs_file :: FilePath - -- ^ Initial Haskell source file path - - , hie_module :: Module - -- ^ The module this HIE file is for - - , hie_types :: A.Array TypeIndex HieTypeFlat - -- ^ Types referenced in the 'hie_asts'. - -- - -- See Note [Efficient serialization of redundant type info] - - , hie_asts :: HieASTs TypeIndex - -- ^ Type-annotated abstract syntax trees - - , hie_exports :: [AvailInfo] - -- ^ The names that this module exports - - , hie_hs_src :: ByteString - -- ^ Raw bytes of the initial Haskell source - } -instance Binary HieFile where - put_ bh hf = do - put_ bh $ hie_hs_file hf - put_ bh $ hie_module hf - put_ bh $ hie_types hf - put_ bh $ hie_asts hf - put_ bh $ hie_exports hf - put_ bh $ hie_hs_src hf - - get bh = HieFile - <$> get bh - <*> get bh - <*> get bh - <*> get bh - <*> get bh - <*> get bh - - -{- -Note [Efficient serialization of redundant type info] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The type information in .hie files is highly repetitive and redundant. For -example, consider the expression - - const True 'a' - -There is a lot of shared structure between the types of subterms: - - * const True 'a' :: Bool - * const True :: Char -> Bool - * const :: Bool -> Char -> Bool - -Since all 3 of these types need to be stored in the .hie file, it is worth -making an effort to deduplicate this shared structure. The trick is to define -a new data type that is a flattened version of 'Type': - - data HieType a = HAppTy a a -- data Type = AppTy Type Type - | HFunTy a a -- | FunTy Type Type - | ... - - type TypeIndex = Int - -Types in the final AST are stored in an 'A.Array TypeIndex (HieType TypeIndex)', -where the 'TypeIndex's in the 'HieType' are references to other elements of the -array. Types recovered from GHC are deduplicated and stored in this compressed -form with sharing of subtrees. --} - -type TypeIndex = Int - --- | A flattened version of 'Type'. --- --- See Note [Efficient serialization of redundant type info] -data HieType a - = HTyVarTy Name - | HAppTy a a - | HTyConApp IfaceTyCon (HieArgs a) - | HForAllTy ((Name, a),ArgFlag) a - | HFunTy a a - | HQualTy a a -- ^ type with constraint: @t1 => t2@ (see 'IfaceDFunTy') - | HLitTy IfaceTyLit - | HCastTy a - | HCoercionTy - deriving (Functor, Foldable, Traversable, Eq) - -type HieTypeFlat = HieType TypeIndex - --- | Roughly isomorphic to the original core 'Type'. -newtype HieTypeFix = Roll (HieType HieTypeFix) - -instance Binary (HieType TypeIndex) where - put_ bh (HTyVarTy n) = do - putByte bh 0 - put_ bh n - put_ bh (HAppTy a b) = do - putByte bh 1 - put_ bh a - put_ bh b - put_ bh (HTyConApp n xs) = do - putByte bh 2 - put_ bh n - put_ bh xs - put_ bh (HForAllTy bndr a) = do - putByte bh 3 - put_ bh bndr - put_ bh a - put_ bh (HFunTy a b) = do - putByte bh 4 - put_ bh a - put_ bh b - put_ bh (HQualTy a b) = do - putByte bh 5 - put_ bh a - put_ bh b - put_ bh (HLitTy l) = do - putByte bh 6 - put_ bh l - put_ bh (HCastTy a) = do - putByte bh 7 - put_ bh a - put_ bh HCoercionTy = putByte bh 8 - - get bh = do - (t :: Word8) <- get bh - case t of - 0 -> HTyVarTy <$> get bh - 1 -> HAppTy <$> get bh <*> get bh - 2 -> HTyConApp <$> get bh <*> get bh - 3 -> HForAllTy <$> get bh <*> get bh - 4 -> HFunTy <$> get bh <*> get bh - 5 -> HQualTy <$> get bh <*> get bh - 6 -> HLitTy <$> get bh - 7 -> HCastTy <$> get bh - 8 -> return HCoercionTy - _ -> panic "Binary (HieArgs Int): invalid tag" - - --- | A list of type arguments along with their respective visibilities (ie. is --- this an argument that would return 'True' for 'isVisibleArgFlag'?). -newtype HieArgs a = HieArgs [(Bool,a)] - deriving (Functor, Foldable, Traversable, Eq) - -instance Binary (HieArgs TypeIndex) where - put_ bh (HieArgs xs) = put_ bh xs - get bh = HieArgs <$> get bh - --- | Mapping from filepaths (represented using 'FastString') to the --- corresponding AST -newtype HieASTs a = HieASTs { getAsts :: M.Map FastString (HieAST a) } - deriving (Functor, Foldable, Traversable) - -instance Binary (HieASTs TypeIndex) where - put_ bh asts = put_ bh $ M.toAscList $ getAsts asts - get bh = HieASTs <$> fmap M.fromDistinctAscList (get bh) - - -data HieAST a = - Node - { nodeInfo :: NodeInfo a - , nodeSpan :: Span - , nodeChildren :: [HieAST a] - } deriving (Functor, Foldable, Traversable) - -instance Binary (HieAST TypeIndex) where - put_ bh ast = do - put_ bh $ nodeInfo ast - put_ bh $ nodeSpan ast - put_ bh $ nodeChildren ast - - get bh = Node - <$> get bh - <*> get bh - <*> get bh - - --- | The information stored in one AST node. --- --- The type parameter exists to provide flexibility in representation of types --- (see Note [Efficient serialization of redundant type info]). -data NodeInfo a = NodeInfo - { nodeAnnotations :: S.Set (FastString,FastString) - -- ^ (name of the AST node constructor, name of the AST node Type) - - , nodeType :: [a] - -- ^ The Haskell types of this node, if any. - - , nodeIdentifiers :: NodeIdentifiers a - -- ^ All the identifiers and their details - } deriving (Functor, Foldable, Traversable) - -instance Binary (NodeInfo TypeIndex) where - put_ bh ni = do - put_ bh $ S.toAscList $ nodeAnnotations ni - put_ bh $ nodeType ni - put_ bh $ M.toList $ nodeIdentifiers ni - get bh = NodeInfo - <$> fmap S.fromDistinctAscList (get bh) - <*> get bh - <*> fmap M.fromList (get bh) - -type Identifier = Either ModuleName Name - -type NodeIdentifiers a = M.Map Identifier (IdentifierDetails a) - --- | Information associated with every identifier --- --- We need to include types with identifiers because sometimes multiple --- identifiers occur in the same span(Overloaded Record Fields and so on) -data IdentifierDetails a = IdentifierDetails - { identType :: Maybe a - , identInfo :: S.Set ContextInfo - } deriving (Eq, Functor, Foldable, Traversable) - -instance Outputable a => Outputable (IdentifierDetails a) where - ppr x = text "IdentifierDetails" <+> ppr (identType x) <+> ppr (identInfo x) - -instance Semigroup (IdentifierDetails a) where - d1 <> d2 = IdentifierDetails (identType d1 <|> identType d2) - (S.union (identInfo d1) (identInfo d2)) - -instance Monoid (IdentifierDetails a) where - mempty = IdentifierDetails Nothing S.empty - -instance Binary (IdentifierDetails TypeIndex) where - put_ bh dets = do - put_ bh $ identType dets - put_ bh $ S.toAscList $ identInfo dets - get bh = IdentifierDetails - <$> get bh - <*> fmap S.fromDistinctAscList (get bh) - - --- | Different contexts under which identifiers exist -data ContextInfo - = Use -- ^ regular variable - | MatchBind - | IEThing IEType -- ^ import/export - | TyDecl - - -- | Value binding - | ValBind - BindType -- ^ whether or not the binding is in an instance - Scope -- ^ scope over which the value is bound - (Maybe Span) -- ^ span of entire binding - - -- | Pattern binding - -- - -- This case is tricky because the bound identifier can be used in two - -- distinct scopes. Consider the following example (with @-XViewPatterns@) - -- - -- @ - -- do (b, a, (a -> True)) <- bar - -- foo a - -- @ - -- - -- The identifier @a@ has two scopes: in the view pattern @(a -> True)@ and - -- in the rest of the @do@-block in @foo a@. - | PatternBind - Scope -- ^ scope /in the pattern/ (the variable bound can be used - -- further in the pattern) - Scope -- ^ rest of the scope outside the pattern - (Maybe Span) -- ^ span of entire binding - - | ClassTyDecl (Maybe Span) - - -- | Declaration - | Decl - DeclType -- ^ type of declaration - (Maybe Span) -- ^ span of entire binding - - -- | Type variable - | TyVarBind Scope TyVarScope - - -- | Record field - | RecField RecFieldContext (Maybe Span) - deriving (Eq, Ord, Show) - -instance Outputable ContextInfo where - ppr = text . show - -instance Binary ContextInfo where - put_ bh Use = putByte bh 0 - put_ bh (IEThing t) = do - putByte bh 1 - put_ bh t - put_ bh TyDecl = putByte bh 2 - put_ bh (ValBind bt sc msp) = do - putByte bh 3 - put_ bh bt - put_ bh sc - put_ bh msp - put_ bh (PatternBind a b c) = do - putByte bh 4 - put_ bh a - put_ bh b - put_ bh c - put_ bh (ClassTyDecl sp) = do - putByte bh 5 - put_ bh sp - put_ bh (Decl a b) = do - putByte bh 6 - put_ bh a - put_ bh b - put_ bh (TyVarBind a b) = do - putByte bh 7 - put_ bh a - put_ bh b - put_ bh (RecField a b) = do - putByte bh 8 - put_ bh a - put_ bh b - put_ bh MatchBind = putByte bh 9 - - get bh = do - (t :: Word8) <- get bh - case t of - 0 -> return Use - 1 -> IEThing <$> get bh - 2 -> return TyDecl - 3 -> ValBind <$> get bh <*> get bh <*> get bh - 4 -> PatternBind <$> get bh <*> get bh <*> get bh - 5 -> ClassTyDecl <$> get bh - 6 -> Decl <$> get bh <*> get bh - 7 -> TyVarBind <$> get bh <*> get bh - 8 -> RecField <$> get bh <*> get bh - 9 -> return MatchBind - _ -> panic "Binary ContextInfo: invalid tag" - - --- | Types of imports and exports -data IEType - = Import - | ImportAs - | ImportHiding - | Export - deriving (Eq, Enum, Ord, Show) - -instance Binary IEType where - put_ bh b = putByte bh (fromIntegral (fromEnum b)) - get bh = do x <- getByte bh; pure $! toEnum (fromIntegral x) - - -data RecFieldContext - = RecFieldDecl - | RecFieldAssign - | RecFieldMatch - | RecFieldOcc - deriving (Eq, Enum, Ord, Show) - -instance Binary RecFieldContext where - put_ bh b = putByte bh (fromIntegral (fromEnum b)) - get bh = do x <- getByte bh; pure $! toEnum (fromIntegral x) - - -data BindType - = RegularBind - | InstanceBind - deriving (Eq, Ord, Show, Enum) - -instance Binary BindType where - put_ bh b = putByte bh (fromIntegral (fromEnum b)) - get bh = do x <- getByte bh; pure $! toEnum (fromIntegral x) - - -data DeclType - = FamDec -- ^ type or data family - | SynDec -- ^ type synonym - | DataDec -- ^ data declaration - | ConDec -- ^ constructor declaration - | PatSynDec -- ^ pattern synonym - | ClassDec -- ^ class declaration - | InstDec -- ^ instance declaration - deriving (Eq, Ord, Show, Enum) - -instance Binary DeclType where - put_ bh b = putByte bh (fromIntegral (fromEnum b)) - get bh = do x <- getByte bh; pure $! toEnum (fromIntegral x) - - -data Scope - = NoScope - | LocalScope Span - | ModuleScope - deriving (Eq, Ord, Show, Typeable, Data) - -instance Outputable Scope where - ppr NoScope = text "NoScope" - ppr (LocalScope sp) = text "LocalScope" <+> ppr sp - ppr ModuleScope = text "ModuleScope" - -instance Binary Scope where - put_ bh NoScope = putByte bh 0 - put_ bh (LocalScope span) = do - putByte bh 1 - put_ bh span - put_ bh ModuleScope = putByte bh 2 - - get bh = do - (t :: Word8) <- get bh - case t of - 0 -> return NoScope - 1 -> LocalScope <$> get bh - 2 -> return ModuleScope - _ -> panic "Binary Scope: invalid tag" - - --- | Scope of a type variable. --- --- This warrants a data type apart from 'Scope' because of complexities --- introduced by features like @-XScopedTypeVariables@ and @-XInstanceSigs@. For --- example, consider: --- --- @ --- foo, bar, baz :: forall a. a -> a --- @ --- --- Here @a@ is in scope in all the definitions of @foo@, @bar@, and @baz@, so we --- need a list of scopes to keep track of this. Furthermore, this list cannot be --- computed until we resolve the binding sites of @foo@, @bar@, and @baz@. --- --- Consequently, @a@ starts with an @'UnresolvedScope' [foo, bar, baz] Nothing@ --- which later gets resolved into a 'ResolvedScopes'. -data TyVarScope - = ResolvedScopes [Scope] - - -- | Unresolved scopes should never show up in the final @.hie@ file - | UnresolvedScope - [Name] -- ^ names of the definitions over which the scope spans - (Maybe Span) -- ^ the location of the instance/class declaration for - -- the case where the type variable is declared in a - -- method type signature - deriving (Eq, Ord) - -instance Show TyVarScope where - show (ResolvedScopes sc) = show sc - show _ = error "UnresolvedScope" - -instance Binary TyVarScope where - put_ bh (ResolvedScopes xs) = do - putByte bh 0 - put_ bh xs - put_ bh (UnresolvedScope ns span) = do - putByte bh 1 - put_ bh ns - put_ bh span - - get bh = do - (t :: Word8) <- get bh - case t of - 0 -> ResolvedScopes <$> get bh - 1 -> UnresolvedScope <$> get bh <*> get bh - _ -> panic "Binary TyVarScope: invalid tag" diff --git a/hie-compat/src-ghc86/Compat/HieUtils.hs b/hie-compat/src-ghc86/Compat/HieUtils.hs deleted file mode 100644 index 4eb5451913..0000000000 --- a/hie-compat/src-ghc86/Compat/HieUtils.hs +++ /dev/null @@ -1,451 +0,0 @@ -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TupleSections #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE FlexibleInstances #-} -module Compat.HieUtils where - -import CoreMap -import DynFlags ( DynFlags ) -import FastString ( FastString, mkFastString ) -import IfaceType -import Name hiding (varName) -import Outputable ( renderWithStyle, ppr, defaultUserStyle ) -import SrcLoc -import ToIface -import TyCon -import TyCoRep -import Type -import Var -import VarEnv - -import Compat.HieTypes - -import qualified Data.Map as M -import qualified Data.Set as S -import qualified Data.IntMap.Strict as IM -import qualified Data.Array as A -import Data.Data ( typeOf, typeRepTyCon, Data(toConstr) ) -import Data.Maybe ( maybeToList ) -import Data.Monoid -import Data.Traversable ( for ) -import Control.Monad.Trans.State.Strict hiding (get) - - -generateReferencesMap - :: Foldable f - => f (HieAST a) - -> M.Map Identifier [(Span, IdentifierDetails a)] -generateReferencesMap = foldr (\ast m -> M.unionWith (++) (go ast) m) M.empty - where - go ast = M.unionsWith (++) (this : map go (nodeChildren ast)) - where - this = fmap (pure . (nodeSpan ast,)) $ nodeIdentifiers $ nodeInfo ast - -renderHieType :: DynFlags -> HieTypeFix -> String -renderHieType df ht = renderWithStyle df (ppr $ hieTypeToIface ht) sty - where sty = defaultUserStyle df - -resolveVisibility :: Type -> [Type] -> [(Bool,Type)] -resolveVisibility kind ty_args - = go (mkEmptyTCvSubst in_scope) kind ty_args - where - in_scope = mkInScopeSet (tyCoVarsOfTypes ty_args) - - go _ _ [] = [] - go env ty ts - | Just ty' <- coreView ty - = go env ty' ts - go env (ForAllTy (TvBndr tv vis) res) (t:ts) - | isVisibleArgFlag vis = (True , t) : ts' - | otherwise = (False, t) : ts' - where - ts' = go (extendTvSubst env tv t) res ts - - go env (FunTy _ res) (t:ts) -- No type-class args in tycon apps - = (True,t) : go env res ts - - go env (TyVarTy tv) ts - | Just ki <- lookupTyVar env tv = go env ki ts - go env kind (t:ts) = (True, t) : go env kind ts -- Ill-kinded - -foldType :: (HieType a -> a) -> HieTypeFix -> a -foldType f (Roll t) = f $ fmap (foldType f) t - -hieTypeToIface :: HieTypeFix -> IfaceType -hieTypeToIface = foldType go - where - go (HTyVarTy n) = IfaceTyVar $ occNameFS $ getOccName n - go (HAppTy a b) = IfaceAppTy a b - go (HLitTy l) = IfaceLitTy l - go (HForAllTy ((n,k),af) t) = let b = (occNameFS $ getOccName n, k) - in IfaceForAllTy (TvBndr b af) t - go (HFunTy a b) = IfaceFunTy a b - go (HQualTy pred b) = IfaceDFunTy pred b - go (HCastTy a) = a - go HCoercionTy = IfaceTyVar "" - go (HTyConApp a xs) = IfaceTyConApp a (hieToIfaceArgs xs) - - -- This isn't fully faithful - we can't produce the 'Inferred' case - hieToIfaceArgs :: HieArgs IfaceType -> IfaceTcArgs - hieToIfaceArgs (HieArgs xs) = go' xs - where - go' [] = ITC_Nil - go' ((True ,x):xs) = ITC_Vis x $ go' xs - go' ((False,x):xs) = ITC_Invis x $ go' xs - -data HieTypeState - = HTS - { tyMap :: !(TypeMap TypeIndex) - , htyTable :: !(IM.IntMap HieTypeFlat) - , freshIndex :: !TypeIndex - } - -initialHTS :: HieTypeState -initialHTS = HTS emptyTypeMap IM.empty 0 - -freshTypeIndex :: State HieTypeState TypeIndex -freshTypeIndex = do - index <- gets freshIndex - modify' $ \hts -> hts { freshIndex = index+1 } - return index - -compressTypes - :: HieASTs Type - -> (HieASTs TypeIndex, A.Array TypeIndex HieTypeFlat) -compressTypes asts = (a, arr) - where - (a, HTS _ m i) = flip runState initialHTS $ - for asts $ \typ -> do - i <- getTypeIndex typ - return i - arr = A.array (0,i-1) (IM.toList m) - -recoverFullType :: TypeIndex -> A.Array TypeIndex HieTypeFlat -> HieTypeFix -recoverFullType i m = go i - where - go i = Roll $ fmap go (m A.! i) - -getTypeIndex :: Type -> State HieTypeState TypeIndex -getTypeIndex t - = do - tm <- gets tyMap - case lookupTypeMap tm t of - Just i -> return i - Nothing -> do - ht <- go t - extendHTS t ht - where - extendHTS t ht = do - i <- freshTypeIndex - modify' $ \(HTS tm tt fi) -> - HTS (extendTypeMap tm t i) (IM.insert i ht tt) fi - return i - - go (TyVarTy v) = return $ HTyVarTy $ varName v - go (AppTy a b) = do - ai <- getTypeIndex a - bi <- getTypeIndex b - return $ HAppTy ai bi - go (TyConApp f xs) = do - let visArgs = HieArgs $ resolveVisibility (tyConKind f) xs - is <- mapM getTypeIndex visArgs - return $ HTyConApp (toIfaceTyCon f) is - go (ForAllTy (TvBndr v a) t) = do - k <- getTypeIndex (varType v) - i <- getTypeIndex t - return $ HForAllTy ((varName v,k),a) i - go (FunTy a b) = do - ai <- getTypeIndex a - bi <- getTypeIndex b - return $ if isPredTy a - then HQualTy ai bi - else HFunTy ai bi - go (LitTy a) = return $ HLitTy $ toIfaceTyLit a - go (CastTy t _) = do - i <- getTypeIndex t - return $ HCastTy i - go (CoercionTy _) = return HCoercionTy - -resolveTyVarScopes :: M.Map FastString (HieAST a) -> M.Map FastString (HieAST a) -resolveTyVarScopes asts = M.map go asts - where - go ast = resolveTyVarScopeLocal ast asts - -resolveTyVarScopeLocal :: HieAST a -> M.Map FastString (HieAST a) -> HieAST a -resolveTyVarScopeLocal ast asts = go ast - where - resolveNameScope dets = dets{identInfo = - S.map resolveScope (identInfo dets)} - resolveScope (TyVarBind sc (UnresolvedScope names Nothing)) = - TyVarBind sc $ ResolvedScopes - [ LocalScope binding - | name <- names - , Just binding <- [getNameBinding name asts] - ] - resolveScope (TyVarBind sc (UnresolvedScope names (Just sp))) = - TyVarBind sc $ ResolvedScopes - [ LocalScope binding - | name <- names - , Just binding <- [getNameBindingInClass name sp asts] - ] - resolveScope scope = scope - go (Node info span children) = Node info' span $ map go children - where - info' = info { nodeIdentifiers = idents } - idents = M.map resolveNameScope $ nodeIdentifiers info - -getNameBinding :: Name -> M.Map FastString (HieAST a) -> Maybe Span -getNameBinding n asts = do - (_,msp) <- getNameScopeAndBinding n asts - msp - -getNameScope :: Name -> M.Map FastString (HieAST a) -> Maybe [Scope] -getNameScope n asts = do - (scopes,_) <- getNameScopeAndBinding n asts - return scopes - -getNameBindingInClass - :: Name - -> Span - -> M.Map FastString (HieAST a) - -> Maybe Span -getNameBindingInClass n sp asts = do - ast <- M.lookup (srcSpanFile sp) asts - getFirst $ foldMap First $ do - child <- flattenAst ast - dets <- maybeToList - $ M.lookup (Right n) $ nodeIdentifiers $ nodeInfo child - let binding = foldMap (First . getBindSiteFromContext) (identInfo dets) - return (getFirst binding) - -getNameScopeAndBinding - :: Name - -> M.Map FastString (HieAST a) - -> Maybe ([Scope], Maybe Span) -getNameScopeAndBinding n asts = case nameSrcSpan n of - RealSrcSpan sp -> do -- @Maybe - ast <- M.lookup (srcSpanFile sp) asts - defNode <- selectLargestContainedBy sp ast - getFirst $ foldMap First $ do -- @[] - node <- flattenAst defNode - dets <- maybeToList - $ M.lookup (Right n) $ nodeIdentifiers $ nodeInfo node - scopes <- maybeToList $ foldMap getScopeFromContext (identInfo dets) - let binding = foldMap (First . getBindSiteFromContext) (identInfo dets) - return $ Just (scopes, getFirst binding) - _ -> Nothing - -getScopeFromContext :: ContextInfo -> Maybe [Scope] -getScopeFromContext (ValBind _ sc _) = Just [sc] -getScopeFromContext (PatternBind a b _) = Just [a, b] -getScopeFromContext (ClassTyDecl _) = Just [ModuleScope] -getScopeFromContext (Decl _ _) = Just [ModuleScope] -getScopeFromContext (TyVarBind a (ResolvedScopes xs)) = Just $ a:xs -getScopeFromContext (TyVarBind a _) = Just [a] -getScopeFromContext _ = Nothing - -getBindSiteFromContext :: ContextInfo -> Maybe Span -getBindSiteFromContext (ValBind _ _ sp) = sp -getBindSiteFromContext (PatternBind _ _ sp) = sp -getBindSiteFromContext _ = Nothing - -flattenAst :: HieAST a -> [HieAST a] -flattenAst n = - n : concatMap flattenAst (nodeChildren n) - -smallestContainingSatisfying - :: Span - -> (HieAST a -> Bool) - -> HieAST a - -> Maybe (HieAST a) -smallestContainingSatisfying sp cond node - | nodeSpan node `containsSpan` sp = getFirst $ mconcat - [ foldMap (First . smallestContainingSatisfying sp cond) $ - nodeChildren node - , First $ if cond node then Just node else Nothing - ] - | sp `containsSpan` nodeSpan node = Nothing - | otherwise = Nothing - -selectLargestContainedBy :: Span -> HieAST a -> Maybe (HieAST a) -selectLargestContainedBy sp node - | sp `containsSpan` nodeSpan node = Just node - | nodeSpan node `containsSpan` sp = - getFirst $ foldMap (First . selectLargestContainedBy sp) $ - nodeChildren node - | otherwise = Nothing - -selectSmallestContaining :: Span -> HieAST a -> Maybe (HieAST a) -selectSmallestContaining sp node - | nodeSpan node `containsSpan` sp = getFirst $ mconcat - [ foldMap (First . selectSmallestContaining sp) $ nodeChildren node - , First (Just node) - ] - | sp `containsSpan` nodeSpan node = Nothing - | otherwise = Nothing - -definedInAsts :: M.Map FastString (HieAST a) -> Name -> Bool -definedInAsts asts n = case nameSrcSpan n of - RealSrcSpan sp -> srcSpanFile sp `elem` M.keys asts - _ -> False - -isOccurrence :: ContextInfo -> Bool -isOccurrence Use = True -isOccurrence _ = False - -scopeContainsSpan :: Scope -> Span -> Bool -scopeContainsSpan NoScope _ = False -scopeContainsSpan ModuleScope _ = True -scopeContainsSpan (LocalScope a) b = a `containsSpan` b - --- | One must contain the other. Leaf nodes cannot contain anything -combineAst :: HieAST Type -> HieAST Type -> HieAST Type -combineAst a@(Node aInf aSpn xs) b@(Node bInf bSpn ys) - | aSpn == bSpn = Node (aInf `combineNodeInfo` bInf) aSpn (mergeAsts xs ys) - | aSpn `containsSpan` bSpn = combineAst b a -combineAst a (Node xs span children) = Node xs span (insertAst a children) - --- | Insert an AST in a sorted list of disjoint Asts -insertAst :: HieAST Type -> [HieAST Type] -> [HieAST Type] -insertAst x = mergeAsts [x] - --- | Merge two nodes together. --- --- Precondition and postcondition: elements in 'nodeType' are ordered. -combineNodeInfo :: NodeInfo Type -> NodeInfo Type -> NodeInfo Type -(NodeInfo as ai ad) `combineNodeInfo` (NodeInfo bs bi bd) = - NodeInfo (S.union as bs) (mergeSorted ai bi) (M.unionWith (<>) ad bd) - where - mergeSorted :: [Type] -> [Type] -> [Type] - mergeSorted la@(a:as) lb@(b:bs) = case nonDetCmpType a b of - LT -> a : mergeSorted as lb - EQ -> a : mergeSorted as bs - GT -> b : mergeSorted la bs - mergeSorted as [] = as - mergeSorted [] bs = bs - - -{- | Merge two sorted, disjoint lists of ASTs, combining when necessary. - -In the absence of position-altering pragmas (ex: @# line "file.hs" 3@), -different nodes in an AST tree should either have disjoint spans (in -which case you can say for sure which one comes first) or one span -should be completely contained in the other (in which case the contained -span corresponds to some child node). - -However, since Haskell does have position-altering pragmas it /is/ -possible for spans to be overlapping. Here is an example of a source file -in which @foozball@ and @quuuuuux@ have overlapping spans: - -@ -module Baz where - -# line 3 "Baz.hs" -foozball :: Int -foozball = 0 - -# line 3 "Baz.hs" -bar, quuuuuux :: Int -bar = 1 -quuuuuux = 2 -@ - -In these cases, we just do our best to produce sensible `HieAST`'s. The blame -should be laid at the feet of whoever wrote the line pragmas in the first place -(usually the C preprocessor...). --} -mergeAsts :: [HieAST Type] -> [HieAST Type] -> [HieAST Type] -mergeAsts xs [] = xs -mergeAsts [] ys = ys -mergeAsts xs@(a:as) ys@(b:bs) - | span_a `containsSpan` span_b = mergeAsts (combineAst a b : as) bs - | span_b `containsSpan` span_a = mergeAsts as (combineAst a b : bs) - | span_a `rightOf` span_b = b : mergeAsts xs bs - | span_a `leftOf` span_b = a : mergeAsts as ys - - -- These cases are to work around ASTs that are not fully disjoint - | span_a `startsRightOf` span_b = b : mergeAsts as ys - | otherwise = a : mergeAsts as ys - where - span_a = nodeSpan a - span_b = nodeSpan b - -rightOf :: Span -> Span -> Bool -rightOf s1 s2 - = (srcSpanStartLine s1, srcSpanStartCol s1) - >= (srcSpanEndLine s2, srcSpanEndCol s2) - && (srcSpanFile s1 == srcSpanFile s2) - -leftOf :: Span -> Span -> Bool -leftOf s1 s2 - = (srcSpanEndLine s1, srcSpanEndCol s1) - <= (srcSpanStartLine s2, srcSpanStartCol s2) - && (srcSpanFile s1 == srcSpanFile s2) - -startsRightOf :: Span -> Span -> Bool -startsRightOf s1 s2 - = (srcSpanStartLine s1, srcSpanStartCol s1) - >= (srcSpanStartLine s2, srcSpanStartCol s2) - --- | combines and sorts ASTs using a merge sort -mergeSortAsts :: [HieAST Type] -> [HieAST Type] -mergeSortAsts = go . map pure - where - go [] = [] - go [xs] = xs - go xss = go (mergePairs xss) - mergePairs [] = [] - mergePairs [xs] = [xs] - mergePairs (xs:ys:xss) = mergeAsts xs ys : mergePairs xss - -simpleNodeInfo :: FastString -> FastString -> NodeInfo a -simpleNodeInfo cons typ = NodeInfo (S.singleton (cons, typ)) [] M.empty - -locOnly :: SrcSpan -> [HieAST a] -locOnly (RealSrcSpan span) = - [Node e span []] - where e = NodeInfo S.empty [] M.empty -locOnly _ = [] - -mkScope :: SrcSpan -> Scope -mkScope (RealSrcSpan sp) = LocalScope sp -mkScope _ = NoScope - -mkLScope :: Located a -> Scope -mkLScope = mkScope . getLoc - -combineScopes :: Scope -> Scope -> Scope -combineScopes ModuleScope _ = ModuleScope -combineScopes _ ModuleScope = ModuleScope -combineScopes NoScope x = x -combineScopes x NoScope = x -combineScopes (LocalScope a) (LocalScope b) = - mkScope $ combineSrcSpans (RealSrcSpan a) (RealSrcSpan b) - -{-# INLINEABLE makeNode #-} -makeNode - :: (Applicative m, Data a) - => a -- ^ helps fill in 'nodeAnnotations' (with 'Data') - -> SrcSpan -- ^ return an empty list if this is unhelpful - -> m [HieAST b] -makeNode x spn = pure $ case spn of - RealSrcSpan span -> [Node (simpleNodeInfo cons typ) span []] - _ -> [] - where - cons = mkFastString . show . toConstr $ x - typ = mkFastString . show . typeRepTyCon . typeOf $ x - -{-# INLINEABLE makeTypeNode #-} -makeTypeNode - :: (Applicative m, Data a) - => a -- ^ helps fill in 'nodeAnnotations' (with 'Data') - -> SrcSpan -- ^ return an empty list if this is unhelpful - -> Type -- ^ type to associate with the node - -> m [HieAST Type] -makeTypeNode x spn etyp = pure $ case spn of - RealSrcSpan span -> - [Node (NodeInfo (S.singleton (cons,typ)) [etyp] M.empty) span []] - _ -> [] - where - cons = mkFastString . show . toConstr $ x - typ = mkFastString . show . typeRepTyCon . typeOf $ x diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Types.hs b/hls-graph/src/Development/IDE/Graph/Internal/Types.hs index 5bcaca0cf6..4edcae9ebc 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Types.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Types.hs @@ -12,11 +12,6 @@ module Development.IDE.Graph.Internal.Types where import Control.Applicative import Control.Monad.Catch -#if __GLASGOW_HASKELL__ < 808 -import Control.Concurrent.STM.Stats (TVar, atomically) -#else -import GHC.Conc (TVar, atomically) -#endif import Control.Monad.IO.Class import Control.Monad.Trans.Reader import Data.Aeson (FromJSON, ToJSON) @@ -31,6 +26,7 @@ import Data.List (intercalate) import Data.Maybe import Data.Typeable import Development.IDE.Graph.Classes +import GHC.Conc (TVar, atomically) import GHC.Generics (Generic) import qualified ListT import qualified StmContainers.Map as SMap diff --git a/plugins/hls-eval-plugin/test/Main.hs b/plugins/hls-eval-plugin/test/Main.hs index a16e405706..cc2baa3ac6 100644 --- a/plugins/hls-eval-plugin/test/Main.hs +++ b/plugins/hls-eval-plugin/test/Main.hs @@ -140,13 +140,10 @@ tests = , goldenWithEval "Can handle eval inside nested comment properly" "TNested" "hs" , goldenWithEval "Test on last line insert results correctly" "TLastLine" "hs" , testGroup "with preprocessors" - [ knownBrokenInEnv [HostOS Windows, GhcVer GHC86] + [ knownBrokenInEnv [HostOS Windows] "CPP eval on Windows and/or GHC <= 8.6 fails for some reasons" $ - goldenWithEval "CPP support" "TCPP" "hs" - , knownBrokenForGhcVersions [GHC86] - "Preprocessor known to fail on GHC <= 8.6" $ - goldenWithEval "Literate Haskell Bird Style" "TLHS" "lhs" - -- , goldenWithEval "Literate Haskell LaTeX Style" "TLHSLateX" "lhs" + goldenWithEval "CPP support" "TCPP" "hs" + , goldenWithEval "Literate Haskell Bird Style" "TLHS" "lhs" ] , goldenWithEval "Works with NoImplicitPrelude" "TNoImplicitPrelude" "hs" , goldenWithEval "Variable 'it' works" "TIt" "hs" diff --git a/plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs b/plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs index 4c3c51f43c..e5b6883fce 100644 --- a/plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs +++ b/plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs @@ -306,11 +306,7 @@ suggestRuleRewrites originatingFile pos ms_mod (L _ HsRules {rds_rules}) = ] | L (locA -> l) r <- rds_rules, pos `isInsideSrcSpan` l, -#if MIN_VERSION_ghc(8,8,0) let HsRule {rd_name = L _ (_, rn)} = r, -#else - let HsRule _ (L _ (_,rn)) _ _ _ _ = r, -#endif let ruleName = unpackFS rn ] where diff --git a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs b/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs index d80e336864..2db38a2a8b 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs @@ -68,12 +68,12 @@ import Development.IDE.Types.Logger (Recorder, cmapWithPrio, WithPriority, Prett import qualified Development.IDE.Core.Shake as Shake -newtype Log +newtype Log = LogShake Shake.Log deriving Show instance Pretty Log where - pretty = \case + pretty = \case LogShake shakeLog -> pretty shakeLog tacticDesc :: T.Text -> T.Text @@ -425,9 +425,7 @@ buildPatHy prov (fromPatCompat -> p0) = mkDerivedConHypothesis prov con args $ zip [0..] [pgt, pgt5] RecCon r -> mkDerivedRecordHypothesis prov con args r -#if __GLASGOW_HASKELL__ >= 808 SigPat _ p _ -> buildPatHy prov p -#endif #if __GLASGOW_HASKELL__ == 808 XPat p -> buildPatHy prov $ unLoc p #endif @@ -585,7 +583,7 @@ wingmanRules recorder plId = do #endif | isHole occ -> maybeToList $ srcSpanToRange span -#if __GLASGOW_HASKELL__ <= 808 +#if __GLASGOW_HASKELL__ == 808 L span (EWildPat _) -> maybeToList $ srcSpanToRange span #endif diff --git a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer/TacticProviders.hs b/plugins/hls-tactics-plugin/src/Wingman/LanguageServer/TacticProviders.hs index fb6f5693b1..40d6362d94 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer/TacticProviders.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/LanguageServer/TacticProviders.hs @@ -150,25 +150,13 @@ commandProvider Refine = requireHoleSort (== Hole) $ provide Refine "" commandProvider BeginMetaprogram = - requireGHC88OrHigher $ requireHoleSort (== Hole) $ provide BeginMetaprogram "" commandProvider RunMetaprogram = - requireGHC88OrHigher $ withMetaprogram $ \mp -> provide RunMetaprogram mp -requireGHC88OrHigher :: TacticProvider -> TacticProvider -#if __GLASGOW_HASKELL__ >= 808 -requireGHC88OrHigher tp tpd = - tp tpd -#else -requireGHC88OrHigher _ _= - mempty -#endif - - ------------------------------------------------------------------------------ -- | Return an empty list if the given predicate doesn't hold over the length guardLength :: (Int -> Bool) -> [a] -> [a] diff --git a/plugins/hls-tactics-plugin/src/Wingman/StaticPlugin.hs b/plugins/hls-tactics-plugin/src/Wingman/StaticPlugin.hs index 563a1fcc63..a1caeef12d 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/StaticPlugin.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/StaticPlugin.hs @@ -14,7 +14,6 @@ import GHC.LanguageExtensions.Type (Extension(EmptyCase, QuasiQuotes)) import Ide.Types -#if __GLASGOW_HASKELL__ >= 808 import Data.Data import Generics.SYB #if __GLASGOW_HASKELL__ >= 900 @@ -22,7 +21,6 @@ import GHC.Driver.Plugins (purePlugin) #else import Plugins (purePlugin) #endif -#endif staticPlugin :: DynFlagsModifications staticPlugin = mempty @@ -34,13 +32,9 @@ staticPlugin = mempty { refLevelHoleFits = Just 0 , maxRefHoleFits = Just 0 , maxValidHoleFits = Just 0 -#if __GLASGOW_HASKELL__ >= 808 , staticPlugins = staticPlugins df <> [metaprogrammingPlugin] -#endif } -#if __GLASGOW_HASKELL__ >= 808 , dynFlagsModifyParser = enableQuasiQuotes -#endif } @@ -71,7 +65,6 @@ allowEmptyCaseButWithWarning = flip xopt_set EmptyCase . flip wopt_set Opt_WarnIncompletePatterns -#if __GLASGOW_HASKELL__ >= 808 metaprogrammingPlugin :: StaticPlugin metaprogrammingPlugin = StaticPlugin $ PluginWithArgs pluginDefinition [] @@ -101,7 +94,6 @@ addMetaprogrammingSyntax = L ss (MetaprogramSyntax mp) -> L ss $ mkMetaprogram ss mp (x :: LHsExpr GhcPs) -> x -#endif metaprogramHoleName :: OccName metaprogramHoleName = mkVarOcc "_$metaprogram" diff --git a/plugins/hls-tactics-plugin/test/CodeAction/RunMetaprogramSpec.hs b/plugins/hls-tactics-plugin/test/CodeAction/RunMetaprogramSpec.hs index 8c5e14a269..e366c34efe 100644 --- a/plugins/hls-tactics-plugin/test/CodeAction/RunMetaprogramSpec.hs +++ b/plugins/hls-tactics-plugin/test/CodeAction/RunMetaprogramSpec.hs @@ -11,17 +11,11 @@ import Wingman.Types spec :: Spec spec = do let metaTest l c f = -#if __GLASGOW_HASKELL__ >= 808 goldenTest RunMetaprogram "" l c f -#else - pure () -#endif -#if __GLASGOW_HASKELL__ >= 808 describe "beginMetaprogram" $ do goldenTest BeginMetaprogram "" 1 7 "MetaBegin" goldenTest BeginMetaprogram "" 1 9 "MetaBeginNoWildify" -#endif describe "golden" $ do metaTest 6 11 "MetaMaybeAp" diff --git a/test/functional/Completion.hs b/test/functional/Completion.hs index 8516051c51..8a33eddbe5 100644 --- a/test/functional/Completion.hs +++ b/test/functional/Completion.hs @@ -376,4 +376,4 @@ compls `shouldNotContainCompl` lbl = @? "Should not contain completion: " ++ show lbl expectFailIfBeforeGhc92 :: String -> TestTree -> TestTree -expectFailIfBeforeGhc92 = knownBrokenForGhcVersions [GHC810, GHC88, GHC86, GHC90] +expectFailIfBeforeGhc92 = knownBrokenForGhcVersions [GHC810, GHC88, GHC90] From 55456fb281ed338ec1a333fa74f02e1d01fc4791 Mon Sep 17 00:00:00 2001 From: George Thomas Date: Wed, 12 Oct 2022 13:15:05 +0100 Subject: [PATCH 163/213] Register Fourmolu plugin properties (#3284) Co-authored-by: Michael Peyton Jones --- plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs b/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs index d62210d71e..96c945386e 100644 --- a/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs +++ b/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs @@ -43,6 +43,7 @@ descriptor :: Recorder (WithPriority LogEvent) -> PluginId -> PluginDescriptor I descriptor recorder plId = (defaultPluginDescriptor plId) { pluginHandlers = mkFormattingHandlers $ provider recorder plId + , pluginConfigDescriptor = defaultConfigDescriptor{configCustomConfig = mkCustomConfig properties} } properties :: Properties '[ 'PropertyKey "external" 'TBoolean] From 5cdaa245dd5fbf9373a54efb3faed67d26a36388 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 12 Oct 2022 18:12:47 +0200 Subject: [PATCH 164/213] Improved message for missing command or plugin (#3285) * No "PluginId" / "CommandId" prefix or quotes from show * No line break, but comma separated --- ghcide/src/Development/IDE/Plugin/HLS.hs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/HLS.hs b/ghcide/src/Development/IDE/Plugin/HLS.hs index dd8c0d60b0..72aa5d5076 100644 --- a/ghcide/src/Development/IDE/Plugin/HLS.hs +++ b/ghcide/src/Development/IDE/Plugin/HLS.hs @@ -71,20 +71,26 @@ prettyResponseError err = errorCode <> ":" <+> errorBody errorBody = pretty $ err ^. LSP.message pluginNotEnabled :: SMethod m -> [(PluginId, b, a)] -> Text -pluginNotEnabled method availPlugins = "No plugin enabled for " <> T.pack (show method) <> ", available:\n" <> T.pack (unlines $ map (\(plid,_,_) -> show plid) availPlugins) +pluginNotEnabled method availPlugins = + "No plugin enabled for " <> T.pack (show method) <> ", available: " + <> (T.intercalate ", " $ map (\(PluginId plid, _, _) -> plid) availPlugins) pluginDoesntExist :: PluginId -> Text pluginDoesntExist (PluginId pid) = "Plugin " <> pid <> " doesn't exist" commandDoesntExist :: CommandId -> PluginId -> [PluginCommand ideState] -> Text -commandDoesntExist (CommandId com) (PluginId pid) legalCmds = "Command " <> com <> " isn't defined for plugin " <> pid <> ". Legal commands are:\n" <> T.pack (unlines $ map (show . commandId) legalCmds) +commandDoesntExist (CommandId com) (PluginId pid) legalCmds = + "Command " <> com <> " isn't defined for plugin " <> pid <> ". Legal commands are: " + <> (T.intercalate ", " $ map (\(PluginCommand{commandId = CommandId cid}) -> cid) legalCmds) failedToParseArgs :: CommandId -- ^ command that failed to parse -> PluginId -- ^ Plugin that created the command -> String -- ^ The JSON Error message -> J.Value -- ^ The Argument Values -> Text -failedToParseArgs (CommandId com) (PluginId pid) err arg = "Error while parsing args for " <> com <> " in plugin " <> pid <> ": " <> T.pack err <> "\narg = " <> T.pack (show arg) +failedToParseArgs (CommandId com) (PluginId pid) err arg = + "Error while parsing args for " <> com <> " in plugin " <> pid <> ": " + <> T.pack err <> ", arg = " <> T.pack (show arg) -- | Build a ResponseError and log it before returning to the caller logAndReturnError :: Recorder (WithPriority Log) -> PluginId -> ErrorCode -> Text -> LSP.LspT Config IO (Either ResponseError a) From e36208bf72f14fb5779c7cfc9031ba82b2dc6fd5 Mon Sep 17 00:00:00 2001 From: Andy Date: Fri, 14 Oct 2022 14:10:33 +0200 Subject: [PATCH 165/213] Bump partial ghc support warning to 9.4 (#3286) - 9.2 is now fully supported - 9.4 is partially supported --- ghcide/src/Development/IDE/Main.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ghcide/src/Development/IDE/Main.hs b/ghcide/src/Development/IDE/Main.hs index 3f27e395aa..fbbd5a88a6 100644 --- a/ghcide/src/Development/IDE/Main.hs +++ b/ghcide/src/Development/IDE/Main.hs @@ -149,7 +149,7 @@ data Log | LogLspStart [PluginId] | LogLspStartDuration !Seconds | LogShouldRunSubset !Bool - | LogOnlyPartialGhc92Support + | LogOnlyPartialGhc94Support | LogSetInitialDynFlagsException !SomeException | LogService Service.Log | LogShake Shake.Log @@ -173,8 +173,8 @@ instance Pretty Log where "Started LSP server in" <+> pretty (showDuration duration) LogShouldRunSubset shouldRunSubset -> "shouldRunSubset:" <+> pretty shouldRunSubset - LogOnlyPartialGhc92Support -> - "Currently, HLS supports GHC 9.2 only partially. See [issue #2982](https://github.com/haskell/haskell-language-server/issues/2982) for more detail." + LogOnlyPartialGhc94Support -> + "Currently, HLS supports GHC 9.4 only partially. See [issue #3190](https://github.com/haskell/haskell-language-server/issues/3190) for more detail." LogSetInitialDynFlagsException e -> "setInitialDynFlags:" <+> pretty (displayException e) LogService log -> pretty log @@ -353,9 +353,9 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re , optRunSubset = runSubset } caps = LSP.resClientCapabilities env - -- FIXME: Remove this after GHC 9.2 gets fully supported - when (ghcVersion == GHC92) $ - log Warning LogOnlyPartialGhc92Support + -- FIXME: Remove this after GHC 9.4 gets fully supported + when (ghcVersion == GHC94) $ + log Warning LogOnlyPartialGhc94Support monitoring <- argsMonitoring initialise (cmapWithPrio LogService recorder) From 8cef7a456aa77ce79ee06ba49f9ff44af928ab05 Mon Sep 17 00:00:00 2001 From: Lei Zhu Date: Sun, 16 Oct 2022 23:56:43 +0800 Subject: [PATCH 166/213] Support ghc 9.4 for hls-class-plugin (#3258) * Support ghc 9.4 for hls-class-plugin * Update support table * Update test description * Update nix dependency --- .github/workflows/test.yml | 2 +- cabal.project | 2 +- configuration-ghc-94.nix | 2 +- docs/support/plugin-support.md | 2 +- flake.nix | 4 ++++ haskell-language-server.cabal | 4 ++-- plugins/hls-class-plugin/hls-class-plugin.cabal | 12 ++---------- plugins/hls-class-plugin/test/Main.hs | 4 ++-- 8 files changed, 14 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 78bcf83e15..f4fbbe9005 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -169,7 +169,7 @@ jobs: name: Test hls-floskell-plugin run: cabal test hls-floskell-plugin --test-options="$TEST_OPTS" || cabal test hls-floskell-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-floskell-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.4.2' + - if: matrix.test name: Test hls-class-plugin run: cabal test hls-class-plugin --test-options="$TEST_OPTS" || cabal test hls-class-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-class-plugin --test-options="$TEST_OPTS" diff --git a/cabal.project b/cabal.project index 2a2ee425a6..4950a95f3a 100644 --- a/cabal.project +++ b/cabal.project @@ -48,7 +48,7 @@ package * write-ghc-environment-files: never -index-state: 2022-09-14T16:53:13Z +index-state: 2022-10-07T12:19:15Z constraints: -- For GHC 9.4, older versions of entropy fail to build on Windows diff --git a/configuration-ghc-94.nix b/configuration-ghc-94.nix index df39a67833..3568f65a51 100644 --- a/configuration-ghc-94.nix +++ b/configuration-ghc-94.nix @@ -25,7 +25,7 @@ let ptr-poker = hself.callCabal2nix "ptr-poker" inputs.ptr-poker { }; ghc-exactprint = - hself.callCabal2nix "ghc-exactprint" inputs.ghc-exactprint-150 { }; + hself.callCabal2nix "ghc-exactprint" inputs.ghc-exactprint-160 { }; # Hlint is still broken hlint = doJailbreak (hself.callCabal2nix "hlint" inputs.hlint { }); diff --git a/docs/support/plugin-support.md b/docs/support/plugin-support.md index e6963ff2b3..1bab3b4b90 100644 --- a/docs/support/plugin-support.md +++ b/docs/support/plugin-support.md @@ -46,7 +46,7 @@ For example, a plugin to provide a formatter which has itself been abandoned has | `hls-pragmas-plugin` | 1 | | | `hls-refactor-plugin` | 1 | 9.4 | | `hls-alternate-number-plugin` | 2 | | -| `hls-class-plugin` | 2 | 9.4 | +| `hls-class-plugin` | 2 | | | `hls-change-type-signature-plugin` | 2 | | | `hls-eval-plugin` | 2 | 9.4 | | `hls-explicit-fixity-plugin` | 2 | | diff --git a/flake.nix b/flake.nix index 0ad8731a9c..1e4a30ab26 100644 --- a/flake.nix +++ b/flake.nix @@ -39,6 +39,10 @@ url = "https://hackage.haskell.org/package/lsp-test-0.14.1.0/lsp-test-0.14.1.0.tar.gz"; flake = false; }; + ghc-exactprint-160 = { + url = "https://hackage.haskell.org/package/ghc-exactprint-1.6.0/ghc-exactprint-1.6.0.tar.gz"; + flake = false; + }; ghc-exactprint-150 = { url = "https://hackage.haskell.org/package/ghc-exactprint-1.5.0/ghc-exactprint-1.5.0.tar.gz"; flake = false; diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index d44b072928..184d3a2729 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -201,7 +201,7 @@ flag dynamic manual: True common class - if flag(class) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) + if flag(class) build-depends: hls-class-plugin ^>= 1.1 cpp-options: -Dhls_class @@ -271,7 +271,7 @@ common splice cpp-options: -Dhls_splice common alternateNumberFormat - if flag(alternateNumberFormat) + if flag(alternateNumberFormat) build-depends: hls-alternate-number-format-plugin ^>= 1.2 cpp-options: -Dhls_alternateNumberFormat diff --git a/plugins/hls-class-plugin/hls-class-plugin.cabal b/plugins/hls-class-plugin/hls-class-plugin.cabal index 7fd5483ed5..67c9ec9ec5 100644 --- a/plugins/hls-class-plugin/hls-class-plugin.cabal +++ b/plugins/hls-class-plugin/hls-class-plugin.cabal @@ -25,10 +25,6 @@ source-repository head location: https://github.com/haskell/haskell-language-server.git library - if impl(ghc >= 9.3) - buildable: False - else - buildable: True exposed-modules: Ide.Plugin.Class other-modules: Ide.Plugin.Class.CodeAction , Ide.Plugin.Class.CodeLens @@ -53,9 +49,9 @@ library , transformers if impl(ghc >=9.2.1) - build-depends: ghc-exactprint ^>= 1.5 + build-depends: ghc-exactprint >= 1.5 else - build-depends: ghc-exactprint >= 0.6.4 && <1.1 + build-depends: ghc-exactprint >= 0.6.4 && <1.1 default-language: Haskell2010 default-extensions: @@ -66,10 +62,6 @@ library ghc-options: -Wall -Wno-unticked-promoted-constructors -Wno-name-shadowing test-suite tests - if impl(ghc >= 9.3) - buildable: False - else - buildable: True type: exitcode-stdio-1.0 default-language: Haskell2010 hs-source-dirs: test diff --git a/plugins/hls-class-plugin/test/Main.hs b/plugins/hls-class-plugin/test/Main.hs index b15efb7498..585f49143e 100644 --- a/plugins/hls-class-plugin/test/Main.hs +++ b/plugins/hls-class-plugin/test/Main.hs @@ -69,7 +69,7 @@ codeActionTests recorder = testGroup executeCodeAction gAction , goldenWithClass recorder "Creates a placeholder for other two methods" "T6" "2" $ \(_:_:ghAction:_) -> do executeCodeAction ghAction - , onlyRunForGhcVersions [GHC92] "Only ghc-9.2 enabled GHC2021 implicitly" $ + , onlyRunForGhcVersions [GHC92, GHC94] "Only ghc-9.2+ enabled GHC2021 implicitly" $ goldenWithClass recorder "Don't insert pragma with GHC2021" "InsertWithGHC2021Enabled" "" $ \(_:eqWithSig:_) -> do executeCodeAction eqWithSig , goldenWithClass recorder "Insert pragma if not exist" "InsertWithoutPragma" "" $ \(_:eqWithSig:_) -> do @@ -96,7 +96,7 @@ codeLensTests recorder = testGroup , goldenCodeLens recorder "Apply code lens for local class" "LocalClassDefine" 0 , goldenCodeLens recorder "Apply code lens on the same line" "Inline" 0 , goldenCodeLens recorder "Don't insert pragma while existing" "CodeLensWithPragma" 0 - , onlyRunForGhcVersions [GHC92] "Only ghc-9.2 enabled GHC2021 implicitly" $ + , onlyRunForGhcVersions [GHC92, GHC94] "Only ghc-9.2+ enabled GHC2021 implicitly" $ goldenCodeLens recorder "Don't insert pragma while GHC2021 enabled" "CodeLensWithGHC2021" 0 , goldenCodeLens recorder "Qualified name" "Qualified" 0 , goldenCodeLens recorder "Type family" "TypeFamily" 0 From f586f4d412292f1eb08c99d1129678fda6631174 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 12:41:33 +0100 Subject: [PATCH 167/213] Bump cachix/install-nix-action from 17 to 18 (#3292) Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 17 to 18. - [Release notes](https://github.com/cachix/install-nix-action/releases) - [Commits](https://github.com/cachix/install-nix-action/compare/v17...v18) --- updated-dependencies: - dependency-name: cachix/install-nix-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/nix.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 749256be57..efc769fbbb 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -55,7 +55,7 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v17 + - uses: cachix/install-nix-action@v18 with: install_url: https://nixos-nix-install-tests.cachix.org/serve/i6laym9jw3wg9mw6ncyrk6gjx4l34vvx/install install_options: '--tarball-url-prefix https://nixos-nix-install-tests.cachix.org/serve' @@ -89,7 +89,7 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v17 + - uses: cachix/install-nix-action@v18 with: install_url: https://nixos-nix-install-tests.cachix.org/serve/i6laym9jw3wg9mw6ncyrk6gjx4l34vvx/install install_options: '--tarball-url-prefix https://nixos-nix-install-tests.cachix.org/serve' From dc62321779c5c2e275624da8bb2246697e7ba7fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 14:28:27 +0100 Subject: [PATCH 168/213] Bump cachix/cachix-action from 10 to 11 (#3291) Bumps [cachix/cachix-action](https://github.com/cachix/cachix-action) from 10 to 11. - [Release notes](https://github.com/cachix/cachix-action/releases) - [Commits](https://github.com/cachix/cachix-action/compare/v10...v11) --- updated-dependencies: - dependency-name: cachix/cachix-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/nix.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index efc769fbbb..96c3a0c894 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -62,7 +62,7 @@ jobs: extra_nix_config: | experimental-features = nix-command flakes nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@v10 + - uses: cachix/cachix-action@v11 with: name: haskell-language-server # Disable pushing, we will do that in job `build` @@ -96,7 +96,7 @@ jobs: extra_nix_config: | experimental-features = nix-command flakes nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@v10 + - uses: cachix/cachix-action@v11 with: name: haskell-language-server authToken: ${{ secrets.HLS_CACHIX_AUTH_TOKEN }} From 388fd40ade4256b1a1556d2f01a53a91cd5cc88b Mon Sep 17 00:00:00 2001 From: Michael Peyton Jones Date: Tue, 18 Oct 2022 22:12:53 +0100 Subject: [PATCH 169/213] Purge GHC 8.8 (#3287) We're also dropping support for this. --- .circleci/config.yml | 6 - .github/workflows/caching.yml | 1 - .github/workflows/flags.yml | 1 - .github/workflows/hackage.yml | 1 - .github/workflows/test.yml | 67 +- .gitpod.Dockerfile | 1 - bindist/ghcs | 1 - docs/contributing/contributing.md | 8 +- docs/contributing/plugin-tutorial.md | 2 +- docs/installation.md | 2 +- docs/support/ghc-version-support.md | 2 +- ghcide-bench/ghcide-bench.cabal | 2 +- ghcide/ghcide.cabal | 4 +- ghcide/src/Development/IDE/Core/Compile.hs | 18 +- ghcide/src/Development/IDE/GHC/CPP.hs | 23 +- ghcide/src/Development/IDE/GHC/Compat.hs | 26 +- ghcide/src/Development/IDE/GHC/Compat/CPP.hs | 4 - ghcide/src/Development/IDE/GHC/Compat/Core.hs | 80 +- ghcide/src/Development/IDE/GHC/Compat/Util.hs | 6 +- ghcide/src/Development/IDE/GHC/Orphans.hs | 5 - ghcide/src/Development/IDE/GHC/Util.hs | 7 - ghcide/src/Development/IDE/LSP/Outline.hs | 4 - .../IDE/Plugin/Completions/Logic.hs | 7 - ghcide/src/Development/IDE/Spans/Pragmas.hs | 13 - ghcide/test/exe/Main.hs | 18 +- ghcide/test/ghcide-test-utils.cabal | 2 +- haskell-language-server.cabal | 4 +- hie-compat/hie-compat.cabal | 2 - hie-compat/src-ghc88/Compat/HieAst.hs | 1760 ----------------- hie-compat/src-ghc88/Compat/HieBin.hs | 389 ---- .../src/Ide/Plugin/Literals.hs | 11 +- .../src/Ide/Plugin/Eval/CodeLens.hs | 3 - .../src/Development/IDE/GHC/Dump.hs | 4 - .../src/Development/IDE/GHC/ExactPrint.hs | 5 - .../src/Ide/Plugin/Retrie.hs | 4 - .../src/Ide/Plugin/Splice.hs | 3 - plugins/hls-stan-plugin/hls-stan-plugin.cabal | 4 +- .../src/Wingman/AbstractLSP/TacticActions.hs | 4 +- plugins/hls-tactics-plugin/src/Wingman/GHC.hs | 25 - .../src/Wingman/LanguageServer.hs | 7 - stack-lts16.yaml | 129 -- test/functional/Completion.hs | 2 +- test/functional/TypeDefinition.hs | 3 +- test/wrapper/Main.hs | 1 - test/wrapper/testdata/stack-8.8.4/Lib.hs | 2 - test/wrapper/testdata/stack-8.8.4/foo.cabal | 7 - test/wrapper/testdata/stack-8.8.4/stack.yaml | 1 - 47 files changed, 66 insertions(+), 2615 deletions(-) delete mode 100644 hie-compat/src-ghc88/Compat/HieAst.hs delete mode 100644 hie-compat/src-ghc88/Compat/HieBin.hs delete mode 100644 stack-lts16.yaml delete mode 100644 test/wrapper/testdata/stack-8.8.4/Lib.hs delete mode 100644 test/wrapper/testdata/stack-8.8.4/foo.cabal delete mode 100644 test/wrapper/testdata/stack-8.8.4/stack.yaml diff --git a/.circleci/config.yml b/.circleci/config.yml index 91f6e2e89c..22aa3f0639 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -75,11 +75,6 @@ defaults: &defaults version: 2 jobs: - stackage-lts16: - environment: - - STACK_FILE: "stack-lts16.yaml" - <<: *defaults - stackage-lts19: environment: - STACK_FILE: "stack-lts19.yaml" @@ -95,6 +90,5 @@ workflows: version: 2 multiple-ghcs: jobs: - - stackage-lts16 - stackage-lts19 - stackage-nightly diff --git a/.github/workflows/caching.yml b/.github/workflows/caching.yml index ae4c9c07fe..b0887b51b9 100644 --- a/.github/workflows/caching.yml +++ b/.github/workflows/caching.yml @@ -84,7 +84,6 @@ jobs: , "9.2.3" , "9.0.2" , "8.10.7" - , "8.8.4" ] os: [ "ubuntu-latest" , "macOS-latest" diff --git a/.github/workflows/flags.yml b/.github/workflows/flags.yml index a4f070a4da..44b329a833 100644 --- a/.github/workflows/flags.yml +++ b/.github/workflows/flags.yml @@ -47,7 +47,6 @@ jobs: ghc: [ "9.2.4" , "9.0.2" , "8.10.7" - , "8.8.4" ] os: [ "ubuntu-latest" ] diff --git a/.github/workflows/hackage.yml b/.github/workflows/hackage.yml index 4378b8ffb7..68f7de50d6 100644 --- a/.github/workflows/hackage.yml +++ b/.github/workflows/hackage.yml @@ -40,7 +40,6 @@ jobs: "haskell-language-server"] ghc: [ "9.0.2" , "8.10.7" - , "8.8.4" ] exclude: - ghc: "9.0.2" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f4fbbe9005..bae4d974a8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,7 +63,6 @@ jobs: , "9.2.3" , "9.0.2" , "8.10.7" - , "8.8.4" ] os: [ "ubuntu-latest" , "macOS-latest" @@ -82,9 +81,6 @@ jobs: - os: ubuntu-latest ghc: '8.10.7' test: true - - os: ubuntu-latest - ghc: '8.8.4' - test: true - os: windows-latest ghc: '9.4.2' test: true @@ -98,8 +94,6 @@ jobs: ghc: '8.10.7' test: true # only build rest of supported ghc versions for windows - - os: windows-latest - ghc: '8.8.4' - os: windows-latest ghc: '9.2.3' @@ -111,9 +105,8 @@ jobs: ghc: ${{ matrix.ghc }} os: ${{ runner.os }} - # repeating builds to workaround segfaults in windows and ghc-8.8.4 - name: Build - run: cabal build || cabal build || cabal build + run: cabal build - name: Set test options # run the tests without parallelism, otherwise tasty will attempt to run @@ -137,125 +130,125 @@ jobs: - if: needs.pre_job.outputs.should_skip_ghcide != 'true' && matrix.test name: Test ghcide # run the tests without parallelism to avoid running out of memory - run: cabal test ghcide --test-options="$TEST_OPTS" || cabal test ghcide --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test ghcide --test-options="$TEST_OPTS" + run: cabal test ghcide --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test ghcide --test-options="$TEST_OPTS" - if: matrix.test name: Test hls-plugin-api - run: cabal test hls-plugin-api --test-options="$TEST_OPTS" || cabal test hls-plugin-api --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-plugin-api --test-options="$TEST_OPTS" + run: cabal test hls-plugin-api --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-plugin-api --test-options="$TEST_OPTS" - if: matrix.test name: Test func-test suite env: HLS_TEST_EXE: hls HLS_WRAPPER_TEST_EXE: hls-wrapper - run: cabal test func-test --test-options="$TEST_OPTS" || cabal test func-test --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test func-test --test-options="$TEST_OPTS" + run: cabal test func-test --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test func-test --test-options="$TEST_OPTS" - if: matrix.test name: Test wrapper-test suite env: HLS_TEST_EXE: hls HLS_WRAPPER_TEST_EXE: hls-wrapper - run: cabal test wrapper-test --test-options="$TEST_OPTS --rerun-log-file .tasty-rerun-log-wrapper" || cabal test wrapper-test --test-options="$TEST_OPTS --rerun-log-file .tasty-rerun-log-wrapper" || cabal test wrapper-test --test-options="$TEST_OPTS --rerun-log-file .tasty-rerun-log-wrapper" + run: cabal test wrapper-test --test-options="$TEST_OPTS --rerun-log-file .tasty-rerun-log-wrapper" - if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2' name: Test hls-brittany-plugin - run: cabal test hls-brittany-plugin --test-options="$TEST_OPTS" || cabal test hls-brittany-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-brittany-plugin --test-options="$TEST_OPTS" + run: cabal test hls-brittany-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-brittany-plugin --test-options="$TEST_OPTS" - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-refactor-plugin - run: cabal test hls-refactor-plugin --test-options="$TEST_OPTS" || cabal test hls-refactor-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-refactor-plugin --test-options="$TEST_OPTS" + run: cabal test hls-refactor-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-refactor-plugin --test-options="$TEST_OPTS" - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-floskell-plugin - run: cabal test hls-floskell-plugin --test-options="$TEST_OPTS" || cabal test hls-floskell-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-floskell-plugin --test-options="$TEST_OPTS" + run: cabal test hls-floskell-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-floskell-plugin --test-options="$TEST_OPTS" - if: matrix.test name: Test hls-class-plugin - run: cabal test hls-class-plugin --test-options="$TEST_OPTS" || cabal test hls-class-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-class-plugin --test-options="$TEST_OPTS" + run: cabal test hls-class-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-class-plugin --test-options="$TEST_OPTS" - if: matrix.test name: Test hls-pragmas-plugin - run: cabal test hls-pragmas-plugin --test-options="$TEST_OPTS" || cabal test hls-pragmas-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-pragmas-plugin --test-options="$TEST_OPTS" + run: cabal test hls-pragmas-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-pragmas-plugin --test-options="$TEST_OPTS" - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-eval-plugin - run: cabal test hls-eval-plugin --test-options="$TEST_OPTS" || cabal test hls-eval-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-eval-plugin --test-options="$TEST_OPTS" + run: cabal test hls-eval-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-eval-plugin --test-options="$TEST_OPTS" - if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2' name: Test hls-haddock-comments-plugin - run: cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS" || cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS" + run: cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS" - if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2' name: Test hls-splice-plugin - run: cabal test hls-splice-plugin --test-options="$TEST_OPTS" || cabal test hls-splice-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-splice-plugin --test-options="$TEST_OPTS" + run: cabal test hls-splice-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-splice-plugin --test-options="$TEST_OPTS" - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-stylish-haskell-plugin - run: cabal test hls-stylish-haskell-plugin --test-options="$TEST_OPTS" || cabal test hls-stylish-haskell-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-stylish-haskell-plugin --test-options="$TEST_OPTS" + run: cabal test hls-stylish-haskell-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-stylish-haskell-plugin --test-options="$TEST_OPTS" - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-ormolu-plugin - run: cabal test hls-ormolu-plugin --test-options="$TEST_OPTS" || cabal test hls-ormolu-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-ormolu-plugin --test-options="$TEST_OPTS" + run: cabal test hls-ormolu-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-ormolu-plugin --test-options="$TEST_OPTS" - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-fourmolu-plugin - run: cabal test hls-fourmolu-plugin --test-options="$TEST_OPTS" || cabal test hls-fourmolu-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-fourmolu-plugin --test-options="$TEST_OPTS" + run: cabal test hls-fourmolu-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-fourmolu-plugin --test-options="$TEST_OPTS" - if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2' name: Test hls-tactics-plugin test suite - run: cabal test hls-tactics-plugin --test-options="$TEST_OPTS" || cabal test hls-tactics-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-tactics-plugin --test-options="$TEST_OPTS" + run: cabal test hls-tactics-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-tactics-plugin --test-options="$TEST_OPTS" - if: matrix.test name: Test hls-refine-imports-plugin test suite - run: cabal test hls-refine-imports-plugin --test-options="$TEST_OPTS" || cabal test hls-refine-imports-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-refine-imports-plugin --test-options="$TEST_OPTS" + run: cabal test hls-refine-imports-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-refine-imports-plugin --test-options="$TEST_OPTS" - if: matrix.test name: Test hls-explicit-imports-plugin test suite - run: cabal test hls-explicit-imports-plugin --test-options="$TEST_OPTS" || cabal test hls-explicit-imports-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-explicit-imports-plugin --test-options="$TEST_OPTS" + run: cabal test hls-explicit-imports-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-explicit-imports-plugin --test-options="$TEST_OPTS" - if: matrix.test name: Test hls-call-hierarchy-plugin test suite - run: cabal test hls-call-hierarchy-plugin --test-options="$TEST_OPTS" || cabal test hls-call-hierarchy-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-call-hierarchy-plugin --test-options="$TEST_OPTS" + run: cabal test hls-call-hierarchy-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-call-hierarchy-plugin --test-options="$TEST_OPTS" - if: matrix.test && matrix.os != 'windows-latest' && matrix.ghc != '9.4.2' name: Test hls-rename-plugin test suite - run: cabal test hls-rename-plugin --test-options="$TEST_OPTS" || cabal test hls-rename-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-rename-plugin --test-options="$TEST_OPTS" + run: cabal test hls-rename-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-rename-plugin --test-options="$TEST_OPTS" - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-hlint-plugin test suite - run: cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-hlint-plugin --test-options="$TEST_OPTS" + run: cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-hlint-plugin --test-options="$TEST_OPTS" - if: matrix.test && matrix.ghc != '9.0.1' && matrix.ghc != '9.0.2' && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2' name: Test hls-stan-plugin test suite - run: cabal test hls-stan-plugin --test-options="$TEST_OPTS" || cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-stan-plugin --test-options="$TEST_OPTS" + run: cabal test hls-stan-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-stan-plugin --test-options="$TEST_OPTS" - if: matrix.test name: Test hls-module-name-plugin test suite - run: cabal test hls-module-name-plugin --test-options="$TEST_OPTS" || cabal test hls-module-name-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-module-name-plugin --test-options="$TEST_OPTS" + run: cabal test hls-module-name-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-module-name-plugin --test-options="$TEST_OPTS" - if: matrix.test name: Test hls-alternate-number-format-plugin test suite - run: cabal test hls-alternate-number-format-plugin --test-options="$TEST_OPTS" || cabal test hls-alternate-number-format-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-alternate-number-format-plugin --test-options="$TEST_OPTS" + run: cabal test hls-alternate-number-format-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-alternate-number-format-plugin --test-options="$TEST_OPTS" - if: matrix.test name: Test hls-qualify-imported-names-plugin test suite - run: cabal test hls-qualify-imported-names-plugin --test-options="$TEST_OPTS" || cabal test hls-qualify-imported-names-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-qualify-imported-names-plugin --test-options="$TEST_OPTS" + run: cabal test hls-qualify-imported-names-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-qualify-imported-names-plugin --test-options="$TEST_OPTS" - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-code-range-plugin test suite - run: cabal test hls-code-range-plugin --test-options="$TEST_OPTS" || cabal test hls-code-range-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-code-range-plugin --test-options="$TEST_OPTS" + run: cabal test hls-code-range-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-code-range-plugin --test-options="$TEST_OPTS" - if: matrix.test name: Test hls-change-type-signature test suite - run: cabal test hls-change-type-signature-plugin --test-options="$TEST_OPTS" || cabal test hls-change-type-signature-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-change-type-signature-plugin --test-options="$TEST_OPTS" + run: cabal test hls-change-type-signature-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-change-type-signature-plugin --test-options="$TEST_OPTS" - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-gadt-plugin test suit - run: cabal test hls-gadt-plugin --test-options="$TEST_OPTS" || cabal test hls-gadt-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-gadt-plugin --test-options="$TEST_OPTS" + run: cabal test hls-gadt-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-gadt-plugin --test-options="$TEST_OPTS" - if: matrix.test name: Test hls-explicit-fixity-plugin test suite - run: cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" || cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" + run: cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" test_post_job: if: always() diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index 87dc2ff6a1..b35e86ebe1 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -7,7 +7,6 @@ RUN sudo install-packages build-essential curl libffi-dev libffi7 libgmp-dev lib echo 'export PATH=$HOME/.cabal/bin:$HOME/.local/bin:$PATH' >> $HOME/.bashrc && \ . /home/gitpod/.ghcup/env && \ # Install all verions of GHC that HLS supports. Putting GHC into Docker image makes workspace start much faster. - ghcup install ghc 8.8.4 && \ ghcup install ghc 8.10.7 && \ ghcup install ghc 9.0.2 && \ ghcup install ghc 9.2.3 && \ diff --git a/bindist/ghcs b/bindist/ghcs index d1c741c324..17e3ffea1c 100644 --- a/bindist/ghcs +++ b/bindist/ghcs @@ -1,4 +1,3 @@ -8.8.4,cabal.project 8.10.7,cabal.project 9.0.2,cabal.project 9.2.3,cabal.project diff --git a/docs/contributing/contributing.md b/docs/contributing/contributing.md index f8f705da1c..7ddeada313 100644 --- a/docs/contributing/contributing.md +++ b/docs/contributing/contributing.md @@ -51,18 +51,14 @@ If you are using nix 2.4 style command (enabled by `experimental-features = nix- you can use `nix develop` instead of `nix-shell` to enter the development shell. To enter the shell with specific GHC versions: * `nix develop` or `nix develop .#haskell-language-server-dev` - default GHC version -* `nix develop .#haskell-language-server-8107-dev` - GHC 8.10.7 -* `nix develop .#haskell-language-server-884-dev` - GHC 8.8.4 -* `nix develop .#haskell-language-server-901-dev` - GHC 9.0.1 +* `nix develop .#haskell-language-server-901-dev` - GHC 9.0.1 (substitute GHC version as appropriate) If you are looking for a Nix expression to create haskell-language-server binaries, see https://github.com/haskell/haskell-language-server/issues/122 To create binaries: * `nix build` or `nix build .#haskell-language-server` - default GHC version -* `nix build .#haskell-language-server-8107` - GHC 8.10.7 -* `nix build .#haskell-language-server-884` - GHC 8.8.4 -* `nix build .#haskell-language-server-901` - GHC 9.0.1 +* `nix build .#haskell-language-server-901` - GHC 9.0.1 (substitute GHC version as appropriate) ## Testing diff --git a/docs/contributing/plugin-tutorial.md b/docs/contributing/plugin-tutorial.md index 53bcfb1a4f..56f1765af2 100644 --- a/docs/contributing/plugin-tutorial.md +++ b/docs/contributing/plugin-tutorial.md @@ -34,7 +34,7 @@ And here is the gist of the algorithm: ## Setup -To get started, let’s fetch the HLS repo and build it. You need at least GHC 8.8 for this: +To get started, let’s fetch the HLS repo and build it. You need at least GHC 8.10 for this: ``` git clone --recursive http://github.com/haskell/haskell-language-server hls diff --git a/docs/installation.md b/docs/installation.md index 7ebfbb432f..b3f8270288 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -157,7 +157,7 @@ Homebrew users can install `haskell-language-server` using the following command brew install haskell-language-server ``` -This formula contains HLS binaries compiled with GHC versions available via Homebrew; at the moment those are: 8.8.4, 8.10.7. +This formula contains HLS binaries compiled with GHC versions available via Homebrew; at the moment those are: 8.10.7. You need to provide your own GHC/Cabal/Stack as required by your project, possibly via Homebrew. diff --git a/docs/support/ghc-version-support.md b/docs/support/ghc-version-support.md index da7887f1fc..9af9d7ed46 100644 --- a/docs/support/ghc-version-support.md +++ b/docs/support/ghc-version-support.md @@ -29,7 +29,7 @@ Support status (see the support policy below for more details): | 8.10.5 | [1.5.1](https://github.com/haskell/haskell-language-server/releases/tag/1.5.1) | deprecated | | 8.10.(4,3,2) | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | | 8.10.1 | [0.9.0](https://github.com/haskell/haskell-language-server/releases/tag/0.9.0) | deprecated | -| 8.8.4 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | full support, will be deprecated after LTS and HLS full support for ghc-9.2 | +| 8.8.4 | [1.8.0](https://github.com/haskell/haskell-language-server/releases/1.8.0) | deprecated | | 8.8.3 | [1.5.1](https://github.com/haskell/haskell-language-server/releases/1.5.1) | deprecated | | 8.8.2 | [1.2.0](https://github.com/haskell/haskell-language-server/releases/tag/1.2.0) | deprecated | | 8.6.5 | [1.8.0.0](https://github.com/haskell/haskell-language-server/releases/tag/1.8.0.0) | deprecated | diff --git a/ghcide-bench/ghcide-bench.cabal b/ghcide-bench/ghcide-bench.cabal index f48484de96..24e9ee2c80 100644 --- a/ghcide-bench/ghcide-bench.cabal +++ b/ghcide-bench/ghcide-bench.cabal @@ -12,7 +12,7 @@ synopsis: An LSP client for running performance experiments on HLS description: An LSP client for running performance experiments on HLS homepage: https://github.com/haskell/haskell-language-server/tree/master/ghcide#readme bug-reports: https://github.com/haskell/haskell-language-server/issues -tested-with: GHC == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 +tested-with: GHC == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 source-repository head type: git diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index abf724fc64..2b3ac1dbba 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -13,7 +13,7 @@ description: A library for building Haskell IDE's on top of the GHC API. homepage: https://github.com/haskell/haskell-language-server/tree/master/ghcide#readme bug-reports: https://github.com/haskell/haskell-language-server/issues -tested-with: GHC == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 +tested-with: GHC == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 extra-source-files: README.md CHANGELOG.md test/data/**/*.project test/data/**/*.cabal @@ -99,7 +99,7 @@ library unliftio-core, ghc-boot-th, ghc-boot, - ghc >= 8.8, + ghc >= 8.10, ghc-check >=0.5.0.8, ghc-paths, cryptohash-sha1 >=0.11.100 && <0.12, diff --git a/ghcide/src/Development/IDE/Core/Compile.hs b/ghcide/src/Development/IDE/Core/Compile.hs index c8527d115d..978e0ceccb 100644 --- a/ghcide/src/Development/IDE/Core/Compile.hs +++ b/ghcide/src/Development/IDE/Core/Compile.hs @@ -104,10 +104,6 @@ import System.FilePath import System.IO.Extra (fixIO, newTempFileWithin) import Unsafe.Coerce -#if !MIN_VERSION_ghc(8,10,0) -import ErrUtils -#endif - #if MIN_VERSION_ghc(9,0,1) import GHC.Tc.Gen.Splice @@ -482,11 +478,9 @@ mkHiFileResultCompile se session' tcm simplified_guts = catchErrs $ do Nothing #endif -#elif MIN_VERSION_ghc(8,10,0) +#else let !partial_iface = force (mkPartialIface session details simplified_guts) final_iface <- mkFullIface session partial_iface -#else - (final_iface,_) <- mkIface session Nothing details simplified_guts #endif -- Write the core file now @@ -637,11 +631,7 @@ generateObjectCode session summary guts = do #else (outputFilename, _mStub, _foreign_files) <- hscGenHardCode session' guts #endif -#if MIN_VERSION_ghc(8,10,0) (ms_location summary) -#else - summary -#endif fp obj <- compileFile session' driverNoStop (outputFilename, Just (As False)) #if MIN_VERSION_ghc(9,3,0) @@ -670,11 +660,7 @@ generateByteCode (CoreFileTime time) hscEnv summary guts = do -- TODO: maybe settings ms_hspp_opts is unnecessary? summary' = summary { ms_hspp_opts = hsc_dflags session } hscInteractive session guts -#if MIN_VERSION_ghc(8,10,0) (ms_location summary') -#else - summary' -#endif let unlinked = BCOs bytecode sptEntries let linkable = LM time (ms_mod summary) [unlinked] pure (map snd warnings, linkable) @@ -739,9 +725,7 @@ unnecessaryDeprecationWarningFlags , Opt_WarnUnusedMatches , Opt_WarnUnusedTypePatterns , Opt_WarnUnusedForalls -#if MIN_VERSION_ghc(8,10,0) , Opt_WarnUnusedRecordWildcards -#endif , Opt_WarnInaccessibleCode , Opt_WarnWarningsDeprecations ] diff --git a/ghcide/src/Development/IDE/GHC/CPP.hs b/ghcide/src/Development/IDE/GHC/CPP.hs index d0aaec5e95..7495de21a4 100644 --- a/ghcide/src/Development/IDE/GHC/CPP.hs +++ b/ghcide/src/Development/IDE/GHC/CPP.hs @@ -16,30 +16,21 @@ module Development.IDE.GHC.CPP(doCpp, addOptP) where import Development.IDE.GHC.Compat as Compat -import GHC -#if !MIN_VERSION_ghc(8,10,0) -import qualified Development.IDE.GHC.Compat.CPP as CPP -#else import Development.IDE.GHC.Compat.Util -#endif +import GHC #if MIN_VERSION_ghc(9,0,0) import qualified GHC.Driver.Pipeline as Pipeline import GHC.Settings -#else -#if MIN_VERSION_ghc (8,10,0) +#elif MIN_VERSION_ghc (8,10,0) import qualified DriverPipeline as Pipeline import ToolSettings -#else -import DynFlags -#endif #endif #if MIN_VERSION_ghc(9,3,0) import qualified GHC.Driver.Pipeline.Execute as Pipeline #endif addOptP :: String -> DynFlags -> DynFlags -#if MIN_VERSION_ghc (8,10,0) addOptP f = alterToolSettings $ \s -> s { toolSettings_opt_P = f : toolSettings_opt_P s , toolSettings_opt_P_fingerprint = fingerprintStrings (f : toolSettings_opt_P s) @@ -47,20 +38,12 @@ addOptP f = alterToolSettings $ \s -> s where fingerprintStrings ss = fingerprintFingerprints $ map fingerprintString ss alterToolSettings f dynFlags = dynFlags { toolSettings = f (toolSettings dynFlags) } -#else -addOptP opt = onSettings (onOptP (opt:)) - where - onSettings f x = x{settings = f $ settings x} - onOptP f x = x{sOpt_P = f $ sOpt_P x} -#endif doCpp :: HscEnv -> Bool -> FilePath -> FilePath -> IO () doCpp env raw input_fn output_fn = #if MIN_VERSION_ghc (9,2,0) Pipeline.doCpp (hsc_logger env) (hsc_tmpfs env) (hsc_dflags env) (hsc_unit_env env) raw input_fn output_fn -#elif MIN_VERSION_ghc (8,10,0) - Pipeline.doCpp (hsc_dflags env) raw input_fn output_fn #else - CPP.doCpp (hsc_dflags env) raw input_fn output_fn + Pipeline.doCpp (hsc_dflags env) raw input_fn output_fn #endif diff --git a/ghcide/src/Development/IDE/GHC/Compat.hs b/ghcide/src/Development/IDE/GHC/Compat.hs index b853fb0a25..216039cd1c 100644 --- a/ghcide/src/Development/IDE/GHC/Compat.hs +++ b/ghcide/src/Development/IDE/GHC/Compat.hs @@ -244,10 +244,6 @@ import Data.List (foldl') import qualified Data.Map as Map import qualified Data.Set as S -#if !MIN_VERSION_ghc(8,10,0) -import Bag (unitBag) -#endif - #if MIN_VERSION_ghc(9,2,0) import GHC.Builtin.Uniques import GHC.ByteCode.Types @@ -404,17 +400,10 @@ pattern PFailedWithErrorMessages msgs #else <- PFailed (const . fmap pprError . getErrorMessages -> msgs) #endif -#elif MIN_VERSION_ghc(8,10,0) -pattern PFailedWithErrorMessages :: (DynFlags -> ErrorMessages) -> ParseResult a -pattern PFailedWithErrorMessages msgs - <- PFailed (getErrorMessages -> msgs) #else pattern PFailedWithErrorMessages :: (DynFlags -> ErrorMessages) -> ParseResult a pattern PFailedWithErrorMessages msgs - <- ((fmap.fmap) unitBag . mkPlainErrMsgIfPFailed -> Just msgs) - -mkPlainErrMsgIfPFailed (PFailed _ pst err) = Just (\dflags -> mkPlainErrMsg dflags pst err) -mkPlainErrMsgIfPFailed _ = Nothing + <- PFailed (getErrorMessages -> msgs) #endif {-# COMPLETE POk, PFailedWithErrorMessages #-} @@ -488,11 +477,7 @@ nameListFromAvails as = getModuleHash :: ModIface -> Fingerprint -#if MIN_VERSION_ghc(8,10,0) getModuleHash = mi_mod_hash . mi_final_exts -#else -getModuleHash = mi_mod_hash -#endif disableWarningsAsErrors :: DynFlags -> DynFlags @@ -500,12 +485,8 @@ disableWarningsAsErrors df = flip gopt_unset Opt_WarnIsError $ foldl' wopt_unset_fatal df [toEnum 0 ..] isQualifiedImport :: ImportDecl a -> Bool -#if MIN_VERSION_ghc(8,10,0) isQualifiedImport ImportDecl{ideclQualified = NotQualified} = False isQualifiedImport ImportDecl{} = True -#else -isQualifiedImport ImportDecl{ideclQualified} = ideclQualified -#endif isQualifiedImport _ = False @@ -566,8 +547,7 @@ generatedNodeInfo = sourceNodeInfo -- before ghc 9.0, we don't distinguish the s #endif data GhcVersion - = GHC88 - | GHC810 + = GHC810 | GHC90 | GHC92 | GHC94 @@ -585,8 +565,6 @@ ghcVersion = GHC92 ghcVersion = GHC90 #elif MIN_VERSION_GLASGOW_HASKELL(8,10,0,0) ghcVersion = GHC810 -#elif MIN_VERSION_GLASGOW_HASKELL(8,8,0,0) -ghcVersion = GHC88 #endif runUnlit :: Logger -> DynFlags -> [Option] -> IO () diff --git a/ghcide/src/Development/IDE/GHC/Compat/CPP.hs b/ghcide/src/Development/IDE/GHC/Compat/CPP.hs index 831ecfa3cc..9da9fa4786 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/CPP.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/CPP.hs @@ -48,11 +48,7 @@ doCpp dflags raw input_fn output_fn = do let verbFlags = getVerbFlags dflags let cpp_prog args | raw = SysTools.runCpp dflags args -#if MIN_VERSION_ghc(8,10,0) | otherwise = SysTools.runCc Nothing -#else - | otherwise = SysTools.runCc -#endif dflags (SysTools.Option "-E" : args) let target_defs = diff --git a/ghcide/src/Development/IDE/GHC/Compat/Core.hs b/ghcide/src/Development/IDE/GHC/Compat/Core.hs index 88acf5cde4..af5c8c1ace 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Core.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Core.hs @@ -76,12 +76,8 @@ module Development.IDE.GHC.Compat.Core ( -- * Interface Files IfaceExport, IfaceTyCon(..), -#if MIN_VERSION_ghc(8,10,0) ModIface, ModIface_(..), -#else - ModIface(..), -#endif HscSource(..), WhereFrom(..), loadInterface, @@ -90,12 +86,8 @@ module Development.IDE.GHC.Compat.Core ( #endif loadModuleInterface, RecompileRequired(..), -#if MIN_VERSION_ghc(8,10,0) mkPartialIface, mkFullIface, -#else - mkIface, -#endif checkOldIface, #if MIN_VERSION_ghc(9,0,0) IsBootInterface(..), @@ -141,7 +133,7 @@ module Development.IDE.GHC.Compat.Core ( #if !MIN_VERSION_ghc(9,2,0) Development.IDE.GHC.Compat.Core.splitForAllTyCoVars, #endif - Development.IDE.GHC.Compat.Core.mkVisFunTys, + mkVisFunTys, Development.IDE.GHC.Compat.Core.mkInfForAllTys, -- * Specs ImpDeclSpec(..), @@ -261,9 +253,6 @@ module Development.IDE.GHC.Compat.Core ( SrcLoc.noSrcSpan, SrcLoc.noSrcLoc, SrcLoc.noLoc, -#if !MIN_VERSION_ghc(8,10,0) - SrcLoc.dL, -#endif -- * Finder FindResult(..), mkHomeModLocation, @@ -403,10 +392,8 @@ module Development.IDE.GHC.Compat.Core ( #else module BasicTypes, module Class, -#if MIN_VERSION_ghc(8,10,0) module Coercion, module Predicate, -#endif module ConLike, module CoreUtils, module DataCon, @@ -453,22 +440,7 @@ module Development.IDE.GHC.Compat.Core ( module GHC.Parser.Header, module GHC.Parser.Lexer, #else -#if MIN_VERSION_ghc(8,10,0) module GHC.Hs, -#else - module HsBinds, - module HsDecls, - module HsDoc, - module HsExtension, - noExtField, - module HsExpr, - module HsImpExp, - module HsLit, - module HsPat, - module HsSyn, - module HsTypes, - module HsUtils, -#endif module ExtractDocs, module Parser, module Lexer, @@ -541,8 +513,7 @@ import GHC.Core.Predicate import GHC.Core.TyCo.Ppr import qualified GHC.Core.TyCo.Rep as TyCoRep import GHC.Core.TyCon -import GHC.Core.Type hiding (mkInfForAllTys, - mkVisFunTys) +import GHC.Core.Type hiding (mkInfForAllTys) import GHC.Core.Unify import GHC.Core.Utils @@ -693,29 +664,13 @@ import ExtractDocs import FamInst import FamInstEnv import Finder hiding (mkHomeModLocation) -#if MIN_VERSION_ghc(8,10,0) import GHC.Hs hiding (HsLet, LetStmt) -#endif import qualified GHCi import GhcMonad import HeaderInfo hiding (getImports) import Hooks import HscMain as GHC import HscTypes -#if !MIN_VERSION_ghc(8,10,0) --- Syntax imports -import HsBinds -import HsDecls -import HsDoc -import HsExpr hiding (HsLet, LetStmt) -import HsExtension -import HsImpExp -import HsLit -import HsPat -import HsSyn hiding (wildCardName, HsLet, LetStmt) -import HsTypes hiding (wildCardName) -import HsUtils -#endif import Id import IfaceSyn import InstEnv @@ -755,12 +710,12 @@ import TcRnMonad hiding (Applicative (..), IORef, allM, anyM, concatMapM, foldrM, mapMaybeM, (<$>)) import TcRnTypes -import TcType hiding (mkVisFunTys) +import TcType import qualified TcType import TidyPgm as GHC import qualified TyCoRep import TyCon -import Type hiding (mkVisFunTys) +import Type import TysPrim import TysWiredIn import Unify @@ -769,16 +724,10 @@ import UniqSupply import Var (Var (varName), setTyVarUnique, setVarUnique, varType) -#if MIN_VERSION_ghc(8,10,0) import Coercion (coercionKind) import Predicate import SrcLoc (Located, SrcLoc (UnhelpfulLoc), SrcSpan (UnhelpfulSpan)) -#else -import SrcLoc (RealLocated, - SrcLoc (UnhelpfulLoc), - SrcSpan (UnhelpfulSpan)) -#endif #endif @@ -890,11 +839,7 @@ pattern ExposePackage s a mr = DynFlags.ExposePackage s a mr #endif pattern FunTy :: Type -> Type -> Type -#if MIN_VERSION_ghc(8,10,0) pattern FunTy arg res <- TyCoRep.FunTy {ft_arg = arg, ft_res = res} -#else -pattern FunTy arg res <- TyCoRep.FunTy arg res -#endif #if MIN_VERSION_ghc(9,0,0) -- type HasSrcSpan x a = (GenLocated SrcSpan a ~ x) @@ -941,14 +886,6 @@ unrestricted :: a -> Scaled a unrestricted = id #endif -mkVisFunTys :: [Scaled Type] -> Type -> Type -mkVisFunTys = -#if __GLASGOW_HASKELL__ == 808 - mkFunTys -#else - TcType.mkVisFunTys -#endif - mkInfForAllTys :: [TyVar] -> Type -> Type mkInfForAllTys = #if MIN_VERSION_ghc(9,0,0) @@ -981,11 +918,6 @@ tcSplitForAllTyVarBinder_maybe = #endif -#if !MIN_VERSION_ghc(8,10,0) -noExtField :: GHC.NoExt -noExtField = GHC.noExt -#endif - #if !MIN_VERSION_ghc(9,0,0) pattern NotBoot, IsBoot :: IsBootInterface pattern NotBoot = False @@ -1132,15 +1064,11 @@ makeSimpleDetails hsc_env = #endif mkIfaceTc hsc_env sf details ms tcGblEnv = -#if MIN_VERSION_ghc(8,10,0) GHC.mkIfaceTc hsc_env sf details #if MIN_VERSION_ghc(9,3,0) ms #endif tcGblEnv -#else - fst <$> GHC.mkIfaceTc hsc_env Nothing sf details tcGblEnv -#endif mkBootModDetailsTc :: HscEnv -> TcGblEnv -> IO ModDetails mkBootModDetailsTc session = GHC.mkBootModDetailsTc diff --git a/ghcide/src/Development/IDE/GHC/Compat/Util.hs b/ghcide/src/Development/IDE/GHC/Compat/Util.hs index 7c521e88e8..c726bfad4c 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Util.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Util.hs @@ -30,10 +30,8 @@ module Development.IDE.GHC.Compat.Util ( -- * Maybes MaybeErr(..), orElse, -#if MIN_VERSION_ghc(8,10,0) -- * Pair Pair(..), -#endif -- * EnumSet EnumSet, toList, @@ -97,10 +95,8 @@ import qualified Exception import FastString import Fingerprint import Maybes -#if MIN_VERSION_ghc(8,10,0) -import Pair -#endif import Outputable (pprHsString) +import Pair import Panic hiding (try) import StringBuffer import UniqDFM diff --git a/ghcide/src/Development/IDE/GHC/Orphans.hs b/ghcide/src/Development/IDE/GHC/Orphans.hs index 6a2ddc7586..9e3d206d0e 100644 --- a/ghcide/src/Development/IDE/GHC/Orphans.hs +++ b/ghcide/src/Development/IDE/GHC/Orphans.hs @@ -104,11 +104,6 @@ instance Show ParsedModule where instance NFData ModSummary where rnf = rwhnf -#if !MIN_VERSION_ghc(8,10,0) -instance NFData FastString where - rnf = rwhnf -#endif - #if MIN_VERSION_ghc(9,2,0) instance Ord FastString where compare a b = if a == b then EQ else compare (fs_sbs a) (fs_sbs b) diff --git a/ghcide/src/Development/IDE/GHC/Util.hs b/ghcide/src/Development/IDE/GHC/Util.hs index 69cc2adf77..70486f4d74 100644 --- a/ghcide/src/Development/IDE/GHC/Util.hs +++ b/ghcide/src/Development/IDE/GHC/Util.hs @@ -280,13 +280,6 @@ ioe_dupHandlesNotCompatible h = -------------------------------------------------------------------------------- -- Tracing exactprint terms --- Should in `Development.IDE.GHC.Orphans`, --- leave it here to prevent cyclic module dependency -#if !MIN_VERSION_ghc(8,10,0) -instance Outputable SDoc where - ppr = id -#endif - -- | Print a GHC value in `defaultUserStyle` without unique symbols. -- It uses `showSDocUnsafe` with `unsafeGlobalDynFlags` internally. -- diff --git a/ghcide/src/Development/IDE/LSP/Outline.hs b/ghcide/src/Development/IDE/LSP/Outline.hs index 2ad518d588..b31cd90f7b 100644 --- a/ghcide/src/Development/IDE/LSP/Outline.hs +++ b/ghcide/src/Development/IDE/LSP/Outline.hs @@ -248,11 +248,7 @@ documentSymbolForImport (L (locA -> (RealSrcSpan l _)) ImportDecl { ideclName, i (defDocumentSymbol l :: DocumentSymbol) { _name = "import " <> printOutputable ideclName , _kind = SkModule -#if MIN_VERSION_ghc(8,10,0) , _detail = case ideclQualified of { NotQualified -> Nothing; _ -> Just "qualified" } -#else - , _detail = if ideclQualified then Just "qualified" else Nothing -#endif } documentSymbolForImport _ = Nothing diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index 78a921bec4..7d2190cac8 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -290,12 +290,8 @@ mkNameCompItem doc thingParent origName provenance thingType isInfix docs !imp = then getArgs ret else Prelude.filter (not . isDictTy) $ map scaledThing args | isPiTy t = getArgs $ snd (splitPiTys t) -#if MIN_VERSION_ghc(8,10,0) | Just (Pair _ t) <- coercionKind <$> isCoercionTy_maybe t = getArgs t -#else - | isCoercionTy t = maybe [] (getArgs . snd) (splitCoercionType_maybe t) -#endif | otherwise = [] @@ -766,9 +762,6 @@ uniqueCompl candidate unique = importedFrom (provenance -> ImportedFrom m) = m importedFrom (provenance -> DefinedIn m) = m importedFrom (provenance -> Local _) = "local" -#if __GLASGOW_HASKELL__ < 810 - importedFrom _ = "" -#endif -- --------------------------------------------------------------------- -- helper functions for infix backticks diff --git a/ghcide/src/Development/IDE/Spans/Pragmas.hs b/ghcide/src/Development/IDE/Spans/Pragmas.hs index f22acf04c3..02d3db2721 100644 --- a/ghcide/src/Development/IDE/Spans/Pragmas.hs +++ b/ghcide/src/Development/IDE/Spans/Pragmas.hs @@ -416,23 +416,10 @@ mkLexerPState dynFlags stringBuffer = startRealSrcLoc = mkRealSrcLoc "asdf" 1 1 updateDynFlags = flip gopt_unset Opt_Haddock . flip gopt_set Opt_KeepRawTokenStream finalDynFlags = updateDynFlags dynFlags -#if !MIN_VERSION_ghc(8,10,1) - mkLexerParserFlags = - mkParserFlags' - <$> warningFlags - <*> extensionFlags - <*> homeUnitId_ - <*> safeImportsOn - <*> gopt Opt_Haddock - <*> gopt Opt_KeepRawTokenStream - <*> const False - finalPState = mkPStatePure (mkLexerParserFlags finalDynFlags) stringBuffer startRealSrcLoc -#else pState = initParserState (initParserOpts finalDynFlags) stringBuffer startRealSrcLoc PState{ options = pStateOptions } = pState finalExtBitsMap = setBit (pExtsBitmap pStateOptions) (fromEnum UsePosPragsBit) finalPStateOptions = pStateOptions{ pExtsBitmap = finalExtBitsMap } finalPState = pState{ options = finalPStateOptions } -#endif in finalPState diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 384efce985..81597e1efd 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -1186,7 +1186,7 @@ checkFileCompiles fp diag = pluginSimpleTests :: TestTree pluginSimpleTests = - ignoreInWindowsForGHC88And810 $ + ignoreInWindowsForGHC810 $ ignoreForGHC92Plus "blocked on ghc-typelits-natnormalise" $ testSessionWithExtraFiles "plugin-knownnat" "simple plugin" $ \dir -> do _ <- openDoc (dir "KnownNat.hs") "haskell" @@ -1201,7 +1201,7 @@ pluginSimpleTests = pluginParsedResultTests :: TestTree pluginParsedResultTests = - ignoreInWindowsForGHC88And810 $ + ignoreInWindowsForGHC810 $ ignoreForGHC92Plus "No need for this plugin anymore!" $ testSessionWithExtraFiles "plugin-recorddot" "parsedResultAction plugin" $ \dir -> do _ <- openDoc (dir "RecordDot.hs") "haskell" @@ -1370,7 +1370,7 @@ thTests = _ <- createDoc "A.hs" "haskell" sourceA _ <- createDoc "B.hs" "haskell" sourceB expectDiagnostics [ ( "B.hs", [(DsWarning, (4, 0), "Top-level binding with no type signature: main :: IO ()")] ) ] - , ignoreInWindowsForGHC88 $ testCase "findsTHnewNameConstructor" $ runWithExtraFiles "THNewName" $ \dir -> do + , testCase "findsTHnewNameConstructor" $ runWithExtraFiles "THNewName" $ \dir -> do -- This test defines a TH value with the meaning "data A = A" in A.hs -- Loads and export the template in B.hs @@ -2273,17 +2273,13 @@ xfail = flip expectFailBecause ignoreInWindowsBecause :: String -> TestTree -> TestTree ignoreInWindowsBecause = ignoreFor (BrokenForOS Windows) -ignoreInWindowsForGHC88And810 :: TestTree -> TestTree -ignoreInWindowsForGHC88And810 = - ignoreFor (BrokenSpecific Windows [GHC88, GHC810]) "tests are unreliable in windows for ghc 8.8 and 8.10" +ignoreInWindowsForGHC810 :: TestTree -> TestTree +ignoreInWindowsForGHC810 = + ignoreFor (BrokenSpecific Windows [GHC810]) "tests are unreliable in windows for ghc 8.10" ignoreForGHC92Plus :: String -> TestTree -> TestTree ignoreForGHC92Plus = ignoreFor (BrokenForGHC [GHC92, GHC94]) -ignoreInWindowsForGHC88 :: TestTree -> TestTree -ignoreInWindowsForGHC88 = - ignoreFor (BrokenSpecific Windows [GHC88]) "tests are unreliable in windows for ghc 8.8" - knownBrokenForGhcVersions :: [GhcVersion] -> String -> TestTree -> TestTree knownBrokenForGhcVersions ghcVers = knownBrokenFor (BrokenForGHC ghcVers) @@ -2455,7 +2451,7 @@ retryFailedCradle = testSession' "retry failed" $ \dir -> do dependentFileTest :: TestTree dependentFileTest = testGroup "addDependentFile" - [testGroup "file-changed" [ignoreInWindowsForGHC88 $ testSession' "test" test] + [testGroup "file-changed" [testSession' "test" test] ] where test dir = do diff --git a/ghcide/test/ghcide-test-utils.cabal b/ghcide/test/ghcide-test-utils.cabal index cccc5e35ac..3a180970c3 100644 --- a/ghcide/test/ghcide-test-utils.cabal +++ b/ghcide/test/ghcide-test-utils.cabal @@ -14,7 +14,7 @@ description: Test utils for ghcide homepage: https://github.com/haskell/haskell-language-server/tree/master/ghcide#readme bug-reports: https://github.com/haskell/haskell-language-server/issues -tested-with: GHC == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 +tested-with: GHC == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 source-repository head type: git diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 184d3a2729..c2938ed6e7 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -14,7 +14,7 @@ copyright: The Haskell IDE Team license: Apache-2.0 license-file: LICENSE build-type: Simple -tested-with: GHC == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 +tested-with: GHC == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 extra-source-files: README.md ChangeLog.md @@ -251,7 +251,7 @@ common hlint cpp-options: -Dhls_hlint common stan - if flag(stan) && (impl(ghc >= 8.8) && impl(ghc < 9.0)) + if flag(stan) && (impl(ghc >= 8.10) && impl(ghc < 9.0)) build-depends: hls-stan-plugin ^>= 1.0 cpp-options: -Dhls_stan diff --git a/hie-compat/hie-compat.cabal b/hie-compat/hie-compat.cabal index 2a7c2d65d8..9cd4cc2f75 100644 --- a/hie-compat/hie-compat.cabal +++ b/hie-compat/hie-compat.cabal @@ -46,8 +46,6 @@ library Compat.HieDebug Compat.HieUtils - if (impl(ghc > 8.7) && impl(ghc < 8.10)) - hs-source-dirs: src-ghc88 src-reexport if (impl(ghc > 8.9) && impl(ghc < 8.11)) hs-source-dirs: src-ghc810 src-reexport if (impl(ghc >= 9.0) && impl(ghc < 9.1) || flag(ghc-lib)) diff --git a/hie-compat/src-ghc88/Compat/HieAst.hs b/hie-compat/src-ghc88/Compat/HieAst.hs deleted file mode 100644 index f1fab23db3..0000000000 --- a/hie-compat/src-ghc88/Compat/HieAst.hs +++ /dev/null @@ -1,1760 +0,0 @@ -{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} -{- -Forked from GHC v8.8.1 to work around the readFile side effect in mkHiefile - -Main functions for .hie file generation --} -{- HLINT ignore -} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE TypeSynonymInstances #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE AllowAmbiguousTypes #-} -{-# LANGUAGE ViewPatterns #-} -{-# LANGUAGE DeriveDataTypeable #-} -module Compat.HieAst ( enrichHie ) where - -import Avail ( Avails ) -import Bag ( Bag, bagToList ) -import BasicTypes -import BooleanFormula -import Class ( FunDep ) -import CoreUtils ( exprType ) -import ConLike ( conLikeName ) -import Desugar ( deSugarExpr ) -import FieldLabel -import HsSyn -import HscTypes -import Module ( ModuleName, ml_hs_file ) -import MonadUtils ( concatMapM, liftIO ) -import Name ( Name, nameSrcSpan ) -import SrcLoc -import TcHsSyn ( hsLitType, hsPatType ) -import Type ( mkFunTys, Type ) -import TysWiredIn ( mkListTy, mkSumTy ) -import Var ( Id, Var, setVarName, varName, varType ) -import TcRnTypes -import MkIface ( mkIfaceExports ) - -import HieTypes -import HieUtils - -import qualified Data.Array as A -import qualified Data.ByteString as BS -import qualified Data.Map as M -import qualified Data.Set as S -import Data.Data ( Data, Typeable ) -import Data.List (foldl', foldl1' ) -import Data.Maybe ( listToMaybe ) -import Control.Monad.Trans.Reader -import Control.Monad.Trans.Class ( lift ) - --- These synonyms match those defined in main/GHC.hs -type RenamedSource = ( HsGroup GhcRn, [LImportDecl GhcRn] - , Maybe [(LIE GhcRn, Avails)] - , Maybe LHsDocString ) -type TypecheckedSource = LHsBinds GhcTc - - -{- Note [Name Remapping] -The Typechecker introduces new names for mono names in AbsBinds. -We don't care about the distinction between mono and poly bindings, -so we replace all occurrences of the mono name with the poly name. --} -newtype HieState = HieState - { name_remapping :: M.Map Name Id - } - -initState :: HieState -initState = HieState M.empty - -class ModifyState a where -- See Note [Name Remapping] - addSubstitution :: a -> a -> HieState -> HieState - -instance ModifyState Name where - addSubstitution _ _ hs = hs - -instance ModifyState Id where - addSubstitution mono poly hs = - hs{name_remapping = M.insert (varName mono) poly (name_remapping hs)} - -modifyState :: ModifyState (IdP p) => [ABExport p] -> HieState -> HieState -modifyState = foldr go id - where - go ABE{abe_poly=poly,abe_mono=mono} f = addSubstitution mono poly . f - go _ f = f - -type HieM = ReaderT HieState Hsc - -enrichHie :: TypecheckedSource -> RenamedSource -> Hsc (HieASTs Type) -enrichHie ts (hsGrp, imports, exports, _) = flip runReaderT initState $ do - tasts <- toHie $ fmap (BC RegularBind ModuleScope) ts - rasts <- processGrp hsGrp - imps <- toHie $ filter (not . ideclImplicit . unLoc) imports - exps <- toHie $ fmap (map $ IEC Export . fst) exports - let spanFile children = case children of - [] -> mkRealSrcSpan (mkRealSrcLoc "" 1 1) (mkRealSrcLoc "" 1 1) - _ -> mkRealSrcSpan (realSrcSpanStart $ nodeSpan $ head children) - (realSrcSpanEnd $ nodeSpan $ last children) - - modulify xs = - Node (simpleNodeInfo "Module" "Module") (spanFile xs) xs - - asts = HieASTs - $ resolveTyVarScopes - $ M.map (modulify . mergeSortAsts) - $ M.fromListWith (++) - $ map (\x -> (srcSpanFile (nodeSpan x),[x])) flat_asts - - flat_asts = concat - [ tasts - , rasts - , imps - , exps - ] - return asts - where - processGrp grp = concatM - [ toHie $ fmap (RS ModuleScope ) hs_valds grp - , toHie $ hs_splcds grp - , toHie $ hs_tyclds grp - , toHie $ hs_derivds grp - , toHie $ hs_fixds grp - , toHie $ hs_defds grp - , toHie $ hs_fords grp - , toHie $ hs_warnds grp - , toHie $ hs_annds grp - , toHie $ hs_ruleds grp - ] - -getRealSpan :: SrcSpan -> Maybe Span -getRealSpan (RealSrcSpan sp) = Just sp -getRealSpan _ = Nothing - -grhss_span :: GRHSs p body -> SrcSpan -grhss_span (GRHSs _ xs bs) = foldl' combineSrcSpans (getLoc bs) (map getLoc xs) -grhss_span (XGRHSs _) = error "XGRHS has no span" - -bindingsOnly :: [Context Name] -> [HieAST a] -bindingsOnly [] = [] -bindingsOnly (C c n : xs) = case nameSrcSpan n of - RealSrcSpan span -> Node nodeinfo span [] : bindingsOnly xs - where nodeinfo = NodeInfo S.empty [] (M.singleton (Right n) info) - info = mempty{identInfo = S.singleton c} - _ -> bindingsOnly xs - -concatM :: Monad m => [m [a]] -> m [a] -concatM xs = concat <$> sequence xs - -{- Note [Capturing Scopes and other non local information] -toHie is a local tranformation, but scopes of bindings cannot be known locally, -hence we have to push the relevant info down into the binding nodes. -We use the following types (*Context and *Scoped) to wrap things and -carry the required info -(Maybe Span) always carries the span of the entire binding, including rhs --} -data Context a = C ContextInfo a -- Used for names and bindings - -data RContext a = RC RecFieldContext a -data RFContext a = RFC RecFieldContext (Maybe Span) a --- ^ context for record fields - -data IEContext a = IEC IEType a --- ^ context for imports/exports - -data BindContext a = BC BindType Scope a --- ^ context for imports/exports - -data PatSynFieldContext a = PSC (Maybe Span) a --- ^ context for pattern synonym fields. - -data SigContext a = SC SigInfo a --- ^ context for type signatures - -data SigInfo = SI SigType (Maybe Span) - -data SigType = BindSig | ClassSig | InstSig - -data RScoped a = RS Scope a --- ^ Scope spans over everything to the right of a, (mostly) not --- including a itself --- (Includes a in a few special cases like recursive do bindings) or --- let/where bindings - --- | Pattern scope -data PScoped a = PS (Maybe Span) - Scope -- ^ use site of the pattern - Scope -- ^ pattern to the right of a, not including a - a - deriving (Typeable, Data) -- Pattern Scope - -{- Note [TyVar Scopes] -Due to -XScopedTypeVariables, type variables can be in scope quite far from -their original binding. We resolve the scope of these type variables -in a separate pass --} -data TScoped a = TS TyVarScope a -- TyVarScope - -data TVScoped a = TVS TyVarScope Scope a -- TyVarScope --- ^ First scope remains constant --- Second scope is used to build up the scope of a tyvar over --- things to its right, ala RScoped - --- | Each element scopes over the elements to the right -listScopes :: Scope -> [Located a] -> [RScoped (Located a)] -listScopes _ [] = [] -listScopes rhsScope [pat] = [RS rhsScope pat] -listScopes rhsScope (pat : pats) = RS sc pat : pats' - where - pats'@((RS scope p):_) = listScopes rhsScope pats - sc = combineScopes scope $ mkScope $ getLoc p - --- | 'listScopes' specialised to 'PScoped' things -patScopes - :: Maybe Span - -> Scope - -> Scope - -> [LPat (GhcPass p)] - -> [PScoped (LPat (GhcPass p))] -patScopes rsp useScope patScope xs = - map (\(RS sc a) -> PS rsp useScope sc (unLoc a)) $ - listScopes patScope (map dL xs) - --- | 'listScopes' specialised to 'TVScoped' things -tvScopes - :: TyVarScope - -> Scope - -> [LHsTyVarBndr a] - -> [TVScoped (LHsTyVarBndr a)] -tvScopes tvScope rhsScope xs = - map (\(RS sc a)-> TVS tvScope sc a) $ listScopes rhsScope xs - -{- Note [Scoping Rules for SigPat] -Explicitly quantified variables in pattern type signatures are not -brought into scope in the rhs, but implicitly quantified variables -are (HsWC and HsIB). -This is unlike other signatures, where explicitly quantified variables -are brought into the RHS Scope -For example -foo :: forall a. ...; -foo = ... -- a is in scope here - -bar (x :: forall a. a -> a) = ... -- a is not in scope here --- ^ a is in scope here (pattern body) - -bax (x :: a) = ... -- a is in scope here -Because of HsWC and HsIB pass on their scope to their children -we must wrap the LHsType in pattern signatures in a -Shielded explictly, so that the HsWC/HsIB scope is not passed -on the the LHsType --} - -data Shielded a = SH Scope a -- Ignores its TScope, uses its own scope instead - -type family ProtectedSig a where - ProtectedSig GhcRn = HsWildCardBndrs GhcRn (HsImplicitBndrs - GhcRn - (Shielded (LHsType GhcRn))) - ProtectedSig GhcTc = NoExt - -class ProtectSig a where - protectSig :: Scope -> LHsSigWcType (NoGhcTc a) -> ProtectedSig a - -instance (HasLoc a) => HasLoc (Shielded a) where - loc (SH _ a) = loc a - -instance (ToHie (TScoped a)) => ToHie (TScoped (Shielded a)) where - toHie (TS _ (SH sc a)) = toHie (TS (ResolvedScopes [sc]) a) - -instance ProtectSig GhcTc where - protectSig _ _ = NoExt - -instance ProtectSig GhcRn where - protectSig sc (HsWC a (HsIB b sig)) = - HsWC a (HsIB b (SH sc sig)) - protectSig _ _ = error "protectSig not given HsWC (HsIB)" - -class HasLoc a where - -- ^ defined so that HsImplicitBndrs and HsWildCardBndrs can - -- know what their implicit bindings are scoping over - loc :: a -> SrcSpan - -instance HasLoc thing => HasLoc (TScoped thing) where - loc (TS _ a) = loc a - -instance HasLoc thing => HasLoc (PScoped thing) where - loc (PS _ _ _ a) = loc a - -instance HasLoc (LHsQTyVars GhcRn) where - loc (HsQTvs _ vs) = loc vs - loc _ = noSrcSpan - -instance HasLoc thing => HasLoc (HsImplicitBndrs a thing) where - loc (HsIB _ a) = loc a - loc _ = noSrcSpan - -instance HasLoc thing => HasLoc (HsWildCardBndrs a thing) where - loc (HsWC _ a) = loc a - loc _ = noSrcSpan - -instance HasLoc (Located a) where - loc (L l _) = l - -instance HasLoc a => HasLoc [a] where - loc [] = noSrcSpan - loc xs = foldl1' combineSrcSpans $ map loc xs - -instance (HasLoc a, HasLoc b) => HasLoc (FamEqn s a b) where - loc (FamEqn _ a Nothing b _ c) = foldl1' combineSrcSpans [loc a, loc b, loc c] - loc (FamEqn _ a (Just tvs) b _ c) = foldl1' combineSrcSpans - [loc a, loc tvs, loc b, loc c] - loc _ = noSrcSpan -instance (HasLoc tm, HasLoc ty) => HasLoc (HsArg tm ty) where - loc (HsValArg tm) = loc tm - loc (HsTypeArg _ ty) = loc ty - loc (HsArgPar sp) = sp - -instance HasLoc (HsDataDefn GhcRn) where - loc def@(HsDataDefn{}) = loc $ dd_cons def - -- Only used for data family instances, so we only need rhs - -- Most probably the rest will be unhelpful anyway - loc _ = noSrcSpan - -instance HasLoc (Pat (GhcPass a)) where - loc (dL -> L l _) = l - --- | The main worker class -class ToHie a where - toHie :: a -> HieM [HieAST Type] - --- | Used to collect type info -class Data a => HasType a where - getTypeNode :: a -> HieM [HieAST Type] - -instance (ToHie a) => ToHie [a] where - toHie = concatMapM toHie - -instance (ToHie a) => ToHie (Bag a) where - toHie = toHie . bagToList - -instance (ToHie a) => ToHie (Maybe a) where - toHie = maybe (pure []) toHie - -instance ToHie (Context (Located NoExt)) where - toHie _ = pure [] - -instance ToHie (TScoped NoExt) where - toHie _ = pure [] - -instance ToHie (IEContext (Located ModuleName)) where - toHie (IEC c (L (RealSrcSpan span) mname)) = - pure $ [Node (NodeInfo S.empty [] idents) span []] - where details = mempty{identInfo = S.singleton (IEThing c)} - idents = M.singleton (Left mname) details - toHie _ = pure [] - -instance ToHie (Context (Located Var)) where - toHie c = case c of - C context (L (RealSrcSpan span) name') - -> do - m <- asks name_remapping - let name = M.findWithDefault name' (varName name') m - pure - [Node - (NodeInfo S.empty [] $ - M.singleton (Right $ varName name) - (IdentifierDetails (Just $ varType name') - (S.singleton context))) - span - []] - _ -> pure [] - -instance ToHie (Context (Located Name)) where - toHie c = case c of - C context (L (RealSrcSpan span) name') -> do - m <- asks name_remapping - let name = case M.lookup name' m of - Just var -> varName var - Nothing -> name' - pure - [Node - (NodeInfo S.empty [] $ - M.singleton (Right name) - (IdentifierDetails Nothing - (S.singleton context))) - span - []] - _ -> pure [] - --- | Dummy instances - never called -instance ToHie (TScoped (LHsSigWcType GhcTc)) where - toHie _ = pure [] -instance ToHie (TScoped (LHsWcType GhcTc)) where - toHie _ = pure [] -instance ToHie (SigContext (LSig GhcTc)) where - toHie _ = pure [] -instance ToHie (TScoped Type) where - toHie _ = pure [] - -instance HasType (LHsBind GhcRn) where - getTypeNode (L spn bind) = makeNode bind spn - -instance HasType (LHsBind GhcTc) where - getTypeNode (L spn bind) = case bind of - FunBind{fun_id = name} -> makeTypeNode bind spn (varType $ unLoc name) - _ -> makeNode bind spn - -instance HasType (LPat GhcRn) where - getTypeNode (dL -> L spn pat) = makeNode pat spn - -instance HasType (LPat GhcTc) where - getTypeNode (dL -> L spn opat) = makeTypeNode opat spn (hsPatType opat) - -instance HasType (LHsExpr GhcRn) where - getTypeNode (L spn e) = makeNode e spn - --- | This instance tries to construct 'HieAST' nodes which include the type of --- the expression. It is not yet possible to do this efficiently for all --- expression forms, so we skip filling in the type for those inputs. --- --- 'HsApp', for example, doesn't have any type information available directly on --- the node. Our next recourse would be to desugar it into a 'CoreExpr' then --- query the type of that. Yet both the desugaring call and the type query both --- involve recursive calls to the function and argument! This is particularly --- problematic when you realize that the HIE traversal will eventually visit --- those nodes too and ask for their types again. --- --- Since the above is quite costly, we just skip cases where computing the --- expression's type is going to be expensive. --- --- See #16233 -instance HasType (LHsExpr GhcTc) where - getTypeNode e@(L spn e') = lift $ - -- Some expression forms have their type immediately available - let tyOpt = case e' of - HsLit _ l -> Just (hsLitType l) - HsOverLit _ o -> Just (overLitType o) - - HsLam _ (MG { mg_ext = groupTy }) -> Just (matchGroupType groupTy) - HsLamCase _ (MG { mg_ext = groupTy }) -> Just (matchGroupType groupTy) - HsCase _ _ (MG { mg_ext = groupTy }) -> Just (mg_res_ty groupTy) - - ExplicitList ty _ _ -> Just (mkListTy ty) - ExplicitSum ty _ _ _ -> Just (mkSumTy ty) - HsDo ty _ _ -> Just ty - HsMultiIf ty _ -> Just ty - - _ -> Nothing - - in - case tyOpt of - _ | skipDesugaring e' -> fallback - | otherwise -> do - hs_env <- Hsc $ \e w -> return (e,w) - (_,mbe) <- liftIO $ deSugarExpr hs_env e - maybe fallback (makeTypeNode e' spn . exprType) mbe - where - fallback = makeNode e' spn - - matchGroupType :: MatchGroupTc -> Type - matchGroupType (MatchGroupTc args res) = mkFunTys args res - - -- | Skip desugaring of these expressions for performance reasons. - -- - -- See impact on Haddock output (esp. missing type annotations or links) - -- before marking more things here as 'False'. See impact on Haddock - -- performance before marking more things as 'True'. - skipDesugaring :: HsExpr a -> Bool - skipDesugaring e = case e of - HsVar{} -> False - HsUnboundVar{} -> False - HsConLikeOut{} -> False - HsRecFld{} -> False - HsOverLabel{} -> False - HsIPVar{} -> False - HsWrap{} -> False - _ -> True - -instance ( ToHie (Context (Located (IdP a))) - , ToHie (MatchGroup a (LHsExpr a)) - , ToHie (PScoped (LPat a)) - , ToHie (GRHSs a (LHsExpr a)) - , ToHie (LHsExpr a) - , ToHie (Located (PatSynBind a a)) - , HasType (LHsBind a) - , ModifyState (IdP a) - , Data (HsBind a) - ) => ToHie (BindContext (LHsBind a)) where - toHie (BC context scope b@(L span bind)) = - concatM $ getTypeNode b : case bind of - FunBind{fun_id = name, fun_matches = matches} -> - [ toHie $ C (ValBind context scope $ getRealSpan span) name - , toHie matches - ] - PatBind{pat_lhs = lhs, pat_rhs = rhs} -> - [ toHie $ PS (getRealSpan span) scope NoScope lhs - , toHie rhs - ] - VarBind{var_rhs = expr} -> - [ toHie expr - ] - AbsBinds{abs_exports = xs, abs_binds = binds} -> - [ local (modifyState xs) $ -- Note [Name Remapping] - toHie $ fmap (BC context scope) binds - ] - PatSynBind _ psb -> - [ toHie $ L span psb -- PatSynBinds only occur at the top level - ] - XHsBindsLR _ -> [] - -instance ( ToHie (LMatch a body) - ) => ToHie (MatchGroup a body) where - toHie mg = concatM $ case mg of - MG{ mg_alts = (L span alts) , mg_origin = FromSource } -> - [ pure $ locOnly span - , toHie alts - ] - MG{} -> [] - XMatchGroup _ -> [] - -instance ( ToHie (Context (Located (IdP a))) - , ToHie (PScoped (LPat a)) - , ToHie (HsPatSynDir a) - ) => ToHie (Located (PatSynBind a a)) where - toHie (L sp psb) = concatM $ case psb of - PSB{psb_id=var, psb_args=dets, psb_def=pat, psb_dir=dir} -> - [ toHie $ C (Decl PatSynDec $ getRealSpan sp) var - , toHie $ toBind dets - , toHie $ PS Nothing lhsScope NoScope pat - , toHie dir - ] - where - lhsScope = combineScopes varScope detScope - varScope = mkLScope var - detScope = case dets of - (PrefixCon args) -> foldr combineScopes NoScope $ map mkLScope args - (InfixCon a b) -> combineScopes (mkLScope a) (mkLScope b) - (RecCon r) -> foldr go NoScope r - go (RecordPatSynField a b) c = combineScopes c - $ combineScopes (mkLScope a) (mkLScope b) - detSpan = case detScope of - LocalScope a -> Just a - _ -> Nothing - toBind (PrefixCon args) = PrefixCon $ map (C Use) args - toBind (InfixCon a b) = InfixCon (C Use a) (C Use b) - toBind (RecCon r) = RecCon $ map (PSC detSpan) r - XPatSynBind _ -> [] - -instance ( ToHie (MatchGroup a (LHsExpr a)) - ) => ToHie (HsPatSynDir a) where - toHie dir = case dir of - ExplicitBidirectional mg -> toHie mg - _ -> pure [] - -instance ( a ~ GhcPass p - , ToHie body - , ToHie (HsMatchContext (NameOrRdrName (IdP a))) - , ToHie (PScoped (LPat a)) - , ToHie (GRHSs a body) - , Data (Match a body) - ) => ToHie (LMatch (GhcPass p) body) where - toHie (L span m ) = concatM $ makeNode m span : case m of - Match{m_ctxt=mctx, m_pats = pats, m_grhss = grhss } -> - [ toHie mctx - , let rhsScope = mkScope $ grhss_span grhss - in toHie $ patScopes Nothing rhsScope NoScope pats - , toHie grhss - ] - XMatch _ -> [] - -instance ( ToHie (Context (Located a)) - ) => ToHie (HsMatchContext a) where - toHie (FunRhs{mc_fun=name}) = toHie $ C MatchBind name - toHie (StmtCtxt a) = toHie a - toHie _ = pure [] - -instance ( ToHie (HsMatchContext a) - ) => ToHie (HsStmtContext a) where - toHie (PatGuard a) = toHie a - toHie (ParStmtCtxt a) = toHie a - toHie (TransStmtCtxt a) = toHie a - toHie _ = pure [] - -instance ( a ~ GhcPass p - , ToHie (Context (Located (IdP a))) - , ToHie (RContext (HsRecFields a (PScoped (LPat a)))) - , ToHie (LHsExpr a) - , ToHie (TScoped (LHsSigWcType a)) - , ProtectSig a - , ToHie (TScoped (ProtectedSig a)) - , HasType (LPat a) - , Data (HsSplice a) - ) => ToHie (PScoped (LPat (GhcPass p))) where - toHie (PS rsp scope pscope lpat@(dL -> L ospan opat)) = - concatM $ getTypeNode lpat : case opat of - WildPat _ -> - [] - VarPat _ lname -> - [ toHie $ C (PatternBind scope pscope rsp) lname - ] - LazyPat _ p -> - [ toHie $ PS rsp scope pscope p - ] - AsPat _ lname pat -> - [ toHie $ C (PatternBind scope - (combineScopes (mkLScope (dL pat)) pscope) - rsp) - lname - , toHie $ PS rsp scope pscope pat - ] - ParPat _ pat -> - [ toHie $ PS rsp scope pscope pat - ] - BangPat _ pat -> - [ toHie $ PS rsp scope pscope pat - ] - ListPat _ pats -> - [ toHie $ patScopes rsp scope pscope pats - ] - TuplePat _ pats _ -> - [ toHie $ patScopes rsp scope pscope pats - ] - SumPat _ pat _ _ -> - [ toHie $ PS rsp scope pscope pat - ] - ConPatIn c dets -> - [ toHie $ C Use c - , toHie $ contextify dets - ] - ConPatOut {pat_con = con, pat_args = dets}-> - [ toHie $ C Use $ fmap conLikeName con - , toHie $ contextify dets - ] - ViewPat _ expr pat -> - [ toHie expr - , toHie $ PS rsp scope pscope pat - ] - SplicePat _ sp -> - [ toHie $ L ospan sp - ] - LitPat _ _ -> - [] - NPat _ _ _ _ -> - [] - NPlusKPat _ n _ _ _ _ -> - [ toHie $ C (PatternBind scope pscope rsp) n - ] - SigPat _ pat sig -> - [ toHie $ PS rsp scope pscope pat - , let cscope = mkLScope (dL pat) in - toHie $ TS (ResolvedScopes [cscope, scope, pscope]) - (protectSig @a cscope sig) - -- See Note [Scoping Rules for SigPat] - ] - CoPat _ _ _ _ -> - [] - XPat _ -> [] - where - contextify (PrefixCon args) = PrefixCon $ patScopes rsp scope pscope args - contextify (InfixCon a b) = InfixCon a' b' - where [a', b'] = patScopes rsp scope pscope [a,b] - contextify (RecCon r) = RecCon $ RC RecFieldMatch $ contextify_rec r - contextify_rec (HsRecFields fds a) = HsRecFields (map go scoped_fds) a - where - go (RS fscope (L spn (HsRecField lbl pat pun))) = - L spn $ HsRecField lbl (PS rsp scope fscope pat) pun - scoped_fds = listScopes pscope fds - -instance ( ToHie body - , ToHie (LGRHS a body) - , ToHie (RScoped (LHsLocalBinds a)) - ) => ToHie (GRHSs a body) where - toHie grhs = concatM $ case grhs of - GRHSs _ grhss binds -> - [ toHie grhss - , toHie $ RS (mkScope $ grhss_span grhs) binds - ] - XGRHSs _ -> [] - -instance ( ToHie (Located body) - , ToHie (RScoped (GuardLStmt a)) - , Data (GRHS a (Located body)) - ) => ToHie (LGRHS a (Located body)) where - toHie (L span g) = concatM $ makeNode g span : case g of - GRHS _ guards body -> - [ toHie $ listScopes (mkLScope body) guards - , toHie body - ] - XGRHS _ -> [] - -instance ( a ~ GhcPass p - , ToHie (Context (Located (IdP a))) - , HasType (LHsExpr a) - , ToHie (PScoped (LPat a)) - , ToHie (MatchGroup a (LHsExpr a)) - , ToHie (LGRHS a (LHsExpr a)) - , ToHie (RContext (HsRecordBinds a)) - , ToHie (RFContext (Located (AmbiguousFieldOcc a))) - , ToHie (ArithSeqInfo a) - , ToHie (LHsCmdTop a) - , ToHie (RScoped (GuardLStmt a)) - , ToHie (RScoped (LHsLocalBinds a)) - , ToHie (TScoped (LHsWcType (NoGhcTc a))) - , ToHie (TScoped (LHsSigWcType (NoGhcTc a))) - , Data (HsExpr a) - , Data (HsSplice a) - , Data (HsTupArg a) - , Data (AmbiguousFieldOcc a) - ) => ToHie (LHsExpr (GhcPass p)) where - toHie e@(L mspan oexpr) = concatM $ getTypeNode e : case oexpr of - HsVar _ (L _ var) -> - [ toHie $ C Use (L mspan var) - -- Patch up var location since typechecker removes it - ] - HsUnboundVar _ _ -> - [] - HsConLikeOut _ con -> - [ toHie $ C Use $ L mspan $ conLikeName con - ] - HsRecFld _ fld -> - [ toHie $ RFC RecFieldOcc Nothing (L mspan fld) - ] - HsOverLabel _ _ _ -> [] - HsIPVar _ _ -> [] - HsOverLit _ _ -> [] - HsLit _ _ -> [] - HsLam _ mg -> - [ toHie mg - ] - HsLamCase _ mg -> - [ toHie mg - ] - HsApp _ a b -> - [ toHie a - , toHie b - ] - HsAppType _ expr sig -> - [ toHie expr - , toHie $ TS (ResolvedScopes []) sig - ] - OpApp _ a b c -> - [ toHie a - , toHie b - , toHie c - ] - NegApp _ a _ -> - [ toHie a - ] - HsPar _ a -> - [ toHie a - ] - SectionL _ a b -> - [ toHie a - , toHie b - ] - SectionR _ a b -> - [ toHie a - , toHie b - ] - ExplicitTuple _ args _ -> - [ toHie args - ] - ExplicitSum _ _ _ expr -> - [ toHie expr - ] - HsCase _ expr matches -> - [ toHie expr - , toHie matches - ] - HsIf _ _ a b c -> - [ toHie a - , toHie b - , toHie c - ] - HsMultiIf _ grhss -> - [ toHie grhss - ] - HsLet _ binds expr -> - [ toHie $ RS (mkLScope expr) binds - , toHie expr - ] - HsDo _ _ (L ispan stmts) -> - [ pure $ locOnly ispan - , toHie $ listScopes NoScope stmts - ] - ExplicitList _ _ exprs -> - [ toHie exprs - ] - RecordCon {rcon_con_name = name, rcon_flds = binds}-> - [ toHie $ C Use name - , toHie $ RC RecFieldAssign $ binds - ] - RecordUpd {rupd_expr = expr, rupd_flds = upds}-> - [ toHie expr - , toHie $ map (RC RecFieldAssign) upds - ] - ExprWithTySig _ expr sig -> - [ toHie expr - , toHie $ TS (ResolvedScopes [mkLScope expr]) sig - ] - ArithSeq _ _ info -> - [ toHie info - ] - HsSCC _ _ _ expr -> - [ toHie expr - ] - HsCoreAnn _ _ _ expr -> - [ toHie expr - ] - HsProc _ pat cmdtop -> - [ toHie $ PS Nothing (mkLScope cmdtop) NoScope pat - , toHie cmdtop - ] - HsStatic _ expr -> - [ toHie expr - ] - HsArrApp _ a b _ _ -> - [ toHie a - , toHie b - ] - HsArrForm _ expr _ cmds -> - [ toHie expr - , toHie cmds - ] - HsTick _ _ expr -> - [ toHie expr - ] - HsBinTick _ _ _ expr -> - [ toHie expr - ] - HsTickPragma _ _ _ _ expr -> - [ toHie expr - ] - HsWrap _ _ a -> - [ toHie $ L mspan a - ] - HsBracket _ b -> - [ toHie b - ] - HsRnBracketOut _ b p -> - [ toHie b - , toHie p - ] - HsTcBracketOut _ b p -> - [ toHie b - , toHie p - ] - HsSpliceE _ x -> - [ toHie $ L mspan x - ] - EWildPat _ -> [] - EAsPat _ a b -> - [ toHie $ C Use a - , toHie b - ] - EViewPat _ a b -> - [ toHie a - , toHie b - ] - ELazyPat _ a -> - [ toHie a - ] - XExpr _ -> [] - -instance ( a ~ GhcPass p - , ToHie (LHsExpr a) - , Data (HsTupArg a) - ) => ToHie (LHsTupArg (GhcPass p)) where - toHie (L span arg) = concatM $ makeNode arg span : case arg of - Present _ expr -> - [ toHie expr - ] - Missing _ -> [] - XTupArg _ -> [] - -instance ( a ~ GhcPass p - , ToHie (PScoped (LPat a)) - , ToHie (LHsExpr a) - , ToHie (SigContext (LSig a)) - , ToHie (RScoped (LHsLocalBinds a)) - , ToHie (RScoped (ApplicativeArg a)) - , ToHie (Located body) - , Data (StmtLR a a (Located body)) - , Data (StmtLR a a (Located (HsExpr a))) - ) => ToHie (RScoped (LStmt (GhcPass p) (Located body))) where - toHie (RS scope (L span stmt)) = concatM $ makeNode stmt span : case stmt of - LastStmt _ body _ _ -> - [ toHie body - ] - BindStmt _ pat body _ _ -> - [ toHie $ PS (getRealSpan $ getLoc body) scope NoScope pat - , toHie body - ] - ApplicativeStmt _ stmts _ -> - [ concatMapM (toHie . RS scope . snd) stmts - ] - BodyStmt _ body _ _ -> - [ toHie body - ] - LetStmt _ binds -> - [ toHie $ RS scope binds - ] - ParStmt _ parstmts _ _ -> - [ concatMapM (\(ParStmtBlock _ stmts _ _) -> - toHie $ listScopes NoScope stmts) - parstmts - ] - TransStmt {trS_stmts = stmts, trS_using = using, trS_by = by} -> - [ toHie $ listScopes scope stmts - , toHie using - , toHie by - ] - RecStmt {recS_stmts = stmts} -> - [ toHie $ map (RS $ combineScopes scope (mkScope span)) stmts - ] - XStmtLR _ -> [] - -instance ( ToHie (LHsExpr a) - , ToHie (PScoped (LPat a)) - , ToHie (BindContext (LHsBind a)) - , ToHie (SigContext (LSig a)) - , ToHie (RScoped (HsValBindsLR a a)) - , Data (HsLocalBinds a) - ) => ToHie (RScoped (LHsLocalBinds a)) where - toHie (RS scope (L sp binds)) = concatM $ makeNode binds sp : case binds of - EmptyLocalBinds _ -> [] - HsIPBinds _ _ -> [] - HsValBinds _ valBinds -> - [ toHie $ RS (combineScopes scope $ mkScope sp) - valBinds - ] - XHsLocalBindsLR _ -> [] - -instance ( ToHie (BindContext (LHsBind a)) - , ToHie (SigContext (LSig a)) - , ToHie (RScoped (XXValBindsLR a a)) - ) => ToHie (RScoped (HsValBindsLR a a)) where - toHie (RS sc v) = concatM $ case v of - ValBinds _ binds sigs -> - [ toHie $ fmap (BC RegularBind sc) binds - , toHie $ fmap (SC (SI BindSig Nothing)) sigs - ] - XValBindsLR x -> [ toHie $ RS sc x ] - -instance ToHie (RScoped (NHsValBindsLR GhcTc)) where - toHie (RS sc (NValBinds binds sigs)) = concatM $ - [ toHie (concatMap (map (BC RegularBind sc) . bagToList . snd) binds) - , toHie $ fmap (SC (SI BindSig Nothing)) sigs - ] -instance ToHie (RScoped (NHsValBindsLR GhcRn)) where - toHie (RS sc (NValBinds binds sigs)) = concatM $ - [ toHie (concatMap (map (BC RegularBind sc) . bagToList . snd) binds) - , toHie $ fmap (SC (SI BindSig Nothing)) sigs - ] - -instance ( ToHie (RContext (LHsRecField a arg)) - ) => ToHie (RContext (HsRecFields a arg)) where - toHie (RC c (HsRecFields fields _)) = toHie $ map (RC c) fields - -instance ( ToHie (RFContext (Located label)) - , ToHie arg - , HasLoc arg - , Data label - , Data arg - ) => ToHie (RContext (LHsRecField' label arg)) where - toHie (RC c (L span recfld)) = concatM $ makeNode recfld span : case recfld of - HsRecField label expr _ -> - [ toHie $ RFC c (getRealSpan $ loc expr) label - , toHie expr - ] - -instance ToHie (RFContext (LFieldOcc GhcRn)) where - toHie (RFC c rhs (L nspan f)) = concatM $ case f of - FieldOcc name _ -> - [ toHie $ C (RecField c rhs) (L nspan name) - ] - XFieldOcc _ -> [] - -instance ToHie (RFContext (LFieldOcc GhcTc)) where - toHie (RFC c rhs (L nspan f)) = concatM $ case f of - FieldOcc var _ -> - let var' = setVarName var (varName var) - in [ toHie $ C (RecField c rhs) (L nspan var') - ] - XFieldOcc _ -> [] - -instance ToHie (RFContext (Located (AmbiguousFieldOcc GhcRn))) where - toHie (RFC c rhs (L nspan afo)) = concatM $ case afo of - Unambiguous name _ -> - [ toHie $ C (RecField c rhs) $ L nspan name - ] - Ambiguous _name _ -> - [ ] - XAmbiguousFieldOcc _ -> [] - -instance ToHie (RFContext (Located (AmbiguousFieldOcc GhcTc))) where - toHie (RFC c rhs (L nspan afo)) = concatM $ case afo of - Unambiguous var _ -> - let var' = setVarName var (varName var) - in [ toHie $ C (RecField c rhs) (L nspan var') - ] - Ambiguous var _ -> - let var' = setVarName var (varName var) - in [ toHie $ C (RecField c rhs) (L nspan var') - ] - XAmbiguousFieldOcc _ -> [] - -instance ( a ~ GhcPass p - , ToHie (PScoped (LPat a)) - , ToHie (BindContext (LHsBind a)) - , ToHie (LHsExpr a) - , ToHie (SigContext (LSig a)) - , ToHie (RScoped (HsValBindsLR a a)) - , Data (StmtLR a a (Located (HsExpr a))) - , Data (HsLocalBinds a) - ) => ToHie (RScoped (ApplicativeArg (GhcPass p))) where - toHie (RS sc (ApplicativeArgOne _ pat expr _)) = concatM - [ toHie $ PS Nothing sc NoScope pat - , toHie expr - ] - toHie (RS sc (ApplicativeArgMany _ stmts _ pat)) = concatM - [ toHie $ listScopes NoScope stmts - , toHie $ PS Nothing sc NoScope pat - ] - toHie (RS _ (XApplicativeArg _)) = pure [] - -instance (ToHie arg, ToHie rec) => ToHie (HsConDetails arg rec) where - toHie (PrefixCon args) = toHie args - toHie (RecCon rec) = toHie rec - toHie (InfixCon a b) = concatM [ toHie a, toHie b] - -instance ( ToHie (LHsCmd a) - , Data (HsCmdTop a) - ) => ToHie (LHsCmdTop a) where - toHie (L span top) = concatM $ makeNode top span : case top of - HsCmdTop _ cmd -> - [ toHie cmd - ] - XCmdTop _ -> [] - -instance ( a ~ GhcPass p - , ToHie (PScoped (LPat a)) - , ToHie (BindContext (LHsBind a)) - , ToHie (LHsExpr a) - , ToHie (MatchGroup a (LHsCmd a)) - , ToHie (SigContext (LSig a)) - , ToHie (RScoped (HsValBindsLR a a)) - , Data (HsCmd a) - , Data (HsCmdTop a) - , Data (StmtLR a a (Located (HsCmd a))) - , Data (HsLocalBinds a) - , Data (StmtLR a a (Located (HsExpr a))) - ) => ToHie (LHsCmd (GhcPass p)) where - toHie (L span cmd) = concatM $ makeNode cmd span : case cmd of - HsCmdArrApp _ a b _ _ -> - [ toHie a - , toHie b - ] - HsCmdArrForm _ a _ _ cmdtops -> - [ toHie a - , toHie cmdtops - ] - HsCmdApp _ a b -> - [ toHie a - , toHie b - ] - HsCmdLam _ mg -> - [ toHie mg - ] - HsCmdPar _ a -> - [ toHie a - ] - HsCmdCase _ expr alts -> - [ toHie expr - , toHie alts - ] - HsCmdIf _ _ a b c -> - [ toHie a - , toHie b - , toHie c - ] - HsCmdLet _ binds cmd' -> - [ toHie $ RS (mkLScope cmd') binds - , toHie cmd' - ] - HsCmdDo _ (L ispan stmts) -> - [ pure $ locOnly ispan - , toHie $ listScopes NoScope stmts - ] - HsCmdWrap _ _ _ -> [] - XCmd _ -> [] - -instance ToHie (TyClGroup GhcRn) where - toHie (TyClGroup _ classes roles instances) = concatM - [ toHie classes - , toHie roles - , toHie instances - ] - toHie (XTyClGroup _) = pure [] - -instance ToHie (LTyClDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - FamDecl {tcdFam = fdecl} -> - [ toHie (L span fdecl) - ] - SynDecl {tcdLName = name, tcdTyVars = vars, tcdRhs = typ} -> - [ toHie $ C (Decl SynDec $ getRealSpan span) name - , toHie $ TS (ResolvedScopes [mkScope $ getLoc typ]) vars - , toHie typ - ] - DataDecl {tcdLName = name, tcdTyVars = vars, tcdDataDefn = defn} -> - [ toHie $ C (Decl DataDec $ getRealSpan span) name - , toHie $ TS (ResolvedScopes [quant_scope, rhs_scope]) vars - , toHie defn - ] - where - quant_scope = mkLScope $ dd_ctxt defn - rhs_scope = sig_sc `combineScopes` con_sc `combineScopes` deriv_sc - sig_sc = maybe NoScope mkLScope $ dd_kindSig defn - con_sc = foldr combineScopes NoScope $ map mkLScope $ dd_cons defn - deriv_sc = mkLScope $ dd_derivs defn - ClassDecl { tcdCtxt = context - , tcdLName = name - , tcdTyVars = vars - , tcdFDs = deps - , tcdSigs = sigs - , tcdMeths = meths - , tcdATs = typs - , tcdATDefs = deftyps - } -> - [ toHie $ C (Decl ClassDec $ getRealSpan span) name - , toHie context - , toHie $ TS (ResolvedScopes [context_scope, rhs_scope]) vars - , toHie deps - , toHie $ map (SC $ SI ClassSig $ getRealSpan span) sigs - , toHie $ fmap (BC InstanceBind ModuleScope) meths - , toHie typs - , concatMapM (pure . locOnly . getLoc) deftyps - , toHie $ map (go . unLoc) deftyps - ] - where - context_scope = mkLScope context - rhs_scope = foldl1' combineScopes $ map mkScope - [ loc deps, loc sigs, loc (bagToList meths), loc typs, loc deftyps] - - go :: TyFamDefltEqn GhcRn - -> FamEqn GhcRn (TScoped (LHsQTyVars GhcRn)) (LHsType GhcRn) - go (FamEqn a var bndrs pat b rhs) = - FamEqn a var bndrs (TS (ResolvedScopes [mkLScope rhs]) pat) b rhs - go (XFamEqn NoExt) = XFamEqn NoExt - XTyClDecl _ -> [] - -instance ToHie (LFamilyDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - FamilyDecl _ info name vars _ sig inj -> - [ toHie $ C (Decl FamDec $ getRealSpan span) name - , toHie $ TS (ResolvedScopes [rhsSpan]) vars - , toHie info - , toHie $ RS injSpan sig - , toHie inj - ] - where - rhsSpan = sigSpan `combineScopes` injSpan - sigSpan = mkScope $ getLoc sig - injSpan = maybe NoScope (mkScope . getLoc) inj - XFamilyDecl _ -> [] - -instance ToHie (FamilyInfo GhcRn) where - toHie (ClosedTypeFamily (Just eqns)) = concatM $ - [ concatMapM (pure . locOnly . getLoc) eqns - , toHie $ map go eqns - ] - where - go (L l ib) = TS (ResolvedScopes [mkScope l]) ib - toHie _ = pure [] - -instance ToHie (RScoped (LFamilyResultSig GhcRn)) where - toHie (RS sc (L span sig)) = concatM $ makeNode sig span : case sig of - NoSig _ -> - [] - KindSig _ k -> - [ toHie k - ] - TyVarSig _ bndr -> - [ toHie $ TVS (ResolvedScopes [sc]) NoScope bndr - ] - XFamilyResultSig _ -> [] - -instance ToHie (Located (FunDep (Located Name))) where - toHie (L span fd@(lhs, rhs)) = concatM $ - [ makeNode fd span - , toHie $ map (C Use) lhs - , toHie $ map (C Use) rhs - ] - -instance (ToHie pats, ToHie rhs, HasLoc pats, HasLoc rhs) - => ToHie (TScoped (FamEqn GhcRn pats rhs)) where - toHie (TS _ f) = toHie f - -instance ( ToHie pats - , ToHie rhs - , HasLoc pats - , HasLoc rhs - ) => ToHie (FamEqn GhcRn pats rhs) where - toHie fe@(FamEqn _ var tybndrs pats _ rhs) = concatM $ - [ toHie $ C (Decl InstDec $ getRealSpan $ loc fe) var - , toHie $ fmap (tvScopes (ResolvedScopes []) scope) tybndrs - , toHie pats - , toHie rhs - ] - where scope = combineScopes patsScope rhsScope - patsScope = mkScope (loc pats) - rhsScope = mkScope (loc rhs) - toHie (XFamEqn _) = pure [] - -instance ToHie (LInjectivityAnn GhcRn) where - toHie (L span ann) = concatM $ makeNode ann span : case ann of - InjectivityAnn lhs rhs -> - [ toHie $ C Use lhs - , toHie $ map (C Use) rhs - ] - -instance ToHie (HsDataDefn GhcRn) where - toHie (HsDataDefn _ _ ctx _ mkind cons derivs) = concatM - [ toHie ctx - , toHie mkind - , toHie cons - , toHie derivs - ] - toHie (XHsDataDefn _) = pure [] - -instance ToHie (HsDeriving GhcRn) where - toHie (L span clauses) = concatM - [ pure $ locOnly span - , toHie clauses - ] - -instance ToHie (LHsDerivingClause GhcRn) where - toHie (L span cl) = concatM $ makeNode cl span : case cl of - HsDerivingClause _ strat (L ispan tys) -> - [ toHie strat - , pure $ locOnly ispan - , toHie $ map (TS (ResolvedScopes [])) tys - ] - XHsDerivingClause _ -> [] - -instance ToHie (Located (DerivStrategy GhcRn)) where - toHie (L span strat) = concatM $ makeNode strat span : case strat of - StockStrategy -> [] - AnyclassStrategy -> [] - NewtypeStrategy -> [] - ViaStrategy s -> [ toHie $ TS (ResolvedScopes []) s ] - -instance ToHie (Located OverlapMode) where - toHie (L span _) = pure $ locOnly span - -instance ToHie (LConDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - ConDeclGADT { con_names = names, con_qvars = qvars - , con_mb_cxt = ctx, con_args = args, con_res_ty = typ } -> - [ toHie $ map (C (Decl ConDec $ getRealSpan span)) names - , toHie $ TS (ResolvedScopes [ctxScope, rhsScope]) qvars - , toHie ctx - , toHie args - , toHie typ - ] - where - rhsScope = combineScopes argsScope tyScope - ctxScope = maybe NoScope mkLScope ctx - argsScope = condecl_scope args - tyScope = mkLScope typ - ConDeclH98 { con_name = name, con_ex_tvs = qvars - , con_mb_cxt = ctx, con_args = dets } -> - [ toHie $ C (Decl ConDec $ getRealSpan span) name - , toHie $ tvScopes (ResolvedScopes []) rhsScope qvars - , toHie ctx - , toHie dets - ] - where - rhsScope = combineScopes ctxScope argsScope - ctxScope = maybe NoScope mkLScope ctx - argsScope = condecl_scope dets - XConDecl _ -> [] - where condecl_scope args = case args of - PrefixCon xs -> foldr combineScopes NoScope $ map mkLScope xs - InfixCon a b -> combineScopes (mkLScope a) (mkLScope b) - RecCon x -> mkLScope x - -instance ToHie (Located [LConDeclField GhcRn]) where - toHie (L span decls) = concatM $ - [ pure $ locOnly span - , toHie decls - ] - -instance ( HasLoc thing - , ToHie (TScoped thing) - ) => ToHie (TScoped (HsImplicitBndrs GhcRn thing)) where - toHie (TS sc (HsIB ibrn a)) = concatM $ - [ pure $ bindingsOnly $ map (C $ TyVarBind (mkScope span) sc) ibrn - , toHie $ TS sc a - ] - where span = loc a - toHie (TS _ (XHsImplicitBndrs _)) = pure [] - -instance ( HasLoc thing - , ToHie (TScoped thing) - ) => ToHie (TScoped (HsWildCardBndrs GhcRn thing)) where - toHie (TS sc (HsWC names a)) = concatM $ - [ pure $ bindingsOnly $ map (C $ TyVarBind (mkScope span) sc) names - , toHie $ TS sc a - ] - where span = loc a - toHie (TS _ (XHsWildCardBndrs _)) = pure [] - -instance ToHie (SigContext (LSig GhcRn)) where - toHie (SC (SI styp msp) (L sp sig)) = concatM $ makeNode sig sp : case sig of - TypeSig _ names typ -> - [ toHie $ map (C TyDecl) names - , toHie $ TS (UnresolvedScope (map unLoc names) Nothing) typ - ] - PatSynSig _ names typ -> - [ toHie $ map (C TyDecl) names - , toHie $ TS (UnresolvedScope (map unLoc names) Nothing) typ - ] - ClassOpSig _ _ names typ -> - [ case styp of - ClassSig -> toHie $ map (C $ ClassTyDecl $ getRealSpan sp) names - _ -> toHie $ map (C $ TyDecl) names - , toHie $ TS (UnresolvedScope (map unLoc names) msp) typ - ] - IdSig _ _ -> [] - FixSig _ fsig -> - [ toHie $ L sp fsig - ] - InlineSig _ name _ -> - [ toHie $ (C Use) name - ] - SpecSig _ name typs _ -> - [ toHie $ (C Use) name - , toHie $ map (TS (ResolvedScopes [])) typs - ] - SpecInstSig _ _ typ -> - [ toHie $ TS (ResolvedScopes []) typ - ] - MinimalSig _ _ form -> - [ toHie form - ] - SCCFunSig _ _ name mtxt -> - [ toHie $ (C Use) name - , pure $ maybe [] (locOnly . getLoc) mtxt - ] - CompleteMatchSig _ _ (L ispan names) typ -> - [ pure $ locOnly ispan - , toHie $ map (C Use) names - , toHie $ fmap (C Use) typ - ] - XSig _ -> [] - -instance ToHie (LHsType GhcRn) where - toHie x = toHie $ TS (ResolvedScopes []) x - -instance ToHie (TScoped (LHsType GhcRn)) where - toHie (TS tsc (L span t)) = concatM $ makeNode t span : case t of - HsForAllTy _ bndrs body -> - [ toHie $ tvScopes tsc (mkScope $ getLoc body) bndrs - , toHie body - ] - HsQualTy _ ctx body -> - [ toHie ctx - , toHie body - ] - HsTyVar _ _ var -> - [ toHie $ C Use var - ] - HsAppTy _ a b -> - [ toHie a - , toHie b - ] - HsAppKindTy _ ty ki -> - [ toHie ty - , toHie $ TS (ResolvedScopes []) ki - ] - HsFunTy _ a b -> - [ toHie a - , toHie b - ] - HsListTy _ a -> - [ toHie a - ] - HsTupleTy _ _ tys -> - [ toHie tys - ] - HsSumTy _ tys -> - [ toHie tys - ] - HsOpTy _ a op b -> - [ toHie a - , toHie $ C Use op - , toHie b - ] - HsParTy _ a -> - [ toHie a - ] - HsIParamTy _ ip ty -> - [ toHie ip - , toHie ty - ] - HsKindSig _ a b -> - [ toHie a - , toHie b - ] - HsSpliceTy _ a -> - [ toHie $ L span a - ] - HsDocTy _ a _ -> - [ toHie a - ] - HsBangTy _ _ ty -> - [ toHie ty - ] - HsRecTy _ fields -> - [ toHie fields - ] - HsExplicitListTy _ _ tys -> - [ toHie tys - ] - HsExplicitTupleTy _ tys -> - [ toHie tys - ] - HsTyLit _ _ -> [] - HsWildCardTy _ -> [] - HsStarTy _ _ -> [] - XHsType _ -> [] - -instance (ToHie tm, ToHie ty) => ToHie (HsArg tm ty) where - toHie (HsValArg tm) = toHie tm - toHie (HsTypeArg _ ty) = toHie ty - toHie (HsArgPar sp) = pure $ locOnly sp - -instance ToHie (TVScoped (LHsTyVarBndr GhcRn)) where - toHie (TVS tsc sc (L span bndr)) = concatM $ makeNode bndr span : case bndr of - UserTyVar _ var -> - [ toHie $ C (TyVarBind sc tsc) var - ] - KindedTyVar _ var kind -> - [ toHie $ C (TyVarBind sc tsc) var - , toHie kind - ] - XTyVarBndr _ -> [] - -instance ToHie (TScoped (LHsQTyVars GhcRn)) where - toHie (TS sc (HsQTvs (HsQTvsRn implicits _) vars)) = concatM $ - [ pure $ bindingsOnly bindings - , toHie $ tvScopes sc NoScope vars - ] - where - varLoc = loc vars - bindings = map (C $ TyVarBind (mkScope varLoc) sc) implicits - toHie (TS _ (XLHsQTyVars _)) = pure [] - -instance ToHie (LHsContext GhcRn) where - toHie (L span tys) = concatM $ - [ pure $ locOnly span - , toHie tys - ] - -instance ToHie (LConDeclField GhcRn) where - toHie (L span field) = concatM $ makeNode field span : case field of - ConDeclField _ fields typ _ -> - [ toHie $ map (RFC RecFieldDecl (getRealSpan $ loc typ)) fields - , toHie typ - ] - XConDeclField _ -> [] - -instance ToHie (LHsExpr a) => ToHie (ArithSeqInfo a) where - toHie (From expr) = toHie expr - toHie (FromThen a b) = concatM $ - [ toHie a - , toHie b - ] - toHie (FromTo a b) = concatM $ - [ toHie a - , toHie b - ] - toHie (FromThenTo a b c) = concatM $ - [ toHie a - , toHie b - , toHie c - ] - -instance ToHie (LSpliceDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - SpliceDecl _ splice _ -> - [ toHie splice - ] - XSpliceDecl _ -> [] - -instance ToHie (HsBracket a) where - toHie _ = pure [] - -instance ToHie PendingRnSplice where - toHie _ = pure [] - -instance ToHie PendingTcSplice where - toHie _ = pure [] - -instance ToHie (LBooleanFormula (Located Name)) where - toHie (L span form) = concatM $ makeNode form span : case form of - Var a -> - [ toHie $ C Use a - ] - And forms -> - [ toHie forms - ] - Or forms -> - [ toHie forms - ] - Parens f -> - [ toHie f - ] - -instance ToHie (Located HsIPName) where - toHie (L span e) = makeNode e span - -instance ( ToHie (LHsExpr a) - , Data (HsSplice a) - ) => ToHie (Located (HsSplice a)) where - toHie (L span sp) = concatM $ makeNode sp span : case sp of - HsTypedSplice _ _ _ expr -> - [ toHie expr - ] - HsUntypedSplice _ _ _ expr -> - [ toHie expr - ] - HsQuasiQuote _ _ _ ispan _ -> - [ pure $ locOnly ispan - ] - HsSpliced _ _ _ -> - [] - HsSplicedT _ -> - [] - XSplice _ -> [] - -instance ToHie (LRoleAnnotDecl GhcRn) where - toHie (L span annot) = concatM $ makeNode annot span : case annot of - RoleAnnotDecl _ var roles -> - [ toHie $ C Use var - , concatMapM (pure . locOnly . getLoc) roles - ] - XRoleAnnotDecl _ -> [] - -instance ToHie (LInstDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - ClsInstD _ d -> - [ toHie $ L span d - ] - DataFamInstD _ d -> - [ toHie $ L span d - ] - TyFamInstD _ d -> - [ toHie $ L span d - ] - XInstDecl _ -> [] - -instance ToHie (LClsInstDecl GhcRn) where - toHie (L span decl) = concatM - [ toHie $ TS (ResolvedScopes [mkScope span]) $ cid_poly_ty decl - , toHie $ fmap (BC InstanceBind ModuleScope) $ cid_binds decl - , toHie $ map (SC $ SI InstSig $ getRealSpan span) $ cid_sigs decl - , pure $ concatMap (locOnly . getLoc) $ cid_tyfam_insts decl - , toHie $ cid_tyfam_insts decl - , pure $ concatMap (locOnly . getLoc) $ cid_datafam_insts decl - , toHie $ cid_datafam_insts decl - , toHie $ cid_overlap_mode decl - ] - -instance ToHie (LDataFamInstDecl GhcRn) where - toHie (L sp (DataFamInstDecl d)) = toHie $ TS (ResolvedScopes [mkScope sp]) d - -instance ToHie (LTyFamInstDecl GhcRn) where - toHie (L sp (TyFamInstDecl d)) = toHie $ TS (ResolvedScopes [mkScope sp]) d - -instance ToHie (Context a) - => ToHie (PatSynFieldContext (RecordPatSynField a)) where - toHie (PSC sp (RecordPatSynField a b)) = concatM $ - [ toHie $ C (RecField RecFieldDecl sp) a - , toHie $ C Use b - ] - -instance ToHie (LDerivDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - DerivDecl _ typ strat overlap -> - [ toHie $ TS (ResolvedScopes []) typ - , toHie strat - , toHie overlap - ] - XDerivDecl _ -> [] - -instance ToHie (LFixitySig GhcRn) where - toHie (L span sig) = concatM $ makeNode sig span : case sig of - FixitySig _ vars _ -> - [ toHie $ map (C Use) vars - ] - XFixitySig _ -> [] - -instance ToHie (LDefaultDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - DefaultDecl _ typs -> - [ toHie typs - ] - XDefaultDecl _ -> [] - -instance ToHie (LForeignDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - ForeignImport {fd_name = name, fd_sig_ty = sig, fd_fi = fi} -> - [ toHie $ C (ValBind RegularBind ModuleScope $ getRealSpan span) name - , toHie $ TS (ResolvedScopes []) sig - , toHie fi - ] - ForeignExport {fd_name = name, fd_sig_ty = sig, fd_fe = fe} -> - [ toHie $ C Use name - , toHie $ TS (ResolvedScopes []) sig - , toHie fe - ] - XForeignDecl _ -> [] - -instance ToHie ForeignImport where - toHie (CImport (L a _) (L b _) _ _ (L c _)) = pure $ concat $ - [ locOnly a - , locOnly b - , locOnly c - ] - -instance ToHie ForeignExport where - toHie (CExport (L a _) (L b _)) = pure $ concat $ - [ locOnly a - , locOnly b - ] - -instance ToHie (LWarnDecls GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - Warnings _ _ warnings -> - [ toHie warnings - ] - XWarnDecls _ -> [] - -instance ToHie (LWarnDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - Warning _ vars _ -> - [ toHie $ map (C Use) vars - ] - XWarnDecl _ -> [] - -instance ToHie (LAnnDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - HsAnnotation _ _ prov expr -> - [ toHie prov - , toHie expr - ] - XAnnDecl _ -> [] - -instance ToHie (Context (Located a)) => ToHie (AnnProvenance a) where - toHie (ValueAnnProvenance a) = toHie $ C Use a - toHie (TypeAnnProvenance a) = toHie $ C Use a - toHie ModuleAnnProvenance = pure [] - -instance ToHie (LRuleDecls GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - HsRules _ _ rules -> - [ toHie rules - ] - XRuleDecls _ -> [] - -instance ToHie (LRuleDecl GhcRn) where - toHie (L _ (XRuleDecl _)) = pure [] - toHie (L span r@(HsRule _ rname _ tybndrs bndrs exprA exprB)) = concatM - [ makeNode r span - , pure $ locOnly $ getLoc rname - , toHie $ fmap (tvScopes (ResolvedScopes []) scope) tybndrs - , toHie $ map (RS $ mkScope span) bndrs - , toHie exprA - , toHie exprB - ] - where scope = bndrs_sc `combineScopes` exprA_sc `combineScopes` exprB_sc - bndrs_sc = maybe NoScope mkLScope (listToMaybe bndrs) - exprA_sc = mkLScope exprA - exprB_sc = mkLScope exprB - -instance ToHie (RScoped (LRuleBndr GhcRn)) where - toHie (RS sc (L span bndr)) = concatM $ makeNode bndr span : case bndr of - RuleBndr _ var -> - [ toHie $ C (ValBind RegularBind sc Nothing) var - ] - RuleBndrSig _ var typ -> - [ toHie $ C (ValBind RegularBind sc Nothing) var - , toHie $ TS (ResolvedScopes [sc]) typ - ] - XRuleBndr _ -> [] - -instance ToHie (LImportDecl GhcRn) where - toHie (L span decl) = concatM $ makeNode decl span : case decl of - ImportDecl { ideclName = name, ideclAs = as, ideclHiding = hidden } -> - [ toHie $ IEC Import name - , toHie $ fmap (IEC ImportAs) as - , maybe (pure []) goIE hidden - ] - XImportDecl _ -> [] - where - goIE (hiding, (L sp liens)) = concatM $ - [ pure $ locOnly sp - , toHie $ map (IEC c) liens - ] - where - c = if hiding then ImportHiding else Import - -instance ToHie (IEContext (LIE GhcRn)) where - toHie (IEC c (L span ie)) = concatM $ makeNode ie span : case ie of - IEVar _ n -> - [ toHie $ IEC c n - ] - IEThingAbs _ n -> - [ toHie $ IEC c n - ] - IEThingAll _ n -> - [ toHie $ IEC c n - ] - IEThingWith _ n _ ns flds -> - [ toHie $ IEC c n - , toHie $ map (IEC c) ns - , toHie $ map (IEC c) flds - ] - IEModuleContents _ n -> - [ toHie $ IEC c n - ] - IEGroup _ _ _ -> [] - IEDoc _ _ -> [] - IEDocNamed _ _ -> [] - XIE _ -> [] - -instance ToHie (IEContext (LIEWrappedName Name)) where - toHie (IEC c (L span iewn)) = concatM $ makeNode iewn span : case iewn of - IEName n -> - [ toHie $ C (IEThing c) n - ] - IEPattern p -> - [ toHie $ C (IEThing c) p - ] - IEType n -> - [ toHie $ C (IEThing c) n - ] - -instance ToHie (IEContext (Located (FieldLbl Name))) where - toHie (IEC c (L span lbl)) = concatM $ makeNode lbl span : case lbl of - FieldLabel _ _ n -> - [ toHie $ C (IEThing c) $ L span n - ] - diff --git a/hie-compat/src-ghc88/Compat/HieBin.hs b/hie-compat/src-ghc88/Compat/HieBin.hs deleted file mode 100644 index 859fc0f07d..0000000000 --- a/hie-compat/src-ghc88/Compat/HieBin.hs +++ /dev/null @@ -1,389 +0,0 @@ -{- -Binary serialization for .hie files. --} -{- HLINT ignore -} -{-# LANGUAGE ScopedTypeVariables #-} -module Compat.HieBin ( readHieFile, readHieFileWithVersion, HieHeader, writeHieFile, HieName(..), toHieName, HieFileResult(..), hieMagic,NameCacheUpdater(..)) where - -import Config ( cProjectVersion ) -import Binary -import BinIface ( getDictFastString ) -import FastMutInt -import FastString ( FastString ) -import Module ( Module ) -import Name -import NameCache -import Outputable -import PrelInfo -import SrcLoc -import UniqSupply ( takeUniqFromSupply ) -import Util ( maybeRead ) -import Unique -import UniqFM -import IfaceEnv - -import qualified Data.Array as A -import Data.IORef -import Data.ByteString ( ByteString ) -import qualified Data.ByteString as BS -import qualified Data.ByteString.Char8 as BSC -import Data.List ( mapAccumR ) -import Data.Word ( Word8, Word32 ) -import Control.Monad ( replicateM, when ) -import System.Directory ( createDirectoryIfMissing ) -import System.FilePath ( takeDirectory ) - -import HieTypes - --- | `Name`'s get converted into `HieName`'s before being written into @.hie@ --- files. See 'toHieName' and 'fromHieName' for logic on how to convert between --- these two types. -data HieName - = ExternalName !Module !OccName !SrcSpan - | LocalName !OccName !SrcSpan - | KnownKeyName !Unique - deriving (Eq) - -instance Ord HieName where - compare (ExternalName a b c) (ExternalName d e f) = compare (a,b,c) (d,e,f) - compare (LocalName a b) (LocalName c d) = compare (a,b) (c,d) - compare (KnownKeyName a) (KnownKeyName b) = nonDetCmpUnique a b - -- Not actually non determinstic as it is a KnownKey - compare ExternalName{} _ = LT - compare LocalName{} ExternalName{} = GT - compare LocalName{} _ = LT - compare KnownKeyName{} _ = GT - -instance Outputable HieName where - ppr (ExternalName m n sp) = text "ExternalName" <+> ppr m <+> ppr n <+> ppr sp - ppr (LocalName n sp) = text "LocalName" <+> ppr n <+> ppr sp - ppr (KnownKeyName u) = text "KnownKeyName" <+> ppr u - - -data HieSymbolTable = HieSymbolTable - { hie_symtab_next :: !FastMutInt - , hie_symtab_map :: !(IORef (UniqFM (Int, HieName))) - } - -data HieDictionary = HieDictionary - { hie_dict_next :: !FastMutInt -- The next index to use - , hie_dict_map :: !(IORef (UniqFM (Int,FastString))) -- indexed by FastString - } - -initBinMemSize :: Int -initBinMemSize = 1024*1024 - --- | The header for HIE files - Capital ASCII letters "HIE". -hieMagic :: [Word8] -hieMagic = [72,73,69] - -hieMagicLen :: Int -hieMagicLen = length hieMagic - -ghcVersion :: ByteString -ghcVersion = BSC.pack cProjectVersion - -putBinLine :: BinHandle -> ByteString -> IO () -putBinLine bh xs = do - mapM_ (putByte bh) $ BS.unpack xs - putByte bh 10 -- newline char - --- | Write a `HieFile` to the given `FilePath`, with a proper header and --- symbol tables for `Name`s and `FastString`s -writeHieFile :: FilePath -> HieFile -> IO () -writeHieFile hie_file_path hiefile = do - bh0 <- openBinMem initBinMemSize - - -- Write the header: hieHeader followed by the - -- hieVersion and the GHC version used to generate this file - mapM_ (putByte bh0) hieMagic - putBinLine bh0 $ BSC.pack $ show hieVersion - putBinLine bh0 $ ghcVersion - - -- remember where the dictionary pointer will go - dict_p_p <- tellBin bh0 - put_ bh0 dict_p_p - - -- remember where the symbol table pointer will go - symtab_p_p <- tellBin bh0 - put_ bh0 symtab_p_p - - -- Make some intial state - symtab_next <- newFastMutInt - writeFastMutInt symtab_next 0 - symtab_map <- newIORef emptyUFM - let hie_symtab = HieSymbolTable { - hie_symtab_next = symtab_next, - hie_symtab_map = symtab_map } - dict_next_ref <- newFastMutInt - writeFastMutInt dict_next_ref 0 - dict_map_ref <- newIORef emptyUFM - let hie_dict = HieDictionary { - hie_dict_next = dict_next_ref, - hie_dict_map = dict_map_ref } - - -- put the main thing - let bh = setUserData bh0 $ newWriteState (putName hie_symtab) - (putName hie_symtab) - (putFastString hie_dict) - put_ bh hiefile - - -- write the symtab pointer at the front of the file - symtab_p <- tellBin bh - putAt bh symtab_p_p symtab_p - seekBin bh symtab_p - - -- write the symbol table itself - symtab_next' <- readFastMutInt symtab_next - symtab_map' <- readIORef symtab_map - putSymbolTable bh symtab_next' symtab_map' - - -- write the dictionary pointer at the front of the file - dict_p <- tellBin bh - putAt bh dict_p_p dict_p - seekBin bh dict_p - - -- write the dictionary itself - dict_next <- readFastMutInt dict_next_ref - dict_map <- readIORef dict_map_ref - putDictionary bh dict_next dict_map - - -- and send the result to the file - createDirectoryIfMissing True (takeDirectory hie_file_path) - writeBinMem bh hie_file_path - return () - -data HieFileResult - = HieFileResult - { hie_file_result_version :: Integer - , hie_file_result_ghc_version :: ByteString - , hie_file_result :: HieFile - } - -type HieHeader = (Integer, ByteString) - --- | Read a `HieFile` from a `FilePath`. Can use --- an existing `NameCache`. Allows you to specify --- which versions of hieFile to attempt to read. --- `Left` case returns the failing header versions. -readHieFileWithVersion :: (HieHeader -> Bool) -> NameCacheUpdater -> FilePath -> IO (Either HieHeader HieFileResult) -readHieFileWithVersion readVersion ncu file = do - bh0 <- readBinMem file - - (hieVersion, ghcVersion) <- readHieFileHeader file bh0 - - if readVersion (hieVersion, ghcVersion) - then do - hieFile <- readHieFileContents bh0 ncu - return $ Right (HieFileResult hieVersion ghcVersion hieFile) - else return $ Left (hieVersion, ghcVersion) - - --- | Read a `HieFile` from a `FilePath`. Can use --- an existing `NameCache`. -readHieFile :: NameCacheUpdater -> FilePath -> IO HieFileResult -readHieFile ncu file = do - - bh0 <- readBinMem file - - (readHieVersion, ghcVersion) <- readHieFileHeader file bh0 - - -- Check if the versions match - when (readHieVersion /= hieVersion) $ - panic $ unwords ["readHieFile: hie file versions don't match for file:" - , file - , "Expected" - , show hieVersion - , "but got", show readHieVersion - ] - hieFile <- readHieFileContents bh0 ncu - return $ HieFileResult hieVersion ghcVersion hieFile - -readBinLine :: BinHandle -> IO ByteString -readBinLine bh = BS.pack . reverse <$> loop [] - where - loop acc = do - char <- get bh :: IO Word8 - if char == 10 -- ASCII newline '\n' - then return acc - else loop (char : acc) - -readHieFileHeader :: FilePath -> BinHandle -> IO HieHeader -readHieFileHeader file bh0 = do - -- Read the header - magic <- replicateM hieMagicLen (get bh0) - version <- BSC.unpack <$> readBinLine bh0 - case maybeRead version of - Nothing -> - panic $ unwords ["readHieFileHeader: hieVersion isn't an Integer:" - , show version - ] - Just readHieVersion -> do - ghcVersion <- readBinLine bh0 - - -- Check if the header is valid - when (magic /= hieMagic) $ - panic $ unwords ["readHieFileHeader: headers don't match for file:" - , file - , "Expected" - , show hieMagic - , "but got", show magic - ] - return (readHieVersion, ghcVersion) - -readHieFileContents :: BinHandle -> NameCacheUpdater -> IO HieFile -readHieFileContents bh0 ncu = do - - dict <- get_dictionary bh0 - - -- read the symbol table so we are capable of reading the actual data - bh1 <- do - let bh1 = setUserData bh0 $ newReadState (error "getSymtabName") - (getDictFastString dict) - symtab <- get_symbol_table bh1 - let bh1' = setUserData bh1 - $ newReadState (getSymTabName symtab) - (getDictFastString dict) - return bh1' - - -- load the actual data - hiefile <- get bh1 - return hiefile - where - get_dictionary bin_handle = do - dict_p <- get bin_handle - data_p <- tellBin bin_handle - seekBin bin_handle dict_p - dict <- getDictionary bin_handle - seekBin bin_handle data_p - return dict - - get_symbol_table bh1 = do - symtab_p <- get bh1 - data_p' <- tellBin bh1 - seekBin bh1 symtab_p - symtab <- getSymbolTable bh1 ncu - seekBin bh1 data_p' - return symtab - -putFastString :: HieDictionary -> BinHandle -> FastString -> IO () -putFastString HieDictionary { hie_dict_next = j_r, - hie_dict_map = out_r} bh f - = do - out <- readIORef out_r - let unique = getUnique f - case lookupUFM out unique of - Just (j, _) -> put_ bh (fromIntegral j :: Word32) - Nothing -> do - j <- readFastMutInt j_r - put_ bh (fromIntegral j :: Word32) - writeFastMutInt j_r (j + 1) - writeIORef out_r $! addToUFM out unique (j, f) - -putSymbolTable :: BinHandle -> Int -> UniqFM (Int,HieName) -> IO () -putSymbolTable bh next_off symtab = do - put_ bh next_off - let names = A.elems (A.array (0,next_off-1) (nonDetEltsUFM symtab)) - mapM_ (putHieName bh) names - -getSymbolTable :: BinHandle -> NameCacheUpdater -> IO SymbolTable -getSymbolTable bh ncu = do - sz <- get bh - od_names <- replicateM sz (getHieName bh) - updateNameCache ncu $ \nc -> - let arr = A.listArray (0,sz-1) names - (nc', names) = mapAccumR fromHieName nc od_names - in (nc',arr) - -getSymTabName :: SymbolTable -> BinHandle -> IO Name -getSymTabName st bh = do - i :: Word32 <- get bh - return $ st A.! (fromIntegral i) - -putName :: HieSymbolTable -> BinHandle -> Name -> IO () -putName (HieSymbolTable next ref) bh name = do - symmap <- readIORef ref - case lookupUFM symmap name of - Just (off, ExternalName mod occ (UnhelpfulSpan _)) - | isGoodSrcSpan (nameSrcSpan name) -> do - let hieName = ExternalName mod occ (nameSrcSpan name) - writeIORef ref $! addToUFM symmap name (off, hieName) - put_ bh (fromIntegral off :: Word32) - Just (off, LocalName _occ span) - | notLocal (toHieName name) || nameSrcSpan name /= span -> do - writeIORef ref $! addToUFM symmap name (off, toHieName name) - put_ bh (fromIntegral off :: Word32) - Just (off, _) -> put_ bh (fromIntegral off :: Word32) - Nothing -> do - off <- readFastMutInt next - writeFastMutInt next (off+1) - writeIORef ref $! addToUFM symmap name (off, toHieName name) - put_ bh (fromIntegral off :: Word32) - - where - notLocal :: HieName -> Bool - notLocal LocalName{} = False - notLocal _ = True - - --- ** Converting to and from `HieName`'s - -toHieName :: Name -> HieName -toHieName name - | isKnownKeyName name = KnownKeyName (nameUnique name) - | isExternalName name = ExternalName (nameModule name) - (nameOccName name) - (nameSrcSpan name) - | otherwise = LocalName (nameOccName name) (nameSrcSpan name) - -fromHieName :: NameCache -> HieName -> (NameCache, Name) -fromHieName nc (ExternalName mod occ span) = - let cache = nsNames nc - in case lookupOrigNameCache cache mod occ of - Just name - | nameSrcSpan name == span -> (nc, name) - | otherwise -> - let name' = setNameLoc name span - new_cache = extendNameCache cache mod occ name' - in ( nc{ nsNames = new_cache }, name' ) - Nothing -> - let (uniq, us) = takeUniqFromSupply (nsUniqs nc) - name = mkExternalName uniq mod occ span - new_cache = extendNameCache cache mod occ name - in ( nc{ nsUniqs = us, nsNames = new_cache }, name ) -fromHieName nc (LocalName occ span) = - let (uniq, us) = takeUniqFromSupply (nsUniqs nc) - name = mkInternalName uniq occ span - in ( nc{ nsUniqs = us }, name ) -fromHieName nc (KnownKeyName u) = case lookupKnownKeyName u of - Nothing -> pprPanic "fromHieName:unknown known-key unique" - (ppr (unpkUnique u)) - Just n -> (nc, n) - --- ** Reading and writing `HieName`'s - -putHieName :: BinHandle -> HieName -> IO () -putHieName bh (ExternalName mod occ span) = do - putByte bh 0 - put_ bh (mod, occ, span) -putHieName bh (LocalName occName span) = do - putByte bh 1 - put_ bh (occName, span) -putHieName bh (KnownKeyName uniq) = do - putByte bh 2 - put_ bh $ unpkUnique uniq - -getHieName :: BinHandle -> IO HieName -getHieName bh = do - t <- getByte bh - case t of - 0 -> do - (modu, occ, span) <- get bh - return $ ExternalName modu occ span - 1 -> do - (occ, span) <- get bh - return $ LocalName occ span - 2 -> do - (c,i) <- get bh - return $ KnownKeyName $ mkUnique c i - _ -> panic "HieBin.getHieName: invalid tag" diff --git a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Literals.hs b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Literals.hs index a3935b92e9..1c23bee738 100644 --- a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Literals.hs +++ b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/Literals.hs @@ -62,17 +62,8 @@ getLiteral (L (locA -> (RealSrcSpan sSpan _)) expr) = case expr of _ -> Nothing getLiteral _ = Nothing - - --- GHC 8.8 typedefs LPat = Pat -#if __GLASGOW_HASKELL__ == 808 -type LocPat a = GenLocated SrcSpan (Pat a) -#else -type LocPat a = LPat a -#endif - -- | Destructure Patterns to unwrap any Literals -getPattern :: LocPat GhcPs -> Maybe Literal +getPattern :: LPat GhcPs -> Maybe Literal getPattern (L (locA -> (RealSrcSpan patSpan _)) pat) = case pat of LitPat _ lit -> case lit of HsInt _ val -> fromIntegralLit patSpan val diff --git a/plugins/hls-eval-plugin/src/Ide/Plugin/Eval/CodeLens.hs b/plugins/hls-eval-plugin/src/Ide/Plugin/Eval/CodeLens.hs index 2ed90bab48..c1a71c4d40 100644 --- a/plugins/hls-eval-plugin/src/Ide/Plugin/Eval/CodeLens.hs +++ b/plugins/hls-eval-plugin/src/Ide/Plugin/Eval/CodeLens.hs @@ -305,9 +305,6 @@ runEvalCmd plId st EvalParams{..} = #endif -- Load the module with its current content (as the saved module might not be up to date) - -- BUG: this fails for files that requires preprocessors (e.g. CPP) for ghc < 8.8 - -- see https://gitlab.haskell.org/ghc/ghc/-/issues/17066 - -- and https://hackage.haskell.org/package/ghc-8.10.1/docs/GHC.html#v:TargetFile eSetTarget <- gStrictTry $ setTargets [thisModuleTarget] dbg "setTarget" eSetTarget diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Dump.hs b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Dump.hs index 19e7efe6e6..e7297e1db8 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Dump.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/Dump.hs @@ -3,11 +3,7 @@ module Development.IDE.GHC.Dump(showAstDataHtml) where import Data.Data hiding (Fixity) import Development.IDE.GHC.Compat hiding (NameAnn) import Development.IDE.GHC.Compat.ExactPrint -#if MIN_VERSION_ghc(8,10,1) import GHC.Hs.Dump -#else -import HsDumpAst -#endif #if MIN_VERSION_ghc(9,2,1) import qualified Data.ByteString as B import Development.IDE.GHC.Compat.Util diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs index d56b513a79..8368efa249 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs @@ -489,13 +489,8 @@ instance p ~ GhcPs => ASTElement AnnListItem (HsExpr p) where graft = graftExpr instance p ~ GhcPs => ASTElement AnnListItem (Pat p) where -#if __GLASGOW_HASKELL__ == 808 - parseAST = fmap (fmap $ right $ second dL) . parsePattern - maybeParensAST = dL . parenthesizePat appPrec . unLoc -#else parseAST = parsePattern maybeParensAST = parenthesizePat appPrec -#endif instance p ~ GhcPs => ASTElement AnnListItem (HsType p) where parseAST = parseType diff --git a/plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs b/plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs index e5b6883fce..3b4d632822 100644 --- a/plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs +++ b/plugins/hls-retrie-plugin/src/Ide/Plugin/Retrie.hs @@ -559,8 +559,4 @@ toImportDecl AddImport {..} = GHC.ImportDecl {ideclSource = ideclSource', ..} ideclExt = GHC.noExtField #endif ideclAs = toMod <$> ideclAsString -#if MIN_VERSION_ghc(8,10,0) ideclQualified = if ideclQualifiedBool then GHC.QualifiedPre else GHC.NotQualified -#else - ideclQualified = ideclQualifiedBool -#endif diff --git a/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs b/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs index 41b5774706..aabb3b09ee 100644 --- a/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs +++ b/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs @@ -126,9 +126,6 @@ expandTHSplice _eStyle ideState params@ExpandSpliceParams {..} = do let exprSuperSpans = listToMaybe $ findSubSpansDesc srcSpan exprSplices _patSuperSpans = -#if __GLASGOW_HASKELL__ == 808 - fmap (second dL) $ -#endif listToMaybe $ findSubSpansDesc srcSpan patSplices typeSuperSpans = listToMaybe $ findSubSpansDesc srcSpan typeSplices diff --git a/plugins/hls-stan-plugin/hls-stan-plugin.cabal b/plugins/hls-stan-plugin/hls-stan-plugin.cabal index 370d206f81..5a8632d173 100644 --- a/plugins/hls-stan-plugin/hls-stan-plugin.cabal +++ b/plugins/hls-stan-plugin/hls-stan-plugin.cabal @@ -26,7 +26,7 @@ flag pedantic manual: True library - if impl(ghc < 8.8) || impl(ghc >= 9.0) + if impl(ghc < 8.10) || impl(ghc >= 9.0) buildable: False else buildable: True @@ -58,7 +58,7 @@ library OverloadedStrings test-suite test - if impl(ghc < 8.8) || impl(ghc >= 9.0) + if impl(ghc < 8.10) || impl(ghc >= 9.0) buildable: False else buildable: True diff --git a/plugins/hls-tactics-plugin/src/Wingman/AbstractLSP/TacticActions.hs b/plugins/hls-tactics-plugin/src/Wingman/AbstractLSP/TacticActions.hs index 3693d7c1d3..abea111b07 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/AbstractLSP/TacticActions.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/AbstractLSP/TacticActions.hs @@ -18,7 +18,7 @@ import Development.IDE.GHC.ExactPrint import Generics.SYB.GHC (mkBindListT, everywhereM') import Wingman.AbstractLSP.Types import Wingman.CaseSplit -import Wingman.GHC (liftMaybe, isHole, pattern AMatch, unXPat) +import Wingman.GHC (liftMaybe, isHole, pattern AMatch) import Wingman.Judgements (jNeedsToBindArgs) import Wingman.LanguageServer (runStaleIde) import Wingman.LanguageServer.TacticProviders @@ -133,7 +133,7 @@ graftHole span rtr ) (occName name) $ iterateSplit - $ mkFirstAgda (fmap unXPat pats) + $ mkFirstAgda pats $ unLoc $ rtr_extract rtr graftHole span rtr diff --git a/plugins/hls-tactics-plugin/src/Wingman/GHC.hs b/plugins/hls-tactics-plugin/src/Wingman/GHC.hs index e90fce6de8..4c548bd72e 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/GHC.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/GHC.hs @@ -277,17 +277,6 @@ class PatCompattable p where fromPatCompat :: PatCompat p -> Pat p toPatCompat :: Pat p -> PatCompat p -#if __GLASGOW_HASKELL__ == 808 -instance PatCompattable GhcTc where - fromPatCompat = id - toPatCompat = id - -instance PatCompattable GhcPs where - fromPatCompat = id - toPatCompat = id - -type PatCompat pass = Pat pass -#else instance PatCompattable GhcTc where fromPatCompat = unLoc toPatCompat = noLoc @@ -297,7 +286,6 @@ instance PatCompattable GhcPs where toPatCompat = noLoc type PatCompat pass = LPat pass -#endif ------------------------------------------------------------------------------ -- | Should make sure it's a fun bind @@ -314,19 +302,6 @@ pattern TopLevelRHS name ps body where_binds <- (GRHSs _ [L _ (GRHS _ [] body)] (L _ where_binds)) ------------------------------------------------------------------------------- --- | In GHC 8.8, sometimes patterns are wrapped in 'XPat'. --- The nitty gritty details are explained at --- https://blog.shaynefletcher.org/2020/03/ghc-haskell-pats-and-lpats.html --- --- We need to remove these in order to succesfull find patterns. -unXPat :: Pat GhcPs -> Pat GhcPs -#if __GLASGOW_HASKELL__ == 808 -unXPat (XPat (L _ pat)) = unXPat pat -#endif -unXPat pat = pat - - liftMaybe :: Monad m => Maybe a -> MaybeT m a liftMaybe a = MaybeT $ pure a diff --git a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs b/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs index 2db38a2a8b..829e1dda90 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs @@ -426,9 +426,6 @@ buildPatHy prov (fromPatCompat -> p0) = RecCon r -> mkDerivedRecordHypothesis prov con args r SigPat _ p _ -> buildPatHy prov p -#if __GLASGOW_HASKELL__ == 808 - XPat p -> buildPatHy prov $ unLoc p -#endif _ -> pure mempty @@ -583,10 +580,6 @@ wingmanRules recorder plId = do #endif | isHole occ -> maybeToList $ srcSpanToRange span -#if __GLASGOW_HASKELL__ == 808 - L span (EWildPat _) -> - maybeToList $ srcSpanToRange span -#endif (_ :: LHsExpr GhcPs) -> mempty ) $ pm_parsed_source pm pure diff --git a/stack-lts16.yaml b/stack-lts16.yaml deleted file mode 100644 index 40449ab4d5..0000000000 --- a/stack-lts16.yaml +++ /dev/null @@ -1,129 +0,0 @@ -resolver: lts-16.31 # last 8.8.4 lts - -packages: - - . - - ./hie-compat - - ./hls-graph - - ./ghcide/ - - ./ghcide/test - - ./shake-bench - - ./hls-plugin-api - - ./hls-test-utils - - ./plugins/hls-call-hierarchy-plugin - - ./plugins/hls-class-plugin - - ./plugins/hls-haddock-comments-plugin - - ./plugins/hls-eval-plugin - - ./plugins/hls-explicit-imports-plugin - - ./plugins/hls-refine-imports-plugin - - ./plugins/hls-hlint-plugin - - ./plugins/hls-stan-plugin - - ./plugins/hls-rename-plugin - - ./plugins/hls-retrie-plugin - - ./plugins/hls-splice-plugin - - ./plugins/hls-tactics-plugin - - ./plugins/hls-qualify-imported-names-plugin - - ./plugins/hls-brittany-plugin - - ./plugins/hls-stylish-haskell-plugin - - ./plugins/hls-floskell-plugin - - ./plugins/hls-fourmolu-plugin - - ./plugins/hls-pragmas-plugin - - ./plugins/hls-module-name-plugin - - ./plugins/hls-ormolu-plugin - - ./plugins/hls-alternate-number-format-plugin - - ./plugins/hls-code-range-plugin - - ./plugins/hls-change-type-signature-plugin - - ./plugins/hls-gadt-plugin - - ./plugins/hls-explicit-fixity-plugin - - ./plugins/hls-refactor-plugin - -ghc-options: - "$everything": -haddock - -extra-deps: - - aeson-1.5.2.0 - - apply-refact-0.9.3.0 - - brittany-0.13.1.2 - - bytestring-trie-0.2.5.0 - - cabal-plan-0.6.2.0 - - clock-0.7.2 - - constrained-dynamic-0.1.0.0 - - extra-1.7.10 - - floskell-0.10.4 - - fourmolu-0.3.0.0 - - ghc-check-0.5.0.8 - - ghc-exactprint-0.6.4 - - ghc-lib-8.10.7.20210828 - - ghc-lib-parser-8.10.7.20210828 - - ghc-source-gen-0.4.1.0 - - ghc-trace-events-0.1.2.1 - - haskell-src-exts-1.21.1 - - hlint-3.2.8 - - HsYAML-aeson-0.2.0.0@rev:2 - - hoogle-5.0.17.11 - - hsimport-0.11.0 - - ilist-0.3.1.0 - - implicit-hie-cradle-0.3.0.5 - - implicit-hie-0.1.2.6 - - megaparsec-9.0.1 - - monad-dijkstra-0.1.1.2 - - opentelemetry-0.6.1 - - opentelemetry-extra-0.6.1 - - refinery-0.4.0.0 - - retrie-1.1.0.0 - - semigroups-0.18.5 - - shake-0.19.4 - - stylish-haskell-0.12.2.0 - - temporary-1.2.1.1 - - th-compat-0.1.2@sha256:3d55de1adc542c1a870c9ada90da2fbbe5f4e8bcd3eed545a55c3df9311b32a8,2854 - - bytestring-encoding-0.1.0.0@sha256:460b49779fbf0112e8e2f1753c1ed9131eb18827600c298f4d6bb51c4e8c1c0d,1727 - - hiedb-0.4.2.0 - - sqlite-simple-0.4.18.0@sha256:3ceea56375c0a3590c814e411a4eb86943f8d31b93b110ca159c90689b6b39e5,3002 - - direct-sqlite-2.3.26@sha256:04e835402f1508abca383182023e4e2b9b86297b8533afbd4e57d1a5652e0c23,3718 - - dependent-map-0.4.0.0@sha256:ca2b131046f4340a1c35d138c5a003fe4a5be96b14efc26291ed35fd08c62221,1657 - - dependent-sum-0.7.1.0@sha256:5599aa89637db434431b1dd3fa7c34bc3d565ee44f0519bfbc877be1927c2531,2068 - - dependent-sum-template-0.1.0.3@sha256:0bbbacdfbd3abf2a15aaf0cf2c27e5bdd159b519441fec39e1e6f2f54424adde,1682 - - constraints-extras-0.3.0.2@sha256:013b8d0392582c6ca068e226718a4fe8be8e22321cc0634f6115505bf377ad26,1853 - - some-1.0.1@sha256:26e5bab7276f48b25ea8660d3fd1166c0f20fd497dac879a40f408e23211f93e,2055 - - unliftio-core-0.2.0.1@sha256:9b3e44ea9aacacbfc35b3b54015af450091916ac3618a41868ebf6546977659a,1082 - - stm-containers-1.1.0.4 - - stm-hamt-1.2.0.6@sha256:fba86ccb4b45c5706c19b0e1315ba63dcac3b5d71de945ec001ba921fae80061,3972 - - primitive-extras-0.10.1 - - primitive-unlifted-0.1.3.1 - - githash-0.1.6.2 - - stan-0.0.1.0 - - dir-traverse-0.2.3.0@sha256:adcc128f201ff95131b15ffe41365dc99c50dc3fa3a910f021521dc734013bfa,2137 - - extensions-0.0.0.1@sha256:16517ab9df3dd6c7a20da746c8ed02cfd59c8cb40ae5719aef8b5dd4edceadc0,3993 - - microaeson-0.1.0.1@sha256:88ba1cc52181b57abc453e222bbb76ca6ab252e38c6507d15a596d6a582fdf69,3968 - - trial-0.0.0.0@sha256:834d3be439dc9b52a759a45a4d3944e5e55c3d50fd5874003147cc1f6231d4aa,4301 - - trial-optparse-applicative-0.0.0.0@sha256:ba05edfc327a281766df5e0f44d91229e6a98afaf59abe1894b293453f076192,2449 - - trial-tomland-0.0.0.0@sha256:743a9baaa36891ed3a44618fdfd5bc4ed9afc39cf9b9fa23ea1b96f3787f5ec0,2526 - - text-rope-0.2 - - co-log-core-0.3.1.0 - - lsp-1.6.0.0 - - lsp-types-1.6.0.0 - - lsp-test-0.14.1.0 - - hie-bios-0.11.0 - - prettyprinter-1.7.1@sha256:9c43c9d8c3cd9f445596e5a2379574bba87f935a4d1fa41b5407ee3cf4edc743,6987 - -configure-options: - ghcide: - - --disable-library-for-ghci - haskell-language-server: - - --disable-library-for-ghci - heapsize: - - --disable-library-for-ghci - -flags: - haskell-language-server: - pedantic: true - retrie: - BuildExecutable: false - hyphenation: - embed: true - hlint: - ghc-lib: true - -nix: - packages: [icu libcxx zlib] - -concurrent-tests: false diff --git a/test/functional/Completion.hs b/test/functional/Completion.hs index 8a33eddbe5..7ad0824179 100644 --- a/test/functional/Completion.hs +++ b/test/functional/Completion.hs @@ -376,4 +376,4 @@ compls `shouldNotContainCompl` lbl = @? "Should not contain completion: " ++ show lbl expectFailIfBeforeGhc92 :: String -> TestTree -> TestTree -expectFailIfBeforeGhc92 = knownBrokenForGhcVersions [GHC810, GHC88, GHC90] +expectFailIfBeforeGhc92 = knownBrokenForGhcVersions [GHC810, GHC90] diff --git a/test/functional/TypeDefinition.hs b/test/functional/TypeDefinition.hs index 96f4ab91f0..f191fbfe7e 100644 --- a/test/functional/TypeDefinition.hs +++ b/test/functional/TypeDefinition.hs @@ -13,8 +13,7 @@ tests = testGroup "type definitions" [ $ getTypeDefinitionTest' 15 21 12 0 , testCase "finds local definition of sum type variable" $ getTypeDefinitionTest' 20 13 17 0 - , knownBrokenForGhcVersions [GHC88] "Definition of sum type not found from data constructor in GHC 8.8.x" $ - testCase "finds local definition of sum type constructor" + , testCase "finds local definition of sum type constructor" $ getTypeDefinitionTest' 23 7 17 0 , testCase "finds non-local definition of type def" $ getTypeDefinitionTest' 29 19 26 0 diff --git a/test/wrapper/Main.hs b/test/wrapper/Main.hs index 1e6f205a6d..6c68440a5f 100644 --- a/test/wrapper/Main.hs +++ b/test/wrapper/Main.hs @@ -12,7 +12,6 @@ main = do projectGhcVersionTests :: TestTree projectGhcVersionTests = testGroup "--project-ghc-version" [ stackTest "8.10.7" - , stackTest "8.8.4" , testCase "cabal with global ghc" $ do ghcVer <- trimEnd <$> readProcess "ghc" ["--numeric-version"] "" testDir "test/wrapper/testdata/cabal-cur-ver" ghcVer diff --git a/test/wrapper/testdata/stack-8.8.4/Lib.hs b/test/wrapper/testdata/stack-8.8.4/Lib.hs deleted file mode 100644 index 30bf1ec6b8..0000000000 --- a/test/wrapper/testdata/stack-8.8.4/Lib.hs +++ /dev/null @@ -1,2 +0,0 @@ -module Lib where -foo = 42 diff --git a/test/wrapper/testdata/stack-8.8.4/foo.cabal b/test/wrapper/testdata/stack-8.8.4/foo.cabal deleted file mode 100644 index affc654cad..0000000000 --- a/test/wrapper/testdata/stack-8.8.4/foo.cabal +++ /dev/null @@ -1,7 +0,0 @@ -cabal-version: 2.4 -name: foo -version: 0.1.0.0 -library - exposed-modules: Lib - build-depends: base - default-language: Haskell2010 diff --git a/test/wrapper/testdata/stack-8.8.4/stack.yaml b/test/wrapper/testdata/stack-8.8.4/stack.yaml deleted file mode 100644 index f9dba12313..0000000000 --- a/test/wrapper/testdata/stack-8.8.4/stack.yaml +++ /dev/null @@ -1 +0,0 @@ -resolver: ghc-8.8.4 From d0e90556939da62d38fca79d78cc818b71721bb1 Mon Sep 17 00:00:00 2001 From: Caique Figueiredo Date: Sat, 22 Oct 2022 05:56:10 -0300 Subject: [PATCH 170/213] Change default cabal install target name (#3298) --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index b3f8270288..9511d3d010 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -26,7 +26,7 @@ More information here: ## Installation from source -Direct installation from source, while possible via `cabal install haskell-language-server` +Direct installation from source, while possible via `cabal install exe:haskell-language-server` and `stack install --stack-yaml stack-.yaml`, is not recommended for most people. Said command builds the `haskell-language-server` binary and installs it in the default `cabal` binaries folder, but the binary will only work with projects that use the same GHC version that built it. From 43508cddbc7a1ee1b0e7860b26cac42250912414 Mon Sep 17 00:00:00 2001 From: wz1000 Date: Sun, 23 Oct 2022 22:17:49 +0530 Subject: [PATCH 171/213] Improve hls-fixity-plugin (#3205) --- .../hls-explicit-fixity-plugin.cabal | 1 + .../src/Ide/Plugin/ExplicitFixity.hs | 135 ++++++------------ 2 files changed, 45 insertions(+), 91 deletions(-) diff --git a/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal b/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal index 066d53737d..493cfd3d8c 100644 --- a/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal +++ b/plugins/hls-explicit-fixity-plugin/hls-explicit-fixity-plugin.cabal @@ -35,6 +35,7 @@ library , hls-plugin-api ^>=1.5 , lsp >=1.2.0.1 , text + , transformers ghc-options: -Wall diff --git a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs index 58899a460f..75e27856b5 100644 --- a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs +++ b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs @@ -1,32 +1,30 @@ {-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE TupleSections #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-} -{-# OPTIONS_GHC -Wno-deprecations #-} -{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} -{-# HLINT ignore "Use nubOrdOn" #-} +{-# OPTIONS_GHC -Wno-orphans #-} +{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} module Ide.Plugin.ExplicitFixity(descriptor) where import Control.DeepSeq -import Control.Monad (forM) +import Control.Monad.Trans.Maybe import Control.Monad.IO.Class (MonadIO, liftIO) -import Data.Coerce (coerce) import Data.Either.Extra import Data.Hashable -import Data.List.Extra (nubOn) -import qualified Data.Map as M +import qualified Data.Map.Strict as M +import qualified Data.Set as S import Data.Maybe -import Data.Monoid import qualified Data.Text as T import Development.IDE hiding (pluginHandlers, pluginRules) import Development.IDE.Core.PositionMapping (idDelta) import Development.IDE.Core.Shake (addPersistentRule) import qualified Development.IDE.Core.Shake as Shake +import Development.IDE.Spans.AtPoint import Development.IDE.GHC.Compat -import Development.IDE.GHC.Compat.Util (FastString) import qualified Development.IDE.GHC.Compat.Util as Util import Development.IDE.LSP.Notifications (ghcideNotificationsPluginPriority) import GHC.Generics (Generic) @@ -48,14 +46,14 @@ descriptor recorder pluginId = (defaultPluginDescriptor pluginId) hover :: PluginMethodHandler IdeState TextDocumentHover hover state _ (HoverParams (TextDocumentIdentifier uri) pos _) = pluginResponse $ do nfp <- getNormalizedFilePath uri - fixityTrees <- handleMaybeM "Unable to get fixity" - $ liftIO - $ runAction "ExplicitFixity.GetFixity" state - $ use GetFixity nfp - -- We don't have much fixities on one position, so `nubOn` is acceptable. - pure $ toHover $ nubOn snd $ findInTree fixityTrees pos fNodeFixty + handleMaybeM "ExplicitFixity: Unable to get fixity" $ liftIO $ runIdeAction "ExplicitFixity" (shakeExtras state) $ runMaybeT $ do + (FixityMap fixmap, _) <- useE GetFixity nfp + (HAR{hieAst}, mapping) <- useE GetHieAst nfp + let ns = getNamesAtPoint hieAst pos mapping + fs = mapMaybe (\n -> (n,) <$> M.lookup n fixmap) ns + pure $ toHover $ fs where - toHover :: [(T.Text, Fixity)] -> Maybe Hover + toHover :: [(Name, Fixity)] -> Maybe Hover toHover [] = Nothing toHover fixities = let -- Splicing fixity info @@ -64,44 +62,19 @@ hover state _ (HoverParams (TextDocumentIdentifier uri) pos _) = pluginResponse contents' = "\n" <> sectionSeparator <> contents in Just $ Hover (HoverContents $ unmarkedUpContent contents') Nothing - fixityText :: (T.Text, Fixity) -> T.Text + fixityText :: (Name, Fixity) -> T.Text fixityText (name, Fixity _ precedence direction) = - printOutputable direction <> " " <> printOutputable precedence <> " `" <> name <> "`" - --- | Transferred from ghc `selectSmallestContaining` -selectSmallestContainingForFixityTree :: Span -> FixityTree -> Maybe FixityTree -selectSmallestContainingForFixityTree sp node - | sp `containsSpan` fNodeSpan node = Just node - | fNodeSpan node `containsSpan` sp = getFirst $ mconcat - [ foldMap (First . selectSmallestContainingForFixityTree sp) $ fNodeChildren node - , First (Just node) - ] - | otherwise = Nothing - --- | Transferred from ghcide `pointCommand` -findInTree :: FixityTrees -> Position -> (FixityTree -> [a]) -> [a] -findInTree tree pos k = - concat $ M.elems $ flip M.mapWithKey tree $ \fs ast -> - maybe [] k (selectSmallestContainingForFixityTree (sp fs) ast) - where - sloc fs = mkRealSrcLoc fs (fromIntegral $ line+1) (fromIntegral $ cha+1) - sp fs = mkRealSrcSpan (sloc fs) (sloc fs) - line = _line pos - cha = _character pos - -data FixityTree = FNode - { fNodeSpan :: Span - , fNodeChildren :: [FixityTree] - , fNodeFixty :: [(T.Text, Fixity)] - } deriving (Generic) + printOutputable direction <> " " <> printOutputable precedence <> " `" <> printOutputable name <> "`" -instance NFData FixityTree where - rnf = rwhnf +newtype FixityMap = FixityMap (M.Map Name Fixity) +instance Show FixityMap where + show _ = "FixityMap" -instance Show FixityTree where - show _ = "" +instance NFData FixityMap where + rnf (FixityMap xs) = rnf xs -type FixityTrees = M.Map FastString FixityTree +instance NFData Fixity where + rnf = rwhnf newtype Log = LogShake Shake.Log @@ -114,53 +87,33 @@ data GetFixity = GetFixity deriving (Show, Eq, Generic) instance Hashable GetFixity instance NFData GetFixity -type instance RuleResult GetFixity = FixityTrees - -fakeFixityTrees :: FixityTrees -fakeFixityTrees = M.empty - --- | Convert a HieASTs to FixityTrees with fixity info gathered -hieAstsToFixitTrees :: MonadIO m => HscEnv -> TcGblEnv -> HieASTs a -> m FixityTrees -hieAstsToFixitTrees hscEnv tcGblEnv ast = - -- coerce to avoid compatibility issues. - M.mapKeysWith const coerce <$> - sequence (M.map (hieAstToFixtyTree hscEnv tcGblEnv) (getAsts ast)) +type instance RuleResult GetFixity = FixityMap -- | Convert a HieAST to FixityTree with fixity info gathered -hieAstToFixtyTree :: MonadIO m => HscEnv -> TcGblEnv -> HieAST a -> m FixityTree -hieAstToFixtyTree hscEnv tcGblEnv ast = case ast of - (Node _ span []) -> FNode span [] <$> getFixities - (Node _ span children) -> do - fixities <- getFixities - childrenFixities <- mapM (hieAstToFixtyTree hscEnv tcGblEnv) children - pure $ FNode span childrenFixities fixities - where - -- Names at the current ast node - names :: [Name] - names = mapMaybe eitherToMaybe $ M.keys $ getNodeIds ast - - getFixities :: MonadIO m => m [(T.Text, Fixity)] - getFixities = liftIO - $ fmap (filter ((/= defaultFixity) . snd) . mapMaybe pickFixity) - $ forM names $ \name -> - (,) (printOutputable name) - . snd - <$> Util.handleGhcException - (const $ pure (emptyMessages, Nothing)) - (initTcWithGbl hscEnv tcGblEnv (realSrcLocSpan $ mkRealSrcLoc "" 1 1) (lookupFixityRn name)) - - pickFixity :: (T.Text, Maybe Fixity) -> Maybe (T.Text, Fixity) - pickFixity (_, Nothing) = Nothing - pickFixity (name, Just f) = Just (name, f) +lookupFixities :: MonadIO m => HscEnv -> TcGblEnv -> S.Set Name -> m (M.Map Name Fixity) +lookupFixities hscEnv tcGblEnv names + = liftIO + $ fmap (fromMaybe M.empty . snd) + $ initTcWithGbl hscEnv tcGblEnv (realSrcLocSpan $ mkRealSrcLoc "" 1 1) + $ M.traverseMaybeWithKey (\_ v -> v) + $ M.fromSet lookupFixity names + where + lookupFixity name = do + f <- Util.handleGhcException + (const $ pure Nothing) + (Just <$> lookupFixityRn name) + if f == Just defaultFixity + then pure Nothing + else pure f fixityRule :: Recorder (WithPriority Log) -> Rules () fixityRule recorder = do define (cmapWithPrio LogShake recorder) $ \GetFixity nfp -> do - HAR{hieAst} <- use_ GetHieAst nfp - env <- hscEnv <$> use_ GhcSession nfp + HAR{refMap} <- use_ GetHieAst nfp + env <- hscEnv <$> use_ GhcSessionDeps nfp -- deps necessary so that we can consult already loaded in ifaces instead of loading in duplicates tcGblEnv <- tmrTypechecked <$> use_ TypeCheck nfp - trees <- hieAstsToFixitTrees env tcGblEnv hieAst - pure ([], Just trees) + fs <- lookupFixities env tcGblEnv (S.mapMonotonic (\(Right n) -> n) $ S.filter isRight $ M.keysSet refMap) + pure ([], Just (FixityMap fs)) -- Ensure that this plugin doesn't block on startup - addPersistentRule GetFixity $ \_ -> pure $ Just (fakeFixityTrees, idDelta, Nothing) + addPersistentRule GetFixity $ \_ -> pure $ Just (FixityMap M.empty, idDelta, Nothing) From e99772d2d78434b28bf037d1e36956aa871fa389 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Thu, 28 Jul 2022 18:35:49 +0100 Subject: [PATCH 172/213] Implement sharing for hls-graph Keys --- ghcide/src/Development/IDE/Core/Shake.hs | 8 +-- ghcide/src/Development/IDE/Types/Shake.hs | 6 +- hls-graph/src/Development/IDE/Graph.hs | 6 +- .../src/Development/IDE/Graph/Database.hs | 2 +- .../Development/IDE/Graph/Internal/Action.hs | 5 +- .../IDE/Graph/Internal/Database.hs | 18 ++--- .../Development/IDE/Graph/Internal/Profile.hs | 10 +-- .../Development/IDE/Graph/Internal/Types.hs | 65 +++++++++++++++---- 8 files changed, 83 insertions(+), 37 deletions(-) diff --git a/ghcide/src/Development/IDE/Core/Shake.hs b/ghcide/src/Development/IDE/Core/Shake.hs index 6d43d6e43f..f69f2362c5 100644 --- a/ghcide/src/Development/IDE/Core/Shake.hs +++ b/ghcide/src/Development/IDE/Core/Shake.hs @@ -324,7 +324,7 @@ getPluginConfig plugin = do addPersistentRule :: IdeRule k v => k -> (NormalizedFilePath -> IdeAction (Maybe (v,PositionDelta,TextDocumentVersion))) -> Rules () addPersistentRule k getVal = do ShakeExtras{persistentKeys} <- getShakeExtrasRules - void $ liftIO $ atomically $ modifyTVar' persistentKeys $ HMap.insert (Key k) (fmap (fmap (first3 toDyn)) . getVal) + void $ liftIO $ atomically $ modifyTVar' persistentKeys $ HMap.insert (newKey k) (fmap (fmap (first3 toDyn)) . getVal) class Typeable a => IsIdeGlobal a where @@ -399,7 +399,7 @@ lastValueIO s@ShakeExtras{positionMapping,persistentKeys,state} k file = do pmap <- readTVarIO persistentKeys mv <- runMaybeT $ do liftIO $ Logger.logDebug (logger s) $ T.pack $ "LOOKUP PERSISTENT FOR: " ++ show k - f <- MaybeT $ pure $ HMap.lookup (Key k) pmap + f <- MaybeT $ pure $ HMap.lookup (newKey k) pmap (dv,del,ver) <- MaybeT $ runIdeAction "lastValueIO" s $ f file MaybeT $ pure $ (,del,ver) <$> fromDynamic dv case mv of @@ -1068,7 +1068,7 @@ defineEarlyCutoff recorder (Rule op) = addRule $ \(Q (key, file)) (old :: Maybe extras <- getShakeExtras let diagnostics ver diags = do traceDiagnostics diags - updateFileDiagnostics recorder file ver (Key key) extras . map (\(_,y,z) -> (y,z)) $ diags + updateFileDiagnostics recorder file ver (newKey key) extras . map (\(_,y,z) -> (y,z)) $ diags defineEarlyCutoff' diagnostics (==) key file old mode $ const $ op key file defineEarlyCutoff recorder (RuleNoDiagnostics op) = addRule $ \(Q (key, file)) (old :: Maybe BS.ByteString) mode -> otTracedAction key file mode traceA $ \traceDiagnostics -> do let diagnostics _ver diags = do @@ -1087,7 +1087,7 @@ defineEarlyCutoff recorder (RuleWithOldValue op) = addRule $ \(Q (key, file)) (o extras <- getShakeExtras let diagnostics ver diags = do traceDiagnostics diags - updateFileDiagnostics recorder file ver (Key key) extras . map (\(_,y,z) -> (y,z)) $ diags + 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 () diff --git a/ghcide/src/Development/IDE/Types/Shake.hs b/ghcide/src/Development/IDE/Types/Shake.hs index 43298d8a7e..1ebf9e125f 100644 --- a/ghcide/src/Development/IDE/Types/Shake.hs +++ b/ghcide/src/Development/IDE/Types/Shake.hs @@ -26,7 +26,7 @@ import Data.Typeable (cast) import Data.Vector (Vector) import Development.IDE.Core.PositionMapping import Development.IDE.Core.RuleTypes (FileVersion) -import Development.IDE.Graph (Key (..), RuleResult) +import Development.IDE.Graph (Key (..), RuleResult, newKey) import qualified Development.IDE.Graph as Shake import Development.IDE.Types.Diagnostics import Development.IDE.Types.Location @@ -75,7 +75,7 @@ isBadDependency x | otherwise = False toKey :: Shake.ShakeValue k => k -> NormalizedFilePath -> Key -toKey = (Key.) . curry Q +toKey = (newKey.) . curry Q fromKey :: Typeable k => Key -> Maybe (k, NormalizedFilePath) fromKey (Key k) @@ -91,7 +91,7 @@ fromKeyType (Key k) = case typeOf k of _ -> Nothing toNoFileKey :: (Show k, Typeable k, Eq k, Hashable k) => k -> Key -toNoFileKey k = Key $ Q (k, emptyFilePath) +toNoFileKey k = newKey $ Q (k, emptyFilePath) newtype Q k = Q (k, NormalizedFilePath) deriving newtype (Eq, Hashable, NFData) diff --git a/hls-graph/src/Development/IDE/Graph.hs b/hls-graph/src/Development/IDE/Graph.hs index ce0711abaa..aa79a6b949 100644 --- a/hls-graph/src/Development/IDE/Graph.hs +++ b/hls-graph/src/Development/IDE/Graph.hs @@ -1,8 +1,10 @@ +{-# LANGUAGE PatternSynonyms #-} module Development.IDE.Graph( - shakeOptions, + shakeOptions, Rules, Action, action, - Key(..), + Key(.., Key), + newKey, actionFinally, actionBracket, actionCatch, actionFork, -- * Configuration ShakeOptions(shakeAllowRedefineRules, shakeExtra), diff --git a/hls-graph/src/Development/IDE/Graph/Database.hs b/hls-graph/src/Development/IDE/Graph/Database.hs index 1d5aab3789..b84c39fe2f 100644 --- a/hls-graph/src/Development/IDE/Graph/Database.hs +++ b/hls-graph/src/Development/IDE/Graph/Database.hs @@ -79,7 +79,7 @@ shakeGetBuildEdges :: ShakeDatabase -> IO Int shakeGetBuildEdges (ShakeDatabase _ _ db) = do keys <- getDatabaseValues db let ress = mapMaybe (getResult . snd) keys - return $ sum $ map (length . getResultDepsDefault [] . resultDeps) ress + return $ sum $ map (length . getResultDepsDefault mempty . resultDeps) ress -- | Returns an approximation of the database keys, -- annotated with how long ago (in # builds) they were visited diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Action.hs b/hls-graph/src/Development/IDE/Graph/Internal/Action.hs index 708a414ae5..d711834102 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Action.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Action.hs @@ -26,6 +26,7 @@ import Control.Monad.Trans.Class import Control.Monad.Trans.Reader import Data.Foldable (toList) import Data.Functor.Identity +import qualified Data.HashSet as HSet import Data.IORef import Development.IDE.Graph.Classes import Development.IDE.Graph.Internal.Database @@ -39,7 +40,7 @@ type ShakeValue a = (Show a, Typeable a, Eq a, Hashable a, NFData a) alwaysRerun :: Action () alwaysRerun = do ref <- Action $ asks actionDeps - liftIO $ modifyIORef ref (AlwaysRerunDeps [] <>) + liftIO $ modifyIORef ref (AlwaysRerunDeps mempty <>) -- No-op for now reschedule :: Double -> Action () @@ -121,7 +122,7 @@ apply ks = do stack <- Action $ asks actionStack (is, vs) <- liftIO $ build db stack ks ref <- Action $ asks actionDeps - liftIO $ modifyIORef ref (ResultDeps (toList is) <>) + liftIO $ modifyIORef ref (ResultDeps (HSet.fromList $ toList is) <>) pure vs -- | Evaluate a list of keys without recording any dependencies. diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Database.hs b/hls-graph/src/Development/IDE/Graph/Internal/Database.hs index 5bb4ed9ff4..af8b6ea1d5 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Database.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Database.hs @@ -9,6 +9,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE ViewPatterns #-} module Development.IDE.Graph.Internal.Database (newDatabase, incDatabase, build, getDirtySet, getKeysAndVisitAge) where @@ -87,7 +88,7 @@ build -- build _ st k | traceShow ("build", st, k) False = undefined build db stack keys = do built <- runAIO $ do - built <- builder db stack (fmap Key keys) + built <- builder db stack (fmap newKey keys) case built of Left clean -> return clean Right dirty -> liftIO dirty @@ -145,7 +146,7 @@ refresh :: Database -> Stack -> Key -> Maybe Result -> AIO (IO Result) -- refresh _ st k _ | traceShow ("refresh", st, k) False = undefined refresh db stack key result = case (addStack key stack, result) of (Left e, _) -> throw e - (Right stack, Just me@Result{resultDeps = ResultDeps deps}) -> do + (Right stack, Just me@Result{resultDeps = ResultDeps (HSet.toList -> deps)}) -> do res <- builder db stack deps let isDirty = any (\(_,dep) -> resultBuilt me < resultChanged dep) case res of @@ -176,7 +177,7 @@ compute db@Database{..} stack key mode result = do actualDeps = if runChanged /= ChangedNothing then deps else previousDeps previousDeps= maybe UnknownDeps resultDeps result let res = Result runValue built' changed built actualDeps execution runStore - case getResultDepsDefault [] actualDeps of + case getResultDepsDefault mempty actualDeps of deps | not(null deps) && runChanged /= ChangedNothing -> do @@ -186,8 +187,8 @@ compute db@Database{..} stack key mode result = do -- on the next build. void $ updateReverseDeps key db - (getResultDepsDefault [] previousDeps) - (HSet.fromList deps) + (getResultDepsDefault mempty previousDeps) + deps _ -> pure () atomicallyNamed "compute" $ SMap.focus (updateStatus $ Clean res) key databaseValues pure res @@ -235,14 +236,13 @@ splitIO act = do updateReverseDeps :: Key -- ^ Id -> Database - -> [Key] -- ^ Previous direct dependencies of Id + -> HashSet Key -- ^ Previous direct dependencies of Id -> HashSet Key -- ^ Current direct dependencies of Id -> IO () -- mask to ensure that all the reverse dependencies are updated updateReverseDeps myId db prev new = do - forM_ prev $ \d -> - unless (d `HSet.member` new) $ - doOne (HSet.delete myId) d + forM_ (HSet.toList $ prev `HSet.difference` new) $ \d -> + doOne (HSet.delete myId) d forM_ (HSet.toList new) $ doOne (HSet.insert myId) where diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Profile.hs b/hls-graph/src/Development/IDE/Graph/Internal/Profile.hs index 0823070216..4f2a3d4118 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Profile.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Profile.hs @@ -60,7 +60,7 @@ data ProfileEntry = ProfileEntry -- resultsOnly :: Map.HashMap Id (Key, Status) -> Map.HashMap Id (Key, Result (Either BS.ByteString Value)) resultsOnly :: [(Key, Status)] -> Map.HashMap Key Result resultsOnly mp = Map.map (\r -> - r{resultDeps = mapResultDeps (filter (isJust . flip Map.lookup keep)) $ resultDeps r} + r{resultDeps = mapResultDeps (Set.filter (isJust . flip Map.lookup keep)) $ resultDeps r} ) keep where keep = Map.fromList $ mapMaybe (traverse getResult) mp @@ -103,7 +103,7 @@ dependencyOrder shw status = prepareForDependencyOrder :: Database -> IO (HashMap Key Result) prepareForDependencyOrder db = do current <- readTVarIO $ databaseStep db - Map.insert (Key "alwaysRerun") (alwaysRerunResult current) . resultsOnly + Map.insert (newKey "alwaysRerun") (alwaysRerunResult current) . resultsOnly <$> getDatabaseValues db -- | Returns a list of profile entries, and a mapping linking a non-error Id to its profile entry @@ -111,7 +111,7 @@ toReport :: Database -> IO ([ProfileEntry], HashMap Key Int) toReport db = do status <- prepareForDependencyOrder db let order = dependencyOrder show - $ map (second (getResultDepsDefault [Key "alwaysRerun"] . resultDeps)) + $ map (second (Set.toList . getResultDepsDefault (Set.singleton $ newKey "alwaysRerun") . resultDeps)) $ Map.toList status ids = Map.fromList $ zip order [0..] @@ -124,14 +124,14 @@ toReport db = do ,prfBuilt = fromStep resultBuilt ,prfVisited = fromStep resultVisited ,prfChanged = fromStep resultChanged - ,prfDepends = map pure $ mapMaybe (`Map.lookup` ids) $ getResultDepsDefault [Key "alwaysRerun"] resultDeps + ,prfDepends = map pure $ Map.elems $ Map.intersectionWith const ids $ Set.toMap $ getResultDepsDefault (Set.singleton $ newKey "alwaysRerun") resultDeps ,prfExecution = resultExecution } where fromStep i = fromJust $ Map.lookup i steps pure ([maybe (error "toReport") (f i) $ Map.lookup i status | i <- order], ids) alwaysRerunResult :: Step -> Result -alwaysRerunResult current = Result (Value $ toDyn "") (Step 0) (Step 0) current (ResultDeps []) 0 mempty +alwaysRerunResult current = Result (Value $ toDyn "") (Step 0) (Step 0) current (ResultDeps mempty) 0 mempty generateHTML :: Maybe [Int] -> [ProfileEntry] -> IO LBS.ByteString generateHTML dirtyKeys xs = do diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Types.hs b/hls-graph/src/Development/IDE/Graph/Internal/Types.hs index 4edcae9ebc..300ec72efe 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Types.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Types.hs @@ -7,6 +7,9 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE ViewPatterns #-} module Development.IDE.Graph.Internal.Types where @@ -20,6 +23,7 @@ import qualified Data.ByteString as BS import Data.Dynamic import qualified Data.HashMap.Strict as Map import Data.HashSet (HashSet, member) +import qualified Data.IntMap as IM import qualified Data.HashSet as Set import Data.IORef import Data.List (intercalate) @@ -32,6 +36,7 @@ import qualified ListT import qualified StmContainers.Map as SMap import StmContainers.Map (Map) import System.Time.Extra (Seconds) +import System.IO.Unsafe import UnliftIO (MonadUnliftIO) @@ -78,16 +83,54 @@ data ShakeDatabase = ShakeDatabase !Int [Action ()] Database newtype Step = Step Int deriving newtype (Eq,Ord,Hashable) -data Key = forall a . (Typeable a, Eq a, Hashable a, Show a) => Key a +--------------------------------------------------------------------- +-- Keys -instance Eq Key where - Key a == Key b = Just a == cast b +data KeyValue = forall a . (Typeable a, Hashable a, Show a) => KeyValue a -instance Hashable Key where - hashWithSalt i (Key x) = hashWithSalt i (typeOf x, x) +newtype Key = UnsafeMkKey Int + +pattern Key a <- (lookupKeyValue -> KeyValue a) + +data KeyMap = KeyMap !(Map.HashMap KeyValue Key) !(IM.IntMap KeyValue) {-# UNPACK #-} !Int + +keyMap :: IORef KeyMap +keyMap = unsafePerformIO $ newIORef (KeyMap Map.empty IM.empty 0) + +{-# NOINLINE keyMap #-} +newKey :: (Typeable a, Hashable a, Show a) => a -> Key +newKey k = unsafePerformIO $ do + let !newKey = KeyValue k + atomicModifyIORef' keyMap $ \km@(KeyMap hm im n) -> + let new_key = Map.lookup newKey hm + in case new_key of + Just v -> (km, v) + Nothing -> + let !new_index = UnsafeMkKey n + in (KeyMap (Map.insert newKey new_index hm) (IM.insert n newKey im) (n+1), new_index) +{-# NOINLINE newKey #-} + +lookupKeyValue :: Key -> KeyValue +lookupKeyValue (UnsafeMkKey x) = unsafePerformIO $ do + KeyMap _ im _ <- readIORef keyMap + pure $! fromJust (IM.lookup x im) + +{-# NOINLINE lookupKeyValue #-} + +instance Eq Key where + UnsafeMkKey a == UnsafeMkKey b = a == b +instance Hashable Key where + hashWithSalt i (UnsafeMkKey x) = hashWithSalt i x instance Show Key where - show (Key x) = show x + show (Key x) = show x + +instance Eq KeyValue where + KeyValue a == KeyValue b = Just a == cast b +instance Hashable KeyValue where + hashWithSalt i (KeyValue x) = hashWithSalt i (typeOf x, x) +instance Show KeyValue where + show (KeyValue x) = show x newtype Value = Value Dynamic @@ -143,15 +186,15 @@ data Result = Result { resultData :: !BS.ByteString } -data ResultDeps = UnknownDeps | AlwaysRerunDeps ![Key] | ResultDeps ![Key] +data ResultDeps = UnknownDeps | AlwaysRerunDeps !(HashSet Key) | ResultDeps !(HashSet Key) deriving (Eq, Show) -getResultDepsDefault :: [Key] -> ResultDeps -> [Key] +getResultDepsDefault :: (HashSet Key) -> ResultDeps -> (HashSet Key) getResultDepsDefault _ (ResultDeps ids) = ids getResultDepsDefault _ (AlwaysRerunDeps ids) = ids getResultDepsDefault def UnknownDeps = def -mapResultDeps :: ([Key] -> [Key]) -> ResultDeps -> ResultDeps +mapResultDeps :: (HashSet Key -> HashSet Key) -> ResultDeps -> ResultDeps mapResultDeps f (ResultDeps ids) = ResultDeps $ f ids mapResultDeps f (AlwaysRerunDeps ids) = AlwaysRerunDeps $ f ids mapResultDeps _ UnknownDeps = UnknownDeps @@ -159,8 +202,8 @@ mapResultDeps _ UnknownDeps = UnknownDeps instance Semigroup ResultDeps where UnknownDeps <> x = x x <> UnknownDeps = x - AlwaysRerunDeps ids <> x = AlwaysRerunDeps (ids <> getResultDepsDefault [] x) - x <> AlwaysRerunDeps ids = AlwaysRerunDeps (getResultDepsDefault [] x <> ids) + AlwaysRerunDeps ids <> x = AlwaysRerunDeps (ids <> getResultDepsDefault mempty x) + x <> AlwaysRerunDeps ids = AlwaysRerunDeps (getResultDepsDefault mempty x <> ids) ResultDeps ids <> ResultDeps ids' = ResultDeps (ids <> ids') instance Monoid ResultDeps where From eafda04529b626115bd912acfe9c32fc3edc0aa0 Mon Sep 17 00:00:00 2001 From: wz1000 Date: Wed, 21 Sep 2022 21:56:05 +0530 Subject: [PATCH 173/213] Update hls-graph/src/Development/IDE/Graph/Internal/Types.hs Co-authored-by: Pepe Iborra --- hls-graph/src/Development/IDE/Graph/Internal/Types.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Types.hs b/hls-graph/src/Development/IDE/Graph/Internal/Types.hs index 300ec72efe..fc007bb517 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Types.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Types.hs @@ -114,7 +114,7 @@ newKey k = unsafePerformIO $ do lookupKeyValue :: Key -> KeyValue lookupKeyValue (UnsafeMkKey x) = unsafePerformIO $ do KeyMap _ im _ <- readIORef keyMap - pure $! fromJust (IM.lookup x im) + pure $! im IM.! x {-# NOINLINE lookupKeyValue #-} From ada1b3dc130ab93e81b317c988a6d987141190d9 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Wed, 28 Sep 2022 15:21:38 +0530 Subject: [PATCH 174/213] hls-graph - avoid duplicating key texts --- ghcide/src/Development/IDE/Core/Shake.hs | 2 +- hls-graph/hls-graph.cabal | 1 + hls-graph/src/Development/IDE/Graph.hs | 2 +- .../src/Development/IDE/Graph/Internal/Types.hs | 17 +++++++++++------ 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/ghcide/src/Development/IDE/Core/Shake.hs b/ghcide/src/Development/IDE/Core/Shake.hs index f69f2362c5..b4c3c53572 100644 --- a/ghcide/src/Development/IDE/Core/Shake.hs +++ b/ghcide/src/Development/IDE/Core/Shake.hs @@ -1207,7 +1207,7 @@ updateFileDiagnostics recorder fp ver k ShakeExtras{diagnostics, hiddenDiagnosti addTagUnsafe :: String -> String -> String -> a -> a addTagUnsafe msg t x v = unsafePerformIO(addTag (msg <> t) x) `seq` v update :: (forall a. String -> String -> a -> a) -> [Diagnostic] -> STMDiagnosticStore -> STM [Diagnostic] - update addTagUnsafe new store = addTagUnsafe "count" (show $ Prelude.length new) $ setStageDiagnostics addTagUnsafe uri ver (T.pack $ show k) new store + update addTagUnsafe new store = addTagUnsafe "count" (show $ Prelude.length new) $ setStageDiagnostics addTagUnsafe uri ver (renderKey k) new store addTag "version" (show ver) mask_ $ do -- Mask async exceptions to ensure that updated diagnostics are always diff --git a/hls-graph/hls-graph.cabal b/hls-graph/hls-graph.cabal index 10c0cc4b32..77fe7dbb59 100644 --- a/hls-graph/hls-graph.cabal +++ b/hls-graph/hls-graph.cabal @@ -82,6 +82,7 @@ library , transformers , unliftio , unordered-containers + , text if flag(embed-files) cpp-options: -DFILE_EMBED diff --git a/hls-graph/src/Development/IDE/Graph.hs b/hls-graph/src/Development/IDE/Graph.hs index aa79a6b949..88167f898d 100644 --- a/hls-graph/src/Development/IDE/Graph.hs +++ b/hls-graph/src/Development/IDE/Graph.hs @@ -4,7 +4,7 @@ module Development.IDE.Graph( Rules, Action, action, Key(.., Key), - newKey, + newKey, renderKey, actionFinally, actionBracket, actionCatch, actionFork, -- * Configuration ShakeOptions(shakeAllowRedefineRules, shakeExtra), diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Types.hs b/hls-graph/src/Development/IDE/Graph/Internal/Types.hs index fc007bb517..a568281a32 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Types.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Types.hs @@ -25,6 +25,8 @@ import qualified Data.HashMap.Strict as Map import Data.HashSet (HashSet, member) import qualified Data.IntMap as IM import qualified Data.HashSet as Set +import qualified Data.Text as T +import Data.Text (Text) import Data.IORef import Data.List (intercalate) import Data.Maybe @@ -86,11 +88,11 @@ newtype Step = Step Int --------------------------------------------------------------------- -- Keys -data KeyValue = forall a . (Typeable a, Hashable a, Show a) => KeyValue a +data KeyValue = forall a . (Typeable a, Hashable a, Show a) => KeyValue a Text newtype Key = UnsafeMkKey Int -pattern Key a <- (lookupKeyValue -> KeyValue a) +pattern Key a <- (lookupKeyValue -> KeyValue a _) data KeyMap = KeyMap !(Map.HashMap KeyValue Key) !(IM.IntMap KeyValue) {-# UNPACK #-} !Int @@ -101,7 +103,7 @@ keyMap = unsafePerformIO $ newIORef (KeyMap Map.empty IM.empty 0) newKey :: (Typeable a, Hashable a, Show a) => a -> Key newKey k = unsafePerformIO $ do - let !newKey = KeyValue k + let !newKey = KeyValue k (T.pack (show k)) atomicModifyIORef' keyMap $ \km@(KeyMap hm im n) -> let new_key = Map.lookup newKey hm in case new_key of @@ -126,11 +128,14 @@ instance Show Key where show (Key x) = show x instance Eq KeyValue where - KeyValue a == KeyValue b = Just a == cast b + KeyValue a _ == KeyValue b _ = Just a == cast b instance Hashable KeyValue where - hashWithSalt i (KeyValue x) = hashWithSalt i (typeOf x, x) + hashWithSalt i (KeyValue x _) = hashWithSalt i (typeOf x, x) instance Show KeyValue where - show (KeyValue x) = show x + show (KeyValue x t) = T.unpack t + +renderKey :: Key -> Text +renderKey (lookupKeyValue -> KeyValue _ t) = t newtype Value = Value Dynamic From 3b64a3b1c5ae4c53b3ab6174f78e687626eecee7 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Wed, 28 Sep 2022 16:43:00 +0530 Subject: [PATCH 175/213] Introduce KeyMap and KeySet --- .hlint.yaml | 1 + ghcide/src/Development/IDE/Core/FileStore.hs | 3 +- ghcide/src/Development/IDE/Core/Shake.hs | 28 ++--- hls-graph/hls-graph.cabal | 2 + hls-graph/src/Development/IDE/Graph.hs | 4 + .../src/Development/IDE/Graph/Database.hs | 2 +- .../Development/IDE/Graph/Internal/Action.hs | 3 +- .../IDE/Graph/Internal/Database.hs | 32 +++-- .../Development/IDE/Graph/Internal/Profile.hs | 48 ++++---- .../Development/IDE/Graph/Internal/Types.hs | 116 +++++++++++++++--- hls-graph/src/Development/IDE/Graph/KeyMap.hs | 15 +++ hls-graph/src/Development/IDE/Graph/KeySet.hs | 16 +++ 12 files changed, 189 insertions(+), 81 deletions(-) create mode 100644 hls-graph/src/Development/IDE/Graph/KeyMap.hs create mode 100644 hls-graph/src/Development/IDE/Graph/KeySet.hs diff --git a/.hlint.yaml b/.hlint.yaml index 369bb797f2..a04776b87f 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -59,6 +59,7 @@ - Development.IDE.Graph.Internal.Database - Development.IDE.Graph.Internal.Paths - Development.IDE.Graph.Internal.Profile + - Development.IDE.Graph.Internal.Types - Ide.Types - Test.Hls - Test.Hls.Command diff --git a/ghcide/src/Development/IDE/Core/FileStore.hs b/ghcide/src/Development/IDE/Core/FileStore.hs index 93a9c0a90f..860ad11939 100644 --- a/ghcide/src/Development/IDE/Core/FileStore.hs +++ b/ghcide/src/Development/IDE/Core/FileStore.hs @@ -55,7 +55,6 @@ import qualified Development.IDE.Types.Logger as L import qualified Data.Binary as B import qualified Data.ByteString.Lazy as LBS -import qualified Data.HashSet as HSet import Data.List (foldl') import qualified Data.Text as Text import Development.IDE.Core.IdeConfiguration (isWorkspaceFile) @@ -256,7 +255,7 @@ setSomethingModified vfs state keys reason = do atomically $ do writeTQueue (indexQueue $ hiedbWriter $ shakeExtras state) (\withHieDb -> withHieDb deleteMissingRealFiles) modifyTVar' (dirtyKeys $ shakeExtras state) $ \x -> - foldl' (flip HSet.insert) x keys + foldl' (flip insertKeySet) x keys void $ restartShakeSession (shakeExtras state) vfs reason [] registerFileWatches :: [String] -> LSP.LspT Config IO Bool diff --git a/ghcide/src/Development/IDE/Core/Shake.hs b/ghcide/src/Development/IDE/Core/Shake.hs index b4c3c53572..5e51fd0fba 100644 --- a/ghcide/src/Development/IDE/Core/Shake.hs +++ b/ghcide/src/Development/IDE/Core/Shake.hs @@ -178,7 +178,7 @@ import System.Time.Extra data Log = LogCreateHieDbExportsMapStart | LogCreateHieDbExportsMapFinish !Int - | LogBuildSessionRestart !String ![DelayedActionInternal] !(HashSet Key) !Seconds !(Maybe FilePath) + | LogBuildSessionRestart !String ![DelayedActionInternal] !(KeySet) !Seconds !(Maybe FilePath) | LogBuildSessionRestartTakingTooLong !Seconds | LogDelayedAction !(DelayedAction ()) !Seconds | LogBuildSessionFinish !(Maybe SomeException) @@ -197,7 +197,7 @@ instance Pretty Log where vcat [ "Restarting build session due to" <+> pretty reason , "Action Queue:" <+> pretty (map actionName actionQueue) - , "Keys:" <+> pretty (map show $ HSet.toList keyBackLog) + , "Keys:" <+> pretty (map show $ toListKeySet keyBackLog) , "Aborting previous build session took" <+> pretty (showDuration abortDuration) <+> pretty shakeProfilePath ] LogBuildSessionRestartTakingTooLong seconds -> "Build restart is taking too long (" <> pretty seconds <> " seconds)" @@ -279,7 +279,7 @@ data ShakeExtras = ShakeExtras ,clientCapabilities :: ClientCapabilities , withHieDb :: WithHieDb -- ^ Use only to read. , hiedbWriter :: HieDbWriter -- ^ use to write - , persistentKeys :: TVar (HMap.HashMap Key GetStalePersistent) + , persistentKeys :: TVar (KeyMap GetStalePersistent) -- ^ Registery for functions that compute/get "stale" results for the rule -- (possibly from disk) , vfsVar :: TVar VFS @@ -290,7 +290,7 @@ data ShakeExtras = ShakeExtras -- We don't need a STM.Map because we never update individual keys ourselves. , defaultConfig :: Config -- ^ Default HLS config, only relevant if the client does not provide any Config - , dirtyKeys :: TVar (HashSet Key) + , dirtyKeys :: TVar KeySet -- ^ Set of dirty rule keys since the last Shake run } @@ -324,7 +324,7 @@ getPluginConfig plugin = do addPersistentRule :: IdeRule k v => k -> (NormalizedFilePath -> IdeAction (Maybe (v,PositionDelta,TextDocumentVersion))) -> Rules () addPersistentRule k getVal = do ShakeExtras{persistentKeys} <- getShakeExtrasRules - void $ liftIO $ atomically $ modifyTVar' persistentKeys $ HMap.insert (newKey k) (fmap (fmap (first3 toDyn)) . getVal) + void $ liftIO $ atomically $ modifyTVar' persistentKeys $ insertKeyMap (newKey k) (fmap (fmap (first3 toDyn)) . getVal) class Typeable a => IsIdeGlobal a where @@ -399,7 +399,7 @@ lastValueIO s@ShakeExtras{positionMapping,persistentKeys,state} k file = do pmap <- readTVarIO persistentKeys mv <- runMaybeT $ do liftIO $ Logger.logDebug (logger s) $ T.pack $ "LOOKUP PERSISTENT FOR: " ++ show k - f <- MaybeT $ pure $ HMap.lookup (newKey k) pmap + f <- MaybeT $ pure $ lookupKeyMap (newKey k) pmap (dv,del,ver) <- MaybeT $ runIdeAction "lastValueIO" s $ f file MaybeT $ pure $ (,del,ver) <$> fromDynamic dv case mv of @@ -509,7 +509,7 @@ deleteValue -> STM () deleteValue ShakeExtras{dirtyKeys, state} key file = do STM.delete (toKey key file) state - modifyTVar' dirtyKeys $ HSet.insert (toKey key file) + modifyTVar' dirtyKeys $ insertKeySet (toKey key file) recordDirtyKeys :: Shake.ShakeValue k @@ -518,7 +518,7 @@ recordDirtyKeys -> [NormalizedFilePath] -> STM (IO ()) recordDirtyKeys ShakeExtras{dirtyKeys} key file = do - modifyTVar' dirtyKeys $ \x -> foldl' (flip HSet.insert) x (toKey key <$> file) + modifyTVar' dirtyKeys $ \x -> foldl' (flip insertKeySet) x (toKey key <$> file) return $ withEventTrace "recordDirtyKeys" $ \addEvent -> do addEvent (fromString $ unlines $ "dirty " <> show key : map fromNormalizedFilePath file) @@ -594,7 +594,7 @@ shakeOpen recorder lspEnv defaultConfig idePlugins logger debouncer positionMapping <- STM.newIO knownTargetsVar <- newTVarIO $ hashed HMap.empty let restartShakeSession = shakeRestart recorder ideState - persistentKeys <- newTVarIO HMap.empty + persistentKeys <- newTVarIO mempty indexPending <- newTVarIO HMap.empty indexCompleted <- newTVarIO 0 indexProgressToken <- newVar Nothing @@ -637,7 +637,7 @@ shakeOpen recorder lspEnv defaultConfig idePlugins logger debouncer -- monitoring let readValuesCounter = fromIntegral . countRelevantKeys checkParents <$> getStateKeys shakeExtras - readDirtyKeys = fromIntegral . countRelevantKeys checkParents . HSet.toList <$> readTVarIO(dirtyKeys shakeExtras) + readDirtyKeys = fromIntegral . countRelevantKeys checkParents . toListKeySet <$> readTVarIO(dirtyKeys shakeExtras) readIndexPending = fromIntegral . HMap.size <$> readTVarIO (indexPending $ hiedbWriter shakeExtras) readExportsMap = fromIntegral . HMap.size . getExportsMap <$> readTVarIO (exportsMap shakeExtras) readDatabaseCount = fromIntegral . countRelevantKeys checkParents . map fst <$> shakeGetDatabaseKeys shakeDb @@ -797,10 +797,10 @@ newSession recorder extras@ShakeExtras{..} vfsMod shakeDb acts reason = do workRun restore = withSpan "Shake session" $ \otSpan -> do setTag otSpan "reason" (fromString reason) setTag otSpan "queue" (fromString $ unlines $ map actionName reenqueued) - whenJust allPendingKeys $ \kk -> setTag otSpan "keys" (BS8.pack $ unlines $ map show $ toList kk) + whenJust allPendingKeys $ \kk -> setTag otSpan "keys" (BS8.pack $ unlines $ map show $ toListKeySet kk) let keysActs = pumpActionThread otSpan : map (run otSpan) (reenqueued ++ acts) res <- try @SomeException $ - restore $ shakeRunDatabaseForKeys (HSet.toList <$> allPendingKeys) shakeDb keysActs + restore $ shakeRunDatabaseForKeys (toListKeySet <$> allPendingKeys) shakeDb keysActs return $ do let exception = case res of @@ -890,7 +890,7 @@ garbageCollectKeys label maxAge checkParents agedKeys = do = atomicallyNamed "GC" $ do gotIt <- STM.focus (Focus.member <* Focus.delete) k values when gotIt $ - modifyTVar' dk (HSet.insert k) + modifyTVar' dk (insertKeySet k) return $ if gotIt then (counter+1, k:keys) else st | otherwise = pure st @@ -1160,7 +1160,7 @@ defineEarlyCutoff' doDiagnostics cmp key file old mode action = do (if eq then ChangedRecomputeSame else ChangedRecomputeDiff) (encodeShakeValue bs) $ A res - liftIO $ atomicallyNamed "define - dirtyKeys" $ modifyTVar' dirtyKeys (HSet.delete $ toKey key file) + liftIO $ atomicallyNamed "define - dirtyKeys" $ modifyTVar' dirtyKeys (deleteKeySet $ toKey key file) return res where -- Highly unsafe helper to compute the version of a file diff --git a/hls-graph/hls-graph.cabal b/hls-graph/hls-graph.cabal index 77fe7dbb59..4897ae77b4 100644 --- a/hls-graph/hls-graph.cabal +++ b/hls-graph/hls-graph.cabal @@ -47,6 +47,8 @@ library Development.IDE.Graph.Classes Development.IDE.Graph.Database Development.IDE.Graph.Rule + Development.IDE.Graph.KeyMap + Development.IDE.Graph.KeySet Development.IDE.Graph.Internal.Action Development.IDE.Graph.Internal.Options Development.IDE.Graph.Internal.Rules diff --git a/hls-graph/src/Development/IDE/Graph.hs b/hls-graph/src/Development/IDE/Graph.hs index 88167f898d..98111080a2 100644 --- a/hls-graph/src/Development/IDE/Graph.hs +++ b/hls-graph/src/Development/IDE/Graph.hs @@ -20,9 +20,13 @@ module Development.IDE.Graph( -- * Actions for inspecting the keys in the database getDirtySet, getKeysAndVisitedAge, + module Development.IDE.Graph.KeyMap, + module Development.IDE.Graph.KeySet, ) where import Development.IDE.Graph.Database +import Development.IDE.Graph.KeyMap +import Development.IDE.Graph.KeySet import Development.IDE.Graph.Internal.Action import Development.IDE.Graph.Internal.Options import Development.IDE.Graph.Internal.Rules diff --git a/hls-graph/src/Development/IDE/Graph/Database.hs b/hls-graph/src/Development/IDE/Graph/Database.hs index b84c39fe2f..2bed4a2360 100644 --- a/hls-graph/src/Development/IDE/Graph/Database.hs +++ b/hls-graph/src/Development/IDE/Graph/Database.hs @@ -79,7 +79,7 @@ shakeGetBuildEdges :: ShakeDatabase -> IO Int shakeGetBuildEdges (ShakeDatabase _ _ db) = do keys <- getDatabaseValues db let ress = mapMaybe (getResult . snd) keys - return $ sum $ map (length . getResultDepsDefault mempty . resultDeps) ress + return $ sum $ map (lengthKeySet . getResultDepsDefault mempty . resultDeps) ress -- | Returns an approximation of the database keys, -- annotated with how long ago (in # builds) they were visited diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Action.hs b/hls-graph/src/Development/IDE/Graph/Internal/Action.hs index d711834102..9602f3a10c 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Action.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Action.hs @@ -26,7 +26,6 @@ import Control.Monad.Trans.Class import Control.Monad.Trans.Reader import Data.Foldable (toList) import Data.Functor.Identity -import qualified Data.HashSet as HSet import Data.IORef import Development.IDE.Graph.Classes import Development.IDE.Graph.Internal.Database @@ -122,7 +121,7 @@ apply ks = do stack <- Action $ asks actionStack (is, vs) <- liftIO $ build db stack ks ref <- Action $ asks actionDeps - liftIO $ modifyIORef ref (ResultDeps (HSet.fromList $ toList is) <>) + liftIO $ modifyIORef ref (ResultDeps (fromListKeySet $ toList is) <>) pure vs -- | Evaluate a list of keys without recording any dependencies. diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Database.hs b/hls-graph/src/Development/IDE/Graph/Internal/Database.hs index af8b6ea1d5..2ee8212520 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Database.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Database.hs @@ -30,8 +30,6 @@ import qualified Control.Monad.Trans.State.Strict as State import Data.Dynamic import Data.Either import Data.Foldable (for_, traverse_) -import Data.HashSet (HashSet) -import qualified Data.HashSet as HSet import Data.IORef.Extra import Data.List.NonEmpty (unzip) import Data.Maybe @@ -61,7 +59,7 @@ incDatabase :: Database -> Maybe [Key] -> IO () incDatabase db (Just kk) = do atomicallyNamed "incDatabase" $ modifyTVar' (databaseStep db) $ \(Step i) -> Step $ i + 1 transitiveDirtyKeys <- transitiveDirtySet db kk - for_ transitiveDirtyKeys $ \k -> + for_ (toListKeySet transitiveDirtyKeys) $ \k -> -- Updating all the keys atomically is not necessary -- since we assume that no build is mutating the db. -- Therefore run one transaction per key to minimise contention. @@ -146,7 +144,7 @@ refresh :: Database -> Stack -> Key -> Maybe Result -> AIO (IO Result) -- refresh _ st k _ | traceShow ("refresh", st, k) False = undefined refresh db stack key result = case (addStack key stack, result) of (Left e, _) -> throw e - (Right stack, Just me@Result{resultDeps = ResultDeps (HSet.toList -> deps)}) -> do + (Right stack, Just me@Result{resultDeps = ResultDeps (toListKeySet -> deps)}) -> do res <- builder db stack deps let isDirty = any (\(_,dep) -> resultBuilt me < resultChanged dep) case res of @@ -178,7 +176,7 @@ compute db@Database{..} stack key mode result = do previousDeps= maybe UnknownDeps resultDeps result let res = Result runValue built' changed built actualDeps execution runStore case getResultDepsDefault mempty actualDeps of - deps | not(null deps) + deps | not(nullKeySet deps) && runChanged /= ChangedNothing -> do -- IMPORTANT: record the reverse deps **before** marking the key Clean. @@ -236,15 +234,15 @@ splitIO act = do updateReverseDeps :: Key -- ^ Id -> Database - -> HashSet Key -- ^ Previous direct dependencies of Id - -> HashSet Key -- ^ Current direct dependencies of Id + -> KeySet -- ^ Previous direct dependencies of Id + -> KeySet -- ^ Current direct dependencies of Id -> IO () -- mask to ensure that all the reverse dependencies are updated updateReverseDeps myId db prev new = do - forM_ (HSet.toList $ prev `HSet.difference` new) $ \d -> - doOne (HSet.delete myId) d - forM_ (HSet.toList new) $ - doOne (HSet.insert myId) + forM_ (toListKeySet $ prev `differenceKeySet` new) $ \d -> + doOne (deleteKeySet myId) d + forM_ (toListKeySet new) $ + doOne (insertKeySet myId) where alterRDeps f = Focus.adjust (onKeyReverseDeps f) @@ -254,18 +252,18 @@ updateReverseDeps myId db prev new = do doOne f id = atomicallyNamed "updateReverseDeps" $ SMap.focus (alterRDeps f) id (databaseValues db) -getReverseDependencies :: Database -> Key -> STM (Maybe (HashSet Key)) +getReverseDependencies :: Database -> Key -> STM (Maybe KeySet) getReverseDependencies db = (fmap.fmap) keyReverseDeps . flip SMap.lookup (databaseValues db) -transitiveDirtySet :: Foldable t => Database -> t Key -> IO (HashSet Key) -transitiveDirtySet database = flip State.execStateT HSet.empty . traverse_ loop +transitiveDirtySet :: Foldable t => Database -> t Key -> IO KeySet +transitiveDirtySet database = flip State.execStateT mempty . traverse_ loop where loop x = do seen <- State.get - if x `HSet.member` seen then pure () else do - State.put (HSet.insert x seen) + if x `memberKeySet` seen then pure () else do + State.put (insertKeySet x seen) next <- lift $ atomically $ getReverseDependencies database x - traverse_ loop (maybe mempty HSet.toList next) + traverse_ loop (maybe mempty toListKeySet next) -------------------------------------------------------------------------------- -- Asynchronous computations with cancellation diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Profile.hs b/hls-graph/src/Development/IDE/Graph/Internal/Profile.hs index 4f2a3d4118..d89b8b7a74 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Profile.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Profile.hs @@ -12,9 +12,7 @@ import Data.Bifunctor import qualified Data.ByteString.Lazy.Char8 as LBS import Data.Char import Data.Dynamic (toDyn) -import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as Map -import qualified Data.HashSet as Set import Data.List (dropWhileEnd, foldl', intercalate, partition, sort, @@ -47,8 +45,8 @@ writeProfile :: FilePath -> Database -> IO () writeProfile out db = do (report, mapping) <- toReport db dirtyKeysMapped <- do - dirtyIds <- Set.fromList . fmap fst <$> getDirtySet db - let dirtyKeysMapped = mapMaybe (`Map.lookup` mapping) . Set.toList $ dirtyIds + dirtyIds <- fromListKeySet . fmap fst <$> getDirtySet db + let dirtyKeysMapped = mapMaybe (`lookupKeyMap` mapping) . toListKeySet $ dirtyIds return $ Just $ sort dirtyKeysMapped rpt <- generateHTML dirtyKeysMapped report LBS.writeFile out rpt @@ -58,17 +56,17 @@ data ProfileEntry = ProfileEntry -- | Eliminate all errors from the database, pretending they don't exist -- resultsOnly :: Map.HashMap Id (Key, Status) -> Map.HashMap Id (Key, Result (Either BS.ByteString Value)) -resultsOnly :: [(Key, Status)] -> Map.HashMap Key Result -resultsOnly mp = Map.map (\r -> - r{resultDeps = mapResultDeps (Set.filter (isJust . flip Map.lookup keep)) $ resultDeps r} +resultsOnly :: [(Key, Status)] -> KeyMap Result +resultsOnly mp = mapKeyMap (\r -> + r{resultDeps = mapResultDeps (filterKeySet (isJust . flip lookupKeyMap keep)) $ resultDeps r} ) keep where - keep = Map.fromList $ mapMaybe (traverse getResult) mp + keep = fromListKeyMap $ mapMaybe (traverse getResult) mp -- | Given a map of representing a dependency order (with a show for error messages), find an ordering for the items such -- that no item points to an item before itself. -- Raise an error if you end up with a cycle. -dependencyOrder :: (Eq a, Hashable a) => (a -> String) -> [(a,[a])] -> [a] +-- dependencyOrder :: (Eq a, Hashable a) => (a -> String) -> [(a,[a])] -> [a] -- Algorithm: -- Divide everyone up into those who have no dependencies [Id] -- And those who depend on a particular Id, Dep :-> Maybe [(Key,[Dep])] @@ -78,8 +76,8 @@ dependencyOrder :: (Eq a, Hashable a) => (a -> String) -> [(a,[a])] -> [a] -- k :-> Nothing means the key has already been freed dependencyOrder shw status = f (map fst noDeps) $ - Map.map Just $ - Map.fromListWith (++) + mapKeyMap Just $ + fromListWithKeyMap (++) [(d, [(k,ds)]) | (k,d:ds) <- hasDeps] where (noDeps, hasDeps) = partition (null . snd) status @@ -89,33 +87,33 @@ dependencyOrder shw status = "Internal invariant broken, database seems to be cyclic" : map (" " ++) bad ++ ["... plus " ++ show (length badOverflow) ++ " more ..." | not $ null badOverflow] - where (bad,badOverflow) = splitAt 10 [shw i | (i, Just _) <- Map.toList mp] + where (bad,badOverflow) = splitAt 10 [shw i | (i, Just _) <- toListKeyMap mp] f (x:xs) mp = x : f (now++xs) later - where Just free = Map.lookupDefault (Just []) x mp - (now,later) = foldl' g ([], Map.insert x Nothing mp) free + where Just free = lookupDefaultKeyMap (Just []) x mp + (now,later) = foldl' g ([], insertKeyMap x Nothing mp) free g (free, mp) (k, []) = (k:free, mp) - g (free, mp) (k, d:ds) = case Map.lookupDefault (Just []) d mp of + g (free, mp) (k, d:ds) = case lookupDefaultKeyMap (Just []) d mp of Nothing -> g (free, mp) (k, ds) - Just todo -> (free, Map.insert d (Just $ (k,ds) : todo) mp) + Just todo -> (free, insertKeyMap d (Just $ (k,ds) : todo) mp) -prepareForDependencyOrder :: Database -> IO (HashMap Key Result) +prepareForDependencyOrder :: Database -> IO (KeyMap Result) prepareForDependencyOrder db = do current <- readTVarIO $ databaseStep db - Map.insert (newKey "alwaysRerun") (alwaysRerunResult current) . resultsOnly + insertKeyMap (newKey "alwaysRerun") (alwaysRerunResult current) . resultsOnly <$> getDatabaseValues db -- | Returns a list of profile entries, and a mapping linking a non-error Id to its profile entry -toReport :: Database -> IO ([ProfileEntry], HashMap Key Int) +toReport :: Database -> IO ([ProfileEntry], KeyMap Int) toReport db = do status <- prepareForDependencyOrder db let order = dependencyOrder show - $ map (second (Set.toList . getResultDepsDefault (Set.singleton $ newKey "alwaysRerun") . resultDeps)) - $ Map.toList status - ids = Map.fromList $ zip order [0..] + $ map (second (toListKeySet . getResultDepsDefault (singletonKeySet $ newKey "alwaysRerun") . resultDeps)) + $ toListKeyMap status + ids = fromListKeyMap $ zip order [0..] - steps = let xs = nubOrd $ concat [[resultChanged, resultBuilt, resultVisited] | Result{..} <- Map.elems status] + steps = let xs = nubOrd $ concat [[resultChanged, resultBuilt, resultVisited] | Result{..} <- elemsKeyMap status] in Map.fromList $ zip (sortBy (flip compare) xs) [0..] @@ -124,11 +122,11 @@ toReport db = do ,prfBuilt = fromStep resultBuilt ,prfVisited = fromStep resultVisited ,prfChanged = fromStep resultChanged - ,prfDepends = map pure $ Map.elems $ Map.intersectionWith const ids $ Set.toMap $ getResultDepsDefault (Set.singleton $ newKey "alwaysRerun") resultDeps + ,prfDepends = map pure $ elemsKeyMap $ restrictKeysKeyMap ids $ getResultDepsDefault (singletonKeySet $ newKey "alwaysRerun") resultDeps ,prfExecution = resultExecution } where fromStep i = fromJust $ Map.lookup i steps - pure ([maybe (error "toReport") (f i) $ Map.lookup i status | i <- order], ids) + pure ([maybe (error "toReport") (f i) $ lookupKeyMap i status | i <- order], ids) alwaysRerunResult :: Step -> Result alwaysRerunResult current = Result (Value $ toDyn "") (Step 0) (Step 0) current (ResultDeps mempty) 0 mempty diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Types.hs b/hls-graph/src/Development/IDE/Graph/Internal/Types.hs index a568281a32..c1c4948d97 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Types.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Types.hs @@ -20,11 +20,13 @@ import Control.Monad.Trans.Reader import Data.Aeson (FromJSON, ToJSON) import Data.Bifunctor (second) import qualified Data.ByteString as BS +import Data.Coerce import Data.Dynamic import qualified Data.HashMap.Strict as Map -import Data.HashSet (HashSet, member) -import qualified Data.IntMap as IM -import qualified Data.HashSet as Set +import qualified Data.IntMap.Strict as IM +import Data.IntMap (IntMap) +import qualified Data.IntSet as IS +import Data.IntSet (IntSet) import qualified Data.Text as T import Data.Text (Text) import Data.IORef @@ -88,34 +90,34 @@ newtype Step = Step Int --------------------------------------------------------------------- -- Keys -data KeyValue = forall a . (Typeable a, Hashable a, Show a) => KeyValue a Text +data KeyValue = forall a . (Eq a, Typeable a, Hashable a, Show a) => KeyValue a Text newtype Key = UnsafeMkKey Int pattern Key a <- (lookupKeyValue -> KeyValue a _) -data KeyMap = KeyMap !(Map.HashMap KeyValue Key) !(IM.IntMap KeyValue) {-# UNPACK #-} !Int +data GlobalKeyValueMap = GlobalKeyValueMap !(Map.HashMap KeyValue Key) !(IntMap KeyValue) {-# UNPACK #-} !Int -keyMap :: IORef KeyMap -keyMap = unsafePerformIO $ newIORef (KeyMap Map.empty IM.empty 0) +keyMap :: IORef GlobalKeyValueMap +keyMap = unsafePerformIO $ newIORef (GlobalKeyValueMap Map.empty IM.empty 0) {-# NOINLINE keyMap #-} -newKey :: (Typeable a, Hashable a, Show a) => a -> Key +newKey :: (Eq a, Typeable a, Hashable a, Show a) => a -> Key newKey k = unsafePerformIO $ do let !newKey = KeyValue k (T.pack (show k)) - atomicModifyIORef' keyMap $ \km@(KeyMap hm im n) -> + atomicModifyIORef' keyMap $ \km@(GlobalKeyValueMap hm im n) -> let new_key = Map.lookup newKey hm in case new_key of Just v -> (km, v) Nothing -> let !new_index = UnsafeMkKey n - in (KeyMap (Map.insert newKey new_index hm) (IM.insert n newKey im) (n+1), new_index) + in (GlobalKeyValueMap (Map.insert newKey new_index hm) (IM.insert n newKey im) (n+1), new_index) {-# NOINLINE newKey #-} lookupKeyValue :: Key -> KeyValue lookupKeyValue (UnsafeMkKey x) = unsafePerformIO $ do - KeyMap _ im _ <- readIORef keyMap + GlobalKeyValueMap _ im _ <- readIORef keyMap pure $! im IM.! x {-# NOINLINE lookupKeyValue #-} @@ -137,14 +139,88 @@ instance Show KeyValue where renderKey :: Key -> Text renderKey (lookupKeyValue -> KeyValue _ t) = t +newtype KeySet = KeySet IntSet + deriving (Eq, Ord, Semigroup, Monoid) + +instance Show KeySet where + showsPrec p (KeySet is)= showParen (p > 10) $ + showString "fromList " . shows ks + where ks = coerce (IS.toList is) :: [Key] + +insertKeySet :: Key -> KeySet -> KeySet +insertKeySet = coerce IS.insert + +memberKeySet :: Key -> KeySet -> Bool +memberKeySet = coerce IS.member + +toListKeySet :: KeySet -> [Key] +toListKeySet = coerce IS.toList + +nullKeySet :: KeySet -> Bool +nullKeySet = coerce IS.null + +differenceKeySet :: KeySet -> KeySet -> KeySet +differenceKeySet = coerce IS.difference + +deleteKeySet :: Key -> KeySet -> KeySet +deleteKeySet = coerce IS.delete + +fromListKeySet :: [Key] -> KeySet +fromListKeySet = coerce IS.fromList + +singletonKeySet :: Key -> KeySet +singletonKeySet = coerce IS.singleton + +filterKeySet :: (Key -> Bool) -> KeySet -> KeySet +filterKeySet = coerce IS.filter + +lengthKeySet :: KeySet -> Int +lengthKeySet = coerce IS.size + +newtype KeyMap a = KeyMap (IntMap a) + deriving (Eq, Ord, Semigroup, Monoid) + +instance Show a => Show (KeyMap a) where + showsPrec p (KeyMap im)= showParen (p > 10) $ + showString "fromList " . shows ks + where ks = coerce (IM.toList im) :: [(Key,a)] + +mapKeyMap :: (a -> b) -> KeyMap a -> KeyMap b +mapKeyMap f (KeyMap m) = KeyMap (IM.map f m) + +insertKeyMap :: Key -> a -> KeyMap a -> KeyMap a +insertKeyMap (UnsafeMkKey k) v (KeyMap m) = KeyMap (IM.insert k v m) + +lookupKeyMap :: Key -> KeyMap a -> Maybe a +lookupKeyMap (UnsafeMkKey k) (KeyMap m) = IM.lookup k m + +lookupDefaultKeyMap :: a -> Key -> KeyMap a -> a +lookupDefaultKeyMap a (UnsafeMkKey k) (KeyMap m) = IM.findWithDefault a k m + +fromListKeyMap :: [(Key,a)] -> KeyMap a +fromListKeyMap xs = KeyMap (IM.fromList (coerce xs)) + +fromListWithKeyMap :: (a -> a -> a) -> [(Key,a)] -> KeyMap a +fromListWithKeyMap f xs = KeyMap (IM.fromListWith f (coerce xs)) + +toListKeyMap :: KeyMap a -> [(Key,a)] +toListKeyMap (KeyMap m) = coerce (IM.toList m) + +elemsKeyMap :: KeyMap a -> [a] +elemsKeyMap (KeyMap m) = IM.elems m + +restrictKeysKeyMap :: KeyMap a -> KeySet -> KeyMap a +restrictKeysKeyMap (KeyMap m) (KeySet s) = KeyMap (IM.restrictKeys m s) + + newtype Value = Value Dynamic data KeyDetails = KeyDetails { keyStatus :: !Status, - keyReverseDeps :: !(HashSet Key) + keyReverseDeps :: !KeySet } -onKeyReverseDeps :: (HashSet Key -> HashSet Key) -> KeyDetails -> KeyDetails +onKeyReverseDeps :: (KeySet -> KeySet) -> KeyDetails -> KeyDetails onKeyReverseDeps f it@KeyDetails{..} = it{keyReverseDeps = f keyReverseDeps} @@ -191,15 +267,15 @@ data Result = Result { resultData :: !BS.ByteString } -data ResultDeps = UnknownDeps | AlwaysRerunDeps !(HashSet Key) | ResultDeps !(HashSet Key) +data ResultDeps = UnknownDeps | AlwaysRerunDeps !KeySet | ResultDeps !KeySet deriving (Eq, Show) -getResultDepsDefault :: (HashSet Key) -> ResultDeps -> (HashSet Key) +getResultDepsDefault :: KeySet -> ResultDeps -> KeySet getResultDepsDefault _ (ResultDeps ids) = ids getResultDepsDefault _ (AlwaysRerunDeps ids) = ids getResultDepsDefault def UnknownDeps = def -mapResultDeps :: (HashSet Key -> HashSet Key) -> ResultDeps -> ResultDeps +mapResultDeps :: (KeySet -> KeySet) -> ResultDeps -> ResultDeps mapResultDeps f (ResultDeps ids) = ResultDeps $ f ids mapResultDeps f (AlwaysRerunDeps ids) = AlwaysRerunDeps $ f ids mapResultDeps _ UnknownDeps = UnknownDeps @@ -273,7 +349,7 @@ fromGraphException x = do --------------------------------------------------------------------- -- CALL STACK -data Stack = Stack [Key] !(HashSet Key) +data Stack = Stack [Key] !KeySet instance Show Stack where show (Stack kk _) = "Stack: " <> intercalate " -> " (map show kk) @@ -288,12 +364,12 @@ instance Exception StackException where addStack :: Key -> Stack -> Either StackException Stack addStack k (Stack ks is) - | k `member` is = Left $ StackException stack2 + | k `memberKeySet` is = Left $ StackException stack2 | otherwise = Right stack2 - where stack2 = Stack (k:ks) (Set.insert k is) + where stack2 = Stack (k:ks) (insertKeySet k is) memberStack :: Key -> Stack -> Bool -memberStack k (Stack _ ks) = k `member` ks +memberStack k (Stack _ ks) = k `memberKeySet` ks emptyStack :: Stack emptyStack = Stack [] mempty diff --git a/hls-graph/src/Development/IDE/Graph/KeyMap.hs b/hls-graph/src/Development/IDE/Graph/KeyMap.hs new file mode 100644 index 0000000000..daa1ae8642 --- /dev/null +++ b/hls-graph/src/Development/IDE/Graph/KeyMap.hs @@ -0,0 +1,15 @@ +module Development.IDE.Graph.KeyMap( + Key, + KeyMap, + mapKeyMap, + insertKeyMap, + lookupKeyMap, + lookupDefaultKeyMap, + fromListKeyMap, + fromListWithKeyMap, + toListKeyMap, + elemsKeyMap, + restrictKeysKeyMap, + ) where + +import Development.IDE.Graph.Internal.Types diff --git a/hls-graph/src/Development/IDE/Graph/KeySet.hs b/hls-graph/src/Development/IDE/Graph/KeySet.hs new file mode 100644 index 0000000000..ef8c46e6b5 --- /dev/null +++ b/hls-graph/src/Development/IDE/Graph/KeySet.hs @@ -0,0 +1,16 @@ +module Development.IDE.Graph.KeySet( + Key, + KeySet, + insertKeySet, + memberKeySet, + toListKeySet, + nullKeySet, + differenceKeySet, + deleteKeySet, + fromListKeySet, + singletonKeySet, + filterKeySet, + lengthKeySet, + ) where + +import Development.IDE.Graph.Internal.Types From 94cd24d9e43a0f93f947075bb5a931b5d92a9f61 Mon Sep 17 00:00:00 2001 From: Zubin Duggal Date: Wed, 19 Oct 2022 18:00:29 +0530 Subject: [PATCH 176/213] Fix testing hls-graph --- hls-graph/src/Development/IDE/Graph/Internal/Types.hs | 4 ++-- hls-graph/test/ActionSpec.hs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hls-graph/src/Development/IDE/Graph/Internal/Types.hs b/hls-graph/src/Development/IDE/Graph/Internal/Types.hs index c1c4948d97..8451f641a3 100644 --- a/hls-graph/src/Development/IDE/Graph/Internal/Types.hs +++ b/hls-graph/src/Development/IDE/Graph/Internal/Types.hs @@ -140,7 +140,7 @@ renderKey :: Key -> Text renderKey (lookupKeyValue -> KeyValue _ t) = t newtype KeySet = KeySet IntSet - deriving (Eq, Ord, Semigroup, Monoid) + deriving newtype (Eq, Ord, Semigroup, Monoid) instance Show KeySet where showsPrec p (KeySet is)= showParen (p > 10) $ @@ -178,7 +178,7 @@ lengthKeySet :: KeySet -> Int lengthKeySet = coerce IS.size newtype KeyMap a = KeyMap (IntMap a) - deriving (Eq, Ord, Semigroup, Monoid) + deriving newtype (Eq, Ord, Semigroup, Monoid) instance Show a => Show (KeyMap a) where showsPrec p (KeyMap im)= showParen (p > 10) $ diff --git a/hls-graph/test/ActionSpec.hs b/hls-graph/test/ActionSpec.hs index d79e6edb40..171e90214b 100644 --- a/hls-graph/test/ActionSpec.hs +++ b/hls-graph/test/ActionSpec.hs @@ -43,8 +43,8 @@ spec = do pure $ do apply1 theKey res `shouldBe` [True] - Just (Clean res) <- lookup (Key theKey) <$> getDatabaseValues theDb - resultDeps res `shouldBe` ResultDeps [Key (Rule @())] + Just (Clean res) <- lookup (newKey theKey) <$> getDatabaseValues theDb + resultDeps res `shouldBe` ResultDeps (singletonKeySet $ newKey (Rule @())) it "tracks reverse dependencies" $ do db@(ShakeDatabase _ _ Database {..}) <- shakeNewDatabase shakeOptions $ do ruleUnit @@ -54,8 +54,8 @@ spec = do pure $ do apply1 theKey res `shouldBe` [True] - Just KeyDetails {..} <- atomically $ STM.lookup (Key (Rule @())) databaseValues - keyReverseDeps `shouldBe` HashSet.fromList [Key theKey] + Just KeyDetails {..} <- atomically $ STM.lookup (newKey (Rule @())) databaseValues + keyReverseDeps `shouldBe` (singletonKeySet $ newKey theKey) it "rethrows exceptions" $ do db <- shakeNewDatabase shakeOptions $ do addRule $ \(Rule :: Rule ()) old mode -> error "boom" @@ -74,5 +74,5 @@ spec = do pure $ do applyWithoutDependency [theKey] res `shouldBe` [[True]] - Just (Clean res) <- lookup (Key theKey) <$> getDatabaseValues theDb + Just (Clean res) <- lookup (newKey theKey) <$> getDatabaseValues theDb resultDeps res `shouldBe` UnknownDeps From a913f47fc583ad780cd06b0c5099caaf33573f7f Mon Sep 17 00:00:00 2001 From: Kobayashi Date: Tue, 25 Oct 2022 00:34:04 +0800 Subject: [PATCH 177/213] support haddock-library 1.11 (#3303) * support haddock-library 1.11 * add test case --- ghcide/ghcide.cabal | 2 +- ghcide/src/Development/IDE/Spans/Common.hs | 9 +++++-- ghcide/test/exe/Main.hs | 28 ++++++++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index 2b3ac1dbba..5d0b50b921 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -62,7 +62,7 @@ library focus, ghc-trace-events, Glob, - haddock-library >= 1.8 && < 1.11, + haddock-library >= 1.8 && < 1.12, hashable, hie-compat ^>= 0.3.0.0, hls-plugin-api ^>= 1.5, diff --git a/ghcide/src/Development/IDE/Spans/Common.hs b/ghcide/src/Development/IDE/Spans/Common.hs index 00b98ded2a..9ae0665998 100644 --- a/ghcide/src/Development/IDE/Spans/Common.hs +++ b/ghcide/src/Development/IDE/Spans/Common.hs @@ -23,6 +23,7 @@ import GHC.Generics import GHC +import Data.Bifunctor (second) import Development.IDE.GHC.Compat import Development.IDE.GHC.Orphans () import Development.IDE.GHC.Util @@ -179,8 +180,12 @@ haddockToMarkdown (H.DocHeader (H.Header level title)) haddockToMarkdown (H.DocUnorderedList things) = '\n' : (unlines $ map (("+ " ++) . trimStart . splitForList . haddockToMarkdown) things) -haddockToMarkdown (H.DocOrderedList things) - = '\n' : (unlines $ map (("1. " ++) . trimStart . splitForList . haddockToMarkdown) things) +haddockToMarkdown (H.DocOrderedList things) = +#if MIN_VERSION_haddock_library(1,11,0) + '\n' : (unlines $ map ((\(num, str) -> show num ++ ". " ++ str) . second (trimStart . splitForList . haddockToMarkdown)) things) +#else + '\n' : (unlines $ map (("1. " ++) . trimStart . splitForList . haddockToMarkdown) things) +#endif haddockToMarkdown (H.DocDefList things) = '\n' : (unlines $ map (\(term, defn) -> "+ **" ++ haddockToMarkdown term ++ "**: " ++ haddockToMarkdown defn) things) diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 81597e1efd..accfade90e 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -2393,6 +2393,34 @@ haddockTests , "" ] ) + , testCase "ordered list" $ checkHaddock + (unlines + [ "may require" + , "different precautions:" + , "" + , " 1. Use @{\\-\\# NOINLINE foo \\#-\\}@ as a pragma on any function @foo@" + , " that calls 'unsafePerformIO'. If the call is inlined," + , " the I\\/O may be performed more than once." + , "" + , " 2. Use the compiler flag @-fno-cse@ to prevent common sub-expression" + , " elimination being performed on the module." + , "" + ] + ) + (unlines + [ "" + , "" + , "may require" + , "different precautions: " + , "1. Use `{-# NOINLINE foo #-}` as a pragma on any function `foo` " + , " that calls `unsafePerformIO` . If the call is inlined," + , " the I/O may be performed more than once." + , "" + , "2. Use the compiler flag `-fno-cse` to prevent common sub-expression" + , " elimination being performed on the module." + , "" + ] + ) ] where checkHaddock s txt = spanDocToMarkdownForTest s @?= txt From caa5ce55c49049632c62018fd7eac1b8dedd76bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Oct 2022 14:02:57 +0000 Subject: [PATCH 178/213] Bump cachix/cachix-action from 11 to 12 (#3310) Bumps [cachix/cachix-action](https://github.com/cachix/cachix-action) from 11 to 12. - [Release notes](https://github.com/cachix/cachix-action/releases) - [Commits](https://github.com/cachix/cachix-action/compare/v11...v12) --- updated-dependencies: - dependency-name: cachix/cachix-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/nix.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 96c3a0c894..96ba533133 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -62,7 +62,7 @@ jobs: extra_nix_config: | experimental-features = nix-command flakes nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@v11 + - uses: cachix/cachix-action@v12 with: name: haskell-language-server # Disable pushing, we will do that in job `build` @@ -96,7 +96,7 @@ jobs: extra_nix_config: | experimental-features = nix-command flakes nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@v11 + - uses: cachix/cachix-action@v12 with: name: haskell-language-server authToken: ${{ secrets.HLS_CACHIX_AUTH_TOKEN }} From 2b94f8581a220305bf7a4f5c055ba97c21a7931d Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Tue, 1 Nov 2022 10:27:18 +0100 Subject: [PATCH 179/213] Record diagnostics source rule when testing (#3301) * when testing: record the source rule in diagnostics * Use _code field * Moce to diagnostic related information --- ghcide/src/Development/IDE/Core/Shake.hs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Core/Shake.hs b/ghcide/src/Development/IDE/Core/Shake.hs index 5e51fd0fba..2f2234cbca 100644 --- a/ghcide/src/Development/IDE/Core/Shake.hs +++ b/ghcide/src/Development/IDE/Core/Shake.hs @@ -1199,7 +1199,7 @@ updateFileDiagnostics :: MonadIO m -> ShakeExtras -> [(ShowDiagnostic,Diagnostic)] -- ^ current results -> m () -updateFileDiagnostics recorder fp ver k ShakeExtras{diagnostics, hiddenDiagnostics, publishedDiagnostics, debouncer, lspEnv} current = +updateFileDiagnostics recorder fp ver k ShakeExtras{diagnostics, hiddenDiagnostics, publishedDiagnostics, debouncer, lspEnv, ideTesting} current0 = liftIO $ withTrace ("update diagnostics " <> fromString(fromNormalizedFilePath fp)) $ \ addTag -> do addTag "key" (show k) let (currentShown, currentHidden) = partition ((== ShowDiag) . fst) current @@ -1208,6 +1208,7 @@ updateFileDiagnostics recorder fp ver k ShakeExtras{diagnostics, hiddenDiagnosti addTagUnsafe msg t x v = unsafePerformIO(addTag (msg <> t) x) `seq` v update :: (forall a. String -> String -> a -> a) -> [Diagnostic] -> STMDiagnosticStore -> STM [Diagnostic] update addTagUnsafe new store = addTagUnsafe "count" (show $ Prelude.length new) $ setStageDiagnostics addTagUnsafe uri ver (renderKey k) new store + current = second diagsFromRule <$> current0 addTag "version" (show ver) mask_ $ do -- Mask async exceptions to ensure that updated diagnostics are always @@ -1230,6 +1231,22 @@ updateFileDiagnostics recorder fp ver k ShakeExtras{diagnostics, hiddenDiagnosti LSP.sendNotification LSP.STextDocumentPublishDiagnostics $ LSP.PublishDiagnosticsParams (fromNormalizedUri uri) (fmap fromIntegral ver) (List newDiags) return action + where + diagsFromRule :: Diagnostic -> Diagnostic + diagsFromRule c@Diagnostic{_range} + | coerce ideTesting = c + {_relatedInformation = + Just $ List [ + DiagnosticRelatedInformation + (Location + (filePathToUri $ fromNormalizedFilePath fp) + _range + ) + (T.pack $ show k) + ] + } + | otherwise = c + newtype Priority = Priority Double From 4cb9ff13912b28c0d9da57edee9cace9f09b55f7 Mon Sep 17 00:00:00 2001 From: Elliot Marsden Date: Thu, 3 Nov 2022 16:23:50 +0000 Subject: [PATCH 180/213] Make splice plugin compatible with GHC 9.2 (#2816) * Compile and get all tests passing * Add back-compat for GHC 9.0 * Update docs and build flags to enable for 9.2 --- .github/workflows/test.yml | 6 +- docs/support/plugin-support.md | 2 +- haskell-language-server.cabal | 2 +- .../src/Development/IDE/GHC/ExactPrint.hs | 46 ++++++----- .../src/Ide/Plugin/Splice.hs | 78 +++++++++++++------ 5 files changed, 87 insertions(+), 47 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bae4d974a8..6a11cb2edc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -106,7 +106,7 @@ jobs: os: ${{ runner.os }} - name: Build - run: cabal build + run: cabal build - name: Set test options # run the tests without parallelism, otherwise tasty will attempt to run @@ -148,7 +148,7 @@ jobs: env: HLS_TEST_EXE: hls HLS_WRAPPER_TEST_EXE: hls-wrapper - run: cabal test wrapper-test --test-options="$TEST_OPTS --rerun-log-file .tasty-rerun-log-wrapper" + run: cabal test wrapper-test --test-options="$TEST_OPTS --rerun-log-file .tasty-rerun-log-wrapper" - if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2' name: Test hls-brittany-plugin @@ -178,7 +178,7 @@ jobs: name: Test hls-haddock-comments-plugin run: cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2' + - if: matrix.test && matrix.ghc != '9.4.2' name: Test hls-splice-plugin run: cabal test hls-splice-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-splice-plugin --test-options="$TEST_OPTS" diff --git a/docs/support/plugin-support.md b/docs/support/plugin-support.md index 1bab3b4b90..4aa8530cf2 100644 --- a/docs/support/plugin-support.md +++ b/docs/support/plugin-support.md @@ -65,4 +65,4 @@ For example, a plugin to provide a formatter which has itself been abandoned has | `hls-haddock-comments-plugin` | 3 | 9.2, 9.4 | | `hls-stan-plugin` | 3 | 8.6, 9.0, 9.2, 9.4 | | `hls-retrie-plugin` | 3 | 9.2, 9.4 | -| `hls-splice-plugin` | 3 | 9.2, 9.4 | +| `hls-splice-plugin` | 3 | 9.4 | diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index c2938ed6e7..ec62f7cd6d 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -266,7 +266,7 @@ common pragmas cpp-options: -Dhls_pragmas common splice - if flag(splice) && (impl(ghc < 9.2.1) || flag(ignore-plugins-ghc-bounds)) + if flag(splice) && (impl(ghc < 9.4.1) || flag(ignore-plugins-ghc-bounds)) build-depends: hls-splice-plugin ^>=1.0.0.1 cpp-options: -Dhls_splice diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs index 8368efa249..ead2e04186 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs @@ -25,6 +25,7 @@ module Development.IDE.GHC.ExactPrint Annotate, setPrecedingLinesT, #else + setPrecedingLines, addParens, addParensToCtxt, modifyAnns, @@ -56,6 +57,7 @@ import Control.Monad.Trans.Except import Control.Monad.Zip import Data.Bifunctor import Data.Bool (bool) +import Data.Default (Default) import qualified Data.DList as DL import Data.Either.Extra (mapLeft) import Data.Foldable (Foldable (fold)) @@ -101,7 +103,13 @@ import GHC (EpAnn (..), spanAsAnchor) import GHC.Parser.Annotation (AnnContext (..), DeltaPos (SameLine), - EpaLocation (EpaDelta)) + EpaLocation (EpaDelta), + deltaPos) +#endif + +#if MIN_VERSION_ghc(9,2,0) +setPrecedingLines :: Default t => LocatedAn t a -> Int -> Int -> LocatedAn t a +setPrecedingLines ast n c = setEntryDP ast (deltaPos n c) #endif ------------------------------------------------------------------------------ @@ -114,10 +122,10 @@ instance Pretty Log where instance Show (Annotated ParsedSource) where show _ = "" - + instance NFData (Annotated ParsedSource) where rnf = rwhnf - + data GetAnnotatedParsedSource = GetAnnotatedParsedSource deriving (Eq, Show, Typeable, GHC.Generic) @@ -374,7 +382,7 @@ graftWithM dst trans = Graft $ \dflags a -> do #if MIN_VERSION_ghc(9,2,0) val'' <- hoistTransform (either Fail.fail pure) $ - annotate dflags True $ maybeParensAST val' + annotate dflags False $ maybeParensAST val' pure val'' #else (anns, val'') <- @@ -468,7 +476,17 @@ graftDeclsWithM dst toDecls = Graft $ \dflags a -> do modifyDeclsT (fmap DL.toList . go) a -class (Data ast, Typeable l, Outputable l, Outputable ast) => ASTElement l ast | ast -> l where +-- In 9.2+, we need `Default l` to do `setPrecedingLines` on annotated elements. +-- In older versions, we pass around annotations explicitly, so the instance isn't needed. +class + ( Data ast + , Typeable l + , Outputable l + , Outputable ast +#if MIN_VERSION_ghc(9,2,0) + , Default l +#endif + ) => ASTElement l ast | ast -> l where parseAST :: Parser (LocatedAn l ast) maybeParensAST :: LocatedAn l ast -> LocatedAn l ast {- | Construct a 'Graft', replacing the node at the given 'SrcSpan' with @@ -520,6 +538,7 @@ fixAnns ParsedModule {..} = ------------------------------------------------------------------------------ + -- | Given an 'LHSExpr', compute its exactprint annotations. -- Note that this function will throw away any existing annotations (and format) annotate :: (ASTElement l ast, Outputable l) @@ -533,7 +552,7 @@ annotate dflags needs_space ast = do let rendered = render dflags ast #if MIN_VERSION_ghc(9,2,0) expr' <- lift $ mapLeft show $ parseAST dflags uniq rendered - pure expr' + pure $ setPrecedingLines expr' 0 (bool 0 1 needs_space) #else (anns, expr') <- lift $ mapLeft show $ parseAST dflags uniq rendered let anns' = setPrecedingLines expr' 0 (bool 0 1 needs_space) anns @@ -542,6 +561,7 @@ annotate dflags needs_space ast = do -- | Given an 'LHsDecl', compute its exactprint annotations. annotateDecl :: DynFlags -> LHsDecl GhcPs -> TransformT (Either String) (LHsDecl GhcPs) +#if !MIN_VERSION_ghc(9,2,0) -- The 'parseDecl' function fails to parse 'FunBind' 'ValD's which contain -- multiple matches. To work around this, we split the single -- 'FunBind'-of-multiple-'Match'es into multiple 'FunBind's-of-one-'Match', @@ -554,17 +574,6 @@ annotateDecl dflags let set_matches matches = ValD ext fb { fun_matches = mg { mg_alts = L alt_src matches }} -#if MIN_VERSION_ghc(9,2,0) - alts' <- for alts $ \alt -> do - uniq <- show <$> uniqueSrcSpanT - let rendered = render dflags $ set_matches [alt] - lift (mapLeft show $ parseDecl dflags uniq rendered) >>= \case - (L _ (ValD _ FunBind { fun_matches = MG { mg_alts = L _ [alt']}})) - -> pure alt' - _ -> lift $ Left "annotateDecl: didn't parse a single FunBind match" - - pure $ L src $ set_matches alts' -#else (anns', alts') <- fmap unzip $ for alts $ \alt -> do uniq <- show <$> uniqueSrcSpanT let rendered = render dflags $ set_matches [alt] @@ -580,7 +589,8 @@ annotateDecl dflags ast = do uniq <- show <$> uniqueSrcSpanT let rendered = render dflags ast #if MIN_VERSION_ghc(9,2,0) - lift $ mapLeft show $ parseDecl dflags uniq rendered + expr' <- lift $ mapLeft show $ parseDecl dflags uniq rendered + pure $ setPrecedingLines expr' 1 0 #else (anns, expr') <- lift $ mapLeft show $ parseDecl dflags uniq rendered let anns' = setPrecedingLines expr' 1 0 anns diff --git a/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs b/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs index aabb3b09ee..9b817ec898 100644 --- a/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs +++ b/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs @@ -15,6 +15,7 @@ {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} @@ -51,10 +52,13 @@ import Development.IDE.GHC.Compat.ExactPrint import qualified Development.IDE.GHC.Compat.Util as Util import Development.IDE.GHC.ExactPrint import GHC.Exts +#if __GLASGOW_HASKELL__ >= 902 +import GHC.Parser.Annotation (SrcSpanAnn'(..)) +import qualified GHC.Types.Error as Error +#endif import Ide.Plugin.Splice.Types import Ide.Types -import Language.Haskell.GHC.ExactPrint (setPrecedingLines, - uniqueSrcSpanT) +import Language.Haskell.GHC.ExactPrint (uniqueSrcSpanT) import Language.LSP.Server import Language.LSP.Types import Language.LSP.Types.Capabilities @@ -135,7 +139,7 @@ expandTHSplice _eStyle ideState params@ExpandSpliceParams {..} = do graftSpliceWith :: forall ast. HasSplice AnnListItem ast => - Maybe (SrcSpan, Located (ast GhcPs)) -> + Maybe (SrcSpan, LocatedAn AnnListItem (ast GhcPs)) -> Maybe (Either String WorkspaceEdit) graftSpliceWith expandeds = expandeds <&> \(_, expanded) -> @@ -236,11 +240,11 @@ adjustToRange uri ran (WorkspaceEdit mhult mlt x) = where adjustTextEdits :: Traversable f => f TextEdit -> f TextEdit adjustTextEdits eds = - let Just minStart = - L.fold - (L.premap (view J.range) L.minimum) - eds - in adjustLine minStart <$> eds + let minStart = + case L.fold (L.premap (view J.range) L.minimum) eds of + Nothing -> error "impossible" + Just v -> v + in adjustLine minStart <$> eds adjustATextEdits :: Traversable f => f (TextEdit |? AnnotatedTextEdit) -> f (TextEdit |? AnnotatedTextEdit) adjustATextEdits = fmap $ \case @@ -263,11 +267,23 @@ adjustToRange uri ran (WorkspaceEdit mhult mlt x) = J.range %~ \r -> if r == bad then ran else bad +-- Define a pattern to get hold of a `SrcSpan` from the location part of a +-- `GenLocated`. In GHC >= 9.2 this will be a SrcSpanAnn', with annotations; +-- earlier it will just be a plain `SrcSpan`. +{-# COMPLETE AsSrcSpan #-} +#if __GLASGOW_HASKELL__ >= 902 +pattern AsSrcSpan :: SrcSpan -> SrcSpanAnn' a +pattern AsSrcSpan locA <- SrcSpanAnn {locA} +#else +pattern AsSrcSpan :: SrcSpan -> SrcSpan +pattern AsSrcSpan loc <- loc +#endif + findSubSpansDesc :: SrcSpan -> [(LHsExpr GhcTc, a)] -> [(SrcSpan, a)] findSubSpansDesc srcSpan = sortOn (Down . SubSpan . fst) . mapMaybe - ( \(L spn _, e) -> do + ( \(L (AsSrcSpan spn) _, e) -> do guard (spn `isSubspanOf` srcSpan) pure (spn, e) ) @@ -321,7 +337,7 @@ manualCalcEdit :: manualCalcEdit clientCapabilities reportEditor ran ps hscEnv typechkd srcSpan _eStyle ExpandSpliceParams {..} = do (warns, resl) <- ExceptT $ do - ((warns, errs), eresl) <- + (msgs, eresl) <- initTcWithGbl hscEnv typechkd srcSpan $ case classifyAST spliceContext of IsHsDecl -> fmap (fmap $ adjustToRange uri ran) $ @@ -348,8 +364,16 @@ manualCalcEdit clientCapabilities reportEditor ran ps hscEnv typechkd srcSpan _e Util.try @_ @SomeException $ (fst <$> expandSplice astP spl) ) - Just <$> either (pure . L _spn) (unRenamedE dflags) eExpr + Just <$> case eExpr of + Left x -> pure $ L _spn x + Right y -> unRenamedE dflags y _ -> pure Nothing + let (warns, errs) = +#if __GLASGOW_HASKELL__ >= 902 + (Error.getWarningMessages msgs, Error.getErrorMessages msgs) +#else + msgs +#endif pure $ (warns,) <$> fromMaybe (Left $ show errs) eresl unless @@ -370,14 +394,17 @@ unRenamedE :: (Fail.MonadFail m, HasSplice l ast) => DynFlags -> ast GhcRn -> - TransformT m (Located (ast GhcPs)) + TransformT m (LocatedAn l (ast GhcPs)) unRenamedE dflags expr = do uniq <- show <$> uniqueSrcSpanT - (anns, expr') <- +#if __GLASGOW_HASKELL__ >= 902 + expr' <- +#else + (_anns, expr') <- +#endif either (fail . show) pure $ - parseAST @_ @(ast GhcPs) dflags uniq $ - showSDoc dflags $ ppr expr - let _anns' = setPrecedingLines expr' 0 1 anns + parseAST @_ @(ast GhcPs) dflags uniq $ + showSDoc dflags $ ppr expr pure expr' data SearchResult r = @@ -416,11 +443,14 @@ codeAction state plId (CodeActionParams _ _ docId ran _) = liftIO $ RealSrcSpan -> GenericQ (SearchResult (RealSrcSpan, SpliceContext)) detectSplice spn = + let + spanIsRelevant x = RealSrcSpan spn Nothing `isSubspanOf` x + in mkQ Continue ( \case - (L l@(RealSrcSpan spLoc _) expr :: LHsExpr GhcPs) - | RealSrcSpan spn Nothing `isSubspanOf` l -> + (L (AsSrcSpan l@(RealSrcSpan spLoc _)) expr :: LHsExpr GhcPs) + | spanIsRelevant l -> case expr of HsSpliceE {} -> Here (spLoc, Expr) _ -> Continue @@ -430,23 +460,23 @@ codeAction state plId (CodeActionParams _ _ docId ran _) = liftIO $ #if __GLASGOW_HASKELL__ == 808 (dL @(Pat GhcPs) -> L l@(RealSrcSpan spLoc _) pat :: Located (Pat GhcPs)) #else - (L l@(RealSrcSpan spLoc _) pat :: LPat GhcPs) + (L (AsSrcSpan l@(RealSrcSpan spLoc _)) pat :: LPat GhcPs) #endif - | RealSrcSpan spn Nothing `isSubspanOf` l -> + | spanIsRelevant l -> case pat of SplicePat{} -> Here (spLoc, Pat) _ -> Continue _ -> Stop `extQ` \case - (L l@(RealSrcSpan spLoc _) ty :: LHsType GhcPs) - | RealSrcSpan spn Nothing `isSubspanOf` l -> + (L (AsSrcSpan l@(RealSrcSpan spLoc _)) ty :: LHsType GhcPs) + | spanIsRelevant l -> case ty of HsSpliceTy {} -> Here (spLoc, HsType) _ -> Continue _ -> Stop `extQ` \case - (L l@(RealSrcSpan spLoc _) decl :: LHsDecl GhcPs) - | RealSrcSpan spn Nothing `isSubspanOf` l -> + (L (AsSrcSpan l@(RealSrcSpan spLoc _)) decl :: LHsDecl GhcPs) + | spanIsRelevant l -> case decl of SpliceD {} -> Here (spLoc, HsDecl) _ -> Continue From d17d9fd0cdecdaee13e6653d31841cd6b2e14e76 Mon Sep 17 00:00:00 2001 From: fendor Date: Sat, 5 Nov 2022 23:14:52 +0100 Subject: [PATCH 181/213] Remove stack from installation docs since it is not supported anymore (#3314) Co-authored-by: Michael Peyton Jones --- docs/installation.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index 9511d3d010..375dceb782 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -4,7 +4,6 @@ - For standalone `.hs`/`.lhs` files, [ghc](https://www.haskell.org/ghc/) must be installed and on the `PATH`. The easiest way to install it is with [ghcup](https://www.haskell.org/ghcup/) or [chocolatey](https://community.chocolatey.org/packages/ghc) on Windows. - For Cabal based projects, both ghc and [cabal-install](https://www.haskell.org/cabal/) must be installed and on the `PATH`. It can also be installed with [ghcup](https://www.haskell.org/ghcup/) or [chocolatey](https://community.chocolatey.org/packages/cabal) on Windows. -- For Stack based projects, [stack](http://haskellstack.org) must be installed and on the `PATH`. ## ghcup @@ -26,18 +25,16 @@ More information here: ## Installation from source -Direct installation from source, while possible via `cabal install exe:haskell-language-server` -and `stack install --stack-yaml stack-.yaml`, is not recommended for most people. +Direct installation from source, while possible via `cabal install exe:haskell-language-server`, is not recommended for most people. Said command builds the `haskell-language-server` binary and installs it in the default `cabal` binaries folder, but the binary will only work with projects that use the same GHC version that built it. ### Common pre-requirements -- `stack` or `cabal` must be in your `PATH` - - You need `stack` version >= 2.1.1 or `cabal` >= 2.4.0.0 +- `cabal` must be in your `PATH` + - You need `cabal` >= 2.4.0.0 - `git` must be in your `PATH` -- The directory where `stack`or `cabal` put the binaries must be in you PATH: - - For `stack` you can get it with `stack path --local-bin` +- The directory where `cabal` put the binaries must be in you PATH: - For `cabal` it is by default `$HOME/.cabal/bin` in Linux and `%APPDATA%\cabal\bin` in windows. Tip: you can quickly check if some command is in your path by running the command. From 9d70df07e66844f6e93bbc79c0402dbe797f8690 Mon Sep 17 00:00:00 2001 From: santiweight Date: Sun, 6 Nov 2022 14:08:28 -0800 Subject: [PATCH 182/213] support "add argument" action (#3149) * support add-argument action * respond to review comments * review: add ability to report errors in CodeAction api * review: use already-defined function * attempts at cpp * fix format error * fix broken test * doc: add self to codeowners; add doc to features.md * formatting * formatting * fix an import * review * formatting * add testcase with comments * fix build error Co-authored-by: Santiago Weight Co-authored-by: Pepe Iborra Co-authored-by: Michael Peyton Jones --- CODEOWNERS | 1 + docs/features.md | 8 + ghcide/src/Development/IDE/GHC/Error.hs | 6 + .../src/Ide/Plugin/QualifyImportedNames.hs | 11 +- .../hls-refactor-plugin.cabal | 2 + .../src/Development/IDE/GHC/ExactPrint.hs | 39 +++ .../src/Development/IDE/Plugin/CodeAction.hs | 140 ++++++++-- .../Development/IDE/Plugin/CodeAction/Args.hs | 48 ++-- plugins/hls-refactor-plugin/test/Main.hs | 246 +++++++++++++++++- 9 files changed, 444 insertions(+), 57 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 627b4f1361..1867d280ba 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -24,6 +24,7 @@ /plugins/hls-qualify-imported-names-plugin @eddiemundo /plugins/hls-refine-imports-plugin /plugins/hls-rename-plugin @OliverMadine +/plugins/hls-refactor-plugin @santiweight /plugins/hls-retrie-plugin @pepeiborra /plugins/hls-code-range-plugin @kokobd /plugins/hls-splice-plugin @konn diff --git a/docs/features.md b/docs/features.md index 54c03d1b86..ef4ae8a88e 100644 --- a/docs/features.md +++ b/docs/features.md @@ -271,6 +271,14 @@ Known Limitations: ![Link to Docs](../plugins/hls-change-type-signature-plugin/README.md) +### Add argument to function + +Provided by: `hls-refactor-plugin` + +Code action kind: `quickfix` + +Add an undefined variable as an argument to the top-level binding. + ### Convert to GADT syntax Provided by: `hls-gadt-plugin` diff --git a/ghcide/src/Development/IDE/GHC/Error.hs b/ghcide/src/Development/IDE/GHC/Error.hs index b16d908c58..a8a7acce27 100644 --- a/ghcide/src/Development/IDE/GHC/Error.hs +++ b/ghcide/src/Development/IDE/GHC/Error.hs @@ -24,6 +24,7 @@ module Development.IDE.GHC.Error , zeroSpan , realSpan , isInsideSrcSpan + , spanContainsRange , noSpan -- * utilities working with severities @@ -43,6 +44,7 @@ import Development.IDE.GHC.Orphans () import Development.IDE.Types.Diagnostics as D import Development.IDE.Types.Location import GHC +import Language.LSP.Types (isSubrangeOf) diagFromText :: T.Text -> D.DiagnosticSeverity -> SrcSpan -> T.Text -> FileDiagnostic @@ -119,6 +121,10 @@ p `isInsideSrcSpan` r = case srcSpanToRange r of Just (Range sp ep) -> sp <= p && p <= ep _ -> False +-- Returns Nothing if the SrcSpan does not represent a valid range +spanContainsRange :: SrcSpan -> Range -> Maybe Bool +spanContainsRange srcSpan range = (range `isSubrangeOf`) <$> srcSpanToRange srcSpan + -- | Convert a GHC severity to a DAML compiler Severity. Severities below -- "Warning" level are dropped (returning Nothing). toDSeverity :: GHC.Severity -> Maybe D.DiagnosticSeverity diff --git a/plugins/hls-qualify-imported-names-plugin/src/Ide/Plugin/QualifyImportedNames.hs b/plugins/hls-qualify-imported-names-plugin/src/Ide/Plugin/QualifyImportedNames.hs index 6d78cee625..62d39bfd6f 100644 --- a/plugins/hls-qualify-imported-names-plugin/src/Ide/Plugin/QualifyImportedNames.hs +++ b/plugins/hls-qualify-imported-names-plugin/src/Ide/Plugin/QualifyImportedNames.hs @@ -18,9 +18,10 @@ import qualified Data.HashMap.Strict as HashMap import Data.List (sortOn) import qualified Data.List as List import qualified Data.Map.Strict as Map -import Data.Maybe (mapMaybe) +import Data.Maybe (fromMaybe, mapMaybe) import Data.Text (Text) import qualified Data.Text as Text +import Development.IDE (spanContainsRange) import Development.IDE.Core.RuleTypes (GetFileContents (GetFileContents), GetHieAst (GetHieAst), HieAstResult (HAR, refMap), @@ -87,16 +88,12 @@ descriptor pluginId = (defaultPluginDescriptor pluginId) { ] } -isRangeWithinSrcSpan :: Range -> SrcSpan -> Bool -isRangeWithinSrcSpan (Range start end) srcSpan = - isInsideSrcSpan start srcSpan && isInsideSrcSpan end srcSpan - findLImportDeclAt :: Range -> ParsedModule -> Maybe (LImportDecl GhcPs) findLImportDeclAt range parsedModule | ParsedModule {..} <- parsedModule , L _ hsModule <- pm_parsed_source , locatedImportDecls <- hsmodImports hsModule = - find (\ (L (locA -> srcSpan) _) -> isRangeWithinSrcSpan range srcSpan) locatedImportDecls + find (\ (L (locA -> srcSpan) _) -> fromMaybe False $ srcSpan `spanContainsRange` range) locatedImportDecls makeCodeActions :: Uri -> [TextEdit] -> [a |? CodeAction] makeCodeActions uri textEdits = [InR CodeAction {..} | not (null textEdits)] @@ -132,7 +129,7 @@ data ImportedBy = ImportedBy { } isRangeWithinImportedBy :: Range -> ImportedBy -> Bool -isRangeWithinImportedBy range (ImportedBy _ srcSpan) = isRangeWithinSrcSpan range srcSpan +isRangeWithinImportedBy range (ImportedBy _ srcSpan) = fromMaybe False $ spanContainsRange srcSpan range globalRdrEnvToNameToImportedByMap :: GlobalRdrEnv -> NameEnv [ImportedBy] globalRdrEnvToNameToImportedByMap = diff --git a/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal b/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal index 7dfa8a020c..80979f2f6e 100644 --- a/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal +++ b/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal @@ -113,6 +113,8 @@ test-suite tests , extra , text-rope , containers + -- ghc is included to enable the MIN_VERSION_ghc macro + , ghc , ghcide , ghcide-test-utils , shake diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs index ead2e04186..67c1f89f32 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs @@ -20,6 +20,10 @@ module Development.IDE.GHC.ExactPrint transform, transformM, ExactPrint(..), +#if MIN_VERSION_ghc(9,2,1) + modifySmallestDeclWithM, + modifyMgMatchesT, +#endif #if !MIN_VERSION_ghc(9,2,0) Anns, Annotate, @@ -438,6 +442,41 @@ graftDecls dst decs0 = Graft $ \dflags a -> do | otherwise = DL.singleton (L src e) <> go rest modifyDeclsT (pure . DL.toList . go) a +#if MIN_VERSION_ghc(9,2,1) + +-- | Replace the smallest declaration whose SrcSpan satisfies the given condition with a new +-- list of declarations. +-- +-- For example, if you would like to move a where-clause-defined variable to the same +-- level as its parent HsDecl, you could use this function. +modifySmallestDeclWithM :: + forall a m. + (HasDecls a, Monad m) => + (SrcSpan -> m Bool) -> + (LHsDecl GhcPs -> TransformT m [LHsDecl GhcPs]) -> + a -> + TransformT m a +modifySmallestDeclWithM validSpan f a = do + let modifyMatchingDecl [] = pure DL.empty + modifyMatchingDecl (e@(L src _) : rest) = + lift (validSpan $ locA src) >>= \case + True -> do + decs' <- f e + pure $ DL.fromList decs' <> DL.fromList rest + False -> (DL.singleton e <>) <$> modifyMatchingDecl rest + modifyDeclsT (fmap DL.toList . modifyMatchingDecl) a + +-- | Modify the each LMatch in a MatchGroup +modifyMgMatchesT :: + Monad m => + MatchGroup GhcPs (LHsExpr GhcPs) -> + (LMatch GhcPs (LHsExpr GhcPs) -> TransformT m (LMatch GhcPs (LHsExpr GhcPs))) -> + TransformT m (MatchGroup GhcPs (LHsExpr GhcPs)) +modifyMgMatchesT (MG xMg (L locMatches matches) originMg) f = do + matches' <- mapM f matches + pure $ MG xMg (L locMatches matches') originMg +#endif + graftSmallestDeclsWithM :: forall a. (HasDecls a) => diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index 4901ccab05..442ffcb253 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -38,6 +38,7 @@ import Data.Ord (comparing) import qualified Data.Set as S import qualified Data.Text as T import qualified Data.Text.Utf16.Rope as Rope +import Data.Tuple.Extra (first) import Development.IDE.Core.Rules import Development.IDE.Core.RuleTypes import Development.IDE.Core.Service @@ -63,7 +64,8 @@ import Development.IDE.Types.Logger hiding import Development.IDE.Types.Options import GHC.Exts (fromList) import qualified GHC.LanguageExtensions as Lang -import Ide.PluginUtils (subRange) +import Ide.PluginUtils (makeDiffTextEdit, + subRange) import Ide.Types import qualified Language.LSP.Server as LSP import Language.LSP.Types (ApplyWorkspaceEditParams (..), @@ -89,7 +91,13 @@ import Language.LSP.VFS (VirtualFile, import qualified Text.Fuzzy.Parallel as TFP import Text.Regex.TDFA (mrAfter, (=~), (=~~)) +#if MIN_VERSION_ghc(9,2,1) +import GHC.Types.SrcLoc (generatedSrcSpan) +import Language.Haskell.GHC.ExactPrint (noAnnSrcSpanDP1, + runTransformT) +#endif #if MIN_VERSION_ghc(9,2,0) +import Extra (maybeToEither) import GHC (AddEpAnn (AddEpAnn), Anchor (anchor_op), AnchorOperation (..), @@ -168,6 +176,9 @@ bindingsPluginDescriptor recorder plId = mkExactprintPluginDescriptor recorder $ , wrap suggestImplicitParameter #endif , wrap suggestNewDefinition +#if MIN_VERSION_ghc(9,2,1) + , wrap suggestAddArgument +#endif , wrap suggestDeleteUnusedBinding ] plId @@ -243,7 +254,7 @@ extendImportHandler' ideState ExtendImport {..} Nothing -> newThing Just p -> p <> "(" <> newThing <> ")" t <- liftMaybe $ snd <$> newImportToEdit n ps (fromMaybe "" contents) - return (nfp, WorkspaceEdit {_changes=Just (fromList [(doc,List [t])]), _documentChanges=Nothing, _changeAnnotations=Nothing}) + return (nfp, WorkspaceEdit {_changes=Just (GHC.Exts.fromList [(doc,List [t])]), _documentChanges=Nothing, _changeAnnotations=Nothing}) | otherwise = mzero @@ -385,7 +396,7 @@ suggestHideShadow ps fileContents mTcM mHar Diagnostic {_message, _range} Just matched <- allMatchRegexUnifySpaces _message "imported from ‘([^’]+)’ at ([^ ]*)", mods <- [(modName, s) | [_, modName, s] <- matched], result <- nubOrdBy (compare `on` fst) $ mods >>= uncurry (suggests identifier), - hideAll <- ("Hide " <> identifier <> " from all occurence imports", concat $ snd <$> result) = + hideAll <- ("Hide " <> identifier <> " from all occurence imports", concatMap snd result) = result <> [hideAll] | otherwise = [] where @@ -881,34 +892,111 @@ suggestReplaceIdentifier contents Diagnostic{_range=_range,..} = [ ("Replace with ‘" <> name <> "’", [mkRenameEdit contents _range name]) | name <- renameSuggestions ] | otherwise = [] +matchVariableNotInScope :: T.Text -> Maybe (T.Text, Maybe T.Text) +matchVariableNotInScope message + -- * Variable not in scope: + -- suggestAcion :: Maybe T.Text -> Range -> Range + -- * Variable not in scope: + -- suggestAcion + | Just (name, typ) <- matchVariableNotInScopeTyped message = Just (name, Just typ) + | Just name <- matchVariableNotInScopeUntyped message = Just (name, Nothing) + | otherwise = Nothing + where + matchVariableNotInScopeTyped message + | Just [name, typ] <- matchRegexUnifySpaces message "Variable not in scope: ([^ ]+) :: ([^*•]+)" = + Just (name, typ) + | otherwise = Nothing + matchVariableNotInScopeUntyped message + | Just [name] <- matchRegexUnifySpaces message "Variable not in scope: ([^ ]+)" = + Just name + | otherwise = Nothing + +matchFoundHole :: T.Text -> Maybe (T.Text, T.Text) +matchFoundHole message + | Just [name, typ] <- matchRegexUnifySpaces message "Found hole: _([^ ]+) :: ([^*•]+) Or perhaps" = + Just (name, typ) + | otherwise = Nothing + +matchFoundHoleIncludeUnderscore :: T.Text -> Maybe (T.Text, T.Text) +matchFoundHoleIncludeUnderscore message = first ("_" <>) <$> matchFoundHole message + suggestNewDefinition :: IdeOptions -> ParsedModule -> Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])] -suggestNewDefinition ideOptions parsedModule contents Diagnostic{_message, _range} --- * Variable not in scope: --- suggestAcion :: Maybe T.Text -> Range -> Range - | Just [name, typ] <- matchRegexUnifySpaces message "Variable not in scope: ([^ ]+) :: ([^*•]+)" - = newDefinitionAction ideOptions parsedModule _range name typ - | Just [name, typ] <- matchRegexUnifySpaces message "Found hole: _([^ ]+) :: ([^*•]+) Or perhaps" - , [(label, newDefinitionEdits)] <- newDefinitionAction ideOptions parsedModule _range name typ - = [(label, mkRenameEdit contents _range name : newDefinitionEdits)] - | otherwise = [] - where - message = unifySpaces _message +suggestNewDefinition ideOptions parsedModule contents Diagnostic {_message, _range} + | Just (name, typ) <- matchVariableNotInScope message = + newDefinitionAction ideOptions parsedModule _range name typ + | Just (name, typ) <- matchFoundHole message, + [(label, newDefinitionEdits)] <- newDefinitionAction ideOptions parsedModule _range name (Just typ) = + [(label, mkRenameEdit contents _range name : newDefinitionEdits)] + | otherwise = [] + where + message = unifySpaces _message -newDefinitionAction :: IdeOptions -> ParsedModule -> Range -> T.Text -> T.Text -> [(T.Text, [TextEdit])] -newDefinitionAction IdeOptions{..} parsedModule Range{_start} name typ - | Range _ lastLineP : _ <- +newDefinitionAction :: IdeOptions -> ParsedModule -> Range -> T.Text -> Maybe T.Text -> [(T.Text, [TextEdit])] +newDefinitionAction IdeOptions {..} parsedModule Range {_start} name typ + | Range _ lastLineP : _ <- [ realSrcSpanToRange sp - | (L (locA -> l@(RealSrcSpan sp _)) _) <- hsmodDecls - , _start `isInsideSrcSpan` l] - , nextLineP <- Position{ _line = _line lastLineP + 1, _character = 0} - = [ ("Define " <> sig - , [TextEdit (Range nextLineP nextLineP) (T.unlines ["", sig, name <> " = _"])] - )] - | otherwise = [] + | (L (locA -> l@(RealSrcSpan sp _)) _) <- hsmodDecls, + _start `isInsideSrcSpan` l + ], + nextLineP <- Position {_line = _line lastLineP + 1, _character = 0} = + [ ( "Define " <> sig, + [TextEdit (Range nextLineP nextLineP) (T.unlines ["", sig, name <> " = _"])] + ) + ] + | otherwise = [] where colon = if optNewColonConvention then " : " else " :: " - sig = name <> colon <> T.dropWhileEnd isSpace typ - ParsedModule{pm_parsed_source = L _ HsModule{hsmodDecls}} = parsedModule + sig = name <> colon <> T.dropWhileEnd isSpace (fromMaybe "_" typ) + ParsedModule {pm_parsed_source = L _ HsModule {hsmodDecls}} = parsedModule + +#if MIN_VERSION_ghc(9,2,1) +-- When GHC tells us that a variable is not bound, it will tell us either: +-- - there is an unbound variable with a given type +-- - there is an unbound variable (GHC provides no type suggestion) +-- +-- When we receive either of these errors, we produce a text edit that will add a new argument (as a new pattern in the +-- last position of each LHS of the top-level bindings for this HsDecl). +-- +-- TODO Include logic to also update the type signature of a binding +-- +-- NOTE When adding a new argument to a declaration, the corresponding argument's type in declaration's signature might +-- not be the last type in the signature, such as: +-- foo :: a -> b -> c -> d +-- foo a b = \c -> ... +-- In this case a new argument would have to add its type between b and c in the signature. +suggestAddArgument :: ParsedModule -> Diagnostic -> Either ResponseError [(T.Text, [TextEdit])] +suggestAddArgument parsedModule Diagnostic {_message, _range} + | Just (name, typ) <- matchVariableNotInScope message = addArgumentAction parsedModule _range name typ + | Just (name, typ) <- matchFoundHoleIncludeUnderscore message = addArgumentAction parsedModule _range name (Just typ) + | otherwise = pure [] + where + message = unifySpaces _message + +-- TODO use typ to modify type signature +addArgumentAction :: ParsedModule -> Range -> T.Text -> Maybe T.Text -> Either ResponseError [(T.Text, [TextEdit])] +addArgumentAction (ParsedModule _ parsedSource _ _) range name _typ = + do + let addArgToMatch (L locMatch (Match xMatch ctxMatch pats rhs)) = do + let unqualName = mkRdrUnqual $ mkVarOcc $ T.unpack name + let newPat = L (noAnnSrcSpanDP1 generatedSrcSpan) $ VarPat NoExtField (noLocA unqualName) + pure $ L locMatch (Match xMatch ctxMatch (pats <> [newPat]) rhs) + insertArg = \case + (L locDecl (ValD xVal (FunBind xFunBind idFunBind mg coreFunBind))) -> do + mg' <- modifyMgMatchesT mg addArgToMatch + let decl' = L locDecl (ValD xVal (FunBind xFunBind idFunBind mg' coreFunBind)) + pure [decl'] + decl -> pure [decl] + case runTransformT $ modifySmallestDeclWithM spanContainsRangeOrErr insertArg (makeDeltaAst parsedSource) of + Left err -> Left err + Right (newSource, _, _) -> + let diff = makeDiffTextEdit (T.pack $ exactPrint parsedSource) (T.pack $ exactPrint newSource) + in pure [("Add argument ‘" <> name <> "’ to function", fromLspList diff)] + where + spanContainsRangeOrErr = maybeToEither (responseError "SrcSpan was not valid range") . (`spanContainsRange` range) +#endif + +fromLspList :: List a -> [a] +fromLspList (List a) = a suggestFillTypeWildcard :: Diagnostic -> [(T.Text, TextEdit)] suggestFillTypeWildcard Diagnostic{_range=_range,..} diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs index ef5c7b623a..82e0134fcb 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs @@ -15,7 +15,8 @@ where import Control.Concurrent.STM.Stats (readTVarIO) import Control.Monad.Reader import Control.Monad.Trans.Maybe -import Data.Either (fromRight) +import Data.Either (fromRight, + partitionEithers) import qualified Data.HashMap.Strict as Map import Data.IORef.Extra import Data.Maybe (fromMaybe) @@ -30,6 +31,8 @@ import Development.IDE.GHC.ExactPrint import Development.IDE.Plugin.CodeAction.ExactPrint (Rewrite, rewriteToEdit) #endif +import Control.Monad.Except (ExceptT (..), + runExceptT) import Development.IDE.Plugin.TypeLenses (GetGlobalBindingTypeSigs (GetGlobalBindingTypeSigs), GlobalBindingTypeSigsResult) import Development.IDE.Spans.LocalBindings (Bindings) @@ -46,7 +49,7 @@ type CodeActionPreferred = Bool type GhcideCodeActionResult = [(CodeActionTitle, Maybe CodeActionKind, Maybe CodeActionPreferred, [TextEdit])] -type GhcideCodeAction = ReaderT CodeActionArgs IO GhcideCodeActionResult +type GhcideCodeAction = ExceptT ResponseError (ReaderT CodeActionArgs IO) GhcideCodeActionResult ------------------------------------------------------------------------------------------------- @@ -79,13 +82,15 @@ runGhcideCodeAction state (CodeActionParams _ _ (TextDocumentIdentifier uri) _ra caaHar <- onceIO $ runRule GetHieAst caaBindings <- onceIO $ runRule GetBindings caaGblSigs <- onceIO $ runRule GetGlobalBindingTypeSigs - liftIO $ - concat - <$> sequence - [ runReaderT codeAction caa + results <- liftIO $ + + sequence + [ runReaderT (runExceptT codeAction) caa | caaDiagnostic <- diags, let caa = CodeActionArgs {..} ] + let (errs, successes) = partitionEithers results + pure $ concat successes mkCA :: T.Text -> Maybe CodeActionKind -> Maybe Bool -> [Diagnostic] -> WorkspaceEdit -> (Command |? CodeAction) mkCA title kind isPreferred diags edit = @@ -194,39 +199,44 @@ instance ToCodeAction a => ToCodeAction [a] where instance ToCodeAction a => ToCodeAction (Maybe a) where toCodeAction = maybe (pure []) toCodeAction +instance ToCodeAction a => ToCodeAction (Either ResponseError a) where + toCodeAction = either (\err -> ExceptT $ ReaderT $ \_ -> pure $ Left err) toCodeAction + instance ToTextEdit a => ToCodeAction (CodeActionTitle, a) where - toCodeAction (title, te) = ReaderT $ \caa -> pure . (title,Just CodeActionQuickFix,Nothing,) <$> toTextEdit caa te + toCodeAction (title, te) = ExceptT $ ReaderT $ \caa -> Right . pure . (title,Just CodeActionQuickFix,Nothing,) <$> toTextEdit caa te instance ToTextEdit a => ToCodeAction (CodeActionTitle, CodeActionKind, a) where - toCodeAction (title, kind, te) = ReaderT $ \caa -> pure . (title,Just kind,Nothing,) <$> toTextEdit caa te + toCodeAction (title, kind, te) = ExceptT $ ReaderT $ \caa -> Right . pure . (title,Just kind,Nothing,) <$> toTextEdit caa te instance ToTextEdit a => ToCodeAction (CodeActionTitle, CodeActionPreferred, a) where - toCodeAction (title, isPreferred, te) = ReaderT $ \caa -> pure . (title,Just CodeActionQuickFix,Just isPreferred,) <$> toTextEdit caa te + toCodeAction (title, isPreferred, te) = ExceptT $ ReaderT $ \caa -> Right . pure . (title,Just CodeActionQuickFix,Just isPreferred,) <$> toTextEdit caa te instance ToTextEdit a => ToCodeAction (CodeActionTitle, CodeActionKind, CodeActionPreferred, a) where - toCodeAction (title, kind, isPreferred, te) = ReaderT $ \caa -> pure . (title,Just kind,Just isPreferred,) <$> toTextEdit caa te + toCodeAction (title, kind, isPreferred, te) = ExceptT $ ReaderT $ \caa -> Right . pure . (title,Just kind,Just isPreferred,) <$> toTextEdit caa te ------------------------------------------------------------------------------------------------- toCodeAction1 :: (ToCodeAction r) => (CodeActionArgs -> IO (Maybe a)) -> (Maybe a -> r) -> GhcideCodeAction -toCodeAction1 get f = ReaderT $ \caa -> get caa >>= flip runReaderT caa . toCodeAction . f +toCodeAction1 get f = ExceptT . ReaderT $ \caa -> do + caaMay <- get caa + flip runReaderT caa . runExceptT . toCodeAction . f $ caaMay toCodeAction2 :: (ToCodeAction r) => (CodeActionArgs -> IO (Maybe a)) -> (a -> r) -> GhcideCodeAction -toCodeAction2 get f = ReaderT $ \caa -> +toCodeAction2 get f = ExceptT . ReaderT $ \caa -> get caa >>= \case - Just x -> flip runReaderT caa . toCodeAction . f $ x - _ -> pure [] + Just x -> flip runReaderT caa . runExceptT . toCodeAction . f $ x + _ -> pure $ Right [] toCodeAction3 :: (ToCodeAction r) => (CodeActionArgs -> IO a) -> (a -> r) -> GhcideCodeAction -toCodeAction3 get f = ReaderT $ \caa -> get caa >>= flip runReaderT caa . toCodeAction . f +toCodeAction3 get f = ExceptT . ReaderT $ \caa -> get caa >>= flip runReaderT caa . runExceptT . toCodeAction . f -- | this instance returns a delta AST, useful for exactprint transforms instance ToCodeAction r => ToCodeAction (ParsedSource -> r) where #if !MIN_VERSION_ghc(9,3,0) - toCodeAction f = ReaderT $ \caa@CodeActionArgs {caaAnnSource = x} -> + toCodeAction f = ExceptT . ReaderT $ \caa@CodeActionArgs {caaAnnSource = x} -> x >>= \case - Just s -> flip runReaderT caa . toCodeAction . f . astA $ s - _ -> pure [] + Just s -> flip runReaderT caa . runExceptT . toCodeAction . f . astA $ s + _ -> pure $ Right [] #else toCodeAction f = ReaderT $ \caa@CodeActionArgs {caaParsedModule = x} -> x >>= \case @@ -241,7 +251,7 @@ instance ToCodeAction r => ToCodeAction (IdeOptions -> r) where toCodeAction = toCodeAction3 caaIdeOptions instance ToCodeAction r => ToCodeAction (Diagnostic -> r) where - toCodeAction f = ReaderT $ \caa@CodeActionArgs {caaDiagnostic = x} -> flip runReaderT caa . toCodeAction $ f x + toCodeAction f = ExceptT . ReaderT $ \caa@CodeActionArgs {caaDiagnostic = x} -> flip runReaderT caa . runExceptT . toCodeAction $ f x instance ToCodeAction r => ToCodeAction (Maybe ParsedModule -> r) where toCodeAction = toCodeAction1 caaParsedModule diff --git a/plugins/hls-refactor-plugin/test/Main.hs b/plugins/hls-refactor-plugin/test/Main.hs index 599d4bde29..124f28acf1 100644 --- a/plugins/hls-refactor-plugin/test/Main.hs +++ b/plugins/hls-refactor-plugin/test/Main.hs @@ -57,6 +57,7 @@ import Text.Regex.TDFA ((=~)) import Development.IDE.Plugin.CodeAction (matchRegExMultipleImports) import Test.Hls +import Control.Applicative (liftA2) import qualified Development.IDE.Plugin.CodeAction as Refactor import qualified Development.IDE.Plugin.HLS.GhcIde as GhcIde @@ -319,6 +320,9 @@ codeActionTests = testGroup "code actions" , exportUnusedTests , addImplicitParamsConstraintTests , removeExportTests +#if MIN_VERSION_ghc(9,2,1) + , addFunctionArgumentTests +#endif ] insertImportTests :: TestTree @@ -1507,7 +1511,7 @@ extendImportTests = testGroup "extend import actions" actionsOrCommands <- getCodeActions docB range let codeActions = filter - (T.isPrefixOf "Add" . codeActionTitle) + (liftA2 (&&) (T.isPrefixOf "Add") (not . T.isPrefixOf "Add argument") . codeActionTitle) [ca | InR ca <- actionsOrCommands] actualTitles = codeActionTitle <$> codeActions -- Note that we are not testing the order of the actions, as the @@ -2047,7 +2051,7 @@ insertNewDefinitionTests = testGroup "insert new definition actions" docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB') _ <- waitForDiagnostics InR action@CodeAction { _title = actionTitle } : _ - <- sortOn (\(InR CodeAction{_title=x}) -> x) <$> + <- filter (\(InR CodeAction{_title=x}) -> "Define" `T.isPrefixOf` x) <$> getCodeActions docB (R 0 0 0 50) liftIO $ actionTitle @?= "Define select :: [Bool] -> Bool" executeCodeAction action @@ -2071,7 +2075,7 @@ insertNewDefinitionTests = testGroup "insert new definition actions" docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB') _ <- waitForDiagnostics InR action@CodeAction { _title = actionTitle } : _ - <- sortOn (\(InR CodeAction{_title=x}) -> x) <$> + <- filter (\(InR CodeAction{_title=x}) -> "Define" `T.isPrefixOf` x) <$> getCodeActions docB (R 0 0 0 50) liftIO $ actionTitle @?= "Define select :: [Bool] -> Bool" executeCodeAction action @@ -2105,7 +2109,7 @@ insertNewDefinitionTests = testGroup "insert new definition actions" docB <- createDoc "ModuleB.hs" "haskell" (T.unlines start) _ <- waitForDiagnostics InR action@CodeAction { _title = actionTitle } : _ - <- sortOn (\(InR CodeAction{_title=x}) -> x) <$> + <- filter (\(InR CodeAction{_title=x}) -> "Define" `T.isPrefixOf` x) <$> getCodeActions docB (R 1 0 0 50) liftIO $ actionTitle @?= "Define select :: Int -> Bool" executeCodeAction action @@ -2131,14 +2135,246 @@ insertNewDefinitionTests = testGroup "insert new definition actions" docB <- createDoc "ModuleB.hs" "haskell" (T.unlines start) _ <- waitForDiagnostics InR action@CodeAction { _title = actionTitle } : _ - <- sortOn (\(InR CodeAction{_title=x}) -> x) <$> + <- filter (\(InR CodeAction{_title=x}) -> "Define" `T.isPrefixOf` x) <$> getCodeActions docB (R 1 0 0 50) liftIO $ actionTitle @?= "Define select :: Int -> Bool" executeCodeAction action contentAfterAction <- documentContents docB liftIO $ contentAfterAction @?= T.unlines expected + , testSession "insert new function definition - untyped error" $ do + let txtB = + ["foo = select" + ] + txtB' = + ["" + ,"someOtherCode = ()" + ] + docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ txtB ++ txtB') + _ <- waitForDiagnostics + InR action@CodeAction { _title = actionTitle } : _ + <- filter (\(InR CodeAction{_title=x}) -> "Define" `T.isPrefixOf` x) <$> + getCodeActions docB (R 0 0 0 50) + liftIO $ actionTitle @?= "Define select :: _" + executeCodeAction action + contentAfterAction <- documentContents docB + liftIO $ contentAfterAction @?= T.unlines (txtB ++ + [ "" + , "select :: _" + , "select = _" + ] + ++ txtB') ] +#if MIN_VERSION_ghc(9,2,1) +addFunctionArgumentTests :: TestTree +addFunctionArgumentTests = + testGroup + "add function argument" + [ testSession "simple" $ do + let foo = + [ "foo True = select [True]", + "", + "foo False = False" + ] + foo' = + [ "foo True select = select [True]", + "", + "foo False select = False" + ] + someOtherCode = + [ "", + "someOtherCode = ()" + ] + docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ foo ++ someOtherCode) + _ <- waitForDiagnostics + InR action@CodeAction {_title = actionTitle} : _ <- + filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) + <$> getCodeActions docB (R 0 0 0 50) + liftIO $ actionTitle @?= "Add argument ‘select’ to function" + executeCodeAction action + contentAfterAction <- documentContents docB + liftIO $ contentAfterAction @?= T.unlines (foo' ++ someOtherCode), + testSession "comments" $ do + let foo = + [ "foo -- c1", + " True -- c2", + " = -- c3", + " select [True]", + "", + "foo False = False" + ] + -- TODO improve behavior slightly? + foo' = + [ "foo -- c1", + " True select -- c2", + " = -- c3", + " select [True]", + "", + "foo False select = False" + ] + someOtherCode = + [ "", + "someOtherCode = ()" + ] + docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ foo ++ someOtherCode) + _ <- waitForDiagnostics + InR action@CodeAction {_title = actionTitle} : _ <- + filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) + <$> getCodeActions docB (R 3 0 3 50) + liftIO $ actionTitle @?= "Add argument ‘select’ to function" + executeCodeAction action + contentAfterAction <- documentContents docB + liftIO $ contentAfterAction @?= T.unlines (foo' ++ someOtherCode), + testSession "leading decls" $ do + let foo = + [ "module Foo where", + "", + "bar = 1", + "", + "foo True = select [True]", + "", + "foo False = False" + ] + foo' = + [ "module Foo where", + "", + "bar = 1", + "", + "foo True select = select [True]", + "", + "foo False select = False" + ] + docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ foo) + _ <- waitForDiagnostics + InR action@CodeAction {_title = actionTitle} : _ <- + filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) + <$> getCodeActions docB (R 4 0 4 50) + liftIO $ actionTitle @?= "Add argument ‘select’ to function" + executeCodeAction action + contentAfterAction <- documentContents docB + liftIO $ contentAfterAction @?= T.unlines foo', + testSession "hole" $ do + let foo = + [ "module Foo where", + "", + "bar = 1", + "", + "foo True = _select [True]", + "", + "foo False = False" + ] + foo' = + [ "module Foo where", + "", + "bar = 1", + "", + "foo True _select = _select [True]", + "", + "foo False _select = False" + ] + docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ foo) + _ <- waitForDiagnostics + InR action@CodeAction {_title = actionTitle} : _ <- + filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) + <$> getCodeActions docB (R 4 0 4 50) + liftIO $ actionTitle @?= "Add argument ‘_select’ to function" + executeCodeAction action + contentAfterAction <- documentContents docB + liftIO $ contentAfterAction @?= T.unlines foo', + testSession "untyped error" $ do + let foo = + [ "foo = select" + ] + foo' = + [ "foo select = select" + ] + someOtherCode = + [ "", + "someOtherCode = ()" + ] + docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ foo ++ someOtherCode) + _ <- waitForDiagnostics + InR action@CodeAction {_title = actionTitle} : _ <- + filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) + <$> getCodeActions docB (R 0 0 0 50) + liftIO $ actionTitle @?= "Add argument ‘select’ to function" + executeCodeAction action + contentAfterAction <- documentContents docB + liftIO $ contentAfterAction @?= T.unlines (foo' ++ someOtherCode), + testSession "untyped error" $ do + let foo = + [ "foo = select" + ] + foo' = + [ "foo select = select" + ] + someOtherCode = + [ "", + "someOtherCode = ()" + ] + docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ foo ++ someOtherCode) + _ <- waitForDiagnostics + InR action@CodeAction {_title = actionTitle} : _ <- + filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) + <$> getCodeActions docB (R 0 0 0 50) + liftIO $ actionTitle @?= "Add argument ‘select’ to function" + executeCodeAction action + contentAfterAction <- documentContents docB + liftIO $ contentAfterAction @?= T.unlines (foo' ++ someOtherCode), + testSession "where clause" $ do + let foo = + [ "foo True = False ", + " where", + " bar = select", + "", + "foo False = False" + ] + -- TODO improve this behaviour (should add select to bar, not foo) + foo' = + [ "foo True select = False ", + " where", + " bar = select", + "", + "foo False select = False" + ] + docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ foo) + _ <- waitForDiagnostics + InR action@CodeAction {_title = actionTitle} : _ <- + filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) + <$> getCodeActions docB (R 2 0 2 50) + liftIO $ actionTitle @?= "Add argument ‘select’ to function" + executeCodeAction action + contentAfterAction <- documentContents docB + liftIO $ contentAfterAction @?= T.unlines foo', + testSession "where clause" $ do + let foo = + [ "foo -- c1", + " -- | c2", + " {- c3 -} True -- c4", + " = select", + "", + "foo False = False" + ] + -- TODO could use improvement here... + foo' = + [ "foo -- c1", + " -- | c2", + " {- c3 -} True select -- c4", + " = select", + "", + "foo False select = False" + ] + docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ foo) + _ <- waitForDiagnostics + InR action@CodeAction {_title = actionTitle} : _ <- + filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) + <$> getCodeActions docB (R 3 0 3 50) + liftIO $ actionTitle @?= "Add argument ‘select’ to function" + executeCodeAction action + contentAfterAction <- documentContents docB + liftIO $ contentAfterAction @?= T.unlines foo' + ] +#endif deleteUnusedDefinitionTests :: TestTree deleteUnusedDefinitionTests = testGroup "delete unused definition action" From 5a8ccdbed07c104905d1c053d8e7b92fea2f5776 Mon Sep 17 00:00:00 2001 From: Gabriella Gonzalez Date: Tue, 8 Nov 2022 01:47:33 -0800 Subject: [PATCH 183/213] Refactor overlay composition (#3323) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … so that it's easier to add and remove overlays by creating a list of overlays to compose. This should be a behavior-preserving change. --- flake.nix | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/flake.nix b/flake.nix index 1e4a30ab26..94941a4321 100644 --- a/flake.nix +++ b/flake.nix @@ -195,17 +195,23 @@ hlsSources = builtins.mapAttrs (_: dir: gitignoreSource dir) sourceDirs; - extended = hpkgs: - (hpkgs.override (old: { - overrides = lib.composeExtensions (old.overrides or (_: _: { })) - haskellOverrides; - })).extend (hself: hsuper: - # disable all checks for our packages - builtins.mapAttrs (_: drv: haskell.lib.dontCheck drv) - (lib.composeExtensions - (haskell.lib.packageSourceOverrides hlsSources) tweaks hself - hsuper)); - + # Disable tests, but only for the packages mentioned in this overlay + # + # We don't want to disable tests for *all* packages + dontCheck = overlay: hself: hsuper: + builtins.mapAttrs (_: haskell.lib.dontCheck) + (overlay hself hsuper); + + extended = hpkgs: hpkgs.override (old: { + overrides = + lib.fold + lib.composeExtensions + (old.overrides or (_: _: { })) + [ haskellOverrides + (dontCheck (haskell.lib.packageSourceOverrides hlsSources)) + tweaks + ]; + }); in { inherit hlsSources; From 7c0201b5091b2ae1a3393a69f6c6c817602380bd Mon Sep 17 00:00:00 2001 From: Gabriella Gonzalez Date: Wed, 9 Nov 2022 07:53:07 -0800 Subject: [PATCH 184/213] Add support for `.env` shells to `flake.nix` (#3322) The main motivation for this change is to enable development of HLS plugins by generating a `.env` shell for every package in this repository. Example usage: ```ShellSession $ nix develop .#haskell-language-server-dev-env.hls-fourmolu-plugin ``` This is a plain shell, though, without any tooling installed, identical to the one that Nixpkgs provides by default. --- flake.lock | 13 +++++++++++++ flake.nix | 15 ++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/flake.lock b/flake.lock index aa439d1b7e..ec20499ad0 100644 --- a/flake.lock +++ b/flake.lock @@ -156,6 +156,18 @@ "url": "https://hackage.haskell.org/package/ghc-exactprint-1.5.0/ghc-exactprint-1.5.0.tar.gz" } }, + "ghc-exactprint-160": { + "flake": false, + "locked": { + "narHash": "sha256-6fW4KSmDo7hi5i2C1lbI/rEyFWrowSGTNyaC+f73JaE=", + "type": "tarball", + "url": "https://hackage.haskell.org/package/ghc-exactprint-1.6.0/ghc-exactprint-1.6.0.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://hackage.haskell.org/package/ghc-exactprint-1.6.0/ghc-exactprint-1.6.0.tar.gz" + } + }, "gitignore": { "flake": false, "locked": { @@ -322,6 +334,7 @@ "ghc-check": "ghc-check", "ghc-exactprint": "ghc-exactprint", "ghc-exactprint-150": "ghc-exactprint-150", + "ghc-exactprint-160": "ghc-exactprint-160", "gitignore": "gitignore", "hie-bios": "hie-bios", "hiedb": "hiedb", diff --git a/flake.nix b/flake.nix index 94941a4321..c4873ed9d7 100644 --- a/flake.nix +++ b/flake.nix @@ -362,6 +362,10 @@ src = null; }; + + mkEnvShell = hpkgs: + pkgs.lib.mapAttrs (name: value: hpkgs.${name}.env) pkgs.hlsSources; + # Create a hls executable # Copied from https://github.com/NixOS/nixpkgs/blob/210784b7c8f3d926b7db73bdad085f4dc5d79428/pkgs/development/tools/haskell/haskell-language-server/withWrapper.nix#L16 mkExe = hpkgs: @@ -394,6 +398,15 @@ haskell-language-server-942-dev-nix = mkDevShellWithNixDeps ghc942 "cabal.project"; }; + # The default shell provided by Nixpkgs for a Haskell package (i.e. the + # one that comes in the `.env` attribute) + envShells = { + haskell-language-server-dev-env = mkEnvShell ghcDefault; + haskell-language-server-902-dev-env = mkEnvShell ghc902; + haskell-language-server-924-dev-env = mkEnvShell ghc924; + haskell-language-server-942-dev-env = mkEnvShell ghc942; + }; + allPackages = { haskell-language-server = mkExe ghcDefault; haskell-language-server-902 = mkExe ghc902; @@ -401,7 +414,7 @@ haskell-language-server-942 = mkExe ghc942; }; - devShells = simpleDevShells // nixDevShells // { + devShells = simpleDevShells // nixDevShells // envShells // { default = simpleDevShells.haskell-language-server-dev; }; From 5d56aa70a84807d7659e72eacd4d91fee08dbdbb Mon Sep 17 00:00:00 2001 From: Ruslan Gadeev Date: Wed, 9 Nov 2022 20:42:12 +0300 Subject: [PATCH 185/213] fix typos (#3325) Co-authored-by: Michael Peyton Jones --- docs/troubleshooting.md | 2 +- ghcide-bench/exe/Main.hs | 2 +- ghcide/CHANGELOG.md | 4 +-- .../session-loader/Development/IDE/Session.hs | 2 +- ghcide/src/Development/IDE/Core/Compile.hs | 26 +++++++++---------- ghcide/src/Development/IDE/Core/RuleTypes.hs | 2 +- ghcide/src/Development/IDE/Core/Rules.hs | 10 +++---- ghcide/src/Development/IDE/Core/Shake.hs | 10 +++---- ghcide/src/Development/IDE/GHC/Compat/CPP.hs | 2 +- .../src/Development/IDE/GHC/Compat/Parser.hs | 2 +- .../src/Development/IDE/Import/FindImports.hs | 2 +- ghcide/src/Development/IDE/Main/HeapStats.hs | 4 +-- .../src/Development/IDE/Plugin/Completions.hs | 2 +- .../IDE/Plugin/Completions/Logic.hs | 4 +-- .../Development/IDE/Spans/Documentation.hs | 4 +-- ghcide/test/exe/Main.hs | 10 +++---- hls-graph/html/shake.js | 2 +- hls-graph/src/Control/Concurrent/STM/Stats.hs | 8 +++--- hls-plugin-api/src/Ide/PluginUtils.hs | 2 +- .../src/Ide/Plugin/Class/CodeAction.hs | 4 +-- .../src/Ide/Plugin/Class/Types.hs | 2 +- .../src/Ide/Plugin/CodeRange.hs | 2 +- .../src/Ide/Plugin/CodeRange/ASTPreProcess.hs | 2 +- plugins/hls-gadt-plugin/src/Ide/Plugin/GHC.hs | 14 +++++----- plugins/hls-gadt-plugin/test/Main.hs | 2 +- .../src/Ide/Plugin/HaddockComments/Data.hs | 22 ++++++++-------- .../hls-hlint-plugin/src/Ide/Plugin/Hlint.hs | 4 +-- .../src/Development/IDE/GHC/ExactPrint.hs | 2 +- .../src/Development/IDE/Plugin/CodeAction.hs | 12 ++++----- .../IDE/Plugin/CodeAction/ExactPrint.hs | 8 +++--- plugins/hls-refactor-plugin/test/Main.hs | 12 ++++----- .../src/Ide/Plugin/Rename.hs | 2 +- plugins/hls-retrie-plugin/changelog.md | 2 +- .../src/Ide/Plugin/Splice.hs | 6 ++--- .../src/Ide/Plugin/Splice/Types.hs | 2 +- plugins/hls-tactics-plugin/COMMANDS.md | 8 +++--- .../src/Wingman/AbstractLSP.hs | 2 +- .../src/Wingman/AbstractLSP/TacticActions.hs | 2 +- .../hls-tactics-plugin/src/Wingman/CodeGen.hs | 2 +- plugins/hls-tactics-plugin/src/Wingman/GHC.hs | 2 +- .../src/Wingman/Judgements.hs | 8 +++--- .../src/Wingman/Judgements/SYB.hs | 4 +-- .../src/Wingman/Judgements/Theta.hs | 2 +- .../src/Wingman/KnownStrategies.hs | 4 +-- .../src/Wingman/LanguageServer.hs | 2 +- .../src/Wingman/Machinery.hs | 2 +- .../Metaprogramming/Parser/Documentation.hs | 4 +-- .../src/Wingman/Simplify.hs | 6 ++--- plugins/hls-tactics-plugin/test/Utils.hs | 4 +-- .../src/Development/Benchmark/Rules.hs | 2 +- 50 files changed, 126 insertions(+), 126 deletions(-) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index a76fc1db19..7160816b96 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -206,5 +206,5 @@ This returns an error in HLS if `tasty-discover` is not in the path: `could not Due to some limitations in the interaction between HLS and `stack`, there are [issues](https://github.com/haskell/haskell-language-server/issues/366) in projects with multiple components (i.e. a main library and executables, test suites or benchmarks): -- The project has to be built succesfully *before* loading it with HLS to get components other than the library work. +- The project has to be built successfully *before* loading it with HLS to get components other than the library work. - Changes in the library are not automatically propagated to other components, especially in the presence of errors in the library. So you have to restart HLS in order for those components to be loaded correctly. The usual symptom is the editor showing errors like `Could not load module ...` or `Cannot satisfy -package ...`. diff --git a/ghcide-bench/exe/Main.hs b/ghcide-bench/exe/Main.hs index 9a7da8976d..e04b15f713 100644 --- a/ghcide-bench/exe/Main.hs +++ b/ghcide-bench/exe/Main.hs @@ -20,7 +20,7 @@ number of iterations. There is ample room for improvement: - Statistical analysis to detect outliers and auto infer the number of iterations needed - GC stats analysis (currently -S is printed as part of the experiment) - - Analyisis of performance over the commit history of the project + - Analysis of performance over the commit history of the project How to run: 1. `cabal exec cabal run ghcide-bench -- -- ghcide-bench-options` diff --git a/ghcide/CHANGELOG.md b/ghcide/CHANGELOG.md index ff893a1ffb..38557e9c1e 100644 --- a/ghcide/CHANGELOG.md +++ b/ghcide/CHANGELOG.md @@ -3,8 +3,8 @@ * Progress reporting improvements (#1784) - Pepe Iborra * Unify session loading using implicit-hie (#1783) - fendor * Fix remove constraint (#1578) - Kostas Dermentzis -* Fix wrong extend import while type constuctor and data constructor have the same name (#1775) - Lei Zhu -* Imporve vscode extension schema generation (#1742) - Potato Hatsue +* Fix wrong extend import while type constructor and data constructor have the same name (#1775) - Lei Zhu +* Improve vscode extension schema generation (#1742) - Potato Hatsue * Add hls-graph abstracting over shake (#1748) - Neil Mitchell * Tease apart the custom SYB from ExactPrint (#1746) - Sandy Maguire * fix class method completion (#1741) - Lei Zhu diff --git a/ghcide/session-loader/Development/IDE/Session.hs b/ghcide/session-loader/Development/IDE/Session.hs index 886b035bb7..2f2f9fcf30 100644 --- a/ghcide/session-loader/Development/IDE/Session.hs +++ b/ghcide/session-loader/Development/IDE/Session.hs @@ -362,7 +362,7 @@ runWithDb recorder fp k = do withHieDb fp $ \writedb -> do -- the type signature is necessary to avoid concretizing the tyvar - -- e.g. `withWriteDbRetrable initConn` without type signature will + -- e.g. `withWriteDbRetryable initConn` without type signature will -- instantiate tyvar `a` to `()` let withWriteDbRetryable :: WithHieDb withWriteDbRetryable = makeWithHieDbRetryable recorder rng writedb diff --git a/ghcide/src/Development/IDE/Core/Compile.hs b/ghcide/src/Development/IDE/Core/Compile.hs index 978e0ceccb..68e7b29000 100644 --- a/ghcide/src/Development/IDE/Core/Compile.hs +++ b/ghcide/src/Development/IDE/Core/Compile.hs @@ -187,10 +187,10 @@ typecheckModule (IdeDefer defer) hsc tc_helpers pm = do tcRnModule session tc_helpers $ demoteIfDefer pm{pm_mod_summary = mod_summary''} let errorPipeline = unDefer . hideDiag dflags . tagDiag diags = map errorPipeline warnings - deferedError = any fst diags + deferredError = any fst diags case etcm of Left errs -> return (map snd diags ++ errs, Nothing) - Right tcm -> return (map snd diags, Just $ tcm{tmrDeferedError = deferedError}) + Right tcm -> return (map snd diags, Just $ tcm{tmrDeferredError = deferredError}) where demoteIfDefer = if defer then demoteTypeErrorsToWarnings else id @@ -494,7 +494,7 @@ mkHiFileResultCompile se session' tcm simplified_guts = catchErrs $ do writeBinCoreFile fp core_file -- We want to drop references to guts and read in a serialized, compact version -- of the core file from disk (as it is deserialised lazily) - -- This is because we don't want to keep the guts in memeory for every file in + -- This is because we don't want to keep the guts in memory for every file in -- the project as it becomes prohibitively expensive -- The serialized file however is much more compact and only requires a few -- hundred megabytes of memory total even in a large project with 1000s of @@ -503,7 +503,7 @@ mkHiFileResultCompile se session' tcm simplified_guts = catchErrs $ do pure $ assert (core_hash1 == core_hash2) $ Just (core_file, fingerprintToBS core_hash2) - -- Verify core file by rountrip testing and comparison + -- Verify core file by roundtrip testing and comparison IdeOptions{optVerifyCoreFile} <- getIdeOptionsIO se case core_file of Just (core, _) | optVerifyCoreFile -> do @@ -773,7 +773,7 @@ generateHieAsts hscEnv tcm = -- These varBinds use unitDataConId but it could be anything as the id name is not used -- during the hie file generation process. It's a workaround for the fact that the hie modules -- don't export an interface which allows for additional information to be added to hie files. - let fake_splice_binds = Util.listToBag (map (mkVarBind unitDataConId) (spliceExpresions $ tmrTopLevelSplices tcm)) + let fake_splice_binds = Util.listToBag (map (mkVarBind unitDataConId) (spliceExpressions $ tmrTopLevelSplices tcm)) real_binds = tcg_binds $ tmrTypechecked tcm #if MIN_VERSION_ghc(9,0,1) ts = tmrTypechecked tcm :: TcGblEnv @@ -801,8 +801,8 @@ generateHieAsts hscEnv tcm = #endif #endif -spliceExpresions :: Splices -> [LHsExpr GhcTc] -spliceExpresions Splices{..} = +spliceExpressions :: Splices -> [LHsExpr GhcTc] +spliceExpressions Splices{..} = DL.toList $ mconcat [ DL.fromList $ map fst exprSplices , DL.fromList $ map fst patSplices @@ -812,7 +812,7 @@ spliceExpresions Splices{..} = ] -- | In addition to indexing the `.hie` file, this function is responsible for --- maintaining the 'IndexQueue' state and notfiying the user about indexing +-- maintaining the 'IndexQueue' state and notifying the user about indexing -- progress. -- -- We maintain a record of all pending index operations in the 'indexPending' @@ -1409,7 +1409,7 @@ instance NFData IdeLinkable where ml_core_file :: ModLocation -> FilePath ml_core_file ml = ml_hi_file ml <.> "core" --- | Retuns an up-to-date module interface, regenerating if needed. +-- | Returns an up-to-date module interface, regenerating if needed. -- Assumes file exists. -- Requires the 'HscEnv' to be set up with dependencies -- See Note [Recompilation avoidance in the presence of TH] @@ -1437,7 +1437,7 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do -- The source is modified if it is newer than the destination (iface file) -- A more precise check for the core file is performed later let sourceMod = case mb_dest_version of - Nothing -> SourceModified -- desitination file doesn't exist, assume modified source + Nothing -> SourceModified -- destination file doesn't exist, assume modified source Just dest_version | source_version <= dest_version -> SourceUnmodified | otherwise -> SourceModified @@ -1466,7 +1466,7 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do Just (old_hir, _) | isNothing linkableNeeded || isJust (hirCoreFp old_hir) -> do - -- Peform the fine grained recompilation check for TH + -- Perform the fine grained recompilation check for TH maybe_recomp <- checkLinkableDependencies get_linkable_hashes (hsc_mod_graph sessionWithMsDynFlags) (hirRuntimeModules old_hir) case maybe_recomp of Just msg -> do_regenerate msg @@ -1478,7 +1478,7 @@ loadInterface session ms linkableNeeded RecompilationInfo{..} = do let runtime_deps | not (mi_used_th iface) = emptyModuleEnv | otherwise = parseRuntimeDeps (md_anns details) - -- Peform the fine grained recompilation check for TH + -- Perform the fine grained recompilation check for TH maybe_recomp <- checkLinkableDependencies get_linkable_hashes (hsc_mod_graph sessionWithMsDynFlags) runtime_deps case maybe_recomp of Just msg -> do_regenerate msg @@ -1598,7 +1598,7 @@ coreFileToLinkable linkableType session ms iface details core_file t = do --- and leads to fun errors like "Cannot continue after interface file error". getDocsBatch :: HscEnv - -> Module -- ^ a moudle where the names are in scope + -> Module -- ^ a module where the names are in scope -> [Name] #if MIN_VERSION_ghc(9,3,0) -> IO [Either String (Maybe [HsDoc GhcRn], IntMap (HsDoc GhcRn))] diff --git a/ghcide/src/Development/IDE/Core/RuleTypes.hs b/ghcide/src/Development/IDE/Core/RuleTypes.hs index 1aa88ed800..92145d494c 100644 --- a/ghcide/src/Development/IDE/Core/RuleTypes.hs +++ b/ghcide/src/Development/IDE/Core/RuleTypes.hs @@ -155,7 +155,7 @@ data TcModuleResult = TcModuleResult , tmrTypechecked :: TcGblEnv , tmrTopLevelSplices :: Splices -- ^ Typechecked splice information - , tmrDeferedError :: !Bool + , tmrDeferredError :: !Bool -- ^ Did we defer any type errors for this module? , tmrRuntimeModules :: !(ModuleEnv ByteString) -- ^ Which modules did we need at runtime while compiling this file? diff --git a/ghcide/src/Development/IDE/Core/Rules.hs b/ghcide/src/Development/IDE/Core/Rules.hs index 83a8e9bad8..d241541ac6 100644 --- a/ghcide/src/Development/IDE/Core/Rules.hs +++ b/ghcide/src/Development/IDE/Core/Rules.hs @@ -782,7 +782,7 @@ ghcSessionDepsDefinition fullModSummary GhcSessionDepsConfig{..} env file = do let inLoadOrder = map (\HiFileResult{..} -> HomeModInfo hirModIface hirModDetails Nothing) ifaces #if MIN_VERSION_ghc(9,3,0) -- On GHC 9.4+, the module graph contains not only ModSummary's but each `ModuleNode` in the graph - -- also points to all the direct descendents of the current module. To get the keys for the descendents + -- also points to all the direct descendants of the current module. To get the keys for the descendants -- we must get their `ModSummary`s !final_deps <- do dep_mss <- map msrModSummary <$> uses_ GetModSummaryWithoutTimestamps deps @@ -950,7 +950,7 @@ getModIfaceRule recorder = defineEarlyCutoff (cmapWithPrio LogShake recorder) $ hiDiags <- case hiFile of Just hiFile | OnDisk <- status - , not (tmrDeferedError tmr) -> liftIO $ writeHiFile se hsc hiFile + , not (tmrDeferredError tmr) -> liftIO $ writeHiFile se hsc hiFile _ -> pure [] return (fp, (diags++hiDiags, hiFile)) NotFOI -> do @@ -1022,9 +1022,9 @@ regenerateHiFile sess f ms compNeeded = do wDiags <- forM masts $ \asts -> liftIO $ writeAndIndexHieFile hsc se (tmrModSummary tmr) f (tcg_exports $ tmrTypechecked tmr) asts source - -- We don't write the `.hi` file if there are defered errors, since we won't get + -- We don't write the `.hi` file if there are deferred errors, since we won't get -- accurate diagnostics next time if we do - hiDiags <- if not $ tmrDeferedError tmr + hiDiags <- if not $ tmrDeferredError tmr then liftIO $ writeHiFile se hsc hiFile else pure [] @@ -1057,7 +1057,7 @@ getClientSettingsRule recorder = defineEarlyCutOffNoFile (cmapWithPrio LogShake settings <- clientSettings <$> getIdeConfiguration return (LBS.toStrict $ B.encode $ hash settings, settings) --- | Returns the client configurarion stored in the IdeState. +-- | Returns the client configuration stored in the IdeState. -- You can use this function to access it from shake Rules getClientConfigAction :: Config -- ^ default value -> Action Config diff --git a/ghcide/src/Development/IDE/Core/Shake.hs b/ghcide/src/Development/IDE/Core/Shake.hs index 2f2234cbca..ea0088bf8e 100644 --- a/ghcide/src/Development/IDE/Core/Shake.hs +++ b/ghcide/src/Development/IDE/Core/Shake.hs @@ -256,7 +256,7 @@ data ShakeExtras = ShakeExtras -- ^ Map from a text document version to a PositionMapping that describes how to map -- positions in a version of that document to positions in the latest version -- First mapping is delta from previous version and second one is an - -- accumlation of all previous mappings. + -- accumulation of all previous mappings. ,progress :: ProgressReporting ,ideTesting :: IdeTesting -- ^ Whether to enable additional lsp messages used by the test suite for checking invariants @@ -280,12 +280,12 @@ data ShakeExtras = ShakeExtras , withHieDb :: WithHieDb -- ^ Use only to read. , hiedbWriter :: HieDbWriter -- ^ use to write , persistentKeys :: TVar (KeyMap GetStalePersistent) - -- ^ Registery for functions that compute/get "stale" results for the rule + -- ^ Registry for functions that compute/get "stale" results for the rule -- (possibly from disk) , vfsVar :: TVar VFS -- ^ A snapshot of the current state of the virtual file system. Updated on shakeRestart -- VFS state is managed by LSP. However, the state according to the lsp library may be newer than the state of the current session, - -- leaving us vulnerable to suble race conditions. To avoid this, we take a snapshot of the state of the VFS on every + -- leaving us vulnerable to subtle race conditions. To avoid this, we take a snapshot of the state of the VFS on every -- restart, so that the whole session sees a single consistent view of the VFS. -- We don't need a STM.Map because we never update individual keys ourselves. , defaultConfig :: Config @@ -662,7 +662,7 @@ getStateKeys = (fmap.fmap) fst . atomically . ListT.toList . STM.listT . state -- | Must be called in the 'Initialized' handler and only once shakeSessionInit :: Recorder (WithPriority Log) -> IdeState -> IO () shakeSessionInit recorder ide@IdeState{..} = do - -- Take a snapshot of the VFS - it should be empty as we've recieved no notifications + -- Take a snapshot of the VFS - it should be empty as we've received no notifications -- till now, but it can't hurt to be in sync with the `lsp` library. vfs <- vfsSnapshot (lspEnv shakeExtras) initSession <- newSession recorder shakeExtras (VFSModified vfs) shakeDb [] "shakeSessionInit" @@ -831,7 +831,7 @@ instantiateDelayedAction (DelayedAction _ s p a) = do b <- newBarrier let a' = do -- work gets reenqueued when the Shake session is restarted - -- it can happen that a work item finished just as it was reenqueud + -- it can happen that a work item finished just as it was reenqueued -- in that case, skipping the work is fine alreadyDone <- liftIO $ isJust <$> waitBarrierMaybe b unless alreadyDone $ do diff --git a/ghcide/src/Development/IDE/GHC/Compat/CPP.hs b/ghcide/src/Development/IDE/GHC/Compat/CPP.hs index 9da9fa4786..905e42b5c0 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/CPP.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/CPP.hs @@ -3,7 +3,7 @@ -- Copied from https://github.com/ghc/ghc/blob/master/compiler/main/DriverPipeline.hs on 14 May 2019 -- Requested to be exposed at https://gitlab.haskell.org/ghc/ghc/merge_requests/944. --- Update the above MR got merged to master on 31 May 2019. When it becomes avialable to ghc-lib, this file can be removed. +-- Update the above MR got merged to master on 31 May 2019. When it becomes available to ghc-lib, this file can be removed. {- HLINT ignore -} -- since copied from upstream diff --git a/ghcide/src/Development/IDE/GHC/Compat/Parser.hs b/ghcide/src/Development/IDE/GHC/Compat/Parser.hs index 391ca9fb82..11773d233c 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Parser.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Parser.hs @@ -2,7 +2,7 @@ {-# LANGUAGE PatternSynonyms #-} {-# HLINT ignore "Unused LANGUAGE pragma" #-} --- | Parser compaibility module. +-- | Parser compatibility module. module Development.IDE.GHC.Compat.Parser ( initParserOpts, initParserState, diff --git a/ghcide/src/Development/IDE/Import/FindImports.hs b/ghcide/src/Development/IDE/Import/FindImports.hs index b0efe64858..a5b356a9a8 100644 --- a/ghcide/src/Development/IDE/Import/FindImports.hs +++ b/ghcide/src/Development/IDE/Import/FindImports.hs @@ -183,7 +183,7 @@ notFoundErr env modName reason = mkError' = diagFromString "not found" DsError (Compat.getLoc modName) modName0 = unLoc modName ppr' = showSDoc dfs - -- We convert the lookup result to a find result to reuse GHC's cannotFindMoudle pretty printer. + -- We convert the lookup result to a find result to reuse GHC's cannotFindModule pretty printer. lookupToFindResult = \case LookupFound _m _pkgConfig -> diff --git a/ghcide/src/Development/IDE/Main/HeapStats.hs b/ghcide/src/Development/IDE/Main/HeapStats.hs index c998630f6a..a1c0b9f3d7 100644 --- a/ghcide/src/Development/IDE/Main/HeapStats.hs +++ b/ghcide/src/Development/IDE/Main/HeapStats.hs @@ -59,8 +59,8 @@ heapStatsThread l = forever $ do threadDelay heapStatsInterval logHeapStats l --- | A helper function which lauches the 'heapStatsThread' and kills it --- appropiately when the inner action finishes. It also checks to see +-- | A helper function which launches the 'heapStatsThread' and kills it +-- appropriately when the inner action finishes. It also checks to see -- if `-T` is enabled. withHeapStats :: Recorder (WithPriority Log) -> IO r -> IO r withHeapStats l k = do diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index a7fea1a075..79cb8420fe 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -80,7 +80,7 @@ produceCompletions recorder = do _ -> return ([], Nothing) define (cmapWithPrio LogShake recorder) $ \NonLocalCompletions file -> do -- For non local completions we avoid depending on the parsed module, - -- synthetizing a fake module with an empty body from the buffer + -- synthesizing a fake module with an empty body from the buffer -- in the ModSummary, which preserves all the imports ms <- fmap fst <$> useWithStale GetModSummaryWithoutTimestamps file sess <- fmap fst <$> useWithStale GhcSessionDeps file diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index 7d2190cac8..b6f652fbf0 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -366,11 +366,11 @@ cacheDataProducer uri env curMod globalEnv inScopeEnv limports = do asNamespace :: ImportDecl GhcPs -> ModuleName asNamespace imp = maybe (iDeclToModName imp) GHC.unLoc (ideclAs imp) -- Full canonical names of imported modules - importDeclerations = map unLoc limports + importDeclarations = map unLoc limports -- The given namespaces for the imported modules (ie. full name, or alias if used) - allModNamesAsNS = map (showModName . asNamespace) importDeclerations + allModNamesAsNS = map (showModName . asNamespace) importDeclarations rdrElts = globalRdrEnvElts globalEnv diff --git a/ghcide/src/Development/IDE/Spans/Documentation.hs b/ghcide/src/Development/IDE/Spans/Documentation.hs index 63f84966a3..367d756dfc 100644 --- a/ghcide/src/Development/IDE/Spans/Documentation.hs +++ b/ghcide/src/Development/IDE/Spans/Documentation.hs @@ -128,7 +128,7 @@ getDocumentation -- TODO : Build a version of GHC exactprint to extract this information -- more accurately. -- TODO : Implement this for GHC 9.2 with in-tree annotations --- (alternatively, just remove it and rely soley on GHC's parsing) +-- (alternatively, just remove it and rely solely on GHC's parsing) getDocumentation sources targetName = fromMaybe [] $ do #if MIN_VERSION_ghc(9,2,0) Nothing @@ -137,7 +137,7 @@ getDocumentation sources targetName = fromMaybe [] $ do targetNameSpan <- realSpan $ getLoc targetName tc <- find ((==) (Just $ srcSpanFile targetNameSpan) . annotationFileName) - $ reverse sources -- TODO : Is reversing the list here really neccessary? + $ reverse sources -- TODO : Is reversing the list here really necessary? -- Top level names bound by the module let bs = [ n | let L _ HsModule{hsmodDecls} = pm_parsed_source tc diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index accfade90e..1f745b527a 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -593,7 +593,7 @@ diagnosticTests = testGroup "diagnostics" [ ( "Foo.hs" -- The test is to make sure that warnings contain unqualified names -- where appropriate. The warning should use an unqualified name 'Ord', not - -- sometihng like 'GHC.Classes.Ord'. The choice of redundant-constraints to + -- something like 'GHC.Classes.Ord'. The choice of redundant-constraints to -- test this is fairly arbitrary. , [(DsWarning, (2, if ghcVersion >= GHC94 then 7 else 0), "Redundant constraint: Ord a") ] @@ -2593,7 +2593,7 @@ simpleMultiTest3 = checkDefs locs (pure [fooL]) expectNoMoreDiagnostics 0.5 --- Like simpleMultiTest but open the files in component 'a' in a seperate session +-- Like simpleMultiTest but open the files in component 'a' in a separate session simpleMultiDefTest :: TestTree simpleMultiDefTest = testCase "simple-multi-def-test" $ runWithExtraFiles "multi" $ \dir -> do let aPath = dir "a/A.hs" @@ -2670,7 +2670,7 @@ ifaceTHTest = testCase "iface-th-test" $ runWithExtraFiles "TH" $ \dir -> do -- Change [TH]a from () to Bool liftIO $ writeFileUTF8 aPath (unlines $ init (lines $ T.unpack aSource) ++ ["th_a = [d| a = False|]"]) - -- Check that the change propogates to C + -- Check that the change propagates to C changeDoc cdoc [TextDocumentContentChangeEvent Nothing Nothing cSource] expectDiagnostics [("THC.hs", [(DsError, (4, 4), "Couldn't match expected type '()' with actual type 'Bool'")]) @@ -2694,11 +2694,11 @@ ifaceErrorTest = testCase "iface-error-test-1" $ runWithExtraFiles "recomp" $ \d -- Change y from Int to B changeDoc bdoc [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["module B where", "y :: Bool", "y = undefined"]] - -- save so that we can that the error propogates to A + -- save so that we can that the error propagates to A sendNotification STextDocumentDidSave (DidSaveTextDocumentParams bdoc Nothing) - -- Check that the error propogates to A + -- Check that the error propagates to A expectDiagnostics [("A.hs", [(DsError, (5, 4), "Couldn't match expected type 'Int' with actual type 'Bool'")])] diff --git a/hls-graph/html/shake.js b/hls-graph/html/shake.js index 90a8f835cd..03e9f78f71 100644 --- a/hls-graph/html/shake.js +++ b/hls-graph/html/shake.js @@ -981,7 +981,7 @@ function speculativeCriticalPath(profile) { return maxCriticalPath; } /* -Calculating a precise critical path, taking into account the deep dependeny structure, is non-obvious. +Calculating a precise critical path, taking into account the deep dependency structure, is non-obvious. Dependencies have the type [{X}], e.g: X = [{a,b},{c,d}] diff --git a/hls-graph/src/Control/Concurrent/STM/Stats.hs b/hls-graph/src/Control/Concurrent/STM/Stats.hs index a79f287ef9..1fc920ff2c 100644 --- a/hls-graph/src/Control/Concurrent/STM/Stats.hs +++ b/hls-graph/src/Control/Concurrent/STM/Stats.hs @@ -58,7 +58,7 @@ data TrackSTMConf = TrackSTMConf { tryThreshold :: Maybe Int -- ^ If the number of retries of one transaction run reaches this -- count, a warning is issued at runtime. If set to @Nothing@, disables the warnings completely. - , globalTheshold :: Maybe Int + , globalThreshold :: Maybe Int -- ^ If the total number of retries of one named transaction reaches -- this count, a warning is issued. If set to @Nothing@, disables the -- warnings completely. @@ -79,7 +79,7 @@ data TrackSTMConf = TrackSTMConf -- -- > defaultTrackSTMConf = TrackSTMConf -- > { tryThreshold = Just 10 --- > , globalTheshold = Just 3000 +-- > , globalThreshold = Just 3000 -- > , exception = True -- > , warnFunction = hPutStrLn stderr -- > , warnInSTMFunction = \_ -> return () @@ -87,7 +87,7 @@ data TrackSTMConf = TrackSTMConf defaultTrackSTMConf :: TrackSTMConf defaultTrackSTMConf = TrackSTMConf { tryThreshold = Just 10 - , globalTheshold = Just 3000 + , globalThreshold = Just 3000 , extendException = True , warnFunction = hPutStrLn stderr , warnInSTMFunction = \_ -> return () @@ -143,7 +143,7 @@ trackSTMConf (TrackSTMConf {..}) name txm = do (1,i) m in (m', let j = maybe 0 snd oldVal in (j,j+i)) - doMB globalTheshold $ \globalRetryThreshold -> + doMB globalThreshold $ \globalRetryThreshold -> when (k `div` globalRetryThreshold /= k' `div` globalRetryThreshold) $ warnFunction $ msgPrefix ++ " reached global retry count of " ++ show k' diff --git a/hls-plugin-api/src/Ide/PluginUtils.hs b/hls-plugin-api/src/Ide/PluginUtils.hs index 5c93407974..3203cbcf8a 100644 --- a/hls-plugin-api/src/Ide/PluginUtils.hs +++ b/hls-plugin-api/src/Ide/PluginUtils.hs @@ -173,7 +173,7 @@ idePluginsToPluginDesc (IdePlugins pp) = pp -- --------------------------------------------------------------------- -- | Returns the current client configuration. It is not wise to permanently --- cache the returned value of this function, as clients can at runitime change +-- cache the returned value of this function, as clients can at runtime change -- their configuration. -- getClientConfig :: MonadLsp Config m => m Config diff --git a/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeAction.hs b/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeAction.hs index 23cfaf6973..43fa3a9e99 100644 --- a/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeAction.hs +++ b/plugins/hls-class-plugin/src/Ide/Plugin/Class/CodeAction.hs @@ -62,13 +62,13 @@ addMethodPlaceholders _ state param@AddMinimalMethodsParams{..} = do pure Null where - toTextDocunemtEdit edit = + toTextDocumentEdit edit = TextDocumentEdit (VersionedTextDocumentIdentifier uri (Just 0)) (List [InL edit]) mergeEdit :: WorkspaceEdit -> [TextEdit] -> WorkspaceEdit mergeEdit WorkspaceEdit{..} edits = WorkspaceEdit { _documentChanges = - (\(List x) -> List $ x ++ map (InL . toTextDocunemtEdit) edits) + (\(List x) -> List $ x ++ map (InL . toTextDocumentEdit) edits) <$> _documentChanges , .. } diff --git a/plugins/hls-class-plugin/src/Ide/Plugin/Class/Types.hs b/plugins/hls-class-plugin/src/Ide/Plugin/Class/Types.hs index b572549325..95579a7cdc 100644 --- a/plugins/hls-class-plugin/src/Ide/Plugin/Class/Types.hs +++ b/plugins/hls-class-plugin/src/Ide/Plugin/Class/Types.hs @@ -68,7 +68,7 @@ data Log instance Pretty Log where pretty = \case LogImplementedMethods cls methods -> - pretty ("Detected implmented methods for class" :: String) + pretty ("Detected implemented methods for class" :: String) <+> pretty (show (getOccString cls) <> ":") -- 'show' is used here to add quotes around the class name <+> pretty methods LogShake log -> pretty log diff --git a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs index 699e51f1f7..d6dfd2820a 100644 --- a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs +++ b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange.hs @@ -144,7 +144,7 @@ getSelectionRanges :: NormalizedFilePath -> [Position] -> ExceptT SelectionRange getSelectionRanges file positions = do (codeRange, positionMapping) <- maybeToExceptT (SelectionRangeBadDependency GetCodeRange) . MaybeT $ useWithStaleFast GetCodeRange file - -- 'positionMapping' should be appied to the input before using them + -- 'positionMapping' should be applied to the input before using them positions' <- maybeToExceptT SelectionRangeInputPositionMappingFailure . MaybeT . pure $ traverse (fromCurrentPosition positionMapping) positions diff --git a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/ASTPreProcess.hs b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/ASTPreProcess.hs index d44ed3debd..d2ee4c1c02 100644 --- a/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/ASTPreProcess.hs +++ b/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/ASTPreProcess.hs @@ -168,7 +168,7 @@ identifierForTypeSig node = (fmap fst . find (\(_, detail) -> TyDecl `Set.member` identInfo detail) . Map.toList . nodeIdentifiers) --- | Determines if the given occurence of an identifier is a function/variable definition in the outer span +-- | Determines if the given occurrence of an identifier is a function/variable definition in the outer span isIdentADef :: Span -> (Span, IdentifierDetails a) -> Bool isIdentADef outerSpan (span, detail) = realSrcSpanStart span >= realSrcSpanStart outerSpan && realSrcSpanEnd span <= realSrcSpanEnd outerSpan diff --git a/plugins/hls-gadt-plugin/src/Ide/Plugin/GHC.hs b/plugins/hls-gadt-plugin/src/Ide/Plugin/GHC.hs index fe4ea1876c..bd651ef0bf 100644 --- a/plugins/hls-gadt-plugin/src/Ide/Plugin/GHC.hs +++ b/plugins/hls-gadt-plugin/src/Ide/Plugin/GHC.hs @@ -79,13 +79,13 @@ h98ToGADTDecl = \case } x -> x --- | Convert H98 data constuctor to GADT data constructor +-- | Convert H98 data constructor to GADT data constructor h98ToGADTConDecl :: - LIdP GP -- ^Type constuctor name, - -- used for constucting final result type in GADT + LIdP GP -- ^Type constructor name, + -- used for constructing final result type in GADT -> LHsQTyVars GP -- ^Type variable names - -- used for constucting final result type in GADT + -- used for constructing final result type in GADT -> Maybe (LHsContext GP) -- ^Data type context -> ConDecl GP @@ -203,7 +203,7 @@ prettyGADTDecl df decl = where go (Anchor a _) = Anchor a (MovedAnchor (DifferentLine 1 2)) - -- Adjust where annotation to the same line of the type constuctor + -- Adjust where annotation to the same line of the type constructor adjustWhere tcdDExt = tcdDExt <&> map (\(AddEpAnn ann l) -> if ann == AnnWhere @@ -237,7 +237,7 @@ prettyGADTDecl df decl = | isConDeclGADTAnn key = adjustCon ann | otherwise = ann - -- Adjust where annotation to the same line of the type constuctor + -- Adjust where annotation to the same line of the type constructor adjustWhere Ann{..} = Ann { annsDP = annsDP <&> (\(keyword, dp) -> @@ -249,7 +249,7 @@ prettyGADTDecl df decl = -- Make every data constructor start with a new line and 2 spaces -- - -- Here we can't force every GADT constuctor has (1, 2) + -- Here we can't force every GADT constructor has (1, 2) -- delta. For the first constructor with (1, 2), it prints -- a new line with 2 spaces, but for other constructors -- with (1, 2), it will print a new line with 4 spaces. diff --git a/plugins/hls-gadt-plugin/test/Main.hs b/plugins/hls-gadt-plugin/test/Main.hs index 1f99e5f8cc..bcde384232 100644 --- a/plugins/hls-gadt-plugin/test/Main.hs +++ b/plugins/hls-gadt-plugin/test/Main.hs @@ -32,7 +32,7 @@ tests = testGroup "GADT" , runTest "DataContext" "DataContext" 2 0 2 31 , runTest "DataContextParen" "DataContextParen" 2 0 3 6 , runTest "Forall" "Forall" 2 0 2 44 - , runTest "ConstuctorContext" "ConstructorContext" 2 0 2 38 + , runTest "ConstructorContext" "ConstructorContext" 2 0 2 38 , runTest "Context" "Context" 2 0 4 41 , runTest "Pragma" "Pragma" 2 0 3 29 , onlyWorkForGhcVersions (==GHC92) "Single deriving has different output on ghc9.2" $ diff --git a/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments/Data.hs b/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments/Data.hs index 3c37556841..373f2d84ea 100644 --- a/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments/Data.hs +++ b/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments/Data.hs @@ -47,10 +47,10 @@ addHaddockCommentsToList :: (Data a, Monad m) => Bool -- ^ If true, for each node, use previous node in the list as the anchor. Otherwise, use the outer node -> SrcSpan -- ^ The outer node - -> KeywordId -- ^ The seperator between adjacent nodes + -> KeywordId -- ^ The separator between adjacent nodes -> [Located a] -- ^ The list of nodes. Haddock comments will be added to each of them -> TransformT m () -addHaddockCommentsToList usePrevNodeAsAnchor outerLoc seperator nodes = +addHaddockCommentsToList usePrevNodeAsAnchor outerLoc separator nodes = -- If you want to understand this function, please first read this page carefully: -- https://hackage.haskell.org/package/ghc-exactprint-0.6.4/docs/Language-Haskell-GHC-ExactPrint-Delta.html -- The important part is that for DP(r,c), if r is zero, c is the offset start from the end of the previous node. @@ -71,23 +71,23 @@ addHaddockCommentsToList usePrevNodeAsAnchor outerLoc seperator nodes = -- For the multiline case (which is the most common), we keep the original indentation of each constructor -- and field. -- - -- For the inline case, we use the first construcotr/field as the base, and align all following items + -- For the inline case, we use the first constructor/field as the base, and align all following items -- to them. let sameLineAsPrev = prevNode >>= ( - \prevNode' -> if notSeperatedByLineEnding prevNode' node + \prevNode' -> if notSeparatedByLineEnding prevNode' node then pure prevNode' else Nothing ) - -- For the inline case, we need to move the seperator to the next line. + -- For the inline case, we need to move the separator to the next line. -- For constructors, it's vertical bar; for fields, it's comma. - -- The seperator is passed in as function argument. + -- The separator is passed in as function argument. when (isJust sameLineAsPrev) $ modifyAnnsT $ \anns -> let newSepCol :: Annotation -> Int newSepCol ann = if usePrevNodeAsAnchor then 0 else deltaColumn (annEntryDelta ann) updateSepAnn :: Annotation -> Annotation updateSepAnn ann = ann {annsDP = - Map.toList . Map.adjust (const $ DP (1, newSepCol ann)) seperator . Map.fromList $ annsDP ann} + Map.toList . Map.adjust (const $ DP (1, newSepCol ann)) separator . Map.fromList $ annsDP ann} in flip (maybe anns) prevNode $ \prevNode' -> Map.adjust updateSepAnn (mkAnnKey prevNode') anns -- Calculate the real column of the anchor let anchorCol = maybe 0 srcSpanStartCol . realSpan . maybe outerLoc getLoc $ @@ -110,7 +110,7 @@ addHaddockCommentsToList usePrevNodeAsAnchor outerLoc seperator nodes = let updateCurrent :: Annotation -> Annotation updateCurrent ann = ann { -- If there exist non-haddock comments, we simply inherit the first one's delta pos, - -- and move them two lines below, to seperate them from our newly added haddock comments + -- and move them two lines below, to separate them from our newly added haddock comments -- Otherwise, inherit the node's entry delta pos. annPriorComments = case annPriorComments ann of (c, dp) : rem -> (emptyPriorHaddockComment, dp) : (c, DP (2,0)) : rem @@ -127,12 +127,12 @@ missingSomeHaddock anns = any $ \lcon@(L _ conDecl) -> case conDecl of _ -> False -- GADT is not supported yet -- | Returns 'True' if the end of the first node and the start of the second node are on the same line. -notSeperatedByLineEnding :: Located a +notSeparatedByLineEnding :: Located a -> Located a -> Bool -notSeperatedByLineEnding (L (RealSrcSpan x _) _) (L (RealSrcSpan y _) _) = +notSeparatedByLineEnding (L (RealSrcSpan x _) _) (L (RealSrcSpan y _) _) = srcLocLine (realSrcSpanEnd x) == srcLocLine (realSrcSpanStart y) -notSeperatedByLineEnding _ _ = False +notSeparatedByLineEnding _ _ = False -- | Empty haddock, suitable for being added to 'annPriorComments' emptyPriorHaddockComment :: Comment diff --git a/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs b/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs index 9f75dff9f3..e1efc2869d 100644 --- a/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs +++ b/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs @@ -430,7 +430,7 @@ codeActionProvider ideState pluginId (CodeActionParams _ _ documentId _ context) LSP.List diags = context ^. LSP.diagnostics --- | Convert a hlint diagonistic into an apply and an ignore code action +-- | Convert a hlint diagnostic into an apply and an ignore code action -- if applicable diagnosticToCodeActions :: DynFlags -> T.Text -> PluginId -> TextDocumentIdentifier -> LSP.Diagnostic -> [LSP.CodeAction] diagnosticToCodeActions dynFlags fileContents pluginId documentId diagnostic @@ -555,7 +555,7 @@ applyHint recorder ide nfp mhint = modsum <- liftIO $ runAction' $ use_ GetModSummary nfp let dflags = ms_hspp_opts $ msrModSummary modsum -- Setting a environment variable with the libdir used by ghc-exactprint. - -- It is a workaround for an error caused by the use of a hadcoded at compile time libdir + -- It is a workaround for an error caused by the use of a hardcoded at compile time libdir -- in ghc-exactprint that makes dependent executables non portables. -- See https://github.com/alanz/ghc-exactprint/issues/96. -- WARNING: this code is not thread safe, so if you try to apply several async refactorings diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs index 67c1f89f32..beb5fb52ed 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs @@ -319,7 +319,7 @@ getNeedsSpaceAndParenthesize :: getNeedsSpaceAndParenthesize dst a = -- Traverse the tree, looking for our replacement node. But keep track of -- the context (parent HsExpr constructor) we're in while we do it. This - -- lets us determine wehther or not we need parentheses. + -- lets us determine whether or not we need parentheses. let (needs_parens, needs_space) = everythingWithContext (Nothing, Nothing) (<>) ( mkQ (mempty, ) $ \x s -> case x of diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index 442ffcb253..d9b11f8786 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -160,7 +160,7 @@ typeSigsPluginDescriptor recorder plId = mkExactprintPluginDescriptor recorder $ mkGhcideCAsPlugin [ wrap $ suggestSignature True , wrap suggestFillTypeWildcard - , wrap suggestAddTypeAnnotationToSatisfyContraints + , wrap suggestAddTypeAnnotationToSatisfyConstraints #if !MIN_VERSION_ghc(9,3,0) , wrap removeRedundantConstraints , wrap suggestConstraint @@ -396,7 +396,7 @@ suggestHideShadow ps fileContents mTcM mHar Diagnostic {_message, _range} Just matched <- allMatchRegexUnifySpaces _message "imported from ‘([^’]+)’ at ([^ ]*)", mods <- [(modName, s) | [_, modName, s] <- matched], result <- nubOrdBy (compare `on` fst) $ mods >>= uncurry (suggests identifier), - hideAll <- ("Hide " <> identifier <> " from all occurence imports", concatMap snd result) = + hideAll <- ("Hide " <> identifier <> " from all occurrence imports", concatMap snd result) = result <> [hideAll] | otherwise = [] where @@ -793,8 +793,8 @@ suggestExportUnusedTopBinding srcOpt ParsedModule{pm_parsed_source = L _ HsModul exportsAs (TyClD _ FamDecl{tcdFam}) = Just (ExportFamily, reLoc $ fdLName tcdFam) exportsAs _ = Nothing -suggestAddTypeAnnotationToSatisfyContraints :: Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])] -suggestAddTypeAnnotationToSatisfyContraints sourceOpt Diagnostic{_range=_range,..} +suggestAddTypeAnnotationToSatisfyConstraints :: Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])] +suggestAddTypeAnnotationToSatisfyConstraints sourceOpt Diagnostic{_range=_range,..} -- File.hs:52:41: warning: -- * Defaulting the following constraint to type ‘Integer’ -- Num p0 arising from the literal ‘1’ @@ -1734,7 +1734,7 @@ findPositionAfterModuleName ps hsmodName' = do prevSrcSpan = maybe (getLoc hsmodName') getLoc hsmodExports -- The relative position of 'where' keyword (in lines, relative to the previous AST node). - -- The exact-print API changed a lot in ghc-9.2, so we need to handle it seperately for different compiler versions. + -- The exact-print API changed a lot in ghc-9.2, so we need to handle it separately for different compiler versions. whereKeywordLineOffset :: Maybe Int #if MIN_VERSION_ghc(9,2,0) whereKeywordLineOffset = case hsmodAnn of @@ -1767,7 +1767,7 @@ findPositionAfterModuleName ps hsmodName' = do deltaPos <- fmap NE.head . NE.nonEmpty .mapMaybe filterWhere $ annsDP ann pure $ deltaRow deltaPos - -- Before ghc 9.2, DeltaPos doesn't take comment into acccount, so we don't need to sum line offset of comments. + -- Before ghc 9.2, DeltaPos doesn't take comment into account, so we don't need to sum line offset of comments. filterWhere :: (KeywordId, DeltaPos) -> Maybe DeltaPos filterWhere (keywordId, deltaPos) = if keywordId == G AnnWhere then Just deltaPos else Nothing diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index 57da3ee2f6..4a64486c90 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -385,7 +385,7 @@ extendImportTopLevel thing (L l it@ImportDecl{..}) nodeHasComma x = isJust $ Map.lookup (mkAnnKey x) anns >>= find isAnnComma . annsDP when shouldAddTrailingComma (addTrailingCommaT x) - -- Parens are attachted to `lies`, so if `lies` was empty previously, + -- Parens are attached to `lies`, so if `lies` was empty previously, -- we need change the ann key from `[]` to `:` to keep parens and other anns. unless hasSibling $ transferAnn (L l' lies) (L l' [x]) id @@ -435,7 +435,7 @@ extendImportViaParent df parent child (L l it@ImportDecl{..}) childLIE = reLocA $ L srcChild $ IEName childRdr #if !MIN_VERSION_ghc(9,2,0) x :: LIE GhcPs = L ll' $ IEThingWith noExtField absIE NoIEWildcard [childLIE] [] - -- take anns from ThingAbs, and attatch parens to it + -- take anns from ThingAbs, and attach parens to it transferAnn lAbs x $ \old -> old{annsDP = annsDP old ++ [(G AnnOpenP, DP (0, 1)), (G AnnCloseP, dp00)]} addSimpleAnnT childRdr dp00 [(G AnnVal, dp00)] #else @@ -518,7 +518,7 @@ extendImportViaParent df parent child (L l it@ImportDecl{..}) addSimpleAnnT parentRdr (DP (0, if hasSibling then 1 else 0)) $ unqalDP 1 isParentOperator addSimpleAnnT childRdr (DP (0, 0)) [(G AnnVal, dp00)] addSimpleAnnT x (DP (0, 0)) [(G AnnOpenP, DP (0, 1)), (G AnnCloseP, DP (0, 0))] - -- Parens are attachted to `pre`, so if `pre` was empty previously, + -- Parens are attached to `pre`, so if `pre` was empty previously, -- we need change the ann key from `[]` to `:` to keep parens and other anns. unless hasSibling $ transferAnn (L l' $ reverse pre) (L l' [x]) id @@ -538,7 +538,7 @@ extendImportViaParent _ _ _ _ = lift $ Left "Unable to extend the import list vi addCommaInImportList :: -- | Initial list [LocatedAn AnnListItem a] - -- | Additionnal item + -- | Additional item -> LocatedAn AnnListItem a -> [LocatedAn AnnListItem a] addCommaInImportList lies x = diff --git a/plugins/hls-refactor-plugin/test/Main.hs b/plugins/hls-refactor-plugin/test/Main.hs index 124f28acf1..978488a307 100644 --- a/plugins/hls-refactor-plugin/test/Main.hs +++ b/plugins/hls-refactor-plugin/test/Main.hs @@ -469,12 +469,12 @@ insertImportTests = testGroup "insert import" "NoExplicitExports.expected.hs" "import Data.Monoid" , checkImport - "add to correctly placed exisiting import" + "add to correctly placed existing import" "ImportAtTop.hs" "ImportAtTop.expected.hs" "import Data.Monoid" , checkImport - "add to multiple correctly placed exisiting imports" + "add to multiple correctly placed existing imports" "MultipleImportsAtTop.hs" "MultipleImportsAtTop.expected.hs" "import Data.Monoid" @@ -1856,7 +1856,7 @@ suggestHideShadowTests = [ testGroup "single" [ testOneCodeAction - "hide unsued" + "hide unused" "Hide on from Data.Function" (1, 2) (1, 4) @@ -1869,7 +1869,7 @@ suggestHideShadowTests = , "g on = on" ] , testOneCodeAction - "extend hiding unsued" + "extend hiding unused" "Hide on from Data.Function" (1, 2) (1, 4) @@ -1880,7 +1880,7 @@ suggestHideShadowTests = , "f on = on" ] , testOneCodeAction - "delete unsued" + "delete unused" "Hide on from Data.Function" (1, 2) (1, 4) @@ -1982,7 +1982,7 @@ suggestHideShadowTests = ] , testOneCodeAction "auto hide all" - "Hide ++ from all occurence imports" + "Hide ++ from all occurrence imports" (2, 2) (2, 6) [ "import B" diff --git a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs index 8a22172a67..a752433e4a 100644 --- a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs +++ b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs @@ -176,7 +176,7 @@ replaceRefs newName refs = everywhere $ mkT replaceLoc --------------------------------------------------------------------------------------------------- -- Reference finding --- | Note: We only find exact name occurences (i.e. type reference "depth" is 0). +-- | Note: We only find exact name occurrences (i.e. type reference "depth" is 0). refsAtName :: MonadIO m => IdeState -> diff --git a/plugins/hls-retrie-plugin/changelog.md b/plugins/hls-retrie-plugin/changelog.md index 9355f52f3c..6aa75fc28b 100644 --- a/plugins/hls-retrie-plugin/changelog.md +++ b/plugins/hls-retrie-plugin/changelog.md @@ -1,2 +1,2 @@ ### 0.1.1.0 (2021-02-..) -* Fix bug in Retrive "fold/unfold in local file" commands (#1202) +* Fix bug in Retrieve "fold/unfold in local file" commands (#1202) diff --git a/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs b/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs index 9b817ec898..885151565e 100644 --- a/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs +++ b/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs @@ -99,7 +99,7 @@ expandTHSplice _eStyle ideState params@ExpandSpliceParams {..} = do liftIO $ runAction "expandTHSplice.fallback.TypeCheck (stale)" ideState $ useWithStale TypeCheck fp (TcModuleResult {..}, _) <- maybe - (throwE "Splice expansion: Type-checking information not found in cache.\nYou can once delete or replace the macro with placeholder, convince the type checker and then revert to original (errornous) macro and expand splice again." + (throwE "Splice expansion: Type-checking information not found in cache.\nYou can once delete or replace the macro with placeholder, convince the type checker and then revert to original (erroneous) macro and expand splice again." ) pure mresl reportEditor @@ -166,7 +166,7 @@ expandTHSplice _eStyle ideState params@ExpandSpliceParams {..} = do (graftDecls (RealSrcSpan spliceSpan Nothing) expanded) ps <&> - -- FIXME: Why ghc-exactprint sweeps preceeding comments? + -- FIXME: Why ghc-exactprint sweeps preceding comments? adjustToRange uri range res <- liftIO $ runMaybeT $ do @@ -483,7 +483,7 @@ codeAction state plId (CodeActionParams _ _ docId ran _) = liftIO $ _ -> Stop -- | Like 'something', but performs top-down searching, cutoffs when 'Stop' received, --- and picks inenrmost result. +-- and picks innermost result. something' :: forall a. GenericQ (SearchResult a) -> GenericQ (Maybe a) something' f = go where diff --git a/plugins/hls-splice-plugin/src/Ide/Plugin/Splice/Types.hs b/plugins/hls-splice-plugin/src/Ide/Plugin/Splice/Types.hs index 728b653308..b9e2124196 100644 --- a/plugins/hls-splice-plugin/src/Ide/Plugin/Splice/Types.hs +++ b/plugins/hls-splice-plugin/src/Ide/Plugin/Splice/Types.hs @@ -51,4 +51,4 @@ inplaceCmdName :: T.Text inplaceCmdName = "expand TemplateHaskell Splice (in-place)" commentedCmdName :: T.Text -commentedCmdName = "expand TemplateHaskell Splice (comented-out)" +commentedCmdName = "expand TemplateHaskell Splice (commented-out)" diff --git a/plugins/hls-tactics-plugin/COMMANDS.md b/plugins/hls-tactics-plugin/COMMANDS.md index d2c158925f..7bdda86cef 100644 --- a/plugins/hls-tactics-plugin/COMMANDS.md +++ b/plugins/hls-tactics-plugin/COMMANDS.md @@ -206,7 +206,7 @@ running `collapse` will produce: arguments: single reference. deterministic. -> Use the given data cosntructor. +> Use the given data constructor. ### Example @@ -254,7 +254,7 @@ case a of arguments: none. deterministic. -> Pattern match on every function paramater, in original binding order. +> Pattern match on every function parameter, in original binding order. ### Example @@ -358,7 +358,7 @@ running `intro aye` will produce: ## intros -arguments: varadic binding. +arguments: variadic binding. deterministic. > Construct a lambda expression, using the specific names if given, generating unique names otherwise. When no arguments are given, all of the function arguments will be bound; otherwise, this tactic will bind only enough to saturate the given names. Extra names are ignored. @@ -408,7 +408,7 @@ running `intros x y z w` will produce: ## let -arguments: varadic binding. +arguments: variadic binding. deterministic. > Create let-bindings for each binder given to this tactic. diff --git a/plugins/hls-tactics-plugin/src/Wingman/AbstractLSP.hs b/plugins/hls-tactics-plugin/src/Wingman/AbstractLSP.hs index dff5363719..da1e068ba6 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/AbstractLSP.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/AbstractLSP.hs @@ -34,7 +34,7 @@ import Wingman.Types ------------------------------------------------------------------------------ --- | Attact the 'Interaction's to a 'PluginDescriptor'. Interactions are +-- | Attach the 'Interaction's to a 'PluginDescriptor'. Interactions are -- self-contained request/response pairs that abstract over the LSP, and -- provide a unified interface for doing interesting things, without needing to -- dive into the underlying API too directly. diff --git a/plugins/hls-tactics-plugin/src/Wingman/AbstractLSP/TacticActions.hs b/plugins/hls-tactics-plugin/src/Wingman/AbstractLSP/TacticActions.hs index abea111b07..bb30f27b02 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/AbstractLSP/TacticActions.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/AbstractLSP/TacticActions.hs @@ -167,7 +167,7 @@ graftDecl dflags dst ix make_decl (L src (AMatch (FunRhs (L _ name) _ _) pats _) -- For whatever reason, ExactPrint annotates newlines to the ends of -- case matches and type signatures, but only allows us to insert -- them at the beginning of those things. Thus, we need want to - -- insert a preceeding newline (done in 'annotateDecl') on all + -- insert a preceding newline (done in 'annotateDecl') on all -- matches, except for the first one --- since it gets its newline -- from the line above. when (ix == 0) $ diff --git a/plugins/hls-tactics-plugin/src/Wingman/CodeGen.hs b/plugins/hls-tactics-plugin/src/Wingman/CodeGen.hs index bda08c539b..322a6f5b8c 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/CodeGen.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/CodeGen.hs @@ -222,7 +222,7 @@ destruct' use_field_puns f hi jdg = do ------------------------------------------------------------------------------ --- | Combinator for performign case splitting, and running sub-rules on the +-- | Combinator for performing case splitting, and running sub-rules on the -- resulting matches. destructLambdaCase' :: Bool -> (ConLike -> Judgement -> Rule) -> Judgement -> Rule destructLambdaCase' use_field_puns f jdg = do diff --git a/plugins/hls-tactics-plugin/src/Wingman/GHC.hs b/plugins/hls-tactics-plugin/src/Wingman/GHC.hs index 4c548bd72e..13562a6ef8 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/GHC.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/GHC.hs @@ -218,7 +218,7 @@ pattern Lambda pats body <- ------------------------------------------------------------------------------ --- | A GRHS that caontains no guards. +-- | A GRHS that contains no guards. pattern UnguardedRHSs :: LHsExpr p -> GRHSs p (LHsExpr p) pattern UnguardedRHSs body <- GRHSs {grhssGRHSs = [L _ (GRHS _ [] body)]} diff --git a/plugins/hls-tactics-plugin/src/Wingman/Judgements.hs b/plugins/hls-tactics-plugin/src/Wingman/Judgements.hs index 2a8dade9dc..0ff03e60ee 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/Judgements.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/Judgements.hs @@ -112,7 +112,7 @@ introduceHypothesis f ns = ------------------------------------------------------------------------------ --- | Introduce bindings in the context of a lamba. +-- | Introduce bindings in the context of a lambda. lambdaHypothesis :: Maybe OccName -- ^ The name of the top level function. For any other -- function, this should be 'Nothing'. @@ -187,7 +187,7 @@ filterPosition defn pos jdg = findPositionVal :: Judgement' a -> OccName -> Int -> Maybe OccName findPositionVal jdg defn pos = listToMaybe $ do -- It's important to inspect the entire hypothesis here, as we need to trace - -- ancstry through potentially disallowed terms in the hypothesis. + -- ancestry through potentially disallowed terms in the hypothesis. (name, hi) <- M.toList $ M.map (overProvenance expandDisallowed) $ hyByName @@ -261,7 +261,7 @@ provAncestryOf (DisallowedPrv _ p2) = provAncestryOf p2 ------------------------------------------------------------------------------ -- TODO(sandy): THIS THING IS A BIG BIG HACK -- --- Why? 'ctxDefiningFuncs' is _all_ of the functions currently beind defined +-- Why? 'ctxDefiningFuncs' is _all_ of the functions currently being defined -- (eg, we might be in a where block). The head of this list is not guaranteed -- to be the one we're interested in. extremelyStupid__definingFunction :: Context -> OccName @@ -302,7 +302,7 @@ disallowing reason ns = ------------------------------------------------------------------------------ -- | The hypothesis, consisting of local terms and the ambient environment --- (impors and class methods.) Hides disallowed values. +-- (imports and class methods.) Hides disallowed values. jHypothesis :: Judgement' a -> Hypothesis a jHypothesis = Hypothesis diff --git a/plugins/hls-tactics-plugin/src/Wingman/Judgements/SYB.hs b/plugins/hls-tactics-plugin/src/Wingman/Judgements/SYB.hs index 909c9f4dc3..8cd6130eb3 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/Judgements/SYB.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/Judgements/SYB.hs @@ -36,7 +36,7 @@ everythingContaining dst f = go ------------------------------------------------------------------------------ -- | Helper function for implementing 'everythingWithin' -- --- NOTE(sandy): Subtly broken. In an ideal world, this function shuld return +-- NOTE(sandy): Subtly broken. In an ideal world, this function should return -- @Just False@ for nodes of /any type/ which do not contain the span. But if -- this functionality exists anywhere within the SYB machinery, I have yet to -- find it. @@ -49,7 +49,7 @@ genericIsSubspan dst = mkQ1 (L noSrcSpan ()) Nothing $ \case ------------------------------------------------------------------------------ -- | Like 'mkQ', but allows for polymorphic instantiation of its specific case. --- This instantation matches whenever the dynamic value has the same +-- This instantiation matches whenever the dynamic value has the same -- constructor as the proxy @f ()@ value. mkQ1 :: forall a r f . (Data a, Data (f ())) diff --git a/plugins/hls-tactics-plugin/src/Wingman/Judgements/Theta.hs b/plugins/hls-tactics-plugin/src/Wingman/Judgements/Theta.hs index f1731a8a33..25bf5a3a21 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/Judgements/Theta.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/Judgements/Theta.hs @@ -77,7 +77,7 @@ mkSubst :: Set TyVar -> Type -> Type -> TCvSubst mkSubst skolems a b = let tyvars = S.fromList $ mapMaybe getTyVar_maybe [a, b] -- If we can unify our skolems, at least one is no longer a skolem. - -- Removing them from this set ensures we can get a subtitution between + -- Removing them from this set ensures we can get a substitution between -- the two. But it's okay to leave them in 'ts_skolems' in general, since -- they won't exist after running this substitution. skolems' = skolems S.\\ tyvars diff --git a/plugins/hls-tactics-plugin/src/Wingman/KnownStrategies.hs b/plugins/hls-tactics-plugin/src/Wingman/KnownStrategies.hs index c5df1c80c1..e898358c49 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/KnownStrategies.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/KnownStrategies.hs @@ -44,8 +44,8 @@ deriveFmap = do -- and then calling mappend recursively. At each recursive call, we filter away -- any binding that isn't in an analogous position. -- --- The recursive call first attempts to use an instace in scope. If that fails, --- it fals back to trying a theta method from the hypothesis with the correct +-- The recursive call first attempts to use an instance in scope. If that fails, +-- it falls back to trying a theta method from the hypothesis with the correct -- name. deriveMappend :: TacticsM () deriveMappend = do diff --git a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs b/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs index 829e1dda90..c0bba854ff 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs @@ -461,7 +461,7 @@ mkFakeVar = do ------------------------------------------------------------------------------ --- | Construct a fake varible to attach the current 'Provenance' to, and then +-- | Construct a fake variable to attach the current 'Provenance' to, and then -- build a sub-hypothesis for the pattern match. mkDerivedConHypothesis :: Provenance diff --git a/plugins/hls-tactics-plugin/src/Wingman/Machinery.hs b/plugins/hls-tactics-plugin/src/Wingman/Machinery.hs index 5da56959a7..ca082ec65e 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/Machinery.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/Machinery.hs @@ -255,7 +255,7 @@ unify goal inst = do Nothing -> cut ------------------------------------------------------------------------------ --- | Get a substition out of a theta's fundeps +-- | Get a substitution out of a theta's fundeps learnFromFundeps :: ThetaType -> RuleM () diff --git a/plugins/hls-tactics-plugin/src/Wingman/Metaprogramming/Parser/Documentation.hs b/plugins/hls-tactics-plugin/src/Wingman/Metaprogramming/Parser/Documentation.hs index 9c2ff0f632..44071a5ae7 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/Metaprogramming/Parser/Documentation.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/Metaprogramming/Parser/Documentation.hs @@ -36,12 +36,12 @@ data Count a where prettyCount :: Count a -> Doc b prettyCount One = "single" -prettyCount Many = "varadic" +prettyCount Many = "variadic" ------------------------------------------------------------------------------ -- | What sorts of arguments does the tactic take? Currently there is no --- distincion between 'Ref' and 'Bind', other than documentation. +-- distinction between 'Ref' and 'Bind', other than documentation. -- -- The type index here is used for the shape of the function the parser should -- take. diff --git a/plugins/hls-tactics-plugin/src/Wingman/Simplify.hs b/plugins/hls-tactics-plugin/src/Wingman/Simplify.hs index 3f170401ee..10eaae97c7 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/Simplify.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/Simplify.hs @@ -33,7 +33,7 @@ pattern Lambda pats body <- ------------------------------------------------------------------------------ --- | Simlify an expression. +-- | Simplify an expression. simplify :: LHsExpr GhcPs -> LHsExpr GhcPs simplify = (!!3) -- Do three passes; this should be good enough for the limited @@ -65,7 +65,7 @@ simplifyEtaReduce = mkT $ \case (unsnoc -> Just (pats, VarPat _ (L _ pat))) (HsApp _ (L _ f) (L _ (HsVar _ (L _ a)))) | pat == a - -- We can only perform this simplifiation if @pat@ is otherwise unused. + -- We can only perform this simplification if @pat@ is otherwise unused. , not (containsHsVar pat f) -> Lambda pats f x -> x @@ -87,7 +87,7 @@ simplifyCompose = mkT $ \case (unsnoc -> Just (pats, VarPat _ (L _ pat))) (unroll -> (fs@(_:_), HsVar _ (L _ a))) | pat == a - -- We can only perform this simplifiation if @pat@ is otherwise unused. + -- We can only perform this simplification if @pat@ is otherwise unused. , not (containsHsVar pat fs) -> Lambda pats (foldr1 (infixCall ".") fs) x -> x diff --git a/plugins/hls-tactics-plugin/test/Utils.hs b/plugins/hls-tactics-plugin/test/Utils.hs index 9f124efdb6..15c0386bb8 100644 --- a/plugins/hls-tactics-plugin/test/Utils.hs +++ b/plugins/hls-tactics-plugin/test/Utils.hs @@ -76,8 +76,8 @@ mkTest => String -- ^ The test name -> FilePath -- ^ The file name stem (without extension) to load -> Int -- ^ Cursor line - -> Int -- ^ Cursor columnn - -> t ( Bool -> Bool -- Use 'not' for actions that shouldnt be present + -> Int -- ^ Cursor column + -> t ( Bool -> Bool -- Use 'not' for actions that shouldn't be present , TacticCommand -- An expected command ... , Text -- ... for this variable ) -- ^ A collection of (un)expected code actions. diff --git a/shake-bench/src/Development/Benchmark/Rules.hs b/shake-bench/src/Development/Benchmark/Rules.hs index 4ffa6d22e6..4aeccc1549 100644 --- a/shake-bench/src/Development/Benchmark/Rules.hs +++ b/shake-bench/src/Development/Benchmark/Rules.hs @@ -41,7 +41,7 @@ │   ├── .log - bench stdout │   └── results.csv - results of all the experiments for the example ├── results.csv - aggregated results of all the examples, experiments, versions and configurations - └── .svg - graph of bytes over elapsed time, for all the examples, experiments, versions and configuratiof + └── .svg - graph of bytes over elapsed time, for all the examples, experiments, versions and configurations For diff graphs, the "previous version" is the preceding entry in the list of versions in the config file. A possible improvement is to obtain this info via `git rev-list`. From 85f788135175a007d3db4ac911c4115b30ab9d87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Berk=20=C3=96zk=C3=BCt=C3=BCk?= Date: Thu, 10 Nov 2022 02:17:50 +0100 Subject: [PATCH 186/213] New plugin: Explicit record fields (#3304) * Initial working version * Auto-add puns pragma, fix behavior with Hs98 fields * Patch pragma so it adds NamedFieldPuns instead of RecordPuns * Refactor big do block * Make it work with record construction * Convert to a rule based approach * Cleanup, remove dead code * Make it compile with all supported GHC versions * Add tests * Minor code reorganization * Move common pragma logic to same file * Remove strictness annotations * Improve documentation * Use interval map for efficient range filtering * Add external documentation * Add tests to GitHub CI * Add debug log for collected records * Add `getExtensions` to ghcide * Indicate that it doesn't work with GHC 9.4 https://github.com/haskell/haskell-language-server/pull/3304#discussion_r1008034647 * Relax version bounds on base * Add plugin to stack packages * Add GHC 8.10 support * Fix GHC 9.4 build failure * Make `conPatDetails` total * Revert "Indicate that it doesn't work with GHC 9.4" This reverts commit 57646d35ad45bf7c0a6af129b276517e3e67ccb0. * Fix unused import caused by new compat exports * Fix ConPat construction in GHC 8.10 * Rename test-suite to make it shorter * Fix nix build by collecting latest hw-prim from Hackage Co-authored-by: Pepe Iborra Co-authored-by: Michael Peyton Jones --- .github/workflows/test.yml | 4 + cabal.project | 3 + docs/features.md | 10 + docs/support/plugin-support.md | 1 + flake.lock | 13 + flake.nix | 5 + ghcide/src/Development/IDE/GHC/Compat/Core.hs | 61 +++- ghcide/src/Development/IDE/GHC/Util.hs | 9 +- ghcide/src/Development/IDE/Spans/Pragmas.hs | 31 +- haskell-language-server.cabal | 11 + .../src/Ide/Plugin/AlternateNumberFormat.hs | 63 ++-- .../src/Ide/Plugin/Brittany.hs | 3 +- .../src/Ide/Plugin/Class/Utils.hs | 3 +- .../CHANGELOG.md | 5 + .../hls-explicit-record-fields-plugin/LICENSE | 30 ++ .../README.md | 18 ++ .../hls-explicit-record-fields-plugin.cabal | 59 ++++ .../src/Ide/Plugin/ExplicitFields.hs | 289 ++++++++++++++++++ .../test/Main.hs | 65 ++++ .../test/testdata/Construction.expected.hs | 18 ++ .../test/testdata/Construction.hs | 17 ++ .../test/testdata/Mixed.expected.hs | 14 + .../test/testdata/Mixed.hs | 14 + .../test/testdata/WildcardOnly.expected.hs | 14 + .../test/testdata/WildcardOnly.hs | 13 + .../testdata/WithExplicitBind.expected.hs | 14 + .../test/testdata/WithExplicitBind.hs | 13 + .../test/testdata/WithPun.expected.hs | 14 + .../test/testdata/WithPun.hs | 14 + .../test/testdata/noop/ExplicitBinds.hs | 12 + .../test/testdata/noop/Infix.hs | 11 + .../test/testdata/noop/Prefix.hs | 11 + .../test/testdata/noop/Puns.hs | 13 + .../wildcard.gif | Bin 0 -> 551286 bytes .../hls-gadt-plugin/src/Ide/Plugin/GADT.hs | 45 +-- .../hls-hlint-plugin/src/Ide/Plugin/Hlint.hs | 3 +- .../src/Development/IDE/Plugin/CodeAction.hs | 3 +- .../src/Wingman/EmptyCase.hs | 2 +- .../Wingman/LanguageServer/TacticProviders.hs | 1 - .../src/Wingman/StaticPlugin.hs | 1 - src/HlsPlugins.hs | 7 + stack-lts19.yaml | 1 + stack.yaml | 1 + 43 files changed, 854 insertions(+), 85 deletions(-) create mode 100644 plugins/hls-explicit-record-fields-plugin/CHANGELOG.md create mode 100644 plugins/hls-explicit-record-fields-plugin/LICENSE create mode 100644 plugins/hls-explicit-record-fields-plugin/README.md create mode 100644 plugins/hls-explicit-record-fields-plugin/hls-explicit-record-fields-plugin.cabal create mode 100644 plugins/hls-explicit-record-fields-plugin/src/Ide/Plugin/ExplicitFields.hs create mode 100644 plugins/hls-explicit-record-fields-plugin/test/Main.hs create mode 100644 plugins/hls-explicit-record-fields-plugin/test/testdata/Construction.expected.hs create mode 100644 plugins/hls-explicit-record-fields-plugin/test/testdata/Construction.hs create mode 100644 plugins/hls-explicit-record-fields-plugin/test/testdata/Mixed.expected.hs create mode 100644 plugins/hls-explicit-record-fields-plugin/test/testdata/Mixed.hs create mode 100644 plugins/hls-explicit-record-fields-plugin/test/testdata/WildcardOnly.expected.hs create mode 100644 plugins/hls-explicit-record-fields-plugin/test/testdata/WildcardOnly.hs create mode 100644 plugins/hls-explicit-record-fields-plugin/test/testdata/WithExplicitBind.expected.hs create mode 100644 plugins/hls-explicit-record-fields-plugin/test/testdata/WithExplicitBind.hs create mode 100644 plugins/hls-explicit-record-fields-plugin/test/testdata/WithPun.expected.hs create mode 100644 plugins/hls-explicit-record-fields-plugin/test/testdata/WithPun.hs create mode 100644 plugins/hls-explicit-record-fields-plugin/test/testdata/noop/ExplicitBinds.hs create mode 100644 plugins/hls-explicit-record-fields-plugin/test/testdata/noop/Infix.hs create mode 100644 plugins/hls-explicit-record-fields-plugin/test/testdata/noop/Prefix.hs create mode 100644 plugins/hls-explicit-record-fields-plugin/test/testdata/noop/Puns.hs create mode 100644 plugins/hls-explicit-record-fields-plugin/wildcard.gif diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6a11cb2edc..6993c86ae6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -250,6 +250,10 @@ jobs: name: Test hls-explicit-fixity-plugin test suite run: cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-explicit-fixity-plugin --test-options="$TEST_OPTS" + - if: matrix.test + name: Test hls-explicit-record-fields-plugin test suite + run: cabal test hls-explicit-record-fields-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-explicit-record-fields-plugin --test-options="$TEST_OPTS" + test_post_job: if: always() runs-on: ubuntu-latest diff --git a/cabal.project b/cabal.project index 4950a95f3a..6220f564a8 100644 --- a/cabal.project +++ b/cabal.project @@ -33,6 +33,7 @@ packages: ./plugins/hls-stan-plugin ./plugins/hls-gadt-plugin ./plugins/hls-explicit-fixity-plugin + ./plugins/hls-explicit-record-fields-plugin ./plugins/hls-refactor-plugin -- Standard location for temporary packages needed for particular environments @@ -55,6 +56,8 @@ constraints: entropy >= 0.4.1.10, -- For GHC 9.4 basement >= 0.0.15, + -- For GHC 9.4 + hw-prim >= 0.6.3.2, hyphenation +embed, -- remove this when hlint sets ghc-lib to true by default -- https://github.com/ndmitchell/hlint/issues/1376 diff --git a/docs/features.md b/docs/features.md index ef4ae8a88e..efb892b1c9 100644 --- a/docs/features.md +++ b/docs/features.md @@ -291,6 +291,16 @@ Convert a datatype to GADT syntax. ![Link to Docs](../plugins/hls-gadt-plugin/README.md) +### Expand record wildcard + +Provided by: `hls-explicit-record-fields-plugin` + +Code action kind: `refactor.rewrite` + +Expand record wildcards, explicitly listing all record fields as field puns. + +![Explicit Wildcard Demo](../plugins/hls-explicit-record-fields-plugin/wildcard.gif) + ## Code lenses ### Add type signature diff --git a/docs/support/plugin-support.md b/docs/support/plugin-support.md index 4aa8530cf2..5dd9f97aaf 100644 --- a/docs/support/plugin-support.md +++ b/docs/support/plugin-support.md @@ -50,6 +50,7 @@ For example, a plugin to provide a formatter which has itself been abandoned has | `hls-change-type-signature-plugin` | 2 | | | `hls-eval-plugin` | 2 | 9.4 | | `hls-explicit-fixity-plugin` | 2 | | +| `hls-explicit-record-fields-plugin` | 2 | | | `hls-floskell-plugin` | 2 | 9.4 | | `hls-fourmolu-plugin` | 2 | 9.4 | | `hls-gadt-plugin` | 2 | 9.4 | diff --git a/flake.lock b/flake.lock index ec20499ad0..0f74f76d26 100644 --- a/flake.lock +++ b/flake.lock @@ -232,6 +232,18 @@ "url": "https://hackage.haskell.org/package/hlint-3.4/hlint-3.4.tar.gz" } }, + "hw-prim": { + "flake": false, + "locked": { + "narHash": "sha256-++rg/bx4TjWUDyHSWKm/8ITwQLonPRLXHPLlnhJy8ik=", + "type": "tarball", + "url": "https://hackage.haskell.org/package/hw-prim-0.6.3.2/hw-prim-0.6.3.2.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://hackage.haskell.org/package/hw-prim-0.6.3.2/hw-prim-0.6.3.2.tar.gz" + } + }, "implicit-hie-cradle": { "flake": false, "locked": { @@ -340,6 +352,7 @@ "hiedb": "hiedb", "hlint": "hlint", "hlint-34": "hlint-34", + "hw-prim": "hw-prim", "implicit-hie-cradle": "implicit-hie-cradle", "lsp": "lsp", "lsp-test": "lsp-test", diff --git a/flake.nix b/flake.nix index c4873ed9d7..b7f1cb6b06 100644 --- a/flake.nix +++ b/flake.nix @@ -111,6 +111,10 @@ url = "https://hackage.haskell.org/package/hiedb-0.4.2.0/hiedb-0.4.2.0.tar.gz"; flake = false; }; + hw-prim = { + url = "https://hackage.haskell.org/package/hw-prim-0.6.3.2/hw-prim-0.6.3.2.tar.gz"; + flake = false; + }; }; outputs = inputs@{ self, nixpkgs, flake-compat, flake-utils, gitignore, all-cabal-hashes-unpacked, ... }: @@ -182,6 +186,7 @@ entropy = hsuper.callCabal2nix "entropy" inputs.entropy {}; hiedb = hsuper.callCabal2nix "hiedb" inputs.hiedb {}; + hw-prim = hsuper.callCabal2nix "hw-prim" inputs.hw-prim {}; implicit-hie-cradle = hself.callCabal2nix "implicit-hie-cradle" inputs.implicit-hie-cradle {}; ghc-check = hself.callCabal2nix "ghc-check" inputs.ghc-check {}; diff --git a/ghcide/src/Development/IDE/GHC/Compat/Core.hs b/ghcide/src/Development/IDE/GHC/Compat/Core.hs index af5c8c1ace..56579f6130 100644 --- a/ghcide/src/Development/IDE/GHC/Compat/Core.hs +++ b/ghcide/src/Development/IDE/GHC/Compat/Core.hs @@ -130,6 +130,8 @@ module Development.IDE.GHC.Compat.Core ( ), pattern FunTy, pattern ConPatIn, + conPatDetails, + mapConPatDetail, #if !MIN_VERSION_ghc(9,2,0) Development.IDE.GHC.Compat.Core.splitForAllTyCoVars, #endif @@ -253,6 +255,7 @@ module Development.IDE.GHC.Compat.Core ( SrcLoc.noSrcSpan, SrcLoc.noSrcLoc, SrcLoc.noLoc, + SrcLoc.mapLoc, -- * Finder FindResult(..), mkHomeModLocation, @@ -461,6 +464,18 @@ module Development.IDE.GHC.Compat.Core ( module GHC.Unit.Finder.Types, module GHC.Unit.Env, module GHC.Driver.Phases, +#endif +# if !MIN_VERSION_ghc(9,4,0) + pattern HsFieldBind, + hfbAnn, + hfbLHS, + hfbRHS, + hfbPun, +#endif +#if !MIN_VERSION_ghc_boot_th(9,4,1) + Extension(.., NamedFieldPuns), +#else + Extension(..) #endif ) where @@ -710,12 +725,12 @@ import TcRnMonad hiding (Applicative (..), IORef, allM, anyM, concatMapM, foldrM, mapMaybeM, (<$>)) import TcRnTypes -import TcType +import TcType import qualified TcType import TidyPgm as GHC import qualified TyCoRep import TyCon -import Type +import Type import TysPrim import TysWiredIn import Unify @@ -755,6 +770,11 @@ import qualified GHC.Driver.Finder as GHC import qualified Finder as GHC #endif +-- NOTE(ozkutuk): Cpp clashes Phase.Cpp, so we hide it. +-- Not the greatest solution, but gets the job done +-- (until the CPP extension is actually needed). +import GHC.LanguageExtensions.Type hiding (Cpp) + mkHomeModLocation :: DynFlags -> ModuleName -> FilePath -> IO Module.ModLocation #if MIN_VERSION_ghc(9,3,0) @@ -936,6 +956,25 @@ pattern ConPatIn con args = ConPat NoExtField con args #endif #endif +conPatDetails :: Pat p -> Maybe (HsConPatDetails p) +#if MIN_VERSION_ghc(9,0,0) +conPatDetails (ConPat _ _ args) = Just args +conPatDetails _ = Nothing +#else +conPatDetails (ConPatIn _ args) = Just args +conPatDetails _ = Nothing +#endif + +mapConPatDetail :: (HsConPatDetails p -> Maybe (HsConPatDetails p)) -> Pat p -> Maybe (Pat p) +#if MIN_VERSION_ghc(9,0,0) +mapConPatDetail f pat@(ConPat _ _ args) = (\args' -> pat { pat_args = args'}) <$> f args +mapConPatDetail _ _ = Nothing +#else +mapConPatDetail f (ConPatIn ss args) = ConPatIn ss <$> f args +mapConPatDetail _ _ = Nothing +#endif + + initDynLinker, initObjLinker :: HscEnv -> IO () initDynLinker = #if !MIN_VERSION_ghc(9,0,0) @@ -1101,3 +1140,21 @@ driverNoStop = hscUpdateHPT :: (HomePackageTable -> HomePackageTable) -> HscEnv -> HscEnv hscUpdateHPT k session = session { hsc_HPT = k (hsc_HPT session) } #endif + +#if !MIN_VERSION_ghc(9,2,0) +match :: HsRecField' id arg -> ((), id, arg, Bool) +match (HsRecField lhs rhs pun) = ((), SrcLoc.unLoc lhs, rhs, pun) + +pattern HsFieldBind :: () -> id -> arg -> Bool -> HsRecField' id arg +pattern HsFieldBind {hfbAnn, hfbLHS, hfbRHS, hfbPun} <- (match -> (hfbAnn, hfbLHS, hfbRHS, hfbPun)) where + HsFieldBind _ lhs rhs pun = HsRecField (SrcLoc.noLoc lhs) rhs pun +#elif !MIN_VERSION_ghc(9,4,0) +pattern HsFieldBind :: XHsRecField id -> id -> arg -> Bool -> HsRecField' id arg +pattern HsFieldBind {hfbAnn, hfbLHS, hfbRHS, hfbPun} <- HsRecField hfbAnn (SrcLoc.unLoc -> hfbLHS) hfbRHS hfbPun where + HsFieldBind ann lhs rhs pun = HsRecField ann (SrcLoc.noLoc lhs) rhs pun +#endif + +#if !MIN_VERSION_ghc_boot_th(9,4,1) +pattern NamedFieldPuns :: Extension +pattern NamedFieldPuns = RecordPuns +#endif diff --git a/ghcide/src/Development/IDE/GHC/Util.hs b/ghcide/src/Development/IDE/GHC/Util.hs index 70486f4d74..ca108ebc4d 100644 --- a/ghcide/src/Development/IDE/GHC/Util.hs +++ b/ghcide/src/Development/IDE/GHC/Util.hs @@ -26,10 +26,12 @@ module Development.IDE.GHC.Util( setHieDir, dontWriteHieFiles, disableWarningsAsErrors, - printOutputable + printOutputable, + getExtensions ) where #if MIN_VERSION_ghc(9,2,0) +import GHC.Data.EnumSet import GHC.Data.FastString import GHC.Data.StringBuffer import GHC.Driver.Env hiding (hscSetFlags) @@ -73,7 +75,7 @@ import Development.IDE.Types.Location import Foreign.ForeignPtr import Foreign.Ptr import Foreign.Storable -import GHC +import GHC hiding (ParsedModule (..)) import GHC.IO.BufferedIO (BufferedIO) import GHC.IO.Device as IODevice import GHC.IO.Encoding @@ -295,3 +297,6 @@ printOutputable = -- More discussion at https://github.com/haskell/haskell-language-server/issues/3115. unescape . T.pack . printWithoutUniques {-# INLINE printOutputable #-} + +getExtensions :: ParsedModule -> [Extension] +getExtensions = toList . extensionFlags . ms_hspp_opts . pm_mod_summary diff --git a/ghcide/src/Development/IDE/Spans/Pragmas.hs b/ghcide/src/Development/IDE/Spans/Pragmas.hs index 02d3db2721..6e00769922 100644 --- a/ghcide/src/Development/IDE/Spans/Pragmas.hs +++ b/ghcide/src/Development/IDE/Spans/Pragmas.hs @@ -6,7 +6,8 @@ module Development.IDE.Spans.Pragmas ( NextPragmaInfo(..) , LineSplitTextEdits(..) , getNextPragmaInfo - , insertNewPragma ) where + , insertNewPragma + , getFirstPragma ) where import Data.Bits (Bits (setBit)) import Data.Function ((&)) @@ -14,11 +15,15 @@ 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) +import Development.IDE (srcSpanToRange, IdeState, NormalizedFilePath, runAction, useWithStale, GhcSession (..), getFileContents, hscEnv) import Development.IDE.GHC.Compat import Development.IDE.GHC.Compat.Util -import GHC.LanguageExtensions.Type (Extension) import qualified Language.LSP.Types as LSP +import Control.Monad.IO.Class (MonadIO (..)) +import Control.Monad.Trans.Except (ExceptT) +import Ide.Types (PluginId(..)) +import qualified Data.Text as T +import Ide.PluginUtils (handleMaybeM) getNextPragmaInfo :: DynFlags -> Maybe Text -> NextPragmaInfo getNextPragmaInfo dynFlags sourceText = @@ -31,13 +36,29 @@ getNextPragmaInfo dynFlags sourceText = | otherwise -> NextPragmaInfo 0 Nothing +-- NOTE(ozkutuk): `RecordPuns` extension is renamed to `NamedFieldPuns` +-- in GHC 9.4, but we still want to insert `NamedFieldPuns` in pre-9.4 +-- GHC as well, hence the replacement. +-- https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6156 +showExtension :: Extension -> Text +showExtension NamedFieldPuns = "NamedFieldPuns" +showExtension ext = pack (show ext) + insertNewPragma :: NextPragmaInfo -> Extension -> LSP.TextEdit -insertNewPragma (NextPragmaInfo _ (Just (LineSplitTextEdits ins _))) newPragma = ins { LSP._newText = "{-# LANGUAGE " <> pack (show newPragma) <> " #-}\n" } :: LSP.TextEdit -insertNewPragma (NextPragmaInfo nextPragmaLine _) newPragma = LSP.TextEdit pragmaInsertRange $ "{-# LANGUAGE " <> pack (show newPragma) <> " #-}\n" +insertNewPragma (NextPragmaInfo _ (Just (LineSplitTextEdits ins _))) newPragma = ins { LSP._newText = "{-# LANGUAGE " <> showExtension newPragma <> " #-}\n" } :: LSP.TextEdit +insertNewPragma (NextPragmaInfo nextPragmaLine _) newPragma = LSP.TextEdit pragmaInsertRange $ "{-# LANGUAGE " <> showExtension newPragma <> " #-}\n" where pragmaInsertPosition = LSP.Position (fromIntegral nextPragmaLine) 0 pragmaInsertRange = LSP.Range pragmaInsertPosition pragmaInsertPosition +getFirstPragma :: MonadIO m => PluginId -> IdeState -> NormalizedFilePath -> ExceptT String m NextPragmaInfo +getFirstPragma (PluginId pId) state nfp = handleMaybeM "Could not get NextPragmaInfo" $ do + ghcSession <- liftIO $ runAction (T.unpack pId <> ".GhcSession") state $ useWithStale GhcSession nfp + (_, fileContents) <- liftIO $ runAction (T.unpack pId <> ".GetFileContents") state $ getFileContents nfp + case ghcSession of + Just (hscEnv -> hsc_dflags -> sessionDynFlags, _) -> pure $ Just $ getNextPragmaInfo sessionDynFlags fileContents + Nothing -> pure Nothing + -- Pre-declaration comments parser ----------------------------------------------------- -- | Each mode represents the "strongest" thing we've seen so far. diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index ec62f7cd6d..5c857c2de6 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -163,6 +163,11 @@ flag explicitFixity default: True manual: True +flag explicitFields + description: Enable explicitFields plugin + default: True + manual: True + -- formatters flag floskell @@ -300,6 +305,11 @@ common explicitFixity build-depends: hls-explicit-fixity-plugin ^>= 1.0 cpp-options: -DexplicitFixity +common explicitFields + if flag(explicitFields) + build-depends: hls-explicit-record-fields-plugin ^>= 1.0 + cpp-options: -DexplicitFields + -- formatters common floskell @@ -358,6 +368,7 @@ library , codeRange , gadt , explicitFixity + , explicitFields , floskell , fourmolu , ormolu diff --git a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs index e73afdbd65..f2961d452a 100644 --- a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs +++ b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs @@ -2,39 +2,34 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} -{-# LANGUAGE ViewPatterns #-} module Ide.Plugin.AlternateNumberFormat (descriptor, Log(..)) where -import Control.Lens ((^.)) -import Control.Monad.Except (ExceptT, MonadIO, liftIO) -import qualified Data.HashMap.Strict as HashMap -import Data.Text (Text, unpack) -import qualified Data.Text as T -import Development.IDE (GetParsedModule (GetParsedModule), - GhcSession (GhcSession), - IdeState, RuleResult, Rules, - define, getFileContents, - hscEnv, realSrcSpanToRange, - runAction, use, useWithStale) -import qualified Development.IDE.Core.Shake as Shake -import Development.IDE.GHC.Compat hiding (getSrcSpan) -import Development.IDE.GHC.Compat.Util (toList) -import Development.IDE.Graph.Classes (Hashable, NFData, rnf) -import Development.IDE.Spans.Pragmas (NextPragmaInfo, - getNextPragmaInfo, - insertNewPragma) -import Development.IDE.Types.Logger as Logger -import GHC.Generics (Generic) -import GHC.LanguageExtensions.Type (Extension) -import Ide.Plugin.Conversion (AlternateFormat, - ExtensionNeeded (NeedsExtension, NoExtension), - alternateFormat) +import Control.Lens ((^.)) +import Control.Monad.Except (ExceptT, MonadIO, liftIO) +import qualified Data.HashMap.Strict as HashMap +import Data.Text (Text, unpack) +import qualified Data.Text as T +import Development.IDE (GetParsedModule (GetParsedModule), + IdeState, RuleResult, Rules, + define, realSrcSpanToRange, + runAction, use) +import qualified Development.IDE.Core.Shake as Shake +import Development.IDE.GHC.Compat hiding (getSrcSpan) +import Development.IDE.GHC.Util (getExtensions) +import Development.IDE.Graph.Classes (Hashable, NFData, rnf) +import Development.IDE.Spans.Pragmas (NextPragmaInfo, getFirstPragma, + insertNewPragma) +import Development.IDE.Types.Logger as Logger +import GHC.Generics (Generic) +import Ide.Plugin.Conversion (AlternateFormat, + ExtensionNeeded (NeedsExtension, NoExtension), + alternateFormat) import Ide.Plugin.Literals -import Ide.PluginUtils (getNormalizedFilePath, - handleMaybeM, pluginResponse) +import Ide.PluginUtils (getNormalizedFilePath, + handleMaybeM, pluginResponse) import Ide.Types import Language.LSP.Types -import qualified Language.LSP.Types.Lens as L +import qualified Language.LSP.Types.Lens as L newtype Log = LogShake Shake.Log deriving Show @@ -75,12 +70,10 @@ collectLiteralsRule :: Recorder (WithPriority Log) -> Rules () collectLiteralsRule recorder = define (cmapWithPrio LogShake recorder) $ \CollectLiterals nfp -> do pm <- use GetParsedModule nfp -- get the current extensions active and transform them into FormatTypes - let exts = getExtensions <$> pm + let exts = map GhcExtension . getExtensions <$> pm -- collect all the literals for a file lits = collectLiterals . pm_parsed_source <$> pm pure ([], CLR <$> lits <*> exts) - where - getExtensions = map GhcExtension . toList . extensionFlags . ms_hspp_opts . pm_mod_summary codeActionHandler :: PluginMethodHandler IdeState 'TextDocumentCodeAction codeActionHandler state pId (CodeActionParams _ _ docId currRange _) = pluginResponse $ do @@ -141,14 +134,6 @@ contains Range {_start, _end} x = isInsideRealSrcSpan _start x || isInsideRealSr isInsideRealSrcSpan :: Position -> RealSrcSpan -> Bool p `isInsideRealSrcSpan` r = let (Range sp ep) = realSrcSpanToRange r in sp <= p && p <= ep -getFirstPragma :: MonadIO m => PluginId -> IdeState -> NormalizedFilePath -> ExceptT String m NextPragmaInfo -getFirstPragma (PluginId pId) state nfp = handleMaybeM "Could not get NextPragmaInfo" $ do - ghcSession <- liftIO $ runAction (unpack pId <> ".GhcSession") state $ useWithStale GhcSession nfp - (_, fileContents) <- liftIO $ runAction (unpack pId <> ".GetFileContents") state $ getFileContents nfp - case ghcSession of - Just (hscEnv -> hsc_dflags -> sessionDynFlags, _) -> pure $ Just $ getNextPragmaInfo sessionDynFlags fileContents - Nothing -> pure Nothing - requestLiterals :: MonadIO m => PluginId -> IdeState -> NormalizedFilePath -> ExceptT String m CollectLiteralsResult requestLiterals (PluginId pId) state = handleMaybeM "Could not Collect Literals" . liftIO diff --git a/plugins/hls-brittany-plugin/src/Ide/Plugin/Brittany.hs b/plugins/hls-brittany-plugin/src/Ide/Plugin/Brittany.hs index 7a61bf9935..b0454e46fb 100644 --- a/plugins/hls-brittany-plugin/src/Ide/Plugin/Brittany.hs +++ b/plugins/hls-brittany-plugin/src/Ide/Plugin/Brittany.hs @@ -17,7 +17,8 @@ import Data.Semigroup import Data.Text (Text) import qualified Data.Text as T import Development.IDE hiding - (pluginHandlers) + (getExtensions, + pluginHandlers) import qualified Development.IDE.GHC.Compat as GHC hiding (Cpp) import qualified Development.IDE.GHC.Compat.Util as GHC diff --git a/plugins/hls-class-plugin/src/Ide/Plugin/Class/Utils.hs b/plugins/hls-class-plugin/src/Ide/Plugin/Class/Utils.hs index 920ed228da..1c5deb10e9 100644 --- a/plugins/hls-class-plugin/src/Ide/Plugin/Class/Utils.hs +++ b/plugins/hls-class-plugin/src/Ide/Plugin/Class/Utils.hs @@ -13,7 +13,6 @@ import Development.IDE.GHC.Compat import Development.IDE.GHC.Compat.Util import Development.IDE.Spans.Pragmas (getNextPragmaInfo, insertNewPragma) -import GHC.LanguageExtensions.Type (Extension) import Ide.PluginUtils import Language.LSP.Types @@ -64,6 +63,6 @@ insertPragmaIfNotPresent state nfp pragma = do $ liftIO $ runAction "classplugin.insertPragmaIfNotPresent.GetParsedModuleWithComments" state $ use GetParsedModuleWithComments nfp - let exts = (toList . extensionFlags . ms_hspp_opts . pm_mod_summary) pm + let exts = getExtensions pm info = getNextPragmaInfo sessionDynFlags fileContents pure [insertNewPragma info pragma | pragma `notElem` exts] diff --git a/plugins/hls-explicit-record-fields-plugin/CHANGELOG.md b/plugins/hls-explicit-record-fields-plugin/CHANGELOG.md new file mode 100644 index 0000000000..609eef3bed --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/CHANGELOG.md @@ -0,0 +1,5 @@ +# Revision history for hls-explicit-record-fields-plugin + +## 1.0.0.0 -- YYYY-mm-dd + +* First version. Released on an unsuspecting world. diff --git a/plugins/hls-explicit-record-fields-plugin/LICENSE b/plugins/hls-explicit-record-fields-plugin/LICENSE new file mode 100644 index 0000000000..00abc29fb4 --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) 2022, Berk Ozkutuk + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Berk Ozkutuk nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plugins/hls-explicit-record-fields-plugin/README.md b/plugins/hls-explicit-record-fields-plugin/README.md new file mode 100644 index 0000000000..d4330c3445 --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/README.md @@ -0,0 +1,18 @@ +# Explicit Record Fields Plugin + +`hls-explicit-record-fields-plugin` is a plugin to expand record wildcards, explicitly listing all record fields as field puns. It works in both record construction and pattern binding scenarios, and it works as you would expect regardless of whether there are explicitly provided fields or puns in addition to the wildcard. + + +## Demo + +![Expand Wildcard Demo](wildcard.gif) + + +## Known limitations + +One of the shortcomings of the current approach is that all fields of the record are expanded, whether they are actually used or not. This results in warnings of unused bindings, if the corresponding warning flag is enabled. + + +## Change log +### 1.0.0.0 +- Release diff --git a/plugins/hls-explicit-record-fields-plugin/hls-explicit-record-fields-plugin.cabal b/plugins/hls-explicit-record-fields-plugin/hls-explicit-record-fields-plugin.cabal new file mode 100644 index 0000000000..2af84b89fb --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/hls-explicit-record-fields-plugin.cabal @@ -0,0 +1,59 @@ +cabal-version: 3.0 +name: hls-explicit-record-fields-plugin +version: 1.0.0.0 +synopsis: Explicit record fields plugin for Haskell Language Server +description: + Please see the README on GitHub at +license: BSD-3-Clause +license-file: LICENSE +author: Berk Ozkutuk +maintainer: berk.ozkutuk@tweag.io +-- copyright: +category: Development +build-type: Simple +extra-doc-files: CHANGELOG.md +-- extra-source-files: + +source-repository head + type: git + location: https://github.com/haskell/haskell-language-server + +common warnings + ghc-options: -Wall + +library + import: warnings + exposed-modules: Ide.Plugin.ExplicitFields + -- other-modules: + -- other-extensions: + build-depends: + , base >=4.12 && <5 + , ghcide ^>=1.7 || ^>=1.8 + , hls-plugin-api ^>=1.4 || ^>=1.5 + , lsp + , lens + , hls-graph + , text + , syb + , transformers + , ghc-boot-th + , unordered-containers + , hw-fingertree + hs-source-dirs: src + default-language: Haskell2010 + +test-suite tests + import: warnings + default-language: Haskell2010 + -- other-modules: + -- other-extensions: + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Main.hs + build-depends: + , base + , filepath + , text + , hls-explicit-record-fields-plugin + , lsp-test + , hls-test-utils diff --git a/plugins/hls-explicit-record-fields-plugin/src/Ide/Plugin/ExplicitFields.hs b/plugins/hls-explicit-record-fields-plugin/src/Ide/Plugin/ExplicitFields.hs new file mode 100644 index 0000000000..1a32ae70bb --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/src/Ide/Plugin/ExplicitFields.hs @@ -0,0 +1,289 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE ViewPatterns #-} + +module Ide.Plugin.ExplicitFields + ( descriptor + ) where + +import Control.Lens ((^.)) +import Control.Monad.IO.Class (MonadIO, liftIO) +import Control.Monad.Trans.Except (ExceptT) +import Data.Foldable (foldl') +import Data.Generics (GenericQ, everything, + extQ, mkQ) +import qualified Data.HashMap.Strict as HashMap +import Data.Maybe (catMaybes, isJust, + mapMaybe, + maybeToList) +import Data.Text (Text) +import Development.IDE (IdeState, + NormalizedFilePath, + Pretty (..), + Range (..), + Recorder (..), Rules, + WithPriority (..), + srcSpanToRange) +import Development.IDE.Core.Rules (runAction) +import Development.IDE.Core.RuleTypes (TcModuleResult (..), + TypeCheck (..)) +import Development.IDE.Core.Shake (define, use) +import qualified Development.IDE.Core.Shake as Shake +import Development.IDE.GHC.Compat (HsConDetails (RecCon), + HsRecFields (..), + LPat, Outputable, + SrcSpan, getLoc, + unLoc) +import Development.IDE.GHC.Compat.Core (Extension (NamedFieldPuns), + GhcPass, + HsExpr (RecordCon, rcon_flds), + LHsExpr, Pass (..), + Pat (..), + conPatDetails, + hfbPun, hs_valds, + mapConPatDetail, + mapLoc) +import Development.IDE.GHC.Util (getExtensions, + printOutputable) +import Development.IDE.Graph (RuleResult) +import Development.IDE.Graph.Classes (Hashable, + NFData (rnf)) +import Development.IDE.Spans.Pragmas (NextPragmaInfo (..), + getFirstPragma, + insertNewPragma) +import Development.IDE.Types.Logger (Priority (..), + cmapWithPrio, + logWith, (<+>)) +import GHC.Generics (Generic) +import qualified HaskellWorks.Data.IntervalMap.FingerTree as IM +import Ide.PluginUtils (getNormalizedFilePath, + handleMaybeM, + pluginResponse) +import Ide.Types (PluginDescriptor (..), + PluginId (..), + PluginMethodHandler, + defaultPluginDescriptor, + mkPluginHandler) +import Language.LSP.Types (CodeAction (..), + CodeActionKind (CodeActionRefactorRewrite), + CodeActionParams (..), + Command, List (..), + Method (..), + Position, + SMethod (..), + TextEdit (..), + WorkspaceEdit (WorkspaceEdit), + fromNormalizedUri, + normalizedFilePathToUri, + type (|?) (InR)) +import qualified Language.LSP.Types.Lens as L + + +data Log + = LogShake Shake.Log + | LogCollectedRecords [RecordInfo] + | LogRenderedRecords [RenderedRecordInfo] + +instance Pretty Log where + pretty = \case + LogShake shakeLog -> pretty shakeLog + LogCollectedRecords recs -> "Collected records with wildcards:" <+> pretty recs + LogRenderedRecords recs -> "Rendered records:" <+> pretty recs + +descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState +descriptor recorder plId = (defaultPluginDescriptor plId) + { pluginHandlers = mkPluginHandler STextDocumentCodeAction codeActionProvider + , pluginRules = collectRecordsRule recorder + } + +codeActionProvider :: PluginMethodHandler IdeState 'TextDocumentCodeAction +codeActionProvider ideState pId (CodeActionParams _ _ docId range _) = pluginResponse $ do + nfp <- getNormalizedFilePath (docId ^. L.uri) + pragma <- getFirstPragma pId ideState nfp + CRR recMap (map unExt -> exts) <- collectRecords' ideState nfp + let actions = map (mkCodeAction nfp exts pragma) (filterRecords range recMap) + pure $ List actions + + where + mkCodeAction :: NormalizedFilePath -> [Extension] -> NextPragmaInfo -> RenderedRecordInfo -> Command |? CodeAction + mkCodeAction nfp exts pragma rec = InR CodeAction + { _title = mkCodeActionTitle exts + , _kind = Just CodeActionRefactorRewrite + , _diagnostics = Nothing + , _isPreferred = Nothing + , _disabled = Nothing + , _edit = Just $ mkWorkspaceEdit nfp edits + , _command = Nothing + , _xdata = Nothing + } + where + edits = catMaybes [ mkTextEdit rec , pragmaEdit ] + + mkTextEdit :: RenderedRecordInfo -> Maybe TextEdit + mkTextEdit (RenderedRecordInfo ss r) = TextEdit <$> srcSpanToRange ss <*> pure r + + pragmaEdit :: Maybe TextEdit + pragmaEdit = if NamedFieldPuns `elem` exts + then Nothing + else Just $ insertNewPragma pragma NamedFieldPuns + + mkWorkspaceEdit :: NormalizedFilePath -> [TextEdit] -> WorkspaceEdit + mkWorkspaceEdit nfp edits = WorkspaceEdit changes Nothing Nothing + where + changes = Just $ HashMap.singleton (fromNormalizedUri (normalizedFilePathToUri nfp)) (List edits) + + mkCodeActionTitle :: [Extension] -> Text + mkCodeActionTitle exts = + if NamedFieldPuns `elem` exts + then title + else title <> " (needs extension: NamedFieldPuns)" + where + title = "Expand record wildcard" + +collectRecordsRule :: Recorder (WithPriority Log) -> Rules () +collectRecordsRule recorder = define (cmapWithPrio LogShake recorder) $ \CollectRecords nfp -> do + tmr <- use TypeCheck nfp + let exts = getEnabledExtensions <$> tmr + recs = concat $ maybeToList (getRecords <$> tmr) + logWith recorder Debug (LogCollectedRecords recs) + let renderedRecs = traverse renderRecordInfo recs + recMap = buildIntervalMap <$> renderedRecs + logWith recorder Debug (LogRenderedRecords (concat renderedRecs)) + pure ([], CRR <$> recMap <*> exts) + where + getEnabledExtensions :: TcModuleResult -> [GhcExtension] + getEnabledExtensions = map GhcExtension . getExtensions . tmrParsed + +getRecords :: TcModuleResult -> [RecordInfo] +getRecords (tmrRenamed -> (hs_valds -> valBinds,_,_,_)) = + collectRecords valBinds + +data CollectRecords = CollectRecords + deriving (Eq, Show, Generic) + +instance Hashable CollectRecords +instance NFData CollectRecords + +data CollectRecordsResult = CRR + { recordInfos :: IM.IntervalMap Position RenderedRecordInfo + , enabledExtensions :: [GhcExtension] + } + deriving (Generic) + +instance NFData CollectRecordsResult + +instance Show CollectRecordsResult where + show _ = "" + +type instance RuleResult CollectRecords = CollectRecordsResult + +-- `Extension` is wrapped so that we can provide an `NFData` instance +-- (without resorting to creating an orphan instance). +newtype GhcExtension = GhcExtension { unExt :: Extension } + +instance NFData GhcExtension where + rnf x = x `seq` () + +data RecordInfo + = RecordInfoPat SrcSpan (Pat (GhcPass 'Renamed)) + | RecordInfoCon SrcSpan (HsExpr (GhcPass 'Renamed)) + +instance Pretty RecordInfo where + pretty (RecordInfoPat ss p) = pretty (printOutputable ss) <> ":" <+> pretty (printOutputable p) + pretty (RecordInfoCon ss e) = pretty (printOutputable ss) <> ":" <+> pretty (printOutputable e) + +data RenderedRecordInfo = RenderedRecordInfo + { renderedSrcSpan :: SrcSpan + , renderedRecord :: Text + } + deriving (Generic) + +instance Pretty RenderedRecordInfo where + pretty (RenderedRecordInfo ss r) = pretty (printOutputable ss) <> ":" <+> pretty r + +instance NFData RenderedRecordInfo + +renderRecordInfo :: RecordInfo -> Maybe RenderedRecordInfo +renderRecordInfo (RecordInfoPat ss pat) = RenderedRecordInfo ss <$> showRecordPat pat +renderRecordInfo (RecordInfoCon ss expr) = RenderedRecordInfo ss <$> showRecordCon expr + +-- We make use of the `Outputable` instances on AST types to pretty-print +-- the renamed and expanded records back into source form, to be substituted +-- with the original record later. However, `Outputable` instance of +-- `HsRecFields` does smart things to print the records that originally had +-- wildcards in their original form (i.e. with dots, without field names), +-- even after the wildcard is removed by the renamer pass. This is undesirable, +-- as we want to print the records in their fully expanded form. +-- Here `rec_dotdot` is set to `Nothing` so that fields are printed without +-- such post-processing. +preprocessRecord :: HsRecFields (GhcPass c) arg -> HsRecFields (GhcPass c) arg +preprocessRecord flds = flds { rec_dotdot = Nothing , rec_flds = rec_flds' } + where + no_pun_count = maybe (length (rec_flds flds)) unLoc (rec_dotdot flds) + -- Field binds of the explicit form (e.g. `{ a = a' }`) should be + -- left as is, hence the split. + (no_puns, puns) = splitAt no_pun_count (rec_flds flds) + -- `hsRecPun` is set to `True` in order to pretty-print the fields as field + -- puns (since there is similar mechanism in the `Outputable` instance as + -- explained above). + puns' = map (mapLoc (\fld -> fld { hfbPun = True })) puns + rec_flds' = no_puns <> puns' + +showRecordPat :: Outputable (Pat (GhcPass c)) => Pat (GhcPass c) -> Maybe Text +showRecordPat = fmap printOutputable . mapConPatDetail (\case + RecCon flds -> Just $ RecCon (preprocessRecord flds) + _ -> Nothing) + +showRecordCon :: Outputable (HsExpr (GhcPass c)) => HsExpr (GhcPass c) -> Maybe Text +showRecordCon expr@(RecordCon _ _ flds) = + Just $ printOutputable $ + expr { rcon_flds = preprocessRecord flds } +showRecordCon _ = Nothing + +collectRecords :: GenericQ [RecordInfo] +collectRecords = everything (<>) (maybeToList . (Nothing `mkQ` getRecPatterns `extQ` getRecCons)) + +getRecCons :: LHsExpr (GhcPass 'Renamed) -> Maybe RecordInfo +getRecCons e@(unLoc -> RecordCon _ _ flds) + | isJust (rec_dotdot flds) = Just $ mkRecInfo e + where + mkRecInfo :: LHsExpr (GhcPass 'Renamed) -> RecordInfo + mkRecInfo expr = RecordInfoCon (getLoc expr) (unLoc expr) +getRecCons _ = Nothing + +getRecPatterns :: LPat (GhcPass 'Renamed) -> Maybe RecordInfo +getRecPatterns conPat@(conPatDetails . unLoc -> Just (RecCon flds)) + | isJust (rec_dotdot flds) = Just $ mkRecInfo conPat + where + mkRecInfo :: LPat (GhcPass 'Renamed) -> RecordInfo + mkRecInfo pat = RecordInfoPat (getLoc pat) (unLoc pat) +getRecPatterns _ = Nothing + +collectRecords' :: MonadIO m => IdeState -> NormalizedFilePath -> ExceptT String m CollectRecordsResult +collectRecords' ideState = + handleMaybeM "Unable to TypeCheck" + . liftIO + . runAction "ExplicitFields" ideState + . use CollectRecords + +rangeToInterval :: Range -> IM.Interval Position +rangeToInterval (Range s e) = IM.Interval s e + +buildIntervalMap :: [RenderedRecordInfo] -> IM.IntervalMap Position RenderedRecordInfo +buildIntervalMap recs = toIntervalMap $ mapMaybe (\recInfo -> (,recInfo) <$> srcSpanToInterval (renderedSrcSpan recInfo)) recs + where + toIntervalMap :: Ord v => [(IM.Interval v, a)] -> IM.IntervalMap v a + toIntervalMap = foldl' (\m (i, v) -> IM.insert i v m) IM.empty + + srcSpanToInterval :: SrcSpan -> Maybe (IM.Interval Position) + srcSpanToInterval = fmap rangeToInterval . srcSpanToRange + +filterRecords :: Range -> IM.IntervalMap Position RenderedRecordInfo -> [RenderedRecordInfo] +filterRecords range = map snd . IM.dominators (rangeToInterval range) diff --git a/plugins/hls-explicit-record-fields-plugin/test/Main.hs b/plugins/hls-explicit-record-fields-plugin/test/Main.hs new file mode 100644 index 0000000000..c31c45223b --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/test/Main.hs @@ -0,0 +1,65 @@ +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeOperators #-} + +module Main ( main ) where + +import Data.Either (rights) +import qualified Data.Text as T +import qualified Ide.Plugin.ExplicitFields as ExplicitFields +import System.FilePath ((<.>), ()) +import Test.Hls + + +main :: IO () +main = defaultTestRunner test + +plugin :: PluginDescriptor IdeState +plugin = ExplicitFields.descriptor mempty "explicit-fields" + +test :: TestTree +test = testGroup "explicit-fields" + [ mkTest "WildcardOnly" "WildcardOnly" 12 10 12 20 + , mkTest "WithPun" "WithPun" 13 10 13 25 + , mkTest "WithExplicitBind" "WithExplicitBind" 12 10 12 32 + , mkTest "Mixed" "Mixed" 13 10 13 37 + , mkTest "Construction" "Construction" 16 5 16 15 + , mkTestNoAction "ExplicitBinds" "ExplicitBinds" 11 10 11 52 + , mkTestNoAction "Puns" "Puns" 12 10 12 31 + , mkTestNoAction "Infix" "Infix" 11 11 11 31 + , mkTestNoAction "Prefix" "Prefix" 10 11 10 28 + ] + +mkTestNoAction :: TestName -> FilePath -> UInt -> UInt -> UInt -> UInt -> TestTree +mkTestNoAction title fp x1 y1 x2 y2 = + testCase title $ + runSessionWithServer plugin (testDataDir "noop") $ do + doc <- openDoc (fp <.> "hs") "haskell" + actions <- getExplicitFieldsActions doc x1 y1 x2 y2 + liftIO $ actions @?= [] + +mkTest :: TestName -> FilePath -> UInt -> UInt -> UInt -> UInt -> TestTree +mkTest title fp x1 y1 x2 y2 = + goldenWithHaskellDoc plugin title testDataDir fp "expected" "hs" $ \doc -> do + (act:_) <- getExplicitFieldsActions doc x1 y1 x2 y2 + executeCodeAction act + +getExplicitFieldsActions + :: TextDocumentIdentifier + -> UInt -> UInt -> UInt -> UInt + -> Session [CodeAction] +getExplicitFieldsActions doc x1 y1 x2 y2 = + findExplicitFieldsAction <$> getCodeActions doc range + where + range = Range (Position x1 y1) (Position x2 y2) + +findExplicitFieldsAction :: [a |? CodeAction] -> [CodeAction] +findExplicitFieldsAction = filter isExplicitFieldsCodeAction . rights . map toEither + +isExplicitFieldsCodeAction :: CodeAction -> Bool +isExplicitFieldsCodeAction CodeAction {_title} = + "Expand record wildcard" `T.isPrefixOf` _title + +testDataDir :: FilePath +testDataDir = "test" "testdata" diff --git a/plugins/hls-explicit-record-fields-plugin/test/testdata/Construction.expected.hs b/plugins/hls-explicit-record-fields-plugin/test/testdata/Construction.expected.hs new file mode 100644 index 0000000000..d1376c084d --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/test/testdata/Construction.expected.hs @@ -0,0 +1,18 @@ +{-# LANGUAGE Haskell2010 #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NamedFieldPuns #-} + +module Construction where + +data MyRec = MyRec + { foo :: Int + , bar :: Int + , baz :: Char + } + +convertMe :: () -> MyRec +convertMe _ = + let foo = 3 + bar = 5 + baz = 'a' + in MyRec {foo, bar, baz} diff --git a/plugins/hls-explicit-record-fields-plugin/test/testdata/Construction.hs b/plugins/hls-explicit-record-fields-plugin/test/testdata/Construction.hs new file mode 100644 index 0000000000..5e18c66209 --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/test/testdata/Construction.hs @@ -0,0 +1,17 @@ +{-# LANGUAGE Haskell2010 #-} +{-# LANGUAGE RecordWildCards #-} + +module Construction where + +data MyRec = MyRec + { foo :: Int + , bar :: Int + , baz :: Char + } + +convertMe :: () -> MyRec +convertMe _ = + let foo = 3 + bar = 5 + baz = 'a' + in MyRec {..} diff --git a/plugins/hls-explicit-record-fields-plugin/test/testdata/Mixed.expected.hs b/plugins/hls-explicit-record-fields-plugin/test/testdata/Mixed.expected.hs new file mode 100644 index 0000000000..93adb44a44 --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/test/testdata/Mixed.expected.hs @@ -0,0 +1,14 @@ +{-# LANGUAGE Haskell2010 #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NamedFieldPuns #-} + +module Mixed where + +data MyRec = MyRec + { foo :: Int + , bar :: Int + , baz :: Char + } + +convertMe :: MyRec -> String +convertMe MyRec {foo, bar = bar', baz} = show foo ++ show bar' ++ show baz diff --git a/plugins/hls-explicit-record-fields-plugin/test/testdata/Mixed.hs b/plugins/hls-explicit-record-fields-plugin/test/testdata/Mixed.hs new file mode 100644 index 0000000000..810c78eca7 --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/test/testdata/Mixed.hs @@ -0,0 +1,14 @@ +{-# LANGUAGE Haskell2010 #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NamedFieldPuns #-} + +module Mixed where + +data MyRec = MyRec + { foo :: Int + , bar :: Int + , baz :: Char + } + +convertMe :: MyRec -> String +convertMe MyRec {foo, bar = bar', ..} = show foo ++ show bar' ++ show baz diff --git a/plugins/hls-explicit-record-fields-plugin/test/testdata/WildcardOnly.expected.hs b/plugins/hls-explicit-record-fields-plugin/test/testdata/WildcardOnly.expected.hs new file mode 100644 index 0000000000..4b196f27fd --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/test/testdata/WildcardOnly.expected.hs @@ -0,0 +1,14 @@ +{-# LANGUAGE Haskell2010 #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NamedFieldPuns #-} + +module WildcardOnly where + +data MyRec = MyRec + { foo :: Int + , bar :: Int + , baz :: Char + } + +convertMe :: MyRec -> String +convertMe MyRec {foo, bar, baz} = show foo ++ show bar ++ show baz diff --git a/plugins/hls-explicit-record-fields-plugin/test/testdata/WildcardOnly.hs b/plugins/hls-explicit-record-fields-plugin/test/testdata/WildcardOnly.hs new file mode 100644 index 0000000000..f339895df4 --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/test/testdata/WildcardOnly.hs @@ -0,0 +1,13 @@ +{-# LANGUAGE Haskell2010 #-} +{-# LANGUAGE RecordWildCards #-} + +module WildcardOnly where + +data MyRec = MyRec + { foo :: Int + , bar :: Int + , baz :: Char + } + +convertMe :: MyRec -> String +convertMe MyRec {..} = show foo ++ show bar ++ show baz diff --git a/plugins/hls-explicit-record-fields-plugin/test/testdata/WithExplicitBind.expected.hs b/plugins/hls-explicit-record-fields-plugin/test/testdata/WithExplicitBind.expected.hs new file mode 100644 index 0000000000..fff4d306cf --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/test/testdata/WithExplicitBind.expected.hs @@ -0,0 +1,14 @@ +{-# LANGUAGE Haskell2010 #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NamedFieldPuns #-} + +module WithExplicitBind where + +data MyRec = MyRec + { foo :: Int + , bar :: Int + , baz :: Char + } + +convertMe :: MyRec -> String +convertMe MyRec {foo = foo', bar, baz} = show foo' ++ show bar ++ show baz diff --git a/plugins/hls-explicit-record-fields-plugin/test/testdata/WithExplicitBind.hs b/plugins/hls-explicit-record-fields-plugin/test/testdata/WithExplicitBind.hs new file mode 100644 index 0000000000..b416a624f5 --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/test/testdata/WithExplicitBind.hs @@ -0,0 +1,13 @@ +{-# LANGUAGE Haskell2010 #-} +{-# LANGUAGE RecordWildCards #-} + +module WithExplicitBind where + +data MyRec = MyRec + { foo :: Int + , bar :: Int + , baz :: Char + } + +convertMe :: MyRec -> String +convertMe MyRec {foo = foo', ..} = show foo' ++ show bar ++ show baz diff --git a/plugins/hls-explicit-record-fields-plugin/test/testdata/WithPun.expected.hs b/plugins/hls-explicit-record-fields-plugin/test/testdata/WithPun.expected.hs new file mode 100644 index 0000000000..c4285b629b --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/test/testdata/WithPun.expected.hs @@ -0,0 +1,14 @@ +{-# LANGUAGE Haskell2010 #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NamedFieldPuns #-} + +module WithPun where + +data MyRec = MyRec + { foo :: Int + , bar :: Int + , baz :: Char + } + +convertMe :: MyRec -> String +convertMe MyRec {foo, bar, baz} = show foo ++ show bar ++ show baz diff --git a/plugins/hls-explicit-record-fields-plugin/test/testdata/WithPun.hs b/plugins/hls-explicit-record-fields-plugin/test/testdata/WithPun.hs new file mode 100644 index 0000000000..4b34cfa652 --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/test/testdata/WithPun.hs @@ -0,0 +1,14 @@ +{-# LANGUAGE Haskell2010 #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NamedFieldPuns #-} + +module WithPun where + +data MyRec = MyRec + { foo :: Int + , bar :: Int + , baz :: Char + } + +convertMe :: MyRec -> String +convertMe MyRec {foo, ..} = show foo ++ show bar ++ show baz diff --git a/plugins/hls-explicit-record-fields-plugin/test/testdata/noop/ExplicitBinds.hs b/plugins/hls-explicit-record-fields-plugin/test/testdata/noop/ExplicitBinds.hs new file mode 100644 index 0000000000..de44a8a57d --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/test/testdata/noop/ExplicitBinds.hs @@ -0,0 +1,12 @@ +{-# LANGUAGE Haskell2010 #-} + +module ExplicitBinds where + +data MyRec = MyRec + { foo :: Int + , bar :: Int + , baz :: Char + } + +convertMe :: MyRec -> String +convertMe MyRec {foo = foo', bar = bar', baz = baz'} = show foo' ++ show bar' ++ show baz' diff --git a/plugins/hls-explicit-record-fields-plugin/test/testdata/noop/Infix.hs b/plugins/hls-explicit-record-fields-plugin/test/testdata/noop/Infix.hs new file mode 100644 index 0000000000..c361e9f2fd --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/test/testdata/noop/Infix.hs @@ -0,0 +1,11 @@ +{-# LANGUAGE Haskell2010 #-} + +module Infix where + +data MyRec = MyRec + { foo :: Int + , bar :: Int + } + +convertMe :: MyRec -> String +convertMe (foo' `MyRec` bar') = show foo' ++ show bar' diff --git a/plugins/hls-explicit-record-fields-plugin/test/testdata/noop/Prefix.hs b/plugins/hls-explicit-record-fields-plugin/test/testdata/noop/Prefix.hs new file mode 100644 index 0000000000..c34ba0a389 --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/test/testdata/noop/Prefix.hs @@ -0,0 +1,11 @@ +{-# LANGUAGE Haskell2010 #-} + +module Prefix where + +data MyRec = MyRec + { foo :: Int + , bar :: Int + } + +convertMe :: MyRec -> String +convertMe (foo' `MyRec` bar') = show foo' ++ show bar' diff --git a/plugins/hls-explicit-record-fields-plugin/test/testdata/noop/Puns.hs b/plugins/hls-explicit-record-fields-plugin/test/testdata/noop/Puns.hs new file mode 100644 index 0000000000..c81e66666d --- /dev/null +++ b/plugins/hls-explicit-record-fields-plugin/test/testdata/noop/Puns.hs @@ -0,0 +1,13 @@ +{-# LANGUAGE Haskell2010 #-} +{-# LANGUAGE NamedFieldPuns #-} + +module Puns where + +data MyRec = MyRec + { foo :: Int + , bar :: Int + , baz :: Char + } + +convertMe :: MyRec -> String +convertMe MyRec {foo, bar, baz} = show foo ++ show bar ++ show baz diff --git a/plugins/hls-explicit-record-fields-plugin/wildcard.gif b/plugins/hls-explicit-record-fields-plugin/wildcard.gif new file mode 100644 index 0000000000000000000000000000000000000000..2cf10d6bf13276e7c09ca0133ae8e273bd572c6c GIT binary patch literal 551286 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
nODr;$}t+p_N-IuJ=vJ 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 z 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%0A|96kKv4kH9IeFyN&Ma0d@qi&ZmFxPINi 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 P(}MZX4}O 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ÿhg>sU)mGR!<2(R>M_-qi(ZGSz zEfEq8=GK!!{hCAQYTdWV}*+KLrajOYMA2K`=IWeBlYEu2~cSnn@Y%5*ZMHRPr z7Bj3(gue}>RGsUtni{O(;n>+web%O_X)*Yqc7SvCrFf(KH?7{55&zgZfR|yk4|ehY z8Bv?lW3RqIuias7#l~Y&`~DcqZK^FX?jUaL2)P&UY}l!Vn(S*jpk3SvX84o46mEgu zP`&nchBWC)#>|uy;;-2AD&qUfc4!n6#r;H$>KRD2@qGF3+w2T$i2i4A5u_4*qL?gE zGy5;bXjx~9Be_Hadv=0#Zgb9S!0%~zmPOVT8}BXn*6tX=N>_4^zFZV_KL2b7tGoSv zMIy+gk9le7oB_>7c2mcH+}<2m!nZ+UNw12|f#o}zbRO)8e_nYywKP57`;$jQEVigT z!(Qd%jqlDA$}SJyj}TI=H@nRlHcO!&S_MN9`HHtS4hFwyzXgq5pKxjT=TDBIo}*3< zJiud1S=_Y0<#LN@V}%DG3I6tS=ik_k9V&^^bUQC0#EVhm&25`iYBLssOjFpgT&Oc6 zpF)6H$b|Nka%-XQjlRt%EYIC1J$s}?5<7N>a^D4Q*&F<^g*;XpPiVxx=eg7^V~X}7 zw`k`)U~P@y>>2~BI+kC};MZNt z|AW3caPBt8zP0+(ui6>w;7<=u{kj9wJ=eQRd8N3OwRXsAz?&bWC|irtnW zZ9e1=%o;<{4ok@^`4xznnePT<;Av;|axvx=kd1cfLBB^sLQn<Sn zm7D*oJskh$RbJeVK=ln9;=KpjO|N=cFH`lhp7;C+{j0KwNgL;Nzk_)Z4ON@VV*8*6 z>$%g?5<9|GpCWY904wh@PyF4o7g^Mdw7drr265lM`uiUj1zM8)euc)ACHs4C-Zhy! z7&}M2x=$%~qJo+4VP?#HM|`T8lyxa~;62{Zio~)Ll=xX!2I)cXMsn(xWoa~Zm|Dc1yOwSwRVb?M)8g@$MgFjRt{kOR>z{xU)4-1LIK^tV>PjW zT~7ClU$>zM8%S^*$BADQx*(vD3Jxpg-+?n6OZIINJ61@W`$i$-{0jPIb}TVzlv3a! zerS9|$6zpjV}^X7;}*+nuA0Ckt`sEF2YnFXyf!^u*cHX}$b-2R6DfJM9bo!JvDVEwMGym?b;RqiSNyF~^mmj+wgH z!pjk;Yb#;n)(!M2qeJ^<+sTEjqtgdl`~-CkEX$QcspZwCQ5UJ_uM#5K$QeF!-Af6f zrdEK?m$O?(#sz=TrTsn1)$P?6?l^ZvzT|vp_o!igyLt6ckZvQe@;v=oQfc4@2Po~! z*xGp-2PF7>KFfalR;I>^Qn;1vG4Z#3#`CcA2V0-C@5(zh6-v`_}c#AW!0r+<1;A>1esT>3HTB zFT+PYgK&+0dc}xbWT%a?r&T-n#HA;vA<@Pruaj*6sBQ8MBkm|j*b z?8XrdL+^`X&nOGrsOni9M<*?L9Uu{Fp7S~TPZ-&ZZ>?i|7U_uGW!UA7I$E};YLaxC zq85fX7{T9JYxGbqlrEjO$w^s+oTi5~j;PT7Xn0#?CI)(F48;sW6q7@gO9S^A*-?lD zKc#$}Rza3(n5~~0#*cRx#xUuKQ1aab%&4bgYV~YJal5}9gzx#Y+q%l@#3=Ropc#TR7C)QPO$m6!6wbgK_DMA-!zkPPo1Il#v!%c(u3n_`_=~Uvo8?7-qXJz{Q}e3Ll;e0dI^d> z)M8_7CUqih%f)7qBzuNMsemG5J{!7 z^x1IWi3y1p@i^hjJ*zGw*D-WUr;R7-Bv3hqcFx9*?2431J!-0*KeJV~R|ie##5tEk z`HHcNgyHyAXrWe8%3&msM53cr-z*S;R>B42xs9c)l` z;TlE_PG1un2K&9LQ##J+9-S}jZ!zT!8a&q{m&B08mo`E!yFQN13*^jttti6>HTTJn zX1O;nFc5-Hkqo$&AJUl#AL!>`0-I3XfUy7N??g<7EPQrZTzE6(?eQov*6+D;w>Ext zuV>#A&5utm*lWaZSGqQ+Aqaw=#S=MGj=MCzgbbaPg-$(Vy!A74tot%Mi~LvT50`Ge zY8sT4`0n#Vt+|B3>JLU{+XxSRMkTHBYhb%P0!!tM6a)NT1TtxGvuQ?wi;g)un&D<; zXiz}LxM|FDNA!hK8B$|OZVxuUZp$J|!ssG*VBJ*uiCY8G7cm_aHe!nqu!UAYBWs~n z3w)R*X>oJ7l&OY)Ei=0=dWZKt(dd_|Y*<<(8TY6fa2q5=an74|G5Z0fdC5ghC|PNs zm5p!`g8;6PoTY}FWylc@eyvEocYQ=XF}XSPIUc2eD$viRTG|oFm~stu;H;-Afo)*a zkMz{_&DmaT`)PCRO_1iBCfFt8O&Y19e6?4-#YUp7TXHfh9iC}oY=}wn>%}FmX&Ug6 zE?4^ux=Gp~wxICl2%=ENPQcE?^|_x|VZfqRDIeAxGbne1o=kj0f@7zH^8fgjm@H&E zn6qE0Wk^|Wq-;(Vp$rqKIiX4j-ck(9CmdK3ZaZ$!W-mx=b<3OZG|A#(G1fQ2 zA4%A1s}YTUs~~w0^1R=7EJBvK!VQRrP;%9PO-vo$rzvHt9An8KQu)M&#(#9^sqz8bA?q?xfkQJ>Ucz3sd?QskyH=tOH%dL- z|7E;60%W>T$TjfujI4NC)g-qL-GyTy)w-cdn?TPdsd=OVr~Vmqg3wK0iXV5hfEe$u z3*YlLyRCLMzV(HQ0{Anrws*2oJ~;*Ly{mjNl=Hz;0MQoFmhwF?PZiMLL*SjMPOe(=F5~ zox@a8+|517T-)EGp2H}&y`7m>r+MG@225kASDd%pb8MBp7pb{?>KQ3Q+&Kqsdc63EMSb%zX^Lfs@!5t;n)FOGOh5|)UHyG#zbMY z5)~!dBkz{Hb;ncxQK{OE>{P7Vd4BTcw5mSL8>6sHcbn2Zvu(~hG;6fn6gI4sBB~XJ z{W9NV+Keh#@EsbR;>8dAy;XE6WX^Q;fbHs~%2fZyCyE(+aRBoD>qfm0yZiI-Fwe6U z)jD9sQ?+c(hD>x^GXvg2Vq0-ZT$i3bc3FIYx`ZvX?;KS7Ji9}ia8KQZ&?M-5aJUHXMzEpgwhY*6@2F9dsS7fXfkFrt}FfvTOZa@EJc!Z7)Exo)}| zUb;qZ&VMLYzF@j;)sMY{1U?8^ucnhOTKUbil%0oXK*-2JNYCKbW{^$_NGH*ck`6*< zhw0>sF#J({Hf zKLoDl>-rENoqxef&V}x|GonKpev?|FCYYUw88ny?i5w=P0n(w=r^ZQ=et!Km9 zW{n*N@fa|;dQfpTbk+F6sz>?7*@mrW?%`Ze391O-H-%z58(;x`20NpkOV#WeG#ZW~ z_Jg&EB*S%f7yGlQ0RS3M*MU@#P8h4?A( zX4NK>szDVgqkP7d$Y<(}k+l*ZnlWg~0RN41xVFWRG2^M$7-}SDQ0qBK!(wO~bjVD+ z*d6lHV-`f;>$w@}#@_4tI+v-=kf=3Z-_xr!3^8Pp#+}CRX;W?nSxq53>tM({4>}}Gvsi?SN}zTHaB3Z zOLq^~kASjH+5asx$VH-lZg9N&-F5LI8uqnW+4y4 zTRN2!*l?E7-q+8V~gZ-A5o8zi)bL6Ge`|Pyt!GJm<3jfAsfg1r6~jr-(l#l zf?zt+*d|cY3lm5#86^<0S3$E>u3iiS8RDv)=c!yTGN>L?i4nb6*3yU};aVT4I)hXR zihj<}V7ux~zGRh5l1lAqbqh}N`g|^(saeZBB}hI#AXMj*4S$1x&azX9U@fdjokmu* zV5;W=CRH0Rm35}obSgAxAf{xkLV9nAHis;QuS`cgl5wb=izgjI=DIAd)aS_bIe|@re3-ld(kW8LYwbM zm&WY*NP{b$o=N}{*Yj2-24>7>BA{G^RG9bQTeUH=4uiq`b4g=aOG}4=%w;N`VIXHV z!sTZG`8ARv6_6J*`o@`H2ePuN;wL%?$E9F%L9lfRbbc&1hEVuZn4>A94mWusTsql z`hpr873up5VLD|xt)zLN3z5s<^WNcXhYuOYN9kT@eE|rDQ;$tUbBAHi^QP{T?kUcS z38xm$XVtc)RVkhyp-TQ<(qu~f-rf(n;BdYzoDkSgT=8-akD(8l=aS(eEL|NE|MKOri$zLf0D{WM^COij zh_E^!l{K^D^n1r&z0+h=Tz!Qp^nf4rgaZ_jatIkG8|GFC5zuB`cOi<)IQ4rAa*Y5$ z+b5eqfP)A}6)C%e)JPyzU*TR45^4siOnZl>GZY<|ikrzAe2_`M5Y|b?4U=zG3orQH zd*3FOSVk>3d}uO_iV2BI?Qe_t@F7$-;O*Ja4ZyDS>i? zI&@}LC&`-Cq1Y?3Zwx|K59<8>r~)OgC| zw5UQkVI*93x)^6d`(Ri$Q?-h#>C03{f)JU?8iG&CeOABCqEy>(N9{#bz9O47iZ1dV z|JSD@z9MBj87C7e)sqmuLa2ajkOG*Z1@6ZHq=Uw9@*tw^TnqYnDVm7v2d7GDY+?zvHpLf>43j^0iJG-?{+vZex>P&pT+P+13 z{`37#(Jljc!aNW6e0Y(K03NPrh!0I%z7)p?eNAE_=q&9qGU%Vgyl#+jGDwNE48p)U z%_QjRnY27lolIA`8DTF-89W1ea7U^P>|*O1?~$&Xo&Ug#*LzL$6Y= z6d=^-UfP+iI(#M^$S@=Tp?Eoi`EdF46fC_k&xLWsfvG}asv5~}p9@o9koYUD$gxVr z27hIlpZ(GXa6lw<**2y8Qy^bx+rub)a&gwPALsb>sQ+^tIlV|q=@8rGhr{prrm(`$-+GlAvXntlbIflfm8*T%GYgms7!=_ zse=cUvVl%rJD_jj@F%joto<}2-2arRqa*?yM`%Tyxj8+(`93r6HdBruMY)5pt3vE= zK!x!Af{WHspTOtw-Pv4(8iDM_5S0ZX5oV+qE}$r8=uzcOsPAyOB-5%*iXqZH+YYOb znTwZtRU??+?~u?DAk}r6of8PbkDh8ew>5gtC^dPN^V$6$L3fz>zPQo@2TUjlg=LR7 z4yRxE&iiz|B0A~-;^tPH>p19-qPv#4Y((|r@3`jG!e^8YZ#_R9Q!Rok;Qoo<39@t{ zcNl+t(cD=BJFOl9qEkRXJxFs{tVkiNGQMK#8Q4COOxU3a{iSFjQl@w9_L#~xn>v_} zP_g}`1PBSIzn}?+)hMFz%L+;W3EM9Ol8&Kr`;LS#RT=V8C8WJa7^=P^b*h6R`4gKt1iAdNAQs$B1Y$3GvmWq_r68&pc#yO*t=v5@T z?X8YTkpf$Y4mqp_fB?MQWRM7xEtGFvRl`3Z{FKO9{F|eCv*~V@;K}cF#ed8HS?{j7 zmoe1;XzO;J?YFl!U>1?gJ^C)wCU>T-?o4fx>#^N-{}Gv%FS@jyIpM@S(&>+DPRAL2 zi6kYi&f?C;PXl}24(!=}CraPtez>cl zKm^5-Q4z$sguK&gimG(ciqPxAHm62&rZV;xwm$F0A?dNT;~xiHo=@a`dh+MRJ&(Wt z`Tc(5imLC)S6ja;yXUqflRp+0Mq6wg!8%n9{rMHVVV%5#L{C-moKWA%R{mq)K0H*d z9ftR;awy;9QKD|(KZRs$@;VhvIHBrTk?36+>~t~YX2srJHNcKNn46zl?$z34y_vaL znf$24@U5?PP-RNs1Duefu+tVIKBhRQCj%xOf|X-eHPhZLJ6tk;B%U>JCc& zQfi6#O&{?d8L7CFbiV=$oVE%W;O2p6zdSr`zw3)_+t0)g3rgm1=Ug23r2pl3B>W+z zvNhx7y>gTAftZ&%=5B?0)jRGu0GsxWPSyoqQqcamASy&W@;v^-@$ac4+YX%Aew3Il zIsSIX-G|>lNuEunUi)j~gu~ApkGI_Y@7KRqPT3)$nWR5(qs&tXeP6*&q-m#AC}*83 z#mHGr@`j+jkWF{3poy6xm2bNO$+%ta)+6fSGHIkns)drL_P&jfz(L(3{ehlpBj_N? zXRTM|LjAzWKyRJ8wJdLwbGbp|n_;SEPAh`Rs~4I}*Y}*V5@IU3Rzl#~q|H6(R}OI( z^0A-cC_DI}?Tg=r53T0NH(%Sm{X>6nz~RSxYaR_N=ex#bodez#6Z^+p<*d>-SY0z^XKAj*!?6#Riu!Ap z)r}KDDT7NT))(g^s_p-tM{Kfa*x&ke2aHi%=KqGWP<^3t-s!#a>%M0TO?w@a!b*;- zHOEJ<;AY+PkWOzh%k%F?XO{a%r8e!CoO#yL_=euPd33!AZ@5q!zFSLdSs82nkI`4O zmIPMmf8$58x-a)_oqbFEj%gh)wsov_pjodR_wa(U=9!p*IM&jbOH0jds8E)BLm*OJrS?> zJgfiQ-n_o@^dmao{$U>Z^G*`zW%s}6y056F)~IjviQLqNZEmlt6^In|qyEu2b$jBHg zBYB>+<}>H-j|CPF<;!n+M$gv?{IItBgvY`9#(b^Z7yV6D2Ns@mymDkMbF~i2ZWl|t zp1;wxE=4|8!#M7o(?gy*6WC23w3^JJvE6Y_evA7OT87p8w?BRq636YRxE&CXjw?{l z%tDl#RqbOA&DXq-xT^T*2rN<2ed2E+v%`th&os1~MQzV91!#HtE6uktvdOm$5lbz> z^yJAZ*Rp6pqPCRNFg>T@aV0M%FkKI_?hU^*<@cQ0Q^tt>hZVM>l#DDmggGM{bG6Tk z!$jslRCSs<4SU#*Cg$i-0Drx|?QE#Me)aZPiuIXs|Kx4_Q?^mu0n=jk(SuM8q)NUb z?#*`Rl_CM~HDQ~lWktD_>)I0)U)Oah)V=d?%e}9d@S_$Qjk$^|W4q2^3v!jDW+NkI?Xlq zb<8ed#KipdHzj%9*hbVwQtKyGkyi8=eicL3c@}aMQ9?OZIx+Hh& z&zOZlpYIwkr$=8wb`P*&(le9PTpl=^l%GmbBOqPNf_nvu%M6`UqHTJC z(g6)Stl^=068kOVEM||Gy%~@M15`QWr*EH2(ogNx{#-M1`>_R}fdw*J`$glP?`r@O z4zE_FbanOiCfRd|L)c#K44iS$$rbjGB`oS);o~P!q^F+0p}+>>SD#7&+%PT=XZnn( z2;ez74{IM}cYm$le=p?nt}MY(qL=(F0U{^0OiL;L)8^%!MbOJj8cReELsM5Idm;~c zm4VQ`e_&8_v{6~-@5okFY1t9F1eIBysqS2{Zr36i<;S;eh|x|7e^@u^{={N4X>43? zE#F6LQh0v-aqK$Z=P01c#SGw0#cxu80A0;bn+B_RZ|f7}f{357OyJ%In(I03a1(sg zo|N>mlP?w~m-Wj-3+$8fkRUf|GP!)5{`yJalMZ41TuD+=kn+$`<9lk7x-M&r%P(OY z%K28yOj&^uHi=^*0>3g%!y2*Wd~2WzNX*=SYF_($4*HsJS>C%konNp@x|RB6W<{bT zsMWxwbv)E`-Un8%lYjqmyHQZwirQV{{EX+4Jau!%L=eA!x3?09e$WF|^=I{QqfYh( zP54V`54(QqBSQKJr6~oHR?yQ3{K1L$^jw~v=1`O-xdRsA5$vFomBZ2`g$wPfe0Mnwg?TW7c`I!E2zVZHdOic6R*ZpS^YTT%2 zUrcW*6h(I@o<~jmLfRuJ4{vGr#Qk^e>fdke$0A?d!uTKUx$QWr+xJ;vW_4j*B<~)` z-N0w`zPq^jk#v3$G^w^d|sck_EV#mE2s0w^<`BXaN_d4`}?D&JOijc z$ONh(P^KSjgaN3S)z1)X610;0z2yzYSqR1G`8(6)e1xz7x?EccVl5C|l6)E+ysgxZ z1jf_Bw&a^F!{5=TiI*5(T8EQ)qVIuxSSBCQC6NuT?x8@ryI2;n_YCrT7Y*s|8Rwf2*A?Pu-UFPF7vt#uaCb(Y$7J}&DlTd)6?zWzu1`ajF-0mr_tRENCz3)3+R1sF1qf`>J?xlB8>y1OOy=Viwvp^DLVQE)w_ryx*1te409hD zq~t*kD(bfp&j|ob6a%`e*)F2>?*U1UMg;_8b z=eCY$hZ&o%Q(QL$V8DKDK%lY$0F!dS8f*l9z*v3P=`o{5ebX4lIEmas1#w1~knlxY zY7C12fF?Oa7(>4_=I5?~4V&CBOroWlEpeu-%Xto(BT^sMh2}7B4K&IJ&%q-gXpxQG zFWKoa5qbBDzSf|ojbP$>i(w7JP9@PH>FfF#UBfClz(!&rq~Z#{I+ZB_O-P`37V_%! zL_>$;e2n4oN=}1l$CVwHdph(38P0$7u#Xzm2Ra-tFraLPe(qNkEOYaq&Ay$dp&n#d zYYFvq#g^^C(Z6lU@GLT_lPs&_DcHHHWVxz$x@xbu>e{&(X1SSkx>>BaS=+hWXSq9d zx^G@_ceV5I%<}N*^axn-Sle#r8It80-s!n##WTikTYT2Gq|R+=E8EiTyt1>r4t07R zS@FuV^X6oE7j$};taz8(`BZ25)OPyRulO|D`JT)2z0m2~w&L4v=hv0xcdgT}Z^f_Q z&i{6n|D8_%u@(PuyMQNI0na)EUakbp+669T1uk_4e%yJn^!B>Xol1j}9OCDorIetJ z?Ll&qpv~gs&R~j_buIp!$uvYyIYh}S0m&r>Z%Pj~=?b>^rl_zG+bRq$XRsZr6>ZO> zE)jR+Nl;#0ic05XCcsKrt3ZQdV zU)3SzJ;khS1MTO;ZryI2{^?ko|6(!LVTuKNcNi0a|4(Bp03a9w-j~GI#f+W9GXTcRJ2L{FiL@z<}4j z;Ks5kP~79xdExPS${FAK|IPR9NPgL(GAA;T<@*{U|4RwWi+$V@kn*4TbQgX83Y^5b z^x{U&*LN@PUA;VWivh*x`1c598XIPP{alFd(1_28CK$E-{A)ptZOW_r*E-&wf|rnP zGXE<~;%7WR8Z~yk>-|&V-nFUKczW$#A=Ehr#6m6?5IGl}A)2+2N0F-HLFjbl)4+&|$&BXqX#`ga& zU~XBR(Sy9)##bzoo{$ug{zH(9?R3KCN*w=3WBXrxF*j$9+=Hqb(gRAzXPAFVX8$jI z(N}#NfA@ig|aFRZ3a=!X+Nz&W;oHrNVHaH(o(scDf zR&dmVDy)Z@p{+i{r;BdhSvnr;%pl%B*o+ ztUHxlzDVHOtks=s`hPVxzuf5y1!oRiOS!D~7oD0PiH@8*mXCK5D}|VUxm~x`^)9YG z`Di2je1O}YA{*09WwSc%F7LrUs*@Ah}8X$X;rCp2oiR z>7T`#v{yGIm>UE4sQrI@Yor4aHD>#3zDK27_1^A&PrUwn@(}I(z}@$jWmY8h;l616 zeT%EAR;#y%2$jG79q^6V)LsA>{bLH8{jqG`-DQH? z8)Ev~=9&I2e!?rrj>ztb+1TF9N%R-9%=a5A?;SjrpA+(2M-Biu4=OfZc&%Z!-)Bh8 zgVCO#xvx&vUKN%4(NmMooAs=Vc09yY3vy8a)$Q=!URZ88j1o#H5M^>J?(rwd{Ko@gq0QpP5{&ozsX3`it(VAL+rvWnIGTp)TQMi~?ucHY?{93-NhjBkt7K(%yOCV@-on_zum~Bo=a6RfC`G2LH4O$N?rDpw@-Hw~q zUO(_Z;3!f$dbPv&o3;8U>2SwQGB1aJX5AW^rEn^-hr}M#{z@NlsvFPWRjP|h0Ae0R z33~q)2d7%mpk{9+*wiY0?dKBMZ^{oietO-pB)8{ElTw&FL5~qN%DmV~TGKylouqJ= z0$RqU7f0_r&5`ePgKcPrFK*70?vm1-ZVAHFYO~$}`y)=>oi`kz zjM&>{%k<6x_0y&}*NNp!E%(>YhZkEaGl~(>&9|-olI0v^QNz@8d1ja2!aLjEW76>; zDQ>#ZgAo(&Lx>+`=ldFN?#G^oi-La9i=unFRa3oB#-<5Dg^#x{4}-e{n3G z*P=m;M2OAc@5TG?P>R2`v3dVls5(IBZhF*+ko7Ymy^x_>tjN&UvXR#4MUkq6Dysg` zs09z$<~TZDxDjH572jycFv9@WynZ|&a{Ie-CkV@F#1%+9Hux{+Z*@G-7&;oQwi@Yb za92;>QS;xy+`4Ad(pl97yqC?Ex=vxE5H~~ia+;=cDjtYk!z|2MQ9R=&cw2x^X42>s zpXbpzr^4}uX`TW_mb{DA(RewH(mW6!_8=r+({i2Va!Ga=bX^Q%`spy8?L<1G^&UU; z{j8)N-A57@DAh8N$9ci25JI6bOc#)jSI2)`<6&BuD(pt&YPJ0(&U~3FE=D#bvR7dT zsji|<$W3THqgBqod=qImHO^TcU-$xvjnu^k8%Sld1{_kdk@28&6~Fbe~ywt?Ev;2u<#sY47#!x6V9KR1v3h*<1szz3Uoo{D?4_f@7PMKsO! ztL!GWe`ssGvDy53O~rqx}>-zH?n9?jApfb(QaEma@0y?NkB;D~|y z*k$Fn0FW#IWfWJ;Yvc@7KC&`aejsx83hEL7=t zfW6E|Ei}R9uLkWA{`@^x*EsHC#0;D^T~fK)}0vkVnLJQ;-V4)>-bH*|WbDpBY4n^B zeU*V@(bK~W-K7w-Bb`UbNGMO3S(*f%NP-u15=Xuw21Foc!Z!`TCW%npcnk*sb=IWt zYBKt|1aldW5lJ!s>Dd}pdA*k)=Yw&TQv0*6U=AMFC+0GxB-ormHqvuO2^0&_O$PSp zIZ^_d)J8&|By*0DQL^|x2(ZIbRR+&NZwXP#eDDe%sZ1pwkfKYwY-oIxfg@T&3aN$x zr^x8bWOO0f=SgJ%VFEfJLZx)M#;St1jPU&k;3$b~1n~#RIGPMq5}~AG1R%!hJAerk z_yY>=I}0~U0pkHgIT?4C4vQ0mb7b5t3TBdzJ4X$;h+j7)!t@ESV$#;*?$BI3;yM{u zE2ckyZtfaP2o!@4Nb5d`_T$y;62+Kh^13wf&a-P@845Xaa&dVgA*z5>BKxO$kidLd z(NlmvzyQ+)2QEE=gN|E#+`$t>9E*gQBw-CepaB46PFl_sc8(HjbRd{hogsD8h>)P( zOK?Z88Yj|0-&po9Wy?!DG0k{6Jt_;gwRWCjtF!H3XK$zQ`Y zkPvr-@@YaaQ%0Sfg_lsli~M*?GPpWqu_foYQ5Ug@g8L+tKSz#Dp~500g?nGaKS08h zPb6nb(98nxm;_x7Y|W%X9i-?gJc1@bpb{;b^g|Pc@^?sx20kv6vNNBAk{QxzLi8dY zCC9db4mev}HH^dK-mj5xgFqT}lJq8{D1u*CQDgy>WFbvfkf&lL3f5X5^S*3%F-Cqd-WQ7`y7HaV|f zf-9lyniR>$Qo%I5Y)3%QzD8&7bnOKK7U}3@3J4j8uY1gzhcfyA1aZhBPKcb5V57f! z`o)5H<6LDVz-C|$2yN`~yOgB|YV$EhR7)>nHBYbFwF4Z)uiF=nXpmrMMcBwSHPBd$ zohKi+j5}^Mk1vpG6^MZI}tY-=uvlA7YVH_m2=C2|0-~qS0Vp(jCpefEG6PT3vm~P$bLTKgb?%` zs$Jw4=64jf_L$_$2rLrn0U!5Cf-Mq*M+wBIJ8Z6bLvI1l%K%tQMMI?UUP0#E6+|}) z0r0~!BmnmZnn#8;Y(#fcpqVhRXu+MiiYT>3jOas}*Upv5@T_J5w~r zHa7>S@Uia!pqGeykKfr}?Y1arN}!{b>8Nyd+-K_UPg40(Dcp++e=e1e5rc8hF<&Xs zC#1NGM34ftmyv5NHpm7MCg1=Xpn&20)>{KRQ@0@w5(AH&+g$yl?vA1JQ)phPl7nh9 zI!S1wcoIcqY*#!1K4e|+5C%os?D|=VxQgeGNl8TXofd%bNF z!qTx?W_yL*kZ&Lv{$3WGVPu{aqlh9@3%{K$!L0m%cazXVn#ngwn5r@U$N|`UhObZp5h{xD!ItaFS)l33&RoBTo}T4|?1- zs@nO@5LC-HZf!OYu3fZ{Q?uIl4!sV5NZer6Qgrh>IFX8J%{8ukzt2TzMLGZ%X&gC0 zM?UA{9uW6=5#clL88MXLXkP5_aolJ8x~|kCm#^h-%?8g=a4QtV)r-M@f)DhHZybNj z7U2;$Z-7-)w6rqFGapnYftIkct7oDn@z@+O_On#Jh-zodu8+7{a4%vkMuJ+F4u9t3 zdg-7=9PGe3c&SkSJso4WD12rL@esCE*au}YctcRgRpBk^83V^|GCRf5Y|@}tC_hVN z_X%-x6trbxXj*gqTCUf)8+eg8@<}RNIC1@Six>lZCLsS_f~^>|Scd`HC>Uit;+Ckm zXUzyWegVyp%B9k8C8#BH@CY^q(}I^9-I3Lf$6OYm4W`ib%IzR~wC(KC%pvd=35}rc z`|&#A=0Vc{32eaOwry5wgsgdPn_Mk^H=Pa^NpTHga8uo6f>eH%0Z*eMpHOhAlAHUM zkflQ82@u?2z~g<(Flujp*RU&Vdfr74E$MvHZnVW(RE8pyTqmKicl57*1+52bqKtxc>Cl|c92z%G*z$3i-fYk=nj zF_?@9++*avn@xwI&QD{L(aM%qlsl@#JUc4p5E0P1_Be@{`=;B_ZYY{!Z;>s*Ws^Xb z1b6+VTb$(XM&*09p|KXPjS_)}C&XAJMn1=QssD(@$eCMXJ$65nUR#j_g3aJjbp#q8 z#XBAQ)TSEh0nSPL6N$J+o?8?!6C;5vhdgM0p8c~0o&)3@-GvVL_PlZ4)DrL7d4arb z7`#kGw0Xdw0dis+aDakr++vs4Vz-s$$#K9YlCKX^cFwTsf3?^9DLUv4?u-kEKi&qi zN3P9Ca7{>WMymC)4QM5OG<(L?gOQ@VhKKFNC!0#pwE{q~GJNfaT$;2ukdDv7TNsn`lO?nPYgsA2OHQe|J~hK z4}K7!r`_+sexjrPSv>n>8eA00XA84Z0Ph?jn4LE>$H)D2J`wxZj?>-6xc$Blc$6kK zjp@$#_Y)eOwXZ7mj_WqLZUz# zzgW>OZ1&tqyT^YvCdSZoxIjAQCDAs#myNRBR-|TuR4R3nHO>&&dt3dJI`(PX1tpI1 z$1`phy0hg6505`eR#wokx+!j6C>%IpWzW{lRmq1itkM~`8<$`EX@nm$7Sq1@4xZSM z@<8~*&0yTj&LfH@M^qMR>1~%2Y(H+&tZIw(;W|yg09nJzd4DS*Hnio7@XD{WI^S|zhj?mfR6)eD|G-fVe& zK$LRnzX9TWp}pm`v+|=@th8H#*K;e<_&YkN9P(vKzXU@A=GE$>a&#+q@p>dh*0fc) zl2b56w^+>}bl8y;K@_->$W+-VDcIb|17M-U%!4^6(0C#u4wzw@dp^5oQfL{-=Y%pWi;fz#x_^OG8f}R3o0Zs;Q_WFimuuy ztE*xdSLYOkm-t_N-|lb=P0uGf_J-+qY*GE^bjkjk)_YG|O(NfFz6QLTv<`}Ajj`ut z=k0YFkJ^VTCtUKHPGvuOmY`B-oGW!$7!Erlzx7QHvv?L80R?QQ855hg1$~*X-W^vS zuS)rgWbk}bYrn$7`jrZs9BpbXD{J4}W!@=VdEHT#b~i5pC+krf(#$Fie0wtTEN!50 z+R}or`=&{YwU!J=#O>V7?WHfRi=i*atA;R4;`~Ud3YGbak#e&f^_x9FNkD9Wypn}r zY+7Xal-6{LZuA4furHk)J(*pTG}y|$vg^@Z(rT9T_|DkM zRL?Pq!*qDws;d+k)qXA?!xKK9wKu5STudEMc&mDS8vm9yb3VP)slz+AW_-i>VrkO! zFgb}-XLI4)4F|8pUvRxGVto~IcU%KLas`kjlF+<|svQA9PTn)B9IoOqC45(;^_S?W z5{zz?gP|Wy4ONesSmzyQX7Zsveio_enBBO3M=xy?M=+1IL#EZTQuk^Gk)ICVJ0EohF7v{Vadl%i5U_jgtL0v*sHKy_w#ZzVEcf|e}Dq&9!$7m4+)WlK= z(_MI5gFJT7YQX*CMNXNNedyo~CVo$ti?!t?6H~NUp_d2;_;T>XQ{uUSoZ~OTW_>aL z#2Ku=Fl?EXN7}Vi>UV|6)nQ1=q6GlF=6w4V^Y%?4eEfuwBf_jRFDTJlOMRN+od_Sn zYFCa*jlA4PPq|_Ogi0EQSH0eh3(Sn23QVAC8+V_L@xv*~$sb2D9K>DI6++q|UArow~_$GEq4Qd3gKQ!1ZFWqOJde^PS9FSmH=|r7Lg<^Yyyi@T`EcG|v zI0gpi*-n^2^^ZH;q7IO-JIMHb#dLvZH$6Qt%w372BsP<4Pv4-Rr%oob99(n-yVRjU z9$q#5$aCi5qJdo6nGQBF5xZWUrIt$om1Q0HSuVkDKUdpFJ^Yj1IsP|VD5_?JMGW!B zKrya@qT^p?q;eYRt86k%ava@b?>wL?NA?QXUVd@thRMG~lRMsuO%?OSXooWpg!Svo z@+qRGzsjfi_DH)*wslI=rzCiEV3K@~x-4+Qmm7%#{Yu>4uVqZz4z#2{DJ~f0>9$WA4rEW< zkaX?KxW1}jr2Fm0`aa8WVWi^9f$zO`Lp$Sqf>pg|uiRYwV`+K**C>zjx~!w%^--Sz zN2yU_1#mj)Nc)iAoy|kO9ZBmlO;_MvA7%;LmfxVYGlolJlSI2>305dupF8gkM)b>n zqRSQINl`I`_xKS*Wv`ic52i*I>!oF!!!O`F4vv2Ek(O7lY0VN755|0j(<@Id&KUm6 z8Bj_Wy76`|fSYnotfgIHS3YjqAiUs~62>LjY-iQ_pU7D{Qd7{8Qb9V?Q%_R81UZ78 zfhX47I|ikOZ$cMb1kI#eTYiURJWkvpcxd!Hl;9EYd?YVcO;7gND%W~U(+E>k& z(EOYs!!66E2P4nKFMS)>*tZ|}{(MFmacI=jl3tWz_u}GYPMhYd<)dXU7Oe7}ZftH? z_OJYB-fZpj{k=Cc8@?ZY;;eRX?CX)Q?o*LZKYcqS8|ce&gK96hCFYE4mu5AEzI+$z zb@_2f{g*_)Jp;~~p9P66CUmbCi<4V!T>jTQ!)oT&*wcW2rVqL7tedG_%ns~y`%hLh zk4mz%v)ojnG`6>{VPi>8;P(u^|DJz}{>*z_>cFU7PH}|({Gb%|>(u7Ji9LQNrzUg~ zx=zNuZgp?CS%0*(()RO`S4>Y?wpY>EjEUOEqt{u=+h;3xHeYryUPHQC?ru^0r@Vti^rq^DTypAVr8H~^>?ean}_7oZH=H1jQPA??p#bRIVt+94wu4&VF`eNnp?;HE^vUjL{ z&_CZ^xABuZ62ATB8@fhUvf_W}0?0GfLL2@!TPGHw0BTURIh^;d@N_@HtW}`L5%-Pw z@brfYN2iXL{^aovRru=a4g}+)gBwjmn6SIXrQzlqSXje%=l;5hu)UC7Ze;OSNAs0S zTXHLGorm6xT9wgwwgl9v2y;beNLU?2B^lyf%PWn!{3^51>=0(=y}NgM>03QFWoUuz zTqB#IHhTv48`;_e>3`POMd z&8Bea(0zsOfO5x&0%Pk6+nYj<7XSDh{PiEK$Gs4Uc;+xaC0OJ`_P+ zX;^+?73QA_wcGACz%@>`h-K?II^;GmL5HQ+uy0Tr znhP`_ay|=5H3mzzeYN*S@|w@5*Q6<2hMnTMt?AeB0Qq#Yn8@N(_shNx&7Xt%_1wKhixPf zJ=bIjhgiNSBYg}0_Tv5%5tZ46h; z7>GpX2a|RLjo!w$4oqoYH*fI+VH@PaB z8gvs`P683lqVZWd(b&yh#aD}Dex6E3tS^6}z+?`oZ>=8+8m4Z4OF^ncKpl%~V2894 z8DMlpBNyqN%&f8{K1vL^X?6{Eum`5|8(E5C?Pq{PzISW~c9`pjC2K%+YFQ3FoGjV- z4|{IdjLzL3#xg?ssQBW?I+dk8oMclrUxcbM^zScnScGS-DjW%CIW3w3J|^L& z4SvS?KFu2MJ3!j%O;ZMU|Dy5xOo~lUo|ztg!w`!e#&Pz>dnXsih{kyY-M5rx6XT~h z3TvW_^F4)>*Z4;*&yZ1c#Hk78L>f2w$j+$Wz1~mkeA_1en(L+rxi2uR;4s9`4=quo z(KI?+w~OoMneQ!PN$xWpgxq*3``#aL#)x~4`$HkuP~y~doZ8aD08cnC88(fuHR4x`}_Yw2NJCvUz(&BJ@j z`BX<%$SRml2NSQI_aThU-`;*j1WlK47|EcO1& z_#!QgUGX96R}EXTQ`f~9VY!FPXy;}NK^NgMoj+iXSl9$}GmO@8nW2Xv{!-)zU21&Dd_Tb+i=MoX*!%?H zx-BT~n}HbON6v%#IVTBxis&D;xLMQWPB0Qorv|KXV=Zqbal3k=T?ni#^uYc*K^7wD z7kSU18di*P{>y)~vU)hhUyyA_`f}LUp&FJqfIB=rRtA!rJtRMcjkC%ri8O|%3b{_r ztX-p^1C?ctR3;hA0p5tzDKIGP<)Lp(=T$J(8x}aq-Ib%Q6VCNpgzp3H+GgMeR_79? z%2Vk1J~hxFdcFr0N+PwX@vl0qauY}wD%fBta3Yw{_&p7@_RNnLmU)KdZ*PM}>;qkT z*x5bs=sNb}0QUA#u160;hXGBXssW2~DfEa%m3u52)TM^?gwCdA=LZAOm5>=HL4e|Q zW*joVjgT1B%yMQxQ@s5-H80oPfdaZWc;t`L5nX8Ahqtz}3u|78ngRvv8s-wmZY#m|gvd_QCM8Dyc-Gl*QC6PVskT7_wkh@>}+Fl5`PvhQt%v9I|Pty+Y z!y%rwjZ*QCV}#7jfrtPdJ>7{&nm5-k40N46F@=0jtx-(D=R4EG(=1=ooQaid_I z5j*%2%bEdQ+b0BH?UOC(Yz;wP>MEz_PJ-hoGarYg?$mc2O{jmwe8Vh`BqCO=n=Ua_ zDXMrk1Rb1~a`)b1Y#9jT?Q%;Vs{T3W2Avi%~ z$g!VsyF%N12&uEZ(N-vMT7+)d zGEtZcHitaf9|qbmjBk12G4Fio?OzJBKr_TvOnDgGZKrgzBzfO0FWVpT_nM>RynpmIN2nq{UXHG z(c9h;K4@@1nh0CB%1l{Jb0$HtUR?7?%2fLFx#_Eon?S0c4xeZ)*J$vaH)%|LX3gx5IQ;#Yb3r`^7rRS5M^f&qSe!eYe2s$(BJ?kE zlbkxRd+O~0F^GX$q+Rz*t2naB_Km;r;L>D)CO6-#HfFxtLQz&+{r7o9ZUOYjg70G5 za{?~#?Y?^ZptRUd6GO{W8*D<~2_o%5GR-D#pwnjr!zi;J8z^^jAFgyvZvcwbl|b1X zKmNe5MqJgG2L!8YXFbEf>(FVxKi>|5z@pu}0!ux4l<2OVyQ<66Ep$Ac|HnGZTM4i$ z*8_I=%ZYSD1!}`-d-)2J8Z+d_L+aW5be|n@D>2V>Q;L@d)S_|}gS1k6CA>}5c-JAv zDSZ2|#`*h7KAO0IfnklcAb>cm^vYNXR84r{7HDF}V;JPkn16-WcTHputG(XcJ*-Y- zxDKfuIn`09X3^G;v$E3N8K@a!{4>ctEtLJ(KQB=_7if}#oqJ$ap!i8J^2$S0<9KNZ+P}AUFoQyqT=?~25ZjxrK-CeZTC=dhX%wOWwP(E z>7u_ISHHpk%D!B#ndy`lTEpsFu*3J{(>iB)j^6{f z=RcYYcTLpn>M z%1Yo6CX?zOxPI5Ff*~Vl^~ZqHoz%ym7DeK(l6A?IRZCx!6UzGj`Qwje+?*`7wWWMn zMGG)c3Y1@ixGa1AF~mSdl(qoer@95mul+Br9`$n-#j6B{JQs}tB}RB`Pf zZkf)pZPix?7|&zhQ8j;^$}ef#9^eMWZLVEP;Vt)$n$)y8MpYbM0tbk;ZlG@ z{&P=xz^y~%c-nIBBHUTXZos=Gsw^2(dM3v)@C%8G3#(z8u+#7>22k6~DdoHtBgZwX z&JX+uvcZc1I%a8M(S z=x#&!rghza&T`v(PhY+98(R(L)Y!nF;|pW1teyYy)sJ5kTV_v)ENe?Ejm{{S7<-QH zhG^fHn#DDUK5Tp^R-y6cFC#tI#UONbEaE1k?GzuX-<>V?l(@Dn(YPC2+KadFW7IFw zhfK4SRg(h-6iO^8G}1i(HJ;abQ95LiI}S#*LSn6JW!2#F(qQ)a4fmwpyKNjeerMa% z9)9x}d?V&Qct(?d)|=u;&Mf)}?!$=(?pvJfFM9ExTelrni(%_=Uk>xQ#>6e5Xm{s* z>km&1^R5kCznh2CemS83Ey-Q`c=`Gix*H}8KVlUnxRz+_p0ReI4ysP0=*AKDW84C~ z9E9aa@{<^i;%jW1(sH;J55V5}*{^%4Tt3A^Pkn6aj?(e%xYX4=Byq7{Z4QDcwrN(M z>>1aPQchg`L(^PX9VQJy83`l>zpoZz4Wk&FbE1BwhqCOy-{Qh_VL#SH4p0#Bi+}si zE5;&O(ktc1vjMXFV>82-PpgXsFI1#N(=4*SOQ<-)NlOWgV!L-nA^q+IUI+Dgd(}6T6sfh6 ziubq9Yu;(L$QPQIMoHbY7HFW3IP-mb_+EuE@!C_u-^DW*?cg<+wOJ~>)sm3jRF%D$ z5u0};F8k~}_qRIh>Q8Q{OvQ6Sg|mJQQ1X8W&kg+g9cXJb&bD5OdHj6GSJQzm`LlPt zC$>z#b-XKA;#1s+JDD3r&dwWbYB*1 z^8>~B(xg5`hT6U^-MZn3&#U5{S-+YQ&i|N?-Q!x;T&(Fu>F||K>^BMb%SX zcSmi7;3FcP_01E~;5LMQhm_tshbHN;zaKtYID9F4v>)nwxA9g@?!_P_;iBRQxWcN(N?TK?0Uy8+=uF_$?Mdt6x^N9L~p(;a;y5t$=FynNH&Z}BE0m;Kxl;_sv`tCv=K~j%{i2nND zQ7i7R3P2%wkBPCa7by4-7aj*rAWO1Fb4QUZ^*!>-oCK9Uk{=@kN=UTi(J@Itn7HEf z3&pjVZGY{3nE*qr+3QyyEanGttxpyG>b^yit7aBw#Rs`oz_A*@vWSnArJmNGz8al)iN$5!v{5zXcHu5(`*(bV<`^%#|J_R0r%k4jFb3FeNi1Y0+0+|Me> z$w7vu&LpVInb+|YUaUnUU+K>YW~|R4mAvJYNd3BtFCCnzOtz=PG^k$V+PJuNxMSE+IRVi3g5aaFO4 zBPjcl;Pov_@`0+4b!)vzhfW_eu5w%2bBtY3{=vhy!r*e1o)7P3pm7|f0(O~tmVIBl z9>4IS{WW$@{<43p!*TCW)ylDwIg4x_gTwvyM2pn;@2}p6xdx*NjnP(~9uNH`q&i+u z)4$_wBLVN?t$n;R(rcZrN0WTkhvs@Xd^vmU!|TAGXE)d&(Q^i-zTU5<;!9Rf7u?s^ z%R5sdjn7@3xfZ@=IRFcvDh^k-K2s7(ovBeMXt+NX4$sQT8w2@q+nj7hF|XwtObp~1dCQ`>_b&Z^C*KeJEyVKdqURXMy&8rL%Isfhq1S6|q!?ItIPVDYnZ{JD+o$vDIk7uG zjS&vJ)=VXSk)pSaz1;aJ=$HJCFdUAQgbdH%jXil0D<5bAspkU4wvIH?QUIIbedze< z2QL@GGU7uI7{B^|2z&3ZrndOecBc|rNPs}-N$9;uFG}d0P(=hx0O=wiC0H;CEp!5? zK|tvOqJkhIq9zmpMWhNUDqT?YfFfc!hs*cA_r`rcyfNONu-6!S@3GdJpE+qK-OsN6 zY6#Ex)IfAn8J7t=k%kDl))=`Mie?9ezlVdBk>-5#a*trAr0R-2`usO#EP@7D6- zmoV+$k{7yYoe{wX->3ytOM$H04XdUx3AZf2mP}z}>SaZQR>W9=+RfZXCPI~^2Ld#> zc+p%8m5%lUCDLlL-c^%es*-robmKdgovJ%eC(`t7HmvJie*eigD;-=5(tEgxde=V{>6Ay4Biq zmA`T$IZr<^7lPiezPR>D#&7JMB%vkk{l;^<#z%a$&O0}q;Y}H_Cv&1UX+KK1{+}|= zBDk_K{b`>b2+whXV^mEyo6c&E-O`*-L;N-#Q}`8fdw1|meankW8lh{oV$}ej!sLsY z)yuz!q)G5JO5(%6EZj#8RaxQEEk zpo{$d{8mk7cC1RQ*HWx?HEZwY4?Tn(UJWppCH?oNEzEnw|N8sxrJsdgoY^sLNX=&C z)a2RQA1{9pC$v;cu2z}eN?vMyq|{KebUJiWWjpHYCLH@uRP5WUnr{tH|05Rn75UHp zn+;$n_T6KL1JqZp_XWoIW^#}=eE;KRtJ8wI%qLMF`Kzm*9Bx{=O1#>-n38;Fp2{u! z@`;=EQRB-e{_HPYe~K935VqO&ts$yDSqt@1#u=$wmo>6}dH*A&_V&rOjmz(~Kh?%U zDq6sA+vdeifllAvBE-%A{tAwc7rp)w^70EFcLyqe8gX)%zsw09&7Ey;mpcB>w_vE9 zjrIwJ8&A@z?)19D&wbQ+W20@MzGM|ymKKe7synOSus>s8Fb6M;b;$V}@!)sxMYrIK zB#E)5K&PvlV5_?)8&5=rZGTBt!)AZ`%~;6jA*gx9^98Sk-PKbbs;y7fReh0=M4qeE1UGWY*{_g~hKDcbqiXr09@IX@|ZxL(b;A$^AnnkTV89_la`dhb*Sfn7SW2{Gmg? z?u^O(@5UA1P3$`?h4hJ^&zQ>X=zln4p{;Li)MsdEx8ZllgZ%aJ$L;ATR&IEu1R~dqi!qU+=B6^SJ<1T!Va>ATX!gv$K z$V9OW1JMJ*$C94dl9cy%SpftBNJL=;B1VJj`jGkV{f@$-!HJ*u@(xsF3LsGdLyRTS zFZ)N}aeA<}VTh4o$O*cLA6+B>Bx>1(aiItQNecSB|0Bx(Uid}Bi0rNiDG(!m*Yyq} z>PA=8m99v+JJ60{g-eU&%0uj*?C)Cr}poK2;C39-xV)sl%RY+ zK|3Yk<}PNB6X{;^`WIgn>YnGme)4{z`v?6bse45ROUe96^5ADlBJ_L*10Q9tFl9F8 zz&hA#bnsvlFOV2^m;CZafP0&0Kv({_3ugWnG`fo>)Ey>YirxblrMr19+;Af!ZAi*# zYiiWcF5Q)W>Q8bWmMBqS7<8vA=pVf^>PAteyRknF!+$2}$@t}J7;1+Y#y{DMb~cU? zF)o2(^EdG4gSsa!7p+sWKLB;|_m`>36{Uvy0OzvyPhUAxrqq4W zvOjlEP#NcT|5*NfVD>Qn^lvTkhnaa@V>f!EUp{b97&1`!eW{f%f-(^JNud{W8d_GhIG)zsAq}UXs%m?lgOF@!_du zy4YsIo!c~7=aLSo!yC^HS$!6ZLXo2(|E&%^4C1HG@;upcpPa2QRPO#NAPiC-w ze3DM1LI9KQbe5D+zt9iu@qYIsAfbp}ZwO6*M6b#Lg}`drV$hr1s`EG%6j^`1Q{u%-Dy)xFMDoNyE)y#4 zHE4Me^x+*WddV8N325*+PLlg`L|iW`DeTFaJdqE9!+i^KG?0!SHu-3cBK;y2}_)qlYDU6sq`m&d?~^!!)OKA{o>1M-}xfO za&O7?T0L0Z$)H@$>aA7m_0)Thd1)&&F^t`U_h-Dmy=va4lXoU;=)n$&s0o`n?#=<( zeqX^Qm{XRO32Vm8QmXJTt0w9~4bQbi<83}woi|NTS@(SYPq?>HAlfzmxjm~q@oRCl z4;1SUBrVQ})`_Q5@%y75Lo%tG6GK0oHUw{K@8GhSt=`Fj z_X%KqU}Mx-m!$rOkGd{aPPWR?0xZ3XDx`~{$GNwv) z3c9~l!w-m92sdX4W^^40@a*!ISFT?aoBJq-GzK^>ipDU@0<(1ipJ@+A&6#P#^i~^d z;J1!2%|u@VMzg(NA3mWf@X_aYouAJ>6NsYSZAY`~Or1n~V}UyiPSCl_Y{ZBE z{%r5ZXyf5(h+7Iv_PuQx4VsPs`7eOWP(T&Y0SBvtk3HwFYcT_(-~-iV6CwV^=`uSD zJVdM&y0AFof7{sDR+!txnVN}<{1mLU#8`2bLFb}iJlk64YjL*4&Y}<QHz!1nz$rVgSAz4W99lB3_!QDZ8c+Ad4W4i;#{nqZXqlGr2ewR1l!KwHml%N z;2a<3gB1?)h^;huda)#5k%ig!C} zMqCNE_`=f4RsD|#VQ{yk+oe@s5GgkiJ{|!{lIX~? zYM8+~5q*`EsZ~}Zn6z$QN9|Rwwgg-JU4v$to2_IzX3~Z;MbfK$tgO-C-iB-ZRj;bAWj8E-Y`D`Uy=#z{nrsa|d7xQWr)7SvMmYIM zcxf+B8^fUg$C8oL5ha0 zk-G<$6rGS~86$0B2b@{>|6@r>|J3;a74BB!P^uRAth3pR?XM#IV11DPrMRF>m;rTl zV@TY;GbS*#f#SpP8EV<&TNq?($GB!?`Xz5nXU7dn<~z05oOl9$oi#!5!+ogA-gvxK zYZMin?%TPwP;MZAzKO1?Ki^t99Gt`J7;N{sLlH?0?k0;&*aR$y&*gW>uzEt#4r;shw+dxs04hV6=d~q%fdIfZv6dGq)h6DIS>jymmCdVIpf{D5=5qfpo_KcQI z(mSHx#mX^_8oAT7vxqM&O{Y+(V0*P9gvDDeBk@qIuFOtwcQZ?rky9OOSd`wZP6W_?KHuG)~r25#N#7u<0Ib~dqj0nwo zq43~PG3KcE^Nb43bMa4+dWwu(?M#G?H70DqW{r$ky#gzN7^r<{L*2dH@)FPoO_;o6 zr22Osv!3(lK3Yl4)ew4wsz^LFXhg!_>P^rC4BzgblRV*J@HQsOojS<}xDFA<11%w& z9;UNEo1W~k@}D(hK-@VViJR*JL$4qc<1@Du085)rU=#qxg`SGO4Gp9`s@Vi@w;i`# zYU5=c;{h)m7`GzveP1R@Jl8GfR$Dx!j;uS4uM#w0i$&{;2J-`8)HVRbIh?XPMxk8I z)X8*dIGQ>MvK!X{rsK_+>z3(-xLuhn!;B81HaZrZss2idJzilQmDngWsD$jnG8xb& zGlH~|MmQh8-`?FU?elnlaAiJ!O9`x)Qjg!pIuJ%Z^5|vhu2-T5vY&0qeHL!mL2SjcXhOhcfYB1(pp=ly;qNqpu-Tq4?Z!7Qnrgl!98cjv}zO zU<-Mt#Gfp0LmboDN6@+UCx;NzE^#71oK0s(;}RC? zZ>ZdDNfl%W9K;-nr0UyIY0n9TSci`qYi6&hakxNypDe(ETl3Z<3v7< zT%X4SdVI7%1qN7q4@}tE1nVl;+HY288KU^+0@=UDNqxf() zJmzq)3edHPv?#{&_fv-y2EJuRw(>>WwMG!%Zx<%DGBQu1#O&fkkP*P-Nw6atQ1d7@ z+K+V;JKuW zUte51m3Au=;&}3Y*NQ!Zs$xSlo$&+#1=g)xgu4IH`@JM*plL!hW+6ij>S@Ji^EsH~ zU=8uy;sMzANcV7=aD*p9QU!+wqq!Se4$#cUj9NcXi9PcC!J+o|ym4NNsZ|rtVJC`@ zSg1SE%DAe{=Okr$zKJ**!sLF^ctE`CKqoDt9=#hBtE}coywSN7u&1?uF^-!XF%gbZ z%^>ncx7@G~itb2_tjU0n)GJOYp{!^?@nNbc;MSdMp#Pjdl*y3rRjc^hme7lA&n@~b zVIRIzdNcPK2_I}Le_Zt{qA>Abz``=Ua&-DISoftmk2(B#pw+d;#7~<3@E&;jFn+WA zXcs_sQY%svkd}mG4>}dQuoz5eqj}E>O&3ib{9`ONPl(N88^KNt-gzLR~=@S|%{O%cf=iqm# zz{0)KBKb$JX-{96N$F;78ZBX63p0wJ;Xc)hG2!SWi$#@V`(Nc;dB4HJ?jVgq0co2u zQ&dQb0e`P?wrtNqH$CI1ZQ3RQSYISvxU($w`HPd(8&sC#`TlU49;(FiOnzn6N4`^A zwszMoGyJOwz+8?x5a*b_1@${jd7w$;WR)M{(8a+U;4e6^avB7P32j<_Q?t$f=%7_q zSm_{Ho8v59A>q*zm#MK}s1BR{87HA$l-fkTE?lo9v3IIjyz$$coH&9Y9KB(l*J=Zd zQ*h1;q!vEC$SPe5VVi9|lrjtZGCC+PoyFXY8&NXsJ_ha2dzs7UncXUF(glgnGB$ND zc0`8;)vRWExmzne#@GPIiRqGp2@>>}i_!Fu{C(?SvBHQmpPRrKhZ}dto9W~*o$#jq zdZ$I+kEM$OTcXTVN|gJ;5%JIdMyUIid>~7u1YZ^X1A`@6?2m`|v3Pq(Je;xsabP&j zOgn{EM?gJzIw(+v4Mk*EzByhbUCb2DML=tu3BcL8Ggxsb5NQyiMuP*@IPe4{t559n zJuPxAeku$b6h|EWi26>09-DpNJL@Sp`E1x_s za8zb?$`iTB#~c^)w}eu4_yZGQB0CYPlNt)^sO&A0FAH-e2{&1nFuellNZ~!&B-z-Z zeULXit&ak)m>(Fb{7I%F@wkwEQW|~@p(9u+gXI7)2u_2G`BJ>CeHHv6 z2~e~hqSOo_k6J{j{=Sa`ijn!5eo;y2Z4}ZML;pcV3$kpQ&{A=w7{_UhHi;)n#Uy6< zh@fuZF0VjVOy6 zsd_M>cDUD8G8n`YSe*P~Q7O$H?llfwTSVq7=0Ye?M`4U{yvlO2sm*~d#}38UCKVYc zP|c%uw*M5SAppjLYm*O8lt{L7;e9%MJ`CAuN<4OvD9GiHD}h~~a`(Or-Q|)3COvPK z2proL5FW`EK^B=EP_cYqJI@FCa#HuUI9=%s-%(^p{R^LKQLF&qum}L z+uU!{>9$7;D-1?8-erkr>Bm}KCm*1d?OyUtn{}OrtgReryeeU zW+Yw;ldv;$S3}$5=w)xnbFYOz%A-h@GTG=u!Ktz~xZHRHiAL6KT$Fvf-a)SbW)^3l z5_dK&gY|eRpEsyuw?{l}8WXxD>)Qt1Wx+g(kECT( zMX_R^V9|AyTkYF&A1Uy-xGXV!j6T`xq!m16(Q@y8j@37*sAcmtqKvfgnLk)z<(slY zXOzzkDMkBwIxOoXUaJ^%atcMBkaHb}`i-FH^(m8-P9i;0 zaZ&v2w)ib#=Q$;*m*^WBi<09YX_L=Q68rHn$GkA1fTzXL$C}&t_2Z9hq%iJna7}+T zv$Bq3*!$+x2EL!sTHOVumO+nq6+e|q3tTq*)c+Xv_K^q$I#kgsy@*^q5JE(?=@C^@ zjfL~19>JtU5{^HS;&w)xqAolX_Bvi&Kq!-IR~b|93p^w~5KVl6i}=Th^U&+jBwy{1 zdd~1$^b2ld^JfgsgL7DV0I4K}&&Mih3|v*yIYyVTn-eu2H^^4k@4;GrG zIByU5%?}3s9VBn|<6CSF++9g*(E#bEQN2%N=AUZmiUltW@NYg%3>Zp2KNMT}l-fIV zetsxTL-g$5A%^*IM!+xuJ)BiJoV(vUoG$<{=ZA}>M@r_O7MYJ+K0m_V#9XQzshl6V z)+$o*ccj+*S-rV%O~A9J%4hB#T&MVF)iuvrTtx3mOB#TlUw!$vP0osmNL2` zn4pYH%LZf6MS0-}dyX;6XrWdL-F-1(@J@iR*$Ay*-8_VLsRNxPOSr5E*1t2+>dzfJ zB?1TElE3KC2iKDdPH$#D%YA*b*$KLo2imZhf%dibh>2h4~bZ z3hBbs6FUBiODI-EiY&_b?vr5bc$imP-k;ZGA5NZ10LD_#)0f<8;{5Wsa@qHx>>dPs!d<4SXas5GUC17*;HHC}{Q7T=bS8*RnkDS72h-LL!zy!0Y(Ml@5) zX4GpT;^aPBA75ZX>69|MIqOW;f0qA9oq$K_C8QX=TpJY^QslrubY2YQ%KOEl=`nT~ z^w(#zbsKeIr)ZaA+7ltVJlWjmu_ee37Iv8V*wYa8@k$q*>-US1IbtT} z8>f38wfV>#HDU(W-5iD*p}y4{UcNm8Swv2Gqhc0opSrxbRq1DY7h@adwBrDeyRwl! z{w4^uSso&oD+r9@Q15~~hZK?A;@-lE_|3)h@tw#+PNmuH$TUBc<{9;S_<|sEoMft1 zG!Dl0Kt3XQa#c~KxYMyq|Dy;Z_v5SYs@2uZ*1-&8^^jySRZQX5;}DoISTH*l6~AvI z`(X#A0|9Oc%Dj!^E&Fl)qJ`^*tnYoN&Z=GFfwOA+ zScq(*0?G?0@@jkhxlK?-3w)WR)9_fBhkEE&W<_+_|NtD=_SM|)G0K@#u0U>cjH5y z1h$YA`U7rwyT;vjmfUbriqDJZMVwVwoNi$!!tchRa9r~Z9e0hRxX(HWh#Kl=#3+GX_%^Pp}*!i$C+10GcL!h>+Z+rC_baJawxl)}pMFf^z; z&rg)opY-b001m+!9jJyJaR@NDf~!&URywTzS&`p4Ck^P8j(|50=!&OXJm9sIewMRp zcNj{#T`s1X%?D=dZ7JA|hU**+7))KbZ$d$p!pGOzFnM+R=xg#7Nk_}$rml9kS^vbB&>3*eC!{;kvG2l^CagsN8p7{MBSx))$=65DhLY(@aDaWgwc8cRGo7I zkEA{J$l+n9X)Vr5i4`!7Wl9-$EyPO55~Ng^IbD`^zP5*Kr=IJSsM?!dfqx@*g?U@# zJr5q?B1UqHC13P3cB60LUI##EQWq4L#P8+2A>GWOOsWoxv?KMCac@E)oSR0=)4KAN z52BwL-F^)=HJ^GV^9uA*Q%^)ik3*$ykL2)O)z0U8ieDMN-hKC)Y|XbLZ(U;M>)y7EvXt+B z$jCQ|Q0Mzx@U?`waH#v?ied^uCW7CX!Fej7`Q{Q64A?X=5t^Sg74y$N>GpTb8CVe`>$a;JR8197wfHty?ERP?;@3FI z3`cxLPwYfC92*e1UW*x1626v8-r@@xFIXyTvU%z_fGC)-3UTpa!I3LR9wAV`v>{4) zA_Ql9*Fo<6(Hw1*pmm8P2AJCd2rB?hn^t8su6*D_Sr)dz_6stLFKOc-Kw^Zj6&qs< z_(2lNC;E6!8}zJIhGvA2*pipqdwCx?6BLIL!5j|61-T-_B25}Sj?2^spC;KwG%@;uxX=ay9%Gk(r%WAW?TxLcSR9R=QqZzV`4DKGo|IGd)Z4*Mm5pEF;c}(K_Q)s#0%-|061#2R#W_LlLo;BM0<> zg&bXWBrf=5%u7VY#m?1Ie&Z8g?`kp z2|-IElPrlb4zI{3*fBrsz%Qk5U~|M#f{%_h0wCQvEG;i1rimq9o5-#h2g2FBLJq%4 z=p(^Nf0eXgqGP{L`+GfGU7m$eQ>S+_`>V+Tvqpp=p zVDSr-B4FK0?9I5MX0Ijx3BPiih`VBk6u8fXDaV&94#&3e(t)la!xR{rNUm^;HUO{M6{axd8|-RCA+O z(F*bI6qlgsC5*_O)}uW+izDoTpYrr|Xf3=}k>BBnsOV=&oIE+MhkP!HT9$+odUb=# zKPDknPoMK@f@-VKKQF|UX&N+Hx3&(~C*2RuUHS=&i4$8uwN8nEil+b-wPVZiOAzG=SckyT%!3Qo_7ipa}#h4}Jf;g^9 z_(}+grsSZTE^*s1OF6Fb%ZWY4hSKC2?>l%aie;{Y&E#1}&HXSf7U)l$+p^gpm9j}0 z?hEcr)a)k&MVu*j4ylfwo%3AP?w>MWZ|{CoRCC&n=3+5K@e%=Z!!9W}UnGhhJN#bvVK@*2-#Z53KpCqw9=i0rkkg z#YfbnIo2X7|BYKfDv{c~h=GSh9fkh1Qo%PswLVcIzdL8YV!cG1is<*}H_7ksn$kSb z4QR8|D>&9Sx@kmt@Ovsk!KTl60C!_c=D`+tklEiaF+f;pblB{(_7?m%?x7BwfA-H~ z8-?_v@@yn4PrG-(M8CDE6gS(SPma#rQMaDXtudrF_?8VGtGD&<1t(eQ4ZrTmi@QVz z=%$s{r=rbLtsj4oDph8y)D82wgxamk+j<5JgbfVL9DihDQxYBel+sF!;^Ii1)>aUj z#+>fl2;WyK_?5MN9#bHAvq84ix_{D++B$ggEXStpkO`qjiFmVQaL>m8Oc!Mm}_hn;YNqQpOMl$E|6Wp zue;7D4AxZb^Dc7&-W;|Y9iEySE}V9XjOU{a46rl?z0ZRW38$~Ov5t84-lPwy zOxm$UpIXn4T-CVsU|^)X^ZCP8=C!{gd2x(AUFH2hZ0A&a*?PNQ90BeyqcqQkKW|W5 zVFc)P%B>vE38?)iI`;wDR<*M+$JqX9arMic2I>4EN``ay!1F1a7t^h_kEV-$5}p5f z$heYN@m2mv-x+(koyYe$RXIBhekYf!wDkQS3ylw|3s%lKs9R#5mX^h>G_EWwRdhNI zLRH9R&-!(T45;8u8$I5$j(E@B356H;>Ro0OUVhVmsd|WkG}Tw8x^|A{I*a5qm?FhJ zhH|@kS;DISNGZ-n=Za4c7EcS^lTJrco=Pj27MJDOnRoU;9d=cO9t~etqF8AqR7_4k zqlOZ5Vyw19$G`843&AFO>Mp}4A^iX#@~+5X0tNVG2y0HLwJ@y#7M_9ZfuEY$LfCsZy?TI&d?RZS3!C;YBXXcjb} zevo7>GHel3x|k_F882f^Vy`7OjA$e_C!0t3_?jq{9?4X3YMv$!cd@rU1z+9mhXAU z_>4yssunZ6eP+BZ7`{m}zOG(EUxtn~%=mwC{E_d|n=eP0n=yew%+5>|4D;!LX@p-4 z0`XPw$*J+>nTFp7_nja-neSgq@Il+>3FnpC__SI&Lx~Jlbzoq*Op6CI2?n!gY>%G# zVWdQ!O+Gn`r_E9`X9F5$&%W?E{M#of=9PgAlUy}r64-KKbw>Mh{b2}AJ`S|`xkzmb ztO9Ui-?0iDW^)?+_LIICr6e6qy>#^4jD34IUHoCMKjwAX%A8OGj~l1c_>}K|IIzx= zPw=Ie=C@#fi|fo`fNUIHrcTUS(5HYiGtlgpSEZMK>S#g1Y{{U#oWo01t2d1o=*kPX z@)qiHXxY#XCgIrYlYz%1yK{uuth~N!bQOApPp!LV|8*G932XBH*H<;-?a>P}jTY{S zmySR5M&vMa9%epj4s1C|%eL^IIvYP_d~YiFUdOjcQ`_7lYqE zy9Dk|n(3LDe{pK?p+yt5JCpq{!;_ZnG|YF_4}CdM>&OJNf&@?CKn!)*I32dcToG&V z-J_NHcdPH-e=*uWI2$kfp-wmu5HyKX$*=zH?w~td`JVH7A&Dot8l*|{O;>d<{_$>e z=6!%-M(FxgOfxL)%blq}kIPBfF)9x}UP7GQ0_F~3BQ@RA-oF3U@B#HBow|@==;w7- z=Uv4X;pIu|=6@NDZ)bCGJV8{tAp)#So&TM*z>`I$eXGp{-qFWZ@|v!=kSghe%!_+G zNA`FCvb*>%I>K;K#BT9F;6sJVi9E}9wQq&os(J7p$I|{~T%5~>&1GL)r%CCs6&)6F zVoUhPAI38u$jjvs9w*A0e?fh}Jw8D)ihXviLahgfla&+B`l9Tt>>HHBnl!L}YNJCqtLtysmr z(cXek$8Y8VkOI>Xz&LA+g$QF6XfH_ryT?#T1H+S9kp?U7@RO*_{>INm^0Lc1YeI9+ zDYD5WNWs;kS*zz2us>GGVYeuBMfUNoRsXOlsaK1zy=)v7i$pL+3RXkx!t_?^e)ckr zVSX-CURudL+!t?Fd`e8J-{VkVb+@(H-L+%+`NP!r;I)(aSibmYd)83`ANf%2teKa*b1%}6x(~{S-zZS`3 z8QwCV?b{u|jU?|#!K1b?z`8>0&B+Cv1NeHY57mNkCUd>aH?m6LO+w(tm8?jAg*WA~ zv+S;o3N6d4jgggW8`s=4tFA>}7yDGxC|0fHUu*Zt2#NSVpcx;q42VGvYS#Y;G)w;< z(CmM5T-kgT0x?$qf1sJQ8x)7GeewUwai#OgICVBn)cxO}+0+fF>SV_MmE%hFhw8v; zhg%PFTr$pGt*>i@r(T&q@49uo)v*MT0alC+=&=r%bG7nhp1{W&ST%!Bh6LrAQ@eL= zCdVzZi{+iKxjeYL`kbvh74WC4#*u?83dSC(yf&hvq5NlUM#PY?A^RHki!(XnGW6Gu6RQb zxBjh9@%XkhSgI_5qybz2DypQIrpiZJvJQUNqu3QlGherZVS<_QWL`x9-2q|G^?p9_ z0B_y-Ol(-wVwSX(QnB}Nrpgi%uBtp>DF?I-VjNd|0+VJ>-VaRqkZB|vjg99D7^lR5 zGS0b|ZDnir{Vj1M9L5nDX@7qm#nHgN#A-T1%>kB<)S7@9FOd8OLS(X3*Vv(#N8Y+9 zG&YA_b?p5K&A0EKvlVqF;|E3UyW^~am$t|IuA2wUW}sbT!iOzdo7!PwioG05rIwg) zLvpt|J;A&p>nWVroC^!hrFs6kgS3aPbQtDcs$@9IC?dE>=`?O1)L8z%b6nj)8EaR2 zcYhy4yR%stSf_a$gilYUCIe|os!12p3ta$TC+_h!RcH9JV7i<0@jMtO^F@$k-wGPI zfTJxEc?AA?4uf>(b*y=0T~G|3L!-1oF*__BDx4vln_1>YJqr_Z9FDU$oAloEzm2lj z*dBQF<Em!Xip^Y;Mf5=+{cB)iPKMfEj@$` zqT=EgM2OWj0+Ku~TU-&jn08y9-woS&dKETtRy^hi$6Sq;zeGAR5fI6qP-JfPLF@9r z4e*;XYv6jn=HsOX2j3cs?5 z%Q&%4WX^N=-1EorPG>tNPyO8Ty2rAs1j)K^@-^vezl9$L@tP!w)I?Ha#$ywaHpFCvE0gYl?M2a%?+qW zY|&lv86Y7l7j_WqAy+T*IQx}h54Rtd>lV6@=y(lb?_h=0e3`1^LUBJIm#G{b__;$59DK1+SC& zG^;FJ-AdSLVgEt=ufEVbHW`4MCF4jZ3l!h2h`1J)9*8V~4>9}YzLEJPI~l?m=6&?C z2aknJJE&6v%?|edt}$0diyxk|!ZiJap~)crJC*}HLDdLyC9Aq6;s&CH2IF}l!dofO zZ}FgbKwMu<*WH*14P_ztXqMPA6piO_G5iFL0C&>q}ocWEPzfT5*w%M=+COp(uB?NBB#uc9_&H+ z`SukU=$3H+HWt>Qr&A}JRg9r5oSj{gJeraE?qh7t%mOw)uKzg=-NAdHqRutu@R*Z)l2WUM@6cov&wDU=qbR~1 z%ws|ktlYis8?L9x?Eb8CL(8Un+<51&3Ci>86rkF2+N=&iwdAuYCg+k#85$0F9vA)H z_QPLRp1{Kp=h_Iqjtg9onCA%6XrE|i6Y;AoF8_;bHFO$c`StJ?&(U5hv19}XdeF=9 z=>|bXfI;~P+L2i$qR8erRQ&F^%aYaJbMoy=8LBR8W9JtT`nrsCzHflC3uvD=6hY*@ zYnjP!6vYovK|-`Qyq2LjXkt!jN_iXfL(B4UmpLx7sTk^x*X7kP?h_g_G*IxS@W+uP zue~USZJb=Ra9rmIgl<8hX~YIk6r-XBKqirxT&h4vYq(6K?v-D!VkiRPP(3=23zyen z3(RXu%TOGyj?$j95(^p!;qvjA1hyr7UU@w=xrCn)p^ho8UJr9kG7|-XMQFQpk$D{d z)mJ-@uWfCp1=N@=m2-ugwq)f2;*1x~>a5B@M$zQ&55SY+Tk)=2?ZyuJP?Nrut8G^v zZ7uK#EkG3~b-;FimtY?x7+14#FHv^;>%;t zE2=BHp{=~{+J{A#^fCz?(^nv&p7B3>H4LeL4=ETSYE&lU9+uH~L~u6Bp%eXMk8zNP z@uZ=TsW>++%8E798Rb^^?UleYg7XuHX6&_$&d+u_a)OKF-y>!p6h$uBzdUzLZ8A~W zJC8>h$3XX-fuUWTphpd=$Y-+ z_sX0zG#=%TbcAWO+u)hX?ZWHIDV{Z@FC*3$VVg2&l{rtxp+C~giSR7;N`huqx zj*AL-@?-AEwJ)71R;jV>|9(2X{{7L*!(M40cG7+g?hPr^_pklEOZ}LVu-ke4@sHV; zX&=ZZ{%jrL`ziJQ&IouELDN3`>%{3ax7v%}!e-MXS?9ni5>SWef5b2Hsfd2RFaE>m z*uNzgmD?`95I?t{lJMy$O|qM=#iH@=(T{itub^oP6N%DPhRU$8EP|oYBtl@yD^nP% z#_9Tdn1{0r<0esq?sRi%iV-{A(ofXFIKxII?g%!+0U>JJo#EmjZO_gib&I$fXL_&W z-LaW|5h6a_nE`BJb#^9cE;HCT3%8v~W@d!4v!a^Rk5aQ@nW$)Nc4S1>X=Zu?H9OED zJ876foy+!a%09P8PcvpZBbf9iS_YeG5y8wxTpZlLC&gybmGCLK#_9QU%-7j?;BHPa zTexyBS!yEZGCoI$wy5 z$vqy&st2IeiOOE2V?FUudrnSEyQCEjox{RT@(3q#9eGP|SD~Ian$!S4@z=d z*eR`Zd25Kg4&%Ig>(KD;&^~3`1v|a6)zFlh8u!HeMaX>M_DB`(>|E3-9<9-P+5$H<6o8#QJPGdg9c2n^=`i z`SxuP>o$n}xZ`hZscv(LekzoJFCO2=7EJA-d7Jfl5^!=v7DPovL{-U(*VW!pX^F~$ zU16#GDwEyF?Qnn%_?7sM%V_cdwcpC9eI;7FrM|dQMU%_Nd2wo~rDIKJTuH?mRs}z~ z(8IqTf+TrlJYSrHE7O{cpzzRX~1&`V(2LD!vT5fl$6d;6K+KLcoiq* z#Cqp{lzsq(_EfB(#D7=%X8g{Un*X1M5 zT>f8dy=PQYZ}_!&(hDv0Dumu8^dds&y`v!TgMcVi35XIKCLx4kA#@bPPz1yl5D)<| z6e)@rEPy>I0xD|gMatwqGwXe4X3giUvsQ9io_jxg?`uu;g5TAQjnw1?e>@j<4;6St zy4;=(e}+dY@+z-sTAFkaTP=$soNuf=3NkOucV=G#s&B}hhP~hjCUTUNd}Z;{t=RmOk~zlyv0gn=aZo-@K6dYNXNbgMjl z3UWsjS3M-k=f{@f1fQ@3TUp3^Fc`95pppQe8+JRuhGyv;QZ6Dn?_x2J`n@_lG(MWAA5GGrkIa>%O2N;5Cm5*CDmbu$DG)EeEw2~=?1w@g4m80hFL zC2R)r0ux!oXDe}C`Gy}SHZ;(!EuLJipvW0qFBrpu)qi2wf(RrEqJFd;YD1-BxRcc-m7ZfU%|b<`YY&V?K# z!h(qM#fTjB=Q)^oXe3u)Rt-?X^Di3uDjSH{#oex{gNEXv20V{gW;B+GZoxwg7}#(w z?2;Sw-b!kH)g697czYB4(P4igm%ljz#j4)fEyjbP_bjm$8e#PL5LdDB#3-s#HviyRWyhPd)CIXV#0QR!t9v-x(qWf z0CqC1iKO*t;6anZS+At%p#P>I{4mT{93qI*aB@ZH3l8y%RB-60z$rXBjtJCX5seJg zy#xV4HlWS{NnizY*gzHxtcZu6<_egyfs5tfa-2XT9%ae{-8%r2@I?kppibi3cey2=EFg= z$w-1H+LQ>I(|{%5p)?M<4G$G3Ce|0o{KtVE;=&@Cu!CIKAqFh+2h4*9`vL%8cZmWg zeH*1jHFNBsXUGDlF;HW!oiY~e!-L5&!M6Y;zcNJ*#};P-Yyeq@MQ9KZr*QaB4o~0L znn+wizJo!u+1_A|q5%heiwi%_?T!@YDy+_N6$+eIJMAQpDf{P<~5sQg_ zj!mVY=f1;Z0Xu1edsnzCEufh7uFAOgl%Xebt50|+Ma zXHWbS9q#=&tiTx@$k-R53bz~3lT@dSOk~}XuNrw?VgEwm-hX3IJucK%t~qYdJ_z7z zyLtZ_JK%TFrx=~t7$DCc4dTHfnCKK$HA)fOwH_1sVRA@8~z(&*lnr;{_hDxfQ^XVwDIbU#PJc z?5HpFrnYgK7qa67_}v+(Jx|e(12g2J_$@Gef}3_XI*JF?WkAij%mhGafh!1UhgCXi zDFg5vg1~(Y=os~QI}0KBRXQFEx8@3FK%pMI1Z3@f^T&okwPrPBwDD8O2|k*PwT}#- z#fhL04D{v_h->IyXmzX4EJ_)}GmScc57~oju;Z4=f z-=c6I!T?Pfs?T7WFgum-rXB#!0sqyVz{jqkZV*w0fB?%ELBx)^5s@!ge4m$K7E{#y zaDy(f6U}yx<7oT?3!Y+QbMmJRn1U%7AQk-mh8Zm2xRW*0>?#@U$nHAE{%*j8mApxK z^#D~%n1RUp#|c0ANK#IU`K#FUNnCt3<%+q~!pVM%{uqMLYr+184xuR=LMKP_ISHE6 zWPm6XNMSk97QmY?l+=8Yy*S_&9x2UKNt8e!I2u>6g2+b1g+2enl{IIBDi^^OyBxFx z9;WLHv%<_gxdjR3!IUvT2ODL`kE1(^wwgn6YyP1;#ZVl)izl>z74G4{we-%XPk_9c zF@YR-Hw)3J1-k_o%V9kTE8SutI)H5C+K`i#y zF!s^+x|Q4b)KWuRw^sjQF=+PhZlxi}_7`vha-PeFOrnB(^GMyuWp3X@GW5j?Pc#qa zf`z!YIi%+Q1{YUw1>I(k!$ZmN@DqSJ8O9g7#qkj56d*q1@$3$m#_SB_@_!5Dl=Yew z6ROVs@pcI$v1PE`ES(VrH4CT?<|+Qbh>R0a+hP!_3Q@h?uh0`X1ltW($~A2r+tY+c z+0wvT4A34P5PKhp_c>dm+G%_M7Klf+akcLRtXbpgfMh*Uq%6sQ|25*2t2kX~j}1-6 z@2g`j-)NR-;q=X!E@zky-5ARqysYj}>3G%uyE~v?-a?ZNnDM-3dhB!m)qq*=%Khhe zpmKq8zSTC@t?f3Why692@2#^|?~WE}$lVWCexM&#uIzHfGg537DWVwKrZ5K98x;_7 zD=6};Eg*Cr@cBR7eUThb`ux|Xmb;`Uicxj4? z^E~wt=ym#eFZlh;8UeN4GQu8e5N`16 ztLcxnOy(#IF{UUiTWq>sa>1t@XR(TgdOyQRuzH}9P4;^Assjr11pg@iwVPxeMn&pg z|BZ+zF5BDNy9gW}h&*#k3nJo(vx7-Tv+NK@Qo!WeA}|4^ajm_YFObGYsp;NnkGv$< zxv8V4F`)D7j%vdaH$$z0tXl}Q%~7-b{biMDm@cM@@DUD>cu{y6(BP99@HNH3p6?v< z4Q#N#Al~7pGmESpNff#x&M#BS7n8%x5OXh#%x0;{MIj$naC)=lk7rluV6>~LXffAG zh-5&Dp}GkFe&34}i{+}71SR@mcQb?)*Kc@*Idoo zV0W};cvo+>D(&q<=*4Vq8{tiwsWN& z!4{OPc~TkWjuk{z;Fn1zH|SC6(%Vu58+2bBt@RPC5Si&A(@~S9e5(B|W76rX{pE-3 zYIH5Fcon79EFf^?d{9a1e$nRF4M(+EllFp!wY;yH;@#(+gq5Nvz2d%HK-iF`l%hi7 zzva1fH;@Ydy3y&_{QF=j% z{uy6$^ri>x7aoX?uOX#9qg7s*< zDK1S4G!kH^^kKVzuU5rVy^=kK4WLu1!`>)Iyjff^SBi|IC;*Wc<%SYT?%#%lPPRJ) zN#>olg+^gQt3{Q+I->mBMV9xr=;?-i4}Sfi^3;|&O=*j*%x}4tE5dZQw2BB0GBV|b znPlO1Z0PZ1k6+EJIO8UPgI{{)cpOwKpt-W#ahNT4pj-_aTHtSpx{RInY=Aol$(B zb|gL5!Xw04=#Zlx$Ma+LbG82HXrsv9H+AHJ5*8eZM>-4?a3M-aWz?*_bjPdjnSU0@ zX9ISj3S#MOxrfmL#DT3I;V4!H<51?cpdqTX)zwVB7C%HF1I+L7Z_TO#qVGTi#LKFE z8Z&Q3*c0uQ?w@kiGU-9;uS+Kw!3$iRUn<`@L9MVUg2fnmBaz#7@eH6{jhJ8VM(~Oc zF07MMQ1*I!+ucoX0$1Gj=B7broaYg%*JWmoRp{a&ufmyUxturkwy=2LOsh8q0J*`% zDc--hBe1U57{Z|j-giIH=zX&B&9Y9=V}`dbspn3N)9phwxd+!_m-adM)Phs&ryt^$@DE+^_h<{eWy+4at>HGVn2bjS|jL3V@j7cAW07XjH9Vj(kr|Hew9{0FUfPp|Z}9*&)lg_KXS!Kd0{ z*vF2a?EEm0KK}6Nf4pGwbhm6@Ptr5&$3IBZv`*CohW1{tVQfudb ze@;zwet{*Cp5Fe%c{biPd;Qqqrq1xywxEuoR{b-u@{bGS6Wwc1lhRibBAQkoHtxW9 z7hnnmFdr}ubLLSbD+q9d$sUxj`vHu^{Zyw_!62gU!TZ-Dg*#0f{h#Ir+A7eE&lTU6 z|3f_ZL++52%bMW9>_0NJeIF-AEt*4O-DJ8s^ae0NaIv8DQl#$3&va08{>k}->$_N$ zb}KVM|MdZWwe`Chvxmc%Z#KsV{QK}5oF+8Ay%>|+3IEyK*3{#4;@Vq*tSMG*&odvnKU2%em$F^-Evv_NBedqo>xY?~ikL z=;$d;C)OE0j`K>p_WFhy&$|R69_=M_C~^n`9A^}gwu>A9R`2Zur>WblL@ycd3Gt44 z{?6cpdE;7iJ^V_^{BD8&rU+TJ{a^< z0i5HJ^2qVN?!FSMREy8*Hl)yQwQ!quuov-L#=Y}lS_iZrz4^1VPSsQdewFoIe9saF ziolmuAs;`HIG578R26@;JF5>6mt0LfXRW^+y%tTRn?b+H{|Fv5CpEzhFuU9Gc%;BU z9nj2j>=86x=y_K$YTFCJ{~Vi1`?q!FKmiRj&D=D#fr~xQ6x96z1 zR?gOR>`l1{B%{Jqs>eyK^RI0;ALNupbrSKm*XOc#1@KpWOD<35>|P;dlZI^?F}dhv zOjru?&OcjZSI*kfKJap%t!HH;J0poj54@NokIgu`PIu#hvm^1Rr!p%W*d?yk=rGH> zW5wU&RD76-SQcH=6&%BY1MQhHxf-sNUJ~DO8%wbyB9hv{ojDEWiQvL&driDq>s1h6 zdo`2ENC>@JN(2L95I_r3#-Wt3!9W8T%L5;Ohux{l#UT|ckQz)^2PV^=J@qhu!F)H^ zg5FLwwV=^6`gARz2NO)onh&GNX9ny5!J~8VtKAB6d+q7 z%Y$ps#km%B!_L=&UGb?ySz!#_vJ5_>D>=J$_waDj0U1Q9R;EV<3x3J5k~nycfeX!T z71Yb5yaA_a!Ok!ksr)u=4mfpzMk=Gp2GhffK8Zr5 zgmxO?w%z$L>QQ2rRWO90P#2z?qi-=##-xf|17q=E6qUMb2QCCxRLFp(8^Br(>MtFn zL*s_3C$HR>M=-NZD{<7bEgl(XTHWTm+beQXX+u3~BfW_^rer>>1D?#<7aUrtJ4ugr z&GP4g$qJSX6HkCcErODXuEQy@IiXCoG=^Jw0zHNAXbFa&Da*hzK$==vDL4g9B0SBP z9y&A}$)U=&Q$-XI;q9%RCEo5=bugids zMn6|^N1#r7^gbB*^+EE?ogBCMocxiI<-nIK{U|=v%Pkf@a4ts#<&{9Bon(&i)X%9PGbjh*_NZ-!u{Sa6IM zVk1Kbh^2!}Uu^~YuVwU)q`vad@&&LjvHX(Bw^XdFXO}3oVw%Q>9i>Hmh}ch};uspuGE{#5bNIJFuN4HaPM7>$Y;QV#*l9faz<9OA8^_k6u1l>p z$SfN!*~vBQKsy>{sHKl|TdA#e=YVaoPPG7VW(c3=O23gxJv#SPvy9@p4DF=A0W8#| z0cuNx+Q(*S4*9z&=Rie9D7=MT-{UiI`pa|+AJ33eukP{7Ia`<>+Pj1OCne?+i(7dKfvSY zBEgj}t~h09VY;Y>7%Hy)kuV!K@r?fwe-X00xl>N@$JBl&4e{Xay$ zgKli+?RKz{7A)B{D>d6Ijmv>Q8tWawdlqBd(IF*wGlGgSyX<^QMeQN>p^P*#JTaM0 zBGW^;nfpu?WE-GqNJMySc&t|GTq`{?m`?O{Lgg44Yk~8Pt7r8-Et-B=+8@ybBDW>z zH8y6r@t{8K&~SWKBoFG~$>{$Tp50<|1`AC&4}Nqr^bYj{iI)*BPSqt$j6)lAUBj($ z(%~{$uRH0X>w%RkG(ME-=B0@*zVu|=W1hx}&G2DI`F-9}f)39@0 z_#OeDxmy0{7l@4e41XF4vI%V1QO!DhmK=MDMb7%FHE{_PHBBzwwP`N5`|x zD;m4)6{j7xXHGcJrb@+}^o}t(I-5qHCE3gpOJ?u1lXyF4=g9Ee_=xeJZHtDY6zX$H^_xepB9p0Feby4Exi_0xdYWV&wXQg}}?p{XOvaK3kb++PnM>gltn&+mfo ze#JFLsTy0{*5iN;kh-AG+v0t{cR!dJ-lxtFo3SKR|9n-9A9g-IC8hiA=%1NWe`e`_ z=1TtjIQ8e%y_R1CfA$CbnV$Lcd-cyBc#`+o#@5Y0i;Apg2XS{c6Wd;ShM!1MD#W0Y zlZsC2OFeI_622{StVyQu-W~hc;BR`Z%{n5#9yb}?ZU*ayEt$Lx`T^) z6N~z@i+k4=4G>E>=_NzWB_s2tU1OIelb|Kjm?g8cCG)H$i_#^_`X#G}OZx_wtS6TC z&o0@lE!iTL@zTq7n#=a)%MLEfjzP;#G0V%LmpulTJtvmEW|zI! zmVFQ_zS1jxnk)Y1D*-Mmfk7(=Vpa~OtpsJQ1edNHs$U6txDq$};d>pAu7xewPb4z6FCSid~Ger0Vv53#|J-pJS7C@|kB zblE5h+9;0MC`sEW&Dtm{-MCu6aqZzo`QS#y#K!g6jmou+8;DJ&^k$XjX0`cdH0)=M z<{68?&APn7%hhcUNvKJA@y?gix&(?D(f_{rnLKtzx%$6q^Rq2J z|EeM?A0(Y^O+vNjovn_!Y_s0fp|sig^6X=gGu>-vtDc-S6+XvQ+Uhkwr#c7ebvf4< zbZ#JN>$>gM(Co%={kiK8x1JAfj7*#>pWPa*-*|~Qe^q*WENkPH`T0_p?Kd$SZ)46E zr)^KTY)qD(FRb5wuetGI@ceH6#P%n|#^<&3d59hE?D~{u`epN-Z-eX8LFpG`c4q3= zXS33CN_T!_k=PG+3I=!PBTh*L(~gwU?%66XXj(jtIs3=ul+I(C^iB0epXeo*-6Ik7 z@OCIDb(b2j)s%N3TFLhx0ttW-VtN)tG@yFEMJ92Mr`(?{rs;ntc0py}lAL+U{M>@- z5JSbKKre1l?OBmdkWJz%+ILr)w5U!ra!F&f!YXZX{>PFghiRWBrr+UL=X6crveB`h z%i3=m{OkSi#;<6+$_=>orl@!Nr<0~P(r6AU4uj2Dp?jOV@5} zSGlO)v95B-@R^0`Wz+X0)hm`i+YdWs-j&!B`}#my#Ho*`5ss2a4E;V!Y;M$7Qhral zM4$Wj&Sg4PsH5~o@hV8>3whUoTt6kQ8NWc2^so3qOa1qsdAQ`)Lxzf1VieP6#iIzx zb<)AsQ%++0&{kSinQ~{es&jNYv}z0$CGzteZkdUSeR&KIb;vI%`gjDce8ef`oW7wn za1>LNDHUZ8x^}$DE>cP=z*QM@1+Y-A#2>P5f*m~saI{!G=zV%kPvy=gHbEt0odjtI z28yMw?U?*xyQ{!cyE`Xg3{p1Ie z^7XBP3gcBiOr8J*0Ttg+67-d3GFd_Zf*R+$}7dgsE_w*NT}tNoTu?I*AQdJJwdc72Um=Q zW|y?m?@0+~Y}GP;-gOBMOo%-;o%rwUc|YUFq^~H>m26$i=<^Z3OkdfFkB$Q#{Eywsm@vC zYQEWt@*HtzSndAqQr36;@UPav3?RaQYyc)aoQ40x%%G&Yqzj+JLfv{LDR-1*Z)a0P zuT5uYc-kKuJaOY7GzB@cpJ(?*pjR7Uim;GipaMfdwlF}Vf;bg-#`V`K4SjnSIRQfu zz@PvG^kUWG3z6>;PAY|>&wMm4UhNetA=9*wG#ON)EO0MILRzpNHro+lcV;XPT1}3C zk3@>RZWs^~W=TA6=o5-xw^UMS5_0pbM7kkuO08W_!tJn9ODZ(VM^FOf9c661VaVy! zUwA-!zmaex%m_P(HgEX0$FA9N|DQ44BafbxwweA>r0vp{IX7 zsMW~)XjC$GDcYn)Hpx0%oSE-mYLy{!EY6tFVo#vof0X(dB3VAW9j}}5sieIFVy?UOJ@(DnApt6fDwQ1z zR>3lpkYeywMhiBG1lGLU(KOz3Gr3J{H1=gC+5A7Q)VMG&ijUH zZ{FV2N9DQ)WPAhw1SR$&`oS4{qVqM`02GaHiQ`7d*D-I(O&1jdc!SY?vn=KjB>3vv z{RlzwDamPMn9!dfRlTXsz!tT~Z_z&5kVq<_>IkG#$U>k%VQjs)X?F?4wvt(Q>^Umk zA2znP_uy73Z!|BjImBQ54a!};2n3)~?5!>r%G^3iuN}MvzE;On(`e7Vz3eY#*G=~9 z(&%A(u2US&!GK5Fc4(1Pi?lwyqt(a6;sRu#vJFK_23=gVK`f(VW(7Qtl-dWDU`h)H zR*Rvg1tZ(-Wx#vj7|{+6UC}|Nx@Z>i<1~kM%C-7Q%ZzmvMKGK}l@8;AMKQ_;Y^1W8 zi1TorJ5xS4#q!TxVg6LLPQzX>%8(s3ftSZp5WL4da$?&z=J}q_(#Llyy=pTZ?@v|! zH)Jp6no2jN&7s_gcaPQR|Begjd_R6l)c2W!+yEH+A~5&4m!vy!uNeE7wK}FpyQeSs zQ-h`L=zfR9_BS_ApUuIBywA%P1jqdDKZ+N0u!92_p01-ZdQ*E*-2GGii;5psw{56? z`~PXu#phmn%Ro9?U;iS0?Qo%1K;!=^Qm$}&Q0GOp2B6v?ysAr{ z`Tr;15|HJ42VnHp{wz|pPOdLNrJiPa*leF~n zlYr4|u{hugkd$$^3;Vp+b;3v5o4yS)R8>db*%7`=W%6CLdXM<|7N)~R#s3zmYy=>4 zPMdNX9rcQJ_<5yQXWJwSnz=0|0|D~X+A;)`yt~>La+IOq04S4z#{>9m$^-D6gAaJO zEHS04bL}s;@8vGkj6ub#IF82W1lv2N0`G0ZMawdC*8i<9{rX~yyJ7&K8Td*39-hRQ z)PK>2PX&|&m)RKc%$_eaiG#6Ku_g*Q3{p`h7?P!wqg-saN6-%D+7;IDHCL}qs{}#b zvuu|EiCe+QJV6F91nf;Dpmv&QiM)%BQgPGyW~jOvyE+g94tU3|xD+BY{iqb^d4{-@ zP?H}=2z-}1&Cj>Q)j7uLY5MAdQFBVbqmi$^{BDRtVhUhM5IHPn^~dau=xPvN0pP?U zN}yHwe5SzVUN+1zcIv740biY0>i^TVEX98@%+)jLXR4Tiq$w=W%dD!B0q$^n1Qb8+ zQqg>yz@dIVq+*#SQ(?2;Ta0q?QChB(h+>9j|CuwdEX1UIFt>lV zeSAe9AZJ^p2m&bp9tDJtG9^{O$H4d$)s-^+h(9M2{yskYRe-G65Gkq(sI&uYt>!K; zThXM5=p=vz2)TNYL%Ibg&-UE@%g?u*89P^gCSMX*I$bm=Y58tWw`jkZOOD84eA&Wt z+=+ip2j3|kRKUfxFcLp`PRvTTIG7O!7AiJx!LyuY8W&!=~Ps} z--m9@JVW^Jrn8^7*S_3e+mMVow|8f?x?FhYm(G!NrzT?(pyx3y|P38=p%Ghv}4x1uE1r;fe5bq_T`vX zVuq72##iS&XB|sWVygZ5hU0IB)zIIdduM884G#6mRbqC9Pi`*V5Q?02`FKla|9a0J zaMMxk_Bp|@s4UDm4`+`%O%D1zai+`*EqKtzXeYr%6Fg?G1h7#`*2=QT-*w)ee4Ol# zgU$}8=bMT9>uc`)2&b;+usuB)r)}&4qP|j;0ACbJNVP)u78F=-0)X|yFe`v9mgxSr zQKugnc;})}jhjfN+$ah!M4j6Ffx-<;3luUD22zB}$9zHj3K{BwS@(&XM~e3*_K&J@ z#niouzoO{BVD>P7(-NYFVtQ=6ZSld0fe^`H(%TU6TCu#xa#s-PPE~tPQvypYRFMLc z131}VldoP$G@lr|CZeIsc&zA*Oot^F-Q&~3cT-o@7O+_s)T!GrnyNxzc4J)DzMx)I zYEdl>>)?C(BPH=zYg}^3-diQbSm+eaXmY@m>N}Y&6QHbh&R%1;3Am7*A*jo-Psw>6 ze%3TdC24Ew71=eSXN7}Ud69H}LHA1!e}TS!{(HhLXF{Xp6qlbHfE{XuJiiYL z?Nnu|++jYJ+53I`vxP+#v`G!Tt#x;+GekFdsN4c-fFk`Zmg{~y0iK{?`FHNB` zN2uz?70%n6>w|dVW=59hw2J)3kj>s_<7WmXQvvLrQU1_*2G+nP5vR*mFAJ zw6O@PPf{(=RT1&3Ki2!fwTY1#v(Am(7sM+MDrP-rtv+mwCLA=HZ7H>Rv9a-zEZo9G zc*U;V%sW9*%-)8kzyXsp>C46uKP)smE8_2;D^ql+k^jvwv``lG9a=c^=URaA?p;cQ zcWYzNi=PKOH!qxl1G1Z8b0Hr#$1gay+&H;CYxw&9xdcv2EzIaB- zblUV&^U(!XblA5&B9DfyE-qx|ZB1+KYPt^#RW6ovZhgn)JsOGeUb^;SYsTW`qtUd{ zrOJO>vv_{KCChucT7G-Z)u-cC>F9FZzU?2rc^z-+y;mATw|@q|>==JIx^nOA_Ai1+ z=j5RGYIFJae5_CB`-#!jw$AO}#JtXrv)*eRAGZIHUUq(78(r)ExBZta(#1vitoO?A z@UnfnzDmDbAK16Ez{u;G*7VtU7P_-o_Offn{N={z*_|b($m2N|pUqe0JIf6|kADWe z+#K)RSz+Zpo{#bQ_x{7qYWvH_f6`w5`}}WbjV;p6%ktU!D!;ov-JE>cz54Lw_Mfx6|F|MgcGm}eb{5Nbx8{7FY)-t~S?%22=H)%vn)TV; z{II*T{_@Gr+RNRYfBd(w3>j=eh6a=2Br>v$jBY0jPLhS!$$U1Lm<2^5m?A}@$dpm| z`4)vqisCv2D??SWpsEE^HTe0KGOA8HRd15Icb$rp$uQ#QTY@vpNEsH{X-dvw2YSR# zsRrQ*=B8sAmL!Gc0kP26P#^~5Lc$OZJ00+ojDea-NQ>Db0Xvepu^+7V0FAN~j`Yj) zXW84S+9_9=+FM}akm6w}QoaD}$hXW$QPT_@9E}haTRtilnBrF`rO}qPVRq;qMdtL} zQL;s5d6i476wD6`i`$hl6|qY?cE`3n!NEFZh9 zQ)NWbTvlRY&Qb_jq4^4#jQ#7c+9aM zB}SmqFUqAgCUz@K;doAF`XeRs#-4OzGuW8$H8#wH|LqO2rzsFH@X#g%O1E=TSmnO1aS?j4{(6ksNti zmgvo@_?;ucJ1xFwDe-Yq${h;z!v+C7h!s`?EDAIFq4<)eG1DZERE8`?VPa*8>|}6* zXqB5`71r=NLfH~QaStL^?V*P$jk(jKG3M+DXCB0sVD8F>#x+3e2^a$&OwU&aIt7lF z^>^b!6;q+^lSfc~j$zNL&8}gBIGOr7HCVeE1cgXj!C3r^_%R=4r_&rUP=tiWSg|*HB4>okX2WAZO_h-PCln-K;RDD_100E8$B~N^p z2gusmVv_ZTFsF@60c-&h)KG9utf=EKrbj)@9u^qs=Eb9%ndF^%?|?Q$+-?+04CLeG z+~2%zv}Iohm=M34&?CN>Cul%dhxnL-=tAGmeuN2IR1K*L1Td255LnBB?da4^LU$F;i*#&MeaFvM7$GbS)k-v&>%rd3_joh7JLW{Yps zfA$zi3^#ZEsI!$S#>CBNcr~X}C(Mrwt7?E%Ik57gSpb=}TxaS;1P7&n*VSp(e&ATU zi_Jrh7f7svlT9b+jv>Cf!vjaLeG#YJlL`v$mU;|jiA=Umxi?U3rL8Z$E!~p-q z9zE+YlfeN0dm8QrWI#@U_Wx@dMtFd@MMyF(2&oFj*^eQ~T4KiWYOYk595FOd`!1W9 zi#Nu*HafO{M-*TT00jt+lI5=LnZ6|>bM&SUCA7`e5`xdW8-d+B6iacsS-d9!=zuPiUc_GuslMqgO_T=wfeK;@D(IT9S!dS}^O32YIe>W# zHf9yGtRe4M;}uYR(u+oXWV6F`FWa^;5X5zee)wxVh@=1__PYmb`czn4wTa$b`pdjY z*zKY2`IoQiKqpUlRzp#u_6N|Jq3tW^LI9j?(qMZ`||Au*k z3|7pC{m(QU=>sq#tH>6Kb5mJZx>hwgh-R$f7Ld&!V=>dsE^2mrw2d{J9 zN}jB(k-nm~z=et|ED)bYj`sIGm%+n_`wBydoJ)A0byZ(U50e~@q_9;Fr9|!Ik=K$T zGW|k;XEdq~A2Pyol8=zt_opS-5ENr{CCH|44T$ z__`f(%pL*6Ry`H4Z=0eOYkp#@Ul)GHp$HbI$8m&!GRN7n!%{6mK)A2O7`CCMLQx+6aI_B}q*^MA=EBK+_?7?kq=Ukk#QO^V8DqY85VH-Abqqzg&N9oAO#{psyYJ6SjCI?)9zB?OS+0LVO6ZJH#&F?%F|(1AO}{2spg_?NGXgSl zgp|3@FaD<%q&s0*>Rvf1?_0;&Y32gAuTa{Dg|}4b>{u!Ftt`ty(UEW!xS-=M()KeM zx+I3IuuTfzI4dIeft;=&RnJR4L1TMUvBSJwe+vDvOPtPGn_t%;QTdYHX0D-=?<^krnkhls6z zuZ^H@(9EZcmYkqqY@?U(8Mzz^%<>IU_4>;#>NDe9Vbp99t9uuvT(EqALFYemVjgBQ zm#WLb`cP2_#?UW*zSgK1D{MB6sip|=Bpkde&7GN`Tb9`|8J2?_LL-hR@ya1xg21$^ zwDw0P6o~g)9TApe6nfRL;gmj+r;4`?E*dRxh0HixVm*z6jKV&}87=U@Wr?|{D?+hl zHG(p;J0Kt3XOUz$ZdxlBS-*Y=hrT~;&EE)@v&aU)-bDwfLV7-z|Lr!3kPg83T*?sA z{Nc#+)O{NL+5!5``WyG^`}G2c{@!Imyaq4288$}^LuzmEz3+aj2!>K(Iju?b> zjUyg}n<2?3&z)4vfWtieg>~Sj?NL|Ykn+OpRym&Z*8;QXW}W>hMOVN-jqm??9As`Q zDr`m|MhR!H$sssVjTn`0PM1bvP4Tj(n0-pHpk16WArUO8|LySJ zkp)3_l2D>0X5e3XmEiVUDqMyDpuDjzR&|TzB8QGhwUIMp2pP7{i#3*dc#y@IwtVc6 z9T-V`G-qfa{KS*VN0aaFa|$G%mczpCNAn{;TUCT}p&dv; z9~iPfu93Re!VL?UA=!+fs*Bp9Y|tMo|Dx(5r8KuVd)>Quy|Hy4L@c|mAOO(bM{v*G zY*D61Qbkh_KM$Fy&yyl50IFO7q4(p~&eT}J@`+l}n;w_H=G^p>XYz-xi;mh%viE6r zKvG?^img+2WI7I7JyQ?g7|w_9kaG*JqHKlmVs}4(BlFfz9=uv!aCx0{x4}E^vLGI0 ze)eZTOWj*+!by-{DKuzl>vGUbF84B;>@UO~c39090htul$jjm!-@5;n%hNe1(Sog( zcuSRJ0-1xmaF|yZ{Z3)w5 zLeVj-V|K*n8Q)xeD9$?fin{kbZm=(QobgIJ?H2f1wCbUdPxhbG3%OZNlWX0pclDi0 zS+u+JnOadf!_otbP)&(;wzowN)kmciYCu8@A7qIa;6pFsl~ISwSW+_r-m1uH%=**6 zJ{z5qtvoKuLV$=lcugiK|rK-te0*Lh? zga-O@>BzL4CTOq1PF)s=b6Cj$JmiTn%D=^c>a_D~LBbaV3$PHSvW=1dQ#R3~$553@-*E!{?|JuD+Gg+mK6B#Vhc;7`-4$kHBI%p-T?@GNJrG@* z$Nq$Tc%>WBn`9dp^(hwHZ#dF(T+u>#N+v>3yb%l2z*Qh#j63x2;?Tgn@>|(mAIo5~ z&kGdBhbayg0gM=)a0(0iZt)i_;rR-xYEp$d7~5M6;-I~^rlf%(hpf(B`FHg-ilO?kii@iM;(gQhI3eK4#eiz{@|8 z$uuXKqNVlr{2|{!iZienY_J^~@HNxKt=Q}b*s>}XB(A1NevkAHFGtDo!8cx_iAks4 zdR^C?%)t zN%V4X|DOegtr_X)^-Z%Kbt%ytMW5d8(U<8|5T2yXKW5OyW_R_$m#yF8ihzF^xN_m_ zxy4c?b=SG>=HCkOJOlf&KlP6{zW+s7uUCz(XI|JaKl3ju-G^T>pc;SMJRRan=1rVO ztlOls;iW9{g$Z)u!}H%t&H#YmuR28Csd%j{3b~%5NEf?^7w*QU@XZQ!^msLk)0<|; z0pgLDSj;p*C>f75X2lvJNya3qDJd=Hgdo5WI>X6mUdzytrl}j!cE2S)<5{?Hpq|Jy z?`$eSFiK`3a`65(X_-l-nGUW3A`^)KQ^1;#8MS^gnsnr-1?5UTJ#mumS(^EO(Dvs4 zP`>^D|6%qqi^0&4GP27ak(9CT8j^h(OGw)&WvMjwU1MLej4c#3l_IIePN>nMNUw$n zEgGe$l<&M=*L8i~*ZX$+e80ba|AX^(9_M)+&*S-gJnj!O`aymjGJ;@uoxJ(<%gE?7 zhM?h_BtSE;aA(QTQ=~M5z1Ts_YF`T?U~HAq=yEm`>4P! zkIbI5gmLTe_L8jf_ALFELD|&oAp?|^HjKM)taB}vhdh&WJNxPPlxL}DpSPn@sqh)Y zoF~z!e$<(34QJoC=loF0ei)ULOU>rkT==GP%q%S@n~pk1_2&&UpbB~TNq{XOV`gbkS8kf9t?zywRB;T}G>@5OqQbZdl{}>US}s{NPbHT* zOT{J;keL)z&IS*ushTV2DMp<~12VQo65ZK$VfQX)C82zmDD*oT#(d~ZF7K3eshzE- zn82{VQ{}{WeRRII_)q=9)2@ZVQSdnJh;)zu77A(Ub`8pTZZw!18YxbABqBV> zg@<=pxRZ}PhZmO>qaHI=CTN%>0&M^7VvA?R2czKiFOVC`;iKQrPd1jE>MapyGYn|( zJxbvt-_Pf;lZt-qJU<^`KtP=Oo_WtOvewq((jv4$Dj}PB;rRlpWw8tr0cAJA&oMH; zOQ65&2ejE*7|_DpsmP|LGxuJG^xQEL$aPJSzyN@THpD(k)TK~g%!_jR*K%rT*mF4W z+Thb(4OP6^fBN{LO7T+(=}nbOKPvfBRbO^ht$0>_kEvQKt@?SVYJH;W&yOk~AO#4_ zwq7hmEDKe}67FDOCt3KPEP`~km{B#+t6DO)np9RT-BB$wS-tgVHCei5n^BE|SB+9^ zjY?UKT1SogWR2#}8g1!XU87n(uiBllwM&Oc5lV!V_*%fKjr~!(HMMp(8)im;Y4RW{ zf<7g?PK5=t{!rV4*vHBRpd8UyZx+6p4LQio+a3#7aYhPmh{vfomhj~tG^`H+Uf)=A z#6vJqgs0F&t_feh@em%Ql&2AUrIHRWc+ojGDGsG@XY2&TM8%6$UQY&5!2d(mGjyQ=gWlq zf)*Bj zkBWpuz>n>~Ax|`Q$6j;^g)PM*5T}t(s3K!DEN_D=62-s1odFlLQJ~Hcz=A#JD;9^B zx+p_W^3WkHs5c97k0}yGN3yB7D1q^vj0j=DY}l}qRD#N3a(x@(6&Vj`$&WjV_7k8> z8Aw+)B%Fc$L`SA@G4EN3Tsk@&03!n2nSHf^5p8ycye$SLAOk$}ymICcT%C8dl8lO^ zq3?0=PYAg8WL!8v)@!38*$6`bdmSrmKaYh>-yC<2wPNF@8OUuhw`oo7wy)q-d~^{F z)lb3p)3Et!h;ux2F?8y3oe4z*AY2HDx{Y7b=94Ew+$u+RLHE(2wj8@a`aPZL`)R9+Z|%d`~ZvZmAoZx3% zHPLkT90-h>>c4-;za}J{Y;#^N^x$5^nK@GUsqe#U`b7Pur20j~C_dF*e+hOAOp29(I}{@``~>qzux0ZvWmGO6d=M zG)~5!<04f&(WUc^Uo}XU!n)~ z&N16TST^(+FhZ?#%~($B#6{g@m+MxA{Bk`6Ov01d3^-8L=NWyP{_>h zx`t z$+{5S!E=~Vg2)U0@X{yl3h!;$C}xE!{9OIv75C7(6y#1kc&$corOX_^3Awpx9C}|k zjw;+HIJWVQTkaVSk=Up38h>I1TtB zS5CrxRI86=Zmxt~u4UN7n%x7YY zb7o7h1I3i-Fg)11r*WWr9$|W;KzTu|V(v-BUEJ2PM2wiTf)?r~%KLgVpJ)7!^aPr9_By0mB4ZcVjL zy?KRWe0C7Y_Cw~dpY2{ky`hM_Bw%|OxD`UFndYk@>Z=?E(hLs-&@6m5?)6XHUPrk8 z4ultj1B;kZ9*YdYfK=`AHri;4lZcknvoEg~Rzh)n?Q;!BpVe~;V;n`6$oOIQ5vV8T zt@h(NF6PfXdYp-0-YCaEBs1$Y@t#zeeh4RKDxvXg|L^lIj>FH^hx0+gi&SLn)%o`eaIa5_A9&B(m?Z(6ls4)cTI!Me1EXyV z!#UhH!<6|`TSwhY2BJgW*~FbYm+gOQ{F@8_hXGuj(}C>%lpS`TzRF*nTX`@QbXVi6 zrpH3vyOrbnmfep}b2@))avF}N#_#ciTVAPZc(Epl8_D5e752@x%7ibj565K9V$w2Q z_x&Pj4TX=CK`z6+2m=bQ2aY)mo$Kt6a9Hor8sJn7^gbSe2jA@W(c{L!(MrpUZlAnvAANm4 zd9(YHPsgcGfAw8-|Lk}F-0wed84o_KI~gZyRqnAI*q^<{Bt^#aOYp-&4gdaf&#xhm zD)yX5Z1GwNds6RMWmVzzEquJi_d&`Q@9#$@Z*3fX(_i7edhD-z=Qa>qeb(qRec7_s zl|DaCyc?}BN!{xEGxGiO7XN`t-(RPfUf(;9l=WMW{xUaOWnJa>JNEmh*AG%<{r{Z( z$#}&P(spE_w%g`C(?V^D=Sm@f+@H*AX!ykv7{gXmh$np<&=Rp5v)=4<)l1A76!DU% zb*AdNIpIF`qV~2v5=(SGLtfW`ZvhW#56B?VvYfkc4T8a-l*CAcDWJ-@^v{-gELtmE z+yMbwZN*${=d(~yhBmD;FiXE1^;TG`1&EJqf$MgsDn(&;IO@N^ezgV7?cJE|kiVtd*o(4L4t-&vI?`Pu1~QnEBT7;9Li&hW-mPclDi z6|uwI^!Y6j(KQ*3$5_Np(qG|wY(p;|WT~cB z2CNgEGL??eQ!0-aMbur0f}|LF`3&14MNDF<2c$#fD^H7sZ%Rj2?3EF{YQ=nmRw}o2 ziwKnh#-4BnP51TF(&#)5WQAq8Z_>urYWveL>8J(UhGK8!#h}87ztWY) zUG~{UAQ%UQKS=@kclglK6S21M7A$pF>t4Ampf5c+rMWy?L|;%n$UV5=2_HZHaN?hi_@uFBM`^hWd2gFm^~el zzqNm&R75qo_~635xud+ZCEL8;Aw>pqX<=HeG7qE!t4s z7xGkg>4@SLFGUBJ?ymgbE5s}p^=%uZOWsL8j)`8FI;a-T{MoSHOI`GDc2+uLog?>jlv zQQe|+3T3Q*EwQKR&6kiAWM?5(!+F2Wy=VpZnlud_U`S04+ zmVKN*B(=o0+R6Vtu#R?UD>hkjRNdyhw^fCfD!DiY)ikzO90 zGl&)xpRdhicpaOn+?KQFBQ!{{gJzZYiLOX<=cA&h*q+rt2_pJ?G468@U2h!wrkS{W zh;VN5>F)TKhL7N(aLBIEW{Q5Z^lAQqcj5CRC8yK;4lEz25_YBYla!tBT=r&3OD}Vs zwZB<5i&kd1eO?cy?!~=#9qCq~yUgwaGR>Y}j4k?m$FA<#^}%&GW4vx%ugg4HJnvyf zkxk|^m_}UW{xf#Zh-Zv$IZa^QJIh#Coci?k2NbYKw+_&YV2s3AHR_If1y+IdcFRM~ zUe6&1F755OPw|apc%uJ{c~1Zg@EZtvMgKSEyqW2|Q+7a%t79q(nUE6#`ht zY+<}SV5FvSD8*uXA&u21{=>Y_50F&f*PC_bw97Va9CS!%!9$xt!9qsSdSq^jeZnr3 z3zY|B(3rZA@(n_D7O;)5(X80;N&jT_Tqp3JsOv8K5I_MJ*aOq;k z#pM*2%jvQ{YQF8nd@<~%cNNildtu^A_>}}Nc(xo@+=&pbV(LWJ>x;M6i%r5 z^0tmyRF;HX=2j|wAyd&nA%N+{a8l@PYP@v$GNJ7D`^yx>(ndO5@&Mq&h}bRfxtBX@ ze!jm}(*Llx8PIYGO}Fjz60svYKQ!OuSe;RBPQ3W|R%PL=&huVG!2C-nF3z3PgQU4mCCYxX_n+TX+ z1)AWGUmsJD8Y^6jO-EP8Y?ND8o;VnMT6s#f*7)|!)&1zV=N_Rg-^P8SK7D&ZOV#-P zGPwBY_ldBEmhY2C+CF`M^$+tt^2O2B*U|GWtACwc-T1WnhJn;v<0Wl6wl3N{Lhbt zhHHO*er)^v=NErKYh(R^(+mn^0^Z>7eyzL*lqJA1%z1&dUI`$Nt|7iCr97|{u(w}v zizV#hb54CcD)#dv0;mZw=oZ>uZiGQ2y9m+Fm~Fuj=Lt$3xn;d&TT;SJ2*y7dA73DU zJd;{&dOtTgOdon)Ain2q{E;Ilfe!>7dN1l0t4&5pK{!fmz_gXn4c_At5z`3qBG7CE zn8(+5q)D`sEHy}7sKW#@P*Wj9W+h^+xZcKqWd^@W-Z$5tAkj}t-laDq215P>k# z%uy+)bt}Ulxap_&GpWbzD2!h|*ADt-sTE5`WVDw_>(dmw_75uCQ<9XZ1v6Ejb z*G3xlt`2QHjuU8t0j`mPwBsg7udq;#jb@u3a#M^}stMVR7Rq*G8D1+jq|rtzgNI`| zu`9J?T$4?8TSMNGt+2rDBH|LBRPg6fo%R7$d*AoFOUhP|8gPzC*yQeuON+?eeX92U z@1GV3csmVsik~xn?ysxNYgB?$hyvcSih>;a@kdn$csCBDnWN1(mtCR^o+_#fWxOU0 ziso@g%7nimT^6PlOXLWuYzg;cM;Gz2D4>W9M$g1FQSY3>C(z@O@!z)8BWhqo*PhLG z?cFu~mv`Ga-y-`E;w5f8yxRu>ct}$U?o4bo3#WyJ^_gF^YM zCUe`4ie-Y&UnHF~tRh!~|64e5RdBYLfbRdLut6x=kVry_LP>(NrM!fi$dN%vx=an` zZbL{w6+ISgZYXr6N}fhXsu28k>H#sp4!99w0hLFN6G(!z1IW%qrN}f>g)SK&gKcH* z4|8ORZe239bYd||AR3S~-ZKSf7UG&_GZ14Wp~SXcDg?I=TVzW}K9qPFscMe#d2Q89 z6{7ej$jh&P8_r)G?1~jJl8T0NHpM$t`oxIE=rEMPCD*0|DaKqcA~6j?NNVLgVI?Zr z>Rvw)p5qGE3_Uu&f5_AD!cL4{zI=+2) zdGqj_NB92mn`Cat?z`Xl+^Y@4@}w|M*QFKX@R}nIcqp447mX zFCL}T$V7|7EVn6J5S~GzZ5nzX$nPQGwCXq{_&eE%w#?f(4hk0DnwDY z0E`q}y(T%U)7NnY@<8tosY1fCw;BsJQFD_~&}zWYoT!cv0sar3H9SJE}?ycSLF`q2qJDHX&;YE!|mS}tu^I8EILO2RJh8r_|5&_S``%Mo6 zFd#Fc@g!UUqF>X82|8PCNrL1CFmDY0=opl{4VsP}+MW)>odY4N0C-(*gxIv0$9GpQ>7k z!ZIX;cPy860IZD2mCSZ|yxdy9QLec~WpfsUA{2Avd(})0N~V*EHVP?G+G-#6*lOrN*;%9FecZ^G$4s#tL+{lHdqYTa zPv&2ua%25sN8S{j8h~lcsPoruXY2+?*LaCDS(x5ie8_9Zb;?tb*~cUmSp)5FJzCrD zts*pTm`TK1J**&J^o|HWS$ur9PYTTbeEkb>9&8D5oV{iewf3RPB<#QcTv_O5h>#!% z37}v=2nPEfEmRfI651i~WBhly2>nfVO=DM^CuT~@2`-9qm3FT{v{x?-ou5iJcY;vH za+Sg8l;_1+n+zh*GJk0NGPSPi09feTe_AzAqGU&+UF2-4T%Y}|gaGk{P?w^3w`Q0| z3Eppqapz3SlO48!7U@9U2*O_~lGO^0Y%cX8%&S>vrU`DVSCPa(KGTyv+TZ? zlE13Oo`2;8j01H*U3BZD!#3>_mK3?TBkV0WEAItQBkESZKw}$@M9G?TUKMTiwxe}M zzb!a*`P0pA4D-=#q~?NG>kZ0ubo$R-3TK~4Yk)Gn>!b8@PdjMIb11pbjtQwj(GDki zB&_O?969&(o518GU<@3-8Z(z_VVSHyB&QQS&7!P3v{Q^LS?>lEgM_#>nb1?G2KUMP zK{$$)t}f{mw3AEe8OVf9o<)d{&WubBFCsm=c+DgILW6{>rv+e&A|e+RtLKROkL-d2 zYM820vG>m2xiq+`5ZwE}*Y$#6`3%73|Fe8dmb^)-upr@zN$XA#?8piWp-*}ZE|51H38VA2-lGYI!0mH`xm)wd0)gc0As2SEE?P4oh8{F*8Fy@Kf5@_!!v*=@ z1z+Aa=G~3lEwt98Cq_}slo8npCp_N&Jf-WZ&;pburj;Cri2D%TctO_WQOBi&B~HBd zgNL5})jb|DW*;96e{?VQkjrp-CSKD{rci@X4rK0B=ruihUa1-NNMBw1I;Ti}vwxtG z`}9ofFuVTld*#bjdkf?rOnKh=_R73%lcQH;;Wlcn%f)*8wuhIy_S$a7>|8*F^;ufo z^qX0|cHzPKdc3~>$hp}UkD(d~=Tq))4*Z&&(|D4#qsPT)5J3kW0zMJ&GmFG zhas}Iu$R3QsFNv89IBSYOF)?kB~z%{X*BcoAE8MU@vYySlQBMxa<($DYXLTx^_vWn zZ8;0MCd!qK+idoGOeb%0^Uz6-*E&IbE8(6Lk(|BDPa!bN5HT?(h+HTBmn)MlSQDpz zuSo+S*|1q%0#~MzPwU`??$p0eNt=IC$3**w|FtIn%a!>gs40KQYilGC&rnWPflIH9 zMdVh-$H|CBlAKh4NilFep*K{+wM4a$q@sh!`!cEVJBjZJ%8U|BweEW9^Tu9P_pAK3 z1JhL|vU7Qb!ng4p*Ys4$h>B;SH1>%V)InbqZ;_LNyeEAm=pb&-d6{vic*&)TyCH`o z&4}Ce4sj2zOx=@_$!#^Y#)}BG&m`-Y?hGYqHQr1aDb?`;NfQ;`HMRD|N(a8Q+N{;R zb=)WJu$Q^3x7EFyDR)x|%YZvx@{lsW=Xi0~(Ls+Cca_K|pY3h->VSrtXfe|($TkrlSDM6BD_;hf8l=G9<|ua!$}#29>DzK; z`wSKG6t;M}Q2t9I><_jIMFExn{4R#vG)^?=&@MImI}q;QOJkYg7z|j-jhV!hP5&&7 z0^_(HOtlqArOczph3G6Q_}Y|;DH6lCX9_a{at7~p%VP#JsHF=BpWN26*h-ue zR&9E5Z8}9I2V7c9I+iXb8gGLj-_aFQNZldsqkerka1avFOcM%b5rK=#mAx%@Zdqu! z2YNR>;mbbQxw6nQ6jr5!N@H zU<5s>}-n#iRc2^REO5lovnL%yLMyNzGgFUj?+CjvYp4%ZIE5t#*$qrv-?uLxI2(Y z8NhckUUmpf7JXMra!VHRZI`t8T$k@8zL)uedVFulX}J*4Or~anEa%$YN8&fl83Ci>3sW6btk3CFq*<|I++>BMqHxOW5P>tD(mM1x*xuBpq_h#qMxIr%#5ABHkq$fuW`lXLD z1tpYMulm=K?K^}&Sx%odw<5jrjD$} zfBuH(emjyp7-UNQ6OZ^m83aw<_LHQO%=IzMj%e}iA%>}PZE(7qjSd7k z(HZ|5$as#FE)tamU(e=+3t^jpU&oyfw=${=AF*S~L=~9Od+_@CgLhyo_fpL&P!8qQ z*nNDf_F+X+ydHDoSR_yve+f_Jj9BlKf3tMmu5bd-?rfBdC6{v4pTcG+0q556z!v7` zc4Ag|^%v0u_hUu|RA`O^Y6^zpgXDr%mUcbJmmR;u}!2mSHROlaukzyk$Ia2Gs(#O^Uu9 zm_HEgo4Vg3SD?hQk;|dnE8ii_1PkOD$~m&gB>bX zrqtPPf<_js$kuI&CYoLq(h3zZcimVyfL7q4U98>MyhMo-2!P=gwvj%vTjGcxEUp!j znJtp0icDKA!R8|#v;rvaQ zU6BYN;0%jOon&6`mk7-g?Qj3GbTJMR@WSE5|% zp!=(`Jf@Vw6#X&3nqK~rWbtaw`t9mwy{aUF8toVuw7>IUtc;(e;!rK@KG-eU!B5uCs>X)e z^hi(gQw&F{@lk_4f7{0?*cyUOs!i`U>E$#VhZ?cs!Cob!<#cLR4Y9$dPt9vN!(*gI zvTd+WGj=(XhOH$H*!1g`Eoc4ow$N6KPOEk1iVj^Q%V?1X65#)JvDgPhgrtCgS@2IE znrL|N?N^14_*3g`xl396vn!2C={;L}{9jWszB)_+4Yy{DjMCXu^h%#Y|mTc$1i8}sv2B1M4w0;$*wRs)?5Ch_mG*~ z4%QUvX0T%s+^BWUa7SNefudPOuy1@&`UCi%@$z&X%M4-;^v5)=v@e5bG^(T|nil#O zCHSkIrOwc(j-?i~P-U_EsF###!$L(3l>CGvr-c-TP_ir}6-Pkia`%xf3>7=2Km^|w z12myg#X@ZPwq7IPv}F& zz6sM~;E0G%_qlro>S!qBe=cJ70+z=Xu<1XWu^~IXO9^1b{*_gW$tK0yT$d|I-d{#C zjb`$Qso{V&z500ABo3*#)j~yQx<_Mut&czOc%Wdro30wMJzvhyF}$GMlchM4UGA8t z5HSOjW2j1P9*1(1?Ni~p{Zs8?N{8~cJ0ZN{NiA4_#~6~9Ar`4Q5;m7S##H+3h-p5O zuAYRTjAF%*$kwKdUlHO8#Kp$xfq4JTprLH^WU6{55%}@QwJIrZAm=fGkD!D@5)bzK zd3apEeRZ3pnk7p3p&63W2KUn({c!O6Q_7|TFlk+RG5ZoDh-G=jX-(k>!H(qWV3uq+ zyWJf)h^uU2NGaOUI}fWT^kTMxs^+YK_8j}QyI3uuL9@AIN1ut*22YI9^Gf3Fu@IoH zWn68s1D7nvHQ=LLAI4x>X3WK48rPG|oqbSaIG}#LLd*#D3@!uN>2%YRqxdj#)a9|(%Yf4)~AvALmqIfxL8P-4o%%cK9P+5;^T5TZ-e z!D=}mv%5s*y3LK?K4sfVT6&|!Q0^9MLRtB)r#x{j@7sWyKKC%UWWU%>A?<=o&cMI7 zQHYgZMmgILNV={TsTbrN%V3ILu3;}~A-4)VggIOHo`rlscbeCaK5jOjPS8iEW^&>r z?$C1s1!2GPo_E3}glQk9viB)lJ#D3#W|8(^B?+lV+r4;j67JQ6Iu&0gVWihK7H=W^ zhGTGBYy``J&+iU@xBPY<%SqT7+UcQlGG!!tyWb_7fE!8rjGYo6n&Q7+cw_tu1KQvA zVP6}C^x0#=Lu4qmvr&?I8%T~;Y{khbaCXM>LUGQ_o60B=9L2x)baLot1ehL`1V!nX6 zHBia%NlFT^k8kR5LpI9Z2-Js4fP`!-?cvxT}$gF*mr z1mZl><+uy7XCnrWw-3JVWx4&CP8zvWIxkqOx7lo zBzx%f5d%=Yf?<8fplMsMWCy6WmJVMMwvVia$_RE7YEqwKfrXgU$7d~fj0oG0mCjr> z4>5Xum1ikgP!@SKz)E4PL~@$@cI4jl_knnm4d&vuK8cI3JR~VxqN})7c?V(->26S1 zZ||3{k0IL|%dbnmXomDc>?5ZlY!ju%O7!`zK012Y&JK;cudF$|(%yURBeqnal3^UW zR9M&dEW2gqMBjRW?y*u2Ylo007?1tSm+d{|k|pjIKJz98>D_khtNjuLuNHl(p*OYB zEGAOA<>v;W|G0bb;ath{o?QuG^Xl9?E>FmaAZ=Mb`Y8Q}J78_=CuzCO{Yi?0=RCa79 zx{rQYdCnv*_Q{O8cA7Fbx=4gwOc;hK$t{-*;u89ezgpstb$W25ZZBsacL<5*Lpkqk zbd&ojJ&}#}WvFRM#3o!#G)d11eT!2i+M#{uLzn}$o1F9A6BexWV;r(KtYg#6C)qCg z7;arndvTgGt675nKf~ir<#3TfOfB zM#QrPuocPW9QvTv4jt`;-;#9H?>o%E^|UTX`+hO4?_COx{CMfl_1DeHuHeQ6Sc~mu z52E*;b&}Eq#oO77K{#b83hX!4?N!TEg`JP+aK`Hu? zF7%4vP_41=kD_ApD^Tzpv%?C^WW-X{pCgHzx+%_QTyC&FtCIzD6Xz_+ceN=^0(!ab z6VTXLIo_9$(2in6^osxA3m)dZ*8EwRy_>)uX9fi(1DW> z??30>zq=*>&S)oWL7;V^lvHsufSMS*4hI)x_u!>;>t_pc#bX+M(%FfY#an!={$^PH zC!<~2yii&D#Oy*aStQ>0s*Dh=zNs(|Rbf-1Ced^tUNh(C)V~>4f-RR}jRxWtDuRh^ zd>0b%Zo_%EmpHe{s18BLNyF!MAQZFJplGOu-Twbm9g=C^2oyNnQh6jbR{F1}SypzbFKlINhXK>M^kY<&O~qi?2jE*b!ITl~g-M-E`jAg~^!(Z6R3 zyG6bGD8VO|o?kf$Rn}(i1~^DDW+ReJKeIF^%4m3?L0FAl<_cyCS`StZfqZ#r8bi+P z!wP~%0dx{6v@4`YD75^I?DOW)2Xj0oC6_D*zMm%e(Aq# zDYS@|h+>Rsnup5UEWJsE3QtINq7b|#SDp%!qzOF7@JRr6-hQde4OxdX1QnYF4SE<} zilpoWgEJYF7ObmtHcgPxZV?*u)xoHz^eYzZVhp@Fq5q6il?E&Au{}saflA7ml!3|! zsL-T8ZW2(;@z?^zcQsiqHt#%vOrpQ(d6Ys;esaGK^ETOCrTKka+eAdm4K!odZI-~F&ru*Mo8FqG#6vVW??2v5uD4)b zfQgRH#}z_YRTQ;?_hpm{>F}pHBn9D5cNSs`#p*0b7SOY6i)Q$WSq^mi65=^U*f=5# z96tk%336%&dKZ_vP;V`iL~b6lva5S0?Ro?jCeyp!f`afAs!~zA#eCoYgsm@khhyOT!g2z?tW6c*nQpLdq1pqyHA zC(ucTAx#X%OD>45jQebAwcPpAf+a-fHyKc`Lk?w3^g*kyE5?aZ=@Ta+UJg$()Ye0K zDoH*YufDx8Ij(c#sF=^Nta;pB^!|wKWr;@Ko2ZKwoX)Qw6L(pdesEOstKPMy zEcE1=e4Td^!Hr#h5nj!K>VmxQQd9?hZVn|dr3$Dw2uac>Qo(5^L_m*$q4QP~^h)tz za}09b*!P;cdwXLZpW$DrF!gvVtM+@r8av60+i6uqn50|D*KO%!xNcic3k`a;KO4T0 zw4)c<(x5*)K`KyZ^=>xGHKQ6Pxa!WpGz_POFXalm!gS%9$8yR04ZCfCcHXKl9N5R3 ziRF>^0EKE5{j)VP)o+us02viEA`i+L1Ny?3R4hwt$rqt;73v&w|HNDJCMi960p`J$ zp`d`9v9|Jh5;XcVUP*r_NtP6kX2Iq}G;JRy-r>M&))j!+keyP&mqK<{y?Utau-D|5 zqV|z`&8VSazn@=4~>KveJ%a}XI>rt6`e^sAR{wP-L=-Qg~XA?;swOMhpl zIW`Aw8h+Vt^!++ByE$0de&V6m_Zuan%^?QE6OUrQ-(=xh!mRBlpOk%XZFFo2cORY{ z@A!U;o!xRI)c)1v#^mj*yI%?cU2TF4pdkZ0HO%QuL1KyOg|W-9lWL>e}a3tNmF>qK;= z>OABmORcf)gRt&%m@jX(Ptk7W^Sd{{tiEuTP$#>_JudZ(oJjHs5T%Q76ML;2VdN9gx5eK)e znr}!qM<8skN4MCCcR+$k5B2y+7A`|@>fuk4MK81;mIaJO zfm+`IaG-ZIB_B@4_=Bh)PpjN|wg8Y5M%pF-*7iucQQ%~h8QFQTyZ*6gvZu)Go!5X- z8h<(--KYVy2!mVqGMAk1eHC^KgGlh$U<(orFaBKdS%G6F{e)^JYo~)WZtW8GYc%H;v*`k}6~=C%d)JiC zvWlxQrp<|juH;w+G>&2x`!lwxAVmr7-ej|6qW*&%SnM^1tUxwaCIm3%^mtTUNv|rO zfY^df*jhE%-JO{erHHDThBP7iiU|TKvP?jD!26Y8LD(HuL4rj&U~VYKbUpG^*h zLXs0d66#0ZE=W?o&x_Y8iJ#oZqeyn};HpPFgx#148^ky^R4Z5rFVh%JWKan^ObmRz z$itXcYJ!>7K1EA3R3wGpm81@TkL-6GoQLk^oPD$^!fitc!YBQ;k^9&J{WsKz2Dhgn zFg*C}dAJ*ZIe<8O%9%E#o-rS$NQXdjVNb>PC;*$&bxwy{t(*w9x-`b4hLDR$AV|5~ z2Ozd|s1Y0D$3}P(5cUk14uHw9Aua?vSyIB*M2Iz=C|+%C#dLXkQ%tGENo&o@mJHp& z3PFY?>LGy)3%ZXdM2dov1|gn&=uvH?5gQ@HOfqCA*)s!XZtELsi;7R%QE6~k1W`>C zl4s!LZ3v|n!(`~taHW$wHrl`n{KUxDX1W z%OlHub%(zMI)|JskPHA8Jcxh@&_+P6Bi^kfY^S8RMrZ1_#gwe3eIOH-IcdaPm=^)z z$AkB(WQA~q1m9>V9UjWeR&3MH2f+V1Dw}`7ivn9+gDYAi?pr(gmcTGv#554$l2h6T zg*wt0BS?T+6PfXV2`o~MD403{Du}7x&pfLV1K;}9HIu&|B8tvs9`I0vXmTO?o|Ly6 zvo-<~8HNBWDUMm9C9b8!7}ykSXXTbPz-MTRRfYipvxI7OtMi+nef z?qOSG#R@q`&lwCUAGnjdGE^FV8Y3|2Z092U__z-oOeYn;NWlN}^h?u$tHo&RHsyHe zqpi8NHaw_xdg4NGW<8xTP+I*(wF>c>3OLX5x}h|*@2GZq5P=u zi-WMqQYQoQX$%khXtj3vP$@D39UTK5<>L@Lu}j*Bu^8wAy$~G$yD^~>N2rA@m*95F&y;{)&p1a0~yY_-2-1q~WI1W>!0aUNb_4DpJgG`d3%vWy?+ zyL?lml{|DWpuwKW?o8pB?Jbo%&HmSKd2PN`C3n&DaAp_n=FgvpKC?osv=N~Ms23LodkK@F zl=*lQyaaDH!CAAu4KkMZW&$=QRZqJ@P^S^L0-(!)eKa6g@(HlDcIM_gdAsji_#koM zOksgN!#B6J{M8*h`;On<({(wmXMfHA*XnkfL^! z=rgl0eb}97BJ4~%;RwvO)Vs4UGtYb3f5<(rG_!M0fS8l$y(fpo&|}4J1Zb-vn)0(W zY)IrS397TvZ|M{uroEeh_-=XC1uZ;{+IsJor!MQOa}(G?g5)7Xui@CfpvAcC0Ll^ulD zHJy!iR6di2F&6Jz%bASMS~cvRR3WTfA!zyZ>^$3JnAPK5oh`fGHCfUl-`TVJvfIL^ zS4aJ(!`O|tVOO?b@l^>Ck$qi!h-sAxRE?qmQ_N5PD@}uW}t7>)tNuhdR^nZF1OjfZ0T>L*hiGL|LD8wlB z@PDalFnjK32Ve7lkYeS*_?xS3pIqn@))AUlO;Xi5`F=_&Lbz*JF4<+;wpC?c)!z9L zPmZhjtz@ZeyNbyZ86rv!r)bW#2+F-AI-s-cZZ})X8b~g9%wO==*Dw2mgN0_13#&uE zzIQa7v+~khUq}1U8n@h!^M@bnf-shQR3H%_pPvz;NGG&Vg~-bgh8DP^u(LE!|C4YW zSpV%!A8=_6R2+xO}AV5v_b(2VCpv{y)sXan-Pa8W&^aNx+O^4nYpG8TH{?aSy zN;G8(I=e~un5q#dCH8&pw|J=>-_wZ+1Aq@ukS%1YZ`kN%1mTtDOu7D-eo) z6?ZvFHpfSoNrspzad&~p>0|>jx3D4!s8ky@RTzDpnei)v^VlR&h(4qW3aZTsCs#xZ zBq5_GhhhLLXf{|8P~mdjC|66`_Pu*R6;y#$)}YeF}0+2~WU; zJK1YXCkKZX&A~(z!y+t?0%?aDQA@xf4Fu(kkgKYBCqE3y{R1j zb5rfSAZjpK#LGj@QsOvXA-Q+m%BYWkZ{ZM4ks`fsOd5amyfIRExeWr#aB1bGooGJy@U<+?f&iCmT>CIq2+9SNFT$()CRTxZDEmjS>3~F`pGZguRV|7J9CEEE)Q{>A&3K+2n)0H zebdVt3Y$LdQ*DA<%@t_gZ(q`P-I?F;*Z)J6B^cMOPhkS-`-=tvD! zI-w&?u%d#7(1H{LBBBD)R8){6APQ>e2#EA1y$J{!PFi^ME$yIt)^QyiQkFPb{iYH>Rm$O>Ohwsq5khu2yj z0!h>{*IWKPrxLU=?ZP}|EI?F}9NlC24F)ca`60l4O~ew3wkA@42@NI7P@bf!_;us3 zi(FI=_LH9<^K%q5a+`mLq=YA3v1#_@5`T*O1Z&W2+)HD<3$9A5^ZOTuo7Xd|JbhK6)OFq z2+Tg2R0r&6+_J3%oS`i;PS^kD2i;>LgjJqf-i$4kEakT0{k{a3QfWVI=1SDiTS(CI ze0)>Mu)vtd9Q9pa`kGn!xT@4p?s?U~YhpA=sQ5y6dqyC#blGqB>h3jWG~v#n2|iu@ zY@%X8G~vpA8n=Vr-rE$D2UsPcUg_Fr4=h|V_eJzA@pT`^pZIpVkbB#B?$d?NOXte4 zF4cTjJUa$6&z-q@$kkZr39&g|kFSJdaL~S$n0(_{08-jCo4@-*k|H0^J>@~F?FVP> z^=bl&g|rb(u^cI`oktl6Wo&n*IMO^zcxH}yISCFG-M(m8ILtS5^>^#&lZ!Xbl*z9X zE24((^B`~}xv;Ai!;bWlmB&#phYJv`E>Abgt{0Tiu2#$cJCm^e_4T&<)+33RTf88i zHIv{~q_^%{#Yxn~ z+y?glu2*TWGBcbI>z4RG^(v})vY77wBQv8BI*r+;r6-B~Pq=UYs|&_~Z*p=}{d-vB z>Y(-`=Rv@&ouT{h?h+GlXI)Xmcf}Uh6}%qR`sxEuz<#W??$PLD%?xX`p`dIYu*FnM zPp+?+d@5BBV=Q4)B;)-$INQx#GPvqk%7wsg-ggH;#l*LdADEr9ZM0r(wV?pZ0;`Lf z&Z2#I+r{3zo3m$QPvFI^zU|nnl%p6p;Gq84TYs4LQTIkO)6w60E(CnnJOL{T>c~~7 zNMMtF{=Iui<+s4}EhYCS3|SA`DskJF7dvD+n$G=rMuuuGc7pOfIJrBUs00d&1oHy1 zhGK{tCN;J`^sdTZWsLQ>~^ zcz+11b;(krKFq;F%c0(wENd7LI7G_2P4rSyo73foF*9w2S7RoU|A`aAf|>0$PPGxr zaIfgK7N|?iDIgxL|5#4)09XS>CITPYOP6vlYWVh0U#t6Zo!Q%#sH|bvHY3L?y|N zdsc+#jT#GI!8JY`z}+V3Q=yi?Y46$+&*OOpJP=UFYbFU216mUKtt#dvcr8@pH(waY zf1L8mu<ZriuK(U@fde#5;=K!KG7Oq|$^RURLqXaFeX! zhh1Ah*iEQ$ALMh?Sx79Rs@EXiSnG*x_sH9%&+r|OiB=WW0Xt+lz}twaq$3jn6U*vY2LHZ{AY3ZJdKC|h%80z45jGv9(dWF=Bu(|f7Of3V= ztX=-Jfzxl)SJE~5_*~~mXwBhqJNnXilZLJO3`PS4h;EbjCvJwekFFP$_%ER zpKistH1=kcOVgVs-km?&G<)PW)TCX}CeqhBA=CH*?z<_ zzMO6B;8~kS34Iv|IZk9LpN3$M_g>i>?aw*L9us4(O6Z3D>|o&D(9GE)LXN&z$8+^) zw5qPDpQ|0XTAdCcgk{g7U-<7eJwEbV5i= zh|GS$fBCa_tCBdUF`EI#_;3;T!9pTjJRK`7u^63(@oo`Wk13IzUcfrrR^^7RA@zr9 z*u7&D@@_7tvKQE2b%>Ef*}X93)lo%-%GkoMrt?o%a1^ulj-n(r)rEnl^~)U30Do>h9gw-W)H7ozGU%^`a@!-kyB>j0=rqg@Vkqh&QCg(g7o2G zq^_$uMH?MCf16tk@i&kOc`bxAF^q%VLzgm1o64v_8{Yov8fN-w={Bd@w`T)vkx?%@H0S#x72#@;n&?)KC0yb&GYccG z3wID}?l)hIzI<}-S=dbc$f<|67F51YWRgP8Gn^gqGp~_M?L8O++kTo#bU-2HVg!$D zIY+L4ZSwr9a%lg|7XCcC`9&)CcLpZ;(`nwmdDK2v#?ypIo@@$2n%U6GE6|-N8$QDk z{H%3qr8>d9!Yjk`&{0-QdeJ4n<5%_fj{mR&`-TgLzIW>M*Q953JBt5eDHr~3Q*6ZM z;}Ji)Eo^JES?oZWBf{M;mDMOsy$UG?KYCpIYjdCVtq9G{mpOEHpB+W@3r)}eFLvPn zTe%R^uTcO+HLLoVG=-)?cfVP$8HxTJpxM@y@MhE?1%JGo(l^=1S%b0kcrldsip36m zyKZyo#cH9A346=aBg}rn(1n?zivJ_tue2!>qxsxQ%~64vs>c~ z&W7p~n@tu2d!pO6q4wt6aR1X<>?v!fD-X1Kv=>aGUwo=>xbt@F|5h#(DA$La797%6 z$uoXh8R32H*9m|#?5%UAa%;f!w`hp&$sfJf4Ziq~i-jE8a~rxo`{hx> z?+-4WjjvLk1eJ1KVh<}f1b70a7EOWGg!22e6^)k8Y=(HR-4MPQlx&nrMEEda!rrPW zwZFgCq)><+}Ft(T@m?BV-gNP|O;LV;s z4jU!Bi@j)7xWifh+eG#5s%dy-SpGg^Q6nhzQwsGLhS0jl);ECMzLLvcb^g+HJ(H>x zyYNXXL10Y8F2Q=Bm4g5bao$;I#PmmO5c9l=DnLK@w=QXA9inUU(V1``A1F9wozMl*uFi@xF+5U9J z>+g@XBO)|G%s@#j?t8*wC*^=_+(?kuzMQ3oqh&!5TR^8dac{mR3CkgVY+ zQHa(plXzp?gKN!?yFAfX6+L|u1>3_zWsEppfT9)QPEA~y`G>lnl7b=t8yy&o9)FFJ z1Y=-Mt|hxRLT7Yv#RP1i9Ckk=< zo;e?nrzcbJxO55#%!|j9VKc|!nxy2)JeU@MmC}=x%j5A(nB6?8k_O-e^o%*gN53c* zbMV>s%*JjF|0s+)9v*Z*ZBR?qly%q2#`A_~@Y9l3gus=7I7&4{!Ds9zr(ys$n~<@e zlrfIMUZVg$0OK3T5G_h5jY&R8MBL9yqERq5u5c}I?lO(SY|J*0z4kTaT2kJ%=I(1D zxrwha^z6NXtF9rLFghCc5LZ`(J3)fC(v!Hba0V&e5d#L-uIRe5xix0k=u%3%%c&oj2t?^*uKe_Ii3=@hWcxcC@sHw>Dajq3@Uhe5XX+ZVk zWFa$}rhg+yiG0R|5FY#x_nd`SU?NWw*zQwMT1+UFf~zXRl@l>qM7``HToMs3F;HAL zPaCA-TA64*){cmDr5Iq~7*swB<43{3<}Y8U#y(==a>y_{2I@WoqX}S1WH#5s=+_kN z-(b|!^Cj5sQ@%y$Bh@h0ddIH_&H@MClPWdK2@=~QpZPNB4B-Kxy37s_c#1L)&O;$l zAn<$^dg3O52p41`XXIIiP!x;^M>7%bI9Px=!iNF0=5NZgxZdTN+!P>#eOz_;p^Q9; zusp{af0?vBpnuSi!lQ(XO2jDelt*w+@@=CBw}TsC-m+&&S^fjh^0`ZoMK$Z2{4&zb_%IRXuyjpo#E7yk@^K2aH4U{kbgzX!s zYJYYsPUmvP?QHm3N)X9?>si&np4!t-8%PpfSxg6gc>)!Nul^WHF*#C=x>Vuy4Vl1z zN=X=@HspCczjf*P?T;5zj1UTIXmLEcfe67R*#OBH#tpe%f;SIvP@|kujMDbK z*N8ILiZXwGRx_t#fG&K&UaPiirG@lNxTff;f;_-5f zh19(Ba;oX&Skv>__s{e9L|@H?JwLRhQ9BeqZ9pvETYh9ml3m0?qMh)n~>Vd_hdmk8r$_mZui#qh(7l$^GITD&OxL zaHVMErMeaY7vCtP)e$J4#p`1L_#g}hVNS5}>NJhbYRl5NRX7oTCRO2o(!H=6)1lJ2Hev=)e+D?; z4)9bCxVsJhx-ckEc|$N`P^@53{Li3-_z*#VNXlzSI&Mg&U`Wnph@S&yLcd}{LGdzR z$$Q*vkMPjPH}Dr{xs{5v)#=bhI^2W~Cj*ogX~eu>#H?V%`sIjii>CDguXR%fkab0$i_}`pyytbRykM#3Ok$~hYf{sDNv(f!vS6xQc?mzeWbk*1_-P*gcS+QH`Mvlu*OTe?0>DX{ z(4wI36R{HW5Rri(F$@1}!m}&UpCjgq`pxP>qFDA*aZdb>$#!8MqP)K)Cm5h5TyrZUo>YPjZtWks^q* z2-3Q|e)<#Wwwd#voxxU5bK*COKTT)aZoKo_=*`&REZQia1t{jmeTk0`FK^VTe0=Qv z@#&}O;z+Ru!V&kvV^nIqL>if-=`NKdle-Pg-$qy*Ai*R`~H};pbmZ zHeSz8|Bzt5oBiC_{~7gpZBK>ykHkht{%fv+$*#^=6E>^u`~Zcqhfe*!#F zpLqsmZb@vu$(T0xflwsq+3Z)F*VDEKH;fNLrwcdkWqz&d|9bn=S4SVHduy#F^J~D} zueT#t&3snf_tv!je#H1dU7x;I{oQ!X`SH2RZm`dEu+MJ59N=WG8_i9Bo89IhO>D;o zN!jh7vV7rLz9hV3XLGrMDtxhjC!BNjaUpDH0Lwwb>=4-G@lf6XPyX!ap=(3}Hm>hn5%F+(||#bHv`j7ei3K)LVB|QA0`_j#J@x`uuM3AdH6{FEmd? z#`7UW%IN5BV1`C&3Q?wbAs7rHaHJ^daVLnFM9D_c2&T8=at4HS(p}qZ(2@H`$|X+` zCp(eZ{WkmUvXZKV_S70w@b>Q#ImH5|nUc#z-20Jt#7@GHR}EKD+zMa0+qhh4Jj&jm z-N)l~9kps(te)Bz>XM#wFL!1R1}cdPDNWd95si>_zE)Q06h~r0hgk#;V+HOPTJ{z{ z%B5}ypAIGTskDDmXQ;3tMu6fYZ(b`n8m}rz<#U=~Hlx7u=f_PL5~NBD@2z}<1Df-4 za2=-}`JP&Nb_lDa>I?e$42=PL@#3TSA+Zty4Cho%k;@Vz6an6~BV4McoNbk0TFR&` zf%7oMRtL}&GYQMt9#5XH7J8Hyr${-hAI^E9PDDm{)*NdIUam2|V~Z-ny!;q>-8@&Y zV2H5Car&ISB(1!hjZ&7&bzCjtJRmiZkQA7@F?Q+?b)yqb$bH?s3jM~7aoRa3iX?;iFLc_(gnV1D^CNZ;?u0#_XnCTq zg;aKMY_KrTM4JImi_WQAY*#XlSTE!4;kALi7Z4TzmQ5A90F2Kq8NwaKR24X!8T=SF zj>v`_O3GO>(tz30E@zfM=d2c^<_d8MWZgo=DCx^yDAYSw951$6tSR3uCcBm`n$Erv z*ipnXaxbW)w9AC?ZA{Ai4Tcjnd$r_&4We%$4Ye8&)qjYTO~`2Z5*6i3a`s+!q9@8Jxlky-tV|x zsY?!up7Pw3$UUbvZIHPj7t=Q^nQEN?=NpmZ2`xoss10h=aTojj#a-GVnuL=Uw{S}lsFZwFK(Y|242(Kc~ax3UUfvKGBKEg)! zLB{%&hLbNRuHCHmKE}^MQW)x=fN}7o@*R*aYB-7*l)u;8xo-o><>OQY>3jAB?bcv9 z)=X-zmNV=kc>AMQZT%d>AA5{1!}m`wibmGj=C9^ZFc#-ZL`Zb;@I_Ea2uxi+aqNi_ z{kB*L1ty}}r4df|g_bEAueu=)g=$&I+N$~6&v2OYAY&l20eXN4cNR;#cei0zQK2YF z*BUR$Ue1V^ilW?iAb|QUc7-WbG_7v0#8~nc-2B!CLCUFVY9(~Pv{rX@S+2Ta^ob=+ z+b8zuDwBre>~gR!gwoZxn!4jU9TDOe2$lthgajs&rA{Q3u;?UnTJixxb5RJVN4&CP zh0+eKl^2(V6Rk z)HOE?A~C7$0Zz>0L`B4k$$9P*W*7aa6}~WhnVgx!sa2L65T_TIAR|NIfcOFw?Fv@QW@dG5G*aLV030j@CH zi~2`Q6a<_nA9MKmr-dhqL`boY1AXyV!<(g)R*O1P#0c)XIrr_x?<6Ue?7l@f$sA;7 z6<$pOIi33+z$it|$$KA$yp8Wcbk7VAv4MzJJwHqk^VrI<5~^)VujFvxG-$udb}2|u zSz+y5?S5w6_8Gr`BMMF;Vh6J0f|VUE2=%_+>NAy?g(znH!xMTbLV@)z`@7IU2}`SQGbusN5sQg(VWiqg$0y$9eV zhIrB1B#Yzkd`}1|L3F=8aZT_jwX|)qdH%zLV`4+^5*QBF)%oShwJDZK)g?J1sb%qm zlfALC{tz5#L4@r@mr^(J`|OT>5g=M6MwXnXjGv#s#HnYfiK0Z+T|WxX#F)j{ut{G7 z<(e`SmgBFtmy=$b7PPo8KdSx_jqeBs$9v*FCV-9i$3AEPgFBPfOr&t!YDVPgePyOS zA**KdU{{~At%k9EfO1B=)2e8c(GVWq=MA%`@iTWcxkmK=Mga~n9`Px|Dc>yfVIHyD zhJ@zFFFllN=?Bw(Jv;L8Nd3^$(^p?FkmF?HW#D|JMl2Z`?5fPKDL(4AuF3%-R zY?DfZ>$RYDiF<4`4%70hg+yY06pf{1nNiHBib^~7z9l6FL-gJMm55ERPWvtN>PH?| zyAF}dv*lQC8bo72G)k^qtF&}l$L*(u-3*x%JjXB4z78Id33&bEk%m=xdJ|7aD^BOs zkT~N`t6D0Q)ELjnF#0lN)D{YOiKz=sFF=J3rj1iB9~w@sa2SjGMRq~*o)26v&0k4 zFIacj*L7WaY(h}#7>jD1K4D6|WV#44W#)mBScZo+7pjz2J)agp$c8wC)-$${brWod)E#iG`K|R+Ze|J`e$n1wqa%ow)Av_%$ zrf176tn)SZr*_4p9}aEqDh_xZt7LhZy;1ag#+AgQf26K|u)w%mXXqMUeN>wm!=fN7il_Je4mu)IA03QK4*Q6hBH_~6N|na669asKV8l9%S~<12g{fWP{^@6j1H z)8=kIJs!W2_H8Azx!*FKVx2e!`>=J(k7?tFP2FiZFB%8lZ6hjwrB_XEzis9tjUhr#_Va_DP*#Sh{048Z*v=y_F$hjG7I{&;ck%W23`dh)+l~LLyi{DvmzH3yq zLnw73&h|A8=zn-_ETqq4(avrmHu!6#JxGMduel9lVva=)VL65*`G=$qI7lHM;^HW- zi6uRK18T=Gvh;YNHMssjf+=Ijl$L1R?V#d?I0}XhmTHEf1;?+qWWbkuboKA1GDzcOs_ZTKj1gvdXlg>=+s!p(&o6^i;E#5ycBLSP;e zUIP+q6rnXBK!(HyAnOiDfEI7}je`^KESn7p=fyj6P=zs6XRlFS49wB1N7NN^yV2oZ zKk9xX-dTT?SI61OdDO>dl#CqnHXU`^8udId7Vt8jK%z;LXtH$EkT{nl08YGw!aBx6 zM#oO9jD^j(oZK1<=NLc9e>_TS{M3yxPWs5f1&S2OX+EZBGqyuW2L>9cYAqvnf1IpW zj@xC#-}}uYoadaRIvQVK?Iz}$C_dph^Xbi{CAKbRyey^1Ab6MxfjEh{>F|$s1=UZzZ|8Wk8o#fB<1aCUC?INm*jR zl?j|O%;R=j?uz2fWjJyMZw&oVq3ojqPecr@L3|H9^d)6DzkmmWzv?qc6Lgy$#k z_YK5j-~>QTq(S2NLf1Oy;5Zly=RyWadf&4-qo!VuPAG$Mco8_J@5<>44Sky!emUiS z)@AfY$EcTANP%bdC?vM#Bwjr;SwAI3!pyvcX6v2z<|>`%ZF}HEJu~1GUlY^XwpQOY@{E^^MhCrdpT!e zCgLPH*?A2axrSmzV#P?F9pVc@goQm#vn8#u<2+;I+L!%xGX1qP{SQ4^(!IZQsKH;q zb4fpU>4?h`(aisVfiIs5hX3;XN2f*8KkPmwvi%?=S7k;;W~heka!$WNES$E67qPF-#!3> zyjKDN<&0cPP#!$PZBbU7%KD;xM2K%9UOaDwN}@_m*q%YHQ3TfFW!9+LYY74&-e%O8 z`dL}iWz(P#^%mIPf$}i|0b}x=f(;4eQKe}{!QCbj;DDlQ0cm2831pBKnHgjQV|B<_ zA76`2nXKywAwa}j;Lwv{ zggu#s3eDmhWoY6$R2kAni_B)L_GX(5?}N)5w9F0G1WLJltlKQHW;T?{NWwH8uiXu* zMN#;xkyIu{nHZ*(B{gwxQ8o__8k6{iXiG6etYL!;BbdreR$ENLbunQd;@RJk?CZvrp%A550mv07=M0rX%(8ca)} z>;EhAS0bS7Q;6~WQv09C9~A*D%wN|qVnzOXMv4G6?6zeA&cV6en(quQ(H_pYnS2Z3 zfWr^+Q$&1hOhJ^4wcVqrrNeyHF7xOlb~)=97@RSzWoIo`4#9|b*nE%%5Cns$B6fm9 z1Pg&apXYfRvduim1ztv!ZSwA;FK_b8y-#08Zr+&BEwHQOl+KPJuNT0(8si>6KQyh# zVGoZPTE%9`>OkNxti-1OhI`U8gUNuyKniXT4~lr7ZaFu9uVhc!U$sxDtZhM0_$?3R z=5^THoa8TmaqYWyeMNQ;kt?^}nM38X=YqRa9AYrG-@FZjn#boZ!dhyOmM1R^l4D^P z7CLM`N5m{pKe5)5@o@2wqKIDy%1fLMemiqL7Ph`SV}sXM>pZUACzLz+vP0+QE#c2!AyS8+e1wQGAfeYqKJ zLqYI}Ca_W1)y`CRcN-iMmw1UzQpNY1R}DZwWMt**SRSNL??l*FJ|*&}r$@p)A1L2| zEAMOH<^XH4^(czSz`h^xHW0EpY%?;9)Wa;@4r+&G6&N zhX4wn6FF~Cte-Vxm6UW0jwBRm0sf+yaWlF(DgJprTT(Jw>lj;zAZU){51wAHlbfIjXY_=J7O)?s?!6vpJxro2U>8G=&UN>_UO%jEMWI8$v@ zeoQ*&lEd&KX4Q(2Q3B zMXjO*4s_Sm=l(w=&03Jf|doepUqDfY(__2 zE3=T`uDce0rZxpwBogcFi6ESZg$>AgQ6NZZ#Ls@5uw^W>w9t<2MZh6!OVP#se+1QWSw13YzEZ_XKj( z-UTdGSV%)mLy$;P0cTfc^Ug7-G>y|OY~F~?wE(nD!(xRrt8*tcA zpno8mV?q!}YdfH;D`8&~4w%nr9T6s^p7?`1Va#6L*X!FKjU=5gLKoRJ)MQ-HDCph` zKVRlz%k<$XmIGqXR0@CAf&Gu!n#FF8q~&d&yK+jiXlXT&u9vqR>OMbI$uE zD59==(z-`7*-J9hJeLU{8uL$SNydubf;avcOQ=YL;+yUeQPtxwcFz(LZixBIit1e* ztFPvC8%IRqAGxZ)o)+Ef#tNuUCG-JjR91Ybv*^^AS(zX0bY=K{>Z9ipJWhneG>9{( z_dS(GcsC>}1Eo&7iY91?LUV3 zLEi$am=UJ`3SR73ykEJ+dUx%;-kYpPMj{Iukd)Xt8H%=SHh+SZu0s+zfc(y%H7yPdz& z_u|{Cf=fj;EpFwizZ6jaO|%%T|2*gRK&S>rwxKK9B5WU~np)An4U@e{1|B3lJ;m_+n za&C!3Tts+3Td?Z_Tv)Lh4+lpNA6qC)oJam{+N#u}LR}-irU@!1zZRi*=?)%35EwNGo^H+UTG%~u#X+nyUCoS(O*0zV`QlKG zYRED@p@}1pWY`WY@P@F?zrRVHyP-#bNSBJDI>2ZaL0!MbNW?+BDZZsa^QoR{H%jwm z1x~&?tiDeqLwWFxz{1`Zkpm()X&8N~m9^sFuh;_YK+mzQi`TTsNDMnEMMr{>MG{W_ zcQFUpX_lf-@@{SGg4Pk$8XU)oC=H{0q69AktA8hJjk6e{;&uXtB%Z!1YI*TW9>6G4 zTCOVQCftZZ4jH^YOS2%-uFM7U!b=9_S^-F(!bDJe6gOMHu*oi90*sBHgyeL+E)7O7 z_hIAmIrPhkVzUOnz`zxR2xWTT?)Fu_H4ac^Ov5H?Xe+c!)uiU7|&6% zdw;9Z@dy$@Jm2*LBD?n6oBiA&OG3PV%#;&_=7m@1MU3jQ;0Tj#Nv#3!5fD{7_$r1QF4XJV5JrPkQ}Fr%YP`InVqtY>)o6kK z`%0rIh>-8ElD}Rfk8Tw5A*JHO0fi3ZmNZql@Me9>pI0l5g6NDPrZqAp%P3t!-(>5; zLyXVCTHYH^)Hf$nb|g6mh$5M2ie~L9zl>tgeq$jme$~h4F}^0Bh-iu$%>O!=AQ-8t zDxc!hkn;bsysz^|irR~kfG>~0yQm;;5u-#D_Jogd-NLbS=}akEK8%7=)E?2qWd25C zmMOARMi&~{6u!uJn^S>>0p!iZdg3FhGNUj!vb+9b5E5bKAwYsPzc~QYFpDf8&CIz z0S=*WrU+ZPv|_9@C&)?B)~>0(DO^+l9~r{&z1lB`VZ{zk`f z9|43X*yoeLpwlez`zOh#u{4#!Ra@q=?}htC1-Sg57V*`#@E=XHZsQm9`;Rf6U^Iyb zA=R`yC4JC|a@iJyx_)B*z(XP3f=-3jiaS_99^NrUL*(Nod-FH^r+6{)fSBeo%PW=P zdEXa*{7`X$Ds17!VoV$8H|d+$#(W?`;*+1Kti&b zl|zNUA5zIp+S3Kz{!RIcTZG)v$c$H=n*onz2KL?zs#CC1JRO;>69C%%d_}hJ1LVYL zJ-~O$IOj$Hx~0@*D?UVdYz6ak2V7_&xoV%5Nra#BJ4~r%VRQfCK{*DKxg;cZ)kW20w!U&ZSl4nH83S{7JVui3}r z%0p-VH!SXIy-s4!XidoQBd^|u%JdJB`Ro;{gqVzuvPdY@Izy{7yJ7FWH%lAB#` zCPd{fcG34+6bV36hAb_o9y0O^9(ld z`B|}s6J))fI^UKmx>+q+e8*GZ)}bpZ#$*wQH7YVXhn>_O5Cwd`w`16~%ZEAT*5C|L zPXagG$e7)?Ll~xpV!$PHNTw{s%3yn{Twys~5lRglv# zRWEcK()k_OSzy(wJkoVTiZ#PkF%QE^ULH6@9Kc+V-uvO31zRvLsoW~y{qZwRkVruPtF7$Kl+_vn|rn&Gpgv7@O z{aJURCW;e%BYi}cEyq`qyu1oKkM=Y1estYk=v>d)Cdlq3`0a(H{EWu7>mC@;34Z$T5| zxq6%`$e)CMc7#g2tctuZZipdS7+sZkphOtx$LSGTc<96?X^bywG|US#r){*4Nk(Z{ z7%i(O8U((4tLcWPCGO=EA^3$q(Jb1#c`QmBLm)DGQ-p@PMM^rLR7|ocH$qdM>Uy93 zWO5Y2*bN9iJW{|S0p;;vYD$gdtqoC*lvbXSdAOqLTV2~2??_7k=CEAXlBB%&N}Y() zgv5(ccjjo#Pl;*y-B@*I0dXot+<ht zrke+Ct6~gdMNfbzu2?{!%^fSz=~}0IIrs2TT_L{Gwa1QA;g%oNf>rz{bQAR!kSxEF zb_Qh+A;(a}xae{7mlN^!xWubKF3Su@MJmh(71?J~O>W}vh2R!UvUt0>NhbO}+*e6v zb=^x+g3n1;)iKTt>d^}whlTyJ^LBc@4b^ib-$mD*64az}ML8-ZS=bT~9aQ^ja)(zz zx%PINj}&qzpM61w%_wk0B**r#yiOv#+U9K9&9d}cUY=*92USCnK&!rBqJr+gRjDLANv)T3Zp z9AKZWGE!o{vxYQw=kCqTv6r=3xBO#nc|}C}GCWJ(FywGzKljO}X7lR;u`fu}J=BeI zE7efm?As&vEJs=CKUjNI+xthI^wX|^R2Tl+#ocjQ6(mAAao)B3d)>De;b(0v?a)Y$ z{xg^+o&=%A^DnyH>Ro+CG9|4NUX&VzA5Y|<{%LoCNzR`nm?J@$?!#jlLXU9Vdfd^$ zYx&(a!})DiS<#A9R@^1!3f(OnKXp7j1s|b-i{6@%>@-3#sGA@hM-92~@x^ck1}8@i zr)xV@WKaiNo?F5$zD3_d`qZ`A%XuaDWtVh!R6Vzrn+f-4pF?H~;iT`);iDeT2W)L& zUAv7SyYsG_?muY1d#Qqj6}YwkJmt}SNK2wnuk3q(aPzi8lc@ae%9P}NAuP+1NB+q z&TDOBynXnHqNr%9^^t>Z+I*1f!=IuPF<{j#>QfpT(se3`Qy>(JfG zpQ!31A>@m3Nf%4CNPG@{4j-|m8}VjRDwN5^DIEKuChz}?qH}R%s{iBocW3tt#)gs0 z<~j@`gs8K*zIyaL>Z$zp z`wMn<&i8!3pYwUY->*-akQVCI5j|-)acY}t(8)FEZq=5RW-ZArIo@>EyU_LPD!5qt zCqmMlguJ~s(qy^z|BcCF&s$U@Qrln3lqKi=+oI{O;0F(1Kd_U`TV;D?Mc?Vi&v$5O z^~@)$?@ZUe-6FcU`;v6+g@yN3rPFF&#eeBP`r+!tW>weFy;bhNrUavAZMZmpk-p+h z3<*Xuuf|%gcA?aSr!Ti&C#WBU%O56>P=54pdf4WHnIIVz{?K_`$gCy*d2O}!)Z*MO z?0yfYw!K&Mv@XOMPwCdWnEPZ>)`1YOggtp&_?%2VF|2^&W zhv-KSu=a0y95iE=-qW6@2`ulWx-6Wr5@Cs-7;3fX+i^|A^@lkco`vlHh$xrL#CL{0p7#p9ao9-xJky%YL_rB5(bcJG{pANG z#;xI)bi3x)`4=G@zOs0x0ZErVQ+r{?-C3K%gqyvP%{<}M6z0k>?dCY@vXdq2-HzFg zE!ZM1+%m9xOK0nrz5dii_^sb&EgjF^T6Z<#&Y7)#k+ErQc?@$Ti3$y5CIRIPGvw{+EP;eR0H9!zOu&@1gh>K&B<_ju8Pk~qe$Je+ z?B4UKsd(o@BSPP(5-&VP8c4mT$7y64^{-1V66~T?q2B?yREf*bEqZ>gU^|y&sm66F z!LPY1qR$gg>s+V2wrL%LJQ2cb2jE4=EP=^wLKI4m4iS>-6yC+JaFY<&q;hwLNk>(V z&&0%LF3O%d(-ctJ5DZ?;j9cwc4v`?LQz+Gs zqTO2iw^)P4__AYL!xQHN;R1M*t3xd5)pQz@vWW_c{m2tO{>OD>e>M6-5Be~|wV$N& zTm!=NJzhz0Kz(Mkp-J4{?byKt&U&Z=u|JD=-~jMsViu^;K3b@^0o!7}$u+~^x12X$ zfKm3LQw3XFi;{Ov&DwdDaaaj*0ENj#c?p8P_+YqBn64pQt(FXlnMUWzx47A*>M^fO ztzVe0m9!PS?`JfL3}qYUc2r^=_5Z=Cp}Of_V0WY18PeN48LO00!QYWP7XM9d285Lg z{89Ce&xD=v%$?(mnYn>oON7$ zBzLaLsrqkC36)6EeO&I%1vMX~d9cZX@Al;gW2Mf2w=u#qD4 z3@wJ2SU^bN2a{I6qg<` zR2X|&fiqDMe>vmA-W0@w%F|Z-*}|Er0{jjivYXIxt_p`{0TvTF6oiuL$h+J)vsAc) zuXYe~L_pST*Z=BGWA7JZ7 zu=nlEISAo2)#>P{0>CIdfXWbi@2TVsOelmK+#$ppGH#_bCw}E&i+a1qTiyO!U1LkDn)ZCd`?IK&EDa2{5+HnHtbM zeKP=NFy@N3S$22sCd(lHM${IIAYT!&oe7!^s8j(oSA^?O;F|$@g~PsYi94?4*|4cm zfw@KF@+qgO%x0nE9K;4+f?klxPSThe5N0gSN}oNSymH~ZgcZHArE z>yx_Ri&8O1z7{)z0gF_gc}m;^3!BhaGX<)`5G~p3T_u4CNI|GMTy%Sr7sP7*J%Nri z$mgP604AQ&v*KgOv-m^|%?%*9cIAnh4Oq4sb#a)rZF%&SpN?*mFnWFgn4!Q}&|1uE z&cJ%K;sp*zMVSjwrc}sYe^f*QZhDf3Th~?*a1fEFMHA|UBssHrwT`?M!Nn7C)7$_P z;F&HMq6c6%Br2Ud{*PALLGZ?9ffhUeAXNhR>Y)U z10auyo~|=gm4=~#7(=vk`6Qo;KO8VeIpe#Oc$C>026qM=4k!?T^N`#5D$+_NWKh-Y z^`}i#?DGy#svg^1JpFT2{Z$7ydmSnSBH_5GE23Sqh1dgH;=hT|5-o0p5<92QsY6HZ z(34nN&Z^+E)=Y1dG63kVEMYd&s2rxkTn(d41pAI~$dCxN*N}7+fmgpsTVa$P^@Jfc zjOLSNB9JP=8N5_iig78T`L2qat0l>WhZHC!Ks(%?CB(!Eu|dB8Qyt8Qajgj9)W+T= zyJ{Tb?u3*(X1^d&m`$@Oy`Icoe;u-F6L$%W@tR)c4cD*CqlW3wIRIy|3)%p5dG9nmXqqNj0Z0GP5KDRSDoYJJG?W>l_KAJ!02{6RHZuSMBuAuj>sphf4=P^S7C z6V-IP0w6;wXo|3tORc{J|kF-Qk`%>4$5hKIzhttBH`Tns~~v@U*yg z1+G>~J~ZGBp%!4i8tkHwtMWV{eJW3hO?!c==aTTk*Z>I3HbeP|9#xfwL8I|HIBrS! zuvwR7K-OPorz^$wCRCgkg6m2^pI70TB2=ml6ZRH!;4CrI$e`sz zUwLRhEjq~tI3V-_g?W<_C}HBP5NJgrdNtx>IN#}G$JKe3G4uFuflbH4B6iH*s)3vCKbOL!a0PI z+YxfL=WP?FuhtP_PKqd(Z8a;gV&jXHe3 z(9uAJ96dm4(AxD`+i&>}DAe7v{SBrlau=Xfe`0LON>=!w&%E-Yq8oJ$aR>L= zb2$=Q10N6{TjtjVpYZ%e+7gV;zH4WqK&z?D%VU_k>(Ss%s`l$(8IxkHuciyhizhX> z<|=aD@AKWh9j8CNHNfZd)wqu{@Xk8afZ;@Z2$?9y09CU=57~?M&SGL6_hUX7b~#0j zvzmBX3uZ3C7pZaK+KjoPr}iS0uL$#NHW>#NLe$8U{&T|!COSj(7Pk?>)!xCGL}0BU&r&ru z0Z2^$d}^Gu=(^_?BeX^0rU-OjjSf58>7lEK{%6Fg`ELfjU**OE&{U}S*^vtQTXz`Q=raJn>E3^^G>zCvhXKXgD%un#_K zZv>_aoPip|nlaN=%;inrAQUd;tKqaK?@e=78h~7;5I}59?gF%4O-G0zi^ymqAlppr zLgc#9(Fd@dZ4O`;>dLN$fEhrvT3SYPjo(LylrV~D>Krp@rhg%!+1ZDK5r?HQM;Fjs zY&tYcO+9J0F*WV}q9{fxVm*6r;*kN0Yq(X2ZZzA1V#lKH{izqv+MfdW@}dL@rcB&^ zH{^h?;EURMZIbZr{sAeGL~BfLAiU5-v-nK{s~3kC`+23Ui^|RDk;mTN6uK2>n>wCz z_elI-kq1)0=iNWK=t`;W!hiA~ots?oJm8iz+ROtqT}CmpVlA8!n+eh1oQS(QA!(i4 zj}ztsQ&x{W8p4vYKUa)zk^lFi`>z!A!JI*}EFIYa9ZR7fC23_8ne+&OdUozHyvfIj zW+uRH6kw}N`F3KUcq}s$+yMeAQV6CpFn2W)U4|zyxQbyKFPgV z{<6;6xTw3t8x$Fal5Z>$${iUY2}dcx>=%@=42*hr5)hhzX)_4oBaS;%eB5+VnL&}H zH!PF>1-P##k24?jJ6BSRl25a^IbxQZ>A!iDazn+BKoM-8_i~o%g&5;ls@p+yI!|4y zoYt-tZ@(Gv(s(*EE`5q>fsmxTfLRsOvrK6&0NRz?Jpg!(=5oiC7;HxF2f@N5x9g98xG2D$X)E?R z%Pe~e+Awx8h|dn`{Etpm;ZasmVllVsYWnIcgw^3@Q_z6HwW~ualX8B%1f(=pgt08Q z?W^*SMeub?dz1o;%rqPAsh#P96kZGb!;RR@hoU5uRG=sra6kPKg(Tc%O6ANpkU3c! zXU1x*T?#WC-0boIb64QFjyFAK{E?GI{fvokvrWfCGDsdtdgs@10=G8T7&FgNV&v1j z?ww*nbfY=bsx#?y+?g>RLzS?a3=+-+k7BatE|c6VKGab{=Cm-+oE6S;y<9jnXIALi zd$Z;^lwDq@?j9L9uROH&@rw2HOT%hAUTk0XK9G;gK4AOxY_8Z8$5_Ip<1ott-zl-I znD60MTp(~UE$=c+pjbFNX1Q`{s+&yyzP`zkmR|qaQEseS@7&$@0}B;)E*su;gS5a}Gll(^Y6km9sEENs%r>6tGaX&dHnX9y(5ZL@f;0Cr15 zQ5IGlG6`4CdjYfzoaqZoXQsyYmq@})V-e%)B)`Oh=MHxaT8F z++8YyI%?Dox(2z=b7+P+uRm;|R6;XW1FIiuyvVTs&3Ut4WjSp4Ms5fFA;S{0{xLuj zB9NULfh<&5Uq!dMgo!CR`4w%MBgak8q?^Pe4wl;JnKMPQDVa=+;Q;Uo)ryNo*FcND z9rK9>ePXR5CAo=DtrwxF{Z7Q8g~=5LqJFk5)$KjK8+}SIaScg_!V(2VMS2NMG*~W_ zYBqSm@i>5B>?U&4UPSB?UXu|nR+M#fgQzpkBMr{_vTnbP{Vwc8Z=WXq>VFb(UX1jG zd+4_8-r)JIwf9=G&sDBdDbYX`X1{SY$|J(6#LM^#VVe9goy4$qC5#$+&ti&1kHMM$ zlfoC>H=W{mg>8KP$N~{GxnPGDvn=;@jG&!w+O|~sF)n_KIToDUp$$&3N+%_&A+Td= zsGZ+6!u*`K2G|X{)-Q6=e6GYe=?K~hj8qFA?B zNJP`aGmgYOSSE?}dnskoQHvsr;y#oLG4^QiR75f%Bh>s%=a{}WxBunbiDfH z%TvFKbY1sJ4W}!=inIKNFttyFo$=brm7L?2d&bwsY~GF9lCrnYqrHc=v7^Lz9qfVI zW`5Ehw^|+%^5Py3)tSJMyNp+(I$Y9=e7Sn+N@j;!PhF96HQ%hDMdqrn;<*MYx2}1> zVD*0?My_-N2PZ*_V;YOxbuY%EScv_UZ*lh9)o&HpC$F9@>Lx5zLey3@#{OS(u(9$} zdKpLRf*Bz!7GsfkZN+!tJm z)Hj%{R6+Xtv(@ueU#TRI%N7;Wh}B!X&047^vT`>Td3D&)MWle!2%(q?CS%`3^*q zeXf0Ao}E0Gm=kM*{%;-8>wD$kN!tgZDAM)oZuA!2%aX4c(%klGh4Vt6I&sp8O%W%r ztbMt3GQt`)S04ZOKJzZoq;K)4(0}EsGca~ut}8i7{$;7(4yiNc!XPogD^H9_?_dA2 z?#!)obcNDDZa|0&T~3c@ieR+ex*JwI18H43X_c#Ni2HNnUNd@pE<3f87zZYn9k%+I z(vZ4nU;{+mB0AvC5kFecxAFct<4?}vqhoWLpi7s3uV70r5npzF%}%Yy6n-%WAGTbm zUXv=Tdb0nS?DB|!Hh6NsrdAizyk#pDcPLMFvFG2EWs)u{w~23#2S*#wYVfM! z{M2*5^K2{8Gu~&k?cC(2U2c0T`RvFeLqDl$zST~B1%7>N!uuXj`wBDr))n@xm-bnw^%XVt zZEWk?)H5r7UtjU7zAfW@TQU6zyzu(&?W1$3MceIae<`g|*d0oO@+_K0mg-<=iPJ(~eI6i#^A$ za4vOX;txvs5a;++zNco0e>CH)i>P+j7zWjDR@-0Rm|iB1!U_|ASmtshd>{u?I zw9s`Wh0wtS(Q?d!jW{QKjXMcjW(NiTJAhCoi1eZ(ejZB9^cl1AEi8p-Admu)_}&m7 zyIk^hsLgEb*3E~Ke+p|)EkFM8@mY6eU?KU|e>blW#;N}Hjpx9(tmq-1r^p{v#Je)h z?Sy6bQB&Y3*=cS3sgJjwr%7}+w{uxW-qkk@h5skfZFf;EN8rh3N|tKTpdT3Z&b~c< zOFiTlK*g28sBhuZ_P}>aQttdyc!!0|SwA%OM(WI8n7jMOFaOL}{Yo_cY7L#tIc^v* z!&XDgzb|N-v58c~Y0Sb^yZxUqPtyq`guQ(id&=uVl*XN7HxcZrX1Cfvlk|4(Y=Tr= zG&C0g`pB*Y?$c+6-3}^uwuDiiRFF47ma9QcvgAW}LhuvRsbUH)y`++Wlr5#sOP@(e zPib+{1clwHYnAm%DHi>^{5*^8*&i-94n-lHiiMZof(3E&_jf68+Te3ki zC6VgAh|)@*>np3VWbDX~nOpedrnYW2P(jn5S|l{w`!q6lW%(2)LvFU)#*m9!{a76S zdm!R9Dqw z5MVpsQO&lTFuSZ02Yspx8Y!Q)yB=;Y)#}c#+Xq?IN$t)+u6oa>3T#jvvIIy?>lmQO zzR4FQM`2+qFc1js>QIJk7Dj2+5Cm9tJ_fEn5K3`H?3SE4hxR{Egz1gT8Vc3q{=tNLDyMJd;^2nxwWch1x%)Swmreqd_@TJZ) zNdP{7OaAmyn~6m-7zBF1By=q1%Iv*vToi;(0L=(+G4~ zH|FSf8ND4b@P*7jm!(EMD~+Ps_Cr>KVq$)+_LopaMpH3hCUcuH1K=P1S4JbMZ?Oworj`FTG;c2LWnI%q?%O1++ML~OZ zWE}Df1pV(BbxMNMhBS;hMERM! zCOQo0-?^~=5&Ol9GEgGhjOyK-+@in0m&-O4>@?qtO%2?a0!riHeZKV;H+q4yI*p@B z!`KmS_z&XmlV>o7@y~YU9+-PGRC38i-WesSRw6T-tgwj9`w47JAh2T)g(KC5qCCS+ zo}!k;<)qrdvUqM;%s}&wAnVI@vLyiZv!$_Z%4^xR{=`%9mtjnt?!_d1!mx%boD>qG zPMTtfZ8XW&REb^LVj-ZIfRf1Qa^rT{tM9L!`!Nff6yh-?CU74v2bhb19|37>azfSD z*4+XhIvW-#-&|G$I-Cb|XrRopVN@y3W$j)8t)6jK8-q?!iWBOp&;S?2EmNV8?qIoH zAYwo+GMLYB4EfGF)Ru#C6EL>YihyB06Sp7iqY6szFE8);;8v7#nTgd0X5h5%zZapL zpDnHfh?Pi8R$&tfz@^9xv?FHiBeD@cKrJOw#km`HrbVHmsw6*xp{<)pZ=?g433=D8 z3i#$MX|&aTsnMM@~)%h@;@ufK7QybaBMpAu{>UkA2GBJ4eeoxouD*?VCGkc zrHRB0MyiualGMLvDu18mC;CF*b{DqTQXxCL8ntyuGL?GPGb0}4zqx-c>$2#5kU54D zaC?`1Yu)T3DPc8(VTw6wY7f!^O?0ZYO;Vh?Y&!4AW>Qe9HJIa)0VTU?B_P^rOd~wL2Ep~v1jga zo>Avh1X&AkafsND3MQv^Gvibxez0`k56Dz;x&^u>rlROk_wg%B(JNN~r1TCU&)^X^ z6=hvk%}zp9TDD_T+i865BM-!VnE-=rH)v)b=+;C4W7*l3UCEO85lr#7P$z_LpboO} znEeTWHr2-=`@4fB^u$48k_eW~C|e*eAyVaM_tG$eir>v3hA|eR{${KMF#^N+j9Mdm z>tP5AjxEL;zO!-&aRO`#!axypYBU`1j6CCa#q9&Lhaz-`BE?q$;Ej~yw>U2v2Yew3 zFeT@UP*n=c={o5p=G(-ugDhYcm+u{1TKG162kC~^GtvQaGENf`Z zSn+-PwEi#qTaQg-dgR<`R7$4kPdBzhE2cohcQZe~sB>Vn9DkVPA!_dAdgk9a>dt=b zD0lWyNZ3lqj45e-DW=sys&UHHGH2K*CEo>2XO_ILXmoCeDA(Sw_0aAxqG{9L%ZEs! zx%F;tEldL1v)v$r2H5o@iY$d8p?Wk&W;nKs+tnx2me0X;RKLqzeshh*FzMm_6w6PrZ{@#Y<2teRu1(IbQSm$ubI53T zBWVZN*zkZ{ylXjb)5#UPplRz2dvJ=S!8Ik)QJShhhRtup4RZ+m$<-?f7`#ESDoyB!~f+^hsEFBX*bSY)>4KUTaSw_tz0Lrvd{Ja1dsWkT~&}NH)R`i z$4rB9J{*LV5?0^k6Lzw!WTKw_t7W^G4!rG@Rs}J}DXuv#q#!e9>#X{&v+W`P)CBGPbq*5ehSU%dIZt{bsZ}>2|RbS~I64InSOvkafm)<;Mx1SN+wl zb5G^}@O{%{JB@rixQec^IrKY@Y}11p!MUwe*Bwsm%7&EUG!%5hs&o~14kD7Cc|` zZ$s^K*?%YR|FwM5bJ%vpd9m}eHMmGAGu5=Y-J#s`;)Fxi-`xTCirgZdpN6^2-j*(S zxRx*_HG@dRu4p$p`ZA2|g>&2$AN<3qMlcX%XeVEjW~N#H(?2?2BJ+2unmlV&W>yQ> zR?ho!n>$?guekWbrGE85kn4f9k2bg--0;4`^{>r8f4C~eq+oV~%z7jHP?`Hh_TkFl zpX|n}m|(XfyHhv19o?IE(e2oQjX&L*8ny?!A3w5hqkHr5=8NtpPG0=!-g0Ij*yH56 zM;kp(b-cgmar(065k?iyp|_}Zn0@eQy^h<+0XG8%TFwr{O#igMt!x|u2+cge#No=6W?I`>WKDDPkE$A4P5Mr9aG? zj(xBwtkFMeQF-Y35u^PpdixXgf&vTn>0bd)VvVA?-Q?bzdoSqjhR|aIU&j`9{d)*I zvf!`Z`Vz7iWmLnPqtXB=Dny&F#nF{G*}Y&8JN^?!b#4{W>L0Uaqn_4A!_ z&!3BL&g`-K`S!qLF9;+y-MC6~?fX4lAAV~Qwwv_S^X%-6Y2|Hgp$zIhgJcwcB4-3!?f1sT4--k=j|NI#1oBZ=%5S)4C_oUD9zru`u z{WqtF{7)|$zzxaW)0q$pYAGA*^*^3I`&nLOO-qnC4YynzAr+Z^F2pQ;wY`p3npAUM zaRUAOMYp+BX;5-mBrD!uSdJ3HM8tXL7jp zKYGJlbO%(P2v|;FoeUI03-JXtD=i%N$mdp8y;4&`5oc|3jEV65=3_OstOUF#U+OoP zQc1bee6i)FRCS3}eW}dLM)Si@h=N805RQqUoZx;(2h%c~oMnRIUUh{Ku)Wg>uqCDYk;cNsOk%_`DIB&XXjJ!~6Z-Rh9dh_oLwQ;DA5CB&--`J(j|;c26L%BYbXIXAA}>Tiq# z6=f=Ipa_A|d@ox}=!xQ-I?zT8Sfinz+I#CgoJqgU5eGVw=#HXW^#IPM9po8i0)o^RyYM_E`+EDjZ)du}f@++J5HmFB(_a1bq2$%eIyMcN*e#lrh7LMNUA>4eF z6P!1+y1Om7WV~*nIQgLZzB}T0}+O-zI1jI z-@L{E)6Z6;J>t@DG>IYzeC^bN8LvEOxf0qn6c7>)gDaY3zOqo5r1mCWy~#obh~;Gz;?{_wlK$o&28 zWE26t0qbV5l9cw51(p~l8h-;Nas$<{fY8fNWJa(UEQ=e91HA~VQI2Wiilb+IK&>5; z;FQ6l6^T9AsK;)}EQ|g9Y)>#KrWw)zZe8!llBz5q&^+?wLesLv28l79mK(^!y*eoi zP&Lpy?Zc;=mnNW+opsW4 ztC%I>Hesfq4i#0T#M)_Ls|^ITd4*0AOx>TBfL=Al?tKZ9QbNbqb-!7=YcL*TNZ%z*z)!voUF?8>3eZNx-hwoVd~$447EVb8pmmOlUX&!eWrCff>8H>udlTYooSLeuB~iqPm>Q;Zd}(O+U)9 z9|FH%NsrW^oO#zmphn}w=x)n>27Ltv3J@+NEFd%N{W3HIs1MweV44svywtB69h!e{ z{yP-Sn!#uSGzrT(88FQFv>yvB2dJ*Y{7qkZMt)0f9cZw=KY6%Gl}_W{=hIOZq9Hrg zBQiidZbH1H8a4)gQDb;l)a3jWB0)?ks^{pfK|dedRMV8hVK#up>3P!wP1^tr;1wth z;nMj)uO@3Xe5%F;T;-STb*DOc-jsMZ+8MG;^U5Zi(gITg#cn{24MC_F zagrzI0W)qGzzjdJNwQdVQ%K{QFLqA%$Wh1N1+1W#@8;R?v8lKjB>}&9M!85p{!&IY zIwXG{<|7oO>OPcWhH_FbG)4L<7nkf!qDi-f8oL~WV`uy^*n>cKC*s?ZdIA+@R|k`v z0NbX~$L$rT5e{3qm#RZ?11Ap=n}D-nvgZu(rEm;A4W71km)%dpDA$J`0x-pZN=SP( z_!@-`0oWdm?uXf#=qz+2OiBWTu?FuVHQvD9>Q__Y*?G|@YLe7Or@^KH6YjD(;}F1v zar0e)9`Z?bHW&gUi|lYCu!~YGehpaXprA&vZT>y}ulqjzX-jS^`qINZouI7{?4UU4 zjY&^QuSSm`So(;E_Tdt!TxfQ*WHITC9IJ}!=H^Sl6F?sZT)!!VBulK~26{xJJBP7* z1!YGJnb*K>$x}9GYB`_`bu#_1?NKc(7S!j-|!A&G_?U?bYc{sy(ksZQ<)NwHvJg&B-L^pY#KOC$PWiJ?SYonkYPwS^vA=G6+tEiVY87lD{z;Y`K?h@(`Ko zkS8gSIOoDta|H9`p%1rV<3nI}okx3kB0#xdCRW}!zqu!7z|+}W)rR6KB{n1738(f& z-_*3CV2%QEBfzm~VosqZ>z*3zx5$ff(56n~a#Mn8eBW}Y0tcB#GN1BwYFM9yRwYm? zx;d{6Q}Vi_=TVB>6py}vO(_5u zBK6J*Gj@WFSIjf|IPdVc8iy410k<%d1{60lY#^n?0pVf&SnkTSV4JIU!-%2uFoD#; zMPV)JhMd=heJAV@W;94mZFtzr7sk;ytJBNDH_#@!E4F<#_w(YJ9zFKbmszsJ1Hlhk*hjLlockO3Z(Lx8E zt<*Q2d8Q-bfjiI6`8PUf0V775Lf$N!enMdNH2hxw)X=|?&~md4#^M`J^JaQ{pZTBp z3JZ0>$@m_5Pw-Z`fBNgNu(oK0fV&k-ZyT`?wvm>;_ghqpJhJ3JD*cuZ>eKk^KYmLm%S_amSv#}MDA85&ZQp(wytU+*sL33a zvNpQPGY9=MUjh@HDq>`hT?H?S?Jn!X;%0}NXO73o3Hm<0d zx^_76;`vhi+I^5eYOr%1wdZ`!sL_h&cLg6+(POk{KLbqO{UiEv!RE_4m`Fzsw>jUc z_e$@!Py$lO1WX zy-x6@ZJ+G$sASfL`g%GT=P}^+u<5ATf(bJO^RJ;jy0U#vQTs}yU2Y;4m_#N76-L~| z$0&<7#LlHrD~8+K4L`;kZa?^VqS7R?YBETP|M}Pc%7+GCi;$fBq`t=c;{N&?)8q{a zAvNmcO6w++WrzW;urZm_!y2h7ja#2YBbVW0Yga_hgLoSpht~RTqxHilEUDRe>@ruL zdG194Vsz^5#Z$|T;$hx!qd#4^tKebqVarYX0)rcAvPP3`Gk~%U$50D{YG~ABn=a<& z988vOR8{<>KLJts*UX>5+aC`n`ffXMOc1$eP2A03VckSAn8bDVUFy7Z>CiF%@)SP{ zw&i)o^egt$i=6)xJc+Gii7mrRW^kmI`MhP@)*thR07r6lW@pj<$R_bc8r-&V=C))} zyba+hN6TkRCEoq0&+QD8UwvD(h^I=NND?of=A4%}jjrt}Tp+KMk732S{*@Aww}2k>an;4Cz_&+aa!aVbZTm+V$SB@uH|E zlj7r35Q$T}cuEc&*fA2;HTYBe1~2NlapU66q2dRdu2+Zm-2Srp_9}yQbD|54Xe~oW zul65y+$;dGui0X=Oamzq4&`9HclPuyoO`yg)X`>=#ZvNY#>L@F{!=MVs+bi)XKX8a z)mrXm^Y(^wMuI5VoRDL@3xya*bx%Ex7%C^g1=wMW=4sgH_$&Gw>qjjo_u2dru>Y8` zd32}ou(&@q^Zo<|jjvBdqaWCTG4X@%3A9Fgpe*rMh)rr{*m1(ceqm@$U4&tc2L>de zU~10&&YyOKCY_J|5vK{_RzPpYjA1|xR0w$`=>1Mf?TYlUZ6++2kie>ph;mDxtl{>m zrL%V0^GfMK)tS+|ToaBabyP3AQ5E@9`Y`H94B0ZKh8kX-*-w=L3sPsPk%6!D>R|7? zjzTwH*;jBgHzQM5v-s=SqI^yGWXx`NhrG*>3L9JP1d!_mC4Dq65E4_2(&I<$jkW-W zmBxUJ=<-70Ekf!<=~uUzy3X&cTfMZFyDYCUcj%_8c$H!JxAyOBt$5=&VYs#lJ9HGi zISwT-&RHy;n{@VQUe$>Os?(6L8)8Nq2?3^vE#7WsVj6Nv8L>tv!5`f6JGgenfsJcF zJUEm6peuBGW@WrGsk>#)^JQKUN>Nr|@AjP$QqWVjGiG6f^U+OaorAAR-%pu;)ViPK zu`=i6p+{XLGD3?%$P{qbz&^AA++`AcU7f^8Y^NA4^E{87^8Osf8SJ4Y#3l5ar_}dP zuT&z3D6yCUJn#pT@SMxC;`Y()VIgN+rhvAfW~z&RipC9`QW^58e4<|fF8 z+a&e$x6vNgkV zkLf^S6cI*&a3%^Y1A)4-DMztNp4~>^q9J-EJCNNJ3TS5*(DJ6196$Bw{fRSg=8cro zvlwK9lWQ9ptSrhPmhV-M5axjXYqj$`H{g(X%-w(5YAbUNnH07V9o6EWRC!`~t&)m_ zW}wu@AP*@gh>i85#E1;jQ}b{ctFa(J4ceAuKQqlHy0m&d9Un@VbKuNLw-~~C$0FrC zrvGPZ80Fa^c?PS~a&_B^gAY(@nd8UX-luE#w`Y(XtB3e8p2d@a9f#%a?XFwY-P;#FE1N}fOFI4h&wpRlY?*^oD73+Z0gKXSQCc8H>W# z9B<1v-Ik2W8Q03A{2Ufe_(TZ;WYa1~+#^Fod}^w{AFCySko7Rp3}fCm^}IXAs(<0o z(K8jcq^g~p$6wmd3-9q>DI?CRe_Q{uRTp1`z4)b0dtxpj6&fh&PNxb z&c|xQO>Zr4MKQ zGIMm{q;FY~`ZmI6UqagXv&^|oW<^5u!7Tsq?YjzlV2i4_gGH?XF4&9 zc*K3f(5=#SYxWDkoC?G`snU3NQ^a@HtjQxuM|@ybBh@YU21Rp|iSeuUdP;~}7L3{V zXuKlu^}E?u-VZ5vn3E#?Kco!i`8USoQWGosXB``pcxbXs%jHr>n}(QL{@<>)xssJd zTxp%L81HikGS6^Q9CWcT*kTShY>BzwHoLZU_q}_mjvHjWiBBg*EB2;+SKJiq=AC|= zm+KbLdD{ zVw=o$4j-pmu3H|idwkxQra5JAv3kqSC;JLgb~_Z0cEiD@fQkq!{lu|?UCicIyAmTy z0*{E9=WI{jduzqYzgLOx&M%J&ukOW`dbKqeE&0VVKC)~(+#|;q=3hO$)nm`!Z0O-Z zshW*4;1KJzCE`v#XH`clH;Y%E)X2A}Q+OSHVw0DEx4Ip9aO&#pU6uJli18?DVE6vz zhCJH~+i}5}xkWsarpcu6!7x31_yjEDK7i+0D@qnBupsEB#>gd#haO+{B4N({M{hjO z)qC*Yx@B=prMhI6U&jMVVxA4q=ZG8S_*K!Dn3uEK;3k0h$Lw2~4x!lBc`);*FxzfB$>_z3=<|4Zp4QEU9Z-L-}1*jk|eE$%S9P2X^z& zpTe$8P`7{1X$SD#(5d{+_|;-^=WE_-0$gF1!Cn(a)t_9y`90?Ol@d!gQ{@j?&?|1p zwK>>NJ~=_W#_F*f*-z+DyYHTj!pmrexUj)K)Fe+t`N8%3p51-b*DL*>2dn=2FX=4z zJpS}(2wH7^a!aIjBNDzniOM=*Pq3408!H356UzW#!wnxv1SzgB>MBaad z6i8%Zb0`ue%WgpY`=U4GtY6S|N9V_xQrK{ByOYriP&RC={bdx;6QQh7N3)@(qp-;& zx6%SI&5L=))=ocKVo9yA>BZ&ab(vwA=@tPEyd8-9N<&ghjd4{GDn+y1MQ`5UBv?0` z`pN8o<9@gC{kP1Halh@LR7x}z$xZ)b=uRA&{Np%)Kf4dZ=A3)(n>j;hbKmC5NSdpV zYJ`w#o0+-is9b3zAxgSb)GSG&Mw0H4O6fqCQa``_1>5s%d!9Yt@8|t`pM&($q}s%Y zdZxA``=ZGomr z+vrF~86(Ht;5npC75wXOU=c;7u(meIgl`75S?W+1vY2sMd3T@97>s zPk2Wmtj%w&6C+~yFq56ARz576;8Z~i{3FyB-@_)PK*?74!jM6(neOa;uYZrKv8fy} zRA#sTD{#A?VC+)qmE-{Q34O((3tInh`okBvG8U&v;|d9;#OVv@UNMT^6%nnkd5w)& zoYYi9Tj&Xqc`I6W8M?kY1VpjA#vE2BPVZl56rk(XOOR?7!;o*+`~+su`LUUfh|Gd^ znv@6OtfkpIM41@juxiK*)phPWUH$2{Q3_V4#^b|gxdvJ( zsBQpFYNM2KEwihCR}hTu^_fLnz*|YWr069NghpFFwH;d`Y@bKQ(7>)@056!7VK^EH z%r;gzmYD@_%*v6jf(t;ZrLd1To?&$_!{}w_gKEO*S5peg+E;-Bm_?v6R7d%60@y$- z_)n6R{Py`KK}7_Z?A~pIt@)i(ZM9u;2MFVjPRjlT&KtCx>*~~5!{IBv^Nhe!U1BNc zIoW6mVOFf+c#a&+K==SC1`S<1{HC96AgH#xvubzYkygURt`fQP$RXTc2KFyt@MItI z1RMRMz+`Qb(91wOG19-tf|3Q0AR2sTR5ES@KlTmUO+qJ@aCYS@G9c)UZn4H@3+Yt= zh#iuK@}VC5H&B*R@{swJHYeF|E5%FRTdK&H1Iy%?>U~2B9%R8hAD#I~+Sfqy8KX!h zUS{TE*Qgts!S@KBMn>!Zk)go;BPi0uH=tlRl}?CdATYItxzpZHzawu|VlZD`^T(7g z4TbF8FQ-0)a^=14V%t$G2wOS6tHd(~0*|+!KXc3XE>0;L&A}}Ah9W^8)Q!TX1wa2D zYPcNuM!{%xr(6kC8Y;x?-EW*^X*d07(}_F(MyfWoi$MY5K#^WrA_1%{~%xI+RbqrvGbSioUaT!&=~}uUEn~m%-DJBi2e@ z`*N^D(T+AF;lVP!2;uQliFkX!IvGwt+WqW-)E))=voKbRTN=9FF1F}?z9~vwh zTad&TZ;ej`!{b3o5d^#!E|&R)b2k9bkKoZ#b?kR-+ZUfyNo^G1nzhv&K(6X7at7q1 z)93tXr<8rXA`mRiS0JethGld+1?IL3puxf6cE;N227;|HFtqjon!a_{g#A*^m4rjb zm{V~Ak&aq*<~({H{U@jZ@FwK%=gGAqHvS{Y00f2F36QfGA|Ur zYH!XG-fkkQ&oX~39Wl;fmyfYFY!-zh1orw^Rs#*^rg)xKcQ|4`U*F-jUL8BVt}g&@ zFIv?`Uno=qaSExNFcxR~7{_o^(b2U`Zolr6tGgb`|2W)bNRZMtN-|+pupE%YLVtP65{N)p&5-sp1@i3++xygobZ{AO@?tzxOD%rm|S!9J`52gdlA zATcz@@A-FpB`$Z`{w7j?b;x?l@hojc@v?7eK8&O+%%56R1onYku6;TvYV$qlMRGj@&mjGC8jQ zdy9-|UD37r^4_|Qv$Vs!C_0~3?>Sq4;TrPD?!)iSpcgWvt`oeHts3t+;;+^>_VAHN zA?Avp@$R*zx%xWtwbSpsW2ylgp_f(}8P(5kSg>veU0)CAZ{BuJ^$$}OpOV%gW`M7^ z!19`e6AU_M|+?iD=J!W_a*V2kc9er-~sG`~eAb*ih*_{bwR3 zkMPdB_68sWh&3@xU*sJWKdBN-tzpiw+hgK0$Q&%rQHZ^ z6kjRt*UoO_TbmUEo7R6UIXotKpn)Ct{@sh^rR z%8KJUYEUwzM^}y$7DhrS(qen9`ro6Y@jFt4ibK|+*0hyu8qy<=r{PP<)SYeh-jp3Pj50-L zzC(GOMkk|tbAVzJ)C2zU6!EQegT9g_UnvoUm1AxViHmS0si3uih4dX8GXB8n7dl%? z9F^zCqQx!+rzc(3Cx7HM-djK#3_^DbrnEnsJR0S85u~1}pqx&a3PRyX(-#ymBICW} zX6X{5;m}Ef3mP8pT7B#*fF-&(r@Bb7#8A4+6aB~(=91>LSbEh!Nh*@y=L1S+d~ube zESOJ=-aYN)N3;$?T|b&<8nl6TJ6>aGIL310+e6Oeuk>3nUNqzbvALe6IhH4Ou-dv=93 zSWW?WRR%6`NO6h))P4ac)8xaoH=sIiCWJfSW2dG;>Kh{?!Z{tuHv|SIpt?!5K@4g3 zkhB*`(P;w65#I`lm1IknU4^A|HKbpKf*&*j}+DGz@7Vepto6i*+k0!oC zS8SJA02IQR3eGd|m!$Na%&mt=Nc!?S$*Qi87|JEs?redR_^>ANYLd2W@+gEWRzx!q z&Ej;L;KOSjWE~&9*eXv004xZvBQ^)b%F*M}nn!>qcj|O}2{WYXgzG5r{0Bw?srLpM zxkC<5mia_e!nQ;>Np|D4u*ur8!2A#_BHKCNh}`Odipo~77#e~xJoExg46fCAw2qXk zZ_fLRv~C-%((`X{F19{BcGPU!_52N#P5XGg?e1p-#Wsfix`%}P%uwM&GeF1MCo_ux zRoel?atR?R#~89&vFf)KnNUDCvJEhvbc>gU`JP)wwi?Y8p@8oT84{S7r?%D5ni=mg zuLTRfG~V}Z-cvm$8QydvtWs-GIR$ne^PiH*t)PuL#cepbl$_4iShl?l?^0paf+yUfn>H@Dv1yOE{0LACk;0FhGDr z_LdXK2Ls|^sWG4MjgSPnmUKZ>*FuOQAOPqfC2HMNF`1&L4U^~jZjDjF#*f@q&d#hJ zGwYcKV6w7+fD7o<^56hL#DD|o>ZGF{?q6{CVamub9~7GgYoY_lR$1V#{sCNe^xk_( z$72tpcGksLTnh^}JR!J^Xc_=Z%+7p?Wdp7i6npgh)ZXU+Y_V7qL}9*0xgm$3a;jiD zL)Hy%H4G6N&Fj`60wz5?J&e8~fLgZ@PaR;<%TqB;f~4rTcPkFF>Ux#f^6nUrKo)DQ z9pXCT;{j6)z|6fv8DirMVIYUDQHYzZHbGjJe?c4ao0*Pj1s2_gyqfx_3|h8$v}}YZ zNf0rM{=^FZy^_Y>(!JL|TAz7Y?;JysGR8Prk(+>8S~E&U@^Ft<1;k{8B|t*}{!pi6 z&-C$j;Jg<;ooq1M+ENBrfQEdK+d<1?-~z>$>9Po#wuz1kxc3;A?DO3X*>h8MsoVd1 zi2k|j{}GL3Es;%NAVr-xWLaYS$)j=%Oh0qcnbg<`7Jg4IG651-8*tCESA1Q8HJ(TR z+&QW2+4#!9?58{5OrP`JmbSw5=sgO6E5O}NevLK+5L8w_W^xE}cNQl&d%i?Xuh13G z`~}SuuUZ-@2as@XIwaj9u=BFGT^;zoOj3;S=>d?1$>C51)m-| zvC6x4UT0ZUKs*ANX97WH$g;f(zZTg_Da6wP$gTdCDpcR!Ax0w^I&{Z4BKz~@{$2YX z9{Tm_h^*`Jl;f9vB1+FJ3sElW1;v12FS%6BdznzEu%njow45R33hfUiskIep1o+B< zU`6qFplmitrLb3BwuuLwFuuRP3xG-n=lKpXd}I!fi=|UAiD87g$;r{_W+`cxD!@luR30|sy!LsXVd|WA;Ez!l)_Ibmigh` zt%~Qlcg*|}^CiJXY??M^^XQPm`Q#ngOA-)&V+fE>n*z%kV9gOX{q{wWgi?p&1h&Z} zDQgFCcl?m1_9zYRwjA*sUk#Tx^U|^HC!sJw%jRfw=W78vwEa zLSd+LmC{MnM6%!hhHdlnKGxuR)iO7wW5dJJ{sN#JZv|NJh7c7Oj^CfS0L}Ui;PEZ! z9iwrF-FZw-fHt7~*Bj|9!r*|Gk{C*>Pa|Xq}c5`!XjGLB!IiXu9J-6bKzg1T)xl80@zgt(hlQti~4T_VXb@B!e+)73kbT(pG>c zRO`AhZf+Du`OzqpzTR7?i;O$B#la1gv^h$T1{8mo6h<=;N0cJi@$ zgqGgD8}Vm7GP18N&-Wl*9bUYJ)BDC>87_;_0PCv)Pa@}f#KCR2EMkpDWZzg-mJ?wH)06un z>Rv%pLB0F>3| zkmV@`C11+Z|uadoY=Gp2c5gfAKgVVx2wE@~9*z~e(F>i?f{itxcxmY3Iwt&-dtPFm@t}UYYb!X=aQ#NFN zboje`l&@S-FuS?mFzv|Y%JmmZhkp%x z9sGJk_OFDPTN}2#xl&pAcfs_?@9}*%znzr*x9ET5k9Dzx64c83^k(>-#hmMpTl;n| z9si^I%+O@~#>3wQ?swPC;5_9SM>fk0nutTR35shovMNOD0u%K&eo z)xUH3F6)57k?)aUBdqCf@0bX|k+)ZI8wdutqd@W>*XjaO(i!*{$_k(`Vv( znLG={BP}S4>qVI6DkGRxeEbJNb-nE~AGpe~lP)pB@u7f_Zaq&m06kcH}uQ1C;A^Ap!BOF|g zzp=VjZMr*5M}eEJJ0FXmEW8W0xF)0~_>|m;;B2OIHq+b~YoHK?qWuo3eFYo1_)T0p zP;ux&ksH9J8RjU*amhrcW>~_@xrEZ>!ipTntH@FomD@T5@!`OZaf=TO6-O*XXGYl@ zj|;`|&})5e`k^Ir)bKqL_KE?HB5YFw$y~S%OYH@ZB{_A$TA7T)06oB0p zMSvl~qO1*g5MLflWxH{e%gHsSGC4Ag$b6t+5fcYHV;8Y2G10h6|M)4`CNTtTurt3u zSDfKkVGofIfmKypd?YlO20P$gW~s8>5WI_>NS#lv5!EUa-+?z%U|Mh>`Zg|=?`CEz z_v0cocCIpnmq`*-s$PMTc(CR~UYPGuxN`MeD{n3f=heHxe!WV3amABDhj|L(9_@!H z&uQTAapx3kf|S{jPocV&_XW#Oi)9$=E!bVWTiQ55Z@*)}BdaRk!`qnE+b$^1s?`1P zH4q|!W&#`#NpE?IEA50I5XV|Mp6va(fmhxM^W)XSYhb@Xz&8TUpW^1<+tjYI{j4#s z%eTS09oLh4$dZ%OcMp0=1upE>wj`OzrAt{-{JfXrEoOA`=okTjIWI=n@L?Ks7_l?; zbV`He6Z=yX_bULff0-MnRKM3Z)yM0kMhH#;2WRszV`9~0Iw|YRspzCz(+`C-dUL6$~n0j*0M}4Cx#BqAvCy}hiq->T+Z4|(Z1wp~11&S-xEP=FA z#Fi*xRWuEzm`=O|CU?&7`|+s$JW8dstL5mJVi?_FvYoyoDphRV7*344t%uWFhFy}Z z788Kq%+71An-1#W$lo^*9h6PMD<XFci^y%>*iia=rUw213=J0 z7{UhPA_G3cVjyQPg9nQj?JJgolEkg+sn@oF&zuAh$ADgfcCDQf ztPll^(q!K=Fx#;(ggZjA&3Kj<0Q5q<0pKei7sI>0ouuBg&VNio#1Ns8BB%F)lgEhH zi*oyZBM_DkPaJvermO2 zPauWrAx8WQ1#1aTTMHmI5_h&&yB!Ujrvtyh;K@}DIi^AS8>s_wKnMfIBik8>P*c&M zUMr9miGa{SFa$_>k>m0Ex1X%USEZw?Xvlvba6oiIFz?1E2Bu+|e@1f9SlD~Qz%T%W zVs4yNK*`0SVi+YMSlA-adkm2J#=ykTV3B;>8L@{ddE4C^Eu~o59{-(cpu68GJ)z$_ z6E-X4N~S3Qj3RIoOd!|+;2T7n&sfHNn6yP5Dl0s_Go2GXH&?q~x(xQ#*R$AK>sa0yVfW4>kjSaC{SG#2{HXdsHmNgFqp z8-f)%UXNkW_Ns4BAmH{3y0;B00$7>-SWnB%t>KKpp*HaB6e>vG<1yISngDHNpa)RE zDjSgLz06pK>car^vI32Op)CViB&6I5bRS$nAAmX$iTz@!&oyBSjfHXcC%pt!Z?i50 z(qJ`W%vbD&>;oc7^IoUf0v<5)w}S#QSm4o5AG8o_)R3+zC+rggd}lvNqGvM$tU$nI zU>*M1Oh8rvvZVA<1k%SMZekIVa^^Gus3V+M7@u9*)t*U!(*fYK0JkO>j$ejSm!U0E zz%T(E>ntt20hu7m_MgSIYQS=j*0D$kN#!o$qKq8##vlMlPnf?Hi8k|bjUPtTu19X= z!{Aq`@&3DYOiB*7;M&D-SyG!T7?_=h#xbOhEQ82&=Sx`Ki+o2(+-g|z1Wo{Z^yA1_(0g;{w%|jWkU+P`#T} zKMhTzZ(du4_EC`G7iOqfp*`C&f&o*thrV>_$u*y-Uq;`tB}kNA2|6i}s2$&$h88sT zJ)bqdsSf{z3SI6uR_KjBWp@aR+QuiS7$5;Ux>W@>L3!>?ybwdL3=v8H`|X>Xg$A&w z?Q~dYJU#3N&g=oEp}wCv^SpLGK9bh%Qo=ZZ1cs*Vj||`p#fVEhurdkhJ{in)cIwf# zeBAEXI430u3Eo)+-6Ektz=fnDfi*8Og#TQUufR@Ei&-OINU|&9#T5xtOot%TU^Ch9 z2g|tfbXEia4O$+%MMIyN<4A$=T6}_@yztbwlvi^bzMAs{Zv1v<5~^+4LH83#n}IsP z#~x1@Gtzx9m$3i$GkxKg`F&l7ZOO)2L=>G7l}eEAO~8oxGIH8TsZiL|V{)$;7KMd9 zCJb27q=nlmhndfA?MKPAK>MC6H(i3n3Z$A6gU3ng*1a0TxN~=%G^tT5lhr2r=BCEH2FrebFYT!IRn%Jw0&M>s3 zeXQRO(;h%5h_zpZY|+dx?%m6(6>p&XL)tD%Chg+JZ#;ku{s%I%2psxvsZQ^HZ z6GMC$`xk{BgZYzkg6J~5%MdB)JAR^*d8!XF8n9dLytyf-x#h9sZkN|0_(ZK~L8$YJ zuJ7?j_T)UcWWI877I9zc*Rvw%MU zL$|#|!O|ygdpek}0$6Oen1IleFhE~SYd4IL{HoJ>bDP7pjB~00!4E$bbsr;82mCo! z;Y$nm6o!irq{b-7k~&SIo$S*UDm4>pR68j+9kzw}CaGM^?`0_x!`Mr*lBZV+h8UB8 zwGw$MT9tNChJwA~3#vAWxy4&1AsJ0_`u0aY&`$@hP5>4E9L2f5wR*l1!}40r_zjM0 zL&rP(M#!n7c0hK}T*$r11|{bPQf?uVfgn(WmU%Y~N$<_C)TM!wuptbnq)G=|6=?u4 zitID4$pW^?31XOiIEcV5+_}2!US!>f@u>G2>von43s3h{+LpQES;UFbb!mLoFGN1J zh+FhJ9+1W=c5*I;R@$4*d1?V2A>OKX;(lnQ<2z9t>Qd002^x4XSvr=rRr{Sf@ft~6 z%8*I{kB0%hZH1;YDO^8bkr$6>n*{+X9PaOUgsDQ9*%^ZeYwb;r?fpW7a;W023i-y_ zGKA0h+?YP~H0h z|FU{>JksYZKfWdM6F`8;%2>k~sCF)~eBBRepy&t?VYO@vinJHY)TxukmfxBa$5G3k z)z&zlb4;1DH*T>^Eq`%RxAJ5EaG8(A1-48=hm}{u<}cV&6}}eN+DbK+X~`w$8XSvC zwT|&mHJUvdh&o4PiOx3WzOr^l-^k|17f0Y5YD$Aeefu>NYpKHEEyq3622A7YqO86I z89&Ki_`HaS&JpGH~%-Tjh(tGXrGGlAh%H|S^2+tP!(1cWF<;F zJZZ%kn36yACeb&(wZtSwJVZ5Xo@EuNl^xQNGW`=Hv2AfRtPOqJIb?XXn^GUPP-EHG zDr(UUGDwG7Lg7FFz))~gF4P*PA+}S%XkdSdg$L7=>R*;e`eoN>n`mVKl1!P7KxEls zvOB-!i<6)r=CcrRr-PL`=#quIIpnAP%nd095-@ZiBR6Wm>(R5f%>i7fuWbM}j|wv- zkxL!MWZFhH)vt)zg%}mQXBPnTfoUVX7{V(~-@LSeD%Ac&mY0|NT_^h%8&womu1Cb4 z$?_^cItEs(b%1IUTO zDL{r>ZY~#chjyVn7?XRR%9pwZ&TlQG`I#Gl09%h^Tn{jXljc?awEmRWG_`s%6TV-+ zMOz)>0n3WJi?pG7YSMUcVF&=&2sHh|=0hKHwq81=-{Y>dzo1vT`{KzTKIlf`BM0!L zmXyRTXMF>2B>W5SbTj`*DJr?#Ca2gR+RK`F7gyaQ;i)h3mk_@L6l)3`DJWANY)LtgaFBafN##pq3^dt^WUVsA->dfV88^Y>tOsG9na9j2`0lE?4peb4DaW`fX(VwP4- z(eCI+=MSgk$o)(WDcw*cpkqj{wUeQh*(VZePnUU*s?H%IxUc1#0N7!n@__s3Qa}rD zxbwkA`pLllplq{zoub6|_wK1K`+jeFYcNd_S=7Ce9j<%#wGaF7T}s9&HJF^Yci!e- z+dm&VADQCiAN{EDUFI2=e<_rr=;>1iS)FQuxK*FFGPjgH@*~Ts)@*G|v`Cnia)V(v z-hk;fa6f8QQE3ghKclHrY~kG}j+mAQcQPQ0Q0bjIFY z9mLpos*cUQFj$=4)eq z8myfKNv`5er?nH-SjyvKI%{*j7yo>NhC4Vsf*=hnNa@OH| zqsu3|GVPCEcO55AO{6uP)PA+n?D1sK6_cjISGNvoi-$Im(A^}d0TO0h5cH<$8&38~>fi~ahSsd!$#qjw3fLN8 zddXdS=C_~-6hok)aLsC0=6+sdd|FYM;q1XuM*ur8hLOA(TZ zSAD5$_d2#0cGK6!oBH{#pKssfVz*d)y19JOZPjLr`4mVh4+&1V<%O~|Sk}u2q=4QI zLjdqu#h&|!;Vf=ZTffE8A-PW57SnA{EQ21?Iv<9}dfT`?WO6c$G-Qp7)dU3xHkR4A zb?!;klMhmgk6vfCUn`zh(`{b&py=T$xK_u|I1c_qK*j}R;-K4rBV0lEM=w~gS{Ub%S9Xz;W*Ty93;p1)0L0g*hx8v`_!U+JXdq* zEwf?Qj}V4C-|YahD2XccM7mpzgL@K??u=XN0q!1hU;@p9Ln$CIPCGUgJ1KxUIpB?h zNMONT*NB8$xgBsH^<=5$p-M5{y-Lo*qlw~?YgSdBGwhSe@;t@enU2)I?V+5&b)=MI zAvY=NzSZnbV^V3I4%!=q0l)67J>@ z&Hrlt`Tm8v@;Vb!x6_=Qg25L}MaYTLwfh<3yUyLs9Q-(-H~yg3XWg&dzD_0NBhi$~ zCieGM0KV@^$1?~)Wy{yb(vZ_#qc@!%+FTQp=;MEvKYg_X_yb|u3H9`x6$oan{z8S$kT z#qo>c`q^37u(bVb8og{WYV-h8L{00q5rb~6HvZrQ5#o%PDMM7kZfEMbRK8w!uUrl} zKpS7B1_Q_mLA z-4hgG;0nQG^c*)D* zjGnT&NhvJTjgByQH&x^FQ=jYL-Pj^4*olw&+gE)ZO~1Q;k$E+u4KWN*GBdxB5i&=xLfCS(d=#&-6o;ad$7*i ziW&hKyx?`gX4?;AjtFXz%#^3I)Q};j9gru5T(v~ODuWZ+i;s0+)gX&#c#clby(X8f zCCt!uRn8>pLXrw6^o1MX11}93I+;IikgRIR1x;VaDW8SVcY{}EXL{;c-FvtZRQT=< z!Jq-1!$HC~p&(Cg#{xdvI2{Ej&$7pb&pFcfG&&)9M&SVVabw-1*_U@NxId}>?DJuB zO9}0D(zDkGGr1YuA}4BR=0ahtICT9PTyuq~Fi$S-k2*9sBK|CYQaa9GE+$_;y6FdN zTYu2S$Fa))kY|3(ni_$73gDS{28nC%ZQo_e20`Y_pmc0ld3u@aZwZ*KF`ZFnp0NP%12$j_9eqdVmTK z-Ok?WivbU^?IKY*o z4J^n0g_6;eN$HsN=$EdO(SQSM3v1i#9+tfkEKdRmVTV_V;9Y!#ZxgFs1^Oh6oGX8N zClwS+N0|3B>!3h-M{&!g*Nwb-IuFz6Q)DNIDwa$~41#G7^l~(08v)t&M_}cwqLf2Q zdJD+XP85faI5^0R1!N70;CkcISn)ekk&`>~QNtmmCi9WiB1&t`2lrsnrvYqEXES{< zdtldGiJ?uj=;QQgx=qwbmP<2r>+WQ5{+07N;zB%Rm+1zLnM5y=pU}&62z!_Y)`)sE zH^9ndaPA~G*pnh19=A=Yz>Rw2#}=so6&ComFqB&qS0~KMnc`5}Ew5lw({?O2LU(bC zB-=(=r;1|f> zn6p^Kp@aM?khu%-!A6DdYdf%Eo!d`8)&YP)C`p}s5t`Vzs=P*9G8$xNLccUJo=4;;)o=GK4_aoWc)EY*CJ|Go^j_DIJ~&~& zG}?fPcji9(ESiiK%h`jIeGrz6NREiM=WV<_XCF(z_3T)Vncnoej;T zB64?oEv}=39oTzF2yaQ;P;pxPlCucO;BXPKJN>*DVR_WTEw15vmRwB~9#$tCB-Dd# z7Fj;DLXJqv~E_RzXZn=K4$SQO&3POsy^D^h&nmm#$FawD=1o7?Ig1eX z{o-9IaE~a-&n?!JPd@Gpsl2>xj4!z~LQB%wXTKC0wtc*$`1#zn>Cj}hFBQ(@+}z_Z z)1I4~)RemU%gCpV`}UB zU#dYYWhs=sNH}@MYjqURcY|!(aANJ)D0?UBOAsERONPR_VnSbN!Q5;l_k!0tK z6?Mb%Jpxp4WAcFg<3&uPVD~Mg}9c%>Txi_Z#?);7hOs)3fqjW8rBt=Dh5HWt( z??ldN9uFk`RC3f9conrr&Z0gvHtO=t2-OD-$MC?p{);=eehAi$XnK`_Ab2TiMf`f! z4K6gBKdG!0JsE$;?_v&s4NgZ1`hqiE?v z(Aq{_{wW!T@78lU*V1u-=KcDf7ysqnxr}SKFJD_Yq(ZVWy3`n3p8@-E)Ln~|G6}m+M)_N|-ZKn`fv367b&i4br0{v1z zG)k!GmJd1(3Dgnk?=ZMDy6L^+D#b%SqD@gQfIIg{Ej5jNN1jp!A~{U(Y@vYZOCrba z^~SL6aDd&@?_L;-8137*C&uq@ySqGvdgk4;yD+l#c8d)x`NZDGihBdoY96S%>M2$l zMusOzQ|u=n=~t#3Ck0{x9j;2}zf?)TzuDLJJtWGu`SNJ#$HDALg}^l;Ec`0KI2Bx9 zPDN+j8?e!fWOYWCFkFih!pg5+^Ll762jrn>&uSj2tQzune*dg(iS3#@RXcP#=H1D-ov0aqr-v$J`u@3% z?gDeolk@f`(gCXD1M@*XL{Q=Ka~8_`+MOY)JFWh7TeY9kbYtKS*WYuSAW`y_cK6I4 zT_u%%pAT}nZ+3CQzBFKd#{TtD`O03uqv5%7ejXV$_<8b>I}u3!1_QR>OYTSkbqS9k z-U@$*yw`DJkNXxDaayhV)n3h9!fDe08@cqhd6Rh3Mvo<)41$nA3X zLWN3{Hs!%+V1!7>`Y^koRs;x$ucl75^L_mHd_%pEndkZj=xiUvjq35W@l17aKys9a zE;Olrlht0_!M`J$Ni$L-xk8mU^t+o)YTF&G?q}d|LK%m6j8*0khQdY4e&nuBzfl1RIvi}vuq{pI3Xg|&va#KPzr@@s#;niIb+C8BA4D5qoT z@Q#6w=mQE`y`#zUyUpV51D?YBn&yU_7b+6X5Y(?#N|(l~yOnV1jw^A2 z#r?_lsU;_@XCw7T<5*EQqPsL|@) zl%A??^SGhJbC2vVS(O?iQSy3fR(d0ztARzNQEl03EMS&RC`j69O1!Z^J&9x!vi(Qn zhio?iNbO6l(&m6f;d2N0m>4QZiO+`^E(;J85(s^)7b@8TLPReL6zRQ?;D3p_&IFKb z5H>nRXVcEn?O&4iVyld8tGd>!c{ux@jf$~e(L(0fIg5R1wpHYV)V&xX(L>q?NBOD^4f2{Q2Y}x{U5QmSCyH zZb{`%DgdMW!sC3|O)DqBD#Rw&F0vu~%MD@=W@cLcl*1j9_Tb}Fb27JcE$zQ-W;5dX zis0ApdlDUzqdKA$+XJMoKlV$BqB!+{BKKfk>}gfpSSz_%UjLC9n7@?a_sBzu^h-OU z*jr0*R?|-@(WgN}z3vLspa7#+UF5t#cwpOKU^c@;eVB@WctkE6rfKZ zHi!{}mHGgfNfRHUuzyIVtruz>-lF417s_N)3NgPuq+=XF>ACb5Z&^;$I){8BiaVOS zMDS4>%#@M0I+J1?IsM3aUrORj_6|^~JuudrCa!5doKg~Pmp7JF*V=h#BG2*0-X%pU zFL;2Zn*V`Z(Q_K5ef{GXi_4tsj>ea%S)b({|F)IWM>8OE6h&&3UDoB4Z>jvfrh}`^ zn+BKmd_DMP^XosaHr-nP{PhS#GBOanFQbCK0loVI?=+e>KE}6!Tla5nh_b?6J^21h zm(_rw#pHO>^0SX=2mdC1{`7|M=6wDFAk`#4SY z2ZyHLi)28k2y6i!N+u96Eif+!uu^NUTq`bxQ*f~E@&P#|M783p(zsb_|?IaEaQ zrl*##lov^NZyk)%Dj#_MGxcs^_ovgUw)bOC)wKMxa}^$}_;3BC?9%ov9}4atUaBEf z{5o{gD&XL&uYOwJ9~%v3pLBj}y6^Y>1lMm3ubto5n`&)X$0S*x6rTC*`}643&F_Z; zT%N^*RkU*6+GToPo4DiLmQ$9X2$}dq7XE$uY~yUq_`n%MvdEbi5v=z+sJB?5j?aE? z3YxN@gFdwO7HZ)^9|P8gWSjRrFQyy{&dn2IeM(`87hL~++_7c-`Pct`-QM!w>d7tt zmiBG@Jm9+V=k=D2^{*Qn{x3>g?kYIHP{E5qIyA6}7;H_GywjY#^;|G%P9Zc-F?vohK29lhPAMZ!*-2Z4 z5r;3C!&k?tG|Z{A#;JDAsrJRGU7y=f6UM2J&8bht;n8BiHAds(oN5H<0TXkK7;<`P2T|Ke#C9w*%RSMmL7YwP2#HyFb(s@kf{2|HP zQXVHrbI4Mhp$eCW$}vpIF(~`+m6}>%el&}Z^A@Y|mVf3gHMNQFbSTI-M7abjoi`TG zJgokBSUbJ3aZ9lEdt)1#U<+m_$0yjQzOm0p*p&BX(>Z<#jKF=lL)aEuA$a|?<<qv$P7ThERqLbcu zneG^{HEb}!ZT^HyPxD5gz{5lE$fNnC_eTvc*99L5DAlmw+nVU-n&{DWYTZ1Re7)_D zU?JcS&hf!Q;LVeM9~XjF6NCRO1lue6kLd-;C55QI4bl0ZsWT6U`i}%O7Ni}3A*^+OYGrx1rb*}U8T-VGs z%kzBZ^Stl-e%+G4LCfdVyzOp?6c)=zxj|0IzKAyf&F{@FYQs-S(!zfkOa9_tu6{dx&G0ncA z1}V`EIm-W_mR-i@gcjnMKiZIq%Hrxi6n#_aO6>M3d-_UvWfh6wNBO)G+cuzfNQmF- z-=CExeNK-bNP)VzC%S#DCstEFCx`b;+tyU2Ep8(xwY2^NJOTq4$mZ(;{l+Zp5a*ydr+Q__Q?4xec55X z+}!t3zvs|r>ON~vRaiUL+6%i~E2tfZB<-S|z2g)9bc<{^+#2y4>flX?& z>97d^gQv0J$ime<^-}%lUmN*{a^>B|jV`i}KASg1jyE|jHUHXtrQ_8i;2$Pr&&M#W zItR!F2C)6fJBJ4_iC~E$5~zr#Fq?SHEL0cQ-ePu7N+4-cvx&eExtAH51c+4zG$4-7 z$yJkmuDZNOq1$Ku(cWe%cx!yIE*+&1c~izx-#~_0jt(XD1OvV2DCYo*%@7#?Nc|1f ztza79S0`Z#20|2ujO))4!Qcs+a$rCem{^;=c)Po<%Vn)`MSeW*%IoH!j~jI%ZUJw6 zMu-1FC#C?w`2levMbN9x22VuK8ZrA&23)?0Qt2RS&#)SRbWmiro-cxlazro`Nkh&W z0TVRUXR`dZepx+RAJn7^&E3hL#VgLSR&8Zl8TFa#_4(E3;b!Nyk&i!4ZNJ*yYB65h zAEUR1)eKA#3A262aEW>wB2mVnUIN#E%2WWL%vvBts1N`nk!(zr0U)UUG+o@b-b$rs z*qEvQ<4YPAfTfX!aWKM#T=v@q*tlH$9s0}@-|+;3xMYkuGab#1rg_}y+!Az* zHi*pxI$*{5Pe+xup}Z~E&mJ%e4^aRziL4}GCuSp^K!nah-z9I^1kw-(K_c_AKr7|= z?Kv9bS078N_26m6X@d~-Y4TdhV##7oRC+L9Y9&i=$l0+*&M^|JJ%~d?|LpuQ_U8^8 zVi$+mK&xJGzeBxtR`qzB~$<}N~_DJz$;buwfe8W`eaJB zKzcNBK=CpW0MEz)al~HqHzWYY0F5m*A#0ie`hM##vLqWn(LgHUa8UpiO(Pt;K0E~t zJ_ThfAaT0VK~NIdmWHqdINvl#<*TP{T#I9v%Ft-(k(5Orh41GS<)9%#e~jSAeJF_} ziJhQX>!((>r25Djxkec&nIyqKc@l|Wfj{a1lO0vH8?{&EclsU-S@e7hzPEAj4QnY zMaCdObsiwGKRu52&*U^n-0cV2gAv|$tJ3V2RaOc77(iGW1}-A9PSALZ6sC)bXU^!? zzas%c`2dZ`Dn#OKC$duYU7l|7c9NKjQ3%3Z7oo@3&gjElssZ=)Ihb7Q66}wft6<1N z6t-Nog>?J8MX=X~0zd&rtDlDeo=>5gykV?w03fcF6W^vONX7=znVx6nP5}cFFv)PZEHX`BvyoUd! z!xrg_X7KL?(rp~-`VUDrGD{Bmn75BZ25w&aYn|7fE{E36>!Ihwlv6(X|2^_JhiOlJ zNPimR>!r|xwxtjnqj9bvSz9oq^JnHOpdvFcEQ%tONn%o_p_U{B6a+B9l9|Al5i%m0 z$T3gKZ`M~FW0ttlytMQSZ2qg|tlAJ31ZMQL3JOsl{is;+8t_6J`siuwf5KW(tjUVD zUY?3P z46}sBMw7V}9vUW4j^RQ?tHgKaw))8ozPj*E?_!bjXMf`9`aDz>#SNY0f9ts%o^1ct zIa~iz?fOsn+R0II-RF5bPr6TziTksCJ{0~E`t0|U##{6et>-q*?ytpfj!6HVlD|2v z{&!mU=8WOr8H<~5?N7b+U3;!%z&=4Tw*gQ9V0V|sE7YjK=uxm`67T`GPLcwXF{MZ2 zEucvS_HY)^M7=al2I~M5>HyUMxT!B?I$r;?aMx#@HTOKmP|KOJwzMuYwYaoe zK77#+{rxHLpVi#CRn=|gBhrPAvX){S)t}MjVgDXJ3`qXx8?~~iIvk7eGLVAO&dB#L ztLwR)6;3@3J%)ueT2=HYFu+Pn6lGEmB925j0z+(xrM4I@OZ^l*GFvN|`7T3Q<4vhb zi{WRynI%x4h-$MPZG|;U@7MmbkN@>XwqaUUEX+^!uH3KDSHCvP<8N>LJzlKW*!Z`3 z>lk9h2Y^vn4QJefeiDjbDPk74Xqd(>D*z2q&E!$c4t8z5zRM>76Yge=7=i#+pa`tD zo4-K-Sh44fqCIC0o(`b+=Xx?MAIhE@?VZp2fM&!EoK~U0q~}b*X(EZk5+uQrp`J|X*6Obr z{CPd%+{DD4h9_0!F>J#UcU<#^rAp&C%xf3(Gm-?oQWtoDRNFLofeb*VD1ng2>QZ-< z)YKoV*`1ReJ2A+`Ij%igqF$-F$+b^V`!>e;M$2w--{#DBOoWX-NSJHVkXtz4PFe8g z%%rJUsl$}H>`g8J*9s-4YD=wAAsU`$kI6XmBLGLm14+=c7FAW-0lI#iB^6FL<-HUJ($QjdQ)T}}kG6)k2gj2B(WJoUMKUWA5wk&;1tOye1lqP_ zxBHEwUI|7ODTFj>YEe&mb$LyB znmCo$GhLQS6CueK?scPTUdD_5J6e7yy7{yh&PbLU?LO|>hR=NtxR$Cb2sUzJc3I=d zz8B{K41)weofGe=PyObZr_)*=40MUl!?m>a+2VId+(NYPGDA$3GK2R7m57SYfdWk7 z!p5{`f7Z$H5JXEp>GkDT=V~|dA1+m&YY{i>c}6sq_116k(&K+lcR&C>=+^!8FTsc& z6nZw>^Yaz%V%q#B%}i=0@!)zNYJ`7abY(hAe>2r)z(ou+|9UT_VI)Eu$D}GUS^rl z=;vlnV{}+Kcd9c#!1U!w96*z`XC6fd@*Q*Tx_aG?#w;n__+W9l!#C$>`imIn3)5P? z(8yQ$B(z*euAvSdM7xjw=BH&mQ`V3$Gj<6s_F>$=eEj|b*D8pWHV%Q~O1b`g(qUfM zBhA$lzivr!nM4n06x+gO01~DV2jhFOLs;#-!Ww$dBH_B=5yTYW|K z?>lRkAw!W@c4R32f>rCKaNzw|spfAV>l+Wa9?O1{(qWfA8P(lVdR_rQvC7!r5t%5s zyagb9@Ci3P$!JsR6-(%d3FY+^wZm)e+`YM(IDf6H<#{i`E^dDrc@PQJk;N%d^06>IH#lbDdknK6d9$%qyw0N}p;Sv)@qPk0*;d9ehF zBayfqNI*cXpsB1Gz@pbHbV9pi&Gx6R2wQOd*~80jsE`gh&W6$umahxxCGGGEJoe{x z!HsgWm+-NO20JKGE{vVrmUIv#YkE;=DK|~%;9FFJe3&U4;yFRIqvfpB?mcCLGy$)W ziFt`KU+EBbem;GqYg4hJf9xFUmcUGzn?=%9)OlA;hhCE<6{AxM#%!8EF^Y*OZPiZ~ z(lZo^!2^m)p6NWbS_w7^YKszptDz?i6m+WgyAQ$&(J>9Am#mkuFdWY^>zw1niT9e; z9RV+KNxNfA*r`E*siMv-U^z}5wfm>-Ew_76wK}z2IHX-;3&L&@zxW+}=^SfM@nG0= zGO(ygLgE>nH@E2=>V6bM`!FE6Qu7YGJ7A=V#~w9+K;l-H{LjIri?I=aBqS%O}>u`=Tl~F$xAs|P?_4~L<2a6s*gWR zNiv_u0xVJWp|nd0hHes%N@i0Ca>OrJiav3xzl$}#WbwW#E9>-c@7`q*@k|fC(V`JGrO+hI;Bvf{^u=0k?eS-Jd4q3EN&Q#yz|l+e zCOB1|>;Aups2Um+Isr%>+944YsW|u!H{j#Vy0SzAy=LK#X92cu4<3mL8kg}LDgi1) z2m>Ty?7>CL+TPkG@F)u&26Ji9;0DT^lx;p>i$7q2H<1o<0Dv(%j~ZwmT?S9jT1zbB z)*x{mE(jcbCvY)|z;EhltPlbbp`5%Q2F$-({vN>Kju+>23>1xH_FwyXh-Q~@$Lq6e z;IXY`+}aD@<97%QI!~e`&*xekGjJCNpZaV(bvjtipy9u{3Dz$;E6twhf%27=UZf{U z2}>^0yjqN*F1BHT5HYX>2@t@ua?rTPJRZ%bQxX-seYQkxm0gj$1SDCsCka>rwj^le z5LOI^2$}J@cXa!*QV)P_VD52NKO8{1_ChV-E3$enHJfwmb>LDNoH5SpwFqj_hfHMy z1b0pV&z_e(I8=tfkk&XBc;x|};qTqr=`CUUQa+gX0b=08H{gFtQtw8DtQF?G^hIRE zz*7JaQ3gjhfz+z|IS}oZ%2HF~fe*^y0;W7e$Ni&F#Gs6#)GQ8r%#MeI$=8vv_TLC= zhHQ6(SL-%|95Ji~VK?;TYN5&PSL;^^I3Pi!NGOs1Y${0q@iYnq9^Lp|Kcg|fj zLLZ{d28jfTyi4lK&VVC{f}Pe<=K#Qyet@Ck$|t`LG~*PABBpe~2W4PWF-bYRU@I9e zgjW)$!PU?o`u4gL?L__ouJ2TX7j1HD2;g`G?2`Tnl7>|(2fyBVI?KAGW`&o|SCh|Q zImfEGv&w^|=g15t7??u_jyd>Dw*+!F+lT1b#__>-8O7U`vxy#G<4vw1so)17AfSwl zraJy3LiDbw)=oI7$vui&IP}^h?72%qwqeh+*G>n7w2BFaF`@|gN0$5T6cx^QBlT@c zh<5?I4tDY_bZONF42yatK!nXa1q)H{_2g{2fOvqy6fO^R`(Vx`8M&?s{mKJ`{oDrfDXv)2VgcGjlD zeHFav(#q+$B_cbL4EHI2@w$byEhR;w>us(?#Yu`V^kH0stT|V4;sB=-7)EF9&&(Q3 zB=6%qL?@2%j2QP*&vVz1DWq;igljWcgzX54JN1*!6ZocHf+TkpFDK3rIo|q6BkG@b z7d55pp;W+XL)*o&fk^G0@0Zp{l zHyI??WW1W2>!5>Yh^HNHJBGlwDjM*l()^DbG1%$GtW=X;3T($bfM>c8$(?% zKmh?#9&9h)kOoGDm4y3TXxs`Su9bP4nVM`mN@tQL(YdxIk6pQI>%HgwQ_i+#m}8 zb>C0K;w-1hsSWjuYIEs|j{x`MylsNWcY*{lcDao6ff)CLw!xUO?YM<_7(Kf7HVM7E*-Zl}$%QI#dwbt$kg5)v%5n`L0xI}@ubw6i zcavp?^`kJ8szqL#+`-_dTHm&L#Tg&so$D27;I_;Vu*Mwe_$cS-tflHxEyQGsB%pCm zyLe`t+?HJ*Z40&cQ$OA>&NTcGO69zmdFqt1 z^{oP_ku!1j88j8EHCqD7P6ToW&A(xh8!sRMX@||L2;) z2EJT{pLJd%Ueaa>P#NP-l_4tUC!DK74lCQCe(P>%V_6vyVLpIxcw0Rxr{%)^lr_rw zVyd=1ndin|=ZWYCPwus*=5@+Sxoo5<`9%t4bfalbhsUHoy@q>pxqWe#nvd6`6qH_~ zXGyMsj>3MYKFAU`*B_%d3DDGT2Tkvf!}UT_cgrpmFS0~Zkr`_Ip`Mx7*U}Q;)nQie z;?ziEy3;UAyal=d5yp;JYnZQ3lm{9_f`XfwNl{?43cS z%Bcx%CiCf*NllxS&e{%@AH~?938LHs>)D##)R3Cu+$V(0_FsW6DG3gP*GoR!>ypWT zz%P$?Fz{s#((gU~BG9lY{Z@sKjaQ6#Dxeq_RJr$J6<1q1rt>ncxZ@8F;I%k^HOBJ{lrU7wf`YHyGQd>N7Fmz@??vjYHU{G zQ6UZiV)`;2FBFP9*P{-o&)D{)7Y*88etemm*jBZediW%e=6pTkenBKQ5-m@D_q)=o zCQo^;D=YRv!OIsPes?>`JvCT;@mu#5J2IN^u8QXKviJ8(HX(Y~0J548sN;Uiee9Y* zRo?Oap5@^xbII7yw%y(E_gAHgoZER{v6ZTJU!XBei4e|h93%zeA5|uBm@-A z%V~3tg7BlyT}OFAqg_H{Y+_@Bu4AJmV_ko6Q?BFhOUCO<TBqBrt9Gn<%+@(p*(n;P=lY+uiCg%s)g#kzEl=7!3b>V5T zzmvM9(`PPCULekxl+IY3n%tG17Q8uaU;0-5?+o{+w-<%q`Tl)-_0qebQ}4n{rxb@NMC~-NA{?gfG8ue);kD z%ZE>2Ko1zO(+tFC28+l7??1*hpwgQ*Cl=5Af^YxqWV#jxpL)~luPN0CFZu!>TP}UM zQCjbWpVcMI{5lm%l*7Y?p|Y#I3-WRrQANKifrEco8r^_3{c-d75Lw$TbHK4i%jqT+Yb7)@cq*7)JxO#Uf;-{S6N=k1;#J4 zec9=E+ZBAcEBbJ!g`(s|{VMjEcC5Xr{-y8v@o7vPI8a6u$RmBMqP*{-TJzJle1UIQ z$|pb%R(SQ;bx89+Pc1EcTD|2q_3**rde8SO?yO1_!8#9qCWaON|l(%gY|kZn06rJwIKN5K40 z)1$%Lctgs8{!~hCJ{BfmHr)Jp^sdU4krK0(%JKW!;T!YAEmdzG5);tE=B?F}kIvZ0 z^>YrSvH_A0yZ^95Sa?!y=y;`;ePtGv399vstW9_rV}#CDcQ%KYZ)H`7ScpW(>`{jk z)jSzu;EBN_|WoI_ut<~`gs+2sNqke@w= z85a_YtHg6GnhDQ{WeAHE&`B0>&8A5f5?hlKxWG5T0NNO-|M=$Ky|(o#YO>r$bw#1ToS&0V8Wd+* z1SZ{+xs$iTBI!y`R*;R}A+sBHf@uh`)(Hb@R62}_PSw_cG-ZxVz=6lai)n^86+Zfv z-j-sQ#c9`xz1A?NHug1FdcIqaTdu6#?$~PERI;$|lb_(a2~0pc9+$>)lp12}-0xl% zSkPB|GLe^Ytc-*}092rkn1z@mKs?K>{CHrE51{T_>m2|z3*3j(8;NZ4P;$wz)R_gt z<_~BnjO%PPIf;K7V8GDvsNBSEy`NkAZ-@xRg9)RkU)z(W()9;Z2mSKOU59ghPhZu? z3>$JPw?$IC)wv$t4R(eUHD&o?ireEYezUjVmbPgT1sI~Nf`eVDspd3765E`BzDbatN zm+y4#?~hsER}ss1-{1dl#Zdu7lj2RxPQ=7dEDqqT-R=5$rM1NjdCqp`4Z<2nx2L#l z>On+h<09~$(4OuWR0ZkPOA2?e3K5XGjnW2aC!ur*Z}b9kZ}I_Li+QSwu{|^2D`eGJ zc~VNn1F44ZQxys$eUkz672ud?A|gmpn~boYO$@Qbr$G~z6B8BLj$}& zyxOSltYX^8BQ6dn{j(`sG^E)PmFM|TzfuglwAjICB0Dz7R zrQ6{o4JgS@eT@iZrVKB18h}(zx^d1iFi{n~{MQ=?KzE&qV9nh$=i`iW;_IgD@aRW* z0F91oKI*rQ*~_PPy77e?R4q?Ffv|7VP%#q8Qm$oCLOX@$b)<4LOrJ#}q$i+cC@IWl5*B5vjHZ0Sl#dS zw^-59)~1fB<~d0U#4>V5Uom?yPFa3-G;GMyO!dZ-vAZ|Oo)>lucmZ6JkJ{^Eq*b*6 zuggO_m*7M#J_AmI+@uskvD7gdZ2WEgr5pgVSdZx8q~<8xv;XZni&c=6^gW&vpu zLg)6{0Amg_%CyIySV#Th44m^qVoD5NHAYV6RPOsft2H?0h9ahNx|b>{%+^(%HikSq z4j!8@^c|Bz^6u{))Vj0EJKuESbSjrnJ9}bd8VG@vr;k063c`z@|Mk+XZAR?==q9d6 z@?H0@A5W(xHj#=cswsTu>+iJmxD^$@t0G)WGpnctT?jbex$y%TE6L-oHTvOom~Epv zit2YRY&J({+|6;Vm#^8|VQ@gb*}h)hTh#wTN+0!d+>g58sL_w#N*xougL?c}FS27O%Km>?!zG%_0rE9z)^0}_u#7Lq>FSluE)^Cj%$>&c-v#!qc zs6I`x)@nmqnz?y5zR;P}YMCE@8vpa7==T9=rw2YL^w#lJw|70yB&{W~wCmhY{#@ad za(KMG^5;2gXkxDbsYB_O*4P3z+(&Xjc;QJfLAm4)R$vjR;^tb*hZj#IMl(SJLd{rU zi-AC}j(D9K`zjL7*9U4tk<9)~-2d}hK-Q6w4jbm=eWeX|tX{`9=i~`Nya!LeZZ3>v z@e;p1Dfz4AQ31bVjLTSZ zrHO3bj;$LlpL9eS0}1c>NST&#trHhz+jBt2ZNs$bGHhGpGu;68lxV3cp62h=G7fQ9 zknp>Z1JLSM6xkuxO|I-mMy~rCi#h{5g-BRe=gm#9Rd4Q?|gzdt>=bUP%UeM>j%U+ilc89%PbM#yUx zCN;o`cQ@AmPvOzumuyla-ycwQ$P#~E&s4=7zJC*M7j*gIrxf13W0BZl1u^u|;2GK* znW@9y$H!VAYf+wCJ5hlrwQez-ZkdMO`i~a|q)09yNF3VaQ0`dyw=wPq7d?#v;O*Cu zN0)zdQ3$C-MfLN}go_o%WVzI+V@s&bQ#jYZJJ*G0qePJ&;?W5NXUeN^it|9Q{87R~ zEzxWzF4guZb<-Gt=r^2_VChWM@w4MS8_$mPBRVHJI_W<4&{LkZ7x43huHn8QC|26Z z-TR4Fl1a zu`;37-glKR7LjdQ$OAlHl*%;jA=8p0-9d3v?<1KrI~iy2`ioSkE*`e-oj#NrjM7A< z!-4cRj{PR|FuBx^T>OW9-n$Ybsm0ss%9}mI0 zHbRJu^PADRHzSo}qm_+Gs=at(VGAbf9rVFYwuDC(t5n8mWRAOE#@MRSwy6veaoPD1 z3?oJSuP-NfIY*ZW|Ga}5$B9JwNqDa<`n)@oc_@FdW2W@h>7Tc{4bd!gBAf*U zjE11J8Ziy)nRgTNJ|$#j@`qmhaNXDz`w{1(@t>{>5NmG>d#VMi?jd%6Ez!m6-=#p_ zhRBZ3fb@aD`47}SDN!q1^bhCAe-JX!|ey+1hPV;hnozQiq+eVmcR5i6^e_ za2(%shyFg&1q+Bo5D^3w*46(Z7h1hrrRvmNJns)ZnnkcgC_9lJxPL$}`fmR%%G);c zI=QP$8xE-YVd@-?5;kNzKc`y#>|X95^h_h-}%e`@W2C4aQyLMwTcKUcnhTVOE)>O9l*h zUYYmH=?fB2)kNJf6gX_fguBCBNVl)=#2D_zcMMm7U!)oDuzt5K{eh(Dk}oJRgqh6Q zuM9Aj?@4}ft^1FxD?8an(zpyF{Fx!V7l$!Qd0-l1sjpwt;!~4-@}lZQ?eY%xcx@-! z6lCv+hrK2l0lo$uzv^|CjX(wEz)tpXBc@~|_xePp@LhKUdR^?V2glcIo;%;MU5F9- z#d)q#^3z5=uh{VXGJ&jJO#MebmDmupL5n| zO%-}H_~2LGh!%g;m0+HW%~OdL7fiSYA?3oqLn;;u5b~E`v0UQ!vo)(N1NPNqX1%Q3 z4Rm$6q-K;|J52Kjpi;4|md(w3G2w9=I2DTk)r(vwESmADEn<&a)USG;8ik&b=hD}O zx^9$Pk|h77xB6s=WTSeXKkiu9WHYT)IqC?<+-2`^K@?)mFV- z&oe~Td;~zc$dy0%*wEJgK4-k|?{-mRf$AP|;3iIt18*GiBJ5611$$M-&I{`13jkN8 z0_xCb?8nmZrCTi-r?n(U_t$A%cSh(m>_os!|xp3>B17C(;B{_h=JoIo4;a(Dmpd zr;$)URj@B~g9e^dE*4ktoPfG;PEe84+io}r2jESljt(RGHQ#EdC5r$>^*+w zDL(Z~IW_+^j_tcG`YeW3@aynlbzk|#=p}#FBo0M~Ku&#E^s+naYX;`4ZG-7N$KSmS zy>d8~4k9)}?Mb(iVh3y|z$H{xJ^-q_<7V|s?KeNvW(R&qWWDCjEe3{}(Wql3wQO(U zqbiZT`IwZ6nAGB`6MxE*gp|LyHZBOA*llEeyUuJ%WX(~)S&qHV&BQ+3M0VlW#x&o& z5#-F6Kvrxb^BLGhyjIwcH)j}NY5WA>j?@8wGa*o9PQ5cM|!@~#1pJ4rWb%=d~A+~LsXAUb9Bw#7<}s12ccODG4i#pPMOa3B0f3Z7WN!k{RxcBZihnTODgLJTTB`QME`pF8Vx%|8oST~wY7zP zmw4a#I|k_wvowSAmwostlXrn7Bes~F^w+aV{gZ~Rc;+~8TNv5|0HhqS|AeH{*tz^Q z3=o3)&Op5eN@U4CcAePcckX>_?sI=J!&$VMMFw`%_V%v(jlWwn-~X_7Sap2iVH;*3 zU+z11i^D#oLu{mR!>6G3c<|s3CY6fppt90v5Q)YJiz>}AUR`V%>mwTC4VCpY;q$uD zXSJZuX+-QBA}WrmBI*wNnFYJXz+G_gcdq@?A!p+DKI^jdnoFG*mmziLHNOJXck075DZADzpRbnbNnMgMELvSc|I~ z$FV&oMnPO$>ZK;NNr(5X|H{6BXPndIc16#y7@ra^6}FoOxWH5JBWs580T8atpF zr%#$0Cys?~vaC#?2s`EMQVqhOV4bkG1*R7f%?@t(yz$?aN@G=s6g-VU~yTG3p^> zUBSpGTc{~+OFaa9E>(32fb*(QO-Nc|y>X6W(8rCR034W?ikYK+Beba7_s0>4$Q2qw zc?Yy~S3&s^tmX<5T*h|O<4cs#cfJp<7%5zr3LNN2JzDGWy%m>NnU`3qXKUMbY|l6y zOJWh7IGJS)YbGIm?ZJr*mdvV!pNp83Md^z^Ij1UVk1CRhQvr|0QQmsve}L<1r&D%HlIDtG2#(*~x^Mpd2NQDMaqmfD6*Vq#-t`>F5*9z*J>(fw=5QnOW5^{ zCheLjoTvIP!h=f1Q}Rdnp^epCfSCEe;Shw93UkxjkWnkJrrIabm(7KCNaqR{Zw>CMN&Jd*=j zBE}Mn3DQ2ylF>+X$3mK5UWoZV@R6D zSD&;_yLbjQzU>QRm4o5nV%|Azsp!%NjyPDAXUNyj~*;tu?AC1 z4)9^C9Kh%MMtXqWu6k)0J^)LwLB~wzUdI$0b^lj1A}^_M6kAT!HfpTFgYL{Oq@SS3 zE~6@WEX&hmg`oIC3T+4uz!HaY?z(v$p)0ZXiJ%mU5svA|q!fYD6$8OUR)q&d@PM?} zpQa1G50f-q{Hq?fx%681ir+I!H=IVu9~WQr8J3D`4^*8_OgQ&Uwmc#oC6%r1sYxiH zSKvXXkKDD`m?LFg2;tj=b`(}X_Fh!mZb^x%xOcW8tyS;%;!B$C8F$uEnC+pv~?GdrF6~h=H{52KH169V*Bzx_KvVOH#raQ&uV)RU(`>^T`M z*VK7*2vD=D&o)eBN!9k>F`%y~T1}w=TNncgX$Uw+@G$D;kdWU%*GC{}4ozID*En?+E zCfU&EVA91Ch0puEm9D3aPA*;3c+_NIVfhsz?hHz=T(I?wC{pV23RYe%rtlQw6693M z&_g8@&IUY0jC59+)MCKfdnHfnZ!r5Pe#pF}y#IE|uGJKC0mr{r*-G8V4^Ng0+@~39 zrGtR~CQ?*8s8C+lvNO`S9x^o=Cf{l$)MRE(xX)-1W;%~eoGadYx!WpQPQeUmy*fJ+ zIv3N9`!8vemav{gOL061`ByeohgriXF~pJlvd`c8dWZdk!|K9Ig5z%8ixLqRvQuu7 zSNt$uct1x_j_HvE^EJ;>(h#1hi$l6?W!7iJ%ukA5n0D$q>d)1#sM%?ia~=d25vHafL9d`C{(7B@D9;yj zh(u=0lN&aO2&H*<77Kj}SMlETgTi;MRc93g%tkq0)j68q*<83>azn~b)8mP?U%-ib zwo?T;K4`}vCccNMx5q(jKdcA1ypnUIOsY$MMk(^3(`Swq#ao;Tu^&Xe+F0yzP&`t1pix0G z&k)8T%o-~95$+Rh@%QSTEyF*)JQw|h!mV(<7p%R|I#`4Iy&-0#Fn=IqbkgI}bV`}- zyC$ih@n-yX@l7OXl%g0~rzzO&25BNuX&Y_roPI-cLy4dc()_&4(ODbz zA&UmZM75xHddTcP`Otsq8;!O;Aw~UqK5E}1?imNIbmSCUu{?h<62~9FllxJ{fL?Wq zeremJFMVE@g;&!1M~l6?-W~H(WVDXEJ|-eWL3t_KNKa{qJ2Qux+7MWNR`a+yIY$UO zDni$w9IS*${YsE^`cHC4r^j();EQ4W$r|tE>vi6zuDbl>Q5i1C1YcGRc;66w`ubJE z)0gj`d40Bdm~#Vl>Bst1IYa#tT;&t_G|WmD>~si)*Oj5z1omBw z%IMiGXS=LAWsp3{@(Z3e4|_Pf684w2lEW0Qa|1y89s9Q zwBay6NI^uz;u`pOV>Lu^zos=`}@3AmoSoj;GNK*CWQ?;g^+l$1pm;{ zH>G?gX6o+gcPig&-Uri`u1WTC#%tZ&@;(#$*2VhbcJRkRP-D7hWY*y;1XEb<+E)aCXY2;b$`={=(c$}$x-G>&x>6%NUZS^0YhkP9=wRqm}>1Fi( z#;4KePrtk(L0CjA|DTH8-t=`aN8IB7SFu}G-%t%RM65bm*QcxPYu&~>o^8HwyfXU0 z>SfzE=7@3g^Z3i>-@nijSVXNm+nFkMzWn9aoiFxRCmvZmwC?Kou{qmv^~>w7mp{KV zdNYniZMr+7MfAqLpMC1~oyGgx?nt!q#MwXXwl$c0XL3evw*U7{x4>vZS(%|x2jeR^%&Ft%)Bvi?YMSc~VQ0Pc>Uwvphl2rI& z1Vlmtc&3>FdeUxp!iYEM;Qy%DmRjEqrfp3Wf6O>I1^syI>|6iioojEu(fg(G5SY-r z2z%oOHGrnCHDC8+Q}z=w5q#f0N^75J(dc}YUt8$-09wiHk1;VNBQDADfX`I)R$VBf z+w%NtiZL@_Fnd4%&!SNG+I$u@1W>!BgJg~>u4IaVQB$dPhFt%PwYUCi`hVbf-|LMf z9SRPlMHwgv2sjXt5~dO(gb~un=+@C8!a$`&99^OVB_&3Kfipls)TxvO6A@AL!`=7Z zbI-l^_xlGd&UViG{eHfl@z@yo0$HqAesdcR;w0Nk=~G1P>iB*c;nleZpOrj-6IBf0qFy>8EHxy@5OGjO(g^^YI#Fq z!%i8+IJtqT@LshlXcfdbT+Y+7GV}^-s>dfA3IoOs?p|3`rn2H~y@rntqHk$T^z^sf z3)vuUJ>M_WbE|hRTZ|jbeb2iyyGO2aIA6={y^uS%x@C-)50mo5vV{~(w^|u6 z|5L;ivug_Y(-Y>(qZpSg#pYga|Aycvy6nagzemV8RroBd`Ge0NQ>x{~m9UF7Hha|1 z=wy(5L;x3FrnFS)RhKF|qB~(i^iV*PR{0>tw`&Nz6_&2aOTM~|UeY|P1mI9L;GXbf zX`3aoAPX-d2H@D)r0>mq#U{4siGyXK@2z| zEKTvW7L(T0uonwXoggSNZXy8|u$2PPhzyy(j{z8pj0Fg`*<=6^js;675I_V%CGi4b zNqY5e%G1jx`O60qYB7xK9LaNGh-?T_y27 z%6FpCG*69Lar;pH@*_2^A z@Ca;{*o1Y0(6mjU{2hRl`WNmse2^DauRGd!^32ES=6hJ6BUj@q#g4`p#Tz#afrtX`@^BVYgEisM?ITfklErOZP_UW z58al`y}}@slcUh8y*J8_5pH1ED(_U@`j?qpOTOHc<B6*pnolq&vRYIyB*o3)ZMsoG^5x|0>pFxbnr2cug zkDqfMo$?MRsz_|Ginre%>n!6AXzf>#?d-xK6-`yTysk%_P7}XnN(kB-LT8bJw+l~q z>YQzA4eFVx*WO0;!0sfykz6SgF?sdsf=Nf|4a+y$e_rzh%kt78!3zHGVNTrCmM4lB z3XuivI1)hVuZNo4^TfUEk8XP-39r5I{vFU6-F+>|%NwD`{r6u=zhvGwm6iLiKjw9f zsMvz*B*t+q=oYtbT!y-gj9svf{&Db1fk zCvJC5#Z3KJkm;?vz%6b8F|%%hII$*Alb=~u1bc2v^iFl$IrF3?Bq->0U5Ap^&@eyc zb-nZ^o+%(Kfr^uHgyvw33=jR*d%Zp&}L04pd1p)z4OaqVr~u;}|QpD!8Q4fqeH zPCOV<>fI(O_GImTzWlqP3tI_M^OIRuk~8lOB<-kqhq?`zovW2y)q7-7sTP&7yltNvG+u* z>LH`c9%JTSTDnogwiw+dz4Qrz;I~=PWT%5VEQw1;|R`AQ{D4WIP zOBqMkf5x|qT@cH(X}an&wCJR!m!c5ss@`DiL{I;xoPjWh>qfyQ#H_FWjBp`Dl3p1d zM8fD!g#C(y|Bi{s*Fy>@;XCPxEr)HU1lOz}Hm;ckFJR*)=m;+!YL9Oc<*>`wE4Vd2 z?gts`F21ik4E^9yY+NxL?vxdOuQ=sX1ne;vqqmHi;#}f6 zAXZ7JcAr?3MEaN>sstMS`HJPC`Vh?qb7OvHvMO1l#&G9^h!!c_j~hwt^yo}K@4UVX1W71QBjWz^u|y6KOE(sC=N)*q4$fKu1;ZP*DK<&l2cQ2YmS_wDu~6j*1$rf66>>0l2Y%$`^X zDz=nlm3fevW;~E>dZ_Aes3HgALIfXRCTY^Ih%!MmKFpGm{kViIS1`U^4n~;4~h!0zE*l=1tib4^)JQH`5;jO?lm8Jg@6wc7{@%^ARF#S zgGF#L@7TCSx{$+f@ERTUn1?&wh4kPWM=-GNbLdB0taTUsL_2hbgefNAHYf->0riGg zJ4l3o+kjTkajOj6b3(8m8}o&YdnbUqF(CUNpbL1|wH+?PgDtc|!oMba0ReEad1m6dWC?0%}4zZ7y+B0K2H4#TZm3UJR;(L zP;fcY4q6Y;?maLn2QBc|-_uY(7`4NZA;GgD!tobHT`G}Jp+RiSV>00}MaV-@WN%FP z19Phn7FJ7}_ESuZ?X{+5g5M$?)tR0DpEj5pyVj%A7n%j5BWa} zOYrGr{2(3Tz!jh-g$g*BA4KGT-MAGVrh+cCLcm3L!9Njk(yy^!7a1-182PHuM#WGw#}PxD!Hv z3(UR(rllVf_ZmP9ng=TZ*za`I%|+M*4@YO7S>LjM&&Apid-WVp-w7x?hTKbfjTx=C zfCQS`am3aCG58ygT2Kdj5HJGocc2T`$%iJ<`}|lHjwY!OYap}CL3Hd zf6L&%fJ~hpoK;u;3Omi60=v)zKc$pEr5=@d-|<;IL`1zfb(gT*XJIM>k!{UzZLkM2 zz_Gj0w=K^P|ANa4;ccs2xS|<}PiqoTYZTOu1bk%|>N^{0w?n~o0$45|+R4Tt z0|vZl10e+b6^n>Gc5S>2K9&d{*#v1f!WRdTi1Z)h;zK7@_= z0ic_+YicJR*zH1vP!L=u?mKWT-D0?#yf}AXGEe z;dMDpO+7Fd=5<#JLi$E6{4C0weLuVdes<>)4#@YkJTpEzg-LV-pfYw|bF zUf(LIIouoQn(i4qCX5<{muQ(YHpM5t(Ch8kJy8TyF=2RmKA`q+$WHW`QZ){dRPMZj zT;*anIdI$BnJI&A=dZ1=c!)b^aJ>wh71Dh>0Ct6hkD|dJQgF7lPi!v1hPPqPB&-0+ z=t6`qlduI8gaA#Mbp)}&YWCu2+A%SYn)RJI=nWz~god4F!MRIgJyuRB%%@wAo{o>) z>tkaB=|cs~W}UL@C0DOI({it@*k2&vNlBg^BCqLo|ut zDBc4)5jtgt7oe6Cb{0F2I$cf|dy!-(XFhhG&R(atFVTLN`R6~sH(9nBn!DjgbQh}FD~IZK2$^$Q6ONyonwxbJq@ee%{I9o0+0 zZO~E945x7)c*|v+(fsTx4IjPJ>M=`F}O3K9~qcU9n4!`NFq0irt1IW zuFn_NbE%`VKUmEgf!M-c{m5+PRMV}_ZoY_Jj$T^ctn4bWG$$b8v_V$i9_8m9Xj@Q0Q!=8yd zvq$Z;#OvN55^94#5y7px`3K77kJ=I8gZ!pXoWAPQ=k0P`Td+A@#W51dSKwyyd4dt>-4 z`+i!@yLq}G0j2?Wro&V;khlKWtJuMA>OfA%z`WVlPX02ej)A+)?BzB@2NEl;!N4_Aqs?)AV%k4b$lB$safg+tKCt8ZHfmA*vCOcxGVSF1ZteU zC-`#!l{`N`GVrZ@rkC<_m_Gk*a0#@dd^C1?-b;{qOCGh}b7VEDvokb5XV}|y*1iA<4nJ-Ie8zucQ zJm&kTOvf&#%ewV0E-IC-%yC%6{J)K8VX;^V}x16ZQTovVqUT>Oy@I>Vh zFB7{(B;QM>Yxc&*c@|oTAM~i`rBEGJqz|U22dZtK`&uHI`%n3Tsp1_E1+)IEvELWg zK8|OLC+Kd^td*!(3$J$#rJkUrUUo3>Ss_%JV&tKT|iS(+hR27f@N6nlm*TO`}f@O&q*f#%Cz;ZW-9rW+m;UN7DN* z9~)TgVKxu_@dQK%*ACU(%X)&*>}ThyA1sOou`~Kt8c7EyC{3nmGwfS!kJK-Hg*`NU zWZm{a^%z?lr%5KX)x0&BI8-K6{+U=#aj{bQC70u0m6O=ze|XQu4GV;pf>!pe+Hg~~ z7WJ`-s!5Gi?TO|abv5DjU;hq0)3`my4-$@ncMqX%SHwP1OkcA4yidW!P4w{^+Xkgq zV{S&#g#HN3MM_ttRb_P$L!?t~Q|(D@Q)5)^)ohC4?aB)m5o07Yfmy*%OsjGg{TZuf0-dxa&jaP{oF6k7}n? z^8@wAO;_6LY(6|wKe!SUmZ!IP=y}c6EUwy}zaPX7Or>hdSlo1Z-e_f z>b+0)b=)uWCmp`?hp$L~3f+H0W3Y|gu3IY;lsI%$!ECGQvShh6Qt4lKy#KUg%-2(n zm!h6mDvtCgkf!zxt*h&44*GNMU>h~- z^kYTmik{@yJ>VJ21|7<{oqCe`JGZ5f+Fo(t@lIdb+mGrU&n7g!Xgu>;lKS#11)ZA9 zO7v0uS%o8Zkn2qb>UWj+qk>r$Omp8a%MIPl=RKSkif_qF!_&@i>p1=&a5OtOW5wl*z$AJ*8QQ+0 zUQ};-qJEvYtP$ag*t4K)d{_qxO1QVm*mTreS{xB&!|xB@z5nN4^VdH*-$(AngCAe} z`gZl!a%X&^(XA(c-~)K?giK3GL*1?ANZ7BKQ8Ei5#;8^x`XDru;)&S3}(;3vrNs&dzv1rd*!#aON@mH z{we=vE4Fv9AC;NmW*mkRaX&K+wttTwL&>CxNa>m7-BaJ2nPsL370xyRcm&&xr=mAY z%oNS5avW?v5Zflq4uDyg3iwr$?Rw@13Qt|~((smPDH%I{PZ+pF-YbK6I&$pA)4Z4w zZ@HgcCyY0r=3j&^$X2S3nn;Lbdk241$h9yt_l2VO2{x)^Y4isE5uy;YAHLO5%>}loOu|i|>yt9sm(i z#o3&aaWh}-Yuz@!y3b3Wr1>6Fs*S@N~Q!=QqYm&U5n0(diRjv9tl5O|{PS#3CuB5M`DUOdHS)E6kwBKLl-0gEeF3GWf?sGL6;fKdh*(z!p%Wda=B&onvp&)r2P zeQS0F1)ap*0xE|Rn}j{o&x|=vx8_HE-N~mDAjAnVMDypsy-ze@_HInMiSD?1OMUp~%4xahd&bEb!+ zEBWc~a^7F=YDZeqn`8IK@nn%r7z+<=0+Iq-mW_-zOs9bngm`BdYPHKqXQdh-etzty`6DbGN8m z^%Y2n&IF0EJEo#mBW_3a1)asuy<{BNXc4~QERTGTIGQzxGXMc1<-8~LaK>(Tw$J6O zv~F{^RnbCr$oZIr*R~A5W@ediNJ7GE@5>0_L&0ipjq7XUmCfV1yvI6UXedB$Ty3T| zRhBsTZfEn?9f6CzlOj3KL!5{m{FfB;A8xyP)HoV*fn9JkleX88tbV|Tdrp-szgLqu zi0Qxr5fRt#ez;&N+v~QjxJ7#b@Y7Vf=EDm^UEi8hSm47$YqEa<9$**C{hy7!C-MDI zbl325Q-O0LR4N)Eqnh|PT1iriSY)N{V;RFJ@GaI`#e-@J?+-O>5x z+ewRrd3~=Gc!OZ7?A?SYy)`q}Mt0t~$t{=S^WK-HW``n-tC1*m85U8Vfb#ooRx$Jy(7QU6=i zfMA~7_|6u;epA(^dzKk7bc;Q$U`FvWy2fF})SJQ5X2Cs!bl zJ++M{Chgx|--Dm0Lx9J=mSAm2Y#Q%S*6RuR<<);VG#M__G=Z=JA0*pP-rW}TM}f^` zokWwJIU*k{{4fsLA)?Cb+WeIy?IVMHKN$(cL9Z%J0R21d#J?c2YW}@nu&5oS2Jv0M zBM?LVD!|TAY3Ln#BkB^YK*8GW&f>)hWgtZbi7Th!0FQDD2uy0ok>FuhK5ic7k7P>% zLs8@Qro?_FFAoh3Fd!WuD+bR1dT9>F1e5AcSL_U*!jb20yoY%NEjY{+#oYigoPA?U zU&?qCmQsrRF8|!?dPyvsNd>>(fR!Smw-(VsOto`GBEHhz&c2Uom23ewC4yL3h7Q-+ z&i*IVJ*4Dm4LMFfaHT9T3c_$W->uJ-uq)8d@C0~i#bl>arOGDTs#3|_A{{$VGfHd0 z{No}{$vI7AzuZ80rN`>es}4@x=>Z<>&(L7oWN9mZ>e)}0s_u!WKQ|QvlmfF|8$QxS zndejf%s5a2JjLzPaf_c?Pegf7CWp2hk+AH+*}oL^SX95}>{}_VY~mCA5Rcer-?Xp8 zT%z%LZD8AdNA2F#o?FMPb)p(Qy?R_;);q;%!H=Tp0BmSI?*-jN=}54cIKbYUMP_@6 zsZ!Hj9;)9($C+dtMQ^yJJy2-tJ;eeZx!ucepKE?@?G6CpPtep#`==+@kcYyJr6PG~ z(ZeJ3k;bvOb*hKk&(WhsKMq2KT^Rmf(8tcU%n3nG9v_w@8edy4KGVL_lAZ}p`fmTy z`||dv@ow&2zysVz^Pdk-h(2cG7wa5)EU}N|Pzd{^z9toX_a7LA<`2JY=r+Ib8mkd} z>PvOm-W%MPvU0I+DQVSB-Gv^#9LVfgQ{Mc7*HxDehjYuTH7j=4-sttUZW7LRpo|1UJ`HWfJ00%o z^HE4UBX{D!rcS=bt#rxXu8o-LbXlYG3TxIbJla-cO4gvrDhY|)L4!47MpB6xfi@p? zV@$Jca^2*e9jTNUg1EVl=kB`}B8@5J{jng|3{$EZmBC!sQ50EOIc~z^-m}wR3k?g) zJV;nikreDqxd?pc3a@>`Ssaq|D;JA?$|V|;D?^OunbjT6P?mjv=@3x>R(P2aV`GLI zyK5m)u7w8(mEvEv;Ez6-iLX!JBAzBcmb74}X%>Raoao7D_-U|Jauza}YhrFjhaFM# zCboAYz2RQz>Lr5y7cUvjcg&271>>A}@+K=CYPT;7xqEVaE9(lAQ_~>8jq<$@Is#4{ zzD7=lIKzR}=aL!#Z`TiM=D}Of;Qvd*&9gnkghY1mhPla-%j}xtj|A&yVkgTTMOBRs zXf}Z{=&h_1yRngmcn{nQX#edtTL3k*Um%1RDg#>?!U@Y4?QXHuC=q% zrLKlqKp8S>AJQWM2F^?Gn8e5_?8omPvUZ0&x*)I+Udlr48E1S<>Z&aW86nrfo^Xvr ze^9N$BW*5WBa$CZ3EeGUh76F$Wh`RRF*~;gLtvf{MAOWKX)T_4M+`>YzfzgTdXq83 zk4*KUZRt^B?UoTph238FvWUu#CtV*b=^~yo({u0GcNrm#dIn$DO!1qkkA}#*)2gSS zVJC=laG-;|H-=|IoTj9T`U{qyz05EVbH>$1xHSgUXUDRl2#pwZrmYARtrR<$WOPb( zKih=k(dLk8qbB@2;H9I;DTj4iwPPu<02K2FFj=Bk9N&FvA1<#jbH;>M_j5>=PJDg{ zU^HBA009_di|>FgfvT2005-{xc@$|c*nJff-yFUK;+71692I629b%X6cS#c}EB_0VD&}dQOyAK=p`U%ea9uJ;@W7=V+!@%8e;Jkc6RNBXJ=C?7>J z)~Y|JAkvwt7S2Ae%v3gopF;zF+_s(Bml_*@2|`|KLxJ`7i^UV$Nuf6O@gp=ov8)q$ z!th!07fb1))8OO`okvaSV}BDABJ8gvf@s*Fa+BF&#&D#(ME;`08ZYJFX#y(zTxw#8 z!qnag5uwxI@-C$X(H#?AleD^5@?VsYu&`yTDzRF{SD!IX7N){g3uy=S!%dBo$ z6tNVU%GZL=I*wR4(^DEK7p+bm3f`TCc32cbujbv2x{|ZY3flR;aaF^fwvb| zD6}^{r+mKfy!2j$US1JYepS?+U;pFP)y@P}LpVYfqzP9qtA%8XbBT9aq_ttcJ4k7HpUm`nm-i?ffkj7Z zxSi2U7WT>skKB{&I9q%XTB#zZt@ScZMDohTkZO%&C9v-}L4hnD)P8aSZ1DWKUiz}$ z`*y<)b(jSGNo#OFg1l1WMB5aI9ZYb6E>bI$-3bg(D>Z+zQ6EsI?}W-L zL%x;}7LWU1M&@U-PW{qD7Vz@`{`vWz=b@l{WLW-hDb$}>=D!ufES$Nfm(Q11yuf4b z34F}M;!pDa?bECzT*;@=BR ze_y5iCXmTjOd27_iI9VIh%qb9n0H0|`$5(B#YipvJpGf{%0h^6q3`cvd@_^_Ko3)( zV$LN8nb!bz$v)OKy%&;Ku9TKd3JYe)^av0#0ph$D(q5`#fhrZz((lnHm|h`h{5W`+ zf4wDJ{QY(L^Mqr_U>9`w(-Pfk|%jccT$U zPs{H_3T^V{yWckn1}sRAs7p3W^pp@Z%mWv%qxaNaQ#l?g3>A}x9wb-oimy^_DwQa! z(g+lW2*HeIs&)wz{$F|o2WW!SK%@U(JtF$Q$g1a{GVcG1tp4A6O4H>|6zZdgt8So@q3 z?f(~9ePZd^7B6evaclCld7YHb|Id-tU7KK9E?Lw2-YYmEBdh43_t1o#aYjeT9MJDC z{A$~}zlXhM|Pp5`$^iR}c&UJdFS# za=ysdst|d=6a`p(fP)3cE@{~T1#U_Z7{T4t9L$B7 zi(x(q<(#r)D%)po?UyjRS>%n{nmcv{Td21Ui+dZPU~v^W@U)0g(Zj zjPt9;m0x8B^v}B9r2Inp4~X8imy5-D=Z3wcCCGLR-8^t_?Um%azc9Rq#xszKB4*?R zxoh|6Wg^{Qc(>j>9WyuJ1Lefw1GLeBWD)~_xjx!1mQ-OXzWKNK{T5MI_?asLai zwq+b6OHWY%I_%G(Sb|h+2H7{QprWzXw6Ezts^0nr&l*=7BZa<_ANGj0@79GOpF4Nh zYuK25JAXIs4%}bcDlWcw{m-u_7r%d2bN?vwQcgRIXt+9P^_gAB89~t*5HXN+?9$Qy zl9GL=pS^;}TeOUq-U*TA5Y&YfAS?2VyPw__`QY1mXncQ2Xu`G=CMu#e=(lfZ)Mv@i z8gaNLa@-v`_&9Dy8Lsyk>l1&`S|OyUVI;-vbGCV|yI7^_ae(Q0cvclMGF^%&a--T4Nk!Rwr5^XJ{ z)2dVfFoD8AzO)y^iaDHAVDMl>zOUYW=f`33Kgt(vRFMAcspz&JH&)$LbOHck+?)WF zTwZVd7~78wSoFu(*J8%N@JVu>IyNmqb3 z<#?iq&XW?GZN(=U5Fv^)Sdj=S8Pxz`j?4{vMFR~=!GTT>=Vm%O*f%e?1fO#SUhd3o zcHG`ihG1SZ;FQhID}u;s@|W2W+^?=7Scrcy`t_UWqPChFw|uZ%U`*W+F9qhn04BUw z$ZVfgLGm7TN-|f@sTEEldrb$;ajS~DR~-%{E?d9*Gxw{1>v5?6E|%Nxw=o_6P2aho zV*Tp3?cqKs=QjBRV~>QArg!u>9(BaY9JqAq?}z3RWuu@a&5Io%W&g-(QO?SCSx2W4>fJc`6sZ;I66(R z?588qz(_lyq7P<;gwlhIoPRnJ@#_f+TRXoy?bs)xcPMeu znsMk$^y1%h2mR#F{(#o#f{;KDH#P5LUT67$OGe1x8JTF52p z*2pinwc>mMd4V*(PlJz$15)9_EGQ@ahKtA#M>%@_5d{T)5VX3pwQTWg`eM@3p<;=T z+B03y-OQ9=|BroxMiHJT`eWae&ELzD?S3kLF(4n!0o0fX;sdvU;jvBWC&(~eQxDqW z5Lxa#Pd6}0F0yJbFg|5Y9zUEH`MbgxfBmsWt?agMgx!tLj!qA-ZSHU9d|mI3Ep8ob zlTS9{e3S|TXBZL+-p&tn020jcgQ_OkCn-bl=wEktBD!U_-xx@MlXUF|;OkcgCi0b13Fb+denfjvE^Yb)AMwyCZ1?kW)woABPT?9l35&CfY zMSnnwl2q&m8i;17NrsaBz@K*7=>#P5^0fO+2RfEZKf0MI!U8Y;#K>h`&?^Fmv+<=| z2(!*rp(7s2@f0TlI3fVy0g+CiY>BK-#qe!Ls)0pNb^2K?AkEYw%^c}z@#^B(xSQ=* z0)&Pup#d^XY#$t-z{J*Z>8a-u8y2Hqso=+%kZ3w|19&0b}UA%5u47&=deI(OfXqG|275E-4Wz9p05VrvjEHz4X3S&`P_glr{!~& zoF9x`GSUMbl}bBCO*<_NarVot=x>1P+&i9lBA~q~Zm^ zH*BYi&|;SWOalw#&kW6B2+dmrq<+G-5HUk+y2GqcH6LT<40%$5GMQ<{#UU_W&L<^MI1P7?0#XhD z0*IgvGER8T`&HICRxN0qjh7RO@h5;lOzZ*~$)})o8E}m_j5_hs87dRv51I8tTfy-Q z%&g2tkT_pxosZbH1gx{L{$#H!FlIT~YeNP9!4l{v;&oXdJ`0N;_Z9|_+f1Yy*L492 zhcU7192D9Ka>LSHjZ>Y)cF$2luk!#2AbwgJ6wSt`Q$QJhWkvyj4l`7TTV-g45+)+n ziI}*ou(E~LTuu|c!&o-S`&H>Rx zybKi-&86$I(d!&U?&lnEqM{Ap*F!6tdzlYsf!7IW362N88I(EdakD+OPqnB-J@-sOQH794<~b0f z46U}q1o|g~w#foyb&_1Cn}Q=%gwV#xh;ab}^@>&h&~rD`B^sv+otrF#tfL3c9{4&N}6eUWzzFXqyHpPy?a-5RR`aIb`=GB6=E; z+yZ1C)@yt*6SXtj29^qwuR@!gTRBFqFo0q|ioCzX)9+8cF*$gy>8y+_RUPY z*XSq`2txw-lXJdFf=bAphZ?cjB*%3g+FZH~Oaw_VLbFK~>jWV8T+7@}sY^yJc9sCr zD{7r(BQEk0*?fd96THn8GAMJmRHrWJpsfOdSB+HSF7!YDybIPbQJGilogs1C2tHpZ z#jze0#98MF9pdFwTcKn~LhEFtK9?>`h!!l{_!FCjFl`kNsl?apJNIug4~OnUvq}xb z*Xq(NJ3SykLmREIpN0#kd+*My`>!zqhN$W0Fyw+DA^^TzF#F7h9%hVuqGMAXA!n$N zoNk0L8FZ)=oompSs}9NmT12?0wAl+3_1agP-Wh(O;yg5;3Fe^udm6FzYSb(?{vw+$ zL(Y5aANwz-K2JUMhzw`pn!5x|C{G9jrnU5!!c%q}>i_JkrIcZfC=e+E`r&M6D-)7h z2Re&s-DTK^ebtRw&dqa45dkpWS=r0&Lx%-xt4)no!nieE7n3jU2j{y(FAd-Ba&Pr_ z`>f-8Rk`^IfP8xbbwCx(XN>T9_`ftPn!({PQT=Q@2e|b-2ok`n?Pk;Y%*U9lP*I__ zB{C`txV%IwG@S94r6w&Q@k?yfb-qx)BdCs*bd`(hC;ETN63U^K>XPf{Xl+vsbx)Xx z8g|fy&mBu$cs>QG^s0tWc8@H(0_TwxeyD!u;e-7e;+BpOl7`Ft46wvT+r$iO7S_%eB|IHXGdc+n z(J|XV%vAzni6lf>#&7fS+iW11g8~9@FPcGMhLG(fa+Z}0r{TKU4Nue&SQP|N`s@z> ziJ`7@MeLLe4~Qle_v)hI&fMO*WE>OjG!azF4XLE0HG&jbe|Hwi3_x-rdEaE0%*8Gy+9HF zMvb872QEFgq`GP)`eGTOt<6CJ{P^kV*gc(>xaI&sotmtiAw>kF7$ZA8{0<+F2H@Ao z{(mh%2X;^;jBde=9Tau;Wl9ts&bjz^oFPLi98~g}{u6|L36!to-tMEfHdo)7hzz*} zN_Fzu-RU{71aKNFmUe;k&4FEs6HDd5iCKVZ>NQ*D8+Hqnn@-nm0DpT^_1G#F!b>q; zO6i++JvHW;pq{!Q`?lKrd2!S0#_M94r&*k(*WyKSZNOy`H#2Y4IixY>!P)S3rAW0e z-Z-*=R|PmEy+L#Dfyp3rPVu$an1(p!C#B3!i{3|dLp#m}Ass(V9}bj>&Zt%f!@3s*fKF-g4d>Z9;H72)m4sznc8>_dFY$Cvj&i3q2 zxQ6VoKXA?^YjJD&PWt)R5dYV^XI%G~pWWR_%~r}JV1q9>0y0Iu*H!@HmiyDC;L^>g z7x0k3wO>F}VQRGgEf)}qM5W0wT&F&Czj<&<7-Gpr{%uGwe(9?|PED5JEk562QV}{>=23j?39b8Hph($v=Jhh|t%GjzQVSzs}9XX4Qr{ z9!RFH2EZh}xDu`jpMpb*F4q0@AAg)8<8<~&ZWvY-$WsDTHNUhT2t+9tf!}|F>|wLwaqJGeC5FY_Tlp# zA2gkU(g=Q82gF5Y8hh$vjQXMiGU{B5vSoII^hu!2+Y7RsJQ^AFZ>9J^_B-c_aHj!J z+N7J*=I<*G9_pJuJ4&?2IFGdSkhM{7iG1p0tecwCV$Bk0A6NYJ%aznSF>3dzH{0Ed zqP%)9Fu`oP7-PgT?~O$-khcd&&3N~4-^$mhpa0a)9>xD*Y5uV(ime?8X77l*Tp!Jd z>i;q!z8&EYB;nJ}YC+cOK3!T~5BW6gQ6FPl_uKqi%cA*oY(x(Mrd8LyTW~HZVC-U~66`}ic$mz&5 z!&`h1dD*tYDCs|_jJ+yXI!|FiiV&`loQ*@WA}PeC=XJQ+!g7CdB@C?-ufb!ly?X^i z^DZZKFp^#1A&`YE%3ja(QtXO(;w$?*!yxfPJ#Nzf8(vEBkejk9R<6Vy%rMT_E!~D! zPlf*mu~)cN$4iYOW@bz4_dKh$4282xe+154kf>};&VXm4WOu{}YDbDR5N~!Kr2G?r z9i&KU8(jyICi26VO7KQUuNt&3J)S<;{9k$BkLYi2x-b5cllvR{{box-zFg^#N{bSu zfy_O77&uuCpOLIn3u9Pd+k2ksIRJnZ*$|uo4P*lKrX>&llS|_dLT3K7+(Lvu#~y1_ zCi>k>nozscF{jKEfTNswz;W?2Zo&*PeO`W!IKb56{PzbZdcvh0xyb|nHm-*b>;f%3Gl%O@8OUIs zzH;ijc*vzG&8I=&U&LF_nfDQB!pvs6B$T zJZyL3uNV7}^SLCM7f;Q1{fttvDz2445a|;ENB5lP$is=fqxmZKkp>`D?Z-Y>L4ePS zU53;3@8f$eER1GATz0;X-*AX`RhBX+{+)T#=fSf8uHT-2cgzGg4pgSdz;3ovwn|-> z*Cov{sW%hM<3-fn4Y0U+8`Pr2eCYO<*oOR2)fAQJgu=>Ot{-Yk_t^Q!2Duw1e2hyD z9Sf|xRTNis$Nvxi2wla$D;W+L(wQc337JP-JAuu2#$28=J8y5e-WgoXP8)v}B?*LL z;ha>Q5AFGK*Mc}kkz-bm43He(~LXTq&Pm5%(f%kfKu1kKT^PM5s%)m@jaP_ z;gc%$z2OfvL+5=S4=j}jJxKy9L_R(C&Qk?H24_KrVye7Uru}G@8oO62_1%NOW5vro z5ieV?mG>8Sqj-JOjS#U&Q&4(-U%;04m{z87{HOZP#E36_{~0D!nC~${9diH?#VJ0Z zcV9bTPX+MXD8HLG+s>0nKR=xFuiT14sTjsdXixCAGES|K3BDvUSc1zyn9;!EU1Yc} zHyMU8g(~R{qNa!_=U*=4PER^AgG5f(q$|rAWQtEi*~S)os5UDX4<% zj49wfy2Nt^*BE9d{S@f5V9N3|~vWs*C@V52JUW^I+80RCQkbPpWhHcmZ z05C9qdKBp5WM`WBD2R7knCm_($eH&RyX!QnK2#5N0vH;n=>~$Asq8@}OS$sEl2Q#C zYp=#%``3-VRrCa1X&ABN$BrAWhUL##?fyYwr7d8S5&2MGQo6p4r}^o^@kBfIL}4*! zF@=zp1P(--)zcOWzO1q1bFTkwOsXOh1b}T)1gyEKaq6&4B*4K%^DJFEp5ItY^D|u3 zw~L?oKbSkusHWF#-T%`o5PFBu1Zjq%6j2hINDWd&gdm70#eg7)Erq5a1OWv_4N3fqquRFxwD`&D*rreZ{+GzqlG%15UH9OGo@XH?S!RLN>Q*VCWk-;GOve|;E3AtCTVQ=OU#{B%*#%0{b#AN%#Vs3Kk68x^t=eVXv2g8GB z!9(u{s9|i~wJTR4pibkYmyc#v6s7gZddJWB*WR6NeD(NG-*IU+d;FFg9YzlX#g1Yq zzzNC$ozB>ktB)w~zCM)N%7Fh_!i=IKh$-VrWo~bu?YFVi6TvIa2cDk2aJZ;T*U7}w z1}mdw*`;LSOn{3ltJrai9t)?{i_Eo4Th`eqTeVnG1VOlp{A9oFk5!qvkNi|Ei^+k! zhR*zQoA>m6lY{=f@mHGK4;v><4u!x^{6k*4IyrpA>BO~{?GZa#CP$8^pD1}RTGoFx z`SiantA9?8{>!o&X(vC$&u~h(E!z?4WHI$DH$9~W;3qk0yP^-tzncoZ3g3SW4>1?dw%t zha0JH4vx|OWBwTh#Dew!q<_ypMU@1uE#oHOvj0K$oQb5|7R^7!VyDWG-WS10B^hTv zg~DVhde_Jxhtt!M;wm@=08gi11mH3g#gy{VEOTvVT(KQq34jlSBG>OPJA_8dT+?HL zUp%%0tU=BYZqAE)@?s(ohEK2ocI!K20vn_|ajyibKS*Mi*(3vcV^!>v)o)uuqVyba zrjAhHSKv`Tg=awFa*rV$2y7DMp0MAMLkS%Xdi_@yMJ}> zJz7AN1xJ2zEep-JafAmB<^QGNrF5!H-+^2@5`^CQ>zL&6Yg%@fROHN8F0pPiXiAdM z{!P07v7eslR}fWuC}27WELm4M`KNB@&=UZS)fS9zaIbH?FH-}-M8kBL(gY51HlxR! zW%t`LA)jdOOQxeB3S^>K_R0QjsS zeuK(DsIrGZ2H$@sPv}A930xvVRb;_N0PBxp5r)uVFan{QP2i&M(S&HJ zH=Itf07{%a&W6hW1FE*v3Lt*7r|NoDK3dXy!AYW29R)8CFl1fFws$bWJ?KI2tvjvv z$l8?fN8@jm4&;88H2<-dpML4!h?j99)+K(M`LHm?#1ZitBafbbW?+k!vOiO}`1fR^^v zh=QlgXV?P5OW##Q=JAj_ep>O0dR86P_3h+rI_(Tb z!kJPJGq=y*5Tq-aUT{pRv&-)%rf&i9v(K;JQN$1qdtXH6NkM0!8FOjd8v1j+`OnpF zjFp+|Mv8?5&WBkzccLr!(Ci0X&EvyUrJW7KtW}8ki`2vFe>I>}45MT-TSA56*D#@*PV%PDZ3MANQb1I6J9qe5zbHJ0v5p_tlmUtODaK^_!VT zTq{qcX&_0+qON7|OrFOr>%{#BPfU?7bTOsQ2p}3rp3tdH;^^*|mA|QdrB`;m?6aIj zdnWUI??3eQNMET9`P2h0i6CEn+;hp3byUjK0>UsRR#N{uCNpn@i9 zDlQoMS$$h^$5)Nu1yoTYIN}yNV93fmp=#}L_+PxNT=%;AMxG&HE{db6$EJHCW5VfM zAkp7r#~O3@)=66%;8v~774O5QtanEQY_5!7Dva9tLcE!#(0Zczw_Z=sSwh;@fZ2zB zMc}vxJ8|z#M-W*7T>R4NcxAew+K&eeVkfSYk(_jAh|7d6?hK)cK_x!vIJ+8u{WdLq`xFkM#o@Ad_pdrF5?G-eOU;upQ|FqLnI^1{efoP3 z9L&H1-!&(u-_g6g4n{d@vx76Nkg`!l~;ImQja`z|8q4V7NCO401-<3 zzvy*18j~T)GbaA$YJ%ehC>j|}rgCXnczY4Ou2Gw&0+yDMq2g%p{C`5&B(u3P`lxG+ zKQk&sA#6vc^jalk2iYPkP+ET_+#V~k0*NW{xWqzPbi|FprYN^d$QE?Mho-v8Oe+-! zXOIc_8uG~epicj|yo2M3FDY*<2rq~TF!G^dzyH0^`u$pC1h$=E+eN0O8Ux`Q8JN!2c;PTui$}C)nXg$AWgDx zB)_T@p1Ifo3J_Dd-uf*QWnQDRM7#Sz$%EIuIY1}bL0<#N2ODQP{sNldjq$_Bh{QBB zRsjg$l5A;Qup)Pc_@QY}5V)%26)g_pOYVaHI?LysLy#$jv?m86DW_2hMZ|bn!_;8$ z4IYGR`)XwmJ;4xuHJJI)S3t`)zTF%ebZ!YmQ-}Nzbzza+UR_`u`s?VqINVy83b6Dn zL(K*)^IpS74Ga|e%viocsZp&;iK41PEc*3ne$@7i5mz*SPX=bMs!{J7>=?Z`<`vUra<`a>IeV*0n%GLq`@OQbP!$tT z=n}Kw20laIw@c8wJ5kU2)I4=^=u^u~I_7ih+!a^jHolzxlGt7Y69vC1VX=)o`U|){yyA5!Db&IJuKcXQ#ler-sd!&lDL4G;4$9756 z0}18;u?wpPIM)i@_9k3&kRl-yzyJw4OE2kDY76O!0TV>6+#unegd^aUdaniTV*nT~ z;UPCdq{hTZmUV{SQljN*9u^qpcA?Bt&#qn=kV>dSXm=Mk5>2Mr_MKRrY=k1fCU1Xf zJsp#{x98C2GpeKsQDgBg)ey(&K&pO>m2^@7adG|$IQtTP`))gUqLvKw)#?1NJm@)H zP?}`uL^V_{FEj5%2S#{e!Qw)Q30d{TFg-&K=bmYWh8-rKTGYxTo5}k4Xu$4b57k9qLe)X^ckZ}(7ITG){B)t`)nNa%%)o#QQdVr6{Sf1bVC3Bk~r{fR8 z*&da0tdEPTXwJxxN>m0*^jlokr5K_71YGd2Q0$yr7{2z=E8<22&Crd5#}v^kuTdne z0SaR0k~(P5b5lS>3By=Ifc4OAaY*eWn=Cd_uOB2g6$KKpYs_OgfG0LAHR6;aYV%<; zaKw$JK25fVzG=9KGSyO$4kDWUUd@d#WXprMrpX&W--W;U?l4d^2~G~xKfO9H(|55{ zOx2DQMSiBVv0Na_CQ3d%m8UJ;N|cHql)_T)Wd9cQttm-4GuFdozgl`pze#H{p7Gj> zUk3n}RzSV(S@5`?w(`Nn+{M4_F>!8o^J>*Hx=1?0Mo3Gg`XOE&G}Sz8wolXE%4MJC zd$d2_4izi_NmRvvez=b73|aC5#NVmEo{&IzM7ox9hfx92o!H@w`-c_oYiQb9sKa6MxKPBjr zlLgS=bF`b^eYV(s-s){-<*F9UT3UDbBT87ee(Yg^yYlQ!C@%ati(zyJ)G%`J;l3a_k&ikqa0-eYaF8| zNFTx^cW(c&ETJgWy}cV5xi1Xt&Y(#Knier-bfvlyA4mkQL24`8}wbj13*_q8lPjYztdIG<*Vh|BTj-A~?2#6?VBM)`ii`gS+LjCg^mZp1o5gf`J&7t_ zMPF$*Mg0dFHAYli$^qN|DJ~p54+@jo5=j;{V;O*wbtZ-Lcc1m&dv6NLQsfCBR76t6 z$v6vuvVSFfT9Jb_8PV|ojl`E5gqr`8@TsK>tmUBGyvbhAD#>@A7vMuFm*`^qA^|*> z2t74vs4$@~DlYed8W$oIp92H*fyhkEipUBhH*@>VkcHkYB?T2Bh>$FJOqXm%oAFy3 z!dPp^P)M?G@B!f6b6Pb>q8^>xsOeDkv-QF9#Xdp+ABFEzqx(MtgtDa!?o_kq#~m^I zUkS>rV$s=J{8yjn``+Ij+%g(}Nks;EC?!IrT3@UzFY0ZrZWkCXEn~BJ^#I8}Z5kdA zAwTK(>xE^P92H5N{RMg^rD927sS(fr2{;v|CoQaKPMeoD&ASXTVz0l=u zI$~qv%joX6D1y>?2~&7psy5>tKND<3n@v|S#Sx(!J$i>On4b(k0242{Y6p`vB=B}C z{nonWe|tZf36t^inZGRGlq!|8fnq7Ml#DsRsXb1#cL%afmfsVRHq|88CeiLlq0&#S zci`=OB1DqPAlb+K2JxlBtUg%TzQNxkdLoq_nk^Ej432$GTl9Uucw96>wVOi4;^PkBTHgNDA8Q$RvLSP! z(uNSkrphrL8|6bURvsWl^I-sa)opEHOJ(-jpnl8n+K_P{_RFwNAD@FY8>V)O{kdji zFXb7;qjL?k=@m5?Xj{dZVZV=Ejsm4&J4X$R6)M?OVAIu3>OT!Z41+mb*(yy{?F?jEHnc6oYuu<;(Qm_!p0pZ{sK(yQ@nCj76T;M z&Vm+5v^Q?EiU-UdJbzbr`=Ra7*TxO-PK<&FQtcMoqh?*i^OS8%er?`?7p@dG%zOj!&s% z&Q>ZO8yr9nwBj&I8w&5ns7|%Cy7(Iqp4ZxeK6;-OWXG7#9x$YUg7#Ah%%0D14dp=B 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 PluginDescriptor IdeState descriptor plId = (defaultPluginDescriptor plId) @@ -52,20 +48,20 @@ toGADTSyntaxCommandId = "GADT.toGADT" -- | A command replaces H98 data decl with GADT decl in place toGADTCommand :: PluginId -> CommandFunction IdeState ToGADTParams -toGADTCommand _ state ToGADTParams{..} = pluginResponse $ do +toGADTCommand pId@(PluginId pId') state ToGADTParams{..} = pluginResponse $ do nfp <- getNormalizedFilePath uri (decls, exts) <- getInRangeH98DeclsAndExts state range nfp (L ann decl) <- case decls of [d] -> pure d _ -> throwE $ "Expected 1 declaration, but got " <> show (Prelude.length decls) - deps <- liftIO $ runAction "GADT.GhcSessionDeps" state $ use GhcSessionDeps nfp + deps <- liftIO $ runAction (T.unpack pId' <> ".GhcSessionDeps") state $ use GhcSessionDeps nfp (hsc_dflags . hscEnv -> df) <- liftEither $ maybeToEither "Get GhcSessionDeps failed" deps txt <- liftEither $ T.pack <$> (prettyGADTDecl df . h98ToGADTDecl) decl range <- liftEither $ maybeToEither "Unable to get data decl range" $ srcSpanToRange $ locA ann - pragma <- getNextPragma state nfp + pragma <- getFirstPragma pId state nfp let insertEdit = [insertNewPragma pragma GADTs | all (`notElem` exts) [GADTSyntax, GADTs]] _ <- lift $ sendRequest @@ -118,14 +114,5 @@ getInRangeH98DeclsAndExts state range nfp = do decls = filter isH98DataDecl $ mapMaybe getDataDecl $ filter (inRange range) hsDecls - exts = (toList . extensionFlags . ms_hspp_opts . pm_mod_summary) pm + exts = getExtensions pm pure (decls, exts) - --- Copy from hls-alternate-number-format-plugin -getNextPragma :: MonadIO m => IdeState -> NormalizedFilePath -> ExceptT String m NextPragmaInfo -getNextPragma state nfp = handleMaybeM "Error: Could not get NextPragmaInfo" $ do - ghcSession <- liftIO $ runAction "GADT.GhcSession" state $ useWithStale GhcSession nfp - (_, fileContents) <- liftIO $ runAction "GADT.GetFileContents" state $ getFileContents nfp - case ghcSession of - Just (hscEnv -> hsc_dflags -> sessionDynFlags, _) -> pure $ Just $ getNextPragmaInfo sessionDynFlags fileContents - Nothing -> pure Nothing diff --git a/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs b/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs index e1efc2869d..280acf26f8 100644 --- a/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs +++ b/plugins/hls-hlint-plugin/src/Ide/Plugin/Hlint.hs @@ -51,7 +51,8 @@ import qualified Data.Text as T import qualified Data.Text.Encoding as T import Data.Typeable import Development.IDE hiding - (Error) + (Error, + getExtensions) import Development.IDE.Core.Rules (defineNoFile, getParsedModuleWithComments, usePropertyAction) diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index d9b11f8786..ee56d75d3b 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -43,7 +43,8 @@ import Development.IDE.Core.Rules import Development.IDE.Core.RuleTypes import Development.IDE.Core.Service import Development.IDE.Core.Shake hiding (Log) -import Development.IDE.GHC.Compat +import Development.IDE.GHC.Compat hiding + (ImplicitPrelude) import Development.IDE.GHC.Compat.ExactPrint import Development.IDE.GHC.Compat.Util import Development.IDE.GHC.Error diff --git a/plugins/hls-tactics-plugin/src/Wingman/EmptyCase.hs b/plugins/hls-tactics-plugin/src/Wingman/EmptyCase.hs index 7c675c36f9..a13d7c1a65 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/EmptyCase.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/EmptyCase.hs @@ -21,7 +21,7 @@ import Development.IDE (hscEnv, realSrcSpanToRange) import Development.IDE.Core.RuleTypes import Development.IDE.Core.Shake (IdeState (..)) import Development.IDE.Core.UseStale -import Development.IDE.GHC.Compat hiding (empty) +import Development.IDE.GHC.Compat hiding (empty, EmptyCase) import Development.IDE.GHC.ExactPrint import Development.IDE.Spans.LocalBindings (getLocalScope) import Ide.Types diff --git a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer/TacticProviders.hs b/plugins/hls-tactics-plugin/src/Wingman/LanguageServer/TacticProviders.hs index 40d6362d94..b5a6521b7e 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer/TacticProviders.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/LanguageServer/TacticProviders.hs @@ -16,7 +16,6 @@ import Data.Monoid import qualified Data.Set as S import qualified Data.Text as T import Development.IDE.GHC.Compat -import GHC.LanguageExtensions.Type (Extension (LambdaCase)) import Ide.Types import Language.LSP.Types hiding (SemanticTokenAbsolute (..), SemanticTokenRelative (..)) import Prelude hiding (span) diff --git a/plugins/hls-tactics-plugin/src/Wingman/StaticPlugin.hs b/plugins/hls-tactics-plugin/src/Wingman/StaticPlugin.hs index a1caeef12d..42065aa289 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/StaticPlugin.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/StaticPlugin.hs @@ -10,7 +10,6 @@ module Wingman.StaticPlugin import Development.IDE.GHC.Compat import Development.IDE.GHC.Compat.Util -import GHC.LanguageExtensions.Type (Extension(EmptyCase, QuasiQuotes)) import Ide.Types diff --git a/src/HlsPlugins.hs b/src/HlsPlugins.hs index 5095a637c0..1aa433fb5d 100644 --- a/src/HlsPlugins.hs +++ b/src/HlsPlugins.hs @@ -94,6 +94,10 @@ import Ide.Plugin.GADT as GADT import Ide.Plugin.ExplicitFixity as ExplicitFixity #endif +#if explicitFields +import Ide.Plugin.ExplicitFields as ExplicitFields +#endif + -- formatters #if hls_floskell @@ -221,4 +225,7 @@ idePlugins recorder = pluginDescToIdePlugins allPlugins #if explicitFixity ++ [let pId = "explicit-fixity" in ExplicitFixity.descriptor (pluginRecorder pId) pId] #endif +#if explicitFields + ++ [let pId = "explicit-fields" in ExplicitFields.descriptor (pluginRecorder pId) pId] +#endif diff --git a/stack-lts19.yaml b/stack-lts19.yaml index af599c2112..88b3024df0 100644 --- a/stack-lts19.yaml +++ b/stack-lts19.yaml @@ -34,6 +34,7 @@ packages: - ./plugins/hls-gadt-plugin - ./plugins/hls-explicit-fixity-plugin - ./plugins/hls-refactor-plugin + - ./plugins/hls-explicit-record-fields-plugin ghc-options: "$everything": -haddock diff --git a/stack.yaml b/stack.yaml index 914b0a980b..86b0e67584 100644 --- a/stack.yaml +++ b/stack.yaml @@ -34,6 +34,7 @@ packages: - ./plugins/hls-gadt-plugin - ./plugins/hls-explicit-fixity-plugin - ./plugins/hls-refactor-plugin +- ./plugins/hls-explicit-record-fields-plugin extra-deps: - floskell-0.10.6@sha256:e77d194189e8540abe2ace2c7cb8efafc747ca35881a2fefcbd2d40a1292e036,3819 From 702053253743f64853afb2b8dd73dfbab0032d0c Mon Sep 17 00:00:00 2001 From: VeryMilkyJoe Date: Fri, 11 Nov 2022 11:25:29 +0100 Subject: [PATCH 187/213] Add formatting plugin for cabal files which uses cabal-fmt (#2047) * Add plugin for formatting cabal files using cabal-fmt * Add 'isolateTests' cabal flag to make plugin install cabal-fmt For CI, we want to run the tests with a specific cabal-fmt version, installed automatically by cabal. However, locally, we might want to test with a locally installed cabal-fmt version. This flag allows developers to either let cabal install the build-tool-depends or install a fitting version locally. * Ignore failing cabal-fmt test on windows * Add log message for missing cabal-fmt executable Co-authored-by: Jana Chadt Co-authored-by: Fendor --- .github/workflows/test.yml | 5 + CODEOWNERS | 1 + cabal.project | 1 + docs/features.md | 7 + docs/support/plugin-support.md | 1 + haskell-language-server.cabal | 10 + hls-plugin-api/src/Ide/Plugin/Config.hs | 13 +- hls-plugin-api/src/Ide/Types.hs | 5 +- hls-test-utils/src/Test/Hls.hs | 60 ++++-- plugins/hls-cabal-fmt-plugin/LICENSE | 201 ++++++++++++++++++ .../hls-cabal-fmt-plugin.cabal | 59 +++++ .../src/Ide/Plugin/CabalFmt.hs | 76 +++++++ plugins/hls-cabal-fmt-plugin/test/Main.hs | 60 ++++++ .../test/testdata/commented_testdata.cabal | 12 ++ ...ommented_testdata.formatted_document.cabal | 15 ++ .../test/testdata/hie.yaml | 3 + .../test/testdata/lib_testdata.cabal | 19 ++ .../lib_testdata.formatted_document.cabal | 20 ++ .../test/testdata/simple_testdata.cabal | 36 ++++ .../simple_testdata.formatted_document.cabal | 36 ++++ .../test/testdata/src/MyLib.hs | 4 + .../test/testdata/src/MyOtherLib.hs | 3 + src/HlsPlugins.hs | 7 + stack-lts19.yaml | 3 + stack.yaml | 3 + test/functional/Format.hs | 1 - 26 files changed, 641 insertions(+), 20 deletions(-) create mode 100644 plugins/hls-cabal-fmt-plugin/LICENSE create mode 100644 plugins/hls-cabal-fmt-plugin/hls-cabal-fmt-plugin.cabal create mode 100644 plugins/hls-cabal-fmt-plugin/src/Ide/Plugin/CabalFmt.hs create mode 100644 plugins/hls-cabal-fmt-plugin/test/Main.hs create mode 100644 plugins/hls-cabal-fmt-plugin/test/testdata/commented_testdata.cabal create mode 100644 plugins/hls-cabal-fmt-plugin/test/testdata/commented_testdata.formatted_document.cabal create mode 100644 plugins/hls-cabal-fmt-plugin/test/testdata/hie.yaml create mode 100644 plugins/hls-cabal-fmt-plugin/test/testdata/lib_testdata.cabal create mode 100644 plugins/hls-cabal-fmt-plugin/test/testdata/lib_testdata.formatted_document.cabal create mode 100644 plugins/hls-cabal-fmt-plugin/test/testdata/simple_testdata.cabal create mode 100644 plugins/hls-cabal-fmt-plugin/test/testdata/simple_testdata.formatted_document.cabal create mode 100644 plugins/hls-cabal-fmt-plugin/test/testdata/src/MyLib.hs create mode 100644 plugins/hls-cabal-fmt-plugin/test/testdata/src/MyOtherLib.hs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6993c86ae6..3a79cf13d1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -254,6 +254,11 @@ jobs: name: Test hls-explicit-record-fields-plugin test suite run: cabal test hls-explicit-record-fields-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-explicit-record-fields-plugin --test-options="$TEST_OPTS" + ## version needs to be limited since the tests depend on cabal-fmt which only builds using specific ghc versions + - if: matrix.test && matrix.ghc == '8.10.7' + name: Test hls-cabal-fmt-plugin test suite + run: cabal test hls-cabal-fmt-plugin --flag=isolateTests --test-options="$TEST_OPTS" || cabal test hls-cabal-fmt-plugin --flag=isolateTests --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-cabal-fmt-plugin --flag=isolateTests --test-options="$TEST_OPTS" + test_post_job: if: always() runs-on: ubuntu-latest diff --git a/CODEOWNERS b/CODEOWNERS index 1867d280ba..591e3a5893 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -9,6 +9,7 @@ # Plugins /plugins/hls-alternate-number-format-plugin @drsooch /plugins/hls-brittany-plugin @fendor +/plugins/hls-cabal-fmt-plugin @VeryMilkyJoe @fendor /plugins/hls-call-hierarchy-plugin @July541 /plugins/hls-class-plugin @Ailrun /plugins/hls-eval-plugin diff --git a/cabal.project b/cabal.project index 6220f564a8..4dee7fc198 100644 --- a/cabal.project +++ b/cabal.project @@ -8,6 +8,7 @@ packages: ./ghcide/test ./hls-plugin-api ./hls-test-utils + ./plugins/hls-cabal-fmt-plugin ./plugins/hls-tactics-plugin ./plugins/hls-brittany-plugin ./plugins/hls-stylish-haskell-plugin diff --git a/docs/features.md b/docs/features.md index efb892b1c9..5b025a82aa 100644 --- a/docs/features.md +++ b/docs/features.md @@ -107,6 +107,13 @@ Format your code with various Haskell code formatters. | Ormolu | `hls-ormolu-plugin` | | Stylish Haskell | `hls-stylish-haskell-plugin` | +Format your cabal files with a cabal code formatter. + +| Formatter | Provided by | +|-----------------|------------------------------| +| cabal-fmt | `hls-cabal-fmt-plugin` | + + ## Document symbols Provided by: `ghcide` diff --git a/docs/support/plugin-support.md b/docs/support/plugin-support.md index 5dd9f97aaf..9f115a46e5 100644 --- a/docs/support/plugin-support.md +++ b/docs/support/plugin-support.md @@ -46,6 +46,7 @@ For example, a plugin to provide a formatter which has itself been abandoned has | `hls-pragmas-plugin` | 1 | | | `hls-refactor-plugin` | 1 | 9.4 | | `hls-alternate-number-plugin` | 2 | | +| `hls-cabal-fmt-plugin` | 2 | | | `hls-class-plugin` | 2 | | | `hls-change-type-signature-plugin` | 2 | | | `hls-eval-plugin` | 2 | 9.4 | diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 5c857c2de6..f7e1b34980 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -205,6 +205,16 @@ flag dynamic default: True manual: True +flag cabalfmt + description: Enable cabal-fmt plugin + default: True + manual: True + +common cabalfmt + if flag(cabalfmt) + build-depends: hls-cabal-fmt-plugin ^>= 0.1.0.0 + cpp-options: -Dhls_cabalfmt + common class if flag(class) build-depends: hls-class-plugin ^>= 1.1 diff --git a/hls-plugin-api/src/Ide/Plugin/Config.hs b/hls-plugin-api/src/Ide/Plugin/Config.hs index 13f33278a8..6990376678 100644 --- a/hls-plugin-api/src/Ide/Plugin/Config.hs +++ b/hls-plugin-api/src/Ide/Plugin/Config.hs @@ -47,11 +47,12 @@ data CheckParents -- will be surprises relating to config options being ignored, initially though. data Config = Config - { checkParents :: CheckParents - , checkProject :: !Bool - , formattingProvider :: !T.Text - , maxCompletions :: !Int - , plugins :: !(Map.Map T.Text PluginConfig) + { checkParents :: CheckParents + , checkProject :: !Bool + , formattingProvider :: !T.Text + , cabalFormattingProvider :: !T.Text + , maxCompletions :: !Int + , plugins :: !(Map.Map T.Text PluginConfig) } deriving (Show,Eq) instance Default Config where @@ -62,6 +63,7 @@ instance Default Config where , formattingProvider = "ormolu" -- , formattingProvider = "floskell" -- , formattingProvider = "stylish-haskell" + , cabalFormattingProvider = "cabal-fmt" , maxCompletions = 40 , plugins = Map.empty } @@ -78,6 +80,7 @@ parseConfig defValue = A.withObject "Config" $ \v -> do <$> (o .:? "checkParents" <|> v .:? "checkParents") .!= checkParents defValue <*> (o .:? "checkProject" <|> v .:? "checkProject") .!= checkProject defValue <*> o .:? "formattingProvider" .!= formattingProvider defValue + <*> o .:? "cabalFormattingProvider" .!= cabalFormattingProvider defValue <*> o .:? "maxCompletions" .!= maxCompletions defValue <*> o .:? "plugin" .!= plugins defValue diff --git a/hls-plugin-api/src/Ide/Types.hs b/hls-plugin-api/src/Ide/Types.hs index f41c17286b..8630905274 100644 --- a/hls-plugin-api/src/Ide/Types.hs +++ b/hls-plugin-api/src/Ide/Types.hs @@ -403,14 +403,15 @@ instance PluginMethod Request TextDocumentCompletion where instance PluginMethod Request TextDocumentFormatting where pluginEnabled STextDocumentFormatting msgParams pluginDesc conf = - pluginResponsible uri pluginDesc && PluginId (formattingProvider conf) == pid + pluginResponsible uri pluginDesc + && (PluginId (formattingProvider conf) == pid || PluginId (cabalFormattingProvider conf) == pid) where uri = msgParams ^. J.textDocument . J.uri pid = pluginId pluginDesc instance PluginMethod Request TextDocumentRangeFormatting where pluginEnabled _ msgParams pluginDesc conf = pluginResponsible uri pluginDesc - && PluginId (formattingProvider conf) == pid + && (PluginId (formattingProvider conf) == pid || PluginId (cabalFormattingProvider conf) == pid) where uri = msgParams ^. J.textDocument . J.uri pid = pluginId pluginDesc diff --git a/hls-test-utils/src/Test/Hls.hs b/hls-test-utils/src/Test/Hls.hs index 768c67d384..48172cff06 100644 --- a/hls-test-utils/src/Test/Hls.hs +++ b/hls-test-utils/src/Test/Hls.hs @@ -17,9 +17,11 @@ module Test.Hls goldenGitDiff, goldenWithHaskellDoc, goldenWithHaskellDocFormatter, + goldenWithCabalDocFormatter, def, runSessionWithServer, runSessionWithServerFormatter, + runSessionWithCabalServerFormatter, runSessionWithServer', waitForProgressDone, waitForAllProgressDone, @@ -70,6 +72,7 @@ import Development.IDE.Types.Options import GHC.IO.Handle import GHC.Stack (emptyCallStack) import Ide.Plugin.Config (Config, PluginConfig, + cabalFormattingProvider, formattingProvider, plugins) import Ide.PluginUtils (idePluginsToPluginDesc, pluginDescToIdePlugins) @@ -130,15 +133,30 @@ goldenWithHaskellDoc plugin title testDataDir path desc ext act = act doc documentContents doc + +runSessionWithServer :: PluginDescriptor IdeState -> FilePath -> Session a -> IO a +runSessionWithServer plugin = runSessionWithServer' [plugin] def def fullCaps + +runSessionWithServerFormatter :: PluginDescriptor IdeState -> String -> PluginConfig -> FilePath -> Session a -> IO a +runSessionWithServerFormatter plugin formatter conf = + runSessionWithServer' + [plugin] + def + { formattingProvider = T.pack formatter + , plugins = M.singleton (T.pack formatter) conf + } + def + fullCaps + goldenWithHaskellDocFormatter - :: PluginDescriptor IdeState - -> String + :: PluginDescriptor IdeState -- ^ Formatter plugin to be used + -> String -- ^ Name of the formatter to be used -> PluginConfig - -> TestName - -> FilePath - -> FilePath - -> FilePath - -> FilePath + -> TestName -- ^ Title of the test + -> FilePath -- ^ Directory of the test data to be used + -> FilePath -- ^ Path to the testdata to be used within the directory + -> FilePath -- ^ Additional suffix to be appended to the output file + -> FilePath -- ^ Extension of the output file -> (TextDocumentIdentifier -> Session ()) -> TestTree goldenWithHaskellDocFormatter plugin formatter conf title testDataDir path desc ext act = @@ -151,15 +169,33 @@ goldenWithHaskellDocFormatter plugin formatter conf title testDataDir path desc act doc documentContents doc -runSessionWithServer :: PluginDescriptor IdeState -> FilePath -> Session a -> IO a -runSessionWithServer plugin = runSessionWithServer' [plugin] def def fullCaps +goldenWithCabalDocFormatter + :: PluginDescriptor IdeState -- ^ Formatter plugin to be used + -> String -- ^ Name of the formatter to be used + -> PluginConfig + -> TestName -- ^ Title of the test + -> FilePath -- ^ Directory of the test data to be used + -> FilePath -- ^ Path to the testdata to be used within the directory + -> FilePath -- ^ Additional suffix to be appended to the output file + -> FilePath -- ^ Extension of the output file + -> (TextDocumentIdentifier -> Session ()) + -> TestTree +goldenWithCabalDocFormatter plugin formatter conf title testDataDir path desc ext act = + goldenGitDiff title (testDataDir path <.> desc <.> ext) + $ runSessionWithCabalServerFormatter plugin formatter conf testDataDir + $ TL.encodeUtf8 . TL.fromStrict + <$> do + doc <- openDoc (path <.> ext) "cabal" + void waitForBuildQueue + act doc + documentContents doc -runSessionWithServerFormatter :: PluginDescriptor IdeState -> String -> PluginConfig -> FilePath -> Session a -> IO a -runSessionWithServerFormatter plugin formatter conf = +runSessionWithCabalServerFormatter :: PluginDescriptor IdeState -> String -> PluginConfig -> FilePath -> Session a -> IO a +runSessionWithCabalServerFormatter plugin formatter conf = runSessionWithServer' [plugin] def - { formattingProvider = T.pack formatter + { cabalFormattingProvider = T.pack formatter , plugins = M.singleton (T.pack formatter) conf } def diff --git a/plugins/hls-cabal-fmt-plugin/LICENSE b/plugins/hls-cabal-fmt-plugin/LICENSE new file mode 100644 index 0000000000..16502c47e2 --- /dev/null +++ b/plugins/hls-cabal-fmt-plugin/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 The Haskell IDE team + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/hls-cabal-fmt-plugin/hls-cabal-fmt-plugin.cabal b/plugins/hls-cabal-fmt-plugin/hls-cabal-fmt-plugin.cabal new file mode 100644 index 0000000000..ad8a8d8ad5 --- /dev/null +++ b/plugins/hls-cabal-fmt-plugin/hls-cabal-fmt-plugin.cabal @@ -0,0 +1,59 @@ +cabal-version: 2.4 +name: hls-cabal-fmt-plugin +version: 0.1.0.0 +synopsis: Integration with the cabal-fmt code formatter +description: + Please see the README on GitHub at + +license: Apache-2.0 +license-file: LICENSE +author: The Haskell IDE Team +copyright: The Haskell IDE Team +maintainer: jana.chadt@nets.at +category: Development +build-type: Simple +extra-source-files: LICENSE + +flag isolateTests + description: Should tests search for 'cabal-fmt' on the $PATH or shall we install it via build-tool-depends? + -- By default, search on the PATH + default: False + manual: True + +common warnings + ghc-options: -Wall + +library + import: warnings + exposed-modules: Ide.Plugin.CabalFmt + hs-source-dirs: src + build-depends: + , base >=4.12 && <5 + , directory + , filepath + , ghcide ^>=1.7 || ^>=1.8 + , hls-plugin-api ^>=1.5 + , lens + , lsp-types + , process + , text + , transformers + + default-language: Haskell2010 + +test-suite tests + import: warnings + type: exitcode-stdio-1.0 + default-language: Haskell2010 + hs-source-dirs: test + main-is: Main.hs + ghc-options: -threaded -rtsopts -with-rtsopts=-N + build-depends: + , base + , directory + , filepath + , hls-cabal-fmt-plugin + , hls-test-utils ^>=1.4 + + if flag(isolateTests) + build-tool-depends: cabal-fmt:cabal-fmt ^>=0.1.6 diff --git a/plugins/hls-cabal-fmt-plugin/src/Ide/Plugin/CabalFmt.hs b/plugins/hls-cabal-fmt-plugin/src/Ide/Plugin/CabalFmt.hs new file mode 100644 index 0000000000..9eb1f97654 --- /dev/null +++ b/plugins/hls-cabal-fmt-plugin/src/Ide/Plugin/CabalFmt.hs @@ -0,0 +1,76 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} + +module Ide.Plugin.CabalFmt where + +import Control.Lens +import Control.Monad.IO.Class +import qualified Data.Text as T +import Development.IDE hiding (pluginHandlers) +import Ide.PluginUtils +import Ide.Types +import Language.LSP.Types as J +import qualified Language.LSP.Types.Lens as J +import Prelude hiding (log) +import System.Directory +import System.Exit +import System.FilePath +import System.Process + +data Log + = LogProcessInvocationFailure Int + | LogReadCreateProcessInfo String [String] + | LogInvalidInvocationInfo + | LogCabalFmtNotFound + deriving (Show) + +instance Pretty Log where + pretty = \case + LogProcessInvocationFailure code -> "Invocation of cabal-fmt failed with code" <+> pretty code + LogReadCreateProcessInfo stdErrorOut args -> + vcat $ + ["Invocation of cabal-fmt with arguments" <+> pretty args] + ++ ["failed with standard error:" <+> pretty stdErrorOut | not (null stdErrorOut)] + LogInvalidInvocationInfo -> "Invocation of cabal-fmt with range was called but is not supported." + LogCabalFmtNotFound -> "Couldn't find executable 'cabal-fmt'" + +descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState +descriptor recorder plId = + (defaultCabalPluginDescriptor plId) + { pluginHandlers = mkFormattingHandlers (provider recorder) + } + +-- | Formatter provider of cabal fmt. +-- Formats the given source in either a given Range or the whole Document. +-- If the provider fails an error is returned that can be displayed to the user. +provider :: Recorder (WithPriority Log) -> FormattingHandler IdeState +provider recorder _ (FormatRange _) _ _ _ = do + logWith recorder Info LogInvalidInvocationInfo + pure $ Left (ResponseError InvalidRequest "You cannot format a text-range using cabal-fmt." Nothing) +provider recorder _ide FormatText contents nfp opts = liftIO $ do + let cabalFmtArgs = [fp, "--indent", show tabularSize] + x <- findExecutable "cabal-fmt" + case x of + Just _ -> do + (exitCode, out, err) <- + readCreateProcessWithExitCode + ( proc "cabal-fmt" cabalFmtArgs + ) + { cwd = Just $ takeDirectory fp + } + "" + log Debug $ LogReadCreateProcessInfo err cabalFmtArgs + case exitCode of + ExitFailure code -> do + log Error $ LogProcessInvocationFailure code + pure $ Left (ResponseError UnknownErrorCode "Failed to invoke cabal-fmt" Nothing) + ExitSuccess -> do + let fmtDiff = makeDiffTextEdit contents (T.pack out) + pure $ Right fmtDiff + Nothing -> do + log Error LogCabalFmtNotFound + pure $ Left (ResponseError InvalidRequest "No installation of cabal-fmt could be found. Please install it into your global environment." Nothing) + where + fp = fromNormalizedFilePath nfp + tabularSize = opts ^. J.tabSize + log = logWith recorder diff --git a/plugins/hls-cabal-fmt-plugin/test/Main.hs b/plugins/hls-cabal-fmt-plugin/test/Main.hs new file mode 100644 index 0000000000..35d6fe6ba8 --- /dev/null +++ b/plugins/hls-cabal-fmt-plugin/test/Main.hs @@ -0,0 +1,60 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE OverloadedStrings #-} +module Main + ( main + ) where + +import qualified Ide.Plugin.CabalFmt as CabalFmt +import System.Directory (findExecutable) +import System.FilePath +import Test.Hls + +data CabalFmtFound = Found | NotFound + +isTestIsolated :: Bool +#if isolateTests +isTestIsolated = True +#else +isTestIsolated = False +#endif + +isCabalFmtFound :: IO CabalFmtFound +isCabalFmtFound = case isTestIsolated of + True -> pure Found + False-> do + cabalFmt <- findExecutable "cabal-fmt" + pure $ maybe NotFound (const Found) cabalFmt + +main :: IO () +main = do + foundCabalFmt <- isCabalFmtFound + defaultTestRunner (tests foundCabalFmt) + +cabalFmtPlugin :: PluginDescriptor IdeState +cabalFmtPlugin = CabalFmt.descriptor mempty "cabal-fmt" + +tests :: CabalFmtFound -> TestTree +tests found = testGroup "cabal-fmt" + [ knownBrokenOnWindows "Eats newlines between comments" $ + cabalFmtGolden found "formats a simple document" "simple_testdata" "formatted_document" $ \doc -> do + formatDoc doc (FormattingOptions 2 True Nothing Nothing Nothing) + + , knownBrokenOnWindows "expand:src comment bug in cabal-fmt on windows" $ + cabalFmtGolden found "formats a document with expand:src comment" "commented_testdata" "formatted_document" $ \doc -> do + formatDoc doc (FormattingOptions 2 True Nothing Nothing Nothing) + + , cabalFmtGolden found "formats a document with lib information" "lib_testdata" "formatted_document" $ \doc -> do + formatDoc doc (FormattingOptions 10 True Nothing Nothing Nothing) + ] + +cabalFmtGolden :: CabalFmtFound -> TestName -> FilePath -> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree +cabalFmtGolden NotFound title _ _ _ = + testCase title $ + assertFailure $ "Couldn't find cabal-fmt on PATH or this is not an isolated run. " + <> "Use cabal flag 'isolateTests' to make it isolated or install cabal-fmt locally." +cabalFmtGolden Found title path desc act = goldenWithCabalDocFormatter cabalFmtPlugin "cabal-fmt" conf title testDataDir path desc "cabal" act + where + conf = def + +testDataDir :: FilePath +testDataDir = "test" "testdata" diff --git a/plugins/hls-cabal-fmt-plugin/test/testdata/commented_testdata.cabal b/plugins/hls-cabal-fmt-plugin/test/testdata/commented_testdata.cabal new file mode 100644 index 0000000000..ae7bcf6590 --- /dev/null +++ b/plugins/hls-cabal-fmt-plugin/test/testdata/commented_testdata.cabal @@ -0,0 +1,12 @@ +cabal-version: 2.4 +name: testdata +version: 0.1.0.0 +author: Banana +extra-source-files: CHANGELOG.md + +library + -- cabal-fmt: expand src + exposed-modules: MyLib + build-depends: base ^>=4.14.1.0 + hs-source-dirs: src + default-language: Haskell2010 diff --git a/plugins/hls-cabal-fmt-plugin/test/testdata/commented_testdata.formatted_document.cabal b/plugins/hls-cabal-fmt-plugin/test/testdata/commented_testdata.formatted_document.cabal new file mode 100644 index 0000000000..28f8e040cf --- /dev/null +++ b/plugins/hls-cabal-fmt-plugin/test/testdata/commented_testdata.formatted_document.cabal @@ -0,0 +1,15 @@ +cabal-version: 2.4 +name: testdata +version: 0.1.0.0 +author: Banana +extra-source-files: CHANGELOG.md + +library + -- cabal-fmt: expand src + exposed-modules: + MyLib + MyOtherLib + + build-depends: base ^>=4.14.1.0 + hs-source-dirs: src + default-language: Haskell2010 diff --git a/plugins/hls-cabal-fmt-plugin/test/testdata/hie.yaml b/plugins/hls-cabal-fmt-plugin/test/testdata/hie.yaml new file mode 100644 index 0000000000..824558147d --- /dev/null +++ b/plugins/hls-cabal-fmt-plugin/test/testdata/hie.yaml @@ -0,0 +1,3 @@ +cradle: + direct: + arguments: [] diff --git a/plugins/hls-cabal-fmt-plugin/test/testdata/lib_testdata.cabal b/plugins/hls-cabal-fmt-plugin/test/testdata/lib_testdata.cabal new file mode 100644 index 0000000000..0f07af1d70 --- /dev/null +++ b/plugins/hls-cabal-fmt-plugin/test/testdata/lib_testdata.cabal @@ -0,0 +1,19 @@ +cabal-version: 2.4 +name: testdata +version: 0.1.0.0 +author: Gregg +extra-source-files: CHANGELOG.md + +library + exposed-modules: MyLib + build-depends: base ^>=4.14.1.0 + hs-source-dirs: src + default-language: Haskell2010 + +executable testdata + main-is: Main.hs + build-depends: + base ^>=4.14.1.0,testdata + hs-source-dirs: app + default-language: + Haskell2010 diff --git a/plugins/hls-cabal-fmt-plugin/test/testdata/lib_testdata.formatted_document.cabal b/plugins/hls-cabal-fmt-plugin/test/testdata/lib_testdata.formatted_document.cabal new file mode 100644 index 0000000000..4df43f1b8f --- /dev/null +++ b/plugins/hls-cabal-fmt-plugin/test/testdata/lib_testdata.formatted_document.cabal @@ -0,0 +1,20 @@ +cabal-version: 2.4 +name: testdata +version: 0.1.0.0 +author: Gregg +extra-source-files: CHANGELOG.md + +library + exposed-modules: MyLib + build-depends: base ^>=4.14.1.0 + hs-source-dirs: src + default-language: Haskell2010 + +executable testdata + main-is: Main.hs + build-depends: + , base ^>=4.14.1.0 + , testdata + + hs-source-dirs: app + default-language: Haskell2010 diff --git a/plugins/hls-cabal-fmt-plugin/test/testdata/simple_testdata.cabal b/plugins/hls-cabal-fmt-plugin/test/testdata/simple_testdata.cabal new file mode 100644 index 0000000000..0421a27ddb --- /dev/null +++ b/plugins/hls-cabal-fmt-plugin/test/testdata/simple_testdata.cabal @@ -0,0 +1,36 @@ +cabal-version: 2.4 +name: testdata +version: 0.1.0.0 + +-- A short (one-line) description of the package. +-- synopsis: + +-- A longer description of the package. +-- description: + +-- A URL where users can report bugs. +-- bug-reports: + +-- The license under which the package is released. +-- license: +author: Milky + +-- An email address to which users can send suggestions, bug reports, and patches. +-- maintainer: + +-- A copyright notice. +-- copyright: +-- category: +extra-source-files: CHANGELOG.md + +executable testdata + main-is: Main.hs + + -- Modules included in this executable, other than Main. + -- other-modules: + + -- LANGUAGE extensions used by modules in this package. + -- other-extensions: + build-depends: base ^>=4.14.1.0 + hs-source-dirs: app + default-language: Haskell2010 diff --git a/plugins/hls-cabal-fmt-plugin/test/testdata/simple_testdata.formatted_document.cabal b/plugins/hls-cabal-fmt-plugin/test/testdata/simple_testdata.formatted_document.cabal new file mode 100644 index 0000000000..993cef832d --- /dev/null +++ b/plugins/hls-cabal-fmt-plugin/test/testdata/simple_testdata.formatted_document.cabal @@ -0,0 +1,36 @@ +cabal-version: 2.4 +name: testdata +version: 0.1.0.0 + +-- A short (one-line) description of the package. +-- synopsis: + +-- A longer description of the package. +-- description: + +-- A URL where users can report bugs. +-- bug-reports: + +-- The license under which the package is released. +-- license: +author: Milky + +-- An email address to which users can send suggestions, bug reports, and patches. +-- maintainer: + +-- A copyright notice. +-- copyright: +-- category: +extra-source-files: CHANGELOG.md + +executable testdata + main-is: Main.hs + + -- Modules included in this executable, other than Main. + -- other-modules: + + -- LANGUAGE extensions used by modules in this package. + -- other-extensions: + build-depends: base ^>=4.14.1.0 + hs-source-dirs: app + default-language: Haskell2010 diff --git a/plugins/hls-cabal-fmt-plugin/test/testdata/src/MyLib.hs b/plugins/hls-cabal-fmt-plugin/test/testdata/src/MyLib.hs new file mode 100644 index 0000000000..e657c4403f --- /dev/null +++ b/plugins/hls-cabal-fmt-plugin/test/testdata/src/MyLib.hs @@ -0,0 +1,4 @@ +module MyLib (someFunc) where + +someFunc :: IO () +someFunc = putStrLn "someFunc" diff --git a/plugins/hls-cabal-fmt-plugin/test/testdata/src/MyOtherLib.hs b/plugins/hls-cabal-fmt-plugin/test/testdata/src/MyOtherLib.hs new file mode 100644 index 0000000000..15450b43b3 --- /dev/null +++ b/plugins/hls-cabal-fmt-plugin/test/testdata/src/MyOtherLib.hs @@ -0,0 +1,3 @@ +module MyOtherLib where + +bar = 2 diff --git a/src/HlsPlugins.hs b/src/HlsPlugins.hs index 1aa433fb5d..b471fa65cb 100644 --- a/src/HlsPlugins.hs +++ b/src/HlsPlugins.hs @@ -108,6 +108,10 @@ import qualified Ide.Plugin.Floskell as Floskell import qualified Ide.Plugin.Fourmolu as Fourmolu #endif +#if hls_cabalfmt +import qualified Ide.Plugin.CabalFmt as CabalFmt +#endif + #if hls_ormolu import qualified Ide.Plugin.Ormolu as Ormolu #endif @@ -151,6 +155,9 @@ idePlugins recorder = pluginDescToIdePlugins allPlugins #if hls_fourmolu let pId = "fourmolu" in Fourmolu.descriptor (pluginRecorder pId) pId: #endif +#if hls_cabalfmt + let pId = "cabalfmt" in CabalFmt.descriptor (pluginRecorder pId) pId: +#endif #if hls_tactic let pId = "tactics" in Tactic.descriptor (pluginRecorder pId) pId: #endif diff --git a/stack-lts19.yaml b/stack-lts19.yaml index 88b3024df0..4e33bd28f8 100644 --- a/stack-lts19.yaml +++ b/stack-lts19.yaml @@ -9,6 +9,7 @@ packages: - ./hls-plugin-api - ./hls-test-utils # - ./shake-bench + - ./plugins/hls-cabal-fmt-plugin - ./plugins/hls-call-hierarchy-plugin - ./plugins/hls-class-plugin - ./plugins/hls-haddock-comments-plugin @@ -41,6 +42,8 @@ ghc-options: extra-deps: - Cabal-3.6.0.0 +# needed for tests of hls-cabal-fmt-plugin +- cabal-fmt-0.1.6@sha256:54041d50c8148c32d1e0a67aef7edeebac50ae33571bef22312f6815908eac19,3626 - floskell-0.10.6@sha256:e77d194189e8540abe2ace2c7cb8efafc747ca35881a2fefcbd2d40a1292e036,3819 - fourmolu-0.6.0.0 - ghc-lib-9.2.4.20220729 diff --git a/stack.yaml b/stack.yaml index 86b0e67584..ca2f39b5cf 100644 --- a/stack.yaml +++ b/stack.yaml @@ -9,6 +9,7 @@ packages: - ./hls-plugin-api - ./hls-test-utils - ./shake-bench +- ./plugins/hls-cabal-fmt-plugin - ./plugins/hls-call-hierarchy-plugin - ./plugins/hls-class-plugin # - ./plugins/hls-haddock-comments-plugin @@ -37,6 +38,8 @@ packages: - ./plugins/hls-explicit-record-fields-plugin extra-deps: +# needed for tests of hls-cabal-fmt-plugin +- cabal-fmt-0.1.6@sha256:54041d50c8148c32d1e0a67aef7edeebac50ae33571bef22312f6815908eac19,3626 - floskell-0.10.6@sha256:e77d194189e8540abe2ace2c7cb8efafc747ca35881a2fefcbd2d40a1292e036,3819 - hiedb-0.4.2.0 - implicit-hie-0.1.2.7@sha256:82bbbb1a8c05f99c8af3c16ac53e80c8648d8bf047b25ed5ce45a135bd736907,3122 diff --git a/test/functional/Format.hs b/test/functional/Format.hs index e08809a8ec..cb434b28f1 100644 --- a/test/functional/Format.hs +++ b/test/functional/Format.hs @@ -55,7 +55,6 @@ providerTests = testGroup "formatting provider" [ _ -> assertFailure $ "strange response from formatting provider:" ++ show result result -> assertFailure $ "strange response from formatting provider:" ++ show result - , requiresOrmoluPlugin . requiresFloskellPlugin $ testCase "can change on the fly" $ runSession hlsCommand fullCaps "test/testdata/format" $ do formattedOrmolu <- liftIO $ T.readFile "test/testdata/format/Format.ormolu.formatted.hs" formattedFloskell <- liftIO $ T.readFile "test/testdata/format/Format.floskell.formatted.hs" From c1a7527c4fb348bee6093d9794b7d3e0c8d563f2 Mon Sep 17 00:00:00 2001 From: Brandon Chinn Date: Fri, 11 Nov 2022 09:17:23 -0800 Subject: [PATCH 188/213] Add support for Fourmolu 0.9 (#3331) Co-authored-by: Michael Peyton Jones --- plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal b/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal index 039ee1615f..e6dc991f81 100644 --- a/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal +++ b/plugins/hls-fourmolu-plugin/hls-fourmolu-plugin.cabal @@ -35,7 +35,7 @@ library build-depends: , base >=4.12 && <5 , filepath - , fourmolu ^>=0.3 || ^>=0.4 || ^>= 0.6 || ^>= 0.7 || ^>= 0.8 + , fourmolu ^>=0.3 || ^>=0.4 || ^>= 0.6 || ^>= 0.7 || ^>= 0.8 || ^>= 0.9 , ghc , ghc-boot-th , ghcide ^>=1.8 From 385dd1b57af75ccd286e104b7e00f2b0cb626588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Berk=20=C3=96zk=C3=BCt=C3=BCk?= Date: Fri, 11 Nov 2022 19:53:07 +0100 Subject: [PATCH 189/213] Add @ozkutuk to CODEOWNERS (#3329) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index 591e3a5893..268f136ff9 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -32,6 +32,7 @@ /plugins/hls-stylish-haskell-plugin @Ailrun /plugins/hls-tactics-plugin @isovector /plugins/hls-stan-plugin @uhbif19 +/plugins/hls-explicit-record-fields-plugin @ozkutuk # Benchmarking /shake-bench @pepeiborra From cd04ff815ce03772c1103bdc7e099137a22d7301 Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Sun, 13 Nov 2022 18:54:37 +0100 Subject: [PATCH 190/213] Make a test more reliable (#3300) * Make iface-error-test-1 more reliable The diagnostics can arrive either before or after progress completed, so there's a race condition here: if they arrive after, then the test script will get confused * make iface-error-test-1 more reliable Progress and diagnostics can arrive in any order: a test that waits for both is creating a race condition * added a comment * Fix single-threaded test execution * Fix another diagnostics/progress race condition in tests --- ghcide/test/exe/Main.hs | 32 +++++++++++++++++++++--- hls-test-utils/src/Test/Hls.hs | 3 ++- plugins/hls-refactor-plugin/test/Main.hs | 1 - 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 1f745b527a..b6883e5379 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -1,6 +1,31 @@ -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 +{- + NOTE On enforcing determinism + + The tests below use two mechanisms to enforce deterministic LSP sequences: + + 1. Progress reporting: waitForProgress(Begin|Done) + 2. Diagnostics: expectDiagnostics + + Either is fine, but diagnostics are generally more reliable. + + Mixing them both in the same test is NOT FINE as it will introduce race + conditions since multiple interleavings are possible. In other words, + the sequence of diagnostics and progress reports is not deterministic. + For example: + + < do something > + waitForProgressDone + expectDiagnostics [...] + + - When the diagnostics arrive after the progress done message, as they usually do, the test will pass + - When the diagnostics arrive before the progress done msg, when on a slow machine occasionally, the test will timeout + + Therefore, avoid mixing both progress reports and diagnostics in the same test + -} + {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} @@ -2690,8 +2715,6 @@ ifaceErrorTest = testCase "iface-error-test-1" $ runWithExtraFiles "recomp" $ \d expectDiagnostics [("P.hs", [(DsWarning,(4,0), "Top-level binding")])] -- So what we know P has been loaded - waitForProgressDone - -- Change y from Int to B changeDoc bdoc [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["module B where", "y :: Bool", "y = undefined"]] -- save so that we can that the error propagates to A @@ -2702,14 +2725,15 @@ ifaceErrorTest = testCase "iface-error-test-1" $ runWithExtraFiles "recomp" $ \d expectDiagnostics [("A.hs", [(DsError, (5, 4), "Couldn't match expected type 'Int' with actual type 'Bool'")])] - -- Check that we wrote the interfaces for B when we saved hidir <- getInterfaceFilesDir bdoc hi_exists <- liftIO $ doesFileExist $ hidir "B.hi" liftIO $ assertBool ("Couldn't find B.hi in " ++ hidir) hi_exists pdoc <- openDoc pPath "haskell" - waitForProgressDone + expectDiagnostics + [("P.hs", [(DsWarning,(4,0), "Top-level binding")]) + ] changeDoc pdoc [TextDocumentContentChangeEvent Nothing Nothing $ pSource <> "\nfoo = y :: Bool" ] -- Now in P we have -- bar = x :: Int diff --git a/hls-test-utils/src/Test/Hls.hs b/hls-test-utils/src/Test/Hls.hs index 48172cff06..7f61f66ae6 100644 --- a/hls-test-utils/src/Test/Hls.hs +++ b/hls-test-utils/src/Test/Hls.hs @@ -97,6 +97,7 @@ import Test.Tasty.ExpectedFailure import Test.Tasty.Golden import Test.Tasty.HUnit import Test.Tasty.Ingredients.Rerun +import Test.Tasty.Runners (NumThreads (..)) newtype Log = LogIDEMain IDEMain.Log @@ -106,7 +107,7 @@ instance Pretty Log where -- | Run 'defaultMainWithRerun', limiting each single test case running at most 10 minutes defaultTestRunner :: TestTree -> IO () -defaultTestRunner = defaultMainWithRerun . adjustOption (const $ mkTimeout 600000000) +defaultTestRunner = defaultMainWithRerun . adjustOption (const $ NumThreads 1) . adjustOption (const $ mkTimeout 600000000) gitDiff :: FilePath -> FilePath -> [String] gitDiff fRef fNew = ["git", "-c", "core.fileMode=false", "diff", "--no-index", "--text", "--exit-code", fRef, fNew] diff --git a/plugins/hls-refactor-plugin/test/Main.hs b/plugins/hls-refactor-plugin/test/Main.hs index 978488a307..b1477b1066 100644 --- a/plugins/hls-refactor-plugin/test/Main.hs +++ b/plugins/hls-refactor-plugin/test/Main.hs @@ -1843,7 +1843,6 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti auxFiles = ["AVec.hs", "BVec.hs", "CVec.hs", "DVec.hs", "EVec.hs", "FVec.hs"] withTarget file locs k = runWithExtraFiles "hiding" $ \dir -> do doc <- openDoc file "haskell" - waitForProgressDone void $ expectDiagnostics [(file, [(DsError, loc, "Ambiguous occurrence") | loc <- locs])] actions <- getAllCodeActions doc k dir doc actions From 1768fb37e43f179645e79e46f69ad991fc89c0c0 Mon Sep 17 00:00:00 2001 From: Pepe Iborra Date: Sun, 13 Nov 2022 20:53:59 +0100 Subject: [PATCH 191/213] Fix minimum capabilities (#3334) --- ghcide/src/Development/IDE/Main.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Main.hs b/ghcide/src/Development/IDE/Main.hs index fbbd5a88a6..45a9484a56 100644 --- a/ghcide/src/Development/IDE/Main.hs +++ b/ghcide/src/Development/IDE/Main.hs @@ -318,9 +318,10 @@ defaultMain recorder Arguments{..} = withHeapStats (cmapWithPrio LogHeapStats re outH <- argsHandleOut numProcessors <- getNumProcessors + let numCapabilities = max 1 $ maybe (numProcessors `div` 2) fromIntegral argsThreads case argCommand of - LSP -> withNumCapabilities (maybe (numProcessors `div` 2) fromIntegral argsThreads) $ do + LSP -> withNumCapabilities numCapabilities $ do t <- offsetTime log Info $ LogLspStart (pluginId <$> ipMap argsHlsPlugins) From 68d353f1ed42f3643bdb6043244b491030cc3e99 Mon Sep 17 00:00:00 2001 From: santiweight Date: Mon, 14 Nov 2022 00:45:23 -0800 Subject: [PATCH 192/213] feat: update type signature during add argument action (#3321) * feat: update type signature during add arg action Co-authored-by: Santiago Weight Co-authored-by: Pepe Iborra --- .../src/Development/IDE/GHC/ExactPrint.hs | 137 ++++++++++++++++-- .../src/Development/IDE/Plugin/CodeAction.hs | 129 +++++++++++++---- plugins/hls-refactor-plugin/test/Main.hs | 33 ++++- .../golden/add-arg/AddArgFromLet.expected.hs | 6 + .../test/data/golden/add-arg/AddArgFromLet.hs | 6 + .../add-arg/AddArgFromWhere.expected.hs | 6 + .../data/golden/add-arg/AddArgFromWhere.hs | 6 + .../add-arg/AddArgWithLambda.expected.hs | 4 + .../data/golden/add-arg/AddArgWithLambda.hs | 4 + .../golden/add-arg/AddArgWithSig.expected.hs | 4 + .../test/data/golden/add-arg/AddArgWithSig.hs | 4 + .../add-arg/AddArgWithSigAndDocs.expected.hs | 11 ++ .../golden/add-arg/AddArgWithSigAndDocs.hs | 11 ++ .../add-arg/AddArgWithTypeSynSig.expected.hs | 5 + .../golden/add-arg/AddArgWithTypeSynSig.hs | 5 + ...ArgWithTypeSynSigContravariant.expected.hs | 5 + .../AddArgWithTypeSynSigContravariant.hs | 5 + .../golden/add-arg/MultiSigFirst.expected.hs | 6 + .../test/data/golden/add-arg/MultiSigFirst.hs | 5 + .../golden/add-arg/MultiSigLast.expected.hs | 7 + .../test/data/golden/add-arg/MultiSigLast.hs | 6 + .../golden/add-arg/MultiSigMiddle.expected.hs | 7 + .../data/golden/add-arg/MultiSigMiddle.hs | 6 + 23 files changed, 376 insertions(+), 42 deletions(-) create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromLet.expected.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromLet.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromWhere.expected.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromWhere.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithLambda.expected.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithLambda.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithSig.expected.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithSig.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithSigAndDocs.expected.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithSigAndDocs.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithTypeSynSig.expected.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithTypeSynSig.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithTypeSynSigContravariant.expected.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithTypeSynSigContravariant.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigFirst.expected.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigFirst.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigLast.expected.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigLast.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigMiddle.expected.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigMiddle.hs diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs index beb5fb52ed..4704afd9eb 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/GHC/ExactPrint.hs @@ -23,6 +23,9 @@ module Development.IDE.GHC.ExactPrint #if MIN_VERSION_ghc(9,2,1) modifySmallestDeclWithM, modifyMgMatchesT, + modifyMgMatchesT', + modifySigWithM, + genAnchor1, #endif #if !MIN_VERSION_ghc(9,2,0) Anns, @@ -111,11 +114,18 @@ import GHC.Parser.Annotation (AnnContext (..), deltaPos) #endif +#if MIN_VERSION_ghc(9,2,1) +import Data.List (partition) +import GHC (Anchor(..), realSrcSpan, AnchorOperation, DeltaPos(..), SrcSpanAnnN) +import GHC.Types.SrcLoc (generatedSrcSpan) +import Control.Lens ((&), _last) +import Control.Lens.Operators ((%~)) +#endif + #if MIN_VERSION_ghc(9,2,0) setPrecedingLines :: Default t => LocatedAn t a -> Int -> Int -> LocatedAn t a setPrecedingLines ast n c = setEntryDP ast (deltaPos n c) #endif - ------------------------------------------------------------------------------ data Log = LogShake Shake.Log deriving Show @@ -449,32 +459,129 @@ graftDecls dst decs0 = Graft $ \dflags a -> do -- -- For example, if you would like to move a where-clause-defined variable to the same -- level as its parent HsDecl, you could use this function. +-- +-- When matching declaration is found in the sub-declarations of `a`, `Just r` is also returned with the new `a`. If +-- not declaration matched, then `Nothing` is returned. modifySmallestDeclWithM :: - forall a m. + forall a m r. (HasDecls a, Monad m) => (SrcSpan -> m Bool) -> - (LHsDecl GhcPs -> TransformT m [LHsDecl GhcPs]) -> + (LHsDecl GhcPs -> TransformT m ([LHsDecl GhcPs], r)) -> a -> - TransformT m a + TransformT m (a, Maybe r) modifySmallestDeclWithM validSpan f a = do - let modifyMatchingDecl [] = pure DL.empty - modifyMatchingDecl (e@(L src _) : rest) = + let modifyMatchingDecl [] = pure (DL.empty, Nothing) + modifyMatchingDecl (ldecl@(L src _) : rest) = lift (validSpan $ locA src) >>= \case True -> do - decs' <- f e - pure $ DL.fromList decs' <> DL.fromList rest - False -> (DL.singleton e <>) <$> modifyMatchingDecl rest - modifyDeclsT (fmap DL.toList . modifyMatchingDecl) a - --- | Modify the each LMatch in a MatchGroup + (decs', r) <- f ldecl + pure $ (DL.fromList decs' <> DL.fromList rest, Just r) + False -> first (DL.singleton ldecl <>) <$> modifyMatchingDecl rest + modifyDeclsT' (fmap (first DL.toList) . modifyMatchingDecl) a + +generatedAnchor :: AnchorOperation -> Anchor +generatedAnchor anchorOp = GHC.Anchor (GHC.realSrcSpan generatedSrcSpan) anchorOp + +setAnchor :: Anchor -> SrcSpanAnnN -> SrcSpanAnnN +setAnchor anc (SrcSpanAnn (EpAnn _ nameAnn comments) span) = + SrcSpanAnn (EpAnn anc nameAnn comments) span +setAnchor _ spanAnnN = spanAnnN + +removeTrailingAnns :: SrcSpanAnnN -> SrcSpanAnnN +removeTrailingAnns (SrcSpanAnn (EpAnn anc nameAnn comments) span) = + let nameAnnSansTrailings = nameAnn {nann_trailing = []} + in SrcSpanAnn (EpAnn anc nameAnnSansTrailings comments) span +removeTrailingAnns spanAnnN = spanAnnN + +-- | Modify the type signature for the given IdP. This function handles splitting a multi-sig +-- SigD into multiple SigD if the type signature is changed. +-- +-- For example, update the type signature for `foo` from `Int` to `Bool`: +-- +-- - foo :: Int +-- + foo :: Bool +-- +-- - foo, bar :: Int +-- + bar :: Int +-- + foo :: Bool +-- +-- - foo, bar, baz :: Int +-- + bar, baz :: Int +-- + foo :: Bool +modifySigWithM :: + forall a m. + (HasDecls a, Monad m) => + IdP GhcPs -> + (LHsSigType GhcPs -> LHsSigType GhcPs) -> + a -> + TransformT m a +modifySigWithM queryId f a = do + let modifyMatchingSigD :: [LHsDecl GhcPs] -> TransformT m (DL.DList (LHsDecl GhcPs)) + modifyMatchingSigD [] = pure (DL.empty) + modifyMatchingSigD (ldecl@(L annSigD (SigD xsig (TypeSig xTypeSig ids (HsWC xHsWc lHsSig)))) : rest) + | queryId `elem` (unLoc <$> ids) = do + let newSig = f lHsSig + -- If this signature update caused no change, then we don't need to split up multi-signatures + if newSig `geq` lHsSig + then pure $ DL.singleton ldecl <> DL.fromList rest + else case partition ((== queryId) . unLoc) ids of + ([L annMatchedId matchedId], otherIds) -> + let matchedId' = L (setAnchor genAnchor0 $ removeTrailingAnns annMatchedId) matchedId + matchedIdSig = + let sig' = SigD xsig (TypeSig xTypeSig [matchedId'] (HsWC xHsWc newSig)) + epAnn = bool (noAnnSrcSpanDP generatedSrcSpan (DifferentLine 1 0)) annSigD (null otherIds) + in L epAnn sig' + otherSig = case otherIds of + [] -> [] + (L (SrcSpanAnn epAnn span) id1:ids) -> [ + let epAnn' = case epAnn of + EpAnn _ nameAnn commentsId1 -> EpAnn genAnchor0 nameAnn commentsId1 + EpAnnNotUsed -> EpAnn genAnchor0 mempty emptyComments + ids' = L (SrcSpanAnn epAnn' span) id1:ids + ids'' = ids' & _last %~ first removeTrailingAnns + in L annSigD (SigD xsig (TypeSig xTypeSig ids'' (HsWC xHsWc lHsSig))) + ] + in pure $ DL.fromList otherSig <> DL.singleton matchedIdSig <> DL.fromList rest + _ -> error "multiple ids matched" + modifyMatchingSigD (ldecl : rest) = (DL.singleton ldecl <>) <$> modifyMatchingSigD rest + modifyDeclsT (fmap DL.toList . modifyMatchingSigD) a + +genAnchor0 :: Anchor +genAnchor0 = generatedAnchor m0 + +genAnchor1 :: Anchor +genAnchor1 = generatedAnchor m1 + +-- | Apply a transformation to the decls contained in @t@ +modifyDeclsT' :: (HasDecls t, HasTransform m) + => ([LHsDecl GhcPs] -> m ([LHsDecl GhcPs], r)) + -> t -> m (t, r) +modifyDeclsT' action t = do + decls <- liftT $ hsDecls t + (decls', r) <- action decls + t' <- liftT $ replaceDecls t decls' + pure (t', r) + +-- | Modify each LMatch in a MatchGroup modifyMgMatchesT :: Monad m => MatchGroup GhcPs (LHsExpr GhcPs) -> (LMatch GhcPs (LHsExpr GhcPs) -> TransformT m (LMatch GhcPs (LHsExpr GhcPs))) -> TransformT m (MatchGroup GhcPs (LHsExpr GhcPs)) -modifyMgMatchesT (MG xMg (L locMatches matches) originMg) f = do - matches' <- mapM f matches - pure $ MG xMg (L locMatches matches') originMg +modifyMgMatchesT mg f = fst <$> modifyMgMatchesT' mg (fmap (, ()) . f) () ((.) pure . const) + +-- | Modify the each LMatch in a MatchGroup +modifyMgMatchesT' :: + Monad m => + MatchGroup GhcPs (LHsExpr GhcPs) -> + (LMatch GhcPs (LHsExpr GhcPs) -> TransformT m (LMatch GhcPs (LHsExpr GhcPs), r)) -> + r -> + (r -> r -> m r) -> + TransformT m (MatchGroup GhcPs (LHsExpr GhcPs), r) +modifyMgMatchesT' (MG xMg (L locMatches matches) originMg) f def combineResults = do + (unzip -> (matches', rs)) <- mapM f matches + r' <- lift $ foldM combineResults def rs + pure $ (MG xMg (L locMatches matches') originMg, r') #endif graftSmallestDeclsWithM :: diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index ee56d75d3b..69047c0aac 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -93,12 +93,14 @@ import qualified Text.Fuzzy.Parallel as TFP import Text.Regex.TDFA (mrAfter, (=~), (=~~)) #if MIN_VERSION_ghc(9,2,1) +import Data.Either.Extra (maybeToEither) import GHC.Types.SrcLoc (generatedSrcSpan) import Language.Haskell.GHC.ExactPrint (noAnnSrcSpanDP1, runTransformT) #endif #if MIN_VERSION_ghc(9,2,0) -import Extra (maybeToEither) +import Control.Monad.Except (lift) +import Debug.Trace import GHC (AddEpAnn (AddEpAnn), Anchor (anchor_op), AnchorOperation (..), @@ -107,7 +109,17 @@ import GHC (AddEpAnn (Ad EpAnn (..), EpaLocation (..), LEpaComment, - LocatedA) + LocatedA, + SrcSpanAnn' (SrcSpanAnn), + SrcSpanAnnA, + SrcSpanAnnN, + TrailingAnn (..), + addTrailingAnnToA, + emptyComments, + noAnn) +import GHC.Hs (IsUnicodeSyntax (..)) +import Language.Haskell.GHC.ExactPrint.Transform (d1) + #else import Language.Haskell.GHC.ExactPrint.Types (Annotation (annsDP), DeltaPos, @@ -958,8 +970,6 @@ newDefinitionAction IdeOptions {..} parsedModule Range {_start} name typ -- When we receive either of these errors, we produce a text edit that will add a new argument (as a new pattern in the -- last position of each LHS of the top-level bindings for this HsDecl). -- --- TODO Include logic to also update the type signature of a binding --- -- NOTE When adding a new argument to a declaration, the corresponding argument's type in declaration's signature might -- not be the last type in the signature, such as: -- foo :: a -> b -> c -> d @@ -973,31 +983,100 @@ suggestAddArgument parsedModule Diagnostic {_message, _range} where message = unifySpaces _message --- TODO use typ to modify type signature +-- Given a name for the new binding, add a new pattern to the match in the last position, +-- returning how many patterns there were in this match prior to the transformation: +-- addArgToMatch "foo" `bar arg1 arg2 = ...` +-- => (`bar arg1 arg2 foo = ...`, 2) +addArgToMatch :: T.Text -> GenLocated l (Match GhcPs body) -> (GenLocated l (Match GhcPs body), Int) +addArgToMatch name (L locMatch (Match xMatch ctxMatch pats rhs)) = + let unqualName = mkRdrUnqual $ mkVarOcc $ T.unpack name + newPat = L (noAnnSrcSpanDP1 generatedSrcSpan) $ VarPat NoExtField (noLocA unqualName) + in (L locMatch (Match xMatch ctxMatch (pats <> [newPat]) rhs), length pats) + +-- Attempt to insert a binding pattern into each match for the given LHsDecl; succeeds only if the function is a FunBind. +-- Also return: +-- - the declaration's name +-- - the number of bound patterns in the declaration's matches prior to the transformation +-- +-- For example: +-- insertArg "new_pat" `foo bar baz = 1` +-- => (`foo bar baz new_pat = 1`, Just ("foo", 2)) +appendFinalPatToMatches :: T.Text -> LHsDecl GhcPs -> TransformT (Either ResponseError) (LHsDecl GhcPs, Maybe (GenLocated SrcSpanAnnN RdrName, Int)) +appendFinalPatToMatches name = \case + (L locDecl (ValD xVal (FunBind xFunBind idFunBind mg coreFunBind))) -> do + (mg', numPatsMay) <- modifyMgMatchesT' mg (pure . second Just . addArgToMatch name) Nothing combineMatchNumPats + numPats <- lift $ maybeToEither (responseError "Unexpected empty match group in HsDecl") numPatsMay + let decl' = L locDecl (ValD xVal (FunBind xFunBind idFunBind mg' coreFunBind)) + pure (decl', Just (idFunBind, numPats)) + decl -> pure (decl, Nothing) + where + combineMatchNumPats Nothing other = pure other + combineMatchNumPats other Nothing = pure other + combineMatchNumPats (Just l) (Just r) + | l == r = pure (Just l) + | otherwise = Left $ responseError "Unexpected different numbers of patterns in HsDecl MatchGroup" + +-- The add argument works as follows: +-- 1. Attempt to add the given name as the last pattern of the declaration that contains `range`. +-- 2. If such a declaration exists, use that declaration's name to modify the signature of said declaration, if it +-- has a type signature. +-- +-- NOTE For the following situation, the type signature is not updated (it's unclear what should happen): +-- type FunctionTySyn = () -> Int +-- foo :: FunctionTySyn +-- foo () = new_def +-- +-- TODO instead of inserting a typed hole; use GHC's suggested type from the error addArgumentAction :: ParsedModule -> Range -> T.Text -> Maybe T.Text -> Either ResponseError [(T.Text, [TextEdit])] -addArgumentAction (ParsedModule _ parsedSource _ _) range name _typ = - do - let addArgToMatch (L locMatch (Match xMatch ctxMatch pats rhs)) = do - let unqualName = mkRdrUnqual $ mkVarOcc $ T.unpack name - let newPat = L (noAnnSrcSpanDP1 generatedSrcSpan) $ VarPat NoExtField (noLocA unqualName) - pure $ L locMatch (Match xMatch ctxMatch (pats <> [newPat]) rhs) - insertArg = \case - (L locDecl (ValD xVal (FunBind xFunBind idFunBind mg coreFunBind))) -> do - mg' <- modifyMgMatchesT mg addArgToMatch - let decl' = L locDecl (ValD xVal (FunBind xFunBind idFunBind mg' coreFunBind)) - pure [decl'] - decl -> pure [decl] - case runTransformT $ modifySmallestDeclWithM spanContainsRangeOrErr insertArg (makeDeltaAst parsedSource) of - Left err -> Left err - Right (newSource, _, _) -> - let diff = makeDiffTextEdit (T.pack $ exactPrint parsedSource) (T.pack $ exactPrint newSource) - in pure [("Add argument ‘" <> name <> "’ to function", fromLspList diff)] - where - spanContainsRangeOrErr = maybeToEither (responseError "SrcSpan was not valid range") . (`spanContainsRange` range) -#endif +addArgumentAction (ParsedModule _ moduleSrc _ _) range name _typ = do + (newSource, _, _) <- runTransformT $ do + (moduleSrc', join -> matchedDeclNameMay) <- addNameAsLastArgOfMatchingDecl (makeDeltaAst moduleSrc) + case matchedDeclNameMay of + Just (matchedDeclName, numPats) -> modifySigWithM (unLoc matchedDeclName) (addTyHoleToTySigArg numPats) moduleSrc' + Nothing -> pure moduleSrc' + let diff = makeDiffTextEdit (T.pack $ exactPrint moduleSrc) (T.pack $ exactPrint newSource) + pure [("Add argument ‘" <> name <> "’ to function", fromLspList diff)] + where + addNameAsLastArgOfMatchingDecl = modifySmallestDeclWithM spanContainsRangeOrErr addNameAsLastArg + addNameAsLastArg = fmap (first (:[])) . appendFinalPatToMatches name + + spanContainsRangeOrErr = maybeToEither (responseError "SrcSpan was not valid range") . (`spanContainsRange` range) + +-- Transform an LHsType into a list of arguments and return type, to make transformations easier. +hsTypeToFunTypeAsList :: LHsType GhcPs -> ([(SrcSpanAnnA, XFunTy GhcPs, HsArrow GhcPs, LHsType GhcPs)], LHsType GhcPs) +hsTypeToFunTypeAsList = \case + L spanAnnA (HsFunTy xFunTy arrow lhs rhs) -> + let (rhsArgs, rhsRes) = hsTypeToFunTypeAsList rhs + in ((spanAnnA, xFunTy, arrow, lhs):rhsArgs, rhsRes) + ty -> ([], ty) + +-- The inverse of `hsTypeToFunTypeAsList` +hsTypeFromFunTypeAsList :: ([(SrcSpanAnnA, XFunTy GhcPs, HsArrow GhcPs, LHsType GhcPs)], LHsType GhcPs) -> LHsType GhcPs +hsTypeFromFunTypeAsList (args, res) = + foldr (\(spanAnnA, xFunTy, arrow, argTy) res -> L spanAnnA $ HsFunTy xFunTy arrow argTy res) res args + +-- Add a typed hole to a type signature in the given argument position: +-- 0 `foo :: ()` => foo :: _ -> () +-- 2 `foo :: FunctionTySyn` => foo :: FunctionTySyn +-- 1 `foo :: () -> () -> Int` => foo :: () -> _ -> () -> Int +addTyHoleToTySigArg :: Int -> LHsSigType GhcPs -> (LHsSigType GhcPs) +addTyHoleToTySigArg loc (L annHsSig (HsSig xHsSig tyVarBndrs lsigTy)) = + let (args, res) = hsTypeToFunTypeAsList lsigTy + wildCardAnn = SrcSpanAnn (EpAnn genAnchor1 (AnnListItem [AddRarrowAnn d1]) emptyComments) generatedSrcSpan + newArg = (SrcSpanAnn mempty generatedSrcSpan, noAnn, HsUnrestrictedArrow NormalSyntax, L wildCardAnn $ HsWildCardTy noExtField) + -- NOTE if the location that the argument wants to be placed at is not one more than the number of arguments + -- in the signature, then we return the original type signature. + -- This situation most likely occurs due to a function type synonym in the signature + insertArg n _ | n < 0 = error "Not possible" + insertArg 0 as = newArg:as + insertArg _ [] = [] + insertArg n (a:as) = a : insertArg (n - 1) as + lsigTy' = hsTypeFromFunTypeAsList (insertArg loc args, res) + in L annHsSig (HsSig xHsSig tyVarBndrs lsigTy') fromLspList :: List a -> [a] fromLspList (List a) = a +#endif suggestFillTypeWildcard :: Diagnostic -> [(T.Text, TextEdit)] suggestFillTypeWildcard Diagnostic{_range=_range,..} diff --git a/plugins/hls-refactor-plugin/test/Main.hs b/plugins/hls-refactor-plugin/test/Main.hs index b1477b1066..46fb1fb616 100644 --- a/plugins/hls-refactor-plugin/test/Main.hs +++ b/plugins/hls-refactor-plugin/test/Main.hs @@ -54,7 +54,8 @@ import Test.Tasty.HUnit import Text.Regex.TDFA ((=~)) -import Development.IDE.Plugin.CodeAction (matchRegExMultipleImports) +import Development.IDE.Plugin.CodeAction (bindingsPluginDescriptor, + matchRegExMultipleImports) import Test.Hls import Control.Applicative (liftA2) @@ -2371,10 +2372,38 @@ addFunctionArgumentTests = liftIO $ actionTitle @?= "Add argument ‘select’ to function" executeCodeAction action contentAfterAction <- documentContents docB - liftIO $ contentAfterAction @?= T.unlines foo' + liftIO $ contentAfterAction @?= T.unlines foo', + mkGoldenAddArgTest "AddArgWithSig" (R 1 0 1 50), + mkGoldenAddArgTest "AddArgWithSigAndDocs" (R 8 0 8 50), + mkGoldenAddArgTest "AddArgFromLet" (R 2 0 2 50), + mkGoldenAddArgTest "AddArgFromWhere" (R 3 0 3 50), + mkGoldenAddArgTest "AddArgWithTypeSynSig" (R 2 0 2 50), + mkGoldenAddArgTest "AddArgWithTypeSynSigContravariant" (R 2 0 2 50), + mkGoldenAddArgTest "AddArgWithLambda" (R 1 0 1 50), + mkGoldenAddArgTest "MultiSigFirst" (R 2 0 2 50), + mkGoldenAddArgTest "MultiSigLast" (R 2 0 2 50), + mkGoldenAddArgTest "MultiSigMiddle" (R 2 0 2 50) ] #endif +mkGoldenAddArgTest :: FilePath -> Range -> TestTree +mkGoldenAddArgTest testFileName range = do + let action docB = do + _ <- waitForDiagnostics + InR action@CodeAction {_title = actionTitle} : _ <- + filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) + <$> getCodeActions docB range + liftIO $ actionTitle @?= "Add argument ‘new_def’ to function" + executeCodeAction action + goldenWithHaskellDoc + (Refactor.bindingsPluginDescriptor mempty "ghcide-code-actions-bindings") + (testFileName <> " (golden)") + "test/data/golden/add-arg" + testFileName + "expected" + "hs" + action + deleteUnusedDefinitionTests :: TestTree deleteUnusedDefinitionTests = testGroup "delete unused definition action" [ testSession "delete unused top level binding" $ diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromLet.expected.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromLet.expected.hs new file mode 100644 index 0000000000..f351aed465 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromLet.expected.hs @@ -0,0 +1,6 @@ +foo :: Bool -> _ -> Int +foo True new_def = + let bar = new_def + in bar + +foo False new_def = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromLet.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromLet.hs new file mode 100644 index 0000000000..091613d232 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromLet.hs @@ -0,0 +1,6 @@ +foo :: Bool -> Int +foo True = + let bar = new_def + in bar + +foo False = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromWhere.expected.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromWhere.expected.hs new file mode 100644 index 0000000000..d208452548 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromWhere.expected.hs @@ -0,0 +1,6 @@ +foo :: Bool -> _ -> Int +foo True new_def = bar + where + bar = new_def + +foo False new_def = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromWhere.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromWhere.hs new file mode 100644 index 0000000000..0047eedb6e --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromWhere.hs @@ -0,0 +1,6 @@ +foo :: Bool -> Int +foo True = bar + where + bar = new_def + +foo False = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithLambda.expected.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithLambda.expected.hs new file mode 100644 index 0000000000..3fcc2dbb4c --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithLambda.expected.hs @@ -0,0 +1,4 @@ +foo :: Bool -> _ -> () -> Int +foo True new_def = \() -> new_def [True] + +foo False new_def = const 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithLambda.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithLambda.hs new file mode 100644 index 0000000000..d08c0ef496 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithLambda.hs @@ -0,0 +1,4 @@ +foo :: Bool -> () -> Int +foo True = \() -> new_def [True] + +foo False = const 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithSig.expected.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithSig.expected.hs new file mode 100644 index 0000000000..f8082bd027 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithSig.expected.hs @@ -0,0 +1,4 @@ +foo :: Bool -> _ -> Int +foo True new_def = new_def [True] + +foo False new_def = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithSig.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithSig.hs new file mode 100644 index 0000000000..3fa44a6dfe --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithSig.hs @@ -0,0 +1,4 @@ +foo :: Bool -> Int +foo True = new_def [True] + +foo False = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithSigAndDocs.expected.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithSigAndDocs.expected.hs new file mode 100644 index 0000000000..12927c7dce --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithSigAndDocs.expected.hs @@ -0,0 +1,11 @@ +foo :: + -- c1 + Bool -- c2 + -- c3 + -> -- c4 + -- | c5 + () -- c6 + -> _ -> Int +foo True () new_def = new_def [True] + +foo False () new_def = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithSigAndDocs.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithSigAndDocs.hs new file mode 100644 index 0000000000..f9033dce3f --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithSigAndDocs.hs @@ -0,0 +1,11 @@ +foo :: + -- c1 + Bool -- c2 + -- c3 + -> -- c4 + -- | c5 + () -- c6 + -> Int +foo True () = new_def [True] + +foo False () = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithTypeSynSig.expected.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithTypeSynSig.expected.hs new file mode 100644 index 0000000000..e36ca8f89d --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithTypeSynSig.expected.hs @@ -0,0 +1,5 @@ +type FunctionTySyn = Bool -> Int +foo :: FunctionTySyn +foo True new_def = new_def [True] + +foo False new_def = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithTypeSynSig.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithTypeSynSig.hs new file mode 100644 index 0000000000..1843a5d460 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithTypeSynSig.hs @@ -0,0 +1,5 @@ +type FunctionTySyn = Bool -> Int +foo :: FunctionTySyn +foo True = new_def [True] + +foo False = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithTypeSynSigContravariant.expected.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithTypeSynSigContravariant.expected.hs new file mode 100644 index 0000000000..e9735428f2 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithTypeSynSigContravariant.expected.hs @@ -0,0 +1,5 @@ +type FunctionTySyn = Bool -> Int +foo :: FunctionTySyn -> () -> _ -> Int +foo True () new_def = new_def [True] + +foo False () new_def = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithTypeSynSigContravariant.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithTypeSynSigContravariant.hs new file mode 100644 index 0000000000..cf2b67f63a --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgWithTypeSynSigContravariant.hs @@ -0,0 +1,5 @@ +type FunctionTySyn = Bool -> Int +foo :: FunctionTySyn -> () -> Int +foo True () = new_def [True] + +foo False () = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigFirst.expected.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigFirst.expected.hs new file mode 100644 index 0000000000..66611817ef --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigFirst.expected.hs @@ -0,0 +1,6 @@ +bar :: Bool -> Int +foo :: Bool -> _ -> Int +bar = const 1 +foo True new_def = new_def [True] + +foo False new_def = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigFirst.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigFirst.hs new file mode 100644 index 0000000000..00ef9ba769 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigFirst.hs @@ -0,0 +1,5 @@ +foo, bar :: Bool -> Int +bar = const 1 +foo True = new_def [True] + +foo False = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigLast.expected.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigLast.expected.hs new file mode 100644 index 0000000000..489f6c2ba8 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigLast.expected.hs @@ -0,0 +1,7 @@ +baz, bar :: Bool -> Int +foo :: Bool -> _ -> Int +bar = const 1 +foo True new_def = new_def [True] + +foo False new_def = 1 +baz = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigLast.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigLast.hs new file mode 100644 index 0000000000..d3e8846728 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigLast.hs @@ -0,0 +1,6 @@ +baz, bar, foo :: Bool -> Int +bar = const 1 +foo True = new_def [True] + +foo False = 1 +baz = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigMiddle.expected.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigMiddle.expected.hs new file mode 100644 index 0000000000..489f6c2ba8 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigMiddle.expected.hs @@ -0,0 +1,7 @@ +baz, bar :: Bool -> Int +foo :: Bool -> _ -> Int +bar = const 1 +foo True new_def = new_def [True] + +foo False new_def = 1 +baz = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigMiddle.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigMiddle.hs new file mode 100644 index 0000000000..80cada1601 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultiSigMiddle.hs @@ -0,0 +1,6 @@ +baz, foo, bar :: Bool -> Int +bar = const 1 +foo True = new_def [True] + +foo False = 1 +baz = 1 \ No newline at end of file From 9e2037696c12798dd31045eb9c79af4db9054b08 Mon Sep 17 00:00:00 2001 From: fendor Date: Wed, 16 Nov 2022 13:23:07 +0100 Subject: [PATCH 193/213] Add hls-cabal-fmt-plugin to hackage release CI script and HLS library (#3335) * Add hls-cabal-fmt-plugin to hackage release CI script * Add cabalfmt common section to HLS Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Michael Peyton Jones --- .github/workflows/hackage.yml | 1 + haskell-language-server.cabal | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/hackage.yml b/.github/workflows/hackage.yml index 68f7de50d6..f909ef6fb9 100644 --- a/.github/workflows/hackage.yml +++ b/.github/workflows/hackage.yml @@ -37,6 +37,7 @@ jobs: "hls-splice-plugin", "hls-tactics-plugin", "hls-call-hierarchy-plugin", "hls-alternate-number-format-plugin", "hls-qualify-imported-names-plugin", "hls-code-range-plugin", + "hls-cabal-fmt-plugin", "haskell-language-server"] ghc: [ "9.0.2" , "8.10.7" diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index f7e1b34980..cc69eed3a1 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -359,6 +359,7 @@ library , pedantic -- plugins , callHierarchy + , cabalfmt , changeTypeSignature , class , haddockComments From 6dd5a3b29f857a73f2c526e93003b26928c3d71d Mon Sep 17 00:00:00 2001 From: Marc Jakobi Date: Fri, 18 Nov 2022 11:00:00 +0100 Subject: [PATCH 194/213] Split neovim/vim configurations (#3342) * Separate sections for Neovim and Vim * Add more up-to-date information for Neovim Co-authored-by: Marc Jakobi --- docs/configuration.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 5162e5f7f0..2ff795e54e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -260,11 +260,22 @@ Open `Preferences > Package Settings > LSP > Settings` and add the following "ha See [the Sublime Text LSP documentation](https://lsp.sublimetext.io) for information on configuring the client. In particular, you can add a "settings" key to the "haskell-language-server" setting to configure specific HLS plugins as described elsewhere in these docs. -### Vim or Neovim +### [Neovim](https://neovim.io) + +Neovim provides a [native LSP implementation with a Lua framework](https://neovim.io/doc/user/lsp). +Plugins that streamline the setup of `haskell-language-server` using Neovim's built-in LSP framework include: + +* [haskell-tools.nvim](https://github.com/MrcJkb/haskell-tools.nvim): A plugin with a focus on Haskell tooling, including `haskell-language-server`. +* [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig): A collection of quickstart configs for various LSP servers. + - Includes a basic [`hls` configuration](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#hls). + +Neovim is also compatible with the [Vim plugins](#vim). + +### [Vim](https://www.vim.org) You can use [Coc](https://github.com/neoclide/coc.nvim), [LanguageClient-neovim](https://github.com/autozimu/LanguageClient-neovim) or any other Vim Language server protocol client. -Coc is recommend since it is the only complete LSP implementation for Vim and Neovim and offers snippets and floating documentation out of the box. +Coc is recommend since it is the only complete LSP implementation for Vim and offers snippets and floating documentation out of the box. #### Coc From bc18cedf157e19c75c5676d8defcb982d6488fad Mon Sep 17 00:00:00 2001 From: santiweight Date: Sun, 20 Nov 2022 07:27:26 -0800 Subject: [PATCH 195/213] refactor: extract AddArgument modules (#3339) * refact: clean up add-arg tests * refact: extract Test.AddArgument * refact: extract AddArgument logic * refact: remove unnecessary cpp * wip Co-authored-by: Santiago Weight --- .../hls-refactor-plugin.cabal | 3 + .../src/Development/IDE/Plugin/CodeAction.hs | 186 +------------- .../IDE/Plugin/Plugins/AddArgument.hs | 157 +++++++++++ .../IDE/Plugin/Plugins/Diagnostic.hs | 53 ++++ plugins/hls-refactor-plugin/test/Main.hs | 243 +----------------- .../test/Test/AddArgument.hs | 74 ++++++ .../AddArgFromWhereComments.expected.hs | 6 + .../golden/add-arg/AddArgFromWhereComments.hs | 6 + .../test/data/golden/add-arg/Hole.expected.hs | 1 + .../test/data/golden/add-arg/Hole.hs | 1 + .../add-arg/MultipleDeclAlts.expected.hs | 2 + .../data/golden/add-arg/MultipleDeclAlts.hs | 2 + .../add-arg/NoTypeSuggestion.expected.hs | 1 + .../data/golden/add-arg/NoTypeSuggestion.hs | 1 + 14 files changed, 315 insertions(+), 421 deletions(-) create mode 100644 plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/AddArgument.hs create mode 100644 plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/Diagnostic.hs create mode 100644 plugins/hls-refactor-plugin/test/Test/AddArgument.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromWhereComments.expected.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromWhereComments.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/Hole.expected.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/Hole.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/MultipleDeclAlts.expected.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/MultipleDeclAlts.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/NoTypeSuggestion.expected.hs create mode 100644 plugins/hls-refactor-plugin/test/data/golden/add-arg/NoTypeSuggestion.hs diff --git a/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal b/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal index 80979f2f6e..3f888cc946 100644 --- a/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal +++ b/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal @@ -34,6 +34,8 @@ library other-modules: Development.IDE.Plugin.CodeAction.Args Development.IDE.Plugin.CodeAction.ExactPrint Development.IDE.Plugin.CodeAction.PositionIndexed + Development.IDE.Plugin.Plugins.AddArgument + Development.IDE.Plugin.Plugins.Diagnostic default-extensions: BangPatterns CPP @@ -97,6 +99,7 @@ test-suite tests default-language: Haskell2010 hs-source-dirs: test main-is: Main.hs + other-modules: Test.AddArgument ghc-options: -O0 -threaded -rtsopts -with-rtsopts=-N -Wunused-imports build-depends: , base diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index 69047c0aac..a28af0fa18 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -38,7 +38,6 @@ import Data.Ord (comparing) import qualified Data.Set as S import qualified Data.Text as T import qualified Data.Text.Utf16.Rope as Rope -import Data.Tuple.Extra (first) import Development.IDE.Core.Rules import Development.IDE.Core.RuleTypes import Development.IDE.Core.Service @@ -57,6 +56,8 @@ import Development.IDE.Plugin.CodeAction.ExactPrint import Development.IDE.Plugin.CodeAction.PositionIndexed import Development.IDE.Plugin.CodeAction.Util import Development.IDE.Plugin.Completions.Types +import qualified Development.IDE.Plugin.Plugins.AddArgument +import Development.IDE.Plugin.Plugins.Diagnostic import Development.IDE.Plugin.TypeLenses (suggestSignature) import Development.IDE.Types.Exports import Development.IDE.Types.Location @@ -65,8 +66,7 @@ import Development.IDE.Types.Logger hiding import Development.IDE.Types.Options import GHC.Exts (fromList) import qualified GHC.LanguageExtensions as Lang -import Ide.PluginUtils (makeDiffTextEdit, - subRange) +import Ide.PluginUtils (subRange) import Ide.Types import qualified Language.LSP.Server as LSP import Language.LSP.Types (ApplyWorkspaceEditParams (..), @@ -92,15 +92,7 @@ import Language.LSP.VFS (VirtualFile, import qualified Text.Fuzzy.Parallel as TFP import Text.Regex.TDFA (mrAfter, (=~), (=~~)) -#if MIN_VERSION_ghc(9,2,1) -import Data.Either.Extra (maybeToEither) -import GHC.Types.SrcLoc (generatedSrcSpan) -import Language.Haskell.GHC.ExactPrint (noAnnSrcSpanDP1, - runTransformT) -#endif #if MIN_VERSION_ghc(9,2,0) -import Control.Monad.Except (lift) -import Debug.Trace import GHC (AddEpAnn (AddEpAnn), Anchor (anchor_op), AnchorOperation (..), @@ -109,17 +101,7 @@ import GHC (AddEpAnn (Ad EpAnn (..), EpaLocation (..), LEpaComment, - LocatedA, - SrcSpanAnn' (SrcSpanAnn), - SrcSpanAnnA, - SrcSpanAnnN, - TrailingAnn (..), - addTrailingAnnToA, - emptyComments, - noAnn) -import GHC.Hs (IsUnicodeSyntax (..)) -import Language.Haskell.GHC.ExactPrint.Transform (d1) - + LocatedA) #else import Language.Haskell.GHC.ExactPrint.Types (Annotation (annsDP), DeltaPos, @@ -189,9 +171,7 @@ bindingsPluginDescriptor recorder plId = mkExactprintPluginDescriptor recorder $ , wrap suggestImplicitParameter #endif , wrap suggestNewDefinition -#if MIN_VERSION_ghc(9,2,1) - , wrap suggestAddArgument -#endif + , wrap Development.IDE.Plugin.Plugins.AddArgument.plugin , wrap suggestDeleteUnusedBinding ] plId @@ -905,34 +885,6 @@ suggestReplaceIdentifier contents Diagnostic{_range=_range,..} = [ ("Replace with ‘" <> name <> "’", [mkRenameEdit contents _range name]) | name <- renameSuggestions ] | otherwise = [] -matchVariableNotInScope :: T.Text -> Maybe (T.Text, Maybe T.Text) -matchVariableNotInScope message - -- * Variable not in scope: - -- suggestAcion :: Maybe T.Text -> Range -> Range - -- * Variable not in scope: - -- suggestAcion - | Just (name, typ) <- matchVariableNotInScopeTyped message = Just (name, Just typ) - | Just name <- matchVariableNotInScopeUntyped message = Just (name, Nothing) - | otherwise = Nothing - where - matchVariableNotInScopeTyped message - | Just [name, typ] <- matchRegexUnifySpaces message "Variable not in scope: ([^ ]+) :: ([^*•]+)" = - Just (name, typ) - | otherwise = Nothing - matchVariableNotInScopeUntyped message - | Just [name] <- matchRegexUnifySpaces message "Variable not in scope: ([^ ]+)" = - Just name - | otherwise = Nothing - -matchFoundHole :: T.Text -> Maybe (T.Text, T.Text) -matchFoundHole message - | Just [name, typ] <- matchRegexUnifySpaces message "Found hole: _([^ ]+) :: ([^*•]+) Or perhaps" = - Just (name, typ) - | otherwise = Nothing - -matchFoundHoleIncludeUnderscore :: T.Text -> Maybe (T.Text, T.Text) -matchFoundHoleIncludeUnderscore message = first ("_" <>) <$> matchFoundHole message - suggestNewDefinition :: IdeOptions -> ParsedModule -> Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])] suggestNewDefinition ideOptions parsedModule contents Diagnostic {_message, _range} | Just (name, typ) <- matchVariableNotInScope message = @@ -962,121 +914,6 @@ newDefinitionAction IdeOptions {..} parsedModule Range {_start} name typ sig = name <> colon <> T.dropWhileEnd isSpace (fromMaybe "_" typ) ParsedModule {pm_parsed_source = L _ HsModule {hsmodDecls}} = parsedModule -#if MIN_VERSION_ghc(9,2,1) --- When GHC tells us that a variable is not bound, it will tell us either: --- - there is an unbound variable with a given type --- - there is an unbound variable (GHC provides no type suggestion) --- --- When we receive either of these errors, we produce a text edit that will add a new argument (as a new pattern in the --- last position of each LHS of the top-level bindings for this HsDecl). --- --- NOTE When adding a new argument to a declaration, the corresponding argument's type in declaration's signature might --- not be the last type in the signature, such as: --- foo :: a -> b -> c -> d --- foo a b = \c -> ... --- In this case a new argument would have to add its type between b and c in the signature. -suggestAddArgument :: ParsedModule -> Diagnostic -> Either ResponseError [(T.Text, [TextEdit])] -suggestAddArgument parsedModule Diagnostic {_message, _range} - | Just (name, typ) <- matchVariableNotInScope message = addArgumentAction parsedModule _range name typ - | Just (name, typ) <- matchFoundHoleIncludeUnderscore message = addArgumentAction parsedModule _range name (Just typ) - | otherwise = pure [] - where - message = unifySpaces _message - --- Given a name for the new binding, add a new pattern to the match in the last position, --- returning how many patterns there were in this match prior to the transformation: --- addArgToMatch "foo" `bar arg1 arg2 = ...` --- => (`bar arg1 arg2 foo = ...`, 2) -addArgToMatch :: T.Text -> GenLocated l (Match GhcPs body) -> (GenLocated l (Match GhcPs body), Int) -addArgToMatch name (L locMatch (Match xMatch ctxMatch pats rhs)) = - let unqualName = mkRdrUnqual $ mkVarOcc $ T.unpack name - newPat = L (noAnnSrcSpanDP1 generatedSrcSpan) $ VarPat NoExtField (noLocA unqualName) - in (L locMatch (Match xMatch ctxMatch (pats <> [newPat]) rhs), length pats) - --- Attempt to insert a binding pattern into each match for the given LHsDecl; succeeds only if the function is a FunBind. --- Also return: --- - the declaration's name --- - the number of bound patterns in the declaration's matches prior to the transformation --- --- For example: --- insertArg "new_pat" `foo bar baz = 1` --- => (`foo bar baz new_pat = 1`, Just ("foo", 2)) -appendFinalPatToMatches :: T.Text -> LHsDecl GhcPs -> TransformT (Either ResponseError) (LHsDecl GhcPs, Maybe (GenLocated SrcSpanAnnN RdrName, Int)) -appendFinalPatToMatches name = \case - (L locDecl (ValD xVal (FunBind xFunBind idFunBind mg coreFunBind))) -> do - (mg', numPatsMay) <- modifyMgMatchesT' mg (pure . second Just . addArgToMatch name) Nothing combineMatchNumPats - numPats <- lift $ maybeToEither (responseError "Unexpected empty match group in HsDecl") numPatsMay - let decl' = L locDecl (ValD xVal (FunBind xFunBind idFunBind mg' coreFunBind)) - pure (decl', Just (idFunBind, numPats)) - decl -> pure (decl, Nothing) - where - combineMatchNumPats Nothing other = pure other - combineMatchNumPats other Nothing = pure other - combineMatchNumPats (Just l) (Just r) - | l == r = pure (Just l) - | otherwise = Left $ responseError "Unexpected different numbers of patterns in HsDecl MatchGroup" - --- The add argument works as follows: --- 1. Attempt to add the given name as the last pattern of the declaration that contains `range`. --- 2. If such a declaration exists, use that declaration's name to modify the signature of said declaration, if it --- has a type signature. --- --- NOTE For the following situation, the type signature is not updated (it's unclear what should happen): --- type FunctionTySyn = () -> Int --- foo :: FunctionTySyn --- foo () = new_def --- --- TODO instead of inserting a typed hole; use GHC's suggested type from the error -addArgumentAction :: ParsedModule -> Range -> T.Text -> Maybe T.Text -> Either ResponseError [(T.Text, [TextEdit])] -addArgumentAction (ParsedModule _ moduleSrc _ _) range name _typ = do - (newSource, _, _) <- runTransformT $ do - (moduleSrc', join -> matchedDeclNameMay) <- addNameAsLastArgOfMatchingDecl (makeDeltaAst moduleSrc) - case matchedDeclNameMay of - Just (matchedDeclName, numPats) -> modifySigWithM (unLoc matchedDeclName) (addTyHoleToTySigArg numPats) moduleSrc' - Nothing -> pure moduleSrc' - let diff = makeDiffTextEdit (T.pack $ exactPrint moduleSrc) (T.pack $ exactPrint newSource) - pure [("Add argument ‘" <> name <> "’ to function", fromLspList diff)] - where - addNameAsLastArgOfMatchingDecl = modifySmallestDeclWithM spanContainsRangeOrErr addNameAsLastArg - addNameAsLastArg = fmap (first (:[])) . appendFinalPatToMatches name - - spanContainsRangeOrErr = maybeToEither (responseError "SrcSpan was not valid range") . (`spanContainsRange` range) - --- Transform an LHsType into a list of arguments and return type, to make transformations easier. -hsTypeToFunTypeAsList :: LHsType GhcPs -> ([(SrcSpanAnnA, XFunTy GhcPs, HsArrow GhcPs, LHsType GhcPs)], LHsType GhcPs) -hsTypeToFunTypeAsList = \case - L spanAnnA (HsFunTy xFunTy arrow lhs rhs) -> - let (rhsArgs, rhsRes) = hsTypeToFunTypeAsList rhs - in ((spanAnnA, xFunTy, arrow, lhs):rhsArgs, rhsRes) - ty -> ([], ty) - --- The inverse of `hsTypeToFunTypeAsList` -hsTypeFromFunTypeAsList :: ([(SrcSpanAnnA, XFunTy GhcPs, HsArrow GhcPs, LHsType GhcPs)], LHsType GhcPs) -> LHsType GhcPs -hsTypeFromFunTypeAsList (args, res) = - foldr (\(spanAnnA, xFunTy, arrow, argTy) res -> L spanAnnA $ HsFunTy xFunTy arrow argTy res) res args - --- Add a typed hole to a type signature in the given argument position: --- 0 `foo :: ()` => foo :: _ -> () --- 2 `foo :: FunctionTySyn` => foo :: FunctionTySyn --- 1 `foo :: () -> () -> Int` => foo :: () -> _ -> () -> Int -addTyHoleToTySigArg :: Int -> LHsSigType GhcPs -> (LHsSigType GhcPs) -addTyHoleToTySigArg loc (L annHsSig (HsSig xHsSig tyVarBndrs lsigTy)) = - let (args, res) = hsTypeToFunTypeAsList lsigTy - wildCardAnn = SrcSpanAnn (EpAnn genAnchor1 (AnnListItem [AddRarrowAnn d1]) emptyComments) generatedSrcSpan - newArg = (SrcSpanAnn mempty generatedSrcSpan, noAnn, HsUnrestrictedArrow NormalSyntax, L wildCardAnn $ HsWildCardTy noExtField) - -- NOTE if the location that the argument wants to be placed at is not one more than the number of arguments - -- in the signature, then we return the original type signature. - -- This situation most likely occurs due to a function type synonym in the signature - insertArg n _ | n < 0 = error "Not possible" - insertArg 0 as = newArg:as - insertArg _ [] = [] - insertArg n (a:as) = a : insertArg (n - 1) as - lsigTy' = hsTypeFromFunTypeAsList (insertArg loc args, res) - in L annHsSig (HsSig xHsSig tyVarBndrs lsigTy') - -fromLspList :: List a -> [a] -fromLspList (List a) = a -#endif suggestFillTypeWildcard :: Diagnostic -> [(T.Text, TextEdit)] suggestFillTypeWildcard Diagnostic{_range=_range,..} @@ -2169,29 +2006,16 @@ rangesForBinding' b (L (locA -> l) (IEThingWith _ thing _ inners)) #endif rangesForBinding' _ _ = [] --- | 'matchRegex' combined with 'unifySpaces' -matchRegexUnifySpaces :: T.Text -> T.Text -> Maybe [T.Text] -matchRegexUnifySpaces message = matchRegex (unifySpaces message) - -- | 'allMatchRegex' combined with 'unifySpaces' allMatchRegexUnifySpaces :: T.Text -> T.Text -> Maybe [[T.Text]] allMatchRegexUnifySpaces message = allMatchRegex (unifySpaces message) --- | Returns Just (the submatches) for the first capture, or Nothing. -matchRegex :: T.Text -> T.Text -> Maybe [T.Text] -matchRegex message regex = case message =~~ regex of - Just (_ :: T.Text, _ :: T.Text, _ :: T.Text, bindings) -> Just bindings - Nothing -> Nothing - -- | Returns Just (all matches) for the first capture, or Nothing. allMatchRegex :: T.Text -> T.Text -> Maybe [[T.Text]] allMatchRegex message regex = message =~~ regex -unifySpaces :: T.Text -> T.Text -unifySpaces = T.unwords . T.words - -- functions to help parse multiple import suggestions -- | Returns the first match if found diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/AddArgument.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/AddArgument.hs new file mode 100644 index 0000000000..d7e59c1db2 --- /dev/null +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/AddArgument.hs @@ -0,0 +1,157 @@ +{-# LANGUAGE CPP #-} +module Development.IDE.Plugin.Plugins.AddArgument (plugin) where + +#if !MIN_VERSION_ghc(9,2,1) +import qualified Data.Text as T +import Language.LSP.Types +#else +import Control.Monad (join) +import Control.Monad.Except (lift) +import Data.Bifunctor (Bifunctor (..)) +import Data.Either.Extra (maybeToEither) +import qualified Data.Text as T +import Development.IDE.GHC.Compat +import Development.IDE.GHC.Compat.ExactPrint (exactPrint, + makeDeltaAst) +import Development.IDE.GHC.Error (spanContainsRange) +import Development.IDE.GHC.ExactPrint (genAnchor1, + modifyMgMatchesT', + modifySigWithM, + modifySmallestDeclWithM) +import Development.IDE.Plugin.Plugins.Diagnostic +import GHC (EpAnn (..), + SrcSpanAnn' (SrcSpanAnn), + SrcSpanAnnA, + SrcSpanAnnN, + TrailingAnn (..), + emptyComments, + noAnn) +import GHC.Hs (IsUnicodeSyntax (..)) +import GHC.Types.SrcLoc (generatedSrcSpan) +import Ide.PluginUtils (makeDiffTextEdit, + responseError) +import Language.Haskell.GHC.ExactPrint (TransformT, + noAnnSrcSpanDP1, + runTransformT) +import Language.Haskell.GHC.ExactPrint.Transform (d1) +import Language.LSP.Types +#endif + +#if !MIN_VERSION_ghc(9,2,1) +plugin :: [(T.Text, [TextEdit])] +plugin = [] +#else +-- When GHC tells us that a variable is not bound, it will tell us either: +-- - there is an unbound variable with a given type +-- - there is an unbound variable (GHC provides no type suggestion) +-- +-- When we receive either of these errors, we produce a text edit that will add a new argument (as a new pattern in the +-- last position of each LHS of the top-level bindings for this HsDecl). +-- +-- NOTE When adding a new argument to a declaration, the corresponding argument's type in declaration's signature might +-- not be the last type in the signature, such as: +-- foo :: a -> b -> c -> d +-- foo a b = \c -> ... +-- In this case a new argument would have to add its type between b and c in the signature. +plugin :: ParsedModule -> Diagnostic -> Either ResponseError [(T.Text, [TextEdit])] +plugin parsedModule Diagnostic {_message, _range} + | Just (name, typ) <- matchVariableNotInScope message = addArgumentAction parsedModule _range name typ + | Just (name, typ) <- matchFoundHoleIncludeUnderscore message = addArgumentAction parsedModule _range name (Just typ) + | otherwise = pure [] + where + message = unifySpaces _message + +-- Given a name for the new binding, add a new pattern to the match in the last position, +-- returning how many patterns there were in this match prior to the transformation: +-- addArgToMatch "foo" `bar arg1 arg2 = ...` +-- => (`bar arg1 arg2 foo = ...`, 2) +addArgToMatch :: T.Text -> GenLocated l (Match GhcPs body) -> (GenLocated l (Match GhcPs body), Int) +addArgToMatch name (L locMatch (Match xMatch ctxMatch pats rhs)) = + let unqualName = mkRdrUnqual $ mkVarOcc $ T.unpack name + newPat = L (noAnnSrcSpanDP1 generatedSrcSpan) $ VarPat NoExtField (noLocA unqualName) + in (L locMatch (Match xMatch ctxMatch (pats <> [newPat]) rhs), Prelude.length pats) + +-- Attempt to insert a binding pattern into each match for the given LHsDecl; succeeds only if the function is a FunBind. +-- Also return: +-- - the declaration's name +-- - the number of bound patterns in the declaration's matches prior to the transformation +-- +-- For example: +-- insertArg "new_pat" `foo bar baz = 1` +-- => (`foo bar baz new_pat = 1`, Just ("foo", 2)) +appendFinalPatToMatches :: T.Text -> LHsDecl GhcPs -> TransformT (Either ResponseError) (LHsDecl GhcPs, Maybe (GenLocated SrcSpanAnnN RdrName, Int)) +appendFinalPatToMatches name = \case + (L locDecl (ValD xVal (FunBind xFunBind idFunBind mg coreFunBind))) -> do + (mg', numPatsMay) <- modifyMgMatchesT' mg (pure . second Just . addArgToMatch name) Nothing combineMatchNumPats + numPats <- lift $ maybeToEither (responseError "Unexpected empty match group in HsDecl") numPatsMay + let decl' = L locDecl (ValD xVal (FunBind xFunBind idFunBind mg' coreFunBind)) + pure (decl', Just (idFunBind, numPats)) + decl -> pure (decl, Nothing) + where + combineMatchNumPats Nothing other = pure other + combineMatchNumPats other Nothing = pure other + combineMatchNumPats (Just l) (Just r) + | l == r = pure (Just l) + | otherwise = Left $ responseError "Unexpected different numbers of patterns in HsDecl MatchGroup" + +-- The add argument works as follows: +-- 1. Attempt to add the given name as the last pattern of the declaration that contains `range`. +-- 2. If such a declaration exists, use that declaration's name to modify the signature of said declaration, if it +-- has a type signature. +-- +-- NOTE For the following situation, the type signature is not updated (it's unclear what should happen): +-- type FunctionTySyn = () -> Int +-- foo :: FunctionTySyn +-- foo () = new_def +-- +-- TODO instead of inserting a typed hole; use GHC's suggested type from the error +addArgumentAction :: ParsedModule -> Range -> T.Text -> Maybe T.Text -> Either ResponseError [(T.Text, [TextEdit])] +addArgumentAction (ParsedModule _ moduleSrc _ _) range name _typ = do + (newSource, _, _) <- runTransformT $ do + (moduleSrc', join -> matchedDeclNameMay) <- addNameAsLastArgOfMatchingDecl (makeDeltaAst moduleSrc) + case matchedDeclNameMay of + Just (matchedDeclName, numPats) -> modifySigWithM (unLoc matchedDeclName) (addTyHoleToTySigArg numPats) moduleSrc' + Nothing -> pure moduleSrc' + let diff = makeDiffTextEdit (T.pack $ exactPrint moduleSrc) (T.pack $ exactPrint newSource) + pure [("Add argument ‘" <> name <> "’ to function", fromLspList diff)] + where + addNameAsLastArgOfMatchingDecl = modifySmallestDeclWithM spanContainsRangeOrErr addNameAsLastArg + addNameAsLastArg = fmap (first (:[])) . appendFinalPatToMatches name + + spanContainsRangeOrErr = maybeToEither (responseError "SrcSpan was not valid range") . (`spanContainsRange` range) + +-- Transform an LHsType into a list of arguments and return type, to make transformations easier. +hsTypeToFunTypeAsList :: LHsType GhcPs -> ([(SrcSpanAnnA, XFunTy GhcPs, HsArrow GhcPs, LHsType GhcPs)], LHsType GhcPs) +hsTypeToFunTypeAsList = \case + L spanAnnA (HsFunTy xFunTy arrow lhs rhs) -> + let (rhsArgs, rhsRes) = hsTypeToFunTypeAsList rhs + in ((spanAnnA, xFunTy, arrow, lhs):rhsArgs, rhsRes) + ty -> ([], ty) + +-- The inverse of `hsTypeToFunTypeAsList` +hsTypeFromFunTypeAsList :: ([(SrcSpanAnnA, XFunTy GhcPs, HsArrow GhcPs, LHsType GhcPs)], LHsType GhcPs) -> LHsType GhcPs +hsTypeFromFunTypeAsList (args, res) = + foldr (\(spanAnnA, xFunTy, arrow, argTy) res -> L spanAnnA $ HsFunTy xFunTy arrow argTy res) res args + +-- Add a typed hole to a type signature in the given argument position: +-- 0 `foo :: ()` => foo :: _ -> () +-- 2 `foo :: FunctionTySyn` => foo :: FunctionTySyn +-- 1 `foo :: () -> () -> Int` => foo :: () -> _ -> () -> Int +addTyHoleToTySigArg :: Int -> LHsSigType GhcPs -> (LHsSigType GhcPs) +addTyHoleToTySigArg loc (L annHsSig (HsSig xHsSig tyVarBndrs lsigTy)) = + let (args, res) = hsTypeToFunTypeAsList lsigTy + wildCardAnn = SrcSpanAnn (EpAnn genAnchor1 (AnnListItem [AddRarrowAnn d1]) emptyComments) generatedSrcSpan + newArg = (SrcSpanAnn mempty generatedSrcSpan, noAnn, HsUnrestrictedArrow NormalSyntax, L wildCardAnn $ HsWildCardTy noExtField) + -- NOTE if the location that the argument wants to be placed at is not one more than the number of arguments + -- in the signature, then we return the original type signature. + -- This situation most likely occurs due to a function type synonym in the signature + insertArg n _ | n < 0 = error "Not possible" + insertArg 0 as = newArg:as + insertArg _ [] = [] + insertArg n (a:as) = a : insertArg (n - 1) as + lsigTy' = hsTypeFromFunTypeAsList (insertArg loc args, res) + in L annHsSig (HsSig xHsSig tyVarBndrs lsigTy') + +fromLspList :: List a -> [a] +fromLspList (List a) = a +#endif diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/Diagnostic.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/Diagnostic.hs new file mode 100644 index 0000000000..e99c23de98 --- /dev/null +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/Diagnostic.hs @@ -0,0 +1,53 @@ +module Development.IDE.Plugin.Plugins.Diagnostic ( + matchVariableNotInScope, + matchRegexUnifySpaces, + unifySpaces, + matchFoundHole, + matchFoundHoleIncludeUnderscore, + ) + where + +import Data.Bifunctor (Bifunctor (..)) +import qualified Data.Text as T +import Text.Regex.TDFA ((=~~)) + +unifySpaces :: T.Text -> T.Text +unifySpaces = T.unwords . T.words + +-- | Returns Just (the submatches) for the first capture, or Nothing. +matchRegex :: T.Text -> T.Text -> Maybe [T.Text] +matchRegex message regex = case message =~~ regex of + Just (_ :: T.Text, _ :: T.Text, _ :: T.Text, bindings) -> Just bindings + Nothing -> Nothing + +-- | 'matchRegex' combined with 'unifySpaces' +matchRegexUnifySpaces :: T.Text -> T.Text -> Maybe [T.Text] +matchRegexUnifySpaces message = matchRegex (unifySpaces message) + +matchFoundHole :: T.Text -> Maybe (T.Text, T.Text) +matchFoundHole message + | Just [name, typ] <- matchRegexUnifySpaces message "Found hole: _([^ ]+) :: ([^*•]+) Or perhaps" = + Just (name, typ) + | otherwise = Nothing + +matchFoundHoleIncludeUnderscore :: T.Text -> Maybe (T.Text, T.Text) +matchFoundHoleIncludeUnderscore message = first ("_" <>) <$> matchFoundHole message + +matchVariableNotInScope :: T.Text -> Maybe (T.Text, Maybe T.Text) +matchVariableNotInScope message + -- * Variable not in scope: + -- suggestAcion :: Maybe T.Text -> Range -> Range + -- * Variable not in scope: + -- suggestAcion + | Just (name, typ) <- matchVariableNotInScopeTyped message = Just (name, Just typ) + | Just name <- matchVariableNotInScopeUntyped message = Just (name, Nothing) + | otherwise = Nothing + where + matchVariableNotInScopeTyped message + | Just [name, typ] <- matchRegexUnifySpaces message "Variable not in scope: ([^ ]+) :: ([^*•]+)" = + Just (name, typ) + | otherwise = Nothing + matchVariableNotInScopeUntyped message + | Just [name] <- matchRegexUnifySpaces message "Variable not in scope: ([^ ]+)" = + Just name + | otherwise = Nothing diff --git a/plugins/hls-refactor-plugin/test/Main.hs b/plugins/hls-refactor-plugin/test/Main.hs index 46fb1fb616..e1b9fe9de7 100644 --- a/plugins/hls-refactor-plugin/test/Main.hs +++ b/plugins/hls-refactor-plugin/test/Main.hs @@ -39,7 +39,6 @@ import Language.LSP.Types hiding SemanticTokenRelative (length), SemanticTokensEdit (_start), mkRange) -import qualified Language.LSP.Types as LSP import Language.LSP.Types.Capabilities import qualified Language.LSP.Types.Lens as L import System.Directory @@ -54,13 +53,13 @@ import Test.Tasty.HUnit import Text.Regex.TDFA ((=~)) -import Development.IDE.Plugin.CodeAction (bindingsPluginDescriptor, - matchRegExMultipleImports) +import Development.IDE.Plugin.CodeAction (matchRegExMultipleImports) import Test.Hls import Control.Applicative (liftA2) import qualified Development.IDE.Plugin.CodeAction as Refactor import qualified Development.IDE.Plugin.HLS.GhcIde as GhcIde +import qualified Test.AddArgument main :: IO () main = defaultTestRunner tests @@ -322,7 +321,7 @@ codeActionTests = testGroup "code actions" , addImplicitParamsConstraintTests , removeExportTests #if MIN_VERSION_ghc(9,2,1) - , addFunctionArgumentTests + , Test.AddArgument.tests #endif ] @@ -2166,244 +2165,8 @@ insertNewDefinitionTests = testGroup "insert new definition actions" ] #if MIN_VERSION_ghc(9,2,1) -addFunctionArgumentTests :: TestTree -addFunctionArgumentTests = - testGroup - "add function argument" - [ testSession "simple" $ do - let foo = - [ "foo True = select [True]", - "", - "foo False = False" - ] - foo' = - [ "foo True select = select [True]", - "", - "foo False select = False" - ] - someOtherCode = - [ "", - "someOtherCode = ()" - ] - docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ foo ++ someOtherCode) - _ <- waitForDiagnostics - InR action@CodeAction {_title = actionTitle} : _ <- - filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) - <$> getCodeActions docB (R 0 0 0 50) - liftIO $ actionTitle @?= "Add argument ‘select’ to function" - executeCodeAction action - contentAfterAction <- documentContents docB - liftIO $ contentAfterAction @?= T.unlines (foo' ++ someOtherCode), - testSession "comments" $ do - let foo = - [ "foo -- c1", - " True -- c2", - " = -- c3", - " select [True]", - "", - "foo False = False" - ] - -- TODO improve behavior slightly? - foo' = - [ "foo -- c1", - " True select -- c2", - " = -- c3", - " select [True]", - "", - "foo False select = False" - ] - someOtherCode = - [ "", - "someOtherCode = ()" - ] - docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ foo ++ someOtherCode) - _ <- waitForDiagnostics - InR action@CodeAction {_title = actionTitle} : _ <- - filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) - <$> getCodeActions docB (R 3 0 3 50) - liftIO $ actionTitle @?= "Add argument ‘select’ to function" - executeCodeAction action - contentAfterAction <- documentContents docB - liftIO $ contentAfterAction @?= T.unlines (foo' ++ someOtherCode), - testSession "leading decls" $ do - let foo = - [ "module Foo where", - "", - "bar = 1", - "", - "foo True = select [True]", - "", - "foo False = False" - ] - foo' = - [ "module Foo where", - "", - "bar = 1", - "", - "foo True select = select [True]", - "", - "foo False select = False" - ] - docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ foo) - _ <- waitForDiagnostics - InR action@CodeAction {_title = actionTitle} : _ <- - filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) - <$> getCodeActions docB (R 4 0 4 50) - liftIO $ actionTitle @?= "Add argument ‘select’ to function" - executeCodeAction action - contentAfterAction <- documentContents docB - liftIO $ contentAfterAction @?= T.unlines foo', - testSession "hole" $ do - let foo = - [ "module Foo where", - "", - "bar = 1", - "", - "foo True = _select [True]", - "", - "foo False = False" - ] - foo' = - [ "module Foo where", - "", - "bar = 1", - "", - "foo True _select = _select [True]", - "", - "foo False _select = False" - ] - docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ foo) - _ <- waitForDiagnostics - InR action@CodeAction {_title = actionTitle} : _ <- - filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) - <$> getCodeActions docB (R 4 0 4 50) - liftIO $ actionTitle @?= "Add argument ‘_select’ to function" - executeCodeAction action - contentAfterAction <- documentContents docB - liftIO $ contentAfterAction @?= T.unlines foo', - testSession "untyped error" $ do - let foo = - [ "foo = select" - ] - foo' = - [ "foo select = select" - ] - someOtherCode = - [ "", - "someOtherCode = ()" - ] - docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ foo ++ someOtherCode) - _ <- waitForDiagnostics - InR action@CodeAction {_title = actionTitle} : _ <- - filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) - <$> getCodeActions docB (R 0 0 0 50) - liftIO $ actionTitle @?= "Add argument ‘select’ to function" - executeCodeAction action - contentAfterAction <- documentContents docB - liftIO $ contentAfterAction @?= T.unlines (foo' ++ someOtherCode), - testSession "untyped error" $ do - let foo = - [ "foo = select" - ] - foo' = - [ "foo select = select" - ] - someOtherCode = - [ "", - "someOtherCode = ()" - ] - docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ foo ++ someOtherCode) - _ <- waitForDiagnostics - InR action@CodeAction {_title = actionTitle} : _ <- - filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) - <$> getCodeActions docB (R 0 0 0 50) - liftIO $ actionTitle @?= "Add argument ‘select’ to function" - executeCodeAction action - contentAfterAction <- documentContents docB - liftIO $ contentAfterAction @?= T.unlines (foo' ++ someOtherCode), - testSession "where clause" $ do - let foo = - [ "foo True = False ", - " where", - " bar = select", - "", - "foo False = False" - ] - -- TODO improve this behaviour (should add select to bar, not foo) - foo' = - [ "foo True select = False ", - " where", - " bar = select", - "", - "foo False select = False" - ] - docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ foo) - _ <- waitForDiagnostics - InR action@CodeAction {_title = actionTitle} : _ <- - filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) - <$> getCodeActions docB (R 2 0 2 50) - liftIO $ actionTitle @?= "Add argument ‘select’ to function" - executeCodeAction action - contentAfterAction <- documentContents docB - liftIO $ contentAfterAction @?= T.unlines foo', - testSession "where clause" $ do - let foo = - [ "foo -- c1", - " -- | c2", - " {- c3 -} True -- c4", - " = select", - "", - "foo False = False" - ] - -- TODO could use improvement here... - foo' = - [ "foo -- c1", - " -- | c2", - " {- c3 -} True select -- c4", - " = select", - "", - "foo False select = False" - ] - docB <- createDoc "ModuleB.hs" "haskell" (T.unlines $ foo) - _ <- waitForDiagnostics - InR action@CodeAction {_title = actionTitle} : _ <- - filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) - <$> getCodeActions docB (R 3 0 3 50) - liftIO $ actionTitle @?= "Add argument ‘select’ to function" - executeCodeAction action - contentAfterAction <- documentContents docB - liftIO $ contentAfterAction @?= T.unlines foo', - mkGoldenAddArgTest "AddArgWithSig" (R 1 0 1 50), - mkGoldenAddArgTest "AddArgWithSigAndDocs" (R 8 0 8 50), - mkGoldenAddArgTest "AddArgFromLet" (R 2 0 2 50), - mkGoldenAddArgTest "AddArgFromWhere" (R 3 0 3 50), - mkGoldenAddArgTest "AddArgWithTypeSynSig" (R 2 0 2 50), - mkGoldenAddArgTest "AddArgWithTypeSynSigContravariant" (R 2 0 2 50), - mkGoldenAddArgTest "AddArgWithLambda" (R 1 0 1 50), - mkGoldenAddArgTest "MultiSigFirst" (R 2 0 2 50), - mkGoldenAddArgTest "MultiSigLast" (R 2 0 2 50), - mkGoldenAddArgTest "MultiSigMiddle" (R 2 0 2 50) - ] #endif -mkGoldenAddArgTest :: FilePath -> Range -> TestTree -mkGoldenAddArgTest testFileName range = do - let action docB = do - _ <- waitForDiagnostics - InR action@CodeAction {_title = actionTitle} : _ <- - filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) - <$> getCodeActions docB range - liftIO $ actionTitle @?= "Add argument ‘new_def’ to function" - executeCodeAction action - goldenWithHaskellDoc - (Refactor.bindingsPluginDescriptor mempty "ghcide-code-actions-bindings") - (testFileName <> " (golden)") - "test/data/golden/add-arg" - testFileName - "expected" - "hs" - action - deleteUnusedDefinitionTests :: TestTree deleteUnusedDefinitionTests = testGroup "delete unused definition action" [ testSession "delete unused top level binding" $ diff --git a/plugins/hls-refactor-plugin/test/Test/AddArgument.hs b/plugins/hls-refactor-plugin/test/Test/AddArgument.hs new file mode 100644 index 0000000000..b52e39d511 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/Test/AddArgument.hs @@ -0,0 +1,74 @@ +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PolyKinds #-} + +module Test.AddArgument (tests) where + +import Data.List.Extra +import qualified Data.Text as T +import Development.IDE.Types.Location +import Language.LSP.Test +import Language.LSP.Types hiding + (SemanticTokenAbsolute (length, line), + SemanticTokenRelative (length), + SemanticTokensEdit (_start), + mkRange) +import Test.Tasty +import Test.Tasty.HUnit + + +import Test.Hls + +import qualified Development.IDE.Plugin.CodeAction as Refactor + +tests :: TestTree +tests = + testGroup + "add argument" +#if !MIN_VERSION_ghc(9,2,1) + [] +#else + [ mkGoldenAddArgTest' "Hole" (r 0 0 0 50) "_new_def", + mkGoldenAddArgTest "NoTypeSuggestion" (r 0 0 0 50), + mkGoldenAddArgTest "MultipleDeclAlts" (r 0 0 0 50), + mkGoldenAddArgTest "AddArgWithSig" (r 1 0 1 50), + mkGoldenAddArgTest "AddArgWithSigAndDocs" (r 8 0 8 50), + mkGoldenAddArgTest "AddArgFromLet" (r 2 0 2 50), + mkGoldenAddArgTest "AddArgFromWhere" (r 3 0 3 50), + mkGoldenAddArgTest "AddArgFromWhereComments" (r 3 0 3 50), + mkGoldenAddArgTest "AddArgWithTypeSynSig" (r 2 0 2 50), + mkGoldenAddArgTest "AddArgWithTypeSynSigContravariant" (r 2 0 2 50), + mkGoldenAddArgTest "AddArgWithLambda" (r 1 0 1 50), + mkGoldenAddArgTest "MultiSigFirst" (r 2 0 2 50), + mkGoldenAddArgTest "MultiSigLast" (r 2 0 2 50), + mkGoldenAddArgTest "MultiSigMiddle" (r 2 0 2 50) + ] + where + r x y x' y' = Range (Position x y) (Position x' y') + +mkGoldenAddArgTest :: FilePath -> Range -> TestTree +mkGoldenAddArgTest testFileName range = mkGoldenAddArgTest' testFileName range "new_def" + +-- Make a golden test for the add argument action. Given varName is the name of the variable not yet defined. +mkGoldenAddArgTest' :: FilePath -> Range -> T.Text -> TestTree +mkGoldenAddArgTest' testFileName range varName = do + let action docB = do + _ <- waitForDiagnostics + InR action@CodeAction {_title = actionTitle} : _ <- + filter (\(InR CodeAction {_title = x}) -> "Add" `isPrefixOf` T.unpack x) + <$> getCodeActions docB range + liftIO $ actionTitle @?= ("Add argument ‘" <> varName <> "’ to function") + executeCodeAction action + goldenWithHaskellDoc + (Refactor.bindingsPluginDescriptor mempty "ghcide-code-actions-bindings") + (testFileName <> " (golden)") + "test/data/golden/add-arg" + testFileName + "expected" + "hs" + action +#endif diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromWhereComments.expected.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromWhereComments.expected.hs new file mode 100644 index 0000000000..30c418cc7e --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromWhereComments.expected.hs @@ -0,0 +1,6 @@ +foo -- c1 + -- | c2 + {- c3 -} True new_def -- c4 + = new_def + +foo False new_def = False diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromWhereComments.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromWhereComments.hs new file mode 100644 index 0000000000..ece25370a5 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/AddArgFromWhereComments.hs @@ -0,0 +1,6 @@ +foo -- c1 + -- | c2 + {- c3 -} True -- c4 + = new_def + +foo False = False diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/Hole.expected.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/Hole.expected.hs new file mode 100644 index 0000000000..1f440e9650 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/Hole.expected.hs @@ -0,0 +1 @@ +foo _new_def = _new_def \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/Hole.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/Hole.hs new file mode 100644 index 0000000000..31761e6934 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/Hole.hs @@ -0,0 +1 @@ +foo = _new_def \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultipleDeclAlts.expected.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultipleDeclAlts.expected.hs new file mode 100644 index 0000000000..fce633e2b9 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultipleDeclAlts.expected.hs @@ -0,0 +1,2 @@ +foo True new_def = new_def +foo False new_def = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultipleDeclAlts.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultipleDeclAlts.hs new file mode 100644 index 0000000000..919ce56546 --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/MultipleDeclAlts.hs @@ -0,0 +1,2 @@ +foo True = new_def +foo False = 1 \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/NoTypeSuggestion.expected.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/NoTypeSuggestion.expected.hs new file mode 100644 index 0000000000..e982cdf35e --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/NoTypeSuggestion.expected.hs @@ -0,0 +1 @@ +foo new_def = new_def \ No newline at end of file diff --git a/plugins/hls-refactor-plugin/test/data/golden/add-arg/NoTypeSuggestion.hs b/plugins/hls-refactor-plugin/test/data/golden/add-arg/NoTypeSuggestion.hs new file mode 100644 index 0000000000..cf9ade10dc --- /dev/null +++ b/plugins/hls-refactor-plugin/test/data/golden/add-arg/NoTypeSuggestion.hs @@ -0,0 +1 @@ +foo = new_def \ No newline at end of file From 0d0678658eabd6ec6f26c15e1a06bbb1e4273dff Mon Sep 17 00:00:00 2001 From: cydparser Date: Mon, 21 Nov 2022 02:01:09 -0800 Subject: [PATCH 196/213] Use latest GHC 9.2 and 9.4 in flake.nix (#3354) --- configuration-ghc-90.nix | 6 +- configuration-ghc-92.nix | 2 - configuration-ghc-94.nix | 13 --- flake.lock | 220 ++++----------------------------------- flake.nix | 101 ++++-------------- 5 files changed, 40 insertions(+), 302 deletions(-) diff --git a/configuration-ghc-90.nix b/configuration-ghc-90.nix index f82e03ce03..e1e5770d6e 100644 --- a/configuration-ghc-90.nix +++ b/configuration-ghc-90.nix @@ -18,13 +18,13 @@ let # https://github.com/nikita-volkov/ptr-poker/issues/11 ptr-poker = hself.callCabal2nix "ptr-poker" inputs.ptr-poker { }; - ghc-lib = hself.ghc-lib_9_2_2_20220307; + ghc-lib = hself.ghc-lib_9_2_4_20220729; ghc-lib-parser = hself.ghc-lib-parser_9_2_4_20220729; - ghc-lib-parser-ex = hself.ghc-lib-parser-ex_9_2_0_4; + ghc-lib-parser-ex = hself.ghc-lib-parser-ex_9_2_1_1; Cabal = hself.Cabal_3_6_3_0; ormolu = hself.ormolu_0_5_0_1; - fourmolu = hself.fourmolu_0_6_0_0; + # Hlint is still broken hlint = doJailbreak (hself.callCabal2nix "hlint" inputs.hlint-34 { }); diff --git a/configuration-ghc-92.nix b/configuration-ghc-92.nix index 3790e182a7..b608af825d 100644 --- a/configuration-ghc-92.nix +++ b/configuration-ghc-92.nix @@ -24,8 +24,6 @@ let # https://github.com/nikita-volkov/ptr-poker/issues/11 ptr-poker = hself.callCabal2nix "ptr-poker" inputs.ptr-poker { }; - ghc-exactprint = - hself.callCabal2nix "ghc-exactprint" inputs.ghc-exactprint-150 { }; # Hlint is still broken hlint = doJailbreak (hself.callCabal2nix "hlint" inputs.hlint { }); diff --git a/configuration-ghc-94.nix b/configuration-ghc-94.nix index 3568f65a51..7c6ca11a82 100644 --- a/configuration-ghc-94.nix +++ b/configuration-ghc-94.nix @@ -31,19 +31,6 @@ let stylish-haskell = appendConfigureFlag hsuper.stylish-haskell "-fghc-lib"; - cereal = hsuper.callHackage "cereal" "0.5.8.3" {}; - base-compat = hsuper.callHackage "base-compat" "0.12.2" {}; - base-compat-batteries = hsuper.callHackage "base-compat-batteries" "0.12.2" {}; - hashable = hsuper.callHackage "hashable" "1.4.1.0" {}; - primitive = hsuper.callHackage "primitive" "0.7.4.0" {}; - ghc-check = hsuper.callHackage "ghc-check" "0.5.0.8" {}; - lens = hsuper.callHackage "lens" "5.2" {}; - integer-logarithms = hsuper.callHackage "integer-logarithms" "1.0.3.1" {}; - hiedb = hsuper.callHackage "hiedb" "0.4.2.0" {}; - hie-bios = hsuper.callHackage "hie-bios" "0.11.0" {}; - lsp = hsuper.callCabal2nix "lsp" inputs.lsp {}; - lsp-types = hsuper.callCabal2nix "lsp-types" inputs.lsp-types {}; - # Re-generate HLS drv excluding some plugins haskell-language-server = hself.callCabal2nixWithOptions "haskell-language-server" ./. diff --git a/flake.lock b/flake.lock index 0f74f76d26..2ac39dc862 100644 --- a/flake.lock +++ b/flake.lock @@ -1,25 +1,13 @@ { "nodes": { - "aeson-1520": { - "flake": false, - "locked": { - "narHash": "sha256-btKp7CTOgC0wT33lROffARW9qr1jx9oKE5EWydaR52c=", - "type": "tarball", - "url": "https://hackage.haskell.org/package/aeson-1.5.2.0/aeson-1.5.2.0.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://hackage.haskell.org/package/aeson-1.5.2.0/aeson-1.5.2.0.tar.gz" - } - }, "all-cabal-hashes-unpacked": { "flake": false, "locked": { - "lastModified": 1663164766, - "narHash": "sha256-7ypfdEzJwfaQMQx9HV8B+r9BV7bN6iIO0lWhDy+8+0k=", + "lastModified": 1668997806, + "narHash": "sha256-HRTQuIO/MxV5OcbCNsHSCeULa7KAjxIBQk5sAVFzrKk=", "owner": "commercialhaskell", "repo": "all-cabal-hashes", - "rev": "ed701f5e64ece3e57efa4227243f9fb64f935253", + "rev": "934c06ca91eb6ceca8a7c484dfc2862e955489f8", "type": "github" }, "original": { @@ -29,50 +17,14 @@ "type": "github" } }, - "brittany-01312": { - "flake": false, - "locked": { - "narHash": "sha256-4rDE2bu4C8cv1D6lkTtLxMwLRyDfIK70BnptSrygK60=", - "type": "tarball", - "url": "https://hackage.haskell.org/package/brittany-0.13.1.2/brittany-0.13.1.2.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://hackage.haskell.org/package/brittany-0.13.1.2/brittany-0.13.1.2.tar.gz" - } - }, - "constraints-extras": { - "flake": false, - "locked": { - "narHash": "sha256-WGDSpT37RrHwpQtExGkL5eEmBk/s9b0rxtT9DYqSGg4=", - "type": "tarball", - "url": "https://hackage.haskell.org/package/constraints-extras-0.3.2.1/constraints-extras-0.3.2.1.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://hackage.haskell.org/package/constraints-extras-0.3.2.1/constraints-extras-0.3.2.1.tar.gz" - } - }, - "entropy": { - "flake": false, - "locked": { - "narHash": "sha256-Oj0vftbS7Pau7OzdMrzRPghqwEiimwQbt0w59cMcH98=", - "type": "tarball", - "url": "https://hackage.haskell.org/package/entropy-0.4.1.10/entropy-0.4.1.10.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://hackage.haskell.org/package/entropy-0.4.1.10/entropy-0.4.1.10.tar.gz" - } - }, "flake-compat": { "flake": false, "locked": { - "lastModified": 1650374568, - "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=", + "lastModified": 1668681692, + "narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=", "owner": "edolstra", "repo": "flake-compat", - "rev": "b4a34015c698c7793d592d66adbab377907a2be8", + "rev": "009399224d5e398d03b22badca40a37ac85412a1", "type": "github" }, "original": { @@ -83,11 +35,11 @@ }, "flake-utils": { "locked": { - "lastModified": 1659877975, - "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", "owner": "numtide", "repo": "flake-utils", - "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", "type": "github" }, "original": { @@ -99,37 +51,13 @@ "fourmolu": { "flake": false, "locked": { - "narHash": "sha256-uo5UE2SzrimnZl+JjJ30Hlg/nIw1OXJTPFIgkQopaI0=", - "type": "tarball", - "url": "https://hackage.haskell.org/package/fourmolu-0.5.0.1/fourmolu-0.5.0.1.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://hackage.haskell.org/package/fourmolu-0.5.0.1/fourmolu-0.5.0.1.tar.gz" - } - }, - "fourmolu-0300": { - "flake": false, - "locked": { - "narHash": "sha256-SFBwhkXfDArITiBSxGSp2qf8gl+yBpWHglBB5aKeaBU=", - "type": "tarball", - "url": "https://hackage.haskell.org/package/fourmolu-0.3.0.0/fourmolu-0.3.0.0.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://hackage.haskell.org/package/fourmolu-0.3.0.0/fourmolu-0.3.0.0.tar.gz" - } - }, - "ghc-check": { - "flake": false, - "locked": { - "narHash": "sha256-pmmQMrk6X00+zbsstV49w/Es9+V9gssrXzJoub2ReEs=", + "narHash": "sha256-vbqgYaAd/JUPFGv6O2+OosBXFceKah9OYrjTuEkEZ3E=", "type": "tarball", - "url": "https://hackage.haskell.org/package/ghc-check-0.5.0.8/ghc-check-0.5.0.8.tar.gz" + "url": "https://hackage.haskell.org/package/fourmolu-0.9.0.0/fourmolu-0.9.0.0.tar.gz" }, "original": { "type": "tarball", - "url": "https://hackage.haskell.org/package/ghc-check-0.5.0.8/ghc-check-0.5.0.8.tar.gz" + "url": "https://hackage.haskell.org/package/fourmolu-0.9.0.0/fourmolu-0.9.0.0.tar.gz" } }, "ghc-exactprint": { @@ -159,13 +87,13 @@ "ghc-exactprint-160": { "flake": false, "locked": { - "narHash": "sha256-6fW4KSmDo7hi5i2C1lbI/rEyFWrowSGTNyaC+f73JaE=", + "narHash": "sha256-TiFybKVP3JIuUfnyESrKXInW9o0C8FLnsMjO4PEJ3I0=", "type": "tarball", - "url": "https://hackage.haskell.org/package/ghc-exactprint-1.6.0/ghc-exactprint-1.6.0.tar.gz" + "url": "https://hackage.haskell.org/package/ghc-exactprint-1.6.1/ghc-exactprint-1.6.1.tar.gz" }, "original": { "type": "tarball", - "url": "https://hackage.haskell.org/package/ghc-exactprint-1.6.0/ghc-exactprint-1.6.0.tar.gz" + "url": "https://hackage.haskell.org/package/ghc-exactprint-1.6.1/ghc-exactprint-1.6.1.tar.gz" } }, "gitignore": { @@ -184,18 +112,6 @@ "type": "github" } }, - "hie-bios": { - "flake": false, - "locked": { - "narHash": "sha256-KLAg++tO9lCOn7R/cSN2wLbrhpeBOOmeTEh7auIbUNk=", - "type": "tarball", - "url": "https://hackage.haskell.org/package/hie-bios-0.11.0/hie-bios-0.11.0.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://hackage.haskell.org/package/hie-bios-0.11.0/hie-bios-0.11.0.tar.gz" - } - }, "hiedb": { "flake": false, "locked": { @@ -232,73 +148,13 @@ "url": "https://hackage.haskell.org/package/hlint-3.4/hlint-3.4.tar.gz" } }, - "hw-prim": { - "flake": false, - "locked": { - "narHash": "sha256-++rg/bx4TjWUDyHSWKm/8ITwQLonPRLXHPLlnhJy8ik=", - "type": "tarball", - "url": "https://hackage.haskell.org/package/hw-prim-0.6.3.2/hw-prim-0.6.3.2.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://hackage.haskell.org/package/hw-prim-0.6.3.2/hw-prim-0.6.3.2.tar.gz" - } - }, - "implicit-hie-cradle": { - "flake": false, - "locked": { - "narHash": "sha256-2NmucBBI7Qi1UGXWG27XFZRCeqeRiwVFWmJKZnp6R5U=", - "type": "tarball", - "url": "https://hackage.haskell.org/package/implicit-hie-cradle-0.3.0.5/implicit-hie-cradle-0.3.0.5.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://hackage.haskell.org/package/implicit-hie-cradle-0.3.0.5/implicit-hie-cradle-0.3.0.5.tar.gz" - } - }, - "lsp": { - "flake": false, - "locked": { - "narHash": "sha256-g5R34SVz0kRD5zpODNsaaaIJOHty10cTS6ZDPi4s8pc=", - "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-1.6.0.0/lsp-1.6.0.0.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-1.6.0.0/lsp-1.6.0.0.tar.gz" - } - }, - "lsp-test": { - "flake": false, - "locked": { - "narHash": "sha256-HhFAdNvmnnnCzsKfTWbqUyTFrCfq1n6pGKfk2R0fcUc=", - "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-test-0.14.1.0/lsp-test-0.14.1.0.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-test-0.14.1.0/lsp-test-0.14.1.0.tar.gz" - } - }, - "lsp-types": { - "flake": false, - "locked": { - "narHash": "sha256-QSixsrCvsWlckG/LLF1z8LsHhqaXxVAxOPIA1NxjVT4=", - "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-types-1.6.0.0/lsp-types-1.6.0.0.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://hackage.haskell.org/package/lsp-types-1.6.0.0/lsp-types-1.6.0.0.tar.gz" - } - }, "nixpkgs": { "locked": { - "lastModified": 1663112159, - "narHash": "sha256-31rjPhB6Hj1QoqLvFSULFf9Z/6z05vR0KrLGYvr9w0M=", + "lastModified": 1668650906, + "narHash": "sha256-JuiYfDO23O8oxUUOmhQflmOoJovyC5G4RjcYQMQjrRE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "78bce1608960b994405f3696ba086ba1d63654e9", + "rev": "3a86856a13c88c8c64ea32082a851fefc79aa700", "type": "github" }, "original": { @@ -320,59 +176,21 @@ "url": "https://hackage.haskell.org/package/ptr-poker-0.1.2.8/ptr-poker-0.1.2.8.tar.gz" } }, - "retrie": { - "flake": false, - "locked": { - "narHash": "sha256-SrUyFea9Qr2SYeNVDJfWZfCguJV2rHK2mO/FO4xLFaY=", - "type": "tarball", - "url": "https://hackage.haskell.org/package/retrie-1.2.0.1/retrie-1.2.0.1.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://hackage.haskell.org/package/retrie-1.2.0.1/retrie-1.2.0.1.tar.gz" - } - }, "root": { "inputs": { - "aeson-1520": "aeson-1520", "all-cabal-hashes-unpacked": "all-cabal-hashes-unpacked", - "brittany-01312": "brittany-01312", - "constraints-extras": "constraints-extras", - "entropy": "entropy", "flake-compat": "flake-compat", "flake-utils": "flake-utils", "fourmolu": "fourmolu", - "fourmolu-0300": "fourmolu-0300", - "ghc-check": "ghc-check", "ghc-exactprint": "ghc-exactprint", "ghc-exactprint-150": "ghc-exactprint-150", "ghc-exactprint-160": "ghc-exactprint-160", "gitignore": "gitignore", - "hie-bios": "hie-bios", "hiedb": "hiedb", "hlint": "hlint", "hlint-34": "hlint-34", - "hw-prim": "hw-prim", - "implicit-hie-cradle": "implicit-hie-cradle", - "lsp": "lsp", - "lsp-test": "lsp-test", - "lsp-types": "lsp-types", "nixpkgs": "nixpkgs", - "ptr-poker": "ptr-poker", - "retrie": "retrie", - "stylish-haskell": "stylish-haskell" - } - }, - "stylish-haskell": { - "flake": false, - "locked": { - "narHash": "sha256-oZSR5UQ+ieMFRq7MvL0sJqOblMk74awvcepuT+JHYtA=", - "type": "tarball", - "url": "https://hackage.haskell.org/package/stylish-haskell-0.14.2.0/stylish-haskell-0.14.2.0.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://hackage.haskell.org/package/stylish-haskell-0.14.2.0/stylish-haskell-0.14.2.0.tar.gz" + "ptr-poker": "ptr-poker" } } }, diff --git a/flake.nix b/flake.nix index b7f1cb6b06..c78cd3a843 100644 --- a/flake.nix +++ b/flake.nix @@ -27,20 +27,8 @@ }; # List of hackage dependencies - lsp = { - url = "https://hackage.haskell.org/package/lsp-1.6.0.0/lsp-1.6.0.0.tar.gz"; - flake = false; - }; - lsp-types = { - url = "https://hackage.haskell.org/package/lsp-types-1.6.0.0/lsp-types-1.6.0.0.tar.gz"; - flake = false; - }; - lsp-test = { - url = "https://hackage.haskell.org/package/lsp-test-0.14.1.0/lsp-test-0.14.1.0.tar.gz"; - flake = false; - }; ghc-exactprint-160 = { - url = "https://hackage.haskell.org/package/ghc-exactprint-1.6.0/ghc-exactprint-1.6.0.tar.gz"; + url = "https://hackage.haskell.org/package/ghc-exactprint-1.6.1/ghc-exactprint-1.6.1.tar.gz"; flake = false; }; ghc-exactprint-150 = { @@ -51,32 +39,8 @@ url = "https://hackage.haskell.org/package/ghc-exactprint-1.4.1/ghc-exactprint-1.4.1.tar.gz"; flake = false; }; - ghc-check = { - url = "https://hackage.haskell.org/package/ghc-check-0.5.0.8/ghc-check-0.5.0.8.tar.gz"; - flake = false; - }; - constraints-extras = { - url = "https://hackage.haskell.org/package/constraints-extras-0.3.2.1/constraints-extras-0.3.2.1.tar.gz"; - flake = false; - }; - retrie = { - url = "https://hackage.haskell.org/package/retrie-1.2.0.1/retrie-1.2.0.1.tar.gz"; - flake = false; - }; fourmolu = { - url = "https://hackage.haskell.org/package/fourmolu-0.5.0.1/fourmolu-0.5.0.1.tar.gz"; - flake = false; - }; - fourmolu-0300 = { - url = "https://hackage.haskell.org/package/fourmolu-0.3.0.0/fourmolu-0.3.0.0.tar.gz"; - flake = false; - }; - aeson-1520= { - url = "https://hackage.haskell.org/package/aeson-1.5.2.0/aeson-1.5.2.0.tar.gz"; - flake = false; - }; - brittany-01312 = { - url = "https://hackage.haskell.org/package/brittany-0.13.1.2/brittany-0.13.1.2.tar.gz"; + url = "https://hackage.haskell.org/package/fourmolu-0.9.0.0/fourmolu-0.9.0.0.tar.gz"; flake = false; }; hlint = { @@ -91,30 +55,10 @@ url = "https://hackage.haskell.org/package/ptr-poker-0.1.2.8/ptr-poker-0.1.2.8.tar.gz"; flake = false; }; - stylish-haskell = { - url = "https://hackage.haskell.org/package/stylish-haskell-0.14.2.0/stylish-haskell-0.14.2.0.tar.gz"; - flake = false; - }; - implicit-hie-cradle = { - url = "https://hackage.haskell.org/package/implicit-hie-cradle-0.3.0.5/implicit-hie-cradle-0.3.0.5.tar.gz"; - flake = false; - }; - hie-bios = { - url = "https://hackage.haskell.org/package/hie-bios-0.11.0/hie-bios-0.11.0.tar.gz"; - flake = false; - }; - entropy = { - url = "https://hackage.haskell.org/package/entropy-0.4.1.10/entropy-0.4.1.10.tar.gz"; - flake = false; - }; hiedb = { url = "https://hackage.haskell.org/package/hiedb-0.4.2.0/hiedb-0.4.2.0.tar.gz"; flake = false; }; - hw-prim = { - url = "https://hackage.haskell.org/package/hw-prim-0.6.3.2/hw-prim-0.6.3.2.tar.gz"; - flake = false; - }; }; outputs = inputs@{ self, nixpkgs, flake-compat, flake-utils, gitignore, all-cabal-hashes-unpacked, ... }: @@ -177,24 +121,15 @@ with haskell.lib; { # Patches don't apply github = overrideCabal hsuper.github (drv: { patches = []; }); - # GHCIDE requires hie-bios ^>=0.9.1 - hie-bios = hself.callCabal2nix "hie-bios" inputs.hie-bios {}; - - lsp = hsuper.callCabal2nix "lsp" inputs.lsp {}; - lsp-types = hsuper.callCabal2nix "lsp-types" inputs.lsp-types {}; - lsp-test = hsuper.callCabal2nix "lsp-test" inputs.lsp-test {}; - - entropy = hsuper.callCabal2nix "entropy" inputs.entropy {}; hiedb = hsuper.callCabal2nix "hiedb" inputs.hiedb {}; - hw-prim = hsuper.callCabal2nix "hw-prim" inputs.hw-prim {}; - implicit-hie-cradle = hself.callCabal2nix "implicit-hie-cradle" inputs.implicit-hie-cradle {}; - ghc-check = hself.callCabal2nix "ghc-check" inputs.ghc-check {}; # https://github.com/NixOS/nixpkgs/issues/140774 ormolu = if final.system == "aarch64-darwin" then overrideCabal hsuper.ormolu (_: { enableSeparateBinOutput = false; }) else hsuper.ormolu; + + fourmolu = hself.callCabal2nix "fourmolu" inputs.fourmolu {}; }; hlsSources = @@ -257,8 +192,8 @@ }; ghc902Config = (import ./configuration-ghc-90.nix) { inherit pkgs inputs; }; - ghc924Config = (import ./configuration-ghc-92.nix) { inherit pkgs inputs; }; - ghc942Config = (import ./configuration-ghc-94.nix) { inherit pkgs inputs; }; + ghc925Config = (import ./configuration-ghc-92.nix) { inherit pkgs inputs; }; + ghc943Config = (import ./configuration-ghc-94.nix) { inherit pkgs inputs; }; # GHC versions # While HLS still works fine with 8.10 GHCs, we only support the versions that are cached @@ -267,14 +202,14 @@ ghcVersion = "ghc" + (pkgs.lib.replaceStrings ["."] [""] pkgs.haskellPackages.ghc.version); cases = { ghc902 = ghc902Config.tweakHpkgs (pkgs.hlsHpkgs "ghc902"); - ghc924 = ghc924Config.tweakHpkgs (pkgs.hlsHpkgs "ghc924"); - ghc942 = ghc942Config.tweakHpkgs (pkgs.hlsHpkgs "ghc942"); + ghc925 = ghc925Config.tweakHpkgs (pkgs.hlsHpkgs "ghc925"); + ghc943 = ghc943Config.tweakHpkgs (pkgs.hlsHpkgs "ghc943"); }; in { default = cases."${ghcVersion}"; } // cases; ghc902 = supportedGHCs.ghc902; - ghc924 = supportedGHCs.ghc924; - ghc942 = supportedGHCs.ghc942; + ghc925 = supportedGHCs.ghc925; + ghc943 = supportedGHCs.ghc943; ghcDefault = supportedGHCs.default; pythonWithPackages = pkgs.python3.withPackages (ps: [ps.sphinx ps.myst-parser ps.sphinx_rtd_theme ps.pip]); @@ -391,16 +326,16 @@ simpleDevShells = { haskell-language-server-dev = mkDevShell ghcDefault "cabal.project"; haskell-language-server-902-dev = mkDevShell ghc902 "cabal.project"; - haskell-language-server-924-dev = mkDevShell ghc924 "cabal.project"; - haskell-language-server-942-dev = mkDevShell ghc942 "cabal.project"; + haskell-language-server-925-dev = mkDevShell ghc925 "cabal.project"; + haskell-language-server-943-dev = mkDevShell ghc943 "cabal.project"; }; # Developement shell, haskell packages are also provided by nix nixDevShells = { haskell-language-server-dev-nix = mkDevShellWithNixDeps ghcDefault "cabal.project"; haskell-language-server-902-dev-nix = mkDevShellWithNixDeps ghc902 "cabal.project"; - haskell-language-server-924-dev-nix = mkDevShellWithNixDeps ghc924 "cabal.project"; - haskell-language-server-942-dev-nix = mkDevShellWithNixDeps ghc942 "cabal.project"; + haskell-language-server-925-dev-nix = mkDevShellWithNixDeps ghc925 "cabal.project"; + haskell-language-server-943-dev-nix = mkDevShellWithNixDeps ghc943 "cabal.project"; }; # The default shell provided by Nixpkgs for a Haskell package (i.e. the @@ -408,15 +343,15 @@ envShells = { haskell-language-server-dev-env = mkEnvShell ghcDefault; haskell-language-server-902-dev-env = mkEnvShell ghc902; - haskell-language-server-924-dev-env = mkEnvShell ghc924; - haskell-language-server-942-dev-env = mkEnvShell ghc942; + haskell-language-server-925-dev-env = mkEnvShell ghc925; + haskell-language-server-943-dev-env = mkEnvShell ghc943; }; allPackages = { haskell-language-server = mkExe ghcDefault; haskell-language-server-902 = mkExe ghc902; - haskell-language-server-924 = mkExe ghc924; - haskell-language-server-942 = mkExe ghc942; + haskell-language-server-925 = mkExe ghc925; + haskell-language-server-943 = mkExe ghc943; }; devShells = simpleDevShells // nixDevShells // envShells // { From 5dbbd852abcf154c593ed7860cb86b2da9a855dc Mon Sep 17 00:00:00 2001 From: "Rune K. Svendsen" Date: Mon, 21 Nov 2022 17:45:26 +0100 Subject: [PATCH 197/213] Parse .cabal files; show error and warning diagnostics (#2954) * Extract cabal plugin in its own package * hls-cabal-plugin: Add plugin Add golden parse test for test/testdata/simple.cabal Add module Ide.Plugin.Cabal.Diag Also: add -Wall Add parseCabalFileContents Use VFS for cabal file contents Diagnostics * Parse and display Errors * Parse and display Warnings Code Actions * Code Action for License Field * Add CI workflow * Add test-suite for hls-cabal-plugin * Fix various cabal issues * Update Codeowners file for hls-cabal-plugin * Document Bytestring is UTF-8 encoded Co-authored-by: Julian Ospald * Remove code duplication * Add cabal files of interest and kick function Configure a "kick" function for cabal files that is run when the shake queue needs to be restarted. Copy pastes from ghcide and 'files of interest'. Maybe more abstraction needed. * Add more documentation * Mark flaky test-case as flaky with issue ref * Make fendor the only CODEOWNER for hls-cabal-plugin * Add missing extra-source-files for hls-cabal-plugin * Add proper CHANGELOG entry for the first version of hls-cabal-plugin * Add support for Cabal 3.8 * Set diagnostics source to cabal * Remove unused function * Add unit tests for code action utilities * Remove overly specific logging of diagnostics from hls-cabal-plugin * Improve logging for Cabal FOIs * Add Range manipulation functions * Use Range manipulation functions from hls-plugin-api * Add more documentation for crucial shake restart function * Add hls-cabal-plugin features to features.md * Re-use existing GetFileContents rule Co-authored-by: Fendor Co-authored-by: fendor Co-authored-by: Julian Ospald Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .github/workflows/hackage.yml | 1 + .github/workflows/test.yml | 4 + CODEOWNERS | 1 + cabal.project | 1 + docs/features.md | 14 + haskell-language-server.cabal | 12 + hls-plugin-api/src/Ide/PluginUtils.hs | 29 +- hls-test-utils/src/Test/Hls.hs | 28 +- plugins/hls-cabal-plugin/CHANGELOG.md | 6 + plugins/hls-cabal-plugin/LICENSE | 20 ++ .../hls-cabal-plugin/hls-cabal-plugin.cabal | 81 ++++++ .../hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 258 ++++++++++++++++++ .../src/Ide/Plugin/Cabal/Diagnostics.hs | 85 ++++++ .../src/Ide/Plugin/Cabal/LicenseSuggest.hs | 73 +++++ .../src/Ide/Plugin/Cabal/Parse.hs | 27 ++ plugins/hls-cabal-plugin/test/Main.hs | 168 ++++++++++++ .../test/testdata/invalid.cabal | 8 + .../test/testdata/licenseCodeAction.cabal | 8 + .../test/testdata/simple-cabal/A.hs | 4 + .../test/testdata/simple-cabal/cabal.project | 1 + .../test/testdata/simple-cabal/hie.yaml | 2 + .../testdata/simple-cabal/simple-cabal.cabal | 10 + .../test/testdata/simple.cabal | 24 ++ src/HlsPlugins.hs | 7 +- stack-lts19.yaml | 1 + stack.yaml | 1 + 26 files changed, 867 insertions(+), 7 deletions(-) create mode 100644 plugins/hls-cabal-plugin/CHANGELOG.md create mode 100644 plugins/hls-cabal-plugin/LICENSE create mode 100644 plugins/hls-cabal-plugin/hls-cabal-plugin.cabal create mode 100644 plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs create mode 100644 plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Diagnostics.hs create mode 100644 plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/LicenseSuggest.hs create mode 100644 plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Parse.hs create mode 100644 plugins/hls-cabal-plugin/test/Main.hs create mode 100644 plugins/hls-cabal-plugin/test/testdata/invalid.cabal create mode 100644 plugins/hls-cabal-plugin/test/testdata/licenseCodeAction.cabal create mode 100644 plugins/hls-cabal-plugin/test/testdata/simple-cabal/A.hs create mode 100644 plugins/hls-cabal-plugin/test/testdata/simple-cabal/cabal.project create mode 100644 plugins/hls-cabal-plugin/test/testdata/simple-cabal/hie.yaml create mode 100644 plugins/hls-cabal-plugin/test/testdata/simple-cabal/simple-cabal.cabal create mode 100644 plugins/hls-cabal-plugin/test/testdata/simple.cabal diff --git a/.github/workflows/hackage.yml b/.github/workflows/hackage.yml index f909ef6fb9..34e6b8b62b 100644 --- a/.github/workflows/hackage.yml +++ b/.github/workflows/hackage.yml @@ -28,6 +28,7 @@ jobs: matrix: package: ["hie-compat", "hls-graph", "shake-bench", "hls-plugin-api", "ghcide", "hls-test-utils", + "hls-cabal-plugin", "hls-brittany-plugin", "hls-floskell-plugin", "hls-fourmolu-plugin", "hls-ormolu-plugin", "hls-stylish-haskell-plugin", "hls-class-plugin", "hls-eval-plugin", "hls-explicit-imports-plugin", diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3a79cf13d1..fb9022e113 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -259,6 +259,10 @@ jobs: name: Test hls-cabal-fmt-plugin test suite run: cabal test hls-cabal-fmt-plugin --flag=isolateTests --test-options="$TEST_OPTS" || cabal test hls-cabal-fmt-plugin --flag=isolateTests --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-cabal-fmt-plugin --flag=isolateTests --test-options="$TEST_OPTS" + - if: matrix.test + name: Test hls-cabal-plugin test suite + run: cabal test hls-cabal-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-cabal-plugin --test-options="$TEST_OPTS" + test_post_job: if: always() runs-on: ubuntu-latest diff --git a/CODEOWNERS b/CODEOWNERS index 268f136ff9..dbe8495fbc 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -9,6 +9,7 @@ # Plugins /plugins/hls-alternate-number-format-plugin @drsooch /plugins/hls-brittany-plugin @fendor +/plugins/hls-cabal-plugin @fendor /plugins/hls-cabal-fmt-plugin @VeryMilkyJoe @fendor /plugins/hls-call-hierarchy-plugin @July541 /plugins/hls-class-plugin @Ailrun diff --git a/cabal.project b/cabal.project index 4dee7fc198..6536f26465 100644 --- a/cabal.project +++ b/cabal.project @@ -8,6 +8,7 @@ packages: ./ghcide/test ./hls-plugin-api ./hls-test-utils + ./plugins/hls-cabal-plugin ./plugins/hls-cabal-fmt-plugin ./plugins/hls-tactics-plugin ./plugins/hls-brittany-plugin diff --git a/docs/features.md b/docs/features.md index 5b025a82aa..c032848abb 100644 --- a/docs/features.md +++ b/docs/features.md @@ -44,6 +44,12 @@ Provided by: `hls-stan-plugin` Provides Stan hints as diagnostics. +### Cabal parse errors and warnings + +Provided by: `hls-cabal-plugin` + +Provides errors and warnings from Cabal as diagnostics + ## Hovers Provided by: `ghcide` @@ -308,6 +314,14 @@ Expand record wildcards, explicitly listing all record fields as field puns. ![Explicit Wildcard Demo](../plugins/hls-explicit-record-fields-plugin/wildcard.gif) +### Unknown SPDX License suggestion + +Provided by: `hls-cabal-plugin` + +Code action kind: `quickfix` + +Correct common misspelling of SPDX Licenses such as `BSD-3-Clause`. + ## Code lenses ### Add type signature diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index cc69eed3a1..6926f53318 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -63,6 +63,12 @@ flag ignore-plugins-ghc-bounds default: False manual: True + +flag cabal + description: Enable cabal plugin + default: True + manual: True + flag class description: Enable class plugin default: True @@ -215,6 +221,11 @@ common cabalfmt build-depends: hls-cabal-fmt-plugin ^>= 0.1.0.0 cpp-options: -Dhls_cabalfmt +common cabal + if flag(cabal) + build-depends: hls-cabal-plugin ^>= 0.1 + cpp-options: -Dhls_cabal + common class if flag(class) build-depends: hls-class-plugin ^>= 1.1 @@ -358,6 +369,7 @@ library , warnings , pedantic -- plugins + , cabal , callHierarchy , cabalfmt , changeTypeSignature diff --git a/hls-plugin-api/src/Ide/PluginUtils.hs b/hls-plugin-api/src/Ide/PluginUtils.hs index 3203cbcf8a..3b1fbb7ac2 100644 --- a/hls-plugin-api/src/Ide/PluginUtils.hs +++ b/hls-plugin-api/src/Ide/PluginUtils.hs @@ -2,9 +2,12 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-} module Ide.PluginUtils - ( WithDeletions(..), - getProcessID, + ( -- * LSP Range manipulation functions normalize, + extendNextLine, + extendLineStart, + WithDeletions(..), + getProcessID, makeDiffTextEdit, makeDiffTextEditAdditive, diffText, @@ -67,9 +70,27 @@ import qualified Text.Megaparsec.Char.Lexer as P -- --------------------------------------------------------------------- -- | Extend to the line below and above to replace newline character. +-- +-- >>> normalize (Range (Position 5 5) (Position 5 10)) +-- Range (Position 5 0) (Position 6 0) normalize :: Range -> Range -normalize (Range (Position sl _) (Position el _)) = - Range (Position sl 0) (Position (el + 1) 0) +normalize = extendLineStart . extendNextLine + +-- | Extend 'Range' to the start of the next line. +-- +-- >>> extendNextLine (Range (Position 5 5) (Position 5 10)) +-- Range (Position 5 5) (Position 6 0) +extendNextLine :: Range -> Range +extendNextLine (Range s (Position el _)) = + Range s (Position (el + 1) 0) + +-- | Extend 'Range' to the start of the current line. +-- +-- >>> extendLineStart (Range (Position 5 5) (Position 5 10)) +-- Range (Position 5 0) (Position 5 10) +extendLineStart :: Range -> Range +extendLineStart (Range (Position sl _) e) = + Range (Position sl 0) e -- --------------------------------------------------------------------- diff --git a/hls-test-utils/src/Test/Hls.hs b/hls-test-utils/src/Test/Hls.hs index 7f61f66ae6..a9d3a595f3 100644 --- a/hls-test-utils/src/Test/Hls.hs +++ b/hls-test-utils/src/Test/Hls.hs @@ -16,6 +16,7 @@ module Test.Hls defaultTestRunner, goldenGitDiff, goldenWithHaskellDoc, + goldenWithCabalDoc, goldenWithHaskellDocFormatter, goldenWithCabalDocFormatter, def, @@ -124,12 +125,35 @@ goldenWithHaskellDoc -> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree -goldenWithHaskellDoc plugin title testDataDir path desc ext act = +goldenWithHaskellDoc = goldenWithDoc "haskell" + +goldenWithCabalDoc + :: PluginDescriptor IdeState + -> TestName + -> FilePath + -> FilePath + -> FilePath + -> FilePath + -> (TextDocumentIdentifier -> Session ()) + -> TestTree +goldenWithCabalDoc = goldenWithDoc "cabal" + +goldenWithDoc + :: T.Text + -> PluginDescriptor IdeState + -> TestName + -> FilePath + -> FilePath + -> FilePath + -> FilePath + -> (TextDocumentIdentifier -> Session ()) + -> TestTree +goldenWithDoc fileType plugin title testDataDir path desc ext act = goldenGitDiff title (testDataDir path <.> desc <.> ext) $ runSessionWithServer plugin testDataDir $ TL.encodeUtf8 . TL.fromStrict <$> do - doc <- openDoc (path <.> ext) "haskell" + doc <- openDoc (path <.> ext) fileType void waitForBuildQueue act doc documentContents doc diff --git a/plugins/hls-cabal-plugin/CHANGELOG.md b/plugins/hls-cabal-plugin/CHANGELOG.md new file mode 100644 index 0000000000..809439f0a8 --- /dev/null +++ b/plugins/hls-cabal-plugin/CHANGELOG.md @@ -0,0 +1,6 @@ +# Revision history for hls-cabal-plugin + +## 0.1.0.0 -- YYYY-mm-dd + +* Provide Diagnostics on parse errors and warnings for .cabal files +* Provide CodeAction for the common SPDX License mistake "BSD3" instead of "BSD-3-Clause" diff --git a/plugins/hls-cabal-plugin/LICENSE b/plugins/hls-cabal-plugin/LICENSE new file mode 100644 index 0000000000..6d34465ea5 --- /dev/null +++ b/plugins/hls-cabal-plugin/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2022 Fendor + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/plugins/hls-cabal-plugin/hls-cabal-plugin.cabal b/plugins/hls-cabal-plugin/hls-cabal-plugin.cabal new file mode 100644 index 0000000000..67170c10ab --- /dev/null +++ b/plugins/hls-cabal-plugin/hls-cabal-plugin.cabal @@ -0,0 +1,81 @@ +cabal-version: 3.0 +name: hls-cabal-plugin +version: 0.1.0.0 +synopsis: Cabal integration plugin with Haskell Language Server +description: + Please see the README on GitHub at + +homepage: +license: MIT +license-file: LICENSE +author: Fendor +maintainer: fendor@posteo.de +category: Development +extra-source-files: + CHANGELOG.md + test/testdata/*.cabal + test/testdata/simple-cabal/A.hs + test/testdata/simple-cabal/cabal.project + test/testdata/simple-cabal/hie.yaml + test/testdata/simple-cabal/simple-cabal.cabal + +common warnings + ghc-options: -Wall + +library + import: warnings + exposed-modules: + Ide.Plugin.Cabal + Ide.Plugin.Cabal.Diagnostics + Ide.Plugin.Cabal.LicenseSuggest + Ide.Plugin.Cabal.Parse + + build-depends: + , base >=4.12 && <5 + , bytestring + -- Ideally, we only want to support a single Cabal version, supporting + -- older versions is completely pointless since Cabal is backwards compatible, + -- the latest Cabal version can parse all versions of the Cabal file format. + -- + -- However, stack is making this difficult, if we change the version of Cabal, + -- we essentially need to make sure all other packages in the snapshot have their + -- Cabal dependency version relaxed. + -- Most packages have a Hackage revision, but stack won't pick these up (for sensible reasons) + -- automatically, forcing us to manually update the packages revision id. + -- This is a lot of work for almost zero benefit, so we just allow more versions here + -- and we eventually completely drop support for building HLS with stack. + , Cabal ^>=3.2 || ^>=3.4 || ^>=3.6 || ^>= 3.8 + , deepseq + , directory + , extra >=1.7.4 + , ghcide ^>= 1.8 + , hashable + , hls-plugin-api ^>=1.5 + , hls-graph ^>=1.8 + , lsp ^>=1.6.0.0 + , lsp-types ^>=1.6.0.0 + , regex-tdfa ^>=1.3.1 + , stm + , text + , unordered-containers >=0.2.10.0 + + hs-source-dirs: src + default-language: Haskell2010 + +test-suite tests + import: warnings + default-language: Haskell2010 + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Main.hs + build-depends: + , base + , bytestring + , filepath + , ghcide + , hls-cabal-plugin + , hls-test-utils ^>=1.4 + , lens + , lsp-types + , tasty-hunit + , text diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs new file mode 100644 index 0000000000..72a16c8ea6 --- /dev/null +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -0,0 +1,258 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE TypeFamilies #-} + +module Ide.Plugin.Cabal (descriptor, Log(..)) where + +import Control.Concurrent.STM +import Control.Concurrent.Strict +import Control.DeepSeq +import Control.Monad.Extra +import Control.Monad.IO.Class +import qualified Data.ByteString as BS +import Data.Hashable +import Data.HashMap.Strict (HashMap) +import qualified Data.HashMap.Strict as HashMap +import qualified Data.List.NonEmpty as NE +import Data.Maybe (mapMaybe) +import qualified Data.Text.Encoding as Encoding +import Data.Typeable +import Development.IDE as D +import Development.IDE.Core.Shake (restartShakeSession) +import qualified Development.IDE.Core.Shake as Shake +import Development.IDE.Graph (alwaysRerun) +import GHC.Generics +import qualified Ide.Plugin.Cabal.Diagnostics as Diagnostics +import qualified Ide.Plugin.Cabal.LicenseSuggest as LicenseSuggest +import qualified Ide.Plugin.Cabal.Parse as Parse +import Ide.Plugin.Config (Config) +import Ide.Types +import Language.LSP.Server (LspM) +import Language.LSP.Types +import qualified Language.LSP.Types as LSP +import qualified Language.LSP.VFS as VFS + +data Log + = LogModificationTime NormalizedFilePath FileVersion + | LogShake Shake.Log + | LogDocOpened Uri + | LogDocModified Uri + | LogDocSaved Uri + | LogDocClosed Uri + | LogFOI (HashMap NormalizedFilePath FileOfInterestStatus) + deriving Show + +instance Pretty Log where + pretty = \case + LogShake log' -> pretty log' + LogModificationTime nfp modTime -> + "Modified:" <+> pretty (fromNormalizedFilePath nfp) <+> pretty (show modTime) + LogDocOpened uri -> + "Opened text document:" <+> pretty (getUri uri) + LogDocModified uri -> + "Modified text document:" <+> pretty (getUri uri) + LogDocSaved uri -> + "Saved text document:" <+> pretty (getUri uri) + LogDocClosed uri -> + "Closed text document:" <+> pretty (getUri uri) + LogFOI files -> + "Set files of interest to:" <+> viaShow files + + +descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState +descriptor recorder plId = (defaultCabalPluginDescriptor plId) + { pluginRules = cabalRules recorder + , pluginHandlers = mkPluginHandler STextDocumentCodeAction licenseSuggestCodeAction + , pluginNotificationHandlers = mconcat + [ mkPluginNotificationHandler LSP.STextDocumentDidOpen $ + \ide vfs _ (DidOpenTextDocumentParams TextDocumentItem{_uri,_version}) -> liftIO $ do + whenUriFile _uri $ \file -> do + log' Debug $ LogDocOpened _uri + addFileOfInterest recorder ide file Modified{firstOpen=True} + restartCabalShakeSession (shakeExtras ide) vfs file "(opened)" + + , mkPluginNotificationHandler LSP.STextDocumentDidChange $ + \ide vfs _ (DidChangeTextDocumentParams VersionedTextDocumentIdentifier{_uri} _) -> liftIO $ do + whenUriFile _uri $ \file -> do + log' Debug $ LogDocModified _uri + addFileOfInterest recorder ide file Modified{firstOpen=False} + restartCabalShakeSession (shakeExtras ide) vfs file "(changed)" + + , mkPluginNotificationHandler LSP.STextDocumentDidSave $ + \ide vfs _ (DidSaveTextDocumentParams TextDocumentIdentifier{_uri} _) -> liftIO $ do + whenUriFile _uri $ \file -> do + log' Debug $ LogDocSaved _uri + addFileOfInterest recorder ide file OnDisk + restartCabalShakeSession (shakeExtras ide) vfs file "(saved)" + + , mkPluginNotificationHandler LSP.STextDocumentDidClose $ + \ide vfs _ (DidCloseTextDocumentParams TextDocumentIdentifier{_uri}) -> liftIO $ do + whenUriFile _uri $ \file -> do + log' Debug $ LogDocClosed _uri + deleteFileOfInterest recorder ide file + restartCabalShakeSession (shakeExtras ide) vfs file "(closed)" + ] + } + where + log' = logWith recorder + + whenUriFile :: Uri -> (NormalizedFilePath -> IO ()) -> IO () + whenUriFile uri act = whenJust (LSP.uriToFilePath uri) $ act . toNormalizedFilePath' + +-- | Helper function to restart the shake session, specifically for modifying .cabal files. +-- No special logic, just group up a bunch of functions you need for the base +-- Notification Handlers. +-- +-- To make sure diagnostics are up to date, we need to tell shake that the file was touched and +-- needs to be re-parsed. That's what we do when we record the dirty key that our parsing +-- rule depends on. +-- Then we restart the shake session, so that changes to our virtual files are actually picked up. +restartCabalShakeSession :: ShakeExtras -> VFS.VFS -> NormalizedFilePath -> String -> IO () +restartCabalShakeSession shakeExtras vfs file actionMsg = do + join $ atomically $ Shake.recordDirtyKeys shakeExtras GetModificationTime [file] + restartShakeSession shakeExtras (VFSModified vfs) (fromNormalizedFilePath file ++ " " ++ actionMsg) [] + +-- ---------------------------------------------------------------- +-- Plugin Rules +-- ---------------------------------------------------------------- + +data ParseCabal = ParseCabal + deriving (Eq, Show, Typeable, Generic) +instance Hashable ParseCabal +instance NFData ParseCabal + +type instance RuleResult ParseCabal = () + +cabalRules :: Recorder (WithPriority Log) -> Rules () +cabalRules recorder = do + -- Make sure we initialise the cabal files-of-interest. + ofInterestRules recorder + -- Rule to produce diagnostics for cabal files. + define (cmapWithPrio LogShake recorder) $ \ParseCabal file -> do + -- whenever this key is marked as dirty (e.g., when a user writes stuff to it), + -- we rerun this rule because this rule *depends* on GetModificationTime. + (t, mCabalSource) <- use_ GetFileContents file + log' Debug $ LogModificationTime file t + contents <- case mCabalSource of + Just sources -> pure $ Encoding.encodeUtf8 sources + Nothing -> do + liftIO $ BS.readFile $ fromNormalizedFilePath file + + (pWarnings, pm) <- liftIO $ Parse.parseCabalFileContents contents + let warningDiags = fmap (Diagnostics.warningDiagnostic file) pWarnings + case pm of + Left (_cabalVersion, pErrorNE) -> do + let errorDiags = NE.toList $ NE.map (Diagnostics.errorDiagnostic file) pErrorNE + allDiags = errorDiags <> warningDiags + pure (allDiags, Nothing) + Right _ -> do + pure (warningDiags, Just ()) + + action $ do + -- Run the cabal kick. This code always runs when 'shakeRestart' is run. + -- Must be careful to not impede the performance too much. Crucial to + -- a snappy IDE experience. + kick + where + log' = logWith recorder + +-- | This is the kick function for the cabal plugin. +-- We run this action, whenever we shake session us run/restarted, which triggers +-- actions to produce diagnostics for cabal files. +-- +-- It is paramount that this kick-function can be run quickly, since it is a blocking +-- function invocation. +kick :: Action () +kick = do + files <- HashMap.keys <$> getCabalFilesOfInterestUntracked + void $ uses ParseCabal files + +-- ---------------------------------------------------------------- +-- Code Actions +-- ---------------------------------------------------------------- + +licenseSuggestCodeAction + :: IdeState + -> PluginId + -> CodeActionParams + -> LspM Config (Either ResponseError (ResponseResult 'TextDocumentCodeAction)) +licenseSuggestCodeAction _ _ (CodeActionParams _ _ (TextDocumentIdentifier uri) _range CodeActionContext{_diagnostics=List diags}) = + pure $ Right $ List $ mapMaybe (fmap InR . LicenseSuggest.licenseErrorAction uri) diags + +-- ---------------------------------------------------------------- +-- Cabal file of Interest rules and global variable +-- ---------------------------------------------------------------- + +-- | Cabal files that are currently open in the lsp-client. +-- Specific actions happen when these files are saved, closed or modified, +-- such as generating diagnostics, re-parsing, etc... +-- +-- We need to store the open files to parse them again if we restart the shake session. +-- Restarting of the shake session happens whenever these files are modified. +newtype OfInterestCabalVar = OfInterestCabalVar (Var (HashMap NormalizedFilePath FileOfInterestStatus)) + +instance Shake.IsIdeGlobal OfInterestCabalVar + +data IsCabalFileOfInterest = IsCabalFileOfInterest + deriving (Eq, Show, Typeable, Generic) +instance Hashable IsCabalFileOfInterest +instance NFData IsCabalFileOfInterest + +type instance RuleResult IsCabalFileOfInterest = CabalFileOfInterestResult + +data CabalFileOfInterestResult = NotCabalFOI | IsCabalFOI FileOfInterestStatus + deriving (Eq, Show, Typeable, Generic) +instance Hashable CabalFileOfInterestResult +instance NFData CabalFileOfInterestResult + +-- | The rule that initialises the files of interest state. +-- +-- Needs to be run on start-up. +ofInterestRules :: Recorder (WithPriority Log) -> Rules () +ofInterestRules recorder = do + Shake.addIdeGlobal . OfInterestCabalVar =<< liftIO (newVar HashMap.empty) + Shake.defineEarlyCutoff (cmapWithPrio LogShake recorder) $ RuleNoDiagnostics $ \IsCabalFileOfInterest f -> do + alwaysRerun + filesOfInterest <- getCabalFilesOfInterestUntracked + let foi = maybe NotCabalFOI IsCabalFOI $ f `HashMap.lookup` filesOfInterest + fp = summarize foi + res = (Just fp, Just foi) + return res + where + summarize NotCabalFOI = BS.singleton 0 + summarize (IsCabalFOI OnDisk) = BS.singleton 1 + summarize (IsCabalFOI (Modified False)) = BS.singleton 2 + summarize (IsCabalFOI (Modified True)) = BS.singleton 3 + +getCabalFilesOfInterestUntracked :: Action (HashMap NormalizedFilePath FileOfInterestStatus) +getCabalFilesOfInterestUntracked = do + OfInterestCabalVar var <- Shake.getIdeGlobalAction + liftIO $ readVar var + +addFileOfInterest :: Recorder (WithPriority Log) -> IdeState -> NormalizedFilePath -> FileOfInterestStatus -> IO () +addFileOfInterest recorder state f v = do + OfInterestCabalVar var <- Shake.getIdeGlobalState state + (prev, files) <- modifyVar var $ \dict -> do + let (prev, new) = HashMap.alterF (, Just v) f dict + pure (new, (prev, new)) + when (prev /= Just v) $ do + join $ atomically $ Shake.recordDirtyKeys (shakeExtras state) IsFileOfInterest [f] + log' Debug $ LogFOI files + where + log' = logWith recorder + +deleteFileOfInterest :: Recorder (WithPriority Log) -> IdeState -> NormalizedFilePath -> IO () +deleteFileOfInterest recorder state f = do + OfInterestCabalVar var <- Shake.getIdeGlobalState state + files <- modifyVar' var $ HashMap.delete f + join $ atomically $ Shake.recordDirtyKeys (shakeExtras state) IsFileOfInterest [f] + log' Debug $ LogFOI files + where + log' = logWith recorder diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Diagnostics.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Diagnostics.hs new file mode 100644 index 0000000000..2b077cfaf1 --- /dev/null +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Diagnostics.hs @@ -0,0 +1,85 @@ +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} +module Ide.Plugin.Cabal.Diagnostics +( errorDiagnostic +, warningDiagnostic +, positionFromCabalPosition + -- * Re-exports +, FileDiagnostic +, Diagnostic(..) +) +where + +import qualified Data.Text as T +import Development.IDE (FileDiagnostic, + ShowDiagnostic (ShowDiag)) +import Distribution.Fields (showPError, showPWarning) +import qualified Ide.Plugin.Cabal.Parse as Lib +import Ide.PluginUtils (extendNextLine) +import Language.LSP.Types (Diagnostic (..), + DiagnosticSeverity (..), + DiagnosticSource, NormalizedFilePath, + Position (Position), Range (Range), + fromNormalizedFilePath) + +-- | Produce a diagnostic from a Cabal parser error +errorDiagnostic :: NormalizedFilePath -> Lib.PError -> FileDiagnostic +errorDiagnostic fp err@(Lib.PError pos _) = + mkDiag fp "cabal" DsError (toBeginningOfNextLine pos) msg + where + msg = T.pack $ showPError (fromNormalizedFilePath fp) err + +-- | Produce a diagnostic from a Cabal parser warning +warningDiagnostic :: NormalizedFilePath -> Lib.PWarning -> FileDiagnostic +warningDiagnostic fp warning@(Lib.PWarning _ pos _) = + mkDiag fp "cabal" DsWarning (toBeginningOfNextLine pos) msg + where + msg = T.pack $ showPWarning (fromNormalizedFilePath fp) warning + +-- | The Cabal parser does not output a _range_ for a warning/error, +-- only a single source code 'Lib.Position'. +-- We define the range to be _from_ this position +-- _to_ the first column of the next line. +toBeginningOfNextLine :: Lib.Position -> Range +toBeginningOfNextLine cabalPos = extendNextLine $ Range pos pos + where + pos = positionFromCabalPosition cabalPos + +-- | Convert a 'Lib.Position' from Cabal to a 'Range' that LSP understands. +-- +-- Prefer this function over hand-rolled unpacking/packing, since LSP is zero-based, +-- while Cabal is one-based. +-- +-- >>> positionFromCabalPosition $ Lib.Position 1 1 +-- Position 0 0 +positionFromCabalPosition :: Lib.Position -> Position +positionFromCabalPosition (Lib.Position line column) = Position (fromIntegral line') (fromIntegral col') + where + -- LSP is zero-based, Cabal is one-based + line' = line-1 + col' = column-1 + +-- | Create a 'FileDiagnostic' +mkDiag + :: NormalizedFilePath + -- ^ Cabal file path + -> DiagnosticSource + -- ^ Where does the diagnostic come from? + -> DiagnosticSeverity + -- ^ Severity + -> Range + -- ^ Which source code range should the editor highlight? + -> T.Text + -- ^ The message displayed by the editor + -> FileDiagnostic +mkDiag file diagSource sev loc msg = (file, ShowDiag,) + Diagnostic + { _range = loc + , _severity = Just sev + , _source = Just diagSource + , _message = msg + , _code = Nothing + , _tags = Nothing + , _relatedInformation = Nothing + } diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/LicenseSuggest.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/LicenseSuggest.hs new file mode 100644 index 0000000000..2381286c95 --- /dev/null +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/LicenseSuggest.hs @@ -0,0 +1,73 @@ +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE ExplicitNamespaces #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +module Ide.Plugin.Cabal.LicenseSuggest +( licenseErrorSuggestion +, licenseErrorAction + -- * Re-exports +, T.Text +, Diagnostic(..) +) +where + +import qualified Data.HashMap.Strict as Map +import qualified Data.Text as T +import Language.LSP.Types (CodeAction (CodeAction), + CodeActionKind (CodeActionQuickFix), + Diagnostic (..), List (List), + Position (Position), Range (Range), + TextEdit (TextEdit), Uri, + WorkspaceEdit (WorkspaceEdit)) +import Text.Regex.TDFA + +-- | Given a diagnostic returned by 'Ide.Plugin.Cabal.Diag.errorDiagnostic', +-- if it represents an "Unknown SPDX license identifier"-error along +-- with a suggestion, then return a 'CodeAction' for replacing the +-- the incorrect license identifier with the suggestion. +licenseErrorAction + :: Uri + -- ^ File for which the diagnostic was generated + -> Diagnostic + -- ^ Output of 'Ide.Plugin.Cabal.Diag.errorDiagnostic' + -> Maybe CodeAction +licenseErrorAction uri diag = + mkCodeAction <$> licenseErrorSuggestion (_message diag) + where + mkCodeAction (original, suggestion) = + let + -- The Cabal parser does not output the _range_ of the incorrect license identifier, + -- only a single source code position. Consequently, in 'Ide.Plugin.Cabal.Diag.errorDiagnostic' + -- we define the range to be from the returned position the first column of the next line. + -- Since the "replace" code action replaces this range, we need to modify the range to + -- start at the first character of the invalid license identifier. We achieve this by + -- subtracting the length of the identifier from the beginning of the range. + adjustRange (Range (Position line col) rangeTo) = + Range (Position line (col - fromIntegral (T.length original))) rangeTo + title = "Replace with " <> suggestion + -- We must also add a newline character to the replacement since the range returned by + -- 'Ide.Plugin.Cabal.Diag.errorDiagnostic' ends at the beginning of the following line. + tedit = [TextEdit (adjustRange $ _range diag) (suggestion <> "\n")] + edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing Nothing + in CodeAction title (Just CodeActionQuickFix) (Just $ List []) Nothing Nothing (Just edit) Nothing Nothing + +-- | Given an error message returned by 'Ide.Plugin.Cabal.Diag.errorDiagnostic', +-- if it represents an "Unknown SPDX license identifier"-error along +-- with a suggestion then return the suggestion (after the "Do you mean"-text) +-- along with the incorrect identifier. +licenseErrorSuggestion + :: T.Text + -- ^ Output of 'Ide.Plugin.Cabal.Diag.errorDiagnostic' + -> Maybe (T.Text, T.Text) + -- ^ (Original (incorrect) license identifier, suggested replacement) +licenseErrorSuggestion message = + mSuggestion message >>= \case + [original, suggestion] -> Just (original, suggestion) + _ -> Nothing + where + regex :: T.Text + regex = "Unknown SPDX license identifier: '(.*)' Do you mean (.*)\\?" + mSuggestion msg = getMatch <$> (msg :: T.Text) =~~ regex + getMatch :: (T.Text, T.Text, T.Text, [T.Text]) -> [T.Text] + getMatch (_, _, _, results) = results diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Parse.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Parse.hs new file mode 100644 index 0000000000..28700c5104 --- /dev/null +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Parse.hs @@ -0,0 +1,27 @@ +module Ide.Plugin.Cabal.Parse +( parseCabalFileContents + -- * Re-exports +, FilePath +, NonEmpty(..) +, PWarning(..) +, Version +, PError(..) +, Position(..) +, GenericPackageDescription(..) +) where + +import qualified Data.ByteString as BS +import Data.List.NonEmpty (NonEmpty (..)) +import Distribution.Fields (PError (..), + PWarning (..)) +import Distribution.Fields.ParseResult (runParseResult) +import Distribution.PackageDescription.Parsec (parseGenericPackageDescription) +import Distribution.Parsec.Position (Position (..)) +import Distribution.Types.GenericPackageDescription (GenericPackageDescription (..)) +import Distribution.Types.Version (Version) + +parseCabalFileContents + :: BS.ByteString -- ^ UTF-8 encoded bytestring + -> IO ([PWarning], Either (Maybe Version, NonEmpty PError) GenericPackageDescription) +parseCabalFileContents bs = + pure $ runParseResult (parseGenericPackageDescription bs) diff --git a/plugins/hls-cabal-plugin/test/Main.hs b/plugins/hls-cabal-plugin/test/Main.hs new file mode 100644 index 0000000000..b2db2f4315 --- /dev/null +++ b/plugins/hls-cabal-plugin/test/Main.hs @@ -0,0 +1,168 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# OPTIONS_GHC -Wno-orphans #-} +{-# LANGUAGE NamedFieldPuns #-} +module Main + ( main + ) where + +import Control.Lens ((^.)) +import qualified Data.ByteString as BS +import Data.Either (isRight) +import Data.Function +import qualified Data.Text as Text +import Development.IDE.Types.Logger +import Ide.Plugin.Cabal +import Ide.Plugin.Cabal.LicenseSuggest (licenseErrorSuggestion) +import qualified Ide.Plugin.Cabal.Parse as Lib +import qualified Language.LSP.Types.Lens as J +import System.FilePath +import Test.Hls + + +cabalPlugin :: Recorder (WithPriority Log) -> PluginDescriptor IdeState +cabalPlugin recorder = descriptor recorder "cabal" + +main :: IO () +main = do + recorder <- initialiseRecorder True + defaultTestRunner $ + testGroup "Cabal Plugin Tests" + [ unitTests + , pluginTests recorder + ] + +-- | @initialiseRecorder silent@ +-- +-- If @'silent' == True@, then don't log anything, otherwise +-- the recorder is the standard recorder of HLS. Useful for debugging. +initialiseRecorder :: Bool -> IO (Recorder (WithPriority Log)) +initialiseRecorder True = pure mempty +initialiseRecorder False = do + docWithPriorityRecorder <- makeDefaultStderrRecorder Nothing Debug + + let docWithFilteredPriorityRecorder = + docWithPriorityRecorder + & cfilter (\WithPriority{ priority } -> priority >= Debug) + pure $ docWithFilteredPriorityRecorder + & cmapWithPrio pretty + +-- ------------------------------------------------------------------------ +-- Unit Tests +-- ------------------------------------------------------------------------ + +unitTests :: TestTree +unitTests = + testGroup "Unit Tests" + [ cabalParserUnitTests, + codeActionUnitTests + ] + +cabalParserUnitTests :: TestTree +cabalParserUnitTests = testGroup "Parsing Cabal" + [ testCase "Simple Parsing works" $ do + (warnings, pm) <- Lib.parseCabalFileContents =<< BS.readFile (testDataDir "simple.cabal") + liftIO $ do + null warnings @? "Found unexpected warnings" + isRight pm @? "Failed to parse GenericPackageDescription" + ] + +codeActionUnitTests :: TestTree +codeActionUnitTests = testGroup "Code Action Tests" + [ testCase "Unknown format" $ do + -- the message has the wrong format + licenseErrorSuggestion "Unknown license identifier: 'BSD3' Do you mean BSD-3-Clause?" @?= Nothing, + + testCase "BSD-3-Clause" $ do + licenseErrorSuggestion "Unknown SPDX license identifier: 'BSD3' Do you mean BSD-3-Clause?" @?= Just ("BSD3", "BSD-3-Clause"), + + testCase "MIT" $ do + -- contains no suggestion + licenseErrorSuggestion "Unknown SPDX license identifier: 'MIT3'" @?= Nothing + ] + +-- ------------------------------------------------------------------------ +-- Integration Tests +-- ------------------------------------------------------------------------ + +pluginTests :: Recorder (WithPriority Log) -> TestTree +pluginTests recorder = testGroup "Plugin Tests" + [ testGroup "Diagnostics" + [ runCabalTestCaseSession "Publishes Diagnostics on Error" recorder "" $ do + doc <- openDoc "invalid.cabal" "cabal" + diags <- waitForDiagnosticsFromSource doc "cabal" + unknownLicenseDiag <- liftIO $ inspectDiagnostic diags ["Unknown SPDX license identifier: 'BSD3'"] + liftIO $ do + length diags @?= 1 + unknownLicenseDiag ^. J.range @?= Range (Position 3 24) (Position 4 0) + unknownLicenseDiag ^. J.severity @?= Just DsError + , runCabalTestCaseSession "Clears diagnostics" recorder "" $ do + doc <- openDoc "invalid.cabal" "cabal" + diags <- waitForDiagnosticsFrom doc + unknownLicenseDiag <- liftIO $ inspectDiagnostic diags ["Unknown SPDX license identifier: 'BSD3'"] + liftIO $ do + length diags @?= 1 + unknownLicenseDiag ^. J.range @?= Range (Position 3 24) (Position 4 0) + unknownLicenseDiag ^. J.severity @?= Just DsError + _ <- applyEdit doc $ TextEdit (Range (Position 3 20) (Position 4 0)) "BSD-3-Clause\n" + newDiags <- waitForDiagnosticsFrom doc + liftIO $ newDiags @?= [] + , runCabalTestCaseSession "No Diagnostics in .hs files from valid .cabal file" recorder "simple-cabal" $ do + hsDoc <- openDoc "A.hs" "haskell" + expectNoMoreDiagnostics 1 hsDoc "typechecking" + cabalDoc <- openDoc "simple-cabal.cabal" "cabal" + expectNoMoreDiagnostics 1 cabalDoc "parsing" + , ignoreTestBecause "Testcase is flaky for certain GHC versions (e.g. 9.2.4). See #3333 for details." $ do + runCabalTestCaseSession "Diagnostics in .hs files from invalid .cabal file" recorder "simple-cabal" $ do + hsDoc <- openDoc "A.hs" "haskell" + expectNoMoreDiagnostics 1 hsDoc "typechecking" + cabalDoc <- openDoc "simple-cabal.cabal" "cabal" + expectNoMoreDiagnostics 1 cabalDoc "parsing" + let theRange = Range (Position 3 20) (Position 3 23) + -- Invalid license + changeDoc cabalDoc [TextDocumentContentChangeEvent (Just theRange) Nothing "MIT3"] + cabalDiags <- waitForDiagnosticsFrom cabalDoc + unknownLicenseDiag <- liftIO $ inspectDiagnostic cabalDiags ["Unknown SPDX license identifier: 'MIT3'"] + expectNoMoreDiagnostics 1 hsDoc "typechecking" + liftIO $ do + length cabalDiags @?= 1 + unknownLicenseDiag ^. J.range @?= Range (Position 3 24) (Position 4 0) + unknownLicenseDiag ^. J.severity @?= Just DsError + ] + , testGroup "Code Actions" + [ runCabalTestCaseSession "BSD-3" recorder "" $ do + doc <- openDoc "licenseCodeAction.cabal" "cabal" + diags <- waitForDiagnosticsFromSource doc "cabal" + reduceDiag <- liftIO $ inspectDiagnostic diags ["Unknown SPDX license identifier: 'BSD3'"] + liftIO $ do + length diags @?= 1 + reduceDiag ^. J.range @?= Range (Position 3 24) (Position 4 0) + reduceDiag ^. J.severity @?= Just DsError + [InR codeAction] <- getCodeActions doc (Range (Position 3 24) (Position 4 0)) + executeCodeAction codeAction + contents <- documentContents doc + liftIO $ contents @?= Text.unlines + [ "cabal-version: 3.0" + , "name: licenseCodeAction" + , "version: 0.1.0.0" + , "license: BSD-3-Clause" + , "" + , "library" + , " build-depends: base" + , " default-language: Haskell2010" + ] + ] + ] + +-- ------------------------------------------------------------------------ +-- Runner utils +-- ------------------------------------------------------------------------ + +runCabalTestCaseSession :: TestName -> Recorder (WithPriority Log) -> FilePath -> Session () -> TestTree +runCabalTestCaseSession title recorder subdir act = testCase title $ runCabalSession recorder subdir act + +runCabalSession :: Recorder (WithPriority Log) -> FilePath -> Session a -> IO a +runCabalSession recorder subdir = + failIfSessionTimeout . runSessionWithServer (cabalPlugin recorder) (testDataDir subdir) + +testDataDir :: FilePath +testDataDir = "test" "testdata" diff --git a/plugins/hls-cabal-plugin/test/testdata/invalid.cabal b/plugins/hls-cabal-plugin/test/testdata/invalid.cabal new file mode 100644 index 0000000000..26f9b8f2d6 --- /dev/null +++ b/plugins/hls-cabal-plugin/test/testdata/invalid.cabal @@ -0,0 +1,8 @@ +cabal-version: 3.0 +name: invalid +version: 0.1.0.0 +license: BSD3 + +library + build-depends: base + default-language: Haskell2010 diff --git a/plugins/hls-cabal-plugin/test/testdata/licenseCodeAction.cabal b/plugins/hls-cabal-plugin/test/testdata/licenseCodeAction.cabal new file mode 100644 index 0000000000..d1bbf8b5c2 --- /dev/null +++ b/plugins/hls-cabal-plugin/test/testdata/licenseCodeAction.cabal @@ -0,0 +1,8 @@ +cabal-version: 3.0 +name: licenseCodeAction +version: 0.1.0.0 +license: BSD3 + +library + build-depends: base + default-language: Haskell2010 diff --git a/plugins/hls-cabal-plugin/test/testdata/simple-cabal/A.hs b/plugins/hls-cabal-plugin/test/testdata/simple-cabal/A.hs new file mode 100644 index 0000000000..c72a91d81a --- /dev/null +++ b/plugins/hls-cabal-plugin/test/testdata/simple-cabal/A.hs @@ -0,0 +1,4 @@ +module A where + +-- definitions don't matter here. +foo = 1 diff --git a/plugins/hls-cabal-plugin/test/testdata/simple-cabal/cabal.project b/plugins/hls-cabal-plugin/test/testdata/simple-cabal/cabal.project new file mode 100644 index 0000000000..e6fdbadb43 --- /dev/null +++ b/plugins/hls-cabal-plugin/test/testdata/simple-cabal/cabal.project @@ -0,0 +1 @@ +packages: . diff --git a/plugins/hls-cabal-plugin/test/testdata/simple-cabal/hie.yaml b/plugins/hls-cabal-plugin/test/testdata/simple-cabal/hie.yaml new file mode 100644 index 0000000000..04cd24395e --- /dev/null +++ b/plugins/hls-cabal-plugin/test/testdata/simple-cabal/hie.yaml @@ -0,0 +1,2 @@ +cradle: + cabal: diff --git a/plugins/hls-cabal-plugin/test/testdata/simple-cabal/simple-cabal.cabal b/plugins/hls-cabal-plugin/test/testdata/simple-cabal/simple-cabal.cabal new file mode 100644 index 0000000000..48ac100d3d --- /dev/null +++ b/plugins/hls-cabal-plugin/test/testdata/simple-cabal/simple-cabal.cabal @@ -0,0 +1,10 @@ +cabal-version: 3.0 +name: simple-cabal +version: 0.1.0.0 +license: MIT + +library + build-depends: base + hs-source-dirs: . + exposed-modules: A + default-language: Haskell2010 diff --git a/plugins/hls-cabal-plugin/test/testdata/simple.cabal b/plugins/hls-cabal-plugin/test/testdata/simple.cabal new file mode 100644 index 0000000000..1adb3b2795 --- /dev/null +++ b/plugins/hls-cabal-plugin/test/testdata/simple.cabal @@ -0,0 +1,24 @@ +cabal-version: 3.0 +name: hls-cabal-plugin +version: 0.1.0.0 +synopsis: +homepage: +license: MIT +license-file: LICENSE +author: Fendor +maintainer: fendor@posteo.de +category: Development +extra-source-files: CHANGELOG.md + +library + exposed-modules: IDE.Plugin.Cabal + build-depends: base ^>=4.14.3.0 + hs-source-dirs: src + default-language: Haskell2010 + +test-suite hls-cabal-plugin-test + default-language: Haskell2010 + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Main.hs + build-depends: base ^>=4.14.3.0 diff --git a/src/HlsPlugins.hs b/src/HlsPlugins.hs index b471fa65cb..6fe2e4ef24 100644 --- a/src/HlsPlugins.hs +++ b/src/HlsPlugins.hs @@ -21,7 +21,9 @@ import qualified Ide.Plugin.QualifyImportedNames as QualifyImportedNames #if hls_callHierarchy import qualified Ide.Plugin.CallHierarchy as CallHierarchy #endif - +#if hls_cabal +import qualified Ide.Plugin.Cabal as Cabal +#endif #if hls_class import qualified Ide.Plugin.Class as Class #endif @@ -146,6 +148,9 @@ idePlugins recorder = pluginDescToIdePlugins allPlugins pluginRecorder :: forall log. (Pretty log) => PluginId -> Recorder (WithPriority log) pluginRecorder pluginId = cmapWithPrio (Log pluginId) recorder allPlugins = +#if hls_cabal + let pId = "cabal" in Cabal.descriptor (pluginRecorder pId) pId : +#endif #if hls_pragmas Pragmas.descriptor "pragmas" : #endif diff --git a/stack-lts19.yaml b/stack-lts19.yaml index 4e33bd28f8..74d90c3361 100644 --- a/stack-lts19.yaml +++ b/stack-lts19.yaml @@ -9,6 +9,7 @@ packages: - ./hls-plugin-api - ./hls-test-utils # - ./shake-bench + - ./plugins/hls-cabal-plugin - ./plugins/hls-cabal-fmt-plugin - ./plugins/hls-call-hierarchy-plugin - ./plugins/hls-class-plugin diff --git a/stack.yaml b/stack.yaml index ca2f39b5cf..b92448278e 100644 --- a/stack.yaml +++ b/stack.yaml @@ -9,6 +9,7 @@ packages: - ./hls-plugin-api - ./hls-test-utils - ./shake-bench +- ./plugins/hls-cabal-plugin - ./plugins/hls-cabal-fmt-plugin - ./plugins/hls-call-hierarchy-plugin - ./plugins/hls-class-plugin From 1e88d162829a2f34dc33a591227512959bb5130b Mon Sep 17 00:00:00 2001 From: santiweight Date: Tue, 22 Nov 2022 03:04:51 -0800 Subject: [PATCH 198/213] wingman: move wingman to new directory (#3352) Co-authored-by: Santiago Weight Co-authored-by: Michael Peyton Jones --- plugins/hls-tactics-plugin/hls-tactics-plugin.cabal | 10 +++++----- .../{ => old}/src/Ide/Plugin/Tactic.hs | 0 .../{ => old}/src/Refinery/Future.hs | 0 .../{ => old}/src/Wingman/AbstractLSP.hs | 0 .../{ => old}/src/Wingman/AbstractLSP/TacticActions.hs | 0 .../{ => old}/src/Wingman/AbstractLSP/Types.hs | 0 .../hls-tactics-plugin/{ => old}/src/Wingman/Auto.hs | 0 .../{ => old}/src/Wingman/CaseSplit.hs | 0 .../{ => old}/src/Wingman/CodeGen.hs | 0 .../{ => old}/src/Wingman/CodeGen/Utils.hs | 0 .../{ => old}/src/Wingman/Context.hs | 0 .../hls-tactics-plugin/{ => old}/src/Wingman/Debug.hs | 0 .../{ => old}/src/Wingman/EmptyCase.hs | 0 .../hls-tactics-plugin/{ => old}/src/Wingman/GHC.hs | 0 .../{ => old}/src/Wingman/Judgements.hs | 0 .../{ => old}/src/Wingman/Judgements/SYB.hs | 0 .../{ => old}/src/Wingman/Judgements/Theta.hs | 0 .../{ => old}/src/Wingman/KnownStrategies.hs | 0 .../src/Wingman/KnownStrategies/QuickCheck.hs | 0 .../{ => old}/src/Wingman/LanguageServer.hs | 0 .../src/Wingman/LanguageServer/Metaprogram.hs | 0 .../src/Wingman/LanguageServer/TacticProviders.hs | 0 .../{ => old}/src/Wingman/Machinery.hs | 0 .../{ => old}/src/Wingman/Metaprogramming/Lexer.hs | 0 .../{ => old}/src/Wingman/Metaprogramming/Parser.hs | 0 .../src/Wingman/Metaprogramming/Parser.hs-boot | 0 .../Wingman/Metaprogramming/Parser/Documentation.hs | 0 .../src/Wingman/Metaprogramming/ProofState.hs | 0 .../hls-tactics-plugin/{ => old}/src/Wingman/Naming.hs | 0 .../hls-tactics-plugin/{ => old}/src/Wingman/Plugin.hs | 0 .../hls-tactics-plugin/{ => old}/src/Wingman/Range.hs | 0 .../{ => old}/src/Wingman/Simplify.hs | 0 .../{ => old}/src/Wingman/StaticPlugin.hs | 0 .../{ => old}/src/Wingman/Tactics.hs | 0 .../hls-tactics-plugin/{ => old}/src/Wingman/Types.hs | 0 .../hls-tactics-plugin/{ => old}/test/AutoTupleSpec.hs | 0 .../{ => old}/test/CodeAction/AutoSpec.hs | 0 .../{ => old}/test/CodeAction/DestructAllSpec.hs | 0 .../{ => old}/test/CodeAction/DestructPunSpec.hs | 0 .../{ => old}/test/CodeAction/DestructSpec.hs | 0 .../{ => old}/test/CodeAction/IntroDestructSpec.hs | 0 .../{ => old}/test/CodeAction/IntrosSpec.hs | 0 .../{ => old}/test/CodeAction/RefineSpec.hs | 0 .../{ => old}/test/CodeAction/RunMetaprogramSpec.hs | 0 .../{ => old}/test/CodeAction/UseDataConSpec.hs | 0 .../{ => old}/test/CodeLens/EmptyCaseSpec.hs | 0 plugins/hls-tactics-plugin/{ => old}/test/Main.hs | 0 .../hls-tactics-plugin/{ => old}/test/ProviderSpec.hs | 0 plugins/hls-tactics-plugin/{ => old}/test/Spec.hs | 0 .../{ => old}/test/UnificationSpec.hs | 0 plugins/hls-tactics-plugin/{ => old}/test/Utils.hs | 2 +- .../{ => old}/test/golden/AutoEmptyString.expected.hs | 0 .../{ => old}/test/golden/AutoEmptyString.hs | 0 .../{ => old}/test/golden/AutoEndo.expected.hs | 0 .../{ => old}/test/golden/AutoEndo.hs | 0 .../test/golden/AutoForallClassMethod.expected.hs | 0 .../{ => old}/test/golden/AutoForallClassMethod.hs | 0 .../{ => old}/test/golden/AutoInfixApply.expected.hs | 0 .../{ => old}/test/golden/AutoInfixApply.hs | 0 .../test/golden/AutoInfixApplyMany.expected.hs | 0 .../{ => old}/test/golden/AutoInfixApplyMany.hs | 0 .../{ => old}/test/golden/AutoInfixInfix.expected.hs | 0 .../{ => old}/test/golden/AutoInfixInfix.hs | 0 .../{ => old}/test/golden/AutoPatSynUse.expected.hs | 0 .../{ => old}/test/golden/AutoPatSynUse.hs | 0 .../{ => old}/test/golden/AutoSplitGADT.expected.hs | 0 .../{ => old}/test/golden/AutoSplitGADT.hs | 0 .../{ => old}/test/golden/AutoThetaEqCtx.expected.hs | 0 .../{ => old}/test/golden/AutoThetaEqCtx.hs | 0 .../{ => old}/test/golden/AutoThetaEqGADT.expected.hs | 0 .../{ => old}/test/golden/AutoThetaEqGADT.hs | 0 .../test/golden/AutoThetaEqGADTDestruct.expected.hs | 0 .../{ => old}/test/golden/AutoThetaEqGADTDestruct.hs | 0 .../{ => old}/test/golden/AutoThetaFix.expected.hs | 0 .../{ => old}/test/golden/AutoThetaFix.hs | 0 .../{ => old}/test/golden/AutoThetaGADT.expected.hs | 0 .../{ => old}/test/golden/AutoThetaGADT.hs | 0 .../test/golden/AutoThetaGADTDestruct.expected.hs | 0 .../{ => old}/test/golden/AutoThetaGADTDestruct.hs | 0 .../golden/AutoThetaMultipleUnification.expected.hs | 0 .../test/golden/AutoThetaMultipleUnification.hs | 0 .../{ => old}/test/golden/AutoThetaRankN.expected.hs | 0 .../{ => old}/test/golden/AutoThetaRankN.hs | 0 .../{ => old}/test/golden/AutoThetaRefl.expected.hs | 0 .../{ => old}/test/golden/AutoThetaRefl.hs | 0 .../test/golden/AutoThetaReflDestruct.expected.hs | 0 .../{ => old}/test/golden/AutoThetaReflDestruct.hs | 0 .../test/golden/AutoThetaSplitUnification.expected.hs | 0 .../{ => old}/test/golden/AutoThetaSplitUnification.hs | 0 .../{ => old}/test/golden/AutoTypeLevel.expected.hs | 0 .../{ => old}/test/golden/AutoTypeLevel.hs | 0 .../test/golden/AutoUnusedPatternMatch.expected.hs | 0 .../{ => old}/test/golden/AutoUnusedPatternMatch.hs | 0 .../{ => old}/test/golden/AutoZip.expected.hs | 0 .../{ => old}/test/golden/AutoZip.hs | 0 .../{ => old}/test/golden/ConProviders.hs | 0 .../{ => old}/test/golden/DestructAllAnd.expected.hs | 0 .../{ => old}/test/golden/DestructAllAnd.hs | 0 .../{ => old}/test/golden/DestructAllFunc.expected.hs | 0 .../{ => old}/test/golden/DestructAllFunc.hs | 0 .../test/golden/DestructAllGADTEvidence.expected.hs | 0 .../{ => old}/test/golden/DestructAllGADTEvidence.hs | 0 .../{ => old}/test/golden/DestructAllMany.expected.hs | 0 .../{ => old}/test/golden/DestructAllMany.hs | 0 .../test/golden/DestructAllNonVarTopMatch.expected.hs | 0 .../{ => old}/test/golden/DestructAllNonVarTopMatch.hs | 0 .../{ => old}/test/golden/DestructAllProvider.hs | 0 .../{ => old}/test/golden/DestructCthulhu.expected.hs | 0 .../{ => old}/test/golden/DestructCthulhu.hs | 0 .../{ => old}/test/golden/DestructDataFam.expected.hs | 0 .../{ => old}/test/golden/DestructDataFam.hs | 0 .../{ => old}/test/golden/DestructInt.expected.hs | 0 .../{ => old}/test/golden/DestructInt.hs | 0 .../{ => old}/test/golden/DestructPun.expected.hs | 0 .../{ => old}/test/golden/DestructPun.hs | 0 .../{ => old}/test/golden/DestructTyFam.expected.hs | 0 .../{ => old}/test/golden/DestructTyFam.hs | 0 .../test/golden/DestructTyToDataFam.expected.hs | 0 .../{ => old}/test/golden/DestructTyToDataFam.hs | 0 .../{ => old}/test/golden/EmptyCaseADT.expected.hs | 0 .../{ => old}/test/golden/EmptyCaseADT.hs | 0 .../{ => old}/test/golden/EmptyCaseApply.expected.hs | 0 .../{ => old}/test/golden/EmptyCaseApply.hs | 0 .../{ => old}/test/golden/EmptyCaseGADT.expected.hs | 0 .../{ => old}/test/golden/EmptyCaseGADT.hs | 0 .../{ => old}/test/golden/EmptyCaseLamCase.expected.hs | 0 .../{ => old}/test/golden/EmptyCaseLamCase.hs | 0 .../{ => old}/test/golden/EmptyCaseNested.expected.hs | 0 .../{ => old}/test/golden/EmptyCaseNested.hs | 0 .../{ => old}/test/golden/EmptyCaseParens.expected.hs | 0 .../{ => old}/test/golden/EmptyCaseParens.hs | 0 .../{ => old}/test/golden/EmptyCaseShadow.expected.hs | 0 .../{ => old}/test/golden/EmptyCaseShadow.hs | 0 .../{ => old}/test/golden/EmptyCaseSpuriousGADT.hs | 0 .../{ => old}/test/golden/Fgmap.expected.hs | 0 .../hls-tactics-plugin/{ => old}/test/golden/Fgmap.hs | 0 .../{ => old}/test/golden/FmapBoth.expected.hs | 0 .../{ => old}/test/golden/FmapBoth.hs | 0 .../{ => old}/test/golden/FmapJoin.expected.hs | 0 .../{ => old}/test/golden/FmapJoin.hs | 0 .../{ => old}/test/golden/FmapJoinInLet.expected.hs | 0 .../{ => old}/test/golden/FmapJoinInLet.hs | 0 .../{ => old}/test/golden/GoldenApplicativeThen.hs | 0 .../{ => old}/test/golden/GoldenArbitrary.expected.hs | 0 .../{ => old}/test/golden/GoldenArbitrary.hs | 0 .../GoldenArbitrarySingleConstructor.expected.hs | 0 .../test/golden/GoldenArbitrarySingleConstructor.hs | 0 .../{ => old}/test/golden/GoldenBigTuple.expected.hs | 0 .../{ => old}/test/golden/GoldenBigTuple.hs | 0 .../{ => old}/test/golden/GoldenEitherAuto.expected.hs | 0 .../{ => old}/test/golden/GoldenEitherAuto.hs | 0 .../test/golden/GoldenEitherHomomorphic.expected.hs | 0 .../{ => old}/test/golden/GoldenEitherHomomorphic.hs | 0 .../{ => old}/test/golden/GoldenFish.hs | 0 .../{ => old}/test/golden/GoldenFmapTree.expected.hs | 0 .../{ => old}/test/golden/GoldenFmapTree.hs | 0 .../{ => old}/test/golden/GoldenFoldr.expected.hs | 0 .../{ => old}/test/golden/GoldenFoldr.hs | 0 .../{ => old}/test/golden/GoldenFromMaybe.expected.hs | 0 .../{ => old}/test/golden/GoldenFromMaybe.hs | 0 .../{ => old}/test/golden/GoldenGADTAuto.expected.hs | 0 .../{ => old}/test/golden/GoldenGADTAuto.hs | 0 .../test/golden/GoldenGADTDestruct.expected.hs | 0 .../{ => old}/test/golden/GoldenGADTDestruct.hs | 0 .../test/golden/GoldenGADTDestructCoercion.expected.hs | 0 .../test/golden/GoldenGADTDestructCoercion.hs | 0 .../{ => old}/test/golden/GoldenIdTypeFam.expected.hs | 0 .../{ => old}/test/golden/GoldenIdTypeFam.hs | 0 .../test/golden/GoldenIdentityFunctor.expected.hs | 0 .../{ => old}/test/golden/GoldenIdentityFunctor.hs | 0 .../{ => old}/test/golden/GoldenIntros.expected.hs | 0 .../{ => old}/test/golden/GoldenIntros.hs | 0 .../{ => old}/test/golden/GoldenJoinCont.expected.hs | 0 .../{ => old}/test/golden/GoldenJoinCont.hs | 0 .../{ => old}/test/golden/GoldenListFmap.expected.hs | 0 .../{ => old}/test/golden/GoldenListFmap.hs | 0 .../{ => old}/test/golden/GoldenNote.expected.hs | 0 .../{ => old}/test/golden/GoldenNote.hs | 0 .../{ => old}/test/golden/GoldenPureList.expected.hs | 0 .../{ => old}/test/golden/GoldenPureList.hs | 0 .../{ => old}/test/golden/GoldenSafeHead.expected.hs | 0 .../{ => old}/test/golden/GoldenSafeHead.hs | 0 .../{ => old}/test/golden/GoldenShow.expected.hs | 0 .../{ => old}/test/golden/GoldenShow.hs | 0 .../test/golden/GoldenShowCompose.expected.hs | 0 .../{ => old}/test/golden/GoldenShowCompose.hs | 0 .../test/golden/GoldenShowMapChar.expected.hs | 0 .../{ => old}/test/golden/GoldenShowMapChar.hs | 0 .../{ => old}/test/golden/GoldenSuperclass.expected.hs | 0 .../{ => old}/test/golden/GoldenSuperclass.hs | 0 .../{ => old}/test/golden/GoldenSwap.expected.hs | 0 .../{ => old}/test/golden/GoldenSwap.hs | 0 .../{ => old}/test/golden/GoldenSwapMany.expected.hs | 0 .../{ => old}/test/golden/GoldenSwapMany.hs | 0 .../test/golden/IntroDestructLetBinding.expected.hs | 0 .../{ => old}/test/golden/IntroDestructLetBinding.hs | 0 .../test/golden/IntroDestructMany.expected.hs | 0 .../{ => old}/test/golden/IntroDestructMany.hs | 0 .../{ => old}/test/golden/IntroDestructOne.expected.hs | 0 .../{ => old}/test/golden/IntroDestructOne.hs | 0 .../{ => old}/test/golden/IntroDestructProvider.hs | 0 .../{ => old}/test/golden/IntrosTooMany.expected.hs | 0 .../{ => old}/test/golden/IntrosTooMany.hs | 0 .../test/golden/KnownBigSemigroup.expected.hs | 0 .../{ => old}/test/golden/KnownBigSemigroup.hs | 0 .../golden/KnownCounterfactualSemigroup.expected.hs | 0 .../test/golden/KnownCounterfactualSemigroup.hs | 0 .../test/golden/KnownDestructedSemigroup.expected.hs | 0 .../{ => old}/test/golden/KnownDestructedSemigroup.hs | 0 .../test/golden/KnownMissingMonoid.expected.hs | 0 .../{ => old}/test/golden/KnownMissingMonoid.hs | 0 .../test/golden/KnownMissingSemigroup.expected.hs | 0 .../{ => old}/test/golden/KnownMissingSemigroup.hs | 0 .../golden/KnownModuleInstanceSemigroup.expected.hs | 0 .../test/golden/KnownModuleInstanceSemigroup.hs | 0 .../{ => old}/test/golden/KnownMonoid.expected.hs | 0 .../{ => old}/test/golden/KnownMonoid.hs | 0 .../{ => old}/test/golden/KnownPolyMonoid.expected.hs | 0 .../{ => old}/test/golden/KnownPolyMonoid.hs | 0 .../test/golden/KnownThetaSemigroup.expected.hs | 0 .../{ => old}/test/golden/KnownThetaSemigroup.hs | 0 .../{ => old}/test/golden/LayoutBind.expected.hs | 0 .../{ => old}/test/golden/LayoutBind.hs | 0 .../{ => old}/test/golden/LayoutDollarApp.expected.hs | 0 .../{ => old}/test/golden/LayoutDollarApp.hs | 0 .../{ => old}/test/golden/LayoutInfixKeep.expected.hs | 0 .../{ => old}/test/golden/LayoutInfixKeep.hs | 0 .../{ => old}/test/golden/LayoutLam.expected.hs | 0 .../{ => old}/test/golden/LayoutLam.hs | 0 .../{ => old}/test/golden/LayoutOpApp.expected.hs | 0 .../{ => old}/test/golden/LayoutOpApp.hs | 0 .../{ => old}/test/golden/LayoutPrefixKeep.expected.hs | 0 .../{ => old}/test/golden/LayoutPrefixKeep.hs | 0 .../{ => old}/test/golden/LayoutRec.expected.hs | 0 .../{ => old}/test/golden/LayoutRec.hs | 0 .../{ => old}/test/golden/LayoutSplitClass.expected.hs | 0 .../{ => old}/test/golden/LayoutSplitClass.hs | 0 .../{ => old}/test/golden/LayoutSplitGuard.expected.hs | 0 .../{ => old}/test/golden/LayoutSplitGuard.hs | 0 .../{ => old}/test/golden/LayoutSplitIn.expected.hs | 0 .../{ => old}/test/golden/LayoutSplitIn.hs | 0 .../{ => old}/test/golden/LayoutSplitLet.expected.hs | 0 .../{ => old}/test/golden/LayoutSplitLet.hs | 0 .../test/golden/LayoutSplitPatSyn.expected.hs | 0 .../{ => old}/test/golden/LayoutSplitPatSyn.hs | 0 .../test/golden/LayoutSplitPattern.expected.hs | 0 .../{ => old}/test/golden/LayoutSplitPattern.hs | 0 .../test/golden/LayoutSplitViewPat.expected.hs | 0 .../{ => old}/test/golden/LayoutSplitViewPat.hs | 0 .../{ => old}/test/golden/LayoutSplitWhere.expected.hs | 0 .../{ => old}/test/golden/LayoutSplitWhere.hs | 0 .../{ => old}/test/golden/MessageCantUnify.hs | 0 .../{ => old}/test/golden/MessageForallA.hs | 0 .../{ => old}/test/golden/MessageNotEnoughGas.hs | 0 .../{ => old}/test/golden/MetaBegin.expected.hs | 0 .../{ => old}/test/golden/MetaBegin.hs | 0 .../test/golden/MetaBeginNoWildify.expected.hs | 0 .../{ => old}/test/golden/MetaBeginNoWildify.hs | 0 .../{ => old}/test/golden/MetaBindAll.expected.hs | 0 .../{ => old}/test/golden/MetaBindAll.hs | 0 .../{ => old}/test/golden/MetaBindOne.expected.hs | 0 .../{ => old}/test/golden/MetaBindOne.hs | 0 .../{ => old}/test/golden/MetaCataAST.expected.hs | 0 .../{ => old}/test/golden/MetaCataAST.hs | 0 .../{ => old}/test/golden/MetaCataCollapse.expected.hs | 0 .../{ => old}/test/golden/MetaCataCollapse.hs | 0 .../test/golden/MetaCataCollapseUnary.expected.hs | 0 .../{ => old}/test/golden/MetaCataCollapseUnary.hs | 0 .../{ => old}/test/golden/MetaChoice.expected.hs | 0 .../{ => old}/test/golden/MetaChoice.hs | 0 .../{ => old}/test/golden/MetaDeepOf.expected.hs | 0 .../{ => old}/test/golden/MetaDeepOf.hs | 0 .../{ => old}/test/golden/MetaFundeps.expected.hs | 0 .../{ => old}/test/golden/MetaFundeps.hs | 0 .../{ => old}/test/golden/MetaIdiom.expected.hs | 0 .../{ => old}/test/golden/MetaIdiom.hs | 0 .../{ => old}/test/golden/MetaIdiomRecord.expected.hs | 0 .../{ => old}/test/golden/MetaIdiomRecord.hs | 0 .../{ => old}/test/golden/MetaLetSimple.expected.hs | 0 .../{ => old}/test/golden/MetaLetSimple.hs | 0 .../{ => old}/test/golden/MetaMaybeAp.expected.hs | 0 .../{ => old}/test/golden/MetaMaybeAp.hs | 0 .../{ => old}/test/golden/MetaPointwise.expected.hs | 0 .../{ => old}/test/golden/MetaPointwise.hs | 0 .../{ => old}/test/golden/MetaTry.expected.hs | 0 .../{ => old}/test/golden/MetaTry.hs | 0 .../{ => old}/test/golden/MetaUseImport.expected.hs | 0 .../{ => old}/test/golden/MetaUseImport.hs | 0 .../{ => old}/test/golden/MetaUseLocal.expected.hs | 0 .../{ => old}/test/golden/MetaUseLocal.hs | 0 .../{ => old}/test/golden/MetaUseMethod.expected.hs | 0 .../{ => old}/test/golden/MetaUseMethod.hs | 0 .../{ => old}/test/golden/MetaUseSymbol.expected.hs | 0 .../{ => old}/test/golden/MetaUseSymbol.hs | 0 .../{ => old}/test/golden/MetaWithArg.expected.hs | 0 .../{ => old}/test/golden/MetaWithArg.hs | 0 .../{ => old}/test/golden/NewtypeRecord.expected.hs | 0 .../{ => old}/test/golden/NewtypeRecord.hs | 0 .../{ => old}/test/golden/ProvideAlreadyDestructed.hs | 0 .../{ => old}/test/golden/ProvideLocalHyOnly.hs | 0 .../{ => old}/test/golden/ProviderHomomorphism.hs | 0 .../{ => old}/test/golden/PunGADT.expected.hs | 0 .../{ => old}/test/golden/PunGADT.hs | 0 .../{ => old}/test/golden/PunMany.expected.hs | 0 .../{ => old}/test/golden/PunMany.hs | 0 .../{ => old}/test/golden/PunManyGADT.expected.hs | 0 .../{ => old}/test/golden/PunManyGADT.hs | 0 .../{ => old}/test/golden/PunShadowing.expected.hs | 0 .../{ => old}/test/golden/PunShadowing.hs | 0 .../{ => old}/test/golden/PunSimple.expected.hs | 0 .../{ => old}/test/golden/PunSimple.hs | 0 .../{ => old}/test/golden/RecordCon.expected.hs | 0 .../{ => old}/test/golden/RecordCon.hs | 0 .../{ => old}/test/golden/RefineCon.expected.hs | 0 .../{ => old}/test/golden/RefineCon.hs | 0 .../{ => old}/test/golden/RefineGADT.expected.hs | 0 .../{ => old}/test/golden/RefineGADT.hs | 0 .../{ => old}/test/golden/RefineIntro.expected.hs | 0 .../{ => old}/test/golden/RefineIntro.hs | 0 .../{ => old}/test/golden/RefineIntroWhere.expected.hs | 0 .../{ => old}/test/golden/RefineIntroWhere.hs | 0 .../{ => old}/test/golden/RefineReader.expected.hs | 0 .../{ => old}/test/golden/RefineReader.hs | 0 .../{ => old}/test/golden/SplitPattern.expected.hs | 0 .../{ => old}/test/golden/SplitPattern.hs | 0 .../test/golden/SubsequentTactics.expected.hs | 0 .../{ => old}/test/golden/SubsequentTactics.hs | 0 plugins/hls-tactics-plugin/{ => old}/test/golden/T1.hs | 0 plugins/hls-tactics-plugin/{ => old}/test/golden/T2.hs | 0 plugins/hls-tactics-plugin/{ => old}/test/golden/T3.hs | 0 .../{ => old}/test/golden/UseConLeft.expected.hs | 0 .../{ => old}/test/golden/UseConLeft.hs | 0 .../{ => old}/test/golden/UseConPair.expected.hs | 0 .../{ => old}/test/golden/UseConPair.hs | 0 .../{ => old}/test/golden/UseConRight.expected.hs | 0 .../{ => old}/test/golden/UseConRight.hs | 0 .../hls-tactics-plugin/{ => old}/test/golden/hie.yaml | 0 .../{ => old}/test/golden/test.cabal | 0 338 files changed, 6 insertions(+), 6 deletions(-) rename plugins/hls-tactics-plugin/{ => old}/src/Ide/Plugin/Tactic.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Refinery/Future.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/AbstractLSP.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/AbstractLSP/TacticActions.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/AbstractLSP/Types.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Auto.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/CaseSplit.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/CodeGen.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/CodeGen/Utils.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Context.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Debug.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/EmptyCase.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/GHC.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Judgements.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Judgements/SYB.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Judgements/Theta.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/KnownStrategies.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/KnownStrategies/QuickCheck.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/LanguageServer.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/LanguageServer/Metaprogram.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/LanguageServer/TacticProviders.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Machinery.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Metaprogramming/Lexer.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Metaprogramming/Parser.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Metaprogramming/Parser.hs-boot (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Metaprogramming/Parser/Documentation.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Metaprogramming/ProofState.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Naming.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Plugin.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Range.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Simplify.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/StaticPlugin.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Tactics.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/src/Wingman/Types.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/AutoTupleSpec.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/CodeAction/AutoSpec.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/CodeAction/DestructAllSpec.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/CodeAction/DestructPunSpec.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/CodeAction/DestructSpec.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/CodeAction/IntroDestructSpec.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/CodeAction/IntrosSpec.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/CodeAction/RefineSpec.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/CodeAction/RunMetaprogramSpec.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/CodeAction/UseDataConSpec.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/CodeLens/EmptyCaseSpec.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/Main.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/ProviderSpec.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/Spec.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/UnificationSpec.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/Utils.hs (99%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoEmptyString.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoEmptyString.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoEndo.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoEndo.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoForallClassMethod.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoForallClassMethod.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoInfixApply.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoInfixApply.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoInfixApplyMany.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoInfixApplyMany.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoInfixInfix.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoInfixInfix.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoPatSynUse.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoPatSynUse.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoSplitGADT.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoSplitGADT.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaEqCtx.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaEqCtx.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaEqGADT.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaEqGADT.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaEqGADTDestruct.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaEqGADTDestruct.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaFix.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaFix.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaGADT.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaGADT.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaGADTDestruct.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaGADTDestruct.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaMultipleUnification.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaMultipleUnification.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaRankN.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaRankN.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaRefl.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaRefl.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaReflDestruct.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaReflDestruct.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaSplitUnification.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoThetaSplitUnification.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoTypeLevel.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoTypeLevel.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoUnusedPatternMatch.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoUnusedPatternMatch.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoZip.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/AutoZip.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/ConProviders.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructAllAnd.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructAllAnd.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructAllFunc.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructAllFunc.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructAllGADTEvidence.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructAllGADTEvidence.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructAllMany.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructAllMany.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructAllNonVarTopMatch.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructAllNonVarTopMatch.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructAllProvider.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructCthulhu.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructCthulhu.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructDataFam.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructDataFam.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructInt.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructInt.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructPun.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructPun.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructTyFam.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructTyFam.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructTyToDataFam.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/DestructTyToDataFam.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/EmptyCaseADT.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/EmptyCaseADT.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/EmptyCaseApply.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/EmptyCaseApply.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/EmptyCaseGADT.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/EmptyCaseGADT.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/EmptyCaseLamCase.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/EmptyCaseLamCase.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/EmptyCaseNested.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/EmptyCaseNested.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/EmptyCaseParens.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/EmptyCaseParens.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/EmptyCaseShadow.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/EmptyCaseShadow.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/EmptyCaseSpuriousGADT.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/Fgmap.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/Fgmap.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/FmapBoth.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/FmapBoth.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/FmapJoin.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/FmapJoin.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/FmapJoinInLet.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/FmapJoinInLet.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenApplicativeThen.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenArbitrary.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenArbitrary.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenArbitrarySingleConstructor.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenArbitrarySingleConstructor.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenBigTuple.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenBigTuple.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenEitherAuto.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenEitherAuto.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenEitherHomomorphic.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenEitherHomomorphic.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenFish.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenFmapTree.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenFmapTree.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenFoldr.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenFoldr.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenFromMaybe.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenFromMaybe.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenGADTAuto.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenGADTAuto.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenGADTDestruct.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenGADTDestruct.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenGADTDestructCoercion.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenGADTDestructCoercion.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenIdTypeFam.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenIdTypeFam.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenIdentityFunctor.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenIdentityFunctor.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenIntros.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenIntros.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenJoinCont.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenJoinCont.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenListFmap.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenListFmap.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenNote.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenNote.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenPureList.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenPureList.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenSafeHead.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenSafeHead.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenShow.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenShow.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenShowCompose.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenShowCompose.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenShowMapChar.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenShowMapChar.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenSuperclass.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenSuperclass.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenSwap.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenSwap.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenSwapMany.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/GoldenSwapMany.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/IntroDestructLetBinding.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/IntroDestructLetBinding.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/IntroDestructMany.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/IntroDestructMany.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/IntroDestructOne.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/IntroDestructOne.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/IntroDestructProvider.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/IntrosTooMany.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/IntrosTooMany.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownBigSemigroup.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownBigSemigroup.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownCounterfactualSemigroup.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownCounterfactualSemigroup.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownDestructedSemigroup.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownDestructedSemigroup.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownMissingMonoid.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownMissingMonoid.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownMissingSemigroup.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownMissingSemigroup.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownModuleInstanceSemigroup.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownModuleInstanceSemigroup.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownMonoid.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownMonoid.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownPolyMonoid.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownPolyMonoid.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownThetaSemigroup.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/KnownThetaSemigroup.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutBind.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutBind.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutDollarApp.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutDollarApp.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutInfixKeep.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutInfixKeep.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutLam.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutLam.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutOpApp.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutOpApp.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutPrefixKeep.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutPrefixKeep.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutRec.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutRec.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutSplitClass.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutSplitClass.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutSplitGuard.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutSplitGuard.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutSplitIn.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutSplitIn.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutSplitLet.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutSplitLet.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutSplitPatSyn.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutSplitPatSyn.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutSplitPattern.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutSplitPattern.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutSplitViewPat.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutSplitViewPat.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutSplitWhere.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/LayoutSplitWhere.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MessageCantUnify.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MessageForallA.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MessageNotEnoughGas.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaBegin.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaBegin.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaBeginNoWildify.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaBeginNoWildify.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaBindAll.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaBindAll.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaBindOne.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaBindOne.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaCataAST.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaCataAST.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaCataCollapse.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaCataCollapse.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaCataCollapseUnary.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaCataCollapseUnary.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaChoice.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaChoice.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaDeepOf.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaDeepOf.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaFundeps.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaFundeps.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaIdiom.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaIdiom.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaIdiomRecord.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaIdiomRecord.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaLetSimple.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaLetSimple.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaMaybeAp.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaMaybeAp.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaPointwise.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaPointwise.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaTry.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaTry.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaUseImport.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaUseImport.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaUseLocal.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaUseLocal.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaUseMethod.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaUseMethod.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaUseSymbol.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaUseSymbol.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaWithArg.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/MetaWithArg.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/NewtypeRecord.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/NewtypeRecord.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/ProvideAlreadyDestructed.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/ProvideLocalHyOnly.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/ProviderHomomorphism.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/PunGADT.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/PunGADT.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/PunMany.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/PunMany.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/PunManyGADT.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/PunManyGADT.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/PunShadowing.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/PunShadowing.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/PunSimple.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/PunSimple.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/RecordCon.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/RecordCon.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/RefineCon.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/RefineCon.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/RefineGADT.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/RefineGADT.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/RefineIntro.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/RefineIntro.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/RefineIntroWhere.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/RefineIntroWhere.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/RefineReader.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/RefineReader.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/SplitPattern.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/SplitPattern.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/SubsequentTactics.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/SubsequentTactics.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/T1.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/T2.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/T3.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/UseConLeft.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/UseConLeft.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/UseConPair.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/UseConPair.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/UseConRight.expected.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/UseConRight.hs (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/hie.yaml (100%) rename plugins/hls-tactics-plugin/{ => old}/test/golden/test.cabal (100%) diff --git a/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal b/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal index d06fe4297c..225b132f47 100644 --- a/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal +++ b/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal @@ -15,9 +15,9 @@ license-file: LICENSE build-type: Simple extra-source-files: README.md - test/golden/*.cabal - test/golden/*.hs - test/golden/*.yaml + old/test/golden/*.cabal + old/test/golden/*.hs + old/test/golden/*.yaml source-repository head type: git @@ -33,7 +33,7 @@ library buildable: False else buildable: True - hs-source-dirs: src + hs-source-dirs: old/src exposed-modules: Ide.Plugin.Tactic Refinery.Future @@ -158,7 +158,7 @@ test-suite tests UnificationSpec Utils - hs-source-dirs: test + hs-source-dirs: old/test ghc-options: -Wall -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N diff --git a/plugins/hls-tactics-plugin/src/Ide/Plugin/Tactic.hs b/plugins/hls-tactics-plugin/old/src/Ide/Plugin/Tactic.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Ide/Plugin/Tactic.hs rename to plugins/hls-tactics-plugin/old/src/Ide/Plugin/Tactic.hs diff --git a/plugins/hls-tactics-plugin/src/Refinery/Future.hs b/plugins/hls-tactics-plugin/old/src/Refinery/Future.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Refinery/Future.hs rename to plugins/hls-tactics-plugin/old/src/Refinery/Future.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/AbstractLSP.hs b/plugins/hls-tactics-plugin/old/src/Wingman/AbstractLSP.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/AbstractLSP.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/AbstractLSP.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/AbstractLSP/TacticActions.hs b/plugins/hls-tactics-plugin/old/src/Wingman/AbstractLSP/TacticActions.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/AbstractLSP/TacticActions.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/AbstractLSP/TacticActions.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/AbstractLSP/Types.hs b/plugins/hls-tactics-plugin/old/src/Wingman/AbstractLSP/Types.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/AbstractLSP/Types.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/AbstractLSP/Types.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Auto.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Auto.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Auto.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Auto.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/CaseSplit.hs b/plugins/hls-tactics-plugin/old/src/Wingman/CaseSplit.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/CaseSplit.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/CaseSplit.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/CodeGen.hs b/plugins/hls-tactics-plugin/old/src/Wingman/CodeGen.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/CodeGen.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/CodeGen.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/CodeGen/Utils.hs b/plugins/hls-tactics-plugin/old/src/Wingman/CodeGen/Utils.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/CodeGen/Utils.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/CodeGen/Utils.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Context.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Context.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Context.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Context.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Debug.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Debug.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Debug.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Debug.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/EmptyCase.hs b/plugins/hls-tactics-plugin/old/src/Wingman/EmptyCase.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/EmptyCase.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/EmptyCase.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/GHC.hs b/plugins/hls-tactics-plugin/old/src/Wingman/GHC.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/GHC.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/GHC.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Judgements.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Judgements.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Judgements.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Judgements.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Judgements/SYB.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Judgements/SYB.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Judgements/SYB.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Judgements/SYB.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Judgements/Theta.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Judgements/Theta.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Judgements/Theta.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Judgements/Theta.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/KnownStrategies.hs b/plugins/hls-tactics-plugin/old/src/Wingman/KnownStrategies.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/KnownStrategies.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/KnownStrategies.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/KnownStrategies/QuickCheck.hs b/plugins/hls-tactics-plugin/old/src/Wingman/KnownStrategies/QuickCheck.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/KnownStrategies/QuickCheck.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/KnownStrategies/QuickCheck.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs b/plugins/hls-tactics-plugin/old/src/Wingman/LanguageServer.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/LanguageServer.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer/Metaprogram.hs b/plugins/hls-tactics-plugin/old/src/Wingman/LanguageServer/Metaprogram.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/LanguageServer/Metaprogram.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/LanguageServer/Metaprogram.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer/TacticProviders.hs b/plugins/hls-tactics-plugin/old/src/Wingman/LanguageServer/TacticProviders.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/LanguageServer/TacticProviders.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/LanguageServer/TacticProviders.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Machinery.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Machinery.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Machinery.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Machinery.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Metaprogramming/Lexer.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Metaprogramming/Lexer.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Metaprogramming/Lexer.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Metaprogramming/Lexer.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Metaprogramming/Parser.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Metaprogramming/Parser.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Metaprogramming/Parser.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Metaprogramming/Parser.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Metaprogramming/Parser.hs-boot b/plugins/hls-tactics-plugin/old/src/Wingman/Metaprogramming/Parser.hs-boot similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Metaprogramming/Parser.hs-boot rename to plugins/hls-tactics-plugin/old/src/Wingman/Metaprogramming/Parser.hs-boot diff --git a/plugins/hls-tactics-plugin/src/Wingman/Metaprogramming/Parser/Documentation.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Metaprogramming/Parser/Documentation.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Metaprogramming/Parser/Documentation.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Metaprogramming/Parser/Documentation.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Metaprogramming/ProofState.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Metaprogramming/ProofState.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Metaprogramming/ProofState.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Metaprogramming/ProofState.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Naming.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Naming.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Naming.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Naming.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Plugin.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Plugin.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Plugin.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Plugin.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Range.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Range.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Range.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Range.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Simplify.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Simplify.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Simplify.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Simplify.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/StaticPlugin.hs b/plugins/hls-tactics-plugin/old/src/Wingman/StaticPlugin.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/StaticPlugin.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/StaticPlugin.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Tactics.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Tactics.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Tactics.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Tactics.hs diff --git a/plugins/hls-tactics-plugin/src/Wingman/Types.hs b/plugins/hls-tactics-plugin/old/src/Wingman/Types.hs similarity index 100% rename from plugins/hls-tactics-plugin/src/Wingman/Types.hs rename to plugins/hls-tactics-plugin/old/src/Wingman/Types.hs diff --git a/plugins/hls-tactics-plugin/test/AutoTupleSpec.hs b/plugins/hls-tactics-plugin/old/test/AutoTupleSpec.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/AutoTupleSpec.hs rename to plugins/hls-tactics-plugin/old/test/AutoTupleSpec.hs diff --git a/plugins/hls-tactics-plugin/test/CodeAction/AutoSpec.hs b/plugins/hls-tactics-plugin/old/test/CodeAction/AutoSpec.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/CodeAction/AutoSpec.hs rename to plugins/hls-tactics-plugin/old/test/CodeAction/AutoSpec.hs diff --git a/plugins/hls-tactics-plugin/test/CodeAction/DestructAllSpec.hs b/plugins/hls-tactics-plugin/old/test/CodeAction/DestructAllSpec.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/CodeAction/DestructAllSpec.hs rename to plugins/hls-tactics-plugin/old/test/CodeAction/DestructAllSpec.hs diff --git a/plugins/hls-tactics-plugin/test/CodeAction/DestructPunSpec.hs b/plugins/hls-tactics-plugin/old/test/CodeAction/DestructPunSpec.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/CodeAction/DestructPunSpec.hs rename to plugins/hls-tactics-plugin/old/test/CodeAction/DestructPunSpec.hs diff --git a/plugins/hls-tactics-plugin/test/CodeAction/DestructSpec.hs b/plugins/hls-tactics-plugin/old/test/CodeAction/DestructSpec.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/CodeAction/DestructSpec.hs rename to plugins/hls-tactics-plugin/old/test/CodeAction/DestructSpec.hs diff --git a/plugins/hls-tactics-plugin/test/CodeAction/IntroDestructSpec.hs b/plugins/hls-tactics-plugin/old/test/CodeAction/IntroDestructSpec.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/CodeAction/IntroDestructSpec.hs rename to plugins/hls-tactics-plugin/old/test/CodeAction/IntroDestructSpec.hs diff --git a/plugins/hls-tactics-plugin/test/CodeAction/IntrosSpec.hs b/plugins/hls-tactics-plugin/old/test/CodeAction/IntrosSpec.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/CodeAction/IntrosSpec.hs rename to plugins/hls-tactics-plugin/old/test/CodeAction/IntrosSpec.hs diff --git a/plugins/hls-tactics-plugin/test/CodeAction/RefineSpec.hs b/plugins/hls-tactics-plugin/old/test/CodeAction/RefineSpec.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/CodeAction/RefineSpec.hs rename to plugins/hls-tactics-plugin/old/test/CodeAction/RefineSpec.hs diff --git a/plugins/hls-tactics-plugin/test/CodeAction/RunMetaprogramSpec.hs b/plugins/hls-tactics-plugin/old/test/CodeAction/RunMetaprogramSpec.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/CodeAction/RunMetaprogramSpec.hs rename to plugins/hls-tactics-plugin/old/test/CodeAction/RunMetaprogramSpec.hs diff --git a/plugins/hls-tactics-plugin/test/CodeAction/UseDataConSpec.hs b/plugins/hls-tactics-plugin/old/test/CodeAction/UseDataConSpec.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/CodeAction/UseDataConSpec.hs rename to plugins/hls-tactics-plugin/old/test/CodeAction/UseDataConSpec.hs diff --git a/plugins/hls-tactics-plugin/test/CodeLens/EmptyCaseSpec.hs b/plugins/hls-tactics-plugin/old/test/CodeLens/EmptyCaseSpec.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/CodeLens/EmptyCaseSpec.hs rename to plugins/hls-tactics-plugin/old/test/CodeLens/EmptyCaseSpec.hs diff --git a/plugins/hls-tactics-plugin/test/Main.hs b/plugins/hls-tactics-plugin/old/test/Main.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/Main.hs rename to plugins/hls-tactics-plugin/old/test/Main.hs diff --git a/plugins/hls-tactics-plugin/test/ProviderSpec.hs b/plugins/hls-tactics-plugin/old/test/ProviderSpec.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/ProviderSpec.hs rename to plugins/hls-tactics-plugin/old/test/ProviderSpec.hs diff --git a/plugins/hls-tactics-plugin/test/Spec.hs b/plugins/hls-tactics-plugin/old/test/Spec.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/Spec.hs rename to plugins/hls-tactics-plugin/old/test/Spec.hs diff --git a/plugins/hls-tactics-plugin/test/UnificationSpec.hs b/plugins/hls-tactics-plugin/old/test/UnificationSpec.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/UnificationSpec.hs rename to plugins/hls-tactics-plugin/old/test/UnificationSpec.hs diff --git a/plugins/hls-tactics-plugin/test/Utils.hs b/plugins/hls-tactics-plugin/old/test/Utils.hs similarity index 99% rename from plugins/hls-tactics-plugin/test/Utils.hs rename to plugins/hls-tactics-plugin/old/test/Utils.hs index 15c0386bb8..db31d910cf 100644 --- a/plugins/hls-tactics-plugin/test/Utils.hs +++ b/plugins/hls-tactics-plugin/old/test/Utils.hs @@ -252,7 +252,7 @@ failing _ _ = pure () tacticPath :: FilePath -tacticPath = "test/golden" +tacticPath = "old/test/golden" executeCommandWithResp :: Command -> Session (ResponseMessage 'WorkspaceExecuteCommand) diff --git a/plugins/hls-tactics-plugin/test/golden/AutoEmptyString.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoEmptyString.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoEmptyString.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoEmptyString.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoEmptyString.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoEmptyString.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoEmptyString.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoEmptyString.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoEndo.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoEndo.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoEndo.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoEndo.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoEndo.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoEndo.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoEndo.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoEndo.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoForallClassMethod.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoForallClassMethod.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoForallClassMethod.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoForallClassMethod.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoForallClassMethod.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoForallClassMethod.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoForallClassMethod.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoForallClassMethod.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoInfixApply.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoInfixApply.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoInfixApply.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoInfixApply.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoInfixApply.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoInfixApply.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoInfixApply.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoInfixApply.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoInfixApplyMany.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoInfixApplyMany.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoInfixApplyMany.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoInfixApplyMany.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoInfixApplyMany.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoInfixApplyMany.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoInfixApplyMany.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoInfixApplyMany.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoInfixInfix.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoInfixInfix.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoInfixInfix.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoInfixInfix.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoInfixInfix.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoInfixInfix.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoInfixInfix.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoInfixInfix.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoPatSynUse.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoPatSynUse.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoPatSynUse.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoPatSynUse.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoPatSynUse.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoPatSynUse.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoPatSynUse.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoPatSynUse.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoSplitGADT.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoSplitGADT.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoSplitGADT.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoSplitGADT.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoSplitGADT.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoSplitGADT.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoSplitGADT.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoSplitGADT.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaEqCtx.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaEqCtx.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaEqCtx.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaEqCtx.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaEqCtx.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaEqCtx.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaEqCtx.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaEqCtx.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaEqGADT.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaEqGADT.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaEqGADT.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaEqGADT.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaEqGADT.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaEqGADT.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaEqGADT.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaEqGADT.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaEqGADTDestruct.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaEqGADTDestruct.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaEqGADTDestruct.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaEqGADTDestruct.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaEqGADTDestruct.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaEqGADTDestruct.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaEqGADTDestruct.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaEqGADTDestruct.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaFix.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaFix.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaFix.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaFix.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaFix.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaFix.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaFix.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaFix.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaGADT.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaGADT.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaGADT.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaGADT.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaGADT.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaGADT.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaGADT.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaGADT.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaGADTDestruct.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaGADTDestruct.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaGADTDestruct.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaGADTDestruct.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaGADTDestruct.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaGADTDestruct.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaGADTDestruct.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaGADTDestruct.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaMultipleUnification.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaMultipleUnification.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaMultipleUnification.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaMultipleUnification.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaMultipleUnification.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaMultipleUnification.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaMultipleUnification.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaMultipleUnification.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaRankN.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaRankN.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaRankN.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaRankN.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaRankN.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaRankN.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaRankN.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaRankN.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaRefl.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaRefl.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaRefl.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaRefl.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaRefl.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaRefl.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaRefl.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaRefl.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaReflDestruct.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaReflDestruct.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaReflDestruct.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaReflDestruct.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaReflDestruct.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaReflDestruct.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaReflDestruct.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaReflDestruct.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaSplitUnification.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaSplitUnification.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaSplitUnification.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaSplitUnification.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaSplitUnification.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoThetaSplitUnification.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoThetaSplitUnification.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoThetaSplitUnification.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoTypeLevel.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoTypeLevel.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoTypeLevel.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoTypeLevel.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoTypeLevel.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoTypeLevel.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoTypeLevel.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoTypeLevel.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoUnusedPatternMatch.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoUnusedPatternMatch.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoUnusedPatternMatch.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoUnusedPatternMatch.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoUnusedPatternMatch.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoUnusedPatternMatch.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoUnusedPatternMatch.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoUnusedPatternMatch.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoZip.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoZip.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoZip.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoZip.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/AutoZip.hs b/plugins/hls-tactics-plugin/old/test/golden/AutoZip.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/AutoZip.hs rename to plugins/hls-tactics-plugin/old/test/golden/AutoZip.hs diff --git a/plugins/hls-tactics-plugin/test/golden/ConProviders.hs b/plugins/hls-tactics-plugin/old/test/golden/ConProviders.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/ConProviders.hs rename to plugins/hls-tactics-plugin/old/test/golden/ConProviders.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructAllAnd.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructAllAnd.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructAllAnd.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructAllAnd.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructAllAnd.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructAllAnd.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructAllAnd.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructAllAnd.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructAllFunc.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructAllFunc.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructAllFunc.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructAllFunc.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructAllFunc.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructAllFunc.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructAllFunc.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructAllFunc.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructAllGADTEvidence.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructAllGADTEvidence.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructAllGADTEvidence.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructAllGADTEvidence.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructAllGADTEvidence.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructAllGADTEvidence.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructAllGADTEvidence.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructAllGADTEvidence.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructAllMany.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructAllMany.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructAllMany.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructAllMany.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructAllMany.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructAllMany.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructAllMany.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructAllMany.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructAllNonVarTopMatch.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructAllNonVarTopMatch.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructAllNonVarTopMatch.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructAllNonVarTopMatch.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructAllNonVarTopMatch.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructAllNonVarTopMatch.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructAllNonVarTopMatch.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructAllNonVarTopMatch.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructAllProvider.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructAllProvider.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructAllProvider.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructAllProvider.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructCthulhu.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructCthulhu.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructCthulhu.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructCthulhu.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructCthulhu.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructCthulhu.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructCthulhu.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructCthulhu.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructDataFam.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructDataFam.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructDataFam.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructDataFam.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructDataFam.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructDataFam.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructDataFam.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructDataFam.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructInt.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructInt.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructInt.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructInt.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructInt.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructInt.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructInt.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructInt.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructPun.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructPun.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructPun.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructPun.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructPun.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructPun.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructPun.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructPun.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructTyFam.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructTyFam.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructTyFam.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructTyFam.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructTyFam.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructTyFam.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructTyFam.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructTyFam.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructTyToDataFam.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructTyToDataFam.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructTyToDataFam.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructTyToDataFam.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/DestructTyToDataFam.hs b/plugins/hls-tactics-plugin/old/test/golden/DestructTyToDataFam.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/DestructTyToDataFam.hs rename to plugins/hls-tactics-plugin/old/test/golden/DestructTyToDataFam.hs diff --git a/plugins/hls-tactics-plugin/test/golden/EmptyCaseADT.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/EmptyCaseADT.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/EmptyCaseADT.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/EmptyCaseADT.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/EmptyCaseADT.hs b/plugins/hls-tactics-plugin/old/test/golden/EmptyCaseADT.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/EmptyCaseADT.hs rename to plugins/hls-tactics-plugin/old/test/golden/EmptyCaseADT.hs diff --git a/plugins/hls-tactics-plugin/test/golden/EmptyCaseApply.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/EmptyCaseApply.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/EmptyCaseApply.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/EmptyCaseApply.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/EmptyCaseApply.hs b/plugins/hls-tactics-plugin/old/test/golden/EmptyCaseApply.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/EmptyCaseApply.hs rename to plugins/hls-tactics-plugin/old/test/golden/EmptyCaseApply.hs diff --git a/plugins/hls-tactics-plugin/test/golden/EmptyCaseGADT.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/EmptyCaseGADT.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/EmptyCaseGADT.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/EmptyCaseGADT.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/EmptyCaseGADT.hs b/plugins/hls-tactics-plugin/old/test/golden/EmptyCaseGADT.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/EmptyCaseGADT.hs rename to plugins/hls-tactics-plugin/old/test/golden/EmptyCaseGADT.hs diff --git a/plugins/hls-tactics-plugin/test/golden/EmptyCaseLamCase.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/EmptyCaseLamCase.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/EmptyCaseLamCase.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/EmptyCaseLamCase.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/EmptyCaseLamCase.hs b/plugins/hls-tactics-plugin/old/test/golden/EmptyCaseLamCase.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/EmptyCaseLamCase.hs rename to plugins/hls-tactics-plugin/old/test/golden/EmptyCaseLamCase.hs diff --git a/plugins/hls-tactics-plugin/test/golden/EmptyCaseNested.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/EmptyCaseNested.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/EmptyCaseNested.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/EmptyCaseNested.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/EmptyCaseNested.hs b/plugins/hls-tactics-plugin/old/test/golden/EmptyCaseNested.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/EmptyCaseNested.hs rename to plugins/hls-tactics-plugin/old/test/golden/EmptyCaseNested.hs diff --git a/plugins/hls-tactics-plugin/test/golden/EmptyCaseParens.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/EmptyCaseParens.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/EmptyCaseParens.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/EmptyCaseParens.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/EmptyCaseParens.hs b/plugins/hls-tactics-plugin/old/test/golden/EmptyCaseParens.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/EmptyCaseParens.hs rename to plugins/hls-tactics-plugin/old/test/golden/EmptyCaseParens.hs diff --git a/plugins/hls-tactics-plugin/test/golden/EmptyCaseShadow.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/EmptyCaseShadow.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/EmptyCaseShadow.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/EmptyCaseShadow.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/EmptyCaseShadow.hs b/plugins/hls-tactics-plugin/old/test/golden/EmptyCaseShadow.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/EmptyCaseShadow.hs rename to plugins/hls-tactics-plugin/old/test/golden/EmptyCaseShadow.hs diff --git a/plugins/hls-tactics-plugin/test/golden/EmptyCaseSpuriousGADT.hs b/plugins/hls-tactics-plugin/old/test/golden/EmptyCaseSpuriousGADT.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/EmptyCaseSpuriousGADT.hs rename to plugins/hls-tactics-plugin/old/test/golden/EmptyCaseSpuriousGADT.hs diff --git a/plugins/hls-tactics-plugin/test/golden/Fgmap.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/Fgmap.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/Fgmap.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/Fgmap.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/Fgmap.hs b/plugins/hls-tactics-plugin/old/test/golden/Fgmap.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/Fgmap.hs rename to plugins/hls-tactics-plugin/old/test/golden/Fgmap.hs diff --git a/plugins/hls-tactics-plugin/test/golden/FmapBoth.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/FmapBoth.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/FmapBoth.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/FmapBoth.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/FmapBoth.hs b/plugins/hls-tactics-plugin/old/test/golden/FmapBoth.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/FmapBoth.hs rename to plugins/hls-tactics-plugin/old/test/golden/FmapBoth.hs diff --git a/plugins/hls-tactics-plugin/test/golden/FmapJoin.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/FmapJoin.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/FmapJoin.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/FmapJoin.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/FmapJoin.hs b/plugins/hls-tactics-plugin/old/test/golden/FmapJoin.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/FmapJoin.hs rename to plugins/hls-tactics-plugin/old/test/golden/FmapJoin.hs diff --git a/plugins/hls-tactics-plugin/test/golden/FmapJoinInLet.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/FmapJoinInLet.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/FmapJoinInLet.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/FmapJoinInLet.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/FmapJoinInLet.hs b/plugins/hls-tactics-plugin/old/test/golden/FmapJoinInLet.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/FmapJoinInLet.hs rename to plugins/hls-tactics-plugin/old/test/golden/FmapJoinInLet.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenApplicativeThen.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenApplicativeThen.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenApplicativeThen.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenApplicativeThen.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenArbitrary.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenArbitrary.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenArbitrary.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenArbitrary.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenArbitrary.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenArbitrary.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenArbitrary.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenArbitrary.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenArbitrarySingleConstructor.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenArbitrarySingleConstructor.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenArbitrarySingleConstructor.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenArbitrarySingleConstructor.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenArbitrarySingleConstructor.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenArbitrarySingleConstructor.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenArbitrarySingleConstructor.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenArbitrarySingleConstructor.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenBigTuple.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenBigTuple.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenBigTuple.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenBigTuple.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenBigTuple.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenBigTuple.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenBigTuple.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenBigTuple.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenEitherAuto.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenEitherAuto.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenEitherAuto.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenEitherAuto.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenEitherAuto.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenEitherAuto.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenEitherAuto.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenEitherAuto.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenEitherHomomorphic.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenEitherHomomorphic.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenEitherHomomorphic.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenEitherHomomorphic.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenEitherHomomorphic.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenEitherHomomorphic.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenEitherHomomorphic.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenEitherHomomorphic.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenFish.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenFish.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenFish.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenFish.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenFmapTree.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenFmapTree.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenFmapTree.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenFmapTree.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenFmapTree.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenFmapTree.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenFmapTree.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenFmapTree.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenFoldr.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenFoldr.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenFoldr.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenFoldr.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenFoldr.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenFoldr.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenFoldr.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenFoldr.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenFromMaybe.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenFromMaybe.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenFromMaybe.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenFromMaybe.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenFromMaybe.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenFromMaybe.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenFromMaybe.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenFromMaybe.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenGADTAuto.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenGADTAuto.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenGADTAuto.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenGADTAuto.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenGADTAuto.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenGADTAuto.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenGADTAuto.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenGADTAuto.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenGADTDestruct.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenGADTDestruct.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenGADTDestruct.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenGADTDestruct.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenGADTDestruct.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenGADTDestruct.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenGADTDestruct.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenGADTDestruct.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenGADTDestructCoercion.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenGADTDestructCoercion.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenGADTDestructCoercion.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenGADTDestructCoercion.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenGADTDestructCoercion.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenGADTDestructCoercion.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenGADTDestructCoercion.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenGADTDestructCoercion.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenIdTypeFam.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenIdTypeFam.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenIdTypeFam.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenIdTypeFam.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenIdTypeFam.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenIdTypeFam.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenIdTypeFam.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenIdTypeFam.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenIdentityFunctor.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenIdentityFunctor.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenIdentityFunctor.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenIdentityFunctor.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenIdentityFunctor.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenIdentityFunctor.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenIdentityFunctor.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenIdentityFunctor.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenIntros.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenIntros.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenIntros.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenIntros.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenIntros.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenIntros.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenIntros.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenIntros.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenJoinCont.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenJoinCont.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenJoinCont.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenJoinCont.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenJoinCont.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenJoinCont.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenJoinCont.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenJoinCont.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenListFmap.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenListFmap.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenListFmap.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenListFmap.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenListFmap.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenListFmap.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenListFmap.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenListFmap.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenNote.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenNote.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenNote.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenNote.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenNote.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenNote.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenNote.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenNote.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenPureList.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenPureList.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenPureList.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenPureList.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenPureList.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenPureList.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenPureList.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenPureList.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenSafeHead.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenSafeHead.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenSafeHead.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenSafeHead.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenSafeHead.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenSafeHead.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenSafeHead.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenSafeHead.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenShow.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenShow.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenShow.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenShow.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenShow.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenShow.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenShow.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenShow.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenShowCompose.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenShowCompose.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenShowCompose.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenShowCompose.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenShowCompose.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenShowCompose.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenShowCompose.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenShowCompose.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenShowMapChar.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenShowMapChar.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenShowMapChar.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenShowMapChar.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenShowMapChar.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenShowMapChar.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenShowMapChar.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenShowMapChar.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenSuperclass.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenSuperclass.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenSuperclass.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenSuperclass.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenSuperclass.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenSuperclass.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenSuperclass.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenSuperclass.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenSwap.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenSwap.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenSwap.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenSwap.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenSwap.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenSwap.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenSwap.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenSwap.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenSwapMany.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenSwapMany.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenSwapMany.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenSwapMany.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenSwapMany.hs b/plugins/hls-tactics-plugin/old/test/golden/GoldenSwapMany.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/GoldenSwapMany.hs rename to plugins/hls-tactics-plugin/old/test/golden/GoldenSwapMany.hs diff --git a/plugins/hls-tactics-plugin/test/golden/IntroDestructLetBinding.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/IntroDestructLetBinding.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/IntroDestructLetBinding.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/IntroDestructLetBinding.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/IntroDestructLetBinding.hs b/plugins/hls-tactics-plugin/old/test/golden/IntroDestructLetBinding.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/IntroDestructLetBinding.hs rename to plugins/hls-tactics-plugin/old/test/golden/IntroDestructLetBinding.hs diff --git a/plugins/hls-tactics-plugin/test/golden/IntroDestructMany.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/IntroDestructMany.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/IntroDestructMany.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/IntroDestructMany.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/IntroDestructMany.hs b/plugins/hls-tactics-plugin/old/test/golden/IntroDestructMany.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/IntroDestructMany.hs rename to plugins/hls-tactics-plugin/old/test/golden/IntroDestructMany.hs diff --git a/plugins/hls-tactics-plugin/test/golden/IntroDestructOne.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/IntroDestructOne.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/IntroDestructOne.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/IntroDestructOne.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/IntroDestructOne.hs b/plugins/hls-tactics-plugin/old/test/golden/IntroDestructOne.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/IntroDestructOne.hs rename to plugins/hls-tactics-plugin/old/test/golden/IntroDestructOne.hs diff --git a/plugins/hls-tactics-plugin/test/golden/IntroDestructProvider.hs b/plugins/hls-tactics-plugin/old/test/golden/IntroDestructProvider.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/IntroDestructProvider.hs rename to plugins/hls-tactics-plugin/old/test/golden/IntroDestructProvider.hs diff --git a/plugins/hls-tactics-plugin/test/golden/IntrosTooMany.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/IntrosTooMany.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/IntrosTooMany.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/IntrosTooMany.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/IntrosTooMany.hs b/plugins/hls-tactics-plugin/old/test/golden/IntrosTooMany.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/IntrosTooMany.hs rename to plugins/hls-tactics-plugin/old/test/golden/IntrosTooMany.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownBigSemigroup.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownBigSemigroup.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownBigSemigroup.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownBigSemigroup.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownBigSemigroup.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownBigSemigroup.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownBigSemigroup.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownBigSemigroup.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownCounterfactualSemigroup.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownCounterfactualSemigroup.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownCounterfactualSemigroup.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownCounterfactualSemigroup.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownCounterfactualSemigroup.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownCounterfactualSemigroup.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownCounterfactualSemigroup.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownCounterfactualSemigroup.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownDestructedSemigroup.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownDestructedSemigroup.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownDestructedSemigroup.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownDestructedSemigroup.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownDestructedSemigroup.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownDestructedSemigroup.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownDestructedSemigroup.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownDestructedSemigroup.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownMissingMonoid.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownMissingMonoid.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownMissingMonoid.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownMissingMonoid.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownMissingMonoid.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownMissingMonoid.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownMissingMonoid.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownMissingMonoid.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownMissingSemigroup.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownMissingSemigroup.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownMissingSemigroup.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownMissingSemigroup.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownMissingSemigroup.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownMissingSemigroup.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownMissingSemigroup.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownMissingSemigroup.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownModuleInstanceSemigroup.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownModuleInstanceSemigroup.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownModuleInstanceSemigroup.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownModuleInstanceSemigroup.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownModuleInstanceSemigroup.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownModuleInstanceSemigroup.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownModuleInstanceSemigroup.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownModuleInstanceSemigroup.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownMonoid.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownMonoid.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownMonoid.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownMonoid.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownMonoid.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownMonoid.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownMonoid.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownMonoid.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownPolyMonoid.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownPolyMonoid.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownPolyMonoid.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownPolyMonoid.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownPolyMonoid.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownPolyMonoid.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownPolyMonoid.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownPolyMonoid.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownThetaSemigroup.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownThetaSemigroup.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownThetaSemigroup.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownThetaSemigroup.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/KnownThetaSemigroup.hs b/plugins/hls-tactics-plugin/old/test/golden/KnownThetaSemigroup.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/KnownThetaSemigroup.hs rename to plugins/hls-tactics-plugin/old/test/golden/KnownThetaSemigroup.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutBind.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutBind.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutBind.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutBind.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutBind.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutBind.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutBind.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutBind.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutDollarApp.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutDollarApp.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutDollarApp.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutDollarApp.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutDollarApp.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutDollarApp.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutDollarApp.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutDollarApp.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutInfixKeep.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutInfixKeep.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutInfixKeep.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutInfixKeep.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutInfixKeep.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutInfixKeep.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutInfixKeep.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutInfixKeep.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutLam.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutLam.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutLam.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutLam.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutLam.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutLam.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutLam.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutLam.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutOpApp.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutOpApp.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutOpApp.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutOpApp.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutOpApp.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutOpApp.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutOpApp.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutOpApp.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutPrefixKeep.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutPrefixKeep.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutPrefixKeep.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutPrefixKeep.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutPrefixKeep.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutPrefixKeep.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutPrefixKeep.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutPrefixKeep.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutRec.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutRec.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutRec.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutRec.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutRec.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutRec.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutRec.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutRec.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitClass.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutSplitClass.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutSplitClass.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutSplitClass.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitClass.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutSplitClass.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutSplitClass.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutSplitClass.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitGuard.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutSplitGuard.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutSplitGuard.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutSplitGuard.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitGuard.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutSplitGuard.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutSplitGuard.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutSplitGuard.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitIn.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutSplitIn.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutSplitIn.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutSplitIn.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitIn.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutSplitIn.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutSplitIn.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutSplitIn.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitLet.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutSplitLet.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutSplitLet.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutSplitLet.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitLet.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutSplitLet.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutSplitLet.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutSplitLet.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitPatSyn.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutSplitPatSyn.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutSplitPatSyn.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutSplitPatSyn.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitPatSyn.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutSplitPatSyn.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutSplitPatSyn.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutSplitPatSyn.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitPattern.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutSplitPattern.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutSplitPattern.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutSplitPattern.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitPattern.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutSplitPattern.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutSplitPattern.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutSplitPattern.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitViewPat.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutSplitViewPat.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutSplitViewPat.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutSplitViewPat.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitViewPat.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutSplitViewPat.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutSplitViewPat.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutSplitViewPat.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitWhere.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutSplitWhere.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutSplitWhere.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutSplitWhere.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitWhere.hs b/plugins/hls-tactics-plugin/old/test/golden/LayoutSplitWhere.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/LayoutSplitWhere.hs rename to plugins/hls-tactics-plugin/old/test/golden/LayoutSplitWhere.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MessageCantUnify.hs b/plugins/hls-tactics-plugin/old/test/golden/MessageCantUnify.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MessageCantUnify.hs rename to plugins/hls-tactics-plugin/old/test/golden/MessageCantUnify.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MessageForallA.hs b/plugins/hls-tactics-plugin/old/test/golden/MessageForallA.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MessageForallA.hs rename to plugins/hls-tactics-plugin/old/test/golden/MessageForallA.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MessageNotEnoughGas.hs b/plugins/hls-tactics-plugin/old/test/golden/MessageNotEnoughGas.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MessageNotEnoughGas.hs rename to plugins/hls-tactics-plugin/old/test/golden/MessageNotEnoughGas.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaBegin.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaBegin.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaBegin.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaBegin.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaBegin.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaBegin.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaBegin.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaBegin.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaBeginNoWildify.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaBeginNoWildify.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaBeginNoWildify.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaBeginNoWildify.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaBeginNoWildify.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaBeginNoWildify.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaBeginNoWildify.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaBeginNoWildify.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaBindAll.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaBindAll.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaBindAll.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaBindAll.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaBindAll.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaBindAll.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaBindAll.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaBindAll.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaBindOne.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaBindOne.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaBindOne.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaBindOne.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaBindOne.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaBindOne.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaBindOne.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaBindOne.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaCataAST.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaCataAST.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaCataAST.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaCataAST.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaCataAST.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaCataAST.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaCataAST.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaCataAST.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaCataCollapse.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaCataCollapse.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaCataCollapse.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaCataCollapse.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaCataCollapse.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaCataCollapse.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaCataCollapse.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaCataCollapse.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaCataCollapseUnary.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaCataCollapseUnary.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaCataCollapseUnary.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaCataCollapseUnary.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaCataCollapseUnary.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaCataCollapseUnary.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaCataCollapseUnary.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaCataCollapseUnary.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaChoice.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaChoice.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaChoice.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaChoice.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaChoice.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaChoice.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaChoice.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaChoice.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaDeepOf.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaDeepOf.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaDeepOf.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaDeepOf.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaDeepOf.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaDeepOf.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaDeepOf.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaDeepOf.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaFundeps.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaFundeps.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaFundeps.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaFundeps.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaFundeps.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaFundeps.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaFundeps.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaFundeps.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaIdiom.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaIdiom.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaIdiom.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaIdiom.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaIdiom.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaIdiom.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaIdiom.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaIdiom.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaIdiomRecord.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaIdiomRecord.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaIdiomRecord.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaIdiomRecord.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaIdiomRecord.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaIdiomRecord.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaIdiomRecord.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaIdiomRecord.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaLetSimple.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaLetSimple.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaLetSimple.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaLetSimple.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaLetSimple.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaLetSimple.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaLetSimple.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaLetSimple.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaMaybeAp.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaMaybeAp.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaMaybeAp.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaMaybeAp.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaMaybeAp.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaMaybeAp.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaMaybeAp.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaMaybeAp.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaPointwise.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaPointwise.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaPointwise.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaPointwise.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaPointwise.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaPointwise.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaPointwise.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaPointwise.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaTry.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaTry.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaTry.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaTry.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaTry.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaTry.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaTry.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaTry.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaUseImport.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaUseImport.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaUseImport.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaUseImport.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaUseImport.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaUseImport.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaUseImport.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaUseImport.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaUseLocal.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaUseLocal.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaUseLocal.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaUseLocal.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaUseLocal.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaUseLocal.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaUseLocal.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaUseLocal.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaUseMethod.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaUseMethod.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaUseMethod.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaUseMethod.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaUseMethod.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaUseMethod.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaUseMethod.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaUseMethod.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaUseSymbol.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaUseSymbol.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaUseSymbol.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaUseSymbol.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaUseSymbol.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaUseSymbol.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaUseSymbol.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaUseSymbol.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaWithArg.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaWithArg.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaWithArg.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaWithArg.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/MetaWithArg.hs b/plugins/hls-tactics-plugin/old/test/golden/MetaWithArg.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/MetaWithArg.hs rename to plugins/hls-tactics-plugin/old/test/golden/MetaWithArg.hs diff --git a/plugins/hls-tactics-plugin/test/golden/NewtypeRecord.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/NewtypeRecord.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/NewtypeRecord.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/NewtypeRecord.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/NewtypeRecord.hs b/plugins/hls-tactics-plugin/old/test/golden/NewtypeRecord.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/NewtypeRecord.hs rename to plugins/hls-tactics-plugin/old/test/golden/NewtypeRecord.hs diff --git a/plugins/hls-tactics-plugin/test/golden/ProvideAlreadyDestructed.hs b/plugins/hls-tactics-plugin/old/test/golden/ProvideAlreadyDestructed.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/ProvideAlreadyDestructed.hs rename to plugins/hls-tactics-plugin/old/test/golden/ProvideAlreadyDestructed.hs diff --git a/plugins/hls-tactics-plugin/test/golden/ProvideLocalHyOnly.hs b/plugins/hls-tactics-plugin/old/test/golden/ProvideLocalHyOnly.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/ProvideLocalHyOnly.hs rename to plugins/hls-tactics-plugin/old/test/golden/ProvideLocalHyOnly.hs diff --git a/plugins/hls-tactics-plugin/test/golden/ProviderHomomorphism.hs b/plugins/hls-tactics-plugin/old/test/golden/ProviderHomomorphism.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/ProviderHomomorphism.hs rename to plugins/hls-tactics-plugin/old/test/golden/ProviderHomomorphism.hs diff --git a/plugins/hls-tactics-plugin/test/golden/PunGADT.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/PunGADT.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/PunGADT.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/PunGADT.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/PunGADT.hs b/plugins/hls-tactics-plugin/old/test/golden/PunGADT.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/PunGADT.hs rename to plugins/hls-tactics-plugin/old/test/golden/PunGADT.hs diff --git a/plugins/hls-tactics-plugin/test/golden/PunMany.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/PunMany.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/PunMany.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/PunMany.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/PunMany.hs b/plugins/hls-tactics-plugin/old/test/golden/PunMany.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/PunMany.hs rename to plugins/hls-tactics-plugin/old/test/golden/PunMany.hs diff --git a/plugins/hls-tactics-plugin/test/golden/PunManyGADT.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/PunManyGADT.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/PunManyGADT.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/PunManyGADT.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/PunManyGADT.hs b/plugins/hls-tactics-plugin/old/test/golden/PunManyGADT.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/PunManyGADT.hs rename to plugins/hls-tactics-plugin/old/test/golden/PunManyGADT.hs diff --git a/plugins/hls-tactics-plugin/test/golden/PunShadowing.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/PunShadowing.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/PunShadowing.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/PunShadowing.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/PunShadowing.hs b/plugins/hls-tactics-plugin/old/test/golden/PunShadowing.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/PunShadowing.hs rename to plugins/hls-tactics-plugin/old/test/golden/PunShadowing.hs diff --git a/plugins/hls-tactics-plugin/test/golden/PunSimple.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/PunSimple.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/PunSimple.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/PunSimple.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/PunSimple.hs b/plugins/hls-tactics-plugin/old/test/golden/PunSimple.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/PunSimple.hs rename to plugins/hls-tactics-plugin/old/test/golden/PunSimple.hs diff --git a/plugins/hls-tactics-plugin/test/golden/RecordCon.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/RecordCon.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/RecordCon.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/RecordCon.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/RecordCon.hs b/plugins/hls-tactics-plugin/old/test/golden/RecordCon.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/RecordCon.hs rename to plugins/hls-tactics-plugin/old/test/golden/RecordCon.hs diff --git a/plugins/hls-tactics-plugin/test/golden/RefineCon.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/RefineCon.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/RefineCon.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/RefineCon.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/RefineCon.hs b/plugins/hls-tactics-plugin/old/test/golden/RefineCon.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/RefineCon.hs rename to plugins/hls-tactics-plugin/old/test/golden/RefineCon.hs diff --git a/plugins/hls-tactics-plugin/test/golden/RefineGADT.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/RefineGADT.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/RefineGADT.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/RefineGADT.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/RefineGADT.hs b/plugins/hls-tactics-plugin/old/test/golden/RefineGADT.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/RefineGADT.hs rename to plugins/hls-tactics-plugin/old/test/golden/RefineGADT.hs diff --git a/plugins/hls-tactics-plugin/test/golden/RefineIntro.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/RefineIntro.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/RefineIntro.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/RefineIntro.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/RefineIntro.hs b/plugins/hls-tactics-plugin/old/test/golden/RefineIntro.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/RefineIntro.hs rename to plugins/hls-tactics-plugin/old/test/golden/RefineIntro.hs diff --git a/plugins/hls-tactics-plugin/test/golden/RefineIntroWhere.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/RefineIntroWhere.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/RefineIntroWhere.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/RefineIntroWhere.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/RefineIntroWhere.hs b/plugins/hls-tactics-plugin/old/test/golden/RefineIntroWhere.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/RefineIntroWhere.hs rename to plugins/hls-tactics-plugin/old/test/golden/RefineIntroWhere.hs diff --git a/plugins/hls-tactics-plugin/test/golden/RefineReader.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/RefineReader.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/RefineReader.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/RefineReader.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/RefineReader.hs b/plugins/hls-tactics-plugin/old/test/golden/RefineReader.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/RefineReader.hs rename to plugins/hls-tactics-plugin/old/test/golden/RefineReader.hs diff --git a/plugins/hls-tactics-plugin/test/golden/SplitPattern.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/SplitPattern.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/SplitPattern.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/SplitPattern.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/SplitPattern.hs b/plugins/hls-tactics-plugin/old/test/golden/SplitPattern.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/SplitPattern.hs rename to plugins/hls-tactics-plugin/old/test/golden/SplitPattern.hs diff --git a/plugins/hls-tactics-plugin/test/golden/SubsequentTactics.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/SubsequentTactics.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/SubsequentTactics.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/SubsequentTactics.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/SubsequentTactics.hs b/plugins/hls-tactics-plugin/old/test/golden/SubsequentTactics.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/SubsequentTactics.hs rename to plugins/hls-tactics-plugin/old/test/golden/SubsequentTactics.hs diff --git a/plugins/hls-tactics-plugin/test/golden/T1.hs b/plugins/hls-tactics-plugin/old/test/golden/T1.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/T1.hs rename to plugins/hls-tactics-plugin/old/test/golden/T1.hs diff --git a/plugins/hls-tactics-plugin/test/golden/T2.hs b/plugins/hls-tactics-plugin/old/test/golden/T2.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/T2.hs rename to plugins/hls-tactics-plugin/old/test/golden/T2.hs diff --git a/plugins/hls-tactics-plugin/test/golden/T3.hs b/plugins/hls-tactics-plugin/old/test/golden/T3.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/T3.hs rename to plugins/hls-tactics-plugin/old/test/golden/T3.hs diff --git a/plugins/hls-tactics-plugin/test/golden/UseConLeft.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/UseConLeft.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/UseConLeft.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/UseConLeft.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/UseConLeft.hs b/plugins/hls-tactics-plugin/old/test/golden/UseConLeft.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/UseConLeft.hs rename to plugins/hls-tactics-plugin/old/test/golden/UseConLeft.hs diff --git a/plugins/hls-tactics-plugin/test/golden/UseConPair.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/UseConPair.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/UseConPair.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/UseConPair.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/UseConPair.hs b/plugins/hls-tactics-plugin/old/test/golden/UseConPair.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/UseConPair.hs rename to plugins/hls-tactics-plugin/old/test/golden/UseConPair.hs diff --git a/plugins/hls-tactics-plugin/test/golden/UseConRight.expected.hs b/plugins/hls-tactics-plugin/old/test/golden/UseConRight.expected.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/UseConRight.expected.hs rename to plugins/hls-tactics-plugin/old/test/golden/UseConRight.expected.hs diff --git a/plugins/hls-tactics-plugin/test/golden/UseConRight.hs b/plugins/hls-tactics-plugin/old/test/golden/UseConRight.hs similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/UseConRight.hs rename to plugins/hls-tactics-plugin/old/test/golden/UseConRight.hs diff --git a/plugins/hls-tactics-plugin/test/golden/hie.yaml b/plugins/hls-tactics-plugin/old/test/golden/hie.yaml similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/hie.yaml rename to plugins/hls-tactics-plugin/old/test/golden/hie.yaml diff --git a/plugins/hls-tactics-plugin/test/golden/test.cabal b/plugins/hls-tactics-plugin/old/test/golden/test.cabal similarity index 100% rename from plugins/hls-tactics-plugin/test/golden/test.cabal rename to plugins/hls-tactics-plugin/old/test/golden/test.cabal From ac83ca47017367695cf7161fbbaaaa145f312251 Mon Sep 17 00:00:00 2001 From: santiweight Date: Tue, 22 Nov 2022 17:26:48 -0800 Subject: [PATCH 199/213] Cleanup Development.IDE.CodeAction (#3360) * refact: extract ImportUtils * refact: extract FillTypeWildcard * refact: Extract FillHole * remove partial hlint warnings * fix import Co-authored-by: Santiago Weight --- .../hls-refactor-plugin.cabal | 3 + .../src/Development/IDE/Plugin/CodeAction.hs | 227 +----------------- .../IDE/Plugin/Plugins/FillHole.hs | 104 ++++++++ .../IDE/Plugin/Plugins/FillTypeWildcard.hs | 78 ++++++ .../IDE/Plugin/Plugins/ImportUtils.hs | 83 +++++++ 5 files changed, 273 insertions(+), 222 deletions(-) create mode 100644 plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/FillHole.hs create mode 100644 plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/FillTypeWildcard.hs create mode 100644 plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/ImportUtils.hs diff --git a/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal b/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal index 3f888cc946..e3e1e6cc69 100644 --- a/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal +++ b/plugins/hls-refactor-plugin/hls-refactor-plugin.cabal @@ -36,6 +36,9 @@ library Development.IDE.Plugin.CodeAction.PositionIndexed Development.IDE.Plugin.Plugins.AddArgument Development.IDE.Plugin.Plugins.Diagnostic + Development.IDE.Plugin.Plugins.FillHole + Development.IDE.Plugin.Plugins.FillTypeWildcard + Development.IDE.Plugin.Plugins.ImportUtils default-extensions: BangPatterns CPP diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index a28af0fa18..3a45c0f154 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -58,6 +58,9 @@ import Development.IDE.Plugin.CodeAction.Util import Development.IDE.Plugin.Completions.Types import qualified Development.IDE.Plugin.Plugins.AddArgument import Development.IDE.Plugin.Plugins.Diagnostic +import Development.IDE.Plugin.Plugins.FillHole (suggestFillHole) +import Development.IDE.Plugin.Plugins.FillTypeWildcard (suggestFillTypeWildcard) +import Development.IDE.Plugin.Plugins.ImportUtils import Development.IDE.Plugin.TypeLenses (suggestSignature) import Development.IDE.Types.Exports import Development.IDE.Types.Location @@ -72,7 +75,7 @@ import qualified Language.LSP.Server as LSP import Language.LSP.Types (ApplyWorkspaceEditParams (..), CodeAction (..), CodeActionContext (CodeActionContext, _diagnostics), - CodeActionKind (CodeActionQuickFix, CodeActionUnknown), + CodeActionKind (CodeActionQuickFix), CodeActionParams (CodeActionParams), Command, Diagnostic (..), @@ -90,8 +93,7 @@ import Language.LSP.Types (ApplyWorkspa import Language.LSP.VFS (VirtualFile, _file_text) import qualified Text.Fuzzy.Parallel as TFP -import Text.Regex.TDFA (mrAfter, - (=~), (=~~)) +import Text.Regex.TDFA ((=~), (=~~)) #if MIN_VERSION_ghc(9,2,0) import GHC (AddEpAnn (AddEpAnn), Anchor (anchor_op), @@ -915,17 +917,6 @@ newDefinitionAction IdeOptions {..} parsedModule Range {_start} name typ ParsedModule {pm_parsed_source = L _ HsModule {hsmodDecls}} = parsedModule -suggestFillTypeWildcard :: Diagnostic -> [(T.Text, TextEdit)] -suggestFillTypeWildcard Diagnostic{_range=_range,..} --- Foo.hs:3:8: error: --- * Found type wildcard `_' standing for `p -> p1 -> p' - - | "Found type wildcard" `T.isInfixOf` _message - , " standing for " `T.isInfixOf` _message - , typeSignature <- extractWildCardTypeSignature _message - = [("Use type signature: ‘" <> typeSignature <> "’", TextEdit _range typeSignature)] - | otherwise = [] - {- Handles two variants with different formatting 1. Could not find module ‘Data.Cha’ @@ -953,88 +944,6 @@ suggestModuleTypo Diagnostic{_range=_range,..} _ -> Nothing -suggestFillHole :: Diagnostic -> [(T.Text, TextEdit)] -suggestFillHole Diagnostic{_range=_range,..} - | Just holeName <- extractHoleName _message - , (holeFits, refFits) <- processHoleSuggestions (T.lines _message) = - let isInfixHole = _message =~ addBackticks holeName :: Bool in - map (proposeHoleFit holeName False isInfixHole) holeFits - ++ map (proposeHoleFit holeName True isInfixHole) refFits - | otherwise = [] - where - extractHoleName = fmap head . flip matchRegexUnifySpaces "Found hole: ([^ ]*)" - addBackticks text = "`" <> text <> "`" - addParens text = "(" <> text <> ")" - proposeHoleFit holeName parenthise isInfixHole name = - let isInfixOperator = T.head name == '(' - name' = getOperatorNotation isInfixHole isInfixOperator name in - ( "replace " <> holeName <> " with " <> name - , TextEdit _range (if parenthise then addParens name' else name') - ) - getOperatorNotation True False name = addBackticks name - getOperatorNotation True True name = T.drop 1 (T.dropEnd 1 name) - getOperatorNotation _isInfixHole _isInfixOperator name = name - -processHoleSuggestions :: [T.Text] -> ([T.Text], [T.Text]) -processHoleSuggestions mm = (holeSuggestions, refSuggestions) -{- - • Found hole: _ :: LSP.Handlers - - Valid hole fits include def - Valid refinement hole fits include - fromMaybe (_ :: LSP.Handlers) (_ :: Maybe LSP.Handlers) - fromJust (_ :: Maybe LSP.Handlers) - haskell-lsp-types-0.22.0.0:Language.LSP.Types.Window.$sel:_value:ProgressParams (_ :: ProgressParams - LSP.Handlers) - T.foldl (_ :: LSP.Handlers -> Char -> LSP.Handlers) - (_ :: LSP.Handlers) - (_ :: T.Text) - T.foldl' (_ :: LSP.Handlers -> Char -> LSP.Handlers) - (_ :: LSP.Handlers) - (_ :: T.Text) --} - where - t = id @T.Text - holeSuggestions = do - -- get the text indented under Valid hole fits - validHolesSection <- - getIndentedGroupsBy (=~ t " *Valid (hole fits|substitutions) include") mm - -- the Valid hole fits line can contain a hole fit - holeFitLine <- - mapHead - (mrAfter . (=~ t " *Valid (hole fits|substitutions) include")) - validHolesSection - let holeFit = T.strip $ T.takeWhile (/= ':') holeFitLine - guard (not $ T.null holeFit) - return holeFit - refSuggestions = do -- @[] - -- get the text indented under Valid refinement hole fits - refinementSection <- - getIndentedGroupsBy (=~ t " *Valid refinement hole fits include") mm - -- get the text for each hole fit - holeFitLines <- getIndentedGroups (tail refinementSection) - let holeFit = T.strip $ T.unwords holeFitLines - guard $ not $ holeFit =~ t "Some refinement hole fits suppressed" - return holeFit - - mapHead f (a:aa) = f a : aa - mapHead _ [] = [] - --- > getIndentedGroups [" H1", " l1", " l2", " H2", " l3"] = [[" H1,", " l1", " l2"], [" H2", " l3"]] -getIndentedGroups :: [T.Text] -> [[T.Text]] -getIndentedGroups [] = [] -getIndentedGroups ll@(l:_) = getIndentedGroupsBy ((== indentation l) . indentation) ll --- | --- > getIndentedGroupsBy (" H" `isPrefixOf`) [" H1", " l1", " l2", " H2", " l3"] = [[" H1", " l1", " l2"], [" H2", " l3"]] -getIndentedGroupsBy :: (T.Text -> Bool) -> [T.Text] -> [[T.Text]] -getIndentedGroupsBy pred inp = case dropWhile (not.pred) inp of - (l:ll) -> case span (\l' -> indentation l < indentation l') ll of - (indented, rest) -> (l:indented) : getIndentedGroupsBy pred rest - _ -> [] - -indentation :: T.Text -> Int -indentation = T.length . T.takeWhile isSpace - #if !MIN_VERSION_ghc(9,3,0) suggestExtendImport :: ExportsMap -> ParsedSource -> Diagnostic -> [(T.Text, CodeActionKind, Rewrite)] suggestExtendImport exportsMap (L _ HsModule {hsmodImports}) Diagnostic{_range=_range,..} @@ -1845,64 +1754,6 @@ mkRenameEdit contents range name curr <- textInRange range <$> contents pure $ "'" `T.isPrefixOf` curr --- | Extract the type and surround it in parentheses except in obviously safe cases. --- --- Inferring when parentheses are actually needed around the type signature would --- require understanding both the precedence of the context of the hole and of --- the signature itself. Inserting them (almost) unconditionally is ugly but safe. -extractWildCardTypeSignature :: T.Text -> T.Text -extractWildCardTypeSignature msg - | enclosed || not isApp || isToplevelSig = sig - | otherwise = "(" <> sig <> ")" - where - msgSigPart = snd $ T.breakOnEnd "standing for " msg - (sig, rest) = T.span (/='’') . T.dropWhile (=='‘') . T.dropWhile (/='‘') $ msgSigPart - -- If we're completing something like ‘foo :: _’ parens can be safely omitted. - isToplevelSig = errorMessageRefersToToplevelHole rest - -- Parenthesize type applications, e.g. (Maybe Char). - isApp = T.any isSpace sig - -- Do not add extra parentheses to lists, tuples and already parenthesized types. - enclosed = not (T.null sig) && (T.head sig, T.last sig) `elem` [('(', ')'), ('[', ']')] - --- | Detect whether user wrote something like @foo :: _@ or @foo :: (_, Int)@. --- The former is considered toplevel case for which the function returns 'True', --- the latter is not toplevel and the returned value is 'False'. --- --- When type hole is at toplevel then there’s a line starting with --- "• In the type signature" which ends with " :: _" like in the --- following snippet: --- --- source/library/Language/Haskell/Brittany/Internal.hs:131:13: error: --- • Found type wildcard ‘_’ standing for ‘HsDecl GhcPs’ --- To use the inferred type, enable PartialTypeSignatures --- • In the type signature: decl :: _ --- In an equation for ‘splitAnnots’: --- splitAnnots m@HsModule {hsmodAnn, hsmodDecls} --- = undefined --- where --- ann :: SrcSpanAnnA --- decl :: _ --- L ann decl = head hsmodDecls --- • Relevant bindings include --- [REDACTED] --- --- When type hole is not at toplevel there’s a stack of where --- the hole was located ending with "In the type signature": --- --- source/library/Language/Haskell/Brittany/Internal.hs:130:20: error: --- • Found type wildcard ‘_’ standing for ‘GhcPs’ --- To use the inferred type, enable PartialTypeSignatures --- • In the first argument of ‘HsDecl’, namely ‘_’ --- In the type ‘HsDecl _’ --- In the type signature: decl :: HsDecl _ --- • Relevant bindings include --- [REDACTED] -errorMessageRefersToToplevelHole :: T.Text -> Bool -errorMessageRefersToToplevelHole msg = - not (T.null prefix) && " :: _" `T.isSuffixOf` T.takeWhile (/= '\n') rest - where - (prefix, rest) = T.breakOn "• In the type signature:" msg - extractRenamableTerms :: T.Text -> [T.Text] extractRenamableTerms msg -- Account for both "Variable not in scope" and "Not in scope" @@ -2054,71 +1905,3 @@ matchRegExMultipleImports message = do imps <- regExImports imports return (binding, imps) --- | Possible import styles for an 'IdentInfo'. --- --- The first 'Text' parameter corresponds to the 'rendered' field of the --- 'IdentInfo'. -data ImportStyle - = ImportTopLevel T.Text - -- ^ Import a top-level export from a module, e.g., a function, a type, a - -- class. - -- - -- > import M (?) - -- - -- Some exports that have a parent, like a type-class method or an - -- associated type/data family, can still be imported as a top-level - -- import. - -- - -- Note that this is not the case for constructors, they must always be - -- imported as part of their parent data type. - - | ImportViaParent T.Text T.Text - -- ^ Import an export (first parameter) through its parent (second - -- parameter). - -- - -- import M (P(?)) - -- - -- @P@ and @?@ can be a data type and a constructor, a class and a method, - -- a class and an associated type/data family, etc. - - | ImportAllConstructors T.Text - -- ^ Import all constructors for a specific data type. - -- - -- import M (P(..)) - -- - -- @P@ can be a data type or a class. - deriving Show - -importStyles :: IdentInfo -> NonEmpty ImportStyle -importStyles IdentInfo {parent, rendered, isDatacon} - | Just p <- parent - -- Constructors always have to be imported via their parent data type, but - -- methods and associated type/data families can also be imported as - -- top-level exports. - = ImportViaParent rendered p - :| [ImportTopLevel rendered | not isDatacon] - <> [ImportAllConstructors p] - | otherwise - = ImportTopLevel rendered :| [] - --- | Used for adding new imports -renderImportStyle :: ImportStyle -> T.Text -renderImportStyle (ImportTopLevel x) = x -renderImportStyle (ImportViaParent x p@(T.uncons -> Just ('(', _))) = "type " <> p <> "(" <> x <> ")" -renderImportStyle (ImportViaParent x p) = p <> "(" <> x <> ")" -renderImportStyle (ImportAllConstructors p) = p <> "(..)" - --- | Used for extending import lists -unImportStyle :: ImportStyle -> (Maybe String, String) -unImportStyle (ImportTopLevel x) = (Nothing, T.unpack x) -unImportStyle (ImportViaParent x y) = (Just $ T.unpack y, T.unpack x) -unImportStyle (ImportAllConstructors x) = (Just $ T.unpack x, wildCardSymbol) - - -quickFixImportKind' :: T.Text -> ImportStyle -> CodeActionKind -quickFixImportKind' x (ImportTopLevel _) = CodeActionUnknown $ "quickfix.import." <> x <> ".list.topLevel" -quickFixImportKind' x (ImportViaParent _ _) = CodeActionUnknown $ "quickfix.import." <> x <> ".list.withParent" -quickFixImportKind' x (ImportAllConstructors _) = CodeActionUnknown $ "quickfix.import." <> x <> ".list.allConstructors" - -quickFixImportKind :: T.Text -> CodeActionKind -quickFixImportKind x = CodeActionUnknown $ "quickfix.import." <> x diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/FillHole.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/FillHole.hs new file mode 100644 index 0000000000..43b11202cf --- /dev/null +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/FillHole.hs @@ -0,0 +1,104 @@ +module Development.IDE.Plugin.Plugins.FillHole + ( suggestFillHole + ) where + +import Control.Monad (guard) +import Data.Char +import qualified Data.Text as T +import Development.IDE.Plugin.Plugins.Diagnostic +import Language.LSP.Types (Diagnostic (..), + TextEdit (TextEdit)) +import Text.Regex.TDFA (MatchResult (..), + (=~)) + +suggestFillHole :: Diagnostic -> [(T.Text, TextEdit)] +suggestFillHole Diagnostic{_range=_range,..} + | Just holeName <- extractHoleName _message + , (holeFits, refFits) <- processHoleSuggestions (T.lines _message) = + let isInfixHole = _message =~ addBackticks holeName :: Bool in + map (proposeHoleFit holeName False isInfixHole) holeFits + ++ map (proposeHoleFit holeName True isInfixHole) refFits + | otherwise = [] + where + extractHoleName = fmap (headOrThrow "impossible") . flip matchRegexUnifySpaces "Found hole: ([^ ]*)" + addBackticks text = "`" <> text <> "`" + addParens text = "(" <> text <> ")" + proposeHoleFit holeName parenthise isInfixHole name = + case T.uncons name of + Nothing -> error "impossible: empty name provided by ghc" + Just (firstChr, _) -> + let isInfixOperator = firstChr == '(' + name' = getOperatorNotation isInfixHole isInfixOperator name in + ( "replace " <> holeName <> " with " <> name + , TextEdit _range (if parenthise then addParens name' else name') + ) + getOperatorNotation True False name = addBackticks name + getOperatorNotation True True name = T.drop 1 (T.dropEnd 1 name) + getOperatorNotation _isInfixHole _isInfixOperator name = name + headOrThrow msg = \case + [] -> error msg + (x:_) -> x + +processHoleSuggestions :: [T.Text] -> ([T.Text], [T.Text]) +processHoleSuggestions mm = (holeSuggestions, refSuggestions) +{- + • Found hole: _ :: LSP.Handlers + + Valid hole fits include def + Valid refinement hole fits include + fromMaybe (_ :: LSP.Handlers) (_ :: Maybe LSP.Handlers) + fromJust (_ :: Maybe LSP.Handlers) + haskell-lsp-types-0.22.0.0:Language.LSP.Types.Window.$sel:_value:ProgressParams (_ :: ProgressParams + LSP.Handlers) + T.foldl (_ :: LSP.Handlers -> Char -> LSP.Handlers) + (_ :: LSP.Handlers) + (_ :: T.Text) + T.foldl' (_ :: LSP.Handlers -> Char -> LSP.Handlers) + (_ :: LSP.Handlers) + (_ :: T.Text) +-} + where + t = id @T.Text + holeSuggestions = do + -- get the text indented under Valid hole fits + validHolesSection <- + getIndentedGroupsBy (=~ t " *Valid (hole fits|substitutions) include") mm + -- the Valid hole fits line can contain a hole fit + holeFitLine <- + mapHead + (mrAfter . (=~ t " *Valid (hole fits|substitutions) include")) + validHolesSection + let holeFit = T.strip $ T.takeWhile (/= ':') holeFitLine + guard (not $ T.null holeFit) + return holeFit + refSuggestions = do -- @[] + -- get the text indented under Valid refinement hole fits + refinementSection <- + getIndentedGroupsBy (=~ t " *Valid refinement hole fits include") mm + case refinementSection of + [] -> error "GHC provided invalid hole fit options" + (_:refinementSection) -> do + -- get the text for each hole fit + holeFitLines <- getIndentedGroups refinementSection + let holeFit = T.strip $ T.unwords holeFitLines + guard $ not $ holeFit =~ t "Some refinement hole fits suppressed" + return holeFit + + mapHead f (a:aa) = f a : aa + mapHead _ [] = [] + +-- > getIndentedGroups [" H1", " l1", " l2", " H2", " l3"] = [[" H1,", " l1", " l2"], [" H2", " l3"]] +getIndentedGroups :: [T.Text] -> [[T.Text]] +getIndentedGroups [] = [] +getIndentedGroups ll@(l:_) = getIndentedGroupsBy ((== indentation l) . indentation) ll +-- | +-- > getIndentedGroupsBy (" H" `isPrefixOf`) [" H1", " l1", " l2", " H2", " l3"] = [[" H1", " l1", " l2"], [" H2", " l3"]] +getIndentedGroupsBy :: (T.Text -> Bool) -> [T.Text] -> [[T.Text]] +getIndentedGroupsBy pred inp = case dropWhile (not.pred) inp of + (l:ll) -> case span (\l' -> indentation l < indentation l') ll of + (indented, rest) -> (l:indented) : getIndentedGroupsBy pred rest + _ -> [] + +indentation :: T.Text -> Int +indentation = T.length . T.takeWhile isSpace + diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/FillTypeWildcard.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/FillTypeWildcard.hs new file mode 100644 index 0000000000..587ac1e133 --- /dev/null +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/FillTypeWildcard.hs @@ -0,0 +1,78 @@ +module Development.IDE.Plugin.Plugins.FillTypeWildcard + ( suggestFillTypeWildcard + ) where + +import Data.Char +import qualified Data.Text as T +import Language.LSP.Types (Diagnostic (..), TextEdit (TextEdit)) + +suggestFillTypeWildcard :: Diagnostic -> [(T.Text, TextEdit)] +suggestFillTypeWildcard Diagnostic{_range=_range,..} +-- Foo.hs:3:8: error: +-- * Found type wildcard `_' standing for `p -> p1 -> p' + | "Found type wildcard" `T.isInfixOf` _message + , " standing for " `T.isInfixOf` _message + , typeSignature <- extractWildCardTypeSignature _message + = [("Use type signature: ‘" <> typeSignature <> "’", TextEdit _range typeSignature)] + | otherwise = [] + +-- | Extract the type and surround it in parentheses except in obviously safe cases. +-- +-- Inferring when parentheses are actually needed around the type signature would +-- require understanding both the precedence of the context of the hole and of +-- the signature itself. Inserting them (almost) unconditionally is ugly but safe. +extractWildCardTypeSignature :: T.Text -> T.Text +extractWildCardTypeSignature msg + | enclosed || not isApp || isToplevelSig = sig + | otherwise = "(" <> sig <> ")" + where + msgSigPart = snd $ T.breakOnEnd "standing for " msg + (sig, rest) = T.span (/='’') . T.dropWhile (=='‘') . T.dropWhile (/='‘') $ msgSigPart + -- If we're completing something like ‘foo :: _’ parens can be safely omitted. + isToplevelSig = errorMessageRefersToToplevelHole rest + -- Parenthesize type applications, e.g. (Maybe Char). + isApp = T.any isSpace sig + -- Do not add extra parentheses to lists, tuples and already parenthesized types. + enclosed = + case T.uncons sig of + Nothing -> error "GHC provided invalid type" + Just (firstChr, _) -> not (T.null sig) && (firstChr, T.last sig) `elem` [('(', ')'), ('[', ']')] + +-- | Detect whether user wrote something like @foo :: _@ or @foo :: (_, Int)@. +-- The former is considered toplevel case for which the function returns 'True', +-- the latter is not toplevel and the returned value is 'False'. +-- +-- When type hole is at toplevel then there’s a line starting with +-- "• In the type signature" which ends with " :: _" like in the +-- following snippet: +-- +-- source/library/Language/Haskell/Brittany/Internal.hs:131:13: error: +-- • Found type wildcard ‘_’ standing for ‘HsDecl GhcPs’ +-- To use the inferred type, enable PartialTypeSignatures +-- • In the type signature: decl :: _ +-- In an equation for ‘splitAnnots’: +-- splitAnnots m@HsModule {hsmodAnn, hsmodDecls} +-- = undefined +-- where +-- ann :: SrcSpanAnnA +-- decl :: _ +-- L ann decl = head hsmodDecls +-- • Relevant bindings include +-- [REDACTED] +-- +-- When type hole is not at toplevel there’s a stack of where +-- the hole was located ending with "In the type signature": +-- +-- source/library/Language/Haskell/Brittany/Internal.hs:130:20: error: +-- • Found type wildcard ‘_’ standing for ‘GhcPs’ +-- To use the inferred type, enable PartialTypeSignatures +-- • In the first argument of ‘HsDecl’, namely ‘_’ +-- In the type ‘HsDecl _’ +-- In the type signature: decl :: HsDecl _ +-- • Relevant bindings include +-- [REDACTED] +errorMessageRefersToToplevelHole :: T.Text -> Bool +errorMessageRefersToToplevelHole msg = + not (T.null prefix) && " :: _" `T.isSuffixOf` T.takeWhile (/= '\n') rest + where + (prefix, rest) = T.breakOn "• In the type signature:" msg diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/ImportUtils.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/ImportUtils.hs new file mode 100644 index 0000000000..81014c0180 --- /dev/null +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/Plugins/ImportUtils.hs @@ -0,0 +1,83 @@ +module Development.IDE.Plugin.Plugins.ImportUtils + ( ImportStyle(..), + quickFixImportKind', + quickFixImportKind, + renderImportStyle, + unImportStyle, + importStyles + ) where + +import Data.List.NonEmpty (NonEmpty ((:|))) +import qualified Data.Text as T +import Development.IDE.Plugin.CodeAction.ExactPrint (wildCardSymbol) +import Development.IDE.Types.Exports (IdentInfo (..)) +import Language.LSP.Types (CodeActionKind (..)) + +-- | Possible import styles for an 'IdentInfo'. +-- +-- The first 'Text' parameter corresponds to the 'rendered' field of the +-- 'IdentInfo'. +data ImportStyle + = ImportTopLevel T.Text + -- ^ Import a top-level export from a module, e.g., a function, a type, a + -- class. + -- + -- > import M (?) + -- + -- Some exports that have a parent, like a type-class method or an + -- associated type/data family, can still be imported as a top-level + -- import. + -- + -- Note that this is not the case for constructors, they must always be + -- imported as part of their parent data type. + + | ImportViaParent T.Text T.Text + -- ^ Import an export (first parameter) through its parent (second + -- parameter). + -- + -- import M (P(?)) + -- + -- @P@ and @?@ can be a data type and a constructor, a class and a method, + -- a class and an associated type/data family, etc. + + | ImportAllConstructors T.Text + -- ^ Import all constructors for a specific data type. + -- + -- import M (P(..)) + -- + -- @P@ can be a data type or a class. + deriving Show + +importStyles :: IdentInfo -> NonEmpty ImportStyle +importStyles IdentInfo {parent, rendered, isDatacon} + | Just p <- parent + -- Constructors always have to be imported via their parent data type, but + -- methods and associated type/data families can also be imported as + -- top-level exports. + = ImportViaParent rendered p + :| [ImportTopLevel rendered | not isDatacon] + <> [ImportAllConstructors p] + | otherwise + = ImportTopLevel rendered :| [] + +-- | Used for adding new imports +renderImportStyle :: ImportStyle -> T.Text +renderImportStyle (ImportTopLevel x) = x +renderImportStyle (ImportViaParent x p@(T.uncons -> Just ('(', _))) = "type " <> p <> "(" <> x <> ")" +renderImportStyle (ImportViaParent x p) = p <> "(" <> x <> ")" +renderImportStyle (ImportAllConstructors p) = p <> "(..)" + +-- | Used for extending import lists +unImportStyle :: ImportStyle -> (Maybe String, String) +unImportStyle (ImportTopLevel x) = (Nothing, T.unpack x) +unImportStyle (ImportViaParent x y) = (Just $ T.unpack y, T.unpack x) +unImportStyle (ImportAllConstructors x) = (Just $ T.unpack x, wildCardSymbol) + + +quickFixImportKind' :: T.Text -> ImportStyle -> CodeActionKind +quickFixImportKind' x (ImportTopLevel _) = CodeActionUnknown $ "quickfix.import." <> x <> ".list.topLevel" +quickFixImportKind' x (ImportViaParent _ _) = CodeActionUnknown $ "quickfix.import." <> x <> ".list.withParent" +quickFixImportKind' x (ImportAllConstructors _) = CodeActionUnknown $ "quickfix.import." <> x <> ".list.allConstructors" + +quickFixImportKind :: T.Text -> CodeActionKind +quickFixImportKind x = CodeActionUnknown $ "quickfix.import." <> x From d7690c500f204ff3804b1ec7af70a6194c4a9908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Dybiec?= Date: Fri, 25 Nov 2022 12:01:54 +0000 Subject: [PATCH 200/213] Add suggestions about licenses in cabal file (#3261) * Suggest licenses in cabal files * Fix testsuite for license suggestions * Apply suggestions from code review Co-authored-by: Michael Peyton Jones Co-authored-by: Fendor Co-authored-by: fendor --- ghcide/src/Text/Fuzzy/Parallel.hs | 4 +- .../hls-cabal-plugin/hls-cabal-plugin.cabal | 1 + .../hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 3 +- .../src/Ide/Plugin/Cabal/LicenseSuggest.hs | 59 ++++++++++++------- plugins/hls-cabal-plugin/test/Main.hs | 47 ++++++++++++--- .../test/testdata/licenseCodeAction2.cabal | 8 +++ 6 files changed, 89 insertions(+), 33 deletions(-) create mode 100644 plugins/hls-cabal-plugin/test/testdata/licenseCodeAction2.cabal diff --git a/ghcide/src/Text/Fuzzy/Parallel.hs b/ghcide/src/Text/Fuzzy/Parallel.hs index 6d822e7538..0137861468 100644 --- a/ghcide/src/Text/Fuzzy/Parallel.hs +++ b/ghcide/src/Text/Fuzzy/Parallel.hs @@ -91,8 +91,8 @@ filter chunkSize maxRes pattern ts extract = partialSortByAscScore maxRes perfec -- match against the pattern. Runs with default settings where -- nothing is added around the matches, as case insensitive. -- --- >>> simpleFilter "vm" ["vim", "emacs", "virtual machine"] --- ["vim","virtual machine"] +-- >>> simpleFilter 1000 10 "vm" ["vim", "emacs", "virtual machine"] +-- [Scored {score = 4, original = "vim"},Scored {score = 4, original = "virtual machine"}] {-# INLINABLE simpleFilter #-} simpleFilter :: Int -- ^ Chunk size. 1000 works well. -> Int -- ^ Max. number of results wanted diff --git a/plugins/hls-cabal-plugin/hls-cabal-plugin.cabal b/plugins/hls-cabal-plugin/hls-cabal-plugin.cabal index 67170c10ab..f202b633c3 100644 --- a/plugins/hls-cabal-plugin/hls-cabal-plugin.cabal +++ b/plugins/hls-cabal-plugin/hls-cabal-plugin.cabal @@ -45,6 +45,7 @@ library -- This is a lot of work for almost zero benefit, so we just allow more versions here -- and we eventually completely drop support for building HLS with stack. , Cabal ^>=3.2 || ^>=3.4 || ^>=3.6 || ^>= 3.8 + , Cabal-syntax ^>= 3.6 , deepseq , directory , extra >=1.7.4 diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index 72a16c8ea6..913cb37ed6 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -21,7 +21,6 @@ import Data.Hashable import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HashMap import qualified Data.List.NonEmpty as NE -import Data.Maybe (mapMaybe) import qualified Data.Text.Encoding as Encoding import Data.Typeable import Development.IDE as D @@ -184,7 +183,7 @@ licenseSuggestCodeAction -> CodeActionParams -> LspM Config (Either ResponseError (ResponseResult 'TextDocumentCodeAction)) licenseSuggestCodeAction _ _ (CodeActionParams _ _ (TextDocumentIdentifier uri) _range CodeActionContext{_diagnostics=List diags}) = - pure $ Right $ List $ mapMaybe (fmap InR . LicenseSuggest.licenseErrorAction uri) diags + pure $ Right $ List $ diags >>= (fmap InR . (LicenseSuggest.licenseErrorAction uri)) -- ---------------------------------------------------------------- -- Cabal file of Interest rules and global variable diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/LicenseSuggest.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/LicenseSuggest.hs index 2381286c95..6165cfd135 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/LicenseSuggest.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/LicenseSuggest.hs @@ -12,16 +12,21 @@ module Ide.Plugin.Cabal.LicenseSuggest ) where -import qualified Data.HashMap.Strict as Map -import qualified Data.Text as T -import Language.LSP.Types (CodeAction (CodeAction), - CodeActionKind (CodeActionQuickFix), - Diagnostic (..), List (List), - Position (Position), Range (Range), - TextEdit (TextEdit), Uri, - WorkspaceEdit (WorkspaceEdit)) +import qualified Data.HashMap.Strict as Map +import qualified Data.Text as T +import Language.LSP.Types (CodeAction (CodeAction), + CodeActionKind (CodeActionQuickFix), + Diagnostic (..), List (List), + Position (Position), + Range (Range), + TextEdit (TextEdit), Uri, + WorkspaceEdit (WorkspaceEdit)) import Text.Regex.TDFA +import qualified Data.List as List +import Distribution.SPDX.LicenseId (licenseId) +import qualified Text.Fuzzy.Parallel as Fuzzy + -- | Given a diagnostic returned by 'Ide.Plugin.Cabal.Diag.errorDiagnostic', -- if it represents an "Unknown SPDX license identifier"-error along -- with a suggestion, then return a 'CodeAction' for replacing the @@ -31,7 +36,7 @@ licenseErrorAction -- ^ File for which the diagnostic was generated -> Diagnostic -- ^ Output of 'Ide.Plugin.Cabal.Diag.errorDiagnostic' - -> Maybe CodeAction + -> [CodeAction] licenseErrorAction uri diag = mkCodeAction <$> licenseErrorSuggestion (_message diag) where @@ -52,22 +57,32 @@ licenseErrorAction uri diag = edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing Nothing in CodeAction title (Just CodeActionQuickFix) (Just $ List []) Nothing Nothing (Just edit) Nothing Nothing --- | Given an error message returned by 'Ide.Plugin.Cabal.Diag.errorDiagnostic', --- if it represents an "Unknown SPDX license identifier"-error along --- with a suggestion then return the suggestion (after the "Do you mean"-text) --- along with the incorrect identifier. -licenseErrorSuggestion - :: T.Text +-- | License name of every license supported by cabal +licenseNames :: [T.Text] +licenseNames = map (T.pack . licenseId) [minBound .. maxBound] + +-- | Given a diagnostic returned by 'Ide.Plugin.Cabal.Diag.errorDiagnostic', +-- provide possible corrections for SPDX license identifiers +-- based on the list specified in Cabal. +-- Results are sorted by best fit, and prefer solutions that have smaller +-- length distance to the original word. +-- +-- >>> take 2 $ licenseErrorSuggestion (T.pack "Unknown SPDX license identifier: 'BSD3'") +-- [("BSD3","BSD-3-Clause"),("BSD3","BSD-3-Clause-LBNL")] +licenseErrorSuggestion :: + T.Text -- ^ Output of 'Ide.Plugin.Cabal.Diag.errorDiagnostic' - -> Maybe (T.Text, T.Text) + -> [(T.Text, T.Text)] -- ^ (Original (incorrect) license identifier, suggested replacement) -licenseErrorSuggestion message = - mSuggestion message >>= \case - [original, suggestion] -> Just (original, suggestion) - _ -> Nothing +licenseErrorSuggestion msg = + (getMatch <$> msg =~~ regex) >>= \case + [original] -> + let matches = map Fuzzy.original $ Fuzzy.simpleFilter 1000 10 original licenseNames + in [(original,candidate) | candidate <- List.sortBy (lengthDistance original) matches] + _ -> [] where regex :: T.Text - regex = "Unknown SPDX license identifier: '(.*)' Do you mean (.*)\\?" - mSuggestion msg = getMatch <$> (msg :: T.Text) =~~ regex + regex = "Unknown SPDX license identifier: '(.*)'" getMatch :: (T.Text, T.Text, T.Text, [T.Text]) -> [T.Text] getMatch (_, _, _, results) = results + lengthDistance original x1 x2 = abs (T.length original - T.length x1) `compare` abs (T.length original - T.length x2) diff --git a/plugins/hls-cabal-plugin/test/Main.hs b/plugins/hls-cabal-plugin/test/Main.hs index b2db2f4315..9fb01274b6 100644 --- a/plugins/hls-cabal-plugin/test/Main.hs +++ b/plugins/hls-cabal-plugin/test/Main.hs @@ -1,11 +1,14 @@ -{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -Wno-orphans #-} -{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE DisambiguateRecordFields #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE TypeOperators #-} module Main ( main ) where import Control.Lens ((^.)) +import Control.Monad (guard) import qualified Data.ByteString as BS import Data.Either (isRight) import Data.Function @@ -70,14 +73,16 @@ codeActionUnitTests :: TestTree codeActionUnitTests = testGroup "Code Action Tests" [ testCase "Unknown format" $ do -- the message has the wrong format - licenseErrorSuggestion "Unknown license identifier: 'BSD3' Do you mean BSD-3-Clause?" @?= Nothing, + licenseErrorSuggestion "Unknown license identifier: 'BSD3' Do you mean BSD-3-Clause?" @?= [], testCase "BSD-3-Clause" $ do - licenseErrorSuggestion "Unknown SPDX license identifier: 'BSD3' Do you mean BSD-3-Clause?" @?= Just ("BSD3", "BSD-3-Clause"), + take 2 (licenseErrorSuggestion "Unknown SPDX license identifier: 'BSD3' Do you mean BSD-3-Clause?") + @?= [("BSD3","BSD-3-Clause"),("BSD3","BSD-3-Clause-LBNL")], - testCase "MIT" $ do + testCase "MiT" $ do -- contains no suggestion - licenseErrorSuggestion "Unknown SPDX license identifier: 'MIT3'" @?= Nothing + take 2 (licenseErrorSuggestion "Unknown SPDX license identifier: 'MiT'") + @?= [("MiT","MIT"),("MiT","MIT-0")] ] -- ------------------------------------------------------------------------ @@ -137,7 +142,7 @@ pluginTests recorder = testGroup "Plugin Tests" length diags @?= 1 reduceDiag ^. J.range @?= Range (Position 3 24) (Position 4 0) reduceDiag ^. J.severity @?= Just DsError - [InR codeAction] <- getCodeActions doc (Range (Position 3 24) (Position 4 0)) + [codeAction] <- getLicenseAction "BSD-3-Clause" <$> getCodeActions doc (Range (Position 3 24) (Position 4 0)) executeCodeAction codeAction contents <- documentContents doc liftIO $ contents @?= Text.unlines @@ -150,8 +155,36 @@ pluginTests recorder = testGroup "Plugin Tests" , " build-depends: base" , " default-language: Haskell2010" ] + , runCabalTestCaseSession "Apache-2.0" recorder "" $ do + doc <- openDoc "licenseCodeAction2.cabal" "cabal" + diags <- waitForDiagnosticsFromSource doc "cabal" + -- test if it supports typos in license name, here 'apahe' + reduceDiag <- liftIO $ inspectDiagnostic diags ["Unknown SPDX license identifier: 'APAHE'"] + liftIO $ do + length diags @?= 1 + reduceDiag ^. J.range @?= Range (Position 3 25) (Position 4 0) + reduceDiag ^. J.severity @?= Just DsError + [codeAction] <- getLicenseAction "Apache-2.0" <$> getCodeActions doc (Range (Position 3 24) (Position 4 0)) + executeCodeAction codeAction + contents <- documentContents doc + liftIO $ contents @?= Text.unlines + [ "cabal-version: 3.0" + , "name: licenseCodeAction2" + , "version: 0.1.0.0" + , "license: Apache-2.0" + , "" + , "library" + , " build-depends: base" + , " default-language: Haskell2010" + ] ] ] + where + getLicenseAction :: Text.Text -> [Command |? CodeAction] -> [CodeAction] + getLicenseAction license codeActions = do + InR action@CodeAction{_title} <- codeActions + guard (_title=="Replace with " <> license) + pure action -- ------------------------------------------------------------------------ -- Runner utils diff --git a/plugins/hls-cabal-plugin/test/testdata/licenseCodeAction2.cabal b/plugins/hls-cabal-plugin/test/testdata/licenseCodeAction2.cabal new file mode 100644 index 0000000000..6f8a886ba1 --- /dev/null +++ b/plugins/hls-cabal-plugin/test/testdata/licenseCodeAction2.cabal @@ -0,0 +1,8 @@ +cabal-version: 3.0 +name: licenseCodeAction2 +version: 0.1.0.0 +license: APAHE + +library + build-depends: base + default-language: Haskell2010 From 9e5fc880d1bcf45bdcda1160a1f2a67408bbf410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Berk=20=C3=96zk=C3=BCt=C3=BCk?= Date: Sat, 26 Nov 2022 17:05:31 +0100 Subject: [PATCH 201/213] Add `RangeMap` for unified "in-range" filtering (#3343) * Move RangeMap under hls-plugin-api and add benchmark * Add some documentation * Rename benchmark case * Make explicit fields plugin work with RealSrcSpans * Add test to ensure both filtering approaches are identical * Add some coverage checks (for reference) * Remove outdated comment * Add use-fingertree flag to hls-plugin-api * Add more instances to RangeMap * Extend test generators to cover multiline ranges * Add more documentation --- hls-plugin-api/bench/Main.hs | 56 ++++++ hls-plugin-api/hls-plugin-api.cabal | 34 ++++ hls-plugin-api/src/Ide/Plugin/RangeMap.hs | 83 ++++++++ hls-plugin-api/test/Ide/PluginUtilsTest.hs | 59 +++++- .../src/Ide/Plugin/AlternateNumberFormat.hs | 20 +- .../test/Main.hs | 2 +- .../hls-explicit-record-fields-plugin.cabal | 1 - .../src/Ide/Plugin/ExplicitFields.hs | 178 ++++++++---------- 8 files changed, 312 insertions(+), 121 deletions(-) create mode 100644 hls-plugin-api/bench/Main.hs create mode 100644 hls-plugin-api/src/Ide/Plugin/RangeMap.hs diff --git a/hls-plugin-api/bench/Main.hs b/hls-plugin-api/bench/Main.hs new file mode 100644 index 0000000000..0fc64f49f1 --- /dev/null +++ b/hls-plugin-api/bench/Main.hs @@ -0,0 +1,56 @@ +-- A benchmark comparing the performance characteristics of list-based +-- vs RangeMap-based "in-range filtering" approaches +module Main (main) where + +import Control.DeepSeq (force) +import Control.Exception (evaluate) +import Control.Monad (replicateM) +import qualified Criterion +import qualified Criterion.Main +import Data.Random (RVar) +import qualified Data.Random as Fu +import qualified Ide.Plugin.RangeMap as RangeMap +import Language.LSP.Types (Position (..), Range (..), UInt, + isSubrangeOf) +import qualified System.Random.Stateful as Random + + +genRangeList :: Int -> RVar [Range] +genRangeList n = replicateM n genRange + +genRange :: RVar Range +genRange = do + x1 <- genPosition + delta <- genRangeLength + let x2 = x1 { _character = _character x1 + delta } + pure $ Range x1 x2 + where + genRangeLength :: RVar UInt + genRangeLength = fromInteger <$> Fu.uniform 5 50 + +genPosition :: RVar Position +genPosition = Position + <$> (fromInteger <$> Fu.uniform 0 10000) + <*> (fromInteger <$> Fu.uniform 0 150) + +filterRangeList :: Range -> [Range] -> [Range] +filterRangeList r = filter (isSubrangeOf r) + +main :: IO () +main = do + rangeLists@[rangeList100, rangeList1000, rangeList10000] + <- traverse (Fu.sampleFrom Random.globalStdGen . genRangeList) [100, 1000, 10000] + [rangeMap100, rangeMap1000, rangeMap10000] <- evaluate $ force $ map (RangeMap.fromList id) rangeLists + targetRange <- Fu.sampleFrom Random.globalStdGen genRange + Criterion.Main.defaultMain + [ Criterion.bgroup "List" + [ Criterion.bench "Size 100" $ Criterion.nf (filterRangeList targetRange) rangeList100 + , Criterion.bench "Size 1000" $ Criterion.nf (filterRangeList targetRange) rangeList1000 + , Criterion.bench "Size 10000" $ Criterion.nf (filterRangeList targetRange) rangeList10000 + ] + , Criterion.bgroup "RangeMap" + [ Criterion.bench "Size 100" $ Criterion.nf (RangeMap.filterByRange targetRange) rangeMap100 + , Criterion.bench "Size 1000" $ Criterion.nf (RangeMap.filterByRange targetRange) rangeMap1000 + , Criterion.bench "Size 10000" $ Criterion.nf (RangeMap.filterByRange targetRange) rangeMap10000 + ] + ] diff --git a/hls-plugin-api/hls-plugin-api.cabal b/hls-plugin-api/hls-plugin-api.cabal index 217e7ae30f..239c7a092b 100644 --- a/hls-plugin-api/hls-plugin-api.cabal +++ b/hls-plugin-api/hls-plugin-api.cabal @@ -20,6 +20,14 @@ flag pedantic default: False manual: True +-- This flag can be used to avoid the dependency on hw-fingertree. +-- We can set this temporarily if we have problems building hw-fingertree +-- for a new version of GHC. +flag use-fingertree + description: Use fingertree implementation of RangeMap + default: True + manual: False + source-repository head type: git location: https://github.com/haskell/haskell-language-server @@ -29,6 +37,7 @@ library Ide.Plugin.Config Ide.Plugin.ConfigUtils Ide.Plugin.Properties + Ide.Plugin.RangeMap Ide.PluginUtils Ide.Types @@ -73,6 +82,10 @@ library if impl(ghc >= 9) ghc-options: -Wunused-packages + if flag(use-fingertree) + cpp-options: -DUSE_FINGERTREE + build-depends: hw-fingertree + default-language: Haskell2010 default-extensions: DataKinds @@ -92,5 +105,26 @@ test-suite tests , tasty , tasty-hunit , tasty-rerun + , tasty-quickcheck , text , lsp-types + , containers + +benchmark rangemap-benchmark + -- Benchmark doesn't make sense if fingertree implementation + -- is not used. + if !flag(use-fingertree) + buildable: False + type: exitcode-stdio-1.0 + default-language: Haskell2010 + hs-source-dirs: bench + main-is: Main.hs + ghc-options: -threaded -Wall + build-depends: + base + , hls-plugin-api + , lsp-types + , criterion + , random + , random-fu + , deepseq diff --git a/hls-plugin-api/src/Ide/Plugin/RangeMap.hs b/hls-plugin-api/src/Ide/Plugin/RangeMap.hs new file mode 100644 index 0000000000..461e0af432 --- /dev/null +++ b/hls-plugin-api/src/Ide/Plugin/RangeMap.hs @@ -0,0 +1,83 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveFoldable #-} +{-# LANGUAGE DeriveFunctor #-} +{-# LANGUAGE DeriveTraversable #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} + +-- | A map that allows fast \"in-range\" filtering. 'RangeMap' is meant +-- to be constructed once and cached as part of a Shake rule. If +-- not, the map will be rebuilt upon each invocation, yielding slower +-- results compared to the list-based approach! +-- +-- Note that 'RangeMap' falls back to the list-based approach if +-- `use-fingertree` flag of `hls-plugin-api` is set to false. +module Ide.Plugin.RangeMap + ( RangeMap(..), + fromList, + fromList', + filterByRange, + ) where + +import Data.Bifunctor (first) +import Data.Foldable (foldl') +import Development.IDE.Graph.Classes (NFData) +import Language.LSP.Types (Position, + Range (Range), + isSubrangeOf) +#ifdef USE_FINGERTREE +import qualified HaskellWorks.Data.IntervalMap.FingerTree as IM +#endif + +-- | A map from code ranges to values. +#ifdef USE_FINGERTREE +newtype RangeMap a = RangeMap + { unRangeMap :: IM.IntervalMap Position a + -- ^ 'IM.Interval' of 'Position' corresponds to a 'Range' + } + deriving newtype (NFData, Semigroup, Monoid) + deriving stock (Functor, Foldable, Traversable) +#else +newtype RangeMap a = RangeMap + { unRangeMap :: [(Range, a)] } + deriving newtype (NFData, Semigroup, Monoid) + deriving stock (Functor, Foldable, Traversable) +#endif + +-- | Construct a 'RangeMap' from a 'Range' accessor and a list of values. +fromList :: (a -> Range) -> [a] -> RangeMap a +fromList extractRange = fromList' . map (\x -> (extractRange x, x)) + +fromList' :: [(Range, a)] -> RangeMap a +#ifdef USE_FINGERTREE +fromList' = RangeMap . toIntervalMap . map (first rangeToInterval) + where + toIntervalMap :: Ord v => [(IM.Interval v, a)] -> IM.IntervalMap v a + toIntervalMap = foldl' (\m (i, v) -> IM.insert i v m) IM.empty +#else +fromList' = RangeMap +#endif + +-- | Filter a 'RangeMap' by a given 'Range'. +filterByRange :: Range -> RangeMap a -> [a] +#ifdef USE_FINGERTREE +filterByRange range = map snd . IM.dominators (rangeToInterval range) . unRangeMap +#else +filterByRange range = map snd . filter (isSubrangeOf range . fst) . unRangeMap +#endif + +#ifdef USE_FINGERTREE +-- NOTE(ozkutuk): In itself, this conversion is wrong. As Michael put it: +-- "LSP Ranges have exclusive upper bounds, whereas the intervals here are +-- supposed to be closed (i.e. inclusive at both ends)" +-- However, in our use-case this turns out not to be an issue (supported +-- by the accompanying property test). I think the reason for this is, +-- even if rangeToInterval isn't a correct 1:1 conversion by itself, it +-- is used for both the construction of the RangeMap and during the actual +-- filtering (filterByRange), so it still behaves identical to the list +-- approach. +-- This definition isn't exported from the module, therefore we need not +-- worry about other uses where it potentially makes a difference. +rangeToInterval :: Range -> IM.Interval Position +rangeToInterval (Range s e) = IM.Interval s e +#endif diff --git a/hls-plugin-api/test/Ide/PluginUtilsTest.hs b/hls-plugin-api/test/Ide/PluginUtilsTest.hs index bad3c1dfbc..f08821cd50 100644 --- a/hls-plugin-api/test/Ide/PluginUtilsTest.hs +++ b/hls-plugin-api/test/Ide/PluginUtilsTest.hs @@ -1,19 +1,27 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeApplications #-} module Ide.PluginUtilsTest ( tests ) where -import Data.Char (isPrint) -import qualified Data.Text as T -import Ide.PluginUtils (positionInRange, unescape) -import Language.LSP.Types (Position (Position), Range (Range)) +import Data.Char (isPrint) +import qualified Data.Set as Set +import qualified Data.Text as T +import qualified Ide.Plugin.RangeMap as RangeMap +import Ide.PluginUtils (positionInRange, unescape) +import Language.LSP.Types (Position (..), Range (Range), UInt, + isSubrangeOf) import Test.Tasty import Test.Tasty.HUnit +import Test.Tasty.QuickCheck tests :: TestTree tests = testGroup "PluginUtils" [ unescapeTest + , localOption (QuickCheckMaxSize 10000) $ + testProperty "RangeMap-List filtering identical" $ + prop_rangemapListEq @Int ] unescapeTest :: TestTree @@ -33,3 +41,46 @@ unescapeTest = testGroup "unescape" , testCase "control characters should not be escaped" $ unescape "\"\\n\\t\"" @?= "\"\\n\\t\"" ] + +genRange :: Gen Range +genRange = oneof [ genRangeInline, genRangeMultiline ] + +genRangeInline :: Gen Range +genRangeInline = do + x1 <- genPosition + delta <- genRangeLength + let x2 = x1 { _character = _character x1 + delta } + pure $ Range x1 x2 + where + genRangeLength :: Gen UInt + genRangeLength = fromInteger <$> chooseInteger (5, 50) + +genRangeMultiline :: Gen Range +genRangeMultiline = do + x1 <- genPosition + let heightDelta = 1 + secondX <- genSecond + let x2 = x1 { _line = _line x1 + heightDelta + , _character = secondX + } + pure $ Range x1 x2 + where + genSecond :: Gen UInt + genSecond = fromInteger <$> chooseInteger (0, 10) + +genPosition :: Gen Position +genPosition = Position + <$> (fromInteger <$> chooseInteger (0, 1000)) + <*> (fromInteger <$> chooseInteger (0, 150)) + +instance Arbitrary Range where + arbitrary = genRange + +prop_rangemapListEq :: (Show a, Eq a, Ord a) => Range -> [(Range, a)] -> Property +prop_rangemapListEq r xs = + let filteredList = (map snd . filter (isSubrangeOf r . fst)) xs + filteredRangeMap = RangeMap.filterByRange r (RangeMap.fromList' xs) + in classify (null filteredList) "no matches" $ + cover 5 (length filteredList == 1) "1 match" $ + cover 2 (length filteredList > 1) ">1 matches" $ + Set.fromList filteredList === Set.fromList filteredRangeMap diff --git a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs index f2961d452a..364179a4d5 100644 --- a/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs +++ b/plugins/hls-alternate-number-format-plugin/src/Ide/Plugin/AlternateNumberFormat.hs @@ -25,6 +25,8 @@ import Ide.Plugin.Conversion (AlternateFormat, ExtensionNeeded (NeedsExtension, NoExtension), alternateFormat) import Ide.Plugin.Literals +import Ide.Plugin.RangeMap (RangeMap) +import qualified Ide.Plugin.RangeMap as RangeMap import Ide.PluginUtils (getNormalizedFilePath, handleMaybeM, pluginResponse) import Ide.Types @@ -52,7 +54,7 @@ instance NFData CollectLiterals type instance RuleResult CollectLiterals = CollectLiteralsResult data CollectLiteralsResult = CLR - { literals :: [Literal] + { literals :: RangeMap Literal , enabledExtensions :: [GhcExtension] } deriving (Generic) @@ -73,7 +75,8 @@ collectLiteralsRule recorder = define (cmapWithPrio LogShake recorder) $ \Collec let exts = map GhcExtension . getExtensions <$> pm -- collect all the literals for a file lits = collectLiterals . pm_parsed_source <$> pm - pure ([], CLR <$> lits <*> exts) + litMap = RangeMap.fromList (realSrcSpanToRange . getSrcSpan) <$> lits + pure ([], CLR <$> litMap <*> exts) codeActionHandler :: PluginMethodHandler IdeState 'TextDocumentCodeAction codeActionHandler state pId (CodeActionParams _ _ docId currRange _) = pluginResponse $ do @@ -81,17 +84,13 @@ codeActionHandler state pId (CodeActionParams _ _ docId currRange _) = pluginRes CLR{..} <- requestLiterals pId state nfp pragma <- getFirstPragma pId state nfp -- remove any invalid literals (see validTarget comment) - let litsInRange = filter inCurrentRange literals + let litsInRange = RangeMap.filterByRange currRange literals -- generate alternateFormats and zip with the literal that generated the alternates literalPairs = map (\lit -> (lit, alternateFormat lit)) litsInRange -- make a code action for every literal and its' alternates (then flatten the result) actions = concatMap (\(lit, alts) -> map (mkCodeAction nfp lit enabledExtensions pragma) alts) literalPairs pure $ List actions where - inCurrentRange :: Literal -> Bool - inCurrentRange lit = let srcSpan = getSrcSpan lit - in currRange `contains` srcSpan - mkCodeAction :: NormalizedFilePath -> Literal -> [GhcExtension] -> NextPragmaInfo -> AlternateFormat -> Command |? CodeAction mkCodeAction nfp lit enabled npi af@(alt, ext) = InR CodeAction { _title = mkCodeActionTitle lit af enabled @@ -127,13 +126,6 @@ mkCodeActionTitle lit (alt, ext) ghcExts needsExtension :: Extension -> [GhcExtension] -> Bool needsExtension ext ghcExts = ext `notElem` map unExt ghcExts --- from HaddockComments.hs -contains :: Range -> RealSrcSpan -> Bool -contains Range {_start, _end} x = isInsideRealSrcSpan _start x || isInsideRealSrcSpan _end x - -isInsideRealSrcSpan :: Position -> RealSrcSpan -> Bool -p `isInsideRealSrcSpan` r = let (Range sp ep) = realSrcSpanToRange r in sp <= p && p <= ep - requestLiterals :: MonadIO m => PluginId -> IdeState -> NormalizedFilePath -> ExceptT String m CollectLiteralsResult requestLiterals (PluginId pId) state = handleMaybeM "Could not Collect Literals" . liftIO diff --git a/plugins/hls-alternate-number-format-plugin/test/Main.hs b/plugins/hls-alternate-number-format-plugin/test/Main.hs index c71fffb9e8..d6b19d4e7b 100644 --- a/plugins/hls-alternate-number-format-plugin/test/Main.hs +++ b/plugins/hls-alternate-number-format-plugin/test/Main.hs @@ -29,7 +29,7 @@ test :: TestTree test = testGroup "alternateNumberFormat" [ codeActionHex "TIntDtoH" 3 13 , codeActionOctal "TIntDtoO" 3 13 - , codeActionBinary "TIntDtoB" 4 12 + , codeActionBinary "TIntDtoB" 4 13 , codeActionNumDecimal "TIntDtoND" 5 13 , codeActionFracExp "TFracDtoE" 3 13 , codeActionFloatHex "TFracDtoHF" 4 13 diff --git a/plugins/hls-explicit-record-fields-plugin/hls-explicit-record-fields-plugin.cabal b/plugins/hls-explicit-record-fields-plugin/hls-explicit-record-fields-plugin.cabal index 2af84b89fb..92a4e1cf5a 100644 --- a/plugins/hls-explicit-record-fields-plugin/hls-explicit-record-fields-plugin.cabal +++ b/plugins/hls-explicit-record-fields-plugin/hls-explicit-record-fields-plugin.cabal @@ -38,7 +38,6 @@ library , transformers , ghc-boot-th , unordered-containers - , hw-fingertree hs-source-dirs: src default-language: Haskell2010 diff --git a/plugins/hls-explicit-record-fields-plugin/src/Ide/Plugin/ExplicitFields.hs b/plugins/hls-explicit-record-fields-plugin/src/Ide/Plugin/ExplicitFields.hs index 1a32ae70bb..b77281f05a 100644 --- a/plugins/hls-explicit-record-fields-plugin/src/Ide/Plugin/ExplicitFields.hs +++ b/plugins/hls-explicit-record-fields-plugin/src/Ide/Plugin/ExplicitFields.hs @@ -4,7 +4,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TupleSections #-} +{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE ViewPatterns #-} @@ -13,77 +13,65 @@ module Ide.Plugin.ExplicitFields ( descriptor ) where -import Control.Lens ((^.)) -import Control.Monad.IO.Class (MonadIO, liftIO) -import Control.Monad.Trans.Except (ExceptT) -import Data.Foldable (foldl') -import Data.Generics (GenericQ, everything, - extQ, mkQ) -import qualified Data.HashMap.Strict as HashMap -import Data.Maybe (catMaybes, isJust, - mapMaybe, - maybeToList) -import Data.Text (Text) -import Development.IDE (IdeState, - NormalizedFilePath, - Pretty (..), - Range (..), - Recorder (..), Rules, - WithPriority (..), - srcSpanToRange) -import Development.IDE.Core.Rules (runAction) -import Development.IDE.Core.RuleTypes (TcModuleResult (..), - TypeCheck (..)) -import Development.IDE.Core.Shake (define, use) -import qualified Development.IDE.Core.Shake as Shake -import Development.IDE.GHC.Compat (HsConDetails (RecCon), - HsRecFields (..), - LPat, Outputable, - SrcSpan, getLoc, - unLoc) -import Development.IDE.GHC.Compat.Core (Extension (NamedFieldPuns), - GhcPass, - HsExpr (RecordCon, rcon_flds), - LHsExpr, Pass (..), - Pat (..), - conPatDetails, - hfbPun, hs_valds, - mapConPatDetail, - mapLoc) -import Development.IDE.GHC.Util (getExtensions, - printOutputable) -import Development.IDE.Graph (RuleResult) -import Development.IDE.Graph.Classes (Hashable, - NFData (rnf)) -import Development.IDE.Spans.Pragmas (NextPragmaInfo (..), - getFirstPragma, - insertNewPragma) -import Development.IDE.Types.Logger (Priority (..), - cmapWithPrio, - logWith, (<+>)) -import GHC.Generics (Generic) -import qualified HaskellWorks.Data.IntervalMap.FingerTree as IM -import Ide.PluginUtils (getNormalizedFilePath, - handleMaybeM, - pluginResponse) -import Ide.Types (PluginDescriptor (..), - PluginId (..), - PluginMethodHandler, - defaultPluginDescriptor, - mkPluginHandler) -import Language.LSP.Types (CodeAction (..), - CodeActionKind (CodeActionRefactorRewrite), - CodeActionParams (..), - Command, List (..), - Method (..), - Position, - SMethod (..), - TextEdit (..), - WorkspaceEdit (WorkspaceEdit), - fromNormalizedUri, - normalizedFilePathToUri, - type (|?) (InR)) -import qualified Language.LSP.Types.Lens as L +import Control.Lens ((^.)) +import Control.Monad.IO.Class (MonadIO, liftIO) +import Control.Monad.Trans.Except (ExceptT) +import Data.Generics (GenericQ, everything, extQ, + mkQ) +import qualified Data.HashMap.Strict as HashMap +import Data.Maybe (isJust, listToMaybe, + maybeToList) +import Data.Text (Text) +import Development.IDE (IdeState, NormalizedFilePath, + Pretty (..), Recorder (..), + Rules, WithPriority (..), + realSrcSpanToRange) +import Development.IDE.Core.Rules (runAction) +import Development.IDE.Core.RuleTypes (TcModuleResult (..), + TypeCheck (..)) +import Development.IDE.Core.Shake (define, use) +import qualified Development.IDE.Core.Shake as Shake +import Development.IDE.GHC.Compat (HsConDetails (RecCon), + HsRecFields (..), LPat, + Outputable, getLoc, unLoc) +import Development.IDE.GHC.Compat.Core (Extension (NamedFieldPuns), + GhcPass, + HsExpr (RecordCon, rcon_flds), + LHsExpr, Pass (..), Pat (..), + RealSrcSpan, conPatDetails, + hfbPun, hs_valds, + mapConPatDetail, mapLoc, + pattern RealSrcSpan) +import Development.IDE.GHC.Util (getExtensions, + printOutputable) +import Development.IDE.Graph (RuleResult) +import Development.IDE.Graph.Classes (Hashable, NFData (rnf)) +import Development.IDE.Spans.Pragmas (NextPragmaInfo (..), + getFirstPragma, + insertNewPragma) +import Development.IDE.Types.Logger (Priority (..), cmapWithPrio, + logWith, (<+>)) +import GHC.Generics (Generic) +import Ide.Plugin.RangeMap (RangeMap) +import qualified Ide.Plugin.RangeMap as RangeMap +import Ide.PluginUtils (getNormalizedFilePath, + handleMaybeM, pluginResponse) +import Ide.Types (PluginDescriptor (..), + PluginId (..), + PluginMethodHandler, + defaultPluginDescriptor, + mkPluginHandler) +import Language.LSP.Types (CodeAction (..), + CodeActionKind (CodeActionRefactorRewrite), + CodeActionParams (..), + Command, List (..), + Method (..), SMethod (..), + TextEdit (..), + WorkspaceEdit (WorkspaceEdit), + fromNormalizedUri, + normalizedFilePathToUri, + type (|?) (InR)) +import qualified Language.LSP.Types.Lens as L data Log @@ -108,7 +96,7 @@ codeActionProvider ideState pId (CodeActionParams _ _ docId range _) = pluginRes nfp <- getNormalizedFilePath (docId ^. L.uri) pragma <- getFirstPragma pId ideState nfp CRR recMap (map unExt -> exts) <- collectRecords' ideState nfp - let actions = map (mkCodeAction nfp exts pragma) (filterRecords range recMap) + let actions = map (mkCodeAction nfp exts pragma) (RangeMap.filterByRange range recMap) pure $ List actions where @@ -124,10 +112,10 @@ codeActionProvider ideState pId (CodeActionParams _ _ docId range _) = pluginRes , _xdata = Nothing } where - edits = catMaybes [ mkTextEdit rec , pragmaEdit ] + edits = mkTextEdit rec : maybeToList pragmaEdit - mkTextEdit :: RenderedRecordInfo -> Maybe TextEdit - mkTextEdit (RenderedRecordInfo ss r) = TextEdit <$> srcSpanToRange ss <*> pure r + mkTextEdit :: RenderedRecordInfo -> TextEdit + mkTextEdit (RenderedRecordInfo ss r) = TextEdit (realSrcSpanToRange ss) r pragmaEdit :: Maybe TextEdit pragmaEdit = if NamedFieldPuns `elem` exts @@ -154,7 +142,7 @@ collectRecordsRule recorder = define (cmapWithPrio LogShake recorder) $ \Collect recs = concat $ maybeToList (getRecords <$> tmr) logWith recorder Debug (LogCollectedRecords recs) let renderedRecs = traverse renderRecordInfo recs - recMap = buildIntervalMap <$> renderedRecs + recMap = RangeMap.fromList (realSrcSpanToRange . renderedSrcSpan) <$> renderedRecs logWith recorder Debug (LogRenderedRecords (concat renderedRecs)) pure ([], CRR <$> recMap <*> exts) where @@ -172,7 +160,7 @@ instance Hashable CollectRecords instance NFData CollectRecords data CollectRecordsResult = CRR - { recordInfos :: IM.IntervalMap Position RenderedRecordInfo + { recordInfos :: RangeMap RenderedRecordInfo , enabledExtensions :: [GhcExtension] } deriving (Generic) @@ -192,15 +180,15 @@ instance NFData GhcExtension where rnf x = x `seq` () data RecordInfo - = RecordInfoPat SrcSpan (Pat (GhcPass 'Renamed)) - | RecordInfoCon SrcSpan (HsExpr (GhcPass 'Renamed)) + = RecordInfoPat RealSrcSpan (Pat (GhcPass 'Renamed)) + | RecordInfoCon RealSrcSpan (HsExpr (GhcPass 'Renamed)) instance Pretty RecordInfo where pretty (RecordInfoPat ss p) = pretty (printOutputable ss) <> ":" <+> pretty (printOutputable p) pretty (RecordInfoCon ss e) = pretty (printOutputable ss) <> ":" <+> pretty (printOutputable e) data RenderedRecordInfo = RenderedRecordInfo - { renderedSrcSpan :: SrcSpan + { renderedSrcSpan :: RealSrcSpan , renderedRecord :: Text } deriving (Generic) @@ -252,18 +240,20 @@ collectRecords = everything (<>) (maybeToList . (Nothing `mkQ` getRecPatterns `e getRecCons :: LHsExpr (GhcPass 'Renamed) -> Maybe RecordInfo getRecCons e@(unLoc -> RecordCon _ _ flds) - | isJust (rec_dotdot flds) = Just $ mkRecInfo e + | isJust (rec_dotdot flds) = mkRecInfo e where - mkRecInfo :: LHsExpr (GhcPass 'Renamed) -> RecordInfo - mkRecInfo expr = RecordInfoCon (getLoc expr) (unLoc expr) + mkRecInfo :: LHsExpr (GhcPass 'Renamed) -> Maybe RecordInfo + mkRecInfo expr = listToMaybe + [ RecordInfoCon realSpan (unLoc expr) | RealSrcSpan realSpan _ <- [ getLoc expr ]] getRecCons _ = Nothing getRecPatterns :: LPat (GhcPass 'Renamed) -> Maybe RecordInfo getRecPatterns conPat@(conPatDetails . unLoc -> Just (RecCon flds)) - | isJust (rec_dotdot flds) = Just $ mkRecInfo conPat + | isJust (rec_dotdot flds) = mkRecInfo conPat where - mkRecInfo :: LPat (GhcPass 'Renamed) -> RecordInfo - mkRecInfo pat = RecordInfoPat (getLoc pat) (unLoc pat) + mkRecInfo :: LPat (GhcPass 'Renamed) -> Maybe RecordInfo + mkRecInfo pat = listToMaybe + [ RecordInfoPat realSpan (unLoc pat) | RealSrcSpan realSpan _ <- [ getLoc pat ]] getRecPatterns _ = Nothing collectRecords' :: MonadIO m => IdeState -> NormalizedFilePath -> ExceptT String m CollectRecordsResult @@ -273,17 +263,3 @@ collectRecords' ideState = . runAction "ExplicitFields" ideState . use CollectRecords -rangeToInterval :: Range -> IM.Interval Position -rangeToInterval (Range s e) = IM.Interval s e - -buildIntervalMap :: [RenderedRecordInfo] -> IM.IntervalMap Position RenderedRecordInfo -buildIntervalMap recs = toIntervalMap $ mapMaybe (\recInfo -> (,recInfo) <$> srcSpanToInterval (renderedSrcSpan recInfo)) recs - where - toIntervalMap :: Ord v => [(IM.Interval v, a)] -> IM.IntervalMap v a - toIntervalMap = foldl' (\m (i, v) -> IM.insert i v m) IM.empty - - srcSpanToInterval :: SrcSpan -> Maybe (IM.Interval Position) - srcSpanToInterval = fmap rangeToInterval . srcSpanToRange - -filterRecords :: Range -> IM.IntervalMap Position RenderedRecordInfo -> [RenderedRecordInfo] -filterRecords range = map snd . IM.dominators (rangeToInterval range) From aeb57a8eb56964c8666d7cd05b6ba46d531de7c7 Mon Sep 17 00:00:00 2001 From: fendor Date: Mon, 28 Nov 2022 16:19:51 +0100 Subject: [PATCH 202/213] Introduce common code for Recorders in Plugin Tests (#3347) * Add generic test plugin recorder initialisation * Migrate some plugins to the new test plugin recorder structure * Introduce improved Test Logging Infrastructure Every plugin should use the new functions. This way, we can guarantee predictable logging behaviour for tests. Most notably, we now have a single control point, for co-log style logging, how to write logs to stderr, and how to write logs for server and plugin at the same time. * HlsPlugins: Make sure every plugin is imported qualified * hls-cabal-plugin: Unify logging infrastructure * hls-cabal-fmt-plugin: Unify logging infrastructure * hls-alternate-number-format-plugin: Unify logging infrastructure * hls-brittany-plugin: Unify logging infrastructure * hls-call-hierarchy-plugin: Unify logging infrastructure * hls-change-type-signature-plugin: Unify logging infrastructure * hls-class-plugin: Unify logging infrastructure * hls-code-range-plugin: Unify logging infrastructure * hls-eval-plugin: Unify logging infrastructure * hls-explicit-fixity-plugin: Unify logging infrastructure * hls-explicit-imports-plugin: Unify logging infrastructure * hls-explicit-record-fields-plugin: Unify logging infrastructure * hls-floskell-plugin: Unify logging infrastructure * hls-fourmolu-plugin: Unify logging infrastructure * hls-gadt-plugin: Unify logging infrastructure * hls-haddock-comments-plugin: Unify logging infrastructure * hls-hlint-plugin: Unify logging infrastructure * hls-module-name-plugin: Unify logging infrastructure * hls-ormolu-plugin: Unify logging infrastructure * hls-pragmas-plugin: Unify logging infrastructure * hls-qualify-imported-names-plugin: Unify logging infrastructure * hls-refactor-plugin: Unify logging infrastructure * hls-refine-imports-plugin: Unify logging infrastructure * hls-rename-plugin: Unify logging infrastructure * hls-splice-plugin: Unify logging infrastructure * hls-stan-plugin: Unify logging infrastructure * hls-stylish-haskell-plugin: Unify logging infrastructure * hls-tactics-plugin: Unify logging infrastructure Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- hls-test-utils/src/Test/Hls.hs | 174 +++++++++++++++--- .../test/Main.hs | 4 +- plugins/hls-brittany-plugin/test/Main.hs | 4 +- plugins/hls-cabal-fmt-plugin/test/Main.hs | 4 +- plugins/hls-cabal-plugin/test/Main.hs | 51 ++--- .../src/Ide/Plugin/CallHierarchy.hs | 4 +- .../src/Ide/Plugin/CallHierarchy/Internal.hs | 4 - .../hls-call-hierarchy-plugin/test/Main.hs | 4 +- .../src/Ide/Plugin/ChangeTypeSignature.hs | 31 ++-- .../test/Main.hs | 16 +- plugins/hls-class-plugin/test/Main.hs | 92 +++++---- plugins/hls-code-range-plugin/test/Main.hs | 25 ++- plugins/hls-eval-plugin/test/Main.hs | 4 +- .../src/Ide/Plugin/ExplicitFixity.hs | 12 +- .../hls-explicit-fixity-plugin/test/Main.hs | 6 +- .../hls-explicit-imports-plugin/test/Main.hs | 4 +- .../src/Ide/Plugin/ExplicitFields.hs | 1 + .../test/Main.hs | 4 +- plugins/hls-floskell-plugin/test/Main.hs | 4 +- .../src/Ide/Plugin/Fourmolu.hs | 1 + plugins/hls-fourmolu-plugin/test/Main.hs | 4 +- plugins/hls-gadt-plugin/test/Main.hs | 4 +- .../src/Ide/Plugin/HaddockComments.hs | 2 +- .../hls-haddock-comments-plugin/test/Main.hs | 4 +- plugins/hls-hlint-plugin/test/Main.hs | 6 +- .../src/Ide/Plugin/ModuleName.hs | 1 + plugins/hls-module-name-plugin/test/Main.hs | 4 +- plugins/hls-ormolu-plugin/test/Main.hs | 4 +- plugins/hls-pragmas-plugin/test/Main.hs | 4 +- .../test/Main.hs | 11 +- plugins/hls-refactor-plugin/test/Main.hs | 23 ++- .../test/Test/AddArgument.hs | 2 +- .../hls-refine-imports-plugin/test/Main.hs | 4 +- .../src/Ide/Plugin/Rename.hs | 2 +- plugins/hls-rename-plugin/test/Main.hs | 4 +- plugins/hls-splice-plugin/test/Main.hs | 4 +- .../hls-stan-plugin/src/Ide/Plugin/Stan.hs | 2 +- plugins/hls-stan-plugin/test/Main.hs | 4 +- .../hls-stylish-haskell-plugin/test/Main.hs | 4 +- plugins/hls-tactics-plugin/old/test/Utils.hs | 10 +- src/HlsPlugins.hs | 12 +- 41 files changed, 331 insertions(+), 233 deletions(-) diff --git a/hls-test-utils/src/Test/Hls.hs b/hls-test-utils/src/Test/Hls.hs index a9d3a595f3..4e5c25a9a1 100644 --- a/hls-test-utils/src/Test/Hls.hs +++ b/hls-test-utils/src/Test/Hls.hs @@ -20,14 +20,18 @@ module Test.Hls goldenWithHaskellDocFormatter, goldenWithCabalDocFormatter, def, + -- * Running HLS for integration tests runSessionWithServer, + runSessionWithServerAndCaps, runSessionWithServerFormatter, runSessionWithCabalServerFormatter, runSessionWithServer', - waitForProgressDone, - waitForAllProgressDone, + -- * Helpful re-exports PluginDescriptor, IdeState, + -- * Assertion helper functions + waitForProgressDone, + waitForAllProgressDone, waitForBuildQueue, waitForTypecheck, waitForAction, @@ -35,6 +39,16 @@ module Test.Hls getLastBuildKeys, waitForKickDone, waitForKickStart, + -- * Plugin descriptor helper functions for tests + PluginTestDescriptor, + pluginTestRecorder, + mkPluginTestDescriptor, + mkPluginTestDescriptor', + -- * Re-export logger types + -- Avoids slightly annoying ghcide imports when they are unnecessary. + WithPriority(..), + Recorder, + Priority(..), ) where @@ -43,6 +57,7 @@ import Control.Concurrent.Async (async, cancel, wait) import Control.Concurrent.Extra import Control.Exception.Base import Control.Monad (guard, unless, void) +import Control.Monad.Extra (forM) import Control.Monad.IO.Class import Data.Aeson (Result (Success), Value (Null), fromJSON, @@ -62,7 +77,7 @@ import qualified Development.IDE.Main as IDEMain import Development.IDE.Plugin.Test (TestRequest (GetBuildKeysBuilt, WaitForIdeRule, WaitForShakeQueue), WaitForIdeRuleResult (ideResultSuccess)) import qualified Development.IDE.Plugin.Test as Test -import Development.IDE.Types.Logger (Logger (Logger), +import Development.IDE.Types.Logger (Doc, Logger (Logger), Pretty (pretty), Priority (Debug), Recorder (Recorder, logger_), @@ -117,7 +132,8 @@ goldenGitDiff :: TestName -> FilePath -> IO ByteString -> TestTree goldenGitDiff name = goldenVsStringDiff name gitDiff goldenWithHaskellDoc - :: PluginDescriptor IdeState + :: Pretty b + => PluginTestDescriptor b -> TestName -> FilePath -> FilePath @@ -128,7 +144,8 @@ goldenWithHaskellDoc goldenWithHaskellDoc = goldenWithDoc "haskell" goldenWithCabalDoc - :: PluginDescriptor IdeState + :: Pretty b + => PluginTestDescriptor b -> TestName -> FilePath -> FilePath @@ -139,8 +156,9 @@ goldenWithCabalDoc goldenWithCabalDoc = goldenWithDoc "cabal" goldenWithDoc - :: T.Text - -> PluginDescriptor IdeState + :: Pretty b + => T.Text + -> PluginTestDescriptor b -> TestName -> FilePath -> FilePath @@ -158,23 +176,119 @@ goldenWithDoc fileType plugin title testDataDir path desc ext act = act doc documentContents doc +-- ------------------------------------------------------------ +-- Helper function for initialising plugins under test +-- ------------------------------------------------------------ + +-- | Plugin under test where a fitting recorder is injected. +type PluginTestDescriptor b = Recorder (WithPriority b) -> PluginDescriptor IdeState + +-- | Wrap a plugin you want to test, and inject a fitting recorder as required. +-- +-- If you want to write the logs to stderr, run your tests with +-- "HLS_TEST_PLUGIN_LOG_STDERR=1", e.g. +-- +-- @ +-- HLS_TEST_PLUGIN_LOG_STDERR=1 cabal test +-- @ +-- +-- +-- To write all logs to stderr, including logs of the server, use: +-- +-- @ +-- HLS_TEST_LOG_STDERR=1 cabal test +-- @ +mkPluginTestDescriptor + :: (Recorder (WithPriority b) -> PluginId -> PluginDescriptor IdeState) + -> PluginId + -> PluginTestDescriptor b +mkPluginTestDescriptor pluginDesc plId recorder = pluginDesc recorder plId + +-- | Wrap a plugin you want to test. +-- +-- Ideally, try to migrate this plugin to co-log logger style architecture. +-- Therefore, you should prefer 'mkPluginTestDescriptor' to this if possible. +mkPluginTestDescriptor' + :: (PluginId -> PluginDescriptor IdeState) + -> PluginId + -> PluginTestDescriptor b +mkPluginTestDescriptor' pluginDesc plId _recorder = pluginDesc plId + +-- | Initialise a recorder that can be instructed to write to stderr by +-- setting the environment variable "HLS_TEST_PLUGIN_LOG_STDERR=1" before +-- running the tests. +-- +-- On the cli, use for example: +-- +-- @ +-- HLS_TEST_PLUGIN_LOG_STDERR=1 cabal test +-- @ +-- +-- To write all logs to stderr, including logs of the server, use: +-- +-- @ +-- HLS_TEST_LOG_STDERR=1 cabal test +-- @ +pluginTestRecorder :: Pretty a => IO (Recorder (WithPriority a)) +pluginTestRecorder = do + (recorder, _) <- initialiseTestRecorder ["HLS_TEST_PLUGIN_LOG_STDERR", "HLS_TEST_LOG_STDERR"] + pure recorder + +-- | Generic recorder initialisation for plugins and the HLS server for test-cases. +-- +-- The created recorder writes to stderr if any of the given environment variables +-- have been set to a value different to @0@. +-- We allow multiple values, to make it possible to define a single environment variable +-- that instructs all recorders in the test-suite to write to stderr. +-- +-- We have to return the base logger function for HLS server logging initialisation. +-- See 'runSessionWithServer'' for details. +initialiseTestRecorder :: Pretty a => [String] -> IO (Recorder (WithPriority a), WithPriority (Doc ann) -> IO ()) +initialiseTestRecorder envVars = do + docWithPriorityRecorder <- makeDefaultStderrRecorder Nothing Debug + -- There are potentially multiple environment variables that enable this logger + definedEnvVars <- forM envVars (\var -> fromMaybe "0" <$> lookupEnv var) + let logStdErr = any (/= "0") definedEnvVars + + docWithFilteredPriorityRecorder = + if logStdErr then cfilter (\WithPriority{ priority } -> priority >= Debug) docWithPriorityRecorder + else mempty + + Recorder {logger_} = docWithFilteredPriorityRecorder + + pure (cmapWithPrio pretty docWithFilteredPriorityRecorder, logger_) -runSessionWithServer :: PluginDescriptor IdeState -> FilePath -> Session a -> IO a -runSessionWithServer plugin = runSessionWithServer' [plugin] def def fullCaps +-- ------------------------------------------------------------ +-- Run an HLS server testing a specific plugin +-- ------------------------------------------------------------ -runSessionWithServerFormatter :: PluginDescriptor IdeState -> String -> PluginConfig -> FilePath -> Session a -> IO a -runSessionWithServerFormatter plugin formatter conf = +runSessionWithServer :: Pretty b => PluginTestDescriptor b -> FilePath -> Session a -> IO a +runSessionWithServer plugin fp act = do + recorder <- pluginTestRecorder + runSessionWithServer' [plugin recorder] def def fullCaps fp act + +runSessionWithServerAndCaps :: Pretty b => PluginTestDescriptor b -> ClientCapabilities -> FilePath -> Session a -> IO a +runSessionWithServerAndCaps plugin caps fp act = do + recorder <- pluginTestRecorder + runSessionWithServer' [plugin recorder] def def caps fp act + +runSessionWithServerFormatter :: Pretty b => PluginTestDescriptor b -> String -> PluginConfig -> FilePath -> Session a -> IO a +runSessionWithServerFormatter plugin formatter conf fp act = do + recorder <- pluginTestRecorder runSessionWithServer' - [plugin] + [plugin recorder] def { formattingProvider = T.pack formatter , plugins = M.singleton (T.pack formatter) conf } def fullCaps + fp + act goldenWithHaskellDocFormatter - :: PluginDescriptor IdeState -- ^ Formatter plugin to be used + :: Pretty b + => PluginTestDescriptor b -- ^ Formatter plugin to be used -> String -- ^ Name of the formatter to be used -> PluginConfig -> TestName -- ^ Title of the test @@ -195,7 +309,8 @@ goldenWithHaskellDocFormatter plugin formatter conf title testDataDir path desc documentContents doc goldenWithCabalDocFormatter - :: PluginDescriptor IdeState -- ^ Formatter plugin to be used + :: Pretty b + => PluginTestDescriptor b -- ^ Formatter plugin to be used -> String -- ^ Name of the formatter to be used -> PluginConfig -> TestName -- ^ Title of the test @@ -215,16 +330,18 @@ goldenWithCabalDocFormatter plugin formatter conf title testDataDir path desc ex act doc documentContents doc -runSessionWithCabalServerFormatter :: PluginDescriptor IdeState -> String -> PluginConfig -> FilePath -> Session a -> IO a -runSessionWithCabalServerFormatter plugin formatter conf = +runSessionWithCabalServerFormatter :: Pretty b => PluginTestDescriptor b -> String -> PluginConfig -> FilePath -> Session a -> IO a +runSessionWithCabalServerFormatter plugin formatter conf fp act = do + recorder <- pluginTestRecorder runSessionWithServer' - [plugin] + [plugin recorder] def { cabalFormattingProvider = T.pack formatter , plugins = M.singleton (T.pack formatter) conf } def fullCaps + fp act -- | Restore cwd after running an action keepCurrentDirectory :: IO a -> IO a @@ -235,11 +352,13 @@ keepCurrentDirectory = bracket getCurrentDirectory setCurrentDirectory . const lock :: Lock lock = unsafePerformIO newLock - -- | Host a server, and run a test session on it -- Note: cwd will be shifted into @root@ in @Session a@ runSessionWithServer' :: - -- | plugins to load on the server + -- | Plugins to load on the server. + -- + -- For improved logging, make sure these plugins have been initalised with + -- the recorder produced by @pluginTestRecorder@. [PluginDescriptor IdeState] -> -- | lsp config for the server Config -> @@ -253,20 +372,19 @@ runSessionWithServer' plugins conf sconf caps root s = withLock lock $ keepCurre (inR, inW) <- createPipe (outR, outW) <- createPipe - docWithPriorityRecorder <- makeDefaultStderrRecorder Nothing Debug - - logStdErr <- fromMaybe "0" <$> lookupEnv "LSP_TEST_LOG_STDERR" + -- Allow three environment variables, because "LSP_TEST_LOG_STDERR" has been used before, + -- (thus, backwards compatibility) and "HLS_TEST_SERVER_LOG_STDERR" because it + -- uses a more descriptive name. + -- It is also in better accordance with 'pluginTestRecorder' which uses "HLS_TEST_PLUGIN_LOG_STDERR". + -- At last, "HLS_TEST_LOG_STDERR" is intended to enable all logging for the server and the plugins + -- under test. + (recorder, logger_) <- initialiseTestRecorder + ["LSP_TEST_LOG_STDERR", "HLS_TEST_SERVER_LOG_STDERR", "HLS_TEST_LOG_STDERR"] let - docWithFilteredPriorityRecorder@Recorder{ logger_ } = - if logStdErr == "0" then mempty - else cfilter (\WithPriority{ priority } -> priority >= Debug) docWithPriorityRecorder - -- exists until old logging style is phased out logger = Logger $ \p m -> logger_ (WithPriority p emptyCallStack (pretty m)) - recorder = cmapWithPrio pretty docWithFilteredPriorityRecorder - arguments@Arguments{ argsHlsPlugins, argsIdeOptions, argsLogger } = defaultArguments (cmapWithPrio LogIDEMain recorder) logger hlsPlugins = diff --git a/plugins/hls-alternate-number-format-plugin/test/Main.hs b/plugins/hls-alternate-number-format-plugin/test/Main.hs index d6b19d4e7b..e3fa6607d5 100644 --- a/plugins/hls-alternate-number-format-plugin/test/Main.hs +++ b/plugins/hls-alternate-number-format-plugin/test/Main.hs @@ -19,8 +19,8 @@ import Text.Regex.TDFA ((=~)) main :: IO () main = defaultTestRunner test -alternateNumberFormatPlugin :: PluginDescriptor IdeState -alternateNumberFormatPlugin = AlternateNumberFormat.descriptor mempty "alternateNumberFormat" +alternateNumberFormatPlugin :: PluginTestDescriptor AlternateNumberFormat.Log +alternateNumberFormatPlugin = mkPluginTestDescriptor AlternateNumberFormat.descriptor "alternateNumberFormat" -- NOTE: For whatever reason, this plugin does not play nice with creating Code Actions on time. -- As a result tests will mostly pass if `import Prelude` is added at the top. We (mostly fendor) surmise this has something diff --git a/plugins/hls-brittany-plugin/test/Main.hs b/plugins/hls-brittany-plugin/test/Main.hs index a7a840d7c3..0483ecbabe 100644 --- a/plugins/hls-brittany-plugin/test/Main.hs +++ b/plugins/hls-brittany-plugin/test/Main.hs @@ -10,8 +10,8 @@ import Test.Hls main :: IO () main = defaultTestRunner tests -brittanyPlugin :: PluginDescriptor IdeState -brittanyPlugin = Brittany.descriptor "brittany" +brittanyPlugin :: PluginTestDescriptor () +brittanyPlugin = mkPluginTestDescriptor' Brittany.descriptor "brittany" tests :: TestTree tests = testGroup "brittany" diff --git a/plugins/hls-cabal-fmt-plugin/test/Main.hs b/plugins/hls-cabal-fmt-plugin/test/Main.hs index 35d6fe6ba8..54c95eddb9 100644 --- a/plugins/hls-cabal-fmt-plugin/test/Main.hs +++ b/plugins/hls-cabal-fmt-plugin/test/Main.hs @@ -30,8 +30,8 @@ main = do foundCabalFmt <- isCabalFmtFound defaultTestRunner (tests foundCabalFmt) -cabalFmtPlugin :: PluginDescriptor IdeState -cabalFmtPlugin = CabalFmt.descriptor mempty "cabal-fmt" +cabalFmtPlugin :: PluginTestDescriptor CabalFmt.Log +cabalFmtPlugin = mkPluginTestDescriptor CabalFmt.descriptor "cabal-fmt" tests :: CabalFmtFound -> TestTree tests found = testGroup "cabal-fmt" diff --git a/plugins/hls-cabal-plugin/test/Main.hs b/plugins/hls-cabal-plugin/test/Main.hs index 9fb01274b6..03a8976bb1 100644 --- a/plugins/hls-cabal-plugin/test/Main.hs +++ b/plugins/hls-cabal-plugin/test/Main.hs @@ -11,9 +11,7 @@ import Control.Lens ((^.)) import Control.Monad (guard) import qualified Data.ByteString as BS import Data.Either (isRight) -import Data.Function import qualified Data.Text as Text -import Development.IDE.Types.Logger import Ide.Plugin.Cabal import Ide.Plugin.Cabal.LicenseSuggest (licenseErrorSuggestion) import qualified Ide.Plugin.Cabal.Parse as Lib @@ -21,34 +19,17 @@ import qualified Language.LSP.Types.Lens as J import System.FilePath import Test.Hls - -cabalPlugin :: Recorder (WithPriority Log) -> PluginDescriptor IdeState -cabalPlugin recorder = descriptor recorder "cabal" +cabalPlugin :: PluginTestDescriptor Log +cabalPlugin = mkPluginTestDescriptor descriptor "cabal" main :: IO () main = do - recorder <- initialiseRecorder True defaultTestRunner $ testGroup "Cabal Plugin Tests" [ unitTests - , pluginTests recorder + , pluginTests ] --- | @initialiseRecorder silent@ --- --- If @'silent' == True@, then don't log anything, otherwise --- the recorder is the standard recorder of HLS. Useful for debugging. -initialiseRecorder :: Bool -> IO (Recorder (WithPriority Log)) -initialiseRecorder True = pure mempty -initialiseRecorder False = do - docWithPriorityRecorder <- makeDefaultStderrRecorder Nothing Debug - - let docWithFilteredPriorityRecorder = - docWithPriorityRecorder - & cfilter (\WithPriority{ priority } -> priority >= Debug) - pure $ docWithFilteredPriorityRecorder - & cmapWithPrio pretty - -- ------------------------------------------------------------------------ -- Unit Tests -- ------------------------------------------------------------------------ @@ -89,10 +70,10 @@ codeActionUnitTests = testGroup "Code Action Tests" -- Integration Tests -- ------------------------------------------------------------------------ -pluginTests :: Recorder (WithPriority Log) -> TestTree -pluginTests recorder = testGroup "Plugin Tests" +pluginTests :: TestTree +pluginTests = testGroup "Plugin Tests" [ testGroup "Diagnostics" - [ runCabalTestCaseSession "Publishes Diagnostics on Error" recorder "" $ do + [ runCabalTestCaseSession "Publishes Diagnostics on Error" "" $ do doc <- openDoc "invalid.cabal" "cabal" diags <- waitForDiagnosticsFromSource doc "cabal" unknownLicenseDiag <- liftIO $ inspectDiagnostic diags ["Unknown SPDX license identifier: 'BSD3'"] @@ -100,7 +81,7 @@ pluginTests recorder = testGroup "Plugin Tests" length diags @?= 1 unknownLicenseDiag ^. J.range @?= Range (Position 3 24) (Position 4 0) unknownLicenseDiag ^. J.severity @?= Just DsError - , runCabalTestCaseSession "Clears diagnostics" recorder "" $ do + , runCabalTestCaseSession "Clears diagnostics" "" $ do doc <- openDoc "invalid.cabal" "cabal" diags <- waitForDiagnosticsFrom doc unknownLicenseDiag <- liftIO $ inspectDiagnostic diags ["Unknown SPDX license identifier: 'BSD3'"] @@ -111,13 +92,13 @@ pluginTests recorder = testGroup "Plugin Tests" _ <- applyEdit doc $ TextEdit (Range (Position 3 20) (Position 4 0)) "BSD-3-Clause\n" newDiags <- waitForDiagnosticsFrom doc liftIO $ newDiags @?= [] - , runCabalTestCaseSession "No Diagnostics in .hs files from valid .cabal file" recorder "simple-cabal" $ do + , runCabalTestCaseSession "No Diagnostics in .hs files from valid .cabal file" "simple-cabal" $ do hsDoc <- openDoc "A.hs" "haskell" expectNoMoreDiagnostics 1 hsDoc "typechecking" cabalDoc <- openDoc "simple-cabal.cabal" "cabal" expectNoMoreDiagnostics 1 cabalDoc "parsing" , ignoreTestBecause "Testcase is flaky for certain GHC versions (e.g. 9.2.4). See #3333 for details." $ do - runCabalTestCaseSession "Diagnostics in .hs files from invalid .cabal file" recorder "simple-cabal" $ do + runCabalTestCaseSession "Diagnostics in .hs files from invalid .cabal file" "simple-cabal" $ do hsDoc <- openDoc "A.hs" "haskell" expectNoMoreDiagnostics 1 hsDoc "typechecking" cabalDoc <- openDoc "simple-cabal.cabal" "cabal" @@ -134,7 +115,7 @@ pluginTests recorder = testGroup "Plugin Tests" unknownLicenseDiag ^. J.severity @?= Just DsError ] , testGroup "Code Actions" - [ runCabalTestCaseSession "BSD-3" recorder "" $ do + [ runCabalTestCaseSession "BSD-3" "" $ do doc <- openDoc "licenseCodeAction.cabal" "cabal" diags <- waitForDiagnosticsFromSource doc "cabal" reduceDiag <- liftIO $ inspectDiagnostic diags ["Unknown SPDX license identifier: 'BSD3'"] @@ -155,7 +136,7 @@ pluginTests recorder = testGroup "Plugin Tests" , " build-depends: base" , " default-language: Haskell2010" ] - , runCabalTestCaseSession "Apache-2.0" recorder "" $ do + , runCabalTestCaseSession "Apache-2.0" "" $ do doc <- openDoc "licenseCodeAction2.cabal" "cabal" diags <- waitForDiagnosticsFromSource doc "cabal" -- test if it supports typos in license name, here 'apahe' @@ -190,12 +171,12 @@ pluginTests recorder = testGroup "Plugin Tests" -- Runner utils -- ------------------------------------------------------------------------ -runCabalTestCaseSession :: TestName -> Recorder (WithPriority Log) -> FilePath -> Session () -> TestTree -runCabalTestCaseSession title recorder subdir act = testCase title $ runCabalSession recorder subdir act +runCabalTestCaseSession :: TestName -> FilePath -> Session () -> TestTree +runCabalTestCaseSession title subdir = testCase title . runCabalSession subdir -runCabalSession :: Recorder (WithPriority Log) -> FilePath -> Session a -> IO a -runCabalSession recorder subdir = - failIfSessionTimeout . runSessionWithServer (cabalPlugin recorder) (testDataDir subdir) +runCabalSession :: FilePath -> Session a -> IO a +runCabalSession subdir = + failIfSessionTimeout . runSessionWithServer cabalPlugin (testDataDir subdir) testDataDir :: FilePath testDataDir = "test" "testdata" diff --git a/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy.hs b/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy.hs index cf7e042986..3e0da1afde 100644 --- a/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy.hs +++ b/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy.hs @@ -5,8 +5,8 @@ import qualified Ide.Plugin.CallHierarchy.Internal as X import Ide.Types import Language.LSP.Types -descriptor :: PluginDescriptor IdeState -descriptor = (defaultPluginDescriptor X.callHierarchyId) +descriptor :: PluginId -> PluginDescriptor IdeState +descriptor plId = (defaultPluginDescriptor plId) { Ide.Types.pluginHandlers = mkPluginHandler STextDocumentPrepareCallHierarchy X.prepareCallHierarchy <> mkPluginHandler SCallHierarchyIncomingCalls X.incomingCalls diff --git a/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Internal.hs b/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Internal.hs index db148733ec..2b23688fd3 100644 --- a/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Internal.hs +++ b/plugins/hls-call-hierarchy-plugin/src/Ide/Plugin/CallHierarchy/Internal.hs @@ -11,7 +11,6 @@ module Ide.Plugin.CallHierarchy.Internal ( prepareCallHierarchy , incomingCalls , outgoingCalls -, callHierarchyId ) where import Control.Lens ((^.)) @@ -38,9 +37,6 @@ import Language.LSP.Types import qualified Language.LSP.Types.Lens as L import Text.Read (readMaybe) -callHierarchyId :: PluginId -callHierarchyId = PluginId "callHierarchy" - -- | Render prepare call hierarchy request. prepareCallHierarchy :: PluginMethodHandler IdeState TextDocumentPrepareCallHierarchy prepareCallHierarchy state _ param = pluginResponse $ do diff --git a/plugins/hls-call-hierarchy-plugin/test/Main.hs b/plugins/hls-call-hierarchy-plugin/test/Main.hs index bbd8c44b93..93ff69b062 100644 --- a/plugins/hls-call-hierarchy-plugin/test/Main.hs +++ b/plugins/hls-call-hierarchy-plugin/test/Main.hs @@ -21,8 +21,8 @@ import qualified System.IO.Extra import Test.Hls import Test.Hls.Util (withCanonicalTempDir) -plugin :: PluginDescriptor IdeState -plugin = descriptor +plugin :: PluginTestDescriptor () +plugin = mkPluginTestDescriptor' descriptor "call-hierarchy" main :: IO () main = defaultTestRunner $ diff --git a/plugins/hls-change-type-signature-plugin/src/Ide/Plugin/ChangeTypeSignature.hs b/plugins/hls-change-type-signature-plugin/src/Ide/Plugin/ChangeTypeSignature.hs index e18a2dac36..5374761a14 100644 --- a/plugins/hls-change-type-signature-plugin/src/Ide/Plugin/ChangeTypeSignature.hs +++ b/plugins/hls-change-type-signature-plugin/src/Ide/Plugin/ChangeTypeSignature.hs @@ -12,7 +12,6 @@ import Control.Monad.Trans.Except (ExceptT) import Data.Foldable (asum) import qualified Data.HashMap.Strict as Map import Data.Maybe (mapMaybe) -import Data.String (IsString) import Data.Text (Text) import qualified Data.Text as T import Development.IDE (realSrcSpanToRange) @@ -25,30 +24,28 @@ import Generics.SYB (extQ, something) import Ide.PluginUtils (getNormalizedFilePath, handleMaybeM, pluginResponse) import Ide.Types (PluginDescriptor (..), + PluginId (PluginId), PluginMethodHandler, defaultPluginDescriptor, mkPluginHandler) import Language.LSP.Types import Text.Regex.TDFA ((=~)) -changeTypeSignatureId :: IsString a => a -changeTypeSignatureId = "changeTypeSignature" +descriptor :: PluginId -> PluginDescriptor IdeState +descriptor plId = (defaultPluginDescriptor plId) { pluginHandlers = mkPluginHandler STextDocumentCodeAction (codeActionHandler plId) } -descriptor :: PluginDescriptor IdeState -descriptor = (defaultPluginDescriptor changeTypeSignatureId) { pluginHandlers = mkPluginHandler STextDocumentCodeAction codeActionHandler } - -codeActionHandler :: PluginMethodHandler IdeState 'TextDocumentCodeAction -codeActionHandler ideState _ CodeActionParams {_textDocument = TextDocumentIdentifier uri, _context = CodeActionContext (List diags) _} = pluginResponse $ do +codeActionHandler :: PluginId -> PluginMethodHandler IdeState 'TextDocumentCodeAction +codeActionHandler plId ideState _ CodeActionParams {_textDocument = TextDocumentIdentifier uri, _context = CodeActionContext (List diags) _} = pluginResponse $ do nfp <- getNormalizedFilePath uri - decls <- getDecls ideState nfp - let actions = mapMaybe (generateAction uri decls) diags + decls <- getDecls plId ideState nfp + let actions = mapMaybe (generateAction plId uri decls) diags pure $ List actions -getDecls :: MonadIO m => IdeState -> NormalizedFilePath -> ExceptT String m [LHsDecl GhcPs] -getDecls state = handleMaybeM "Could not get Parsed Module" +getDecls :: MonadIO m => PluginId -> IdeState -> NormalizedFilePath -> ExceptT String m [LHsDecl GhcPs] +getDecls (PluginId changeTypeSignatureId) state = handleMaybeM "Could not get Parsed Module" . liftIO . fmap (fmap (hsmodDecls . unLoc . pm_parsed_source)) - . runAction (changeTypeSignatureId <> ".GetParsedModule") state + . runAction (T.unpack changeTypeSignatureId <> ".GetParsedModule") state . use GetParsedModule -- | Text representing a Declaration's Name @@ -76,8 +73,8 @@ data ChangeSignature = ChangeSignature { type SigName = (HasOccName (IdP GhcPs)) -- | Create a CodeAction from a Diagnostic -generateAction :: SigName => Uri -> [LHsDecl GhcPs] -> Diagnostic -> Maybe (Command |? CodeAction) -generateAction uri decls diag = changeSigToCodeAction uri <$> diagnosticToChangeSig decls diag +generateAction :: SigName => PluginId -> Uri -> [LHsDecl GhcPs] -> Diagnostic -> Maybe (Command |? CodeAction) +generateAction plId uri decls diag = changeSigToCodeAction plId uri <$> diagnosticToChangeSig decls diag -- | Convert a diagnostic into a ChangeSignature and add the proper SrcSpan diagnosticToChangeSig :: SigName => [LHsDecl GhcPs] -> Diagnostic -> Maybe ChangeSignature @@ -148,8 +145,8 @@ stripSignature (T.filter (/= '\n') -> sig) = if T.isInfixOf " => " sig then T.strip $ snd $ T.breakOnEnd " => " sig else T.strip $ snd $ T.breakOnEnd " :: " sig -changeSigToCodeAction :: Uri -> ChangeSignature -> Command |? CodeAction -changeSigToCodeAction uri ChangeSignature{..} = InR CodeAction { _title = mkChangeSigTitle declName actualType +changeSigToCodeAction :: PluginId -> Uri -> ChangeSignature -> Command |? CodeAction +changeSigToCodeAction (PluginId changeTypeSignatureId) uri ChangeSignature{..} = InR CodeAction { _title = mkChangeSigTitle declName actualType , _kind = Just (CodeActionUnknown ("quickfix." <> changeTypeSignatureId)) , _diagnostics = Just $ List [diagnostic] , _isPreferred = Nothing diff --git a/plugins/hls-change-type-signature-plugin/test/Main.hs b/plugins/hls-change-type-signature-plugin/test/Main.hs index 3aba829522..84d9b8ef90 100644 --- a/plugins/hls-change-type-signature-plugin/test/Main.hs +++ b/plugins/hls-change-type-signature-plugin/test/Main.hs @@ -9,8 +9,8 @@ import Ide.Plugin.ChangeTypeSignature (errorMessageRegexes) import qualified Ide.Plugin.ChangeTypeSignature as ChangeTypeSignature import System.FilePath ((<.>), ()) import Test.Hls (CodeAction (..), Command, - GhcVersion (..), IdeState, - PluginDescriptor, + GhcVersion (..), + PluginTestDescriptor, Position (Position), Range (Range), Session, TestName, TestTree, @@ -21,9 +21,11 @@ import Test.Hls (CodeAction (..), Command, getCodeActions, goldenWithHaskellDoc, knownBrokenForGhcVersions, - liftIO, openDoc, - runSessionWithServer, testCase, - testGroup, toEither, type (|?), + liftIO, + mkPluginTestDescriptor', + openDoc, runSessionWithServer, + testCase, testGroup, toEither, + type (|?), waitForAllProgressDone, waitForDiagnostics, (@?=)) import Text.Regex.TDFA ((=~)) @@ -31,8 +33,8 @@ import Text.Regex.TDFA ((=~)) main :: IO () main = defaultTestRunner test -changeTypeSignaturePlugin :: PluginDescriptor IdeState -changeTypeSignaturePlugin = ChangeTypeSignature.descriptor +changeTypeSignaturePlugin :: PluginTestDescriptor () +changeTypeSignaturePlugin = mkPluginTestDescriptor' ChangeTypeSignature.descriptor "changeTypeSignature" test :: TestTree test = testGroup "changeTypeSignature" [ diff --git a/plugins/hls-class-plugin/test/Main.hs b/plugins/hls-class-plugin/test/Main.hs index 585f49143e..c9c14aa85c 100644 --- a/plugins/hls-class-plugin/test/Main.hs +++ b/plugins/hls-class-plugin/test/Main.hs @@ -9,38 +9,30 @@ module Main ( main ) where -import Control.Lens (Prism', prism', (^.), (^..), - (^?)) -import Control.Monad (void) -import Data.Aeson (toJSON, (.=)) -import Data.Functor.Contravariant (contramap) +import Control.Lens (Prism', prism', (^.), (^..), (^?)) +import Control.Monad (void) import Data.Maybe -import Development.IDE.Types.Logger -import qualified Ide.Plugin.Class as Class -import Ide.Plugin.Config (PluginConfig (plcConfig)) -import qualified Ide.Plugin.Config as Plugin -import qualified Language.LSP.Types.Lens as J +import qualified Ide.Plugin.Class as Class +import qualified Language.LSP.Types.Lens as J import System.FilePath import Test.Hls main :: IO () -main = do - recorder <- makeDefaultStderrRecorder Nothing Debug - defaultTestRunner . tests $ contramap (fmap pretty) recorder +main = defaultTestRunner tests -classPlugin :: Recorder (WithPriority Class.Log) -> PluginDescriptor IdeState -classPlugin recorder = Class.descriptor recorder "class" +classPlugin :: PluginTestDescriptor Class.Log +classPlugin = mkPluginTestDescriptor Class.descriptor "class" -tests :: Recorder (WithPriority Class.Log) -> TestTree -tests recorder = testGroup +tests :: TestTree +tests = testGroup "class" - [codeActionTests recorder , codeLensTests recorder] + [codeActionTests, codeLensTests] -codeActionTests :: Recorder (WithPriority Class.Log) -> TestTree -codeActionTests recorder = testGroup +codeActionTests :: TestTree +codeActionTests = testGroup "code actions" [ testCase "Produces addMinimalMethodPlaceholders code actions for one instance" $ do - runSessionWithServer (classPlugin recorder) testDataDir $ do + runSessionWithServer classPlugin testDataDir $ do doc <- openDoc "T1.hs" "haskell" _ <- waitForDiagnosticsFromSource doc "typecheck" caResults <- getAllCodeActions doc @@ -51,40 +43,40 @@ codeActionTests recorder = testGroup , Just "Add placeholders for '/='" , Just "Add placeholders for '/=' with signature(s)" ] - , goldenWithClass recorder "Creates a placeholder for '=='" "T1" "eq" $ \(eqAction:_) -> do + , goldenWithClass "Creates a placeholder for '=='" "T1" "eq" $ \(eqAction:_) -> do executeCodeAction eqAction - , goldenWithClass recorder "Creates a placeholder for '/='" "T1" "ne" $ \(_:_:neAction:_) -> do + , goldenWithClass "Creates a placeholder for '/='" "T1" "ne" $ \(_:_:neAction:_) -> do executeCodeAction neAction - , goldenWithClass recorder "Creates a placeholder for 'fmap'" "T2" "fmap" $ \(_:_:_:_:fmapAction:_) -> do + , goldenWithClass "Creates a placeholder for 'fmap'" "T2" "fmap" $ \(_:_:_:_:fmapAction:_) -> do executeCodeAction fmapAction - , goldenWithClass recorder "Creates a placeholder for multiple methods 1" "T3" "1" $ \(mmAction:_) -> do + , goldenWithClass "Creates a placeholder for multiple methods 1" "T3" "1" $ \(mmAction:_) -> do executeCodeAction mmAction - , goldenWithClass recorder "Creates a placeholder for multiple methods 2" "T3" "2" $ \(_:_:mmAction:_) -> do + , goldenWithClass "Creates a placeholder for multiple methods 2" "T3" "2" $ \(_:_:mmAction:_) -> do executeCodeAction mmAction - , goldenWithClass recorder "Creates a placeholder for a method starting with '_'" "T4" "" $ \(_fAction:_) -> do + , goldenWithClass "Creates a placeholder for a method starting with '_'" "T4" "" $ \(_fAction:_) -> do executeCodeAction _fAction - , goldenWithClass recorder "Creates a placeholder for '==' with extra lines" "T5" "" $ \(eqAction:_) -> do + , goldenWithClass "Creates a placeholder for '==' with extra lines" "T5" "" $ \(eqAction:_) -> do executeCodeAction eqAction - , goldenWithClass recorder "Creates a placeholder for only the unimplemented methods of multiple methods" "T6" "1" $ \(gAction:_) -> do + , goldenWithClass "Creates a placeholder for only the unimplemented methods of multiple methods" "T6" "1" $ \(gAction:_) -> do executeCodeAction gAction - , goldenWithClass recorder "Creates a placeholder for other two methods" "T6" "2" $ \(_:_:ghAction:_) -> do + , goldenWithClass "Creates a placeholder for other two methods" "T6" "2" $ \(_:_:ghAction:_) -> do executeCodeAction ghAction , onlyRunForGhcVersions [GHC92, GHC94] "Only ghc-9.2+ enabled GHC2021 implicitly" $ - goldenWithClass recorder "Don't insert pragma with GHC2021" "InsertWithGHC2021Enabled" "" $ \(_:eqWithSig:_) -> do + goldenWithClass "Don't insert pragma with GHC2021" "InsertWithGHC2021Enabled" "" $ \(_:eqWithSig:_) -> do executeCodeAction eqWithSig - , goldenWithClass recorder "Insert pragma if not exist" "InsertWithoutPragma" "" $ \(_:eqWithSig:_) -> do + , goldenWithClass "Insert pragma if not exist" "InsertWithoutPragma" "" $ \(_:eqWithSig:_) -> do executeCodeAction eqWithSig - , goldenWithClass recorder "Don't insert pragma if exist" "InsertWithPragma" "" $ \(_:eqWithSig:_) -> do + , goldenWithClass "Don't insert pragma if exist" "InsertWithPragma" "" $ \(_:eqWithSig:_) -> do executeCodeAction eqWithSig - , goldenWithClass recorder "Only insert pragma once" "InsertPragmaOnce" "" $ \(_:multi:_) -> do + , goldenWithClass "Only insert pragma once" "InsertPragmaOnce" "" $ \(_:multi:_) -> do executeCodeAction multi ] -codeLensTests :: Recorder (WithPriority Class.Log) -> TestTree -codeLensTests recorder = testGroup +codeLensTests :: TestTree +codeLensTests = testGroup "code lens" [ testCase "Has code lens" $ do - runSessionWithServer (classPlugin recorder) testDataDir $ do + runSessionWithServer classPlugin testDataDir $ do doc <- openDoc "CodeLensSimple.hs" "haskell" lens <- getCodeLenses doc let titles = map (^. J.title) $ mapMaybe (^. J.command) lens @@ -92,14 +84,14 @@ codeLensTests recorder = testGroup [ "(==) :: B -> B -> Bool" , "(==) :: A -> A -> Bool" ] - , goldenCodeLens recorder "Apply code lens" "CodeLensSimple" 1 - , goldenCodeLens recorder "Apply code lens for local class" "LocalClassDefine" 0 - , goldenCodeLens recorder "Apply code lens on the same line" "Inline" 0 - , goldenCodeLens recorder "Don't insert pragma while existing" "CodeLensWithPragma" 0 + , goldenCodeLens "Apply code lens" "CodeLensSimple" 1 + , goldenCodeLens "Apply code lens for local class" "LocalClassDefine" 0 + , goldenCodeLens "Apply code lens on the same line" "Inline" 0 + , goldenCodeLens "Don't insert pragma while existing" "CodeLensWithPragma" 0 , onlyRunForGhcVersions [GHC92, GHC94] "Only ghc-9.2+ enabled GHC2021 implicitly" $ - goldenCodeLens recorder "Don't insert pragma while GHC2021 enabled" "CodeLensWithGHC2021" 0 - , goldenCodeLens recorder "Qualified name" "Qualified" 0 - , goldenCodeLens recorder "Type family" "TypeFamily" 0 + goldenCodeLens "Don't insert pragma while GHC2021 enabled" "CodeLensWithGHC2021" 0 + , goldenCodeLens "Qualified name" "Qualified" 0 + , goldenCodeLens "Type family" "TypeFamily" 0 ] _CACodeAction :: Prism' (Command |? CodeAction) CodeAction @@ -108,16 +100,16 @@ _CACodeAction = prism' InR $ \case _ -> Nothing -goldenCodeLens :: Recorder (WithPriority Class.Log) -> TestName -> FilePath -> Int -> TestTree -goldenCodeLens recorder title path idx = - goldenWithHaskellDoc (classPlugin recorder) title testDataDir path "expected" "hs" $ \doc -> do +goldenCodeLens :: TestName -> FilePath -> Int -> TestTree +goldenCodeLens title path idx = + goldenWithHaskellDoc classPlugin title testDataDir path "expected" "hs" $ \doc -> do lens <- getCodeLenses doc executeCommand $ fromJust $ (lens !! idx) ^. J.command void $ skipManyTill anyMessage (message SWorkspaceApplyEdit) -goldenWithClass :: Recorder (WithPriority Class.Log) -> TestName -> FilePath -> FilePath -> ([CodeAction] -> Session ()) -> TestTree -goldenWithClass recorder title path desc act = - goldenWithHaskellDoc (classPlugin recorder) title testDataDir path (desc <.> "expected") "hs" $ \doc -> do +goldenWithClass ::TestName -> FilePath -> FilePath -> ([CodeAction] -> Session ()) -> TestTree +goldenWithClass title path desc act = + goldenWithHaskellDoc classPlugin title testDataDir path (desc <.> "expected") "hs" $ \doc -> do _ <- waitForDiagnosticsFromSource doc "typecheck" actions <- concatMap (^.. _CACodeAction) <$> getAllCodeActions doc act actions diff --git a/plugins/hls-code-range-plugin/test/Main.hs b/plugins/hls-code-range-plugin/test/Main.hs index 5ad43de5f2..2b5f018e4f 100644 --- a/plugins/hls-code-range-plugin/test/Main.hs +++ b/plugins/hls-code-range-plugin/test/Main.hs @@ -18,19 +18,18 @@ import Language.LSP.Types.Lens import System.FilePath ((<.>), ()) import Test.Hls -plugin :: Recorder (WithPriority Log) -> PluginDescriptor IdeState -plugin recorder = descriptor recorder "codeRange" +plugin :: PluginTestDescriptor Log +plugin = mkPluginTestDescriptor descriptor "codeRange" main :: IO () main = do - recorder <- contramap (fmap pretty) <$> makeDefaultStderrRecorder Nothing Debug defaultTestRunner $ testGroup "Code Range" [ testGroup "Integration Tests" [ - selectionRangeGoldenTest recorder "Import" [(4, 36), (1, 8)], - selectionRangeGoldenTest recorder "Function" [(5, 19), (5, 12), (4, 4), (3, 5)], - selectionRangeGoldenTest recorder "Empty" [(1, 5)], - foldingRangeGoldenTest recorder "Function" + selectionRangeGoldenTest "Import" [(4, 36), (1, 8)], + selectionRangeGoldenTest "Function" [(5, 19), (5, 12), (4, 4), (3, 5)], + selectionRangeGoldenTest "Empty" [(1, 5)], + foldingRangeGoldenTest "Function" ], testGroup "Unit Tests" [ Ide.Plugin.CodeRangeTest.testTree, @@ -38,9 +37,9 @@ main = do ] ] -selectionRangeGoldenTest :: Recorder (WithPriority Log) -> TestName -> [(UInt, UInt)] -> TestTree -selectionRangeGoldenTest recorder testName positions = goldenGitDiff testName (testDataDir testName <.> "golden" <.> "txt") $ do - res <- runSessionWithServer (plugin recorder) testDataDir $ do +selectionRangeGoldenTest :: TestName -> [(UInt, UInt)] -> TestTree +selectionRangeGoldenTest testName positions = goldenGitDiff testName (testDataDir testName <.> "golden" <.> "txt") $ do + res <- runSessionWithServer plugin testDataDir $ do doc <- openDoc (testName <.> "hs") "haskell" resp <- request STextDocumentSelectionRange $ SelectionRangeParams Nothing Nothing doc (List $ fmap (uncurry Position . (\(x, y) -> (x-1, y-1))) positions) @@ -67,9 +66,9 @@ selectionRangeGoldenTest recorder testName positions = goldenGitDiff testName (t showPosition (Position line col) = "(" <> showLBS (line + 1) <> "," <> showLBS (col + 1) <> ")" showLBS = fromString . show -foldingRangeGoldenTest :: Recorder (WithPriority Log) -> TestName -> TestTree -foldingRangeGoldenTest recorder testName = goldenGitDiff testName (testDataDir testName <.> "golden" <.> "txt") $ do - res <- runSessionWithServer (plugin recorder) testDataDir $ do +foldingRangeGoldenTest :: TestName -> TestTree +foldingRangeGoldenTest testName = goldenGitDiff testName (testDataDir testName <.> "golden" <.> "txt") $ do + res <- runSessionWithServer plugin testDataDir $ do doc <- openDoc (testName <.> "hs") "haskell" resp <- request STextDocumentFoldingRange $ FoldingRangeParams Nothing Nothing doc let res = resp ^. result diff --git a/plugins/hls-eval-plugin/test/Main.hs b/plugins/hls-eval-plugin/test/Main.hs index cc2baa3ac6..df9c83b4ac 100644 --- a/plugins/hls-eval-plugin/test/Main.hs +++ b/plugins/hls-eval-plugin/test/Main.hs @@ -29,8 +29,8 @@ import Test.Hls main :: IO () main = defaultTestRunner tests -evalPlugin :: PluginDescriptor IdeState -evalPlugin = Eval.descriptor mempty "eval" +evalPlugin :: PluginTestDescriptor Eval.Log +evalPlugin = mkPluginTestDescriptor Eval.descriptor "eval" tests :: TestTree tests = diff --git a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs index 75e27856b5..29b30a94c2 100644 --- a/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs +++ b/plugins/hls-explicit-fixity-plugin/src/Ide/Plugin/ExplicitFixity.hs @@ -1,32 +1,32 @@ {-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE TupleSections #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -Wno-orphans #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} -module Ide.Plugin.ExplicitFixity(descriptor) where +module Ide.Plugin.ExplicitFixity(descriptor, Log) where import Control.DeepSeq -import Control.Monad.Trans.Maybe import Control.Monad.IO.Class (MonadIO, liftIO) +import Control.Monad.Trans.Maybe import Data.Either.Extra import Data.Hashable import qualified Data.Map.Strict as M -import qualified Data.Set as S import Data.Maybe +import qualified Data.Set as S import qualified Data.Text as T import Development.IDE hiding (pluginHandlers, pluginRules) import Development.IDE.Core.PositionMapping (idDelta) import Development.IDE.Core.Shake (addPersistentRule) import qualified Development.IDE.Core.Shake as Shake -import Development.IDE.Spans.AtPoint import Development.IDE.GHC.Compat import qualified Development.IDE.GHC.Compat.Util as Util import Development.IDE.LSP.Notifications (ghcideNotificationsPluginPriority) +import Development.IDE.Spans.AtPoint import GHC.Generics (Generic) import Ide.PluginUtils (getNormalizedFilePath, handleMaybeM, @@ -94,7 +94,7 @@ lookupFixities :: MonadIO m => HscEnv -> TcGblEnv -> S.Set Name -> m (M.Map Name lookupFixities hscEnv tcGblEnv names = liftIO $ fmap (fromMaybe M.empty . snd) - $ initTcWithGbl hscEnv tcGblEnv (realSrcLocSpan $ mkRealSrcLoc "" 1 1) + $ initTcWithGbl hscEnv tcGblEnv (realSrcLocSpan $ mkRealSrcLoc "" 1 1) $ M.traverseMaybeWithKey (\_ v -> v) $ M.fromSet lookupFixity names where diff --git a/plugins/hls-explicit-fixity-plugin/test/Main.hs b/plugins/hls-explicit-fixity-plugin/test/Main.hs index 82d374029f..c62f368e6d 100644 --- a/plugins/hls-explicit-fixity-plugin/test/Main.hs +++ b/plugins/hls-explicit-fixity-plugin/test/Main.hs @@ -3,12 +3,12 @@ module Main where import qualified Data.Text as T -import Ide.Plugin.ExplicitFixity (descriptor) +import Ide.Plugin.ExplicitFixity (Log, descriptor) import System.FilePath import Test.Hls -plugin :: PluginDescriptor IdeState -plugin = descriptor mempty "explicit-fixity" +plugin :: PluginTestDescriptor Log +plugin = mkPluginTestDescriptor descriptor "explicit-fixity" main :: IO () main = defaultTestRunner tests diff --git a/plugins/hls-explicit-imports-plugin/test/Main.hs b/plugins/hls-explicit-imports-plugin/test/Main.hs index 498ee975fd..c52f1f7d33 100644 --- a/plugins/hls-explicit-imports-plugin/test/Main.hs +++ b/plugins/hls-explicit-imports-plugin/test/Main.hs @@ -15,8 +15,8 @@ import qualified Ide.Plugin.ExplicitImports as ExplicitImports import System.FilePath ((<.>), ()) import Test.Hls -explicitImportsPlugin :: PluginDescriptor IdeState -explicitImportsPlugin = ExplicitImports.descriptor mempty "explicitImports" +explicitImportsPlugin :: PluginTestDescriptor ExplicitImports.Log +explicitImportsPlugin = mkPluginTestDescriptor ExplicitImports.descriptor "explicitImports" longModule :: T.Text longModule = "F" <> T.replicate 80 "o" diff --git a/plugins/hls-explicit-record-fields-plugin/src/Ide/Plugin/ExplicitFields.hs b/plugins/hls-explicit-record-fields-plugin/src/Ide/Plugin/ExplicitFields.hs index b77281f05a..e2bf77265d 100644 --- a/plugins/hls-explicit-record-fields-plugin/src/Ide/Plugin/ExplicitFields.hs +++ b/plugins/hls-explicit-record-fields-plugin/src/Ide/Plugin/ExplicitFields.hs @@ -11,6 +11,7 @@ module Ide.Plugin.ExplicitFields ( descriptor + , Log ) where import Control.Lens ((^.)) diff --git a/plugins/hls-explicit-record-fields-plugin/test/Main.hs b/plugins/hls-explicit-record-fields-plugin/test/Main.hs index c31c45223b..2955c5bc4d 100644 --- a/plugins/hls-explicit-record-fields-plugin/test/Main.hs +++ b/plugins/hls-explicit-record-fields-plugin/test/Main.hs @@ -15,8 +15,8 @@ import Test.Hls main :: IO () main = defaultTestRunner test -plugin :: PluginDescriptor IdeState -plugin = ExplicitFields.descriptor mempty "explicit-fields" +plugin :: PluginTestDescriptor ExplicitFields.Log +plugin = mkPluginTestDescriptor ExplicitFields.descriptor "explicit-fields" test :: TestTree test = testGroup "explicit-fields" diff --git a/plugins/hls-floskell-plugin/test/Main.hs b/plugins/hls-floskell-plugin/test/Main.hs index 155291eec4..908139f377 100644 --- a/plugins/hls-floskell-plugin/test/Main.hs +++ b/plugins/hls-floskell-plugin/test/Main.hs @@ -10,8 +10,8 @@ import Test.Hls main :: IO () main = defaultTestRunner tests -floskellPlugin :: PluginDescriptor IdeState -floskellPlugin = Floskell.descriptor "floskell" +floskellPlugin :: PluginTestDescriptor () +floskellPlugin = mkPluginTestDescriptor' Floskell.descriptor "floskell" tests :: TestTree tests = testGroup "floskell" diff --git a/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs b/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs index 96c945386e..8dd8611397 100644 --- a/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs +++ b/plugins/hls-fourmolu-plugin/src/Ide/Plugin/Fourmolu.hs @@ -9,6 +9,7 @@ module Ide.Plugin.Fourmolu ( descriptor, provider, + LogEvent, ) where import Control.Exception (IOException, try) diff --git a/plugins/hls-fourmolu-plugin/test/Main.hs b/plugins/hls-fourmolu-plugin/test/Main.hs index 872126f3a2..056003cc7e 100644 --- a/plugins/hls-fourmolu-plugin/test/Main.hs +++ b/plugins/hls-fourmolu-plugin/test/Main.hs @@ -15,8 +15,8 @@ import Test.Hls main :: IO () main = defaultTestRunner tests -fourmoluPlugin :: PluginDescriptor IdeState -fourmoluPlugin = Fourmolu.descriptor mempty "fourmolu" +fourmoluPlugin :: PluginTestDescriptor Fourmolu.LogEvent +fourmoluPlugin = mkPluginTestDescriptor Fourmolu.descriptor "fourmolu" tests :: TestTree tests = diff --git a/plugins/hls-gadt-plugin/test/Main.hs b/plugins/hls-gadt-plugin/test/Main.hs index bcde384232..ec4f901736 100644 --- a/plugins/hls-gadt-plugin/test/Main.hs +++ b/plugins/hls-gadt-plugin/test/Main.hs @@ -15,8 +15,8 @@ import Test.Hls main :: IO () main = defaultTestRunner tests -gadtPlugin :: PluginDescriptor IdeState -gadtPlugin = GADT.descriptor "GADT" +gadtPlugin :: PluginTestDescriptor () +gadtPlugin = mkPluginTestDescriptor' GADT.descriptor "GADT" tests :: TestTree tests = testGroup "GADT" diff --git a/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments.hs b/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments.hs index 66ea479416..2e9f4a5149 100644 --- a/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments.hs +++ b/plugins/hls-haddock-comments-plugin/src/Ide/Plugin/HaddockComments.hs @@ -7,7 +7,7 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ViewPatterns #-} -module Ide.Plugin.HaddockComments (descriptor) where +module Ide.Plugin.HaddockComments (descriptor, E.Log) where import Control.Monad (join, when) import Control.Monad.IO.Class diff --git a/plugins/hls-haddock-comments-plugin/test/Main.hs b/plugins/hls-haddock-comments-plugin/test/Main.hs index eaf10903a0..7df393abf6 100644 --- a/plugins/hls-haddock-comments-plugin/test/Main.hs +++ b/plugins/hls-haddock-comments-plugin/test/Main.hs @@ -18,8 +18,8 @@ import Test.Hls main :: IO () main = defaultTestRunner tests -haddockCommentsPlugin :: PluginDescriptor IdeState -haddockCommentsPlugin = HaddockComments.descriptor mempty "haddockComments" +haddockCommentsPlugin :: PluginTestDescriptor HaddockComments.Log +haddockCommentsPlugin = mkPluginTestDescriptor HaddockComments.descriptor "haddockComments" tests :: TestTree tests = diff --git a/plugins/hls-hlint-plugin/test/Main.hs b/plugins/hls-hlint-plugin/test/Main.hs index ee1ab380d6..966aa68655 100644 --- a/plugins/hls-hlint-plugin/test/Main.hs +++ b/plugins/hls-hlint-plugin/test/Main.hs @@ -24,8 +24,8 @@ import Test.Hls main :: IO () main = defaultTestRunner tests -hlintPlugin :: PluginDescriptor IdeState -hlintPlugin = HLint.descriptor mempty "hlint" +hlintPlugin :: PluginTestDescriptor HLint.Log +hlintPlugin = mkPluginTestDescriptor HLint.descriptor "hlint" tests :: TestTree tests = testGroup "hlint" [ @@ -101,7 +101,7 @@ suggestionsTests = contents <- skipManyTill anyMessage $ getDocumentEdit doc liftIO $ contents @?= "main = undefined\nfoo x = x\n" - , testCase "falls back to pre 3.8 code actions" $ runSessionWithServer' [hlintPlugin] def def noLiteralCaps "test/testdata" $ do + , testCase "falls back to pre 3.8 code actions" $ runSessionWithServerAndCaps hlintPlugin noLiteralCaps "test/testdata" $ do doc <- openDoc "Base.hs" "haskell" _ <- waitForDiagnosticsFromSource doc "hlint" diff --git a/plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs b/plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs index e2083b2114..d520da077e 100644 --- a/plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs +++ b/plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs @@ -14,6 +14,7 @@ Provide CodeLenses to: -} module Ide.Plugin.ModuleName ( descriptor, + Log, ) where import Control.Monad (forM_, void) diff --git a/plugins/hls-module-name-plugin/test/Main.hs b/plugins/hls-module-name-plugin/test/Main.hs index 914fcb69dd..06da6aefcf 100644 --- a/plugins/hls-module-name-plugin/test/Main.hs +++ b/plugins/hls-module-name-plugin/test/Main.hs @@ -12,8 +12,8 @@ import Test.Hls main :: IO () main = defaultTestRunner tests -moduleNamePlugin :: PluginDescriptor IdeState -moduleNamePlugin = ModuleName.descriptor mempty "moduleName" +moduleNamePlugin :: PluginTestDescriptor ModuleName.Log +moduleNamePlugin = mkPluginTestDescriptor ModuleName.descriptor "moduleName" tests :: TestTree tests = diff --git a/plugins/hls-ormolu-plugin/test/Main.hs b/plugins/hls-ormolu-plugin/test/Main.hs index bc637bd4dc..f03b65719d 100644 --- a/plugins/hls-ormolu-plugin/test/Main.hs +++ b/plugins/hls-ormolu-plugin/test/Main.hs @@ -11,8 +11,8 @@ import Test.Hls main :: IO () main = defaultTestRunner tests -ormoluPlugin :: PluginDescriptor IdeState -ormoluPlugin = Ormolu.descriptor "ormolu" +ormoluPlugin :: PluginTestDescriptor () +ormoluPlugin = mkPluginTestDescriptor' Ormolu.descriptor "ormolu" tests :: TestTree tests = testGroup "ormolu" diff --git a/plugins/hls-pragmas-plugin/test/Main.hs b/plugins/hls-pragmas-plugin/test/Main.hs index 0b5941a88a..4285062f05 100644 --- a/plugins/hls-pragmas-plugin/test/Main.hs +++ b/plugins/hls-pragmas-plugin/test/Main.hs @@ -15,8 +15,8 @@ import Test.Hls.Util (onlyWorkForGhcVersions) main :: IO () main = defaultTestRunner tests -pragmasPlugin :: PluginDescriptor IdeState -pragmasPlugin = descriptor "pragmas" +pragmasPlugin :: PluginTestDescriptor () +pragmasPlugin = mkPluginTestDescriptor' descriptor "pragmas" tests :: TestTree tests = diff --git a/plugins/hls-qualify-imported-names-plugin/test/Main.hs b/plugins/hls-qualify-imported-names-plugin/test/Main.hs index 3f118ecc46..38409c218e 100644 --- a/plugins/hls-qualify-imported-names-plugin/test/Main.hs +++ b/plugins/hls-qualify-imported-names-plugin/test/Main.hs @@ -15,6 +15,7 @@ import Test.Hls (CodeAction (CodeAction, _title Command (Command), IdeState, MonadIO (liftIO), PluginDescriptor, + PluginTestDescriptor, Position (Position), Range (Range), Session, TestName, TestTree, @@ -23,8 +24,10 @@ import Test.Hls (CodeAction (CodeAction, _title defaultTestRunner, executeCodeAction, getCodeActions, - goldenWithHaskellDoc, openDoc, - rename, runSessionWithServer, + goldenWithHaskellDoc, + mkPluginTestDescriptor', + openDoc, rename, + runSessionWithServer, testCase, testGroup, type (|?) (InR), (@?=)) @@ -126,8 +129,8 @@ codeActionGoldenTest testCaseName goldenFilename point = testDataDir :: String testDataDir = "test" "data" -pluginDescriptor :: PluginDescriptor IdeState -pluginDescriptor = QualifyImportedNames.descriptor "qualifyImportedNames" +pluginDescriptor :: PluginTestDescriptor () +pluginDescriptor = mkPluginTestDescriptor' QualifyImportedNames.descriptor "qualifyImportedNames" getCodeActionTitle :: (Command |? CodeAction) -> Maybe Text getCodeActionTitle commandOrCodeAction diff --git a/plugins/hls-refactor-plugin/test/Main.hs b/plugins/hls-refactor-plugin/test/Main.hs index e1b9fe9de7..5d9baa0c21 100644 --- a/plugins/hls-refactor-plugin/test/Main.hs +++ b/plugins/hls-refactor-plugin/test/Main.hs @@ -64,14 +64,17 @@ import qualified Test.AddArgument main :: IO () main = defaultTestRunner tests -refactorPlugin :: [PluginDescriptor IdeState] -refactorPlugin = - [ Refactor.iePluginDescriptor mempty "ghcide-code-actions-imports-exports" - , Refactor.typeSigsPluginDescriptor mempty "ghcide-code-actions-type-signatures" - , Refactor.bindingsPluginDescriptor mempty "ghcide-code-actions-bindings" - , Refactor.fillHolePluginDescriptor mempty "ghcide-code-actions-fill-holes" - , Refactor.extendImportPluginDescriptor mempty "ghcide-completions-1" - ] ++ GhcIde.descriptors mempty +refactorPlugin :: IO [PluginDescriptor IdeState] +refactorPlugin = do + exactprintLog <- pluginTestRecorder + ghcideLog <- pluginTestRecorder + pure $ + [ Refactor.iePluginDescriptor exactprintLog "ghcide-code-actions-imports-exports" + , Refactor.typeSigsPluginDescriptor exactprintLog "ghcide-code-actions-type-signatures" + , Refactor.bindingsPluginDescriptor exactprintLog "ghcide-code-actions-bindings" + , Refactor.fillHolePluginDescriptor exactprintLog "ghcide-code-actions-fill-holes" + , Refactor.extendImportPluginDescriptor exactprintLog "ghcide-completions-1" + ] ++ GhcIde.descriptors ghcideLog tests :: TestTree tests = @@ -3729,7 +3732,9 @@ run' :: (FilePath -> Session a) -> IO a run' s = withTempDir $ \dir -> runInDir dir (s dir) runInDir :: FilePath -> Session a -> IO a -runInDir dir = runSessionWithServer' refactorPlugin def def lspTestCaps dir +runInDir dir act = do + plugin <- refactorPlugin + runSessionWithServer' plugin def def lspTestCaps dir act lspTestCaps :: ClientCapabilities lspTestCaps = fullCaps { _window = Just $ WindowClientCapabilities (Just True) Nothing Nothing } diff --git a/plugins/hls-refactor-plugin/test/Test/AddArgument.hs b/plugins/hls-refactor-plugin/test/Test/AddArgument.hs index b52e39d511..7bd26224af 100644 --- a/plugins/hls-refactor-plugin/test/Test/AddArgument.hs +++ b/plugins/hls-refactor-plugin/test/Test/AddArgument.hs @@ -64,7 +64,7 @@ mkGoldenAddArgTest' testFileName range varName = do liftIO $ actionTitle @?= ("Add argument ‘" <> varName <> "’ to function") executeCodeAction action goldenWithHaskellDoc - (Refactor.bindingsPluginDescriptor mempty "ghcide-code-actions-bindings") + (mkPluginTestDescriptor Refactor.bindingsPluginDescriptor "ghcide-code-actions-bindings") (testFileName <> " (golden)") "test/data/golden/add-arg" testFileName diff --git a/plugins/hls-refine-imports-plugin/test/Main.hs b/plugins/hls-refine-imports-plugin/test/Main.hs index bbd1ad6958..20df99f96a 100644 --- a/plugins/hls-refine-imports-plugin/test/Main.hs +++ b/plugins/hls-refine-imports-plugin/test/Main.hs @@ -23,8 +23,8 @@ main = defaultTestRunner $ , codeLensGoldenTest "UsualCase" 1 ] -refineImportsPlugin :: PluginDescriptor IdeState -refineImportsPlugin = RefineImports.descriptor mempty "refineImports" +refineImportsPlugin :: PluginTestDescriptor RefineImports.Log +refineImportsPlugin = mkPluginTestDescriptor RefineImports.descriptor "refineImports" -- code action tests diff --git a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs index a752433e4a..bb3da0fe81 100644 --- a/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs +++ b/plugins/hls-rename-plugin/src/Ide/Plugin/Rename.hs @@ -9,7 +9,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} -module Ide.Plugin.Rename (descriptor) where +module Ide.Plugin.Rename (descriptor, E.Log) where #if MIN_VERSION_ghc(9,2,1) import GHC.Parser.Annotation (AnnContext, AnnList, diff --git a/plugins/hls-rename-plugin/test/Main.hs b/plugins/hls-rename-plugin/test/Main.hs index 5d662b1ad6..0896d9d5bb 100644 --- a/plugins/hls-rename-plugin/test/Main.hs +++ b/plugins/hls-rename-plugin/test/Main.hs @@ -12,8 +12,8 @@ import Test.Hls main :: IO () main = defaultTestRunner tests -renamePlugin :: PluginDescriptor IdeState -renamePlugin = Rename.descriptor mempty "rename" +renamePlugin :: PluginTestDescriptor Rename.Log +renamePlugin = mkPluginTestDescriptor Rename.descriptor "rename" -- See https://github.com/wz1000/HieDb/issues/45 recordConstructorIssue :: String diff --git a/plugins/hls-splice-plugin/test/Main.hs b/plugins/hls-splice-plugin/test/Main.hs index a33d3b4211..492e68100c 100644 --- a/plugins/hls-splice-plugin/test/Main.hs +++ b/plugins/hls-splice-plugin/test/Main.hs @@ -21,8 +21,8 @@ import Test.Hls main :: IO () main = defaultTestRunner tests -splicePlugin :: PluginDescriptor IdeState -splicePlugin = Splice.descriptor "splice" +splicePlugin :: PluginTestDescriptor () +splicePlugin = mkPluginTestDescriptor' Splice.descriptor "splice" tests :: TestTree tests = testGroup "splice" diff --git a/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs b/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs index db2f18b9ef..334c56a7cb 100644 --- a/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs +++ b/plugins/hls-stan-plugin/src/Ide/Plugin/Stan.hs @@ -1,4 +1,4 @@ -module Ide.Plugin.Stan (descriptor) where +module Ide.Plugin.Stan (descriptor, Log) where import Control.DeepSeq (NFData) import Control.Monad (void) diff --git a/plugins/hls-stan-plugin/test/Main.hs b/plugins/hls-stan-plugin/test/Main.hs index 3f6c5e9bad..48e9128329 100644 --- a/plugins/hls-stan-plugin/test/Main.hs +++ b/plugins/hls-stan-plugin/test/Main.hs @@ -38,8 +38,8 @@ tests = testDir :: FilePath testDir = "test/testdata" -stanPlugin :: PluginDescriptor IdeState -stanPlugin = Stan.descriptor mempty "stan" +stanPlugin :: PluginTestDescriptor Stan.Log +stanPlugin = mkPluginTestDescriptor Stan.descriptor "stan" runStanSession :: FilePath -> Session a -> IO a runStanSession subdir = diff --git a/plugins/hls-stylish-haskell-plugin/test/Main.hs b/plugins/hls-stylish-haskell-plugin/test/Main.hs index 236b705c42..bd6f55e9e6 100644 --- a/plugins/hls-stylish-haskell-plugin/test/Main.hs +++ b/plugins/hls-stylish-haskell-plugin/test/Main.hs @@ -10,8 +10,8 @@ import Test.Hls main :: IO () main = defaultTestRunner tests -stylishHaskellPlugin :: PluginDescriptor IdeState -stylishHaskellPlugin = StylishHaskell.descriptor "stylishHaskell" +stylishHaskellPlugin :: PluginTestDescriptor () +stylishHaskellPlugin = mkPluginTestDescriptor' StylishHaskell.descriptor "stylishHaskell" tests :: TestTree tests = testGroup "stylish-haskell" diff --git a/plugins/hls-tactics-plugin/old/test/Utils.hs b/plugins/hls-tactics-plugin/old/test/Utils.hs index db31d910cf..becc2ad3be 100644 --- a/plugins/hls-tactics-plugin/old/test/Utils.hs +++ b/plugins/hls-tactics-plugin/old/test/Utils.hs @@ -34,8 +34,8 @@ import Wingman.LanguageServer (mkShowMessageParams) import Wingman.Types -plugin :: PluginDescriptor IdeState -plugin = Tactic.descriptor mempty "tactics" +plugin :: PluginTestDescriptor Log +plugin = mkPluginTestDescriptor Tactic.descriptor "tactics" ------------------------------------------------------------------------------ -- | Get a range at the given line and column corresponding to having nothing @@ -61,13 +61,15 @@ resetGlobalHoleRef = writeIORef globalHoleRef 0 runSessionForTactics :: Session a -> IO a -runSessionForTactics = +runSessionForTactics act = do + recorder <- pluginTestRecorder runSessionWithServer' - [plugin] + [plugin recorder] def (def { messageTimeout = 20 } ) fullCaps tacticPath + act ------------------------------------------------------------------------------ -- | Make a tactic unit test. diff --git a/src/HlsPlugins.hs b/src/HlsPlugins.hs index 6fe2e4ef24..21f1ec4e9b 100644 --- a/src/HlsPlugins.hs +++ b/src/HlsPlugins.hs @@ -85,19 +85,19 @@ import qualified Ide.Plugin.CodeRange as CodeRange #endif #if hls_changeTypeSignature -import Ide.Plugin.ChangeTypeSignature as ChangeTypeSignature +import qualified Ide.Plugin.ChangeTypeSignature as ChangeTypeSignature #endif #if hls_gadt -import Ide.Plugin.GADT as GADT +import qualified Ide.Plugin.GADT as GADT #endif #if explicitFixity -import Ide.Plugin.ExplicitFixity as ExplicitFixity +import qualified Ide.Plugin.ExplicitFixity as ExplicitFixity #endif #if explicitFields -import Ide.Plugin.ExplicitFields as ExplicitFields +import qualified Ide.Plugin.ExplicitFields as ExplicitFields #endif -- formatters @@ -182,7 +182,7 @@ idePlugins recorder = pluginDescToIdePlugins allPlugins Brittany.descriptor "brittany" : #endif #if hls_callHierarchy - CallHierarchy.descriptor : + CallHierarchy.descriptor "callHierarchy" : #endif #if hls_class let pId = "class" in Class.descriptor (pluginRecorder pId) pId: @@ -221,7 +221,7 @@ idePlugins recorder = pluginDescToIdePlugins allPlugins let pId = "codeRange" in CodeRange.descriptor (pluginRecorder pId) pId: #endif #if hls_changeTypeSignature - ChangeTypeSignature.descriptor : + ChangeTypeSignature.descriptor "changeTypeSignature" : #endif #if hls_gadt GADT.descriptor "gadt" : From 389315413c65946bb5ded2ea90cb4c6758517d3f Mon Sep 17 00:00:00 2001 From: fendor Date: Thu, 1 Dec 2022 11:42:51 +0100 Subject: [PATCH 203/213] Bump gha versions in setup-build/action.yml (#3366) * Bump gha versions in setup-build/action.yml * Also bump cache for compiled-deps action * Make sure caching is run for the same GHC versions as test.yml * Specify a GHC in pre-commit.yml --- .github/actions/setup-build/action.yml | 6 +++--- .github/workflows/caching.yml | 9 ++++++++- .github/workflows/pre-commit.yml | 2 ++ .github/workflows/test.yml | 16 ++++++++++++---- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/.github/actions/setup-build/action.yml b/.github/actions/setup-build/action.yml index 0bc80aca7f..0b41e17f7a 100644 --- a/.github/actions/setup-build/action.yml +++ b/.github/actions/setup-build/action.yml @@ -23,7 +23,7 @@ inputs: runs: using: "composite" steps: - - uses: haskell/actions/setup@v1 + - uses: haskell/actions/setup@v2 id: HaskEnvSetup with: ghc-version : ${{ inputs.ghc }} @@ -74,7 +74,7 @@ runs: # We have to restore package sources before `cabal update` # because it overwrites the hackage index with the cached one - name: Hackage sources cache - uses: actions/cache@v2 + uses: actions/cache@v3 env: cache-name: hackage-sources with: @@ -99,7 +99,7 @@ runs: - name: Compiled deps cache id: compiled-deps - uses: actions/cache@v2 + uses: actions/cache@v3 env: cache-name: compiled-deps with: diff --git a/.github/workflows/caching.yml b/.github/workflows/caching.yml index b0887b51b9..b75a0cb71b 100644 --- a/.github/workflows/caching.yml +++ b/.github/workflows/caching.yml @@ -80,7 +80,11 @@ jobs: strategy: fail-fast: false matrix: - ghc: [ "9.2.4" + # This list of GHC versions must fit to the list of GHC versions + # specified in 'test.yml' + ghc: [ "9.4.2" + , "9.4.1" + , "9.2.4" , "9.2.3" , "9.0.2" , "8.10.7" @@ -89,6 +93,9 @@ jobs: , "macOS-latest" , "windows-latest" ] + exclude: + - os: windows-latest + ghc: '9.4.1' steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 43122276fa..0136d48441 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -26,6 +26,8 @@ jobs: - uses: actions/checkout@v3 - uses: ./.github/actions/setup-build with: + # select a stable GHC version + ghc: 9.2.5 os: ${{ runner.os }} shorten-hls: false diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fb9022e113..9385f5af36 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,6 +56,9 @@ jobs: runs-on: ${{ matrix.os }} strategy: fail-fast: true + # when you edit this list of GHC versions, + # **don't forget** + # to update the ghc versions in 'caching.yml'. matrix: ghc: [ "9.4.2" , "9.4.1" @@ -66,7 +69,15 @@ jobs: ] os: [ "ubuntu-latest" , "macOS-latest" + , "windows-latest" ] + # don't build these versions + # they are broken for good reasons, e.g. compiler is bugged + # on that platform. + exclude: + - os: windows-latest + ghc: '9.4.1' + # Mark which GHC versions on which platform we want to test. include: # only test supported ghc major versions - os: ubuntu-latest @@ -93,9 +104,6 @@ jobs: - os: windows-latest ghc: '8.10.7' test: true - # only build rest of supported ghc versions for windows - - os: windows-latest - ghc: '9.2.3' steps: - uses: actions/checkout@v3 @@ -115,7 +123,7 @@ jobs: run: | echo "TEST_OPTS=-j1 --rerun-update --rerun-filter failures,exceptions" >> $GITHUB_ENV - - name: Cache test log bewteen attempts of the same run + - name: Cache test log between attempts of the same run uses: actions/cache@v3 env: cache-name: cache-test-log From 000a76cb951115d9bcdb08fc6d66b438d3bafce6 Mon Sep 17 00:00:00 2001 From: fendor Date: Fri, 2 Dec 2022 12:47:48 +0100 Subject: [PATCH 204/213] Delete dead code in hls-test-utils (#3368) * Remove dead and obsolete code * Organise export list with respect to utility functions Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- hls-test-utils/src/Test/Hls/Util.hs | 85 ++++++------------------- test/functional/FunctionalCodeAction.hs | 1 - test/wrapper/Main.hs | 4 +- 3 files changed, 20 insertions(+), 70 deletions(-) diff --git a/hls-test-utils/src/Test/Hls/Util.hs b/hls-test-utils/src/Test/Hls/Util.hs index 5ab1e093dd..e654ee9660 100644 --- a/hls-test-utils/src/Test/Hls/Util.hs +++ b/hls-test-utils/src/Test/Hls/Util.hs @@ -6,37 +6,40 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeOperators #-} module Test.Hls.Util - ( + ( -- * Test Capabilities codeActionSupportCaps , expectCodeAction - , dontExpectCodeAction - , expectDiagnostic - , expectNoMoreDiagnostics - , expectSameLocations - , failIfSessionTimeout - , flushStackEnvironment - , fromAction - , fromCommand - , getCompletionByLabel + -- * Environment specifications + -- for ignoring tests , ghcVersion, GhcVersion(..) , hostOS, OS(..) , matchesCurrentEnv, EnvSpec(..) - , noLiteralCaps , ignoreForGhcVersions , ignoreInEnv , onlyRunForGhcVersions - , inspectCodeAction - , inspectCommand - , inspectDiagnostic , knownBrokenOnWindows , knownBrokenForGhcVersions , knownBrokenInEnv , onlyWorkForGhcVersions - , setupBuildToolFiles + -- * Extract code actions + , fromAction + , fromCommand + -- * Session Assertion Helpers + , dontExpectCodeAction + , expectDiagnostic + , expectNoMoreDiagnostics + , expectSameLocations + , failIfSessionTimeout + , getCompletionByLabel + , noLiteralCaps + , inspectCodeAction + , inspectCommand + , inspectDiagnostic , SymbolLocation , waitForDiagnosticsFrom , waitForDiagnosticsFromSource , waitForDiagnosticsFromSourceWithTimeout + -- * Temporary directories , withCurrentDirectoryInTmp , withCurrentDirectoryInTmp' , withCanonicalTempDir @@ -61,7 +64,6 @@ import qualified Language.LSP.Types.Capabilities as C import Language.LSP.Types.Lens (textDocument) import qualified Language.LSP.Types.Lens as L import System.Directory -import System.Environment import System.FilePath import System.Info.Extra (isMac, isWindows) import qualified System.IO.Extra @@ -87,34 +89,9 @@ codeActionSupportCaps = def & textDocument ?~ textDocumentCaps literalSupport = CodeActionLiteralSupport def -- --------------------------------------------------------------------- - -setupBuildToolFiles :: IO () -setupBuildToolFiles = do - forM_ files setupDirectFilesIn - -setupDirectFilesIn :: FilePath -> IO () -setupDirectFilesIn f = - writeFile (f ++ "hie.yaml") hieYamlCradleDirectContents - - +-- Environment specification for ignoring tests -- --------------------------------------------------------------------- -files :: [FilePath] -files = - [ "./test/testdata/" - -- , "./test/testdata/addPackageTest/cabal-exe/" - -- , "./test/testdata/addPackageTest/hpack-exe/" - -- , "./test/testdata/addPackageTest/cabal-lib/" - -- , "./test/testdata/addPackageTest/hpack-lib/" - -- , "./test/testdata/addPragmas/" - -- , "./test/testdata/badProjects/cabal/" - -- , "./test/testdata/completion/" - -- , "./test/testdata/definition/" - -- , "./test/testdata/gototest/" - -- , "./test/testdata/redundantImportTest/" - -- , "./test/testdata/wErrorTest/" - ] - data EnvSpec = HostOS OS | GhcVer GhcVersion deriving (Show, Eq) @@ -168,30 +145,6 @@ onlyRunForGhcVersions vers = -- --------------------------------------------------------------------- -hieYamlCradleDirectContents :: String -hieYamlCradleDirectContents = unlines - [ "# WARNING: THIS FILE IS AUTOGENERATED IN test/utils/TestUtils.hs. IT WILL BE OVERWRITTEN ON EVERY TEST RUN" - , "cradle:" - , " direct:" - , " arguments:" - , " - -i." - ] - - --- --------------------------------------------------------------------- - -flushStackEnvironment :: IO () -flushStackEnvironment = do - -- We need to clear these environment variables to prevent - -- collisions with stack usages - -- See https://github.com/commercialhaskell/stack/issues/4875 - unsetEnv "GHC_PACKAGE_PATH" - unsetEnv "GHC_ENVIRONMENT" - unsetEnv "HASKELL_PACKAGE_SANDBOX" - unsetEnv "HASKELL_PACKAGE_SANDBOXES" - --- --------------------------------------------------------------------- - -- | Like 'withCurrentDirectory', but will copy the directory over to the system -- temporary directory first to avoid haskell-language-server's source tree from -- interfering with the cradle. diff --git a/test/functional/FunctionalCodeAction.hs b/test/functional/FunctionalCodeAction.hs index b28038cc5b..c6cd481705 100644 --- a/test/functional/FunctionalCodeAction.hs +++ b/test/functional/FunctionalCodeAction.hs @@ -106,7 +106,6 @@ importTests = testGroup "import suggestions" [ packageTests :: TestTree packageTests = testGroup "add package suggestions" [ ignoreTestBecause "no support for adding dependent packages via code action" $ testCase "adds to .cabal files" $ do - flushStackEnvironment runSession hlsCommand fullCaps "test/testdata/addPackageTest/cabal-exe" $ do doc <- openDoc "AddPackage.hs" "haskell" diff --git a/test/wrapper/Main.hs b/test/wrapper/Main.hs index 6c68440a5f..ca652bbf0f 100644 --- a/test/wrapper/Main.hs +++ b/test/wrapper/Main.hs @@ -5,9 +5,7 @@ import System.Process import Test.Hls main :: IO () -main = do - flushStackEnvironment - defaultTestRunner $ testGroup "haskell-language-server-wrapper" [projectGhcVersionTests] +main = defaultTestRunner $ testGroup "haskell-language-server-wrapper" [projectGhcVersionTests] projectGhcVersionTests :: TestTree projectGhcVersionTests = testGroup "--project-ghc-version" From cc1b90d9b7fa7dcc1baecbfacef14afcdd5d43ed Mon Sep 17 00:00:00 2001 From: fendor Date: Fri, 2 Dec 2022 15:47:32 +0100 Subject: [PATCH 205/213] Reword intro section in releases.md (#3378) --- docs/contributing/releases.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/contributing/releases.md b/docs/contributing/releases.md index b7c47c6d4d..a429f01440 100644 --- a/docs/contributing/releases.md +++ b/docs/contributing/releases.md @@ -1,19 +1,21 @@ # Releases and distributable binaries Starting with 0.2.1.0 haskell-language-server provides pre-built binaries on -each [GitHub -release](https://github.com/haskell/haskell-language-server/releases). These -binaries are used by the [vscode-hie-server -extension](https://github.com/alanz/vscode-hie-server) to provide automatic -installation for users on VS Code, but they can also be installed manually +each [GitHub release](https://github.com/haskell/haskell-language-server/releases). +These binaries are used by the [vscode-haskell extension](https://github.com/haskell/vscode-haskell) +to provide automatic installation for users on VS Code, but they can also be installed manually when added to the path. Starting with 0.8.0.0 haskell-language-server and all its related packages -(core libraries like ghcide, plugins and hls itself) is being released in +(core libraries like ghcide, plugins and hls itself) is being released on [hackage](https://hackage.haskell.org/package/haskell-language-server) as well. -This allow cabal users to install it with `cabal install haskell-language-server` +This allows cabal users to install it with `cabal install haskell-language-server` and it is being used in nix environments. +Since 1.7.0.0, HLS binaries are no longer uploaded to GitHub but to [downloads.haskell.org](https://downloads.haskell.org/~hls/). +[GHCup](https://www.haskell.org/ghcup/) uses these binaries to enable automatic installation of HLS binaries in +various lsp-client plugins, such as [vscode-haskell](https://github.com/haskell/vscode-haskell). + ## Minimal checklist ### prerelease sanity checks From 5d5f7e42d4edf3f203f5831a25d8db28d2871965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Berk=20=C3=96zk=C3=BCt=C3=BCk?= Date: Fri, 2 Dec 2022 17:46:44 +0100 Subject: [PATCH 206/213] Make redundant import removal work on PatSyn imports (#3377) * Make redundant import removal work on patsyn imports * Make stylish-haskell happy Co-authored-by: Michael Peyton Jones --- .../src/Development/IDE/Plugin/CodeAction.hs | 14 ++++++++++- test/functional/FunctionalCodeAction.hs | 23 +++++++++++++++---- .../src/CodeActionRedundant.hs | 5 ++++ 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index 3a45c0f154..2b98b95a77 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -1841,7 +1841,19 @@ smallerRangesForBindingExport lies b = ranges' _ = [] rangesForBinding' :: String -> LIE GhcPs -> [SrcSpan] -rangesForBinding' b (L (locA -> l) x@IEVar{}) | T.unpack (printOutputable x) == b = [l] +#if !MIN_VERSION_ghc(9,2,0) +rangesForBinding' b (L (locA -> l) (IEVar _ nm)) + | L _ (IEPattern (L _ b')) <- nm + , T.unpack (printOutputable b') == b + = [l] +#else +rangesForBinding' b (L (locA -> l) (IEVar _ nm)) + | L _ (IEPattern _ (L _ b')) <- nm + , T.unpack (printOutputable b') == b + = [l] +#endif +rangesForBinding' b (L (locA -> l) x@IEVar{}) + | T.unpack (printOutputable x) == b = [l] rangesForBinding' b (L (locA -> l) x@IEThingAbs{}) | T.unpack (printOutputable x) == b = [l] rangesForBinding' b (L (locA -> l) (IEThingAll _ x)) | T.unpack (printOutputable x) == b = [l] #if !MIN_VERSION_ghc(9,2,0) diff --git a/test/functional/FunctionalCodeAction.hs b/test/functional/FunctionalCodeAction.hs index c6cd481705..da715f5817 100644 --- a/test/functional/FunctionalCodeAction.hs +++ b/test/functional/FunctionalCodeAction.hs @@ -176,16 +176,21 @@ redundantImportTests = testGroup "redundant import code actions" [ doc <- openDoc "src/CodeActionRedundant.hs" "haskell" diags <- waitForDiagnosticsFromSource doc "typecheck" - liftIO $ expectDiagnostic diags ["The import of", "Data.List", "is redundant"] + liftIO $ expectDiagnostic diags [ "The import of", "Data.List", "is redundant" ] + liftIO $ expectDiagnostic diags [ "Empty", "from module", "Data.Sequence" ] mActions <- getAllCodeActions doc let allActions = map fromAction mActions actionTitles = map (view L.title) allActions - liftIO $ actionTitles `shouldContain` ["Remove import", "Remove all redundant imports"] + liftIO $ actionTitles `shouldContain` + [ "Remove import" + , "Remove Empty from import" + , "Remove all redundant imports" + ] - let mbRemoveAction = find (\x -> x ^. L.title == "Remove import") allActions + let mbRemoveAction = find (\x -> x ^. L.title == "Remove all redundant imports") allActions case mbRemoveAction of Just removeAction -> do @@ -202,7 +207,17 @@ redundantImportTests = testGroup "redundant import code actions" [ -- provides workspace edit property which skips round trip to -- the server contents <- documentContents doc - liftIO $ contents @?= "{-# OPTIONS_GHC -Wunused-imports #-}\nmodule CodeActionRedundant where\nmain :: IO ()\nmain = putStrLn \"hello\"\n" + liftIO $ contents @?= T.unlines + [ "{-# OPTIONS_GHC -Wunused-imports #-}" + , "{-# LANGUAGE PatternSynonyms #-}" + , "module CodeActionRedundant where" + , "-- We need a non-reduntant import in the import list" + , "-- to properly test the removal of the singular redundant item" + , "import Data.Sequence (singleton)" + , "main :: IO ()" + , "main = putStrLn \"hello\"" + , " where unused = Data.Sequence.singleton 42" + ] , testCase "doesn't touch other imports" $ runSession hlsCommand noLiteralCaps "test/testdata/redundantImportTest/" $ do doc <- openDoc "src/MultipleImports.hs" "haskell" diff --git a/test/testdata/redundantImportTest/src/CodeActionRedundant.hs b/test/testdata/redundantImportTest/src/CodeActionRedundant.hs index f56d232f27..168868e3b9 100644 --- a/test/testdata/redundantImportTest/src/CodeActionRedundant.hs +++ b/test/testdata/redundantImportTest/src/CodeActionRedundant.hs @@ -1,5 +1,10 @@ {-# OPTIONS_GHC -Wunused-imports #-} +{-# LANGUAGE PatternSynonyms #-} module CodeActionRedundant where import Data.List +-- We need a non-reduntant import in the import list +-- to properly test the removal of the singular redundant item +import Data.Sequence (pattern Empty, singleton) main :: IO () main = putStrLn "hello" + where unused = Data.Sequence.singleton 42 From f3ad27ba1634871b2240b8cd7de9f31b91a2e502 Mon Sep 17 00:00:00 2001 From: santiweight Date: Sun, 4 Dec 2022 21:50:52 -0800 Subject: [PATCH 207/213] Wingman copy old to new (#3363) * wingman: make a copy of wingman in new directory * wip Co-authored-by: Santiago Weight --- .../hls-tactics-plugin.cabal | 20 +- .../new/src/Ide/Plugin/Tactic.hs | 5 + .../new/src/Refinery/Future.hs | 140 ++++ .../new/src/Wingman/AbstractLSP.hs | 266 +++++++ .../src/Wingman/AbstractLSP/TacticActions.hs | 178 +++++ .../new/src/Wingman/AbstractLSP/Types.hs | 169 +++++ .../new/src/Wingman/Auto.hs | 32 + .../new/src/Wingman/CaseSplit.hs | 109 +++ .../new/src/Wingman/CodeGen.hs | 346 +++++++++ .../new/src/Wingman/CodeGen/Utils.hs | 79 ++ .../new/src/Wingman/Context.hs | 112 +++ .../new/src/Wingman/Debug.hs | 65 ++ .../new/src/Wingman/EmptyCase.hs | 170 +++++ .../hls-tactics-plugin/new/src/Wingman/GHC.hs | 369 ++++++++++ .../new/src/Wingman/Judgements.hs | 474 ++++++++++++ .../new/src/Wingman/Judgements/SYB.hs | 98 +++ .../new/src/Wingman/Judgements/Theta.hs | 235 ++++++ .../new/src/Wingman/KnownStrategies.hs | 82 +++ .../src/Wingman/KnownStrategies/QuickCheck.hs | 109 +++ .../new/src/Wingman/LanguageServer.hs | 662 +++++++++++++++++ .../src/Wingman/LanguageServer/Metaprogram.hs | 59 ++ .../Wingman/LanguageServer/TacticProviders.hs | 309 ++++++++ .../new/src/Wingman/Machinery.hs | 450 ++++++++++++ .../new/src/Wingman/Metaprogramming/Lexer.hs | 99 +++ .../new/src/Wingman/Metaprogramming/Parser.hs | 501 +++++++++++++ .../Wingman/Metaprogramming/Parser.hs-boot | 7 + .../Metaprogramming/Parser/Documentation.hs | 237 ++++++ .../src/Wingman/Metaprogramming/ProofState.hs | 117 +++ .../new/src/Wingman/Naming.hs | 276 +++++++ .../new/src/Wingman/Plugin.hs | 46 ++ .../new/src/Wingman/Range.hs | 23 + .../new/src/Wingman/Simplify.hs | 113 +++ .../new/src/Wingman/StaticPlugin.hs | 111 +++ .../new/src/Wingman/Tactics.hs | 692 ++++++++++++++++++ .../new/src/Wingman/Types.hs | 562 ++++++++++++++ .../new/test/AutoTupleSpec.hs | 57 ++ .../new/test/CodeAction/AutoSpec.hs | 92 +++ .../new/test/CodeAction/DestructAllSpec.hs | 38 + .../new/test/CodeAction/DestructPunSpec.hs | 20 + .../new/test/CodeAction/DestructSpec.hs | 120 +++ .../new/test/CodeAction/IntroDestructSpec.hs | 36 + .../new/test/CodeAction/IntrosSpec.hs | 19 + .../new/test/CodeAction/RefineSpec.hs | 23 + .../new/test/CodeAction/RunMetaprogramSpec.hs | 43 ++ .../new/test/CodeAction/UseDataConSpec.hs | 42 ++ .../new/test/CodeLens/EmptyCaseSpec.hs | 25 + plugins/hls-tactics-plugin/new/test/Main.hs | 8 + .../new/test/ProviderSpec.hs | 28 + plugins/hls-tactics-plugin/new/test/Spec.hs | 1 + .../new/test/UnificationSpec.hs | 83 +++ plugins/hls-tactics-plugin/new/test/Utils.hs | 263 +++++++ .../test/golden/AutoEmptyString.expected.hs | 2 + .../new/test/golden/AutoEmptyString.hs | 2 + .../new/test/golden/AutoEndo.expected.hs | 11 + .../new/test/golden/AutoEndo.hs | 10 + .../golden/AutoForallClassMethod.expected.hs | 12 + .../new/test/golden/AutoForallClassMethod.hs | 12 + .../test/golden/AutoInfixApply.expected.hs | 3 + .../new/test/golden/AutoInfixApply.hs | 3 + .../golden/AutoInfixApplyMany.expected.hs | 3 + .../new/test/golden/AutoInfixApplyMany.hs | 3 + .../test/golden/AutoInfixInfix.expected.hs | 2 + .../new/test/golden/AutoInfixInfix.hs | 2 + .../new/test/golden/AutoPatSynUse.expected.hs | 8 + .../new/test/golden/AutoPatSynUse.hs | 8 + .../new/test/golden/AutoSplitGADT.expected.hs | 12 + .../new/test/golden/AutoSplitGADT.hs | 12 + .../test/golden/AutoThetaEqCtx.expected.hs | 5 + .../new/test/golden/AutoThetaEqCtx.hs | 5 + .../test/golden/AutoThetaEqGADT.expected.hs | 7 + .../new/test/golden/AutoThetaEqGADT.hs | 7 + .../AutoThetaEqGADTDestruct.expected.hs | 8 + .../test/golden/AutoThetaEqGADTDestruct.hs | 8 + .../new/test/golden/AutoThetaFix.expected.hs | 13 + .../new/test/golden/AutoThetaFix.hs | 13 + .../new/test/golden/AutoThetaGADT.expected.hs | 7 + .../new/test/golden/AutoThetaGADT.hs | 7 + .../golden/AutoThetaGADTDestruct.expected.hs | 7 + .../new/test/golden/AutoThetaGADTDestruct.hs | 7 + .../AutoThetaMultipleUnification.expected.hs | 21 + .../golden/AutoThetaMultipleUnification.hs | 21 + .../test/golden/AutoThetaRankN.expected.hs | 8 + .../new/test/golden/AutoThetaRankN.hs | 8 + .../new/test/golden/AutoThetaRefl.expected.hs | 7 + .../new/test/golden/AutoThetaRefl.hs | 7 + .../golden/AutoThetaReflDestruct.expected.hs | 8 + .../new/test/golden/AutoThetaReflDestruct.hs | 8 + .../AutoThetaSplitUnification.expected.hs | 17 + .../test/golden/AutoThetaSplitUnification.hs | 17 + .../new/test/golden/AutoTypeLevel.expected.hs | 21 + .../new/test/golden/AutoTypeLevel.hs | 20 + .../golden/AutoUnusedPatternMatch.expected.hs | 2 + .../new/test/golden/AutoUnusedPatternMatch.hs | 2 + .../new/test/golden/AutoZip.expected.hs | 6 + .../new/test/golden/AutoZip.hs | 3 + .../new/test/golden/ConProviders.hs | 21 + .../test/golden/DestructAllAnd.expected.hs | 5 + .../new/test/golden/DestructAllAnd.hs | 2 + .../test/golden/DestructAllFunc.expected.hs | 4 + .../new/test/golden/DestructAllFunc.hs | 3 + .../DestructAllGADTEvidence.expected.hs | 21 + .../test/golden/DestructAllGADTEvidence.hs | 20 + .../test/golden/DestructAllMany.expected.hs | 27 + .../new/test/golden/DestructAllMany.hs | 4 + .../DestructAllNonVarTopMatch.expected.hs | 6 + .../test/golden/DestructAllNonVarTopMatch.hs | 3 + .../new/test/golden/DestructAllProvider.hs | 12 + .../test/golden/DestructCthulhu.expected.hs | 54 ++ .../new/test/golden/DestructCthulhu.hs | 31 + .../test/golden/DestructDataFam.expected.hs | 8 + .../new/test/golden/DestructDataFam.hs | 8 + .../new/test/golden/DestructInt.expected.hs | 7 + .../new/test/golden/DestructInt.hs | 7 + .../new/test/golden/DestructPun.expected.hs | 8 + .../new/test/golden/DestructPun.hs | 7 + .../new/test/golden/DestructTyFam.expected.hs | 9 + .../new/test/golden/DestructTyFam.hs | 8 + .../golden/DestructTyToDataFam.expected.hs | 18 + .../new/test/golden/DestructTyToDataFam.hs | 18 + .../new/test/golden/EmptyCaseADT.expected.hs | 8 + .../new/test/golden/EmptyCaseADT.hs | 5 + .../test/golden/EmptyCaseApply.expected.hs | 3 + .../new/test/golden/EmptyCaseApply.hs | 1 + .../new/test/golden/EmptyCaseGADT.expected.hs | 13 + .../new/test/golden/EmptyCaseGADT.hs | 11 + .../test/golden/EmptyCaseLamCase.expected.hs | 6 + .../new/test/golden/EmptyCaseLamCase.hs | 4 + .../test/golden/EmptyCaseNested.expected.hs | 5 + .../new/test/golden/EmptyCaseNested.hs | 3 + .../test/golden/EmptyCaseParens.expected.hs | 3 + .../new/test/golden/EmptyCaseParens.hs | 1 + .../test/golden/EmptyCaseShadow.expected.hs | 10 + .../new/test/golden/EmptyCaseShadow.hs | 7 + .../new/test/golden/EmptyCaseSpuriousGADT.hs | 8 + .../new/test/golden/Fgmap.expected.hs | 2 + .../new/test/golden/Fgmap.hs | 2 + .../new/test/golden/FmapBoth.expected.hs | 3 + .../new/test/golden/FmapBoth.hs | 3 + .../new/test/golden/FmapJoin.expected.hs | 2 + .../new/test/golden/FmapJoin.hs | 2 + .../new/test/golden/FmapJoinInLet.expected.hs | 4 + .../new/test/golden/FmapJoinInLet.hs | 4 + .../new/test/golden/GoldenApplicativeThen.hs | 2 + .../test/golden/GoldenArbitrary.expected.hs | 53 ++ .../new/test/golden/GoldenArbitrary.hs | 26 + ...ldenArbitrarySingleConstructor.expected.hs | 7 + .../GoldenArbitrarySingleConstructor.hs | 6 + .../test/golden/GoldenBigTuple.expected.hs | 4 + .../new/test/golden/GoldenBigTuple.hs | 4 + .../test/golden/GoldenEitherAuto.expected.hs | 3 + .../new/test/golden/GoldenEitherAuto.hs | 2 + .../GoldenEitherHomomorphic.expected.hs | 3 + .../test/golden/GoldenEitherHomomorphic.hs | 2 + .../new/test/golden/GoldenFish.hs | 5 + .../test/golden/GoldenFmapTree.expected.hs | 5 + .../new/test/golden/GoldenFmapTree.hs | 4 + .../new/test/golden/GoldenFoldr.expected.hs | 3 + .../new/test/golden/GoldenFoldr.hs | 2 + .../test/golden/GoldenFromMaybe.expected.hs | 3 + .../new/test/golden/GoldenFromMaybe.hs | 2 + .../test/golden/GoldenGADTAuto.expected.hs | 7 + .../new/test/golden/GoldenGADTAuto.hs | 7 + .../golden/GoldenGADTDestruct.expected.hs | 7 + .../new/test/golden/GoldenGADTDestruct.hs | 7 + .../GoldenGADTDestructCoercion.expected.hs | 8 + .../test/golden/GoldenGADTDestructCoercion.hs | 8 + .../test/golden/GoldenIdTypeFam.expected.hs | 7 + .../new/test/golden/GoldenIdTypeFam.hs | 7 + .../golden/GoldenIdentityFunctor.expected.hs | 3 + .../new/test/golden/GoldenIdentityFunctor.hs | 3 + .../new/test/golden/GoldenIntros.expected.hs | 2 + .../new/test/golden/GoldenIntros.hs | 2 + .../test/golden/GoldenJoinCont.expected.hs | 4 + .../new/test/golden/GoldenJoinCont.hs | 4 + .../test/golden/GoldenListFmap.expected.hs | 3 + .../new/test/golden/GoldenListFmap.hs | 2 + .../new/test/golden/GoldenNote.expected.hs | 3 + .../new/test/golden/GoldenNote.hs | 2 + .../test/golden/GoldenPureList.expected.hs | 2 + .../new/test/golden/GoldenPureList.hs | 2 + .../test/golden/GoldenSafeHead.expected.hs | 3 + .../new/test/golden/GoldenSafeHead.hs | 2 + .../new/test/golden/GoldenShow.expected.hs | 2 + .../new/test/golden/GoldenShow.hs | 2 + .../test/golden/GoldenShowCompose.expected.hs | 2 + .../new/test/golden/GoldenShowCompose.hs | 2 + .../test/golden/GoldenShowMapChar.expected.hs | 2 + .../new/test/golden/GoldenShowMapChar.hs | 2 + .../test/golden/GoldenSuperclass.expected.hs | 8 + .../new/test/golden/GoldenSuperclass.hs | 8 + .../new/test/golden/GoldenSwap.expected.hs | 2 + .../new/test/golden/GoldenSwap.hs | 2 + .../test/golden/GoldenSwapMany.expected.hs | 2 + .../new/test/golden/GoldenSwapMany.hs | 2 + .../IntroDestructLetBinding.expected.hs | 6 + .../test/golden/IntroDestructLetBinding.hs | 5 + .../test/golden/IntroDestructMany.expected.hs | 4 + .../new/test/golden/IntroDestructMany.hs | 3 + .../test/golden/IntroDestructOne.expected.hs | 6 + .../new/test/golden/IntroDestructOne.hs | 5 + .../new/test/golden/IntroDestructProvider.hs | 9 + .../new/test/golden/IntrosTooMany.expected.hs | 2 + .../new/test/golden/IntrosTooMany.hs | 2 + .../test/golden/KnownBigSemigroup.expected.hs | 9 + .../new/test/golden/KnownBigSemigroup.hs | 7 + .../KnownCounterfactualSemigroup.expected.hs | 7 + .../golden/KnownCounterfactualSemigroup.hs | 7 + .../KnownDestructedSemigroup.expected.hs | 5 + .../test/golden/KnownDestructedSemigroup.hs | 5 + .../golden/KnownMissingMonoid.expected.hs | 8 + .../new/test/golden/KnownMissingMonoid.hs | 8 + .../golden/KnownMissingSemigroup.expected.hs | 5 + .../new/test/golden/KnownMissingSemigroup.hs | 5 + .../KnownModuleInstanceSemigroup.expected.hs | 12 + .../golden/KnownModuleInstanceSemigroup.hs | 11 + .../new/test/golden/KnownMonoid.expected.hs | 8 + .../new/test/golden/KnownMonoid.hs | 8 + .../test/golden/KnownPolyMonoid.expected.hs | 8 + .../new/test/golden/KnownPolyMonoid.hs | 8 + .../golden/KnownThetaSemigroup.expected.hs | 5 + .../new/test/golden/KnownThetaSemigroup.hs | 5 + .../new/test/golden/LayoutBind.expected.hs | 8 + .../new/test/golden/LayoutBind.hs | 6 + .../test/golden/LayoutDollarApp.expected.hs | 5 + .../new/test/golden/LayoutDollarApp.hs | 3 + .../test/golden/LayoutInfixKeep.expected.hs | 5 + .../new/test/golden/LayoutInfixKeep.hs | 4 + .../new/test/golden/LayoutLam.expected.hs | 5 + .../new/test/golden/LayoutLam.hs | 3 + .../new/test/golden/LayoutOpApp.expected.hs | 4 + .../new/test/golden/LayoutOpApp.hs | 2 + .../test/golden/LayoutPrefixKeep.expected.hs | 4 + .../new/test/golden/LayoutPrefixKeep.hs | 3 + .../new/test/golden/LayoutRec.expected.hs | 5 + .../new/test/golden/LayoutRec.hs | 5 + .../test/golden/LayoutSplitClass.expected.hs | 5 + .../new/test/golden/LayoutSplitClass.hs | 4 + .../test/golden/LayoutSplitGuard.expected.hs | 5 + .../new/test/golden/LayoutSplitGuard.hs | 3 + .../new/test/golden/LayoutSplitIn.expected.hs | 5 + .../new/test/golden/LayoutSplitIn.hs | 5 + .../test/golden/LayoutSplitLet.expected.hs | 7 + .../new/test/golden/LayoutSplitLet.hs | 6 + .../test/golden/LayoutSplitPatSyn.expected.hs | 11 + .../new/test/golden/LayoutSplitPatSyn.hs | 10 + .../golden/LayoutSplitPattern.expected.hs | 9 + .../new/test/golden/LayoutSplitPattern.hs | 8 + .../golden/LayoutSplitViewPat.expected.hs | 6 + .../new/test/golden/LayoutSplitViewPat.hs | 5 + .../test/golden/LayoutSplitWhere.expected.hs | 14 + .../new/test/golden/LayoutSplitWhere.hs | 12 + .../new/test/golden/MessageCantUnify.hs | 8 + .../new/test/golden/MessageForallA.hs | 2 + .../new/test/golden/MessageNotEnoughGas.hs | 13 + .../new/test/golden/MetaBegin.expected.hs | 1 + .../new/test/golden/MetaBegin.hs | 1 + .../golden/MetaBeginNoWildify.expected.hs | 2 + .../new/test/golden/MetaBeginNoWildify.hs | 2 + .../new/test/golden/MetaBindAll.expected.hs | 2 + .../new/test/golden/MetaBindAll.hs | 2 + .../new/test/golden/MetaBindOne.expected.hs | 2 + .../new/test/golden/MetaBindOne.hs | 2 + .../new/test/golden/MetaCataAST.expected.hs | 23 + .../new/test/golden/MetaCataAST.hs | 11 + .../test/golden/MetaCataCollapse.expected.hs | 14 + .../new/test/golden/MetaCataCollapse.hs | 10 + .../golden/MetaCataCollapseUnary.expected.hs | 8 + .../new/test/golden/MetaCataCollapseUnary.hs | 8 + .../new/test/golden/MetaChoice.expected.hs | 2 + .../new/test/golden/MetaChoice.hs | 2 + .../new/test/golden/MetaDeepOf.expected.hs | 8 + .../new/test/golden/MetaDeepOf.hs | 8 + .../new/test/golden/MetaFundeps.expected.hs | 16 + .../new/test/golden/MetaFundeps.hs | 16 + .../new/test/golden/MetaIdiom.expected.hs | 6 + .../new/test/golden/MetaIdiom.hs | 6 + .../test/golden/MetaIdiomRecord.expected.hs | 8 + .../new/test/golden/MetaIdiomRecord.hs | 8 + .../new/test/golden/MetaLetSimple.expected.hs | 7 + .../new/test/golden/MetaLetSimple.hs | 2 + .../new/test/golden/MetaMaybeAp.expected.hs | 5 + .../new/test/golden/MetaMaybeAp.hs | 11 + .../new/test/golden/MetaPointwise.expected.hs | 8 + .../new/test/golden/MetaPointwise.hs | 7 + .../new/test/golden/MetaTry.expected.hs | 2 + .../new/test/golden/MetaTry.hs | 2 + .../new/test/golden/MetaUseImport.expected.hs | 6 + .../new/test/golden/MetaUseImport.hs | 6 + .../new/test/golden/MetaUseLocal.expected.hs | 7 + .../new/test/golden/MetaUseLocal.hs | 7 + .../new/test/golden/MetaUseMethod.expected.hs | 12 + .../new/test/golden/MetaUseMethod.hs | 12 + .../new/test/golden/MetaUseSymbol.expected.hs | 4 + .../new/test/golden/MetaUseSymbol.hs | 4 + .../new/test/golden/MetaWithArg.expected.hs | 2 + .../new/test/golden/MetaWithArg.hs | 2 + .../new/test/golden/NewtypeRecord.expected.hs | 7 + .../new/test/golden/NewtypeRecord.hs | 7 + .../test/golden/ProvideAlreadyDestructed.hs | 9 + .../new/test/golden/ProvideLocalHyOnly.hs | 2 + .../new/test/golden/ProviderHomomorphism.hs | 22 + .../new/test/golden/PunGADT.expected.hs | 12 + .../new/test/golden/PunGADT.hs | 12 + .../new/test/golden/PunMany.expected.hs | 8 + .../new/test/golden/PunMany.hs | 7 + .../new/test/golden/PunManyGADT.expected.hs | 19 + .../new/test/golden/PunManyGADT.hs | 18 + .../new/test/golden/PunShadowing.expected.hs | 5 + .../new/test/golden/PunShadowing.hs | 5 + .../new/test/golden/PunSimple.expected.hs | 5 + .../new/test/golden/PunSimple.hs | 5 + .../new/test/golden/RecordCon.expected.hs | 9 + .../new/test/golden/RecordCon.hs | 9 + .../new/test/golden/RefineCon.expected.hs | 3 + .../new/test/golden/RefineCon.hs | 3 + .../new/test/golden/RefineGADT.expected.hs | 9 + .../new/test/golden/RefineGADT.hs | 9 + .../new/test/golden/RefineIntro.expected.hs | 2 + .../new/test/golden/RefineIntro.hs | 2 + .../test/golden/RefineIntroWhere.expected.hs | 6 + .../new/test/golden/RefineIntroWhere.hs | 6 + .../new/test/golden/RefineReader.expected.hs | 5 + .../new/test/golden/RefineReader.hs | 5 + .../new/test/golden/SplitPattern.expected.hs | 12 + .../new/test/golden/SplitPattern.hs | 8 + .../test/golden/SubsequentTactics.expected.hs | 5 + .../new/test/golden/SubsequentTactics.hs | 5 + .../hls-tactics-plugin/new/test/golden/T1.hs | 3 + .../hls-tactics-plugin/new/test/golden/T2.hs | 12 + .../hls-tactics-plugin/new/test/golden/T3.hs | 8 + .../new/test/golden/UseConLeft.expected.hs | 3 + .../new/test/golden/UseConLeft.hs | 3 + .../new/test/golden/UseConPair.expected.hs | 2 + .../new/test/golden/UseConPair.hs | 2 + .../new/test/golden/UseConRight.expected.hs | 3 + .../new/test/golden/UseConRight.hs | 3 + .../new/test/golden/hie.yaml | 1 + .../new/test/golden/test.cabal | 17 + 338 files changed, 10224 insertions(+), 4 deletions(-) create mode 100644 plugins/hls-tactics-plugin/new/src/Ide/Plugin/Tactic.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Refinery/Future.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/AbstractLSP.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/AbstractLSP/TacticActions.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/AbstractLSP/Types.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Auto.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/CaseSplit.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/CodeGen.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/CodeGen/Utils.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Context.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Debug.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/EmptyCase.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/GHC.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Judgements.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Judgements/SYB.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Judgements/Theta.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/KnownStrategies.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/KnownStrategies/QuickCheck.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/LanguageServer.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/LanguageServer/Metaprogram.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/LanguageServer/TacticProviders.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Machinery.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/Lexer.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/Parser.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/Parser.hs-boot create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/Parser/Documentation.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/ProofState.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Naming.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Plugin.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Range.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Simplify.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/StaticPlugin.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Tactics.hs create mode 100644 plugins/hls-tactics-plugin/new/src/Wingman/Types.hs create mode 100644 plugins/hls-tactics-plugin/new/test/AutoTupleSpec.hs create mode 100644 plugins/hls-tactics-plugin/new/test/CodeAction/AutoSpec.hs create mode 100644 plugins/hls-tactics-plugin/new/test/CodeAction/DestructAllSpec.hs create mode 100644 plugins/hls-tactics-plugin/new/test/CodeAction/DestructPunSpec.hs create mode 100644 plugins/hls-tactics-plugin/new/test/CodeAction/DestructSpec.hs create mode 100644 plugins/hls-tactics-plugin/new/test/CodeAction/IntroDestructSpec.hs create mode 100644 plugins/hls-tactics-plugin/new/test/CodeAction/IntrosSpec.hs create mode 100644 plugins/hls-tactics-plugin/new/test/CodeAction/RefineSpec.hs create mode 100644 plugins/hls-tactics-plugin/new/test/CodeAction/RunMetaprogramSpec.hs create mode 100644 plugins/hls-tactics-plugin/new/test/CodeAction/UseDataConSpec.hs create mode 100644 plugins/hls-tactics-plugin/new/test/CodeLens/EmptyCaseSpec.hs create mode 100644 plugins/hls-tactics-plugin/new/test/Main.hs create mode 100644 plugins/hls-tactics-plugin/new/test/ProviderSpec.hs create mode 100644 plugins/hls-tactics-plugin/new/test/Spec.hs create mode 100644 plugins/hls-tactics-plugin/new/test/UnificationSpec.hs create mode 100644 plugins/hls-tactics-plugin/new/test/Utils.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoEmptyString.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoEmptyString.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoEndo.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoEndo.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoForallClassMethod.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoForallClassMethod.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoInfixApply.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoInfixApply.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoInfixApplyMany.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoInfixApplyMany.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoInfixInfix.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoInfixInfix.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoPatSynUse.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoPatSynUse.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoSplitGADT.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoSplitGADT.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqCtx.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqCtx.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqGADT.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqGADT.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqGADTDestruct.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqGADTDestruct.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaFix.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaFix.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaGADT.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaGADT.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaGADTDestruct.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaGADTDestruct.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaMultipleUnification.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaMultipleUnification.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaRankN.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaRankN.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaRefl.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaRefl.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaReflDestruct.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaReflDestruct.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaSplitUnification.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoThetaSplitUnification.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoTypeLevel.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoTypeLevel.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoUnusedPatternMatch.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoUnusedPatternMatch.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoZip.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/AutoZip.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/ConProviders.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructAllAnd.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructAllAnd.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructAllFunc.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructAllFunc.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructAllGADTEvidence.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructAllGADTEvidence.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructAllMany.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructAllMany.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructAllNonVarTopMatch.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructAllNonVarTopMatch.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructAllProvider.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructCthulhu.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructCthulhu.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructDataFam.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructDataFam.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructInt.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructInt.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructPun.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructPun.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructTyFam.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructTyFam.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructTyToDataFam.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/DestructTyToDataFam.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/EmptyCaseADT.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/EmptyCaseADT.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/EmptyCaseApply.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/EmptyCaseApply.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/EmptyCaseGADT.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/EmptyCaseGADT.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/EmptyCaseLamCase.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/EmptyCaseLamCase.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/EmptyCaseNested.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/EmptyCaseNested.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/EmptyCaseParens.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/EmptyCaseParens.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/EmptyCaseShadow.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/EmptyCaseShadow.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/EmptyCaseSpuriousGADT.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/Fgmap.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/Fgmap.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/FmapBoth.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/FmapBoth.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/FmapJoin.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/FmapJoin.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/FmapJoinInLet.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/FmapJoinInLet.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenApplicativeThen.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenArbitrary.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenArbitrary.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenArbitrarySingleConstructor.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenArbitrarySingleConstructor.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenBigTuple.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenBigTuple.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenEitherAuto.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenEitherAuto.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenEitherHomomorphic.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenEitherHomomorphic.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenFish.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenFmapTree.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenFmapTree.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenFoldr.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenFoldr.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenFromMaybe.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenFromMaybe.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenGADTAuto.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenGADTAuto.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenGADTDestruct.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenGADTDestruct.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenGADTDestructCoercion.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenGADTDestructCoercion.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenIdTypeFam.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenIdTypeFam.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenIdentityFunctor.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenIdentityFunctor.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenIntros.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenIntros.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenJoinCont.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenJoinCont.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenListFmap.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenListFmap.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenNote.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenNote.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenPureList.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenPureList.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenSafeHead.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenSafeHead.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenShow.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenShow.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenShowCompose.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenShowCompose.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenShowMapChar.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenShowMapChar.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenSuperclass.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenSuperclass.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenSwap.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenSwap.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenSwapMany.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/GoldenSwapMany.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/IntroDestructLetBinding.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/IntroDestructLetBinding.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/IntroDestructMany.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/IntroDestructMany.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/IntroDestructOne.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/IntroDestructOne.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/IntroDestructProvider.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/IntrosTooMany.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/IntrosTooMany.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownBigSemigroup.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownBigSemigroup.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownCounterfactualSemigroup.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownCounterfactualSemigroup.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownDestructedSemigroup.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownDestructedSemigroup.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownMissingMonoid.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownMissingMonoid.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownMissingSemigroup.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownMissingSemigroup.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownModuleInstanceSemigroup.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownModuleInstanceSemigroup.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownMonoid.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownMonoid.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownPolyMonoid.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownPolyMonoid.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownThetaSemigroup.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/KnownThetaSemigroup.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutBind.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutBind.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutDollarApp.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutDollarApp.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutInfixKeep.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutInfixKeep.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutLam.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutLam.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutOpApp.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutOpApp.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutPrefixKeep.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutPrefixKeep.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutRec.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutRec.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutSplitClass.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutSplitClass.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutSplitGuard.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutSplitGuard.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutSplitIn.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutSplitIn.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutSplitLet.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutSplitLet.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutSplitPatSyn.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutSplitPatSyn.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutSplitPattern.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutSplitPattern.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutSplitViewPat.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutSplitViewPat.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutSplitWhere.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/LayoutSplitWhere.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MessageCantUnify.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MessageForallA.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MessageNotEnoughGas.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaBegin.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaBegin.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaBeginNoWildify.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaBeginNoWildify.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaBindAll.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaBindAll.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaBindOne.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaBindOne.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaCataAST.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaCataAST.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaCataCollapse.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaCataCollapse.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaCataCollapseUnary.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaCataCollapseUnary.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaChoice.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaChoice.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaDeepOf.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaDeepOf.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaFundeps.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaFundeps.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaIdiom.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaIdiom.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaIdiomRecord.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaIdiomRecord.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaLetSimple.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaLetSimple.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaMaybeAp.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaMaybeAp.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaPointwise.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaPointwise.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaTry.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaTry.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaUseImport.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaUseImport.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaUseLocal.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaUseLocal.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaUseMethod.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaUseMethod.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaUseSymbol.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaUseSymbol.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaWithArg.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/MetaWithArg.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/NewtypeRecord.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/NewtypeRecord.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/ProvideAlreadyDestructed.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/ProvideLocalHyOnly.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/ProviderHomomorphism.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/PunGADT.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/PunGADT.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/PunMany.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/PunMany.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/PunManyGADT.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/PunManyGADT.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/PunShadowing.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/PunShadowing.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/PunSimple.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/PunSimple.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/RecordCon.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/RecordCon.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/RefineCon.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/RefineCon.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/RefineGADT.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/RefineGADT.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/RefineIntro.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/RefineIntro.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/RefineIntroWhere.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/RefineIntroWhere.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/RefineReader.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/RefineReader.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/SplitPattern.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/SplitPattern.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/SubsequentTactics.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/SubsequentTactics.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/T1.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/T2.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/T3.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/UseConLeft.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/UseConLeft.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/UseConPair.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/UseConPair.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/UseConRight.expected.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/UseConRight.hs create mode 100644 plugins/hls-tactics-plugin/new/test/golden/hie.yaml create mode 100644 plugins/hls-tactics-plugin/new/test/golden/test.cabal diff --git a/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal b/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal index 225b132f47..2ad0a7365e 100644 --- a/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal +++ b/plugins/hls-tactics-plugin/hls-tactics-plugin.cabal @@ -15,6 +15,11 @@ license-file: LICENSE build-type: Simple extra-source-files: README.md + new/src/**/*.hs-boot + old/src/**/*.hs-boot + new/test/golden/*.cabal + new/test/golden/*.hs + new/test/golden/*.yaml old/test/golden/*.cabal old/test/golden/*.hs old/test/golden/*.yaml @@ -29,11 +34,15 @@ flag pedantic manual: True library - if impl(ghc >= 9.3) + if impl(ghc >= 9.2.1) buildable: False else buildable: True - hs-source-dirs: old/src + + if impl(ghc >= 9.2.1) + hs-source-dirs: new/src + else + hs-source-dirs: old/src exposed-modules: Ide.Plugin.Tactic Refinery.Future @@ -135,7 +144,7 @@ library ViewPatterns test-suite tests - if impl(ghc >= 9.3) + if impl(ghc >= 9.2.1) buildable: False else buildable: True @@ -158,7 +167,10 @@ test-suite tests UnificationSpec Utils - hs-source-dirs: old/test + if impl(ghc >= 9.2.1) + hs-source-dirs: new/test + else + hs-source-dirs: old/test ghc-options: -Wall -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N diff --git a/plugins/hls-tactics-plugin/new/src/Ide/Plugin/Tactic.hs b/plugins/hls-tactics-plugin/new/src/Ide/Plugin/Tactic.hs new file mode 100644 index 0000000000..cf326ee653 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Ide/Plugin/Tactic.hs @@ -0,0 +1,5 @@ +-- | A plugin that uses tactics to synthesize code +module Ide.Plugin.Tactic (descriptor, Log(..)) where + +import Wingman.Plugin + diff --git a/plugins/hls-tactics-plugin/new/src/Refinery/Future.hs b/plugins/hls-tactics-plugin/new/src/Refinery/Future.hs new file mode 100644 index 0000000000..e829672831 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Refinery/Future.hs @@ -0,0 +1,140 @@ +{-# LANGUAGE RankNTypes #-} + +------------------------------------------------------------------------------ +-- | Things that belong in the future release of refinery v5. +module Refinery.Future + ( runStreamingTacticT + , hoistListT + , consume + ) where + +import Control.Applicative +import Control.Monad (ap, (>=>)) +import Control.Monad.State.Lazy (runStateT) +import Control.Monad.Trans +import Data.Either (isRight) +import Data.Functor ((<&>)) +import Data.Tuple (swap) +import Refinery.ProofState +import Refinery.Tactic.Internal + + + +hoistElem :: Functor m => (forall x. m x -> n x) -> Elem m a -> Elem n a +hoistElem _ Done = Done +hoistElem f (Next a lt) = Next a $ hoistListT f lt + + +hoistListT :: Functor m => (forall x. m x -> n x) -> ListT m a -> ListT n a +hoistListT f t = ListT $ f $ fmap (hoistElem f) $ unListT t + + +consume :: Monad m => ListT m a -> (a -> m ()) -> m () +consume lt f = unListT lt >>= \case + Done -> pure () + Next a lt' -> f a >> consume lt' f + + +newHole :: MonadExtract meta ext err s m => s -> m (s, (meta, ext)) +newHole = fmap swap . runStateT hole + +runStreamingTacticT :: (MonadExtract meta ext err s m) => TacticT jdg ext err s m () -> jdg -> s -> ListT m (Either err (Proof s meta jdg ext)) +runStreamingTacticT t j s = streamProofs s $ fmap snd $ proofState t j + +data Elem m a + = Done + | Next a (ListT m a) + deriving stock Functor + + +point :: Applicative m => a -> Elem m a +point a = Next a $ ListT $ pure Done + +newtype ListT m a = ListT { unListT :: m (Elem m a) } + +cons :: (Applicative m) => a -> ListT m a -> ListT m a +cons x xs = ListT $ pure $ Next x xs + +instance Functor m => Functor (ListT m) where + fmap f (ListT xs) = ListT $ xs <&> \case + Done -> Done + Next a xs -> Next (f a) (fmap f xs) + +instance (Monad m) => Applicative (ListT m) where + pure = return + (<*>) = ap + +instance (Monad m) => Alternative (ListT m) where + empty = ListT $ pure Done + (ListT xs) <|> (ListT ys) = + ListT $ xs >>= \case + Done -> ys + Next x xs -> pure (Next x (xs <|> ListT ys)) + +instance (Monad m) => Monad (ListT m) where + return a = cons a empty + (ListT xs) >>= k = + ListT $ xs >>= \case + Done -> pure Done + Next x xs -> unListT $ k x <|> (xs >>= k) + + +instance MonadTrans ListT where + lift m = ListT $ fmap (\x -> Next x empty) m + + +interleaveT :: (Monad m) => Elem m a -> Elem m a -> Elem m a +interleaveT xs ys = + case xs of + Done -> ys + Next x xs -> Next x $ ListT $ fmap (interleaveT ys) $ unListT xs + +-- ys <&> \case +-- Done -> Next x xs +-- Next y ys -> Next x (cons y (interleaveT xs ys)) + +force :: (Monad m) => Elem m a -> m [a] +force = \case + Done -> pure [] + Next x xs' -> (x:) <$> (unListT xs' >>= force) + +ofList :: Monad m => [a] -> Elem m a +ofList [] = Done +ofList (x:xs) = Next x $ ListT $ pure $ ofList xs + +streamProofs :: forall ext err s m goal meta. (MonadExtract meta ext err s m) => s -> ProofStateT ext ext err s m goal -> ListT m (Either err (Proof s meta goal ext)) +streamProofs s p = ListT $ go s [] pure p + where + go :: s -> [(meta, goal)] -> (err -> m err) -> ProofStateT ext ext err s m goal -> m (Elem m (Either err (Proof s meta goal ext))) + go s goals _ (Subgoal goal k) = do + (s', (meta, h)) <- newHole s + -- Note [Handler Reset]: + -- We reset the handler stack to avoid the handlers leaking across subgoals. + -- This would happen when we had a handler that wasn't followed by an error call. + -- pair >> goal >>= \g -> (handler_ $ \_ -> traceM $ "Handling " <> show g) <|> failure "Error" + -- We would see the "Handling a" message when solving for b. + go s' (goals ++ [(meta, goal)]) pure $ k h + go s goals handlers (Effect m) = m >>= go s goals handlers + go s goals handlers (Stateful f) = + let (s', p) = f s + in go s' goals handlers p + go s goals handlers (Alt p1 p2) = + unListT $ ListT (go s goals handlers p1) <|> ListT (go s goals handlers p2) + go s goals handlers (Interleave p1 p2) = + interleaveT <$> go s goals handlers p1 <*> go s goals handlers p2 + go s goals handlers (Commit p1 p2) = do + solns <- force =<< go s goals handlers p1 + if any isRight solns then pure $ ofList solns else go s goals handlers p2 + go _ _ _ Empty = pure Done + go _ _ handlers (Failure err _) = do + annErr <- handlers err + pure $ point $ Left annErr + go s goals handlers (Handle p h) = + -- Note [Handler ordering]: + -- If we have multiple handlers in scope, then we want the handlers closer to the error site to + -- run /first/. This allows the handlers up the stack to add their annotations on top of the + -- ones lower down, which is the behavior that we desire. + -- IE: for @handler f >> handler g >> failure err@, @g@ ought to be run before @f@. + go s goals (h >=> handlers) p + go s goals _ (Axiom ext) = pure $ point $ Right (Proof ext s goals) + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/AbstractLSP.hs b/plugins/hls-tactics-plugin/new/src/Wingman/AbstractLSP.hs new file mode 100644 index 0000000000..da1e068ba6 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/AbstractLSP.hs @@ -0,0 +1,266 @@ +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE RecordWildCards #-} + +{-# LANGUAGE NoMonoLocalBinds #-} + +{-# OPTIONS_GHC -Wno-orphans #-} + +module Wingman.AbstractLSP (installInteractions) where + +import Control.Monad (void) +import Control.Monad.IO.Class +import Control.Monad.Trans (lift) +import Control.Monad.Trans.Maybe (MaybeT, mapMaybeT) +import qualified Data.Aeson as A +import Data.Coerce +import Data.Foldable (traverse_) +import Data.Monoid (Last (..)) +import qualified Data.Text as T +import Data.Traversable (for) +import Data.Tuple.Extra (uncurry3) +import Development.IDE (IdeState) +import Development.IDE.Core.UseStale +import Development.IDE.GHC.ExactPrint (GetAnnotatedParsedSource(GetAnnotatedParsedSource)) +import qualified Ide.Plugin.Config as Plugin +import Ide.Types +import Language.LSP.Server (LspM, sendRequest, getClientCapabilities) +import qualified Language.LSP.Types as LSP +import Language.LSP.Types hiding (CodeLens, CodeAction) +import Wingman.AbstractLSP.Types +import Wingman.EmptyCase (fromMaybeT) +import Wingman.LanguageServer (getTacticConfig, getIdeDynflags, mkWorkspaceEdits, runStaleIde, showLspMessage, mkShowMessageParams) +import Wingman.StaticPlugin (enableQuasiQuotes) +import Wingman.Types + + +------------------------------------------------------------------------------ +-- | Attach the 'Interaction's to a 'PluginDescriptor'. Interactions are +-- self-contained request/response pairs that abstract over the LSP, and +-- provide a unified interface for doing interesting things, without needing to +-- dive into the underlying API too directly. +installInteractions + :: [Interaction] + -> PluginDescriptor IdeState + -> PluginDescriptor IdeState +installInteractions is desc = + let plId = pluginId desc + in desc + { pluginCommands = pluginCommands desc <> fmap (buildCommand plId) is + , pluginHandlers = pluginHandlers desc <> buildHandlers is + } + + +------------------------------------------------------------------------------ +-- | Extract 'PluginHandlers' from 'Interaction's. +buildHandlers + :: [Interaction] + -> PluginHandlers IdeState +buildHandlers cs = + flip foldMap cs $ \(Interaction (c :: Continuation sort target b)) -> + case c_makeCommand c of + SynthesizeCodeAction k -> + mkPluginHandler STextDocumentCodeAction $ codeActionProvider @target (c_sort c) k + SynthesizeCodeLens k -> + mkPluginHandler STextDocumentCodeLens $ codeLensProvider @target (c_sort c) k + + +------------------------------------------------------------------------------ +-- | Extract a 'PluginCommand' from an 'Interaction'. +buildCommand + :: PluginId + -> Interaction + -> PluginCommand IdeState +buildCommand plId (Interaction (c :: Continuation sort target b)) = + PluginCommand + { commandId = toCommandId $ c_sort c + , commandDesc = T.pack "" + , commandFunc = runContinuation plId c + } + + +------------------------------------------------------------------------------ +-- | Boilerplate for running a 'Continuation' as part of an LSP command. +runContinuation + :: forall sort a b + . IsTarget a + => PluginId + -> Continuation sort a b + -> CommandFunction IdeState (FileContext, b) +runContinuation plId cont state (fc, b) = do + fromMaybeT + (Left $ ResponseError + { _code = InternalError + , _message = T.pack "TODO(sandy)" + , _xdata = Nothing + } ) $ do + env@LspEnv{..} <- buildEnv state plId fc + nfp <- getNfp $ fc_uri le_fileContext + let stale a = runStaleIde "runContinuation" state nfp a + args <- fetchTargetArgs @a env + res <- c_runCommand cont env args fc b + + -- This block returns a maybe error. + fmap (maybe (Right A.Null) Left . coerce . foldMap Last) $ + for res $ \case + ErrorMessages errs -> do + traverse_ showUserFacingMessage errs + pure Nothing + RawEdit edits -> do + sendEdits edits + pure Nothing + GraftEdit gr -> do + ccs <- lift getClientCapabilities + TrackedStale pm _ <- mapMaybeT liftIO $ stale GetAnnotatedParsedSource + case mkWorkspaceEdits (enableQuasiQuotes le_dflags) ccs (fc_uri le_fileContext) (unTrack pm) gr of + Left errs -> + pure $ Just $ ResponseError + { _code = InternalError + , _message = T.pack $ show errs + , _xdata = Nothing + } + Right edits -> do + sendEdits edits + pure Nothing + + +------------------------------------------------------------------------------ +-- | Push a 'WorkspaceEdit' to the client. +sendEdits :: WorkspaceEdit -> MaybeT (LspM Plugin.Config) () +sendEdits edits = + void $ lift $ + sendRequest + SWorkspaceApplyEdit + (ApplyWorkspaceEditParams Nothing edits) + (const $ pure ()) + + +------------------------------------------------------------------------------ +-- | Push a 'UserFacingMessage' to the client. +showUserFacingMessage + :: UserFacingMessage + -> MaybeT (LspM Plugin.Config) () +showUserFacingMessage ufm = + void $ lift $ showLspMessage $ mkShowMessageParams ufm + + +------------------------------------------------------------------------------ +-- | Build an 'LspEnv', which contains the majority of things we need to know +-- in a 'Continuation'. +buildEnv + :: IdeState + -> PluginId + -> FileContext + -> MaybeT (LspM Plugin.Config) LspEnv +buildEnv state plId fc = do + cfg <- lift $ getTacticConfig plId + nfp <- getNfp $ fc_uri fc + dflags <- mapMaybeT liftIO $ getIdeDynflags state nfp + pure $ LspEnv + { le_ideState = state + , le_pluginId = plId + , le_dflags = dflags + , le_config = cfg + , le_fileContext = fc + } + + +------------------------------------------------------------------------------ +-- | Lift a 'Continuation' into an LSP CodeAction. +codeActionProvider + :: forall target sort b + . (IsContinuationSort sort, A.ToJSON b, IsTarget target) + => sort + -> ( LspEnv + -> TargetArgs target + -> MaybeT (LspM Plugin.Config) [(Metadata, b)] + ) + -> PluginMethodHandler IdeState TextDocumentCodeAction +codeActionProvider sort k state plId + (CodeActionParams _ _ (TextDocumentIdentifier uri) range _) = do + fromMaybeT (Right $ List []) $ do + let fc = FileContext + { fc_uri = uri + , fc_range = Just $ unsafeMkCurrent range + } + env <- buildEnv state plId fc + args <- fetchTargetArgs @target env + actions <- k env args + pure + $ Right + $ List + $ fmap (InR . uncurry (makeCodeAction plId fc sort)) actions + + +------------------------------------------------------------------------------ +-- | Lift a 'Continuation' into an LSP CodeLens. +codeLensProvider + :: forall target sort b + . (IsContinuationSort sort, A.ToJSON b, IsTarget target) + => sort + -> ( LspEnv + -> TargetArgs target + -> MaybeT (LspM Plugin.Config) [(Range, Metadata, b)] + ) + -> PluginMethodHandler IdeState TextDocumentCodeLens +codeLensProvider sort k state plId + (CodeLensParams _ _ (TextDocumentIdentifier uri)) = do + fromMaybeT (Right $ List []) $ do + let fc = FileContext + { fc_uri = uri + , fc_range = Nothing + } + env <- buildEnv state plId fc + args <- fetchTargetArgs @target env + actions <- k env args + pure + $ Right + $ List + $ fmap (uncurry3 $ makeCodeLens plId sort fc) actions + + +------------------------------------------------------------------------------ +-- | Build a 'LSP.CodeAction'. +makeCodeAction + :: (A.ToJSON b, IsContinuationSort sort) + => PluginId + -> FileContext + -> sort + -> Metadata + -> b + -> LSP.CodeAction +makeCodeAction plId fc sort (Metadata title kind preferred) b = + let cmd_id = toCommandId sort + cmd = mkLspCommand plId cmd_id title $ Just [A.toJSON (fc, b)] + in LSP.CodeAction + { _title = title + , _kind = Just kind + , _diagnostics = Nothing + , _isPreferred = Just preferred + , _disabled = Nothing + , _edit = Nothing + , _command = Just cmd + , _xdata = Nothing + } + + +------------------------------------------------------------------------------ +-- | Build a 'LSP.CodeLens'. +makeCodeLens + :: (A.ToJSON b, IsContinuationSort sort) + => PluginId + -> sort + -> FileContext + -> Range + -> Metadata + -> b + -> LSP.CodeLens +makeCodeLens plId sort fc range (Metadata title _ _) b = + let fc' = fc { fc_range = Just $ unsafeMkCurrent range } + cmd_id = toCommandId sort + cmd = mkLspCommand plId cmd_id title $ Just [A.toJSON (fc', b)] + in LSP.CodeLens + { _range = range + , _command = Just cmd + , _xdata = Nothing + } + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/AbstractLSP/TacticActions.hs b/plugins/hls-tactics-plugin/new/src/Wingman/AbstractLSP/TacticActions.hs new file mode 100644 index 0000000000..bb30f27b02 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/AbstractLSP/TacticActions.hs @@ -0,0 +1,178 @@ +{-# LANGUAGE RecordWildCards #-} + +{-# LANGUAGE NoMonoLocalBinds #-} + +module Wingman.AbstractLSP.TacticActions where + +import Control.Monad (when) +import Control.Monad.IO.Class (liftIO) +import Control.Monad.Trans (lift) +import Control.Monad.Trans.Maybe (mapMaybeT) +import Data.Foldable +import Data.Maybe (listToMaybe) +import Data.Proxy +import Development.IDE hiding (rangeToRealSrcSpan) +import Development.IDE.Core.UseStale +import Development.IDE.GHC.Compat +import Development.IDE.GHC.ExactPrint +import Generics.SYB.GHC (mkBindListT, everywhereM') +import Wingman.AbstractLSP.Types +import Wingman.CaseSplit +import Wingman.GHC (liftMaybe, isHole, pattern AMatch) +import Wingman.Judgements (jNeedsToBindArgs) +import Wingman.LanguageServer (runStaleIde) +import Wingman.LanguageServer.TacticProviders +import Wingman.Machinery (runTactic, scoreSolution) +import Wingman.Range +import Wingman.Types +import Development.IDE.Core.Service (getIdeOptionsIO) +import Development.IDE.Types.Options (IdeTesting(IdeTesting), IdeOptions (IdeOptions, optTesting)) + + +------------------------------------------------------------------------------ +-- | An 'Interaction' for a 'TacticCommand'. +makeTacticInteraction + :: TacticCommand + -> Interaction +makeTacticInteraction cmd = + Interaction $ Continuation @_ @HoleTarget cmd + (SynthesizeCodeAction $ \env hj -> do + pure $ commandProvider cmd $ + TacticProviderData + { tpd_lspEnv = env + , tpd_jdg = hj_jdg hj + , tpd_hole_sort = hj_hole_sort hj + } + ) + $ \LspEnv{..} HoleJudgment{..} FileContext{..} var_name -> do + nfp <- getNfp fc_uri + let stale a = runStaleIde "tacticCmd" le_ideState nfp a + + let span = fmap (rangeToRealSrcSpan (fromNormalizedFilePath nfp)) hj_range + TrackedStale _ pmmap <- mapMaybeT liftIO $ stale GetAnnotatedParsedSource + pm_span <- liftMaybe $ mapAgeFrom pmmap span + IdeOptions{optTesting = IdeTesting isTesting} <- + liftIO $ getIdeOptionsIO (shakeExtras le_ideState) + + let t = commandTactic cmd var_name + timeout = if isTesting then maxBound else cfg_timeout_seconds le_config * seconds + + liftIO $ runTactic timeout hj_ctx hj_jdg t >>= \case + Left err -> + pure + $ pure + $ ErrorMessages + $ pure + $ mkUserFacingMessage err + Right rtr -> + case rtr_extract rtr of + L _ (HsVar _ (L _ rdr)) | isHole (occName rdr) -> + pure + $ addTimeoutMessage rtr + $ pure + $ ErrorMessages + $ pure NothingToDo + _ -> do + for_ (rtr_other_solns rtr) $ \soln -> do + traceMX "other solution" $ syn_val soln + traceMX "with score" $ scoreSolution soln (rtr_jdg rtr) [] + traceMX "solution" $ rtr_extract rtr + pure + $ addTimeoutMessage rtr + $ pure + $ GraftEdit + $ graftHole (RealSrcSpan (unTrack pm_span) Nothing) rtr + + +addTimeoutMessage :: RunTacticResults -> [ContinuationResult] -> [ContinuationResult] +addTimeoutMessage rtr = mappend + [ ErrorMessages $ pure TimedOut + | rtr_timed_out rtr + ] + + +------------------------------------------------------------------------------ +-- | The number of microseconds in a second +seconds :: Num a => a +seconds = 1e6 + + +------------------------------------------------------------------------------ +-- | Transform some tactic errors into a 'UserFacingMessage'. +mkUserFacingMessage :: [TacticError] -> UserFacingMessage +mkUserFacingMessage errs + | elem OutOfGas errs = NotEnoughGas +mkUserFacingMessage [] = NothingToDo +mkUserFacingMessage _ = TacticErrors + + +------------------------------------------------------------------------------ +-- | Graft a 'RunTacticResults' into the correct place in an AST. Correctly +-- deals with top-level holes, in which we might need to fiddle with the +-- 'Match's that bind variables. +graftHole + :: SrcSpan + -> RunTacticResults + -> Graft (Either String) ParsedSource +graftHole span rtr + | _jIsTopHole (rtr_jdg rtr) + = genericGraftWithSmallestM + (Proxy @(Located [LMatch GhcPs (LHsExpr GhcPs)])) span + $ \dflags matches -> + everywhereM' + $ mkBindListT $ \ix -> + graftDecl dflags span ix $ \name pats -> + splitToDecl + (case not $ jNeedsToBindArgs (rtr_jdg rtr) of + -- If the user has explicitly bound arguments, use the + -- fixity they wrote. + True -> matchContextFixity . m_ctxt . unLoc + =<< listToMaybe matches + -- Otherwise, choose based on the name of the function. + False -> Nothing + ) + (occName name) + $ iterateSplit + $ mkFirstAgda pats + $ unLoc + $ rtr_extract rtr +graftHole span rtr + = graft span + $ rtr_extract rtr + + +------------------------------------------------------------------------------ +-- | Keep a fixity if one was present in the 'HsMatchContext'. +matchContextFixity :: HsMatchContext p -> Maybe LexicalFixity +matchContextFixity (FunRhs _ l _) = Just l +matchContextFixity _ = Nothing + + +------------------------------------------------------------------------------ +-- | Helper function to route 'mergeFunBindMatches' into the right place in an +-- AST --- correctly dealing with inserting into instance declarations. +graftDecl + :: DynFlags + -> SrcSpan + -> Int + -> (RdrName -> [Pat GhcPs] -> LHsDecl GhcPs) + -> LMatch GhcPs (LHsExpr GhcPs) + -> TransformT (Either String) [LMatch GhcPs (LHsExpr GhcPs)] +graftDecl dflags dst ix make_decl (L src (AMatch (FunRhs (L _ name) _ _) pats _)) + | dst `isSubspanOf` src = do + L _ dec <- annotateDecl dflags $ make_decl name pats + case dec of + ValD _ FunBind{ fun_matches = MG { mg_alts = L _ alts@(first_match : _)} + } -> do + -- For whatever reason, ExactPrint annotates newlines to the ends of + -- case matches and type signatures, but only allows us to insert + -- them at the beginning of those things. Thus, we need want to + -- insert a preceding newline (done in 'annotateDecl') on all + -- matches, except for the first one --- since it gets its newline + -- from the line above. + when (ix == 0) $ + setPrecedingLinesT first_match 0 0 + pure alts + _ -> lift $ Left "annotateDecl didn't produce a funbind" +graftDecl _ _ _ _ x = pure $ pure x + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/AbstractLSP/Types.hs b/plugins/hls-tactics-plugin/new/src/Wingman/AbstractLSP/Types.hs new file mode 100644 index 0000000000..18d38c6eca --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/AbstractLSP/Types.hs @@ -0,0 +1,169 @@ +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeFamilies #-} + +{-# OPTIONS_GHC -Wno-orphans #-} + +module Wingman.AbstractLSP.Types where + +import Control.Monad.IO.Class +import Control.Monad.Trans.Maybe (MaybeT (MaybeT), mapMaybeT) +import qualified Data.Aeson as A +import Data.Text (Text) +import Development.IDE (IdeState) +import Development.IDE.GHC.ExactPrint (Graft) +import Development.IDE.Core.UseStale +import Development.IDE.GHC.Compat hiding (Target) +import GHC.Generics (Generic) +import qualified Ide.Plugin.Config as Plugin +import Ide.Types +import Language.LSP.Server (LspM) +import Language.LSP.Types hiding (CodeLens, CodeAction) +import Wingman.LanguageServer (judgementForHole) +import Wingman.Types + + +------------------------------------------------------------------------------ +-- | An 'Interaction' is an existential 'Continuation', which handles both +-- sides of the request/response interaction for LSP. +data Interaction where + Interaction + :: (IsTarget target, IsContinuationSort sort, A.ToJSON b, A.FromJSON b) + => Continuation sort target b + -> Interaction + + +------------------------------------------------------------------------------ +-- | Metadata for a command. Used by both code actions and lenses, though for +-- lenses, only 'md_title' is currently used. +data Metadata + = Metadata + { md_title :: Text + , md_kind :: CodeActionKind + , md_preferred :: Bool + } + deriving stock (Eq, Show) + + +------------------------------------------------------------------------------ +-- | Whether we're defining a CodeAction or CodeLens. +data SynthesizeCommand a b + = SynthesizeCodeAction + ( LspEnv + -> TargetArgs a + -> MaybeT (LspM Plugin.Config) [(Metadata, b)] + ) + | SynthesizeCodeLens + ( LspEnv + -> TargetArgs a + -> MaybeT (LspM Plugin.Config) [(Range, Metadata, b)] + ) + + +------------------------------------------------------------------------------ +-- | Transform a "continuation sort" into a 'CommandId'. +class IsContinuationSort a where + toCommandId :: a -> CommandId + +instance IsContinuationSort CommandId where + toCommandId = id + +instance IsContinuationSort Text where + toCommandId = CommandId + + +------------------------------------------------------------------------------ +-- | Ways a 'Continuation' can resolve. +data ContinuationResult + = -- | Produce some error messages. + ErrorMessages [UserFacingMessage] + -- | Produce an explicit 'WorkspaceEdit'. + | RawEdit WorkspaceEdit + -- | Produce a 'Graft', corresponding to a transformation of the current + -- AST. + | GraftEdit (Graft (Either String) ParsedSource) + + +------------------------------------------------------------------------------ +-- | A 'Continuation' is a single object corresponding to an action that users +-- can take via LSP. It generalizes codeactions and codelenses, allowing for +-- a significant amount of code reuse. +-- +-- Given @Continuation sort target payload@: +-- +-- the @sort@ corresponds to a 'CommandId', allowing you to namespace actions +-- rather than working directly with text. This functionality is driven via +-- 'IsContinuationSort'. +-- +-- the @target@ is used to fetch data from LSP on both sides of the +-- request/response barrier. For example, you can use it to resolve what node +-- in the AST the incoming range refers to. This functionality is driven via +-- 'IsTarget'. +-- +-- the @payload@ is used for data you'd explicitly like to send from the +-- request to the response. It's like @target@, but only gets computed once. +-- This is beneficial if you can do it, but requires that your data is +-- serializable via JSON. +data Continuation sort target payload = Continuation + { c_sort :: sort + , c_makeCommand :: SynthesizeCommand target payload + , c_runCommand + :: LspEnv + -> TargetArgs target + -> FileContext + -> payload + -> MaybeT (LspM Plugin.Config) [ContinuationResult] + } + + +------------------------------------------------------------------------------ +-- | What file are we looking at, and what bit of it? +data FileContext = FileContext + { fc_uri :: Uri + , fc_range :: Maybe (Tracked 'Current Range) + -- ^ For code actions, this is 'Just'. For code lenses, you'll get + -- a 'Nothing' in the request, and a 'Just' in the response. + } + deriving stock (Eq, Ord, Show, Generic) + deriving anyclass (A.ToJSON, A.FromJSON) + + +------------------------------------------------------------------------------ +-- | Everything we need to resolve continuations. +data LspEnv = LspEnv + { le_ideState :: IdeState + , le_pluginId :: PluginId + , le_dflags :: DynFlags + , le_config :: Config + , le_fileContext :: FileContext + } + + +------------------------------------------------------------------------------ +-- | Extract some information from LSP, so it can be passed to the requests and +-- responses of a 'Continuation'. +class IsTarget t where + type TargetArgs t + fetchTargetArgs + :: LspEnv + -> MaybeT (LspM Plugin.Config) (TargetArgs t) + +------------------------------------------------------------------------------ +-- | A 'HoleTarget' is a target (see 'IsTarget') which succeeds if the given +-- range is an HsExpr hole. It gives continuations access to the resulting +-- tactic judgement. +data HoleTarget = HoleTarget + deriving stock (Eq, Ord, Show, Enum, Bounded) + +getNfp :: Applicative m => Uri -> MaybeT m NormalizedFilePath +getNfp = MaybeT . pure . uriToNormalizedFilePath . toNormalizedUri + +instance IsTarget HoleTarget where + type TargetArgs HoleTarget = HoleJudgment + fetchTargetArgs LspEnv{..} = do + let FileContext{..} = le_fileContext + range <- MaybeT $ pure fc_range + nfp <- getNfp fc_uri + mapMaybeT liftIO $ judgementForHole le_ideState nfp range le_config + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Auto.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Auto.hs new file mode 100644 index 0000000000..3748af1e5b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Auto.hs @@ -0,0 +1,32 @@ + +module Wingman.Auto where + +import Control.Monad.Reader.Class (asks) +import Control.Monad.State (gets) +import qualified Data.Set as S +import Refinery.Tactic +import Wingman.Judgements +import Wingman.KnownStrategies +import Wingman.Machinery (tracing, getCurrentDefinitions) +import Wingman.Tactics +import Wingman.Types + + +------------------------------------------------------------------------------ +-- | Automatically solve a goal. +auto :: TacticsM () +auto = do + jdg <- goal + skolems <- gets ts_skolems + gas <- asks $ cfg_auto_gas . ctxConfig + current <- getCurrentDefinitions + traceMX "goal" jdg + traceMX "ctx" current + traceMX "skolems" skolems + commit knownStrategies + . tracing "auto" + . localTactic (auto' gas) + . disallowing RecursiveCall + . S.fromList + $ fmap fst current + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/CaseSplit.hs b/plugins/hls-tactics-plugin/new/src/Wingman/CaseSplit.hs new file mode 100644 index 0000000000..373fc9b23b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/CaseSplit.hs @@ -0,0 +1,109 @@ +module Wingman.CaseSplit + ( mkFirstAgda + , iterateSplit + , splitToDecl + ) where + +import Data.Bool (bool) +import Data.Data +import Data.Generics +import Data.Set (Set) +import qualified Data.Set as S +import Development.IDE.GHC.Compat +import GHC.Exts (IsString (fromString)) +import GHC.SourceGen (funBindsWithFixity, match, wildP) +import Wingman.GHC +import Wingman.Types + + + +------------------------------------------------------------------------------ +-- | Construct an 'AgdaMatch' from patterns in scope (should be the LHS of the +-- match) and a body. +mkFirstAgda :: [Pat GhcPs] -> HsExpr GhcPs -> AgdaMatch +mkFirstAgda pats (Lambda pats' body) = mkFirstAgda (pats <> pats') body +mkFirstAgda pats body = AgdaMatch pats body + + +------------------------------------------------------------------------------ +-- | Transform an 'AgdaMatch' whose body is a case over a bound pattern, by +-- splitting it into multiple matches: one for each alternative of the case. +agdaSplit :: AgdaMatch -> [AgdaMatch] +agdaSplit (AgdaMatch pats (Case (HsVar _ (L _ var)) matches)) + -- Ensure the thing we're destructing is actually a pattern that's been + -- bound. + | containsVar var pats + = do + (pat, body) <- matches + -- TODO(sandy): use an at pattern if necessary + pure $ AgdaMatch (rewriteVarPat var pat pats) $ unLoc body +agdaSplit x = [x] + + +------------------------------------------------------------------------------ +-- | Replace unused bound patterns with wild patterns. +wildify :: AgdaMatch -> AgdaMatch +wildify (AgdaMatch pats body) = + let make_wild = bool id (wildifyT (allOccNames body)) $ not $ containsHole body + in AgdaMatch (make_wild pats) body + + +------------------------------------------------------------------------------ +-- | Helper function for 'wildify'. +wildifyT :: Data a => Set OccName -> a -> a +wildifyT (S.map occNameString -> used) = everywhere $ mkT $ \case + VarPat _ (L _ var) | S.notMember (occNameString $ occName var) used -> wildP + (x :: Pat GhcPs) -> x + + +------------------------------------------------------------------------------ +-- | Determine whether the given 'RdrName' exists as a 'VarPat' inside of @a@. +containsVar :: Data a => RdrName -> a -> Bool +containsVar name = everything (||) $ + mkQ False (\case + VarPat _ (L _ var) -> eqRdrName name var + (_ :: Pat GhcPs) -> False + ) + `extQ` \case + HsRecField lbl _ True -> eqRdrName name $ unLoc $ rdrNameFieldOcc $ unLoc lbl + (_ :: HsRecField' (FieldOcc GhcPs) (PatCompat GhcPs)) -> False + + +------------------------------------------------------------------------------ +-- | Replace a 'VarPat' with the given @'Pat' GhcPs@. +rewriteVarPat :: Data a => RdrName -> Pat GhcPs -> a -> a +rewriteVarPat name rep = everywhere $ + mkT (\case + VarPat _ (L _ var) | eqRdrName name var -> rep + (x :: Pat GhcPs) -> x + ) + `extT` \case + HsRecField lbl _ True + | eqRdrName name $ unLoc $ rdrNameFieldOcc $ unLoc lbl + -> HsRecField lbl (toPatCompat rep) False + (x :: HsRecField' (FieldOcc GhcPs) (PatCompat GhcPs)) -> x + + +------------------------------------------------------------------------------ +-- | Construct an 'HsDecl' from a set of 'AgdaMatch'es. +splitToDecl + :: Maybe LexicalFixity + -> OccName -- ^ The name of the function + -> [AgdaMatch] + -> LHsDecl GhcPs +splitToDecl fixity name ams = do + traceX "fixity" fixity $ + noLoc $ + funBindsWithFixity fixity (fromString . occNameString . occName $ name) $ do + AgdaMatch pats body <- ams + pure $ match pats body + + +------------------------------------------------------------------------------ +-- | Sometimes 'agdaSplit' exposes another opportunity to do 'agdaSplit'. This +-- function runs it a few times, hoping it will find a fixpoint. +iterateSplit :: AgdaMatch -> [AgdaMatch] +iterateSplit am = + let iterated = iterate (agdaSplit =<<) $ pure am + in fmap wildify . (!! 5) $ iterated + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/CodeGen.hs b/plugins/hls-tactics-plugin/new/src/Wingman/CodeGen.hs new file mode 100644 index 0000000000..322a6f5b8c --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/CodeGen.hs @@ -0,0 +1,346 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE OverloadedLabels #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} + +module Wingman.CodeGen + ( module Wingman.CodeGen + , module Wingman.CodeGen.Utils + ) where + + +import Control.Lens ((%~), (<>~), (&)) +import Control.Monad.Except +import Control.Monad.Reader (ask) +import Control.Monad.State +import Data.Bifunctor (second) +import Data.Bool (bool) +import Data.Functor ((<&>)) +import Data.Generics.Labels () +import Data.List +import qualified Data.Set as S +import Data.Traversable +import Development.IDE.GHC.Compat +import GHC.Exts +import GHC.SourceGen (occNameToStr) +import GHC.SourceGen.Binds +import GHC.SourceGen.Expr +import GHC.SourceGen.Overloaded +import GHC.SourceGen.Pat +import Wingman.CodeGen.Utils +import Wingman.GHC +import Wingman.Judgements +import Wingman.Judgements.Theta +import Wingman.Machinery +import Wingman.Naming +import Wingman.Types + + +destructMatches + :: Bool + -> (ConLike -> Judgement -> Rule) + -- ^ How to construct each match + -> Maybe OccName + -- ^ Scrutinee + -> CType + -- ^ Type being destructed + -> Judgement + -> RuleM (Synthesized [RawMatch]) +-- TODO(sandy): In an ideal world, this would be the same codepath as +-- 'destructionFor'. Make sure to change that if you ever change this. +destructMatches use_field_puns f scrut t jdg = do + let hy = jEntireHypothesis jdg + g = jGoal jdg + case tacticsGetDataCons $ unCType t of + Nothing -> cut -- throwError $ GoalMismatch "destruct" g + Just (dcs, apps) -> + fmap unzipTrace $ for dcs $ \dc -> do + let con = RealDataCon dc + ev = concatMap (mkEvidence . scaledThing) $ dataConInstArgTys dc apps + -- We explicitly do not need to add the method hypothesis to + -- #syn_scoped + method_hy = foldMap evidenceToHypothesis ev + args = conLikeInstOrigArgTys' con apps + ctx <- ask + + let names_in_scope = hyNamesInScope hy + names = mkManyGoodNames (hyNamesInScope hy) args + (names', destructed) = + mkDestructPat (bool Nothing (Just names_in_scope) use_field_puns) con names + + let hy' = patternHypothesis scrut con jdg + $ zip names' + $ coerce args + j = withNewCoercions (evidenceToCoercions ev) + $ introduce ctx hy' + $ introduce ctx method_hy + $ withNewGoal g jdg + ext <- f con j + pure $ ext + & #syn_trace %~ rose ("match " <> show dc <> " {" <> intercalate ", " (fmap show names') <> "}") + . pure + & #syn_scoped <>~ hy' + & #syn_val %~ match [destructed] . unLoc + + +------------------------------------------------------------------------------ +-- | Generate just the 'Match'es for a case split on a specific type. +destructionFor :: Hypothesis a -> Type -> Maybe [LMatch GhcPs (LHsExpr GhcPs)] +-- TODO(sandy): In an ideal world, this would be the same codepath as +-- 'destructMatches'. Make sure to change that if you ever change this. +destructionFor hy t = do + case tacticsGetDataCons t of + Nothing -> Nothing + Just ([], _) -> Nothing + Just (dcs, apps) -> do + for dcs $ \dc -> do + let con = RealDataCon dc + args = conLikeInstOrigArgTys' con apps + names = mkManyGoodNames (hyNamesInScope hy) args + pure + . noLoc + . Match + noExtField + CaseAlt + [toPatCompat $ snd $ mkDestructPat Nothing con names] + . GRHSs noExtField (pure $ noLoc $ GRHS noExtField [] $ noLoc $ var "_") + . noLoc + $ EmptyLocalBinds noExtField + + + +------------------------------------------------------------------------------ +-- | Produces a pattern for a data con and the names of its fields. +mkDestructPat :: Maybe (S.Set OccName) -> ConLike -> [OccName] -> ([OccName], Pat GhcPs) +mkDestructPat already_in_scope con names + | RealDataCon dcon <- con + , isTupleDataCon dcon = + (names, tuple pat_args) + | fields@(_:_) <- zip (conLikeFieldLabels con) names + , Just in_scope <- already_in_scope = + let (names', rec_fields) = + unzip $ fields <&> \(label, name) -> do + let label_occ = mkVarOccFS $ flLabel label + case S.member label_occ in_scope of + -- We have a shadow, so use the generated name instead + True -> + (name,) $ noLoc $ + HsRecField + (noLoc $ mkFieldOcc $ noLoc $ Unqual label_occ) + (noLoc $ bvar' name) + False + -- No shadow, safe to use a pun + False -> + (label_occ,) $ noLoc $ + HsRecField + (noLoc $ mkFieldOcc $ noLoc $ Unqual label_occ) + (noLoc $ bvar' label_occ) + True + + in (names', ) + $ ConPatIn (noLoc $ Unqual $ occName $ conLikeName con) + $ RecCon + $ HsRecFields rec_fields Nothing + | otherwise = + (names, ) $ infixifyPatIfNecessary con $ + conP + (coerceName $ conLikeName con) + pat_args + where + pat_args = fmap bvar' names + + +infixifyPatIfNecessary :: ConLike -> Pat GhcPs -> Pat GhcPs +infixifyPatIfNecessary dcon x + | conLikeIsInfix dcon = + case x of + ConPatIn op (PrefixCon [lhs, rhs]) -> + ConPatIn op $ InfixCon lhs rhs + y -> y + | otherwise = x + + + +unzipTrace :: [Synthesized a] -> Synthesized [a] +unzipTrace = sequenceA + + +-- | Essentially same as 'dataConInstOrigArgTys' in GHC, +-- but only accepts universally quantified types as the second arguments +-- and automatically introduces existentials. +-- +-- NOTE: The behaviour depends on GHC's 'dataConInstOrigArgTys'. +-- We need some tweaks if the compiler changes the implementation. +conLikeInstOrigArgTys' + :: ConLike + -- ^ 'DataCon'structor + -> [Type] + -- ^ /Universally/ quantified type arguments to a result type. + -- It /MUST NOT/ contain any dictionaries, coercion and existentials. + -- + -- For example, for @MkMyGADT :: b -> MyGADT a c@, we + -- must pass @[a, c]@ as this argument but not @b@, as @b@ is an existential. + -> [Type] + -- ^ Types of arguments to the ConLike with returned type is instantiated with the second argument. +conLikeInstOrigArgTys' con uniTys = + let exvars = conLikeExTys con + in fmap scaledThing $ conLikeInstOrigArgTys con $ + uniTys ++ fmap mkTyVarTy exvars + -- Rationale: At least in GHC <= 8.10, 'dataConInstOrigArgTys' + -- unifies the second argument with DataCon's universals followed by existentials. + -- If the definition of 'dataConInstOrigArgTys' changes, + -- this place must be changed accordingly. + + +conLikeExTys :: ConLike -> [TyCoVar] +conLikeExTys (RealDataCon d) = dataConExTyCoVars d +conLikeExTys (PatSynCon p) = patSynExTys p + +patSynExTys :: PatSyn -> [TyCoVar] +patSynExTys ps = patSynExTyVars ps + + +------------------------------------------------------------------------------ +-- | Combinator for performing case splitting, and running sub-rules on the +-- resulting matches. + +destruct' :: Bool -> (ConLike -> Judgement -> Rule) -> HyInfo CType -> Judgement -> Rule +destruct' use_field_puns f hi jdg = do + when (isDestructBlacklisted jdg) cut -- throwError NoApplicableTactic + let term = hi_name hi + ext + <- destructMatches + use_field_puns + f + (Just term) + (hi_type hi) + $ disallowing AlreadyDestructed (S.singleton term) jdg + pure $ ext + & #syn_trace %~ rose ("destruct " <> show term) . pure + & #syn_val %~ noLoc . case' (var' term) + + +------------------------------------------------------------------------------ +-- | Combinator for performing case splitting, and running sub-rules on the +-- resulting matches. +destructLambdaCase' :: Bool -> (ConLike -> Judgement -> Rule) -> Judgement -> Rule +destructLambdaCase' use_field_puns f jdg = do + when (isDestructBlacklisted jdg) cut -- throwError NoApplicableTactic + let g = jGoal jdg + case splitFunTy_maybe (unCType g) of +#if __GLASGOW_HASKELL__ >= 900 + Just (_multiplicity, arg, _) | isAlgType arg -> +#else + Just (arg, _) | isAlgType arg -> +#endif + fmap (fmap noLoc lambdaCase) <$> + destructMatches use_field_puns f Nothing (CType arg) jdg + _ -> cut -- throwError $ GoalMismatch "destructLambdaCase'" g + + +------------------------------------------------------------------------------ +-- | Construct a data con with subgoals for each field. +buildDataCon + :: Bool -- Should we blacklist destruct? + -> Judgement + -> ConLike -- ^ The data con to build + -> [Type] -- ^ Type arguments for the data con + -> RuleM (Synthesized (LHsExpr GhcPs)) +buildDataCon should_blacklist jdg dc tyapps = do + args <- case dc of + RealDataCon dc' -> do + let (skolems', theta, args) = dataConInstSig dc' tyapps + modify $ \ts -> + evidenceToSubst (foldMap mkEvidence theta) ts + & #ts_skolems <>~ S.fromList skolems' + pure args + _ -> + -- If we have a 'PatSyn', we can't continue, since there is no + -- 'dataConInstSig' equivalent for 'PatSyn's. I don't think this is + -- a fundamental problem, but I don't know enough about the GHC internals + -- to implement it myself. + -- + -- Fortunately, this isn't an issue in practice, since 'PatSyn's are + -- never in the hypothesis. + cut -- throwError $ TacticPanic "Can't build Pattern constructors yet" + ext + <- fmap unzipTrace + $ traverse ( \(arg, n) -> + newSubgoal + . filterSameTypeFromOtherPositions dc n + . bool id blacklistingDestruct should_blacklist + . flip withNewGoal jdg + $ CType arg + ) $ zip args [0..] + pure $ ext + & #syn_trace %~ rose (show dc) . pure + & #syn_val %~ mkCon dc tyapps + + +------------------------------------------------------------------------------ +-- | Make a function application, correctly handling the infix case. +mkApply :: OccName -> [HsExpr GhcPs] -> LHsExpr GhcPs +mkApply occ (lhs : rhs : more) + | isSymOcc occ + = noLoc $ foldl' (@@) (op lhs (coerceName occ) rhs) more +mkApply occ args = noLoc $ foldl' (@@) (var' occ) args + + +------------------------------------------------------------------------------ +-- | Run a tactic over each term in the given 'Hypothesis', binding the results +-- of each in a let expression. +letForEach + :: (OccName -> OccName) -- ^ How to name bound variables + -> (HyInfo CType -> TacticsM ()) -- ^ The tactic to run + -> Hypothesis CType -- ^ Terms to generate bindings for + -> Judgement -- ^ The goal of original hole + -> RuleM (Synthesized (LHsExpr GhcPs)) +letForEach rename solve (unHypothesis -> hy) jdg = do + case hy of + [] -> newSubgoal jdg + _ -> do + ctx <- ask + let g = jGoal jdg + terms <- fmap sequenceA $ for hy $ \hi -> do + let name = rename $ hi_name hi + let generalized_let_ty = CType alphaTy + res <- tacticToRule (withNewGoal generalized_let_ty jdg) $ solve hi + pure $ fmap ((name,) . unLoc) res + let hy' = fmap (g <$) $ syn_val terms + matches = fmap (fmap (\(occ, expr) -> valBind (occNameToStr occ) expr)) terms + g <- fmap (fmap unLoc) $ newSubgoal $ introduce ctx (userHypothesis hy') jdg + pure $ fmap noLoc $ let' <$> matches <*> g + + +------------------------------------------------------------------------------ +-- | Let-bind the given occname judgement pairs. +nonrecLet + :: [(OccName, Judgement)] + -> Judgement + -> RuleM (Synthesized (LHsExpr GhcPs)) +nonrecLet occjdgs jdg = do + occexts <- traverse newSubgoal $ fmap snd occjdgs + ctx <- ask + ext <- newSubgoal + $ introduce ctx (userHypothesis $ fmap (second jGoal) occjdgs) jdg + pure $ fmap noLoc $ + let' + <$> traverse + (\(occ, ext) -> valBind (occNameToStr occ) <$> fmap unLoc ext) + (zip (fmap fst occjdgs) occexts) + <*> fmap unLoc ext + + +------------------------------------------------------------------------------ +-- | Converts a function application into applicative form +idiomize :: LHsExpr GhcPs -> LHsExpr GhcPs +idiomize x = noLoc $ case unLoc x of + HsApp _ (L _ (HsVar _ (L _ x))) gshgp3 -> + op (bvar' $ occName x) "<$>" (unLoc gshgp3) + HsApp _ gsigp gshgp3 -> + op (unLoc $ idiomize gsigp) "<*>" (unLoc gshgp3) + RecordCon _ con flds -> + unLoc $ idiomize $ noLoc $ foldl' (@@) (HsVar noExtField con) $ fmap unLoc flds + y -> y + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/CodeGen/Utils.hs b/plugins/hls-tactics-plugin/new/src/Wingman/CodeGen/Utils.hs new file mode 100644 index 0000000000..d683db9ffd --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/CodeGen/Utils.hs @@ -0,0 +1,79 @@ +module Wingman.CodeGen.Utils where + +import Data.String +import Data.List +import Development.IDE.GHC.Compat +import GHC.SourceGen (RdrNameStr (UnqualStr), recordConE, string) +import GHC.SourceGen.Overloaded as SourceGen +import Wingman.GHC (getRecordFields) + + +------------------------------------------------------------------------------ +-- | Make a data constructor with the given arguments. +mkCon :: ConLike -> [Type] -> [LHsExpr GhcPs] -> LHsExpr GhcPs +mkCon con apps (fmap unLoc -> args) + | RealDataCon dcon <- con + , dcon == nilDataCon + , [ty] <- apps + , ty `eqType` charTy = noLoc $ string "" + + | RealDataCon dcon <- con + , isTupleDataCon dcon = + noLoc $ tuple args + + | RealDataCon dcon <- con + , dataConIsInfix dcon + , (lhs : rhs : args') <- args = + noLoc $ foldl' (@@) (op lhs (coerceName con_name) rhs) args' + + | Just fields <- getRecordFields con + , length fields >= 2 = -- record notation is unnatural on single field ctors + noLoc $ recordConE (coerceName con_name) $ do + (arg, (field, _)) <- zip args fields + pure (coerceName field, arg) + + | otherwise = + noLoc $ foldl' (@@) (bvar' $ occName con_name) args + where + con_name = conLikeName con + + +coerceName :: HasOccName a => a -> RdrNameStr +coerceName = UnqualStr . fromString . occNameString . occName + + +------------------------------------------------------------------------------ +-- | Like 'var', but works over standard GHC 'OccName's. +var' :: SourceGen.Var a => OccName -> a +var' = var . fromString . occNameString + + +------------------------------------------------------------------------------ +-- | Like 'bvar', but works over standard GHC 'OccName's. +bvar' :: BVar a => OccName -> a +bvar' = bvar . fromString . occNameString + + +------------------------------------------------------------------------------ +-- | Get an HsExpr corresponding to a function name. +mkFunc :: String -> HsExpr GhcPs +mkFunc = var' . mkVarOcc + + +------------------------------------------------------------------------------ +-- | Get an HsExpr corresponding to a value name. +mkVal :: String -> HsExpr GhcPs +mkVal = var' . mkVarOcc + + +------------------------------------------------------------------------------ +-- | Like 'op', but easier to call. +infixCall :: String -> HsExpr GhcPs -> HsExpr GhcPs -> HsExpr GhcPs +infixCall s = flip op (fromString s) + + +------------------------------------------------------------------------------ +-- | Like '(@@)', but uses a dollar instead of parentheses. +appDollar :: HsExpr GhcPs -> HsExpr GhcPs -> HsExpr GhcPs +appDollar = infixCall "$" + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Context.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Context.hs new file mode 100644 index 0000000000..3c1b40ba1f --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Context.hs @@ -0,0 +1,112 @@ +{-# LANGUAGE CPP #-} + +module Wingman.Context where + +import Control.Arrow +import Control.Monad.Reader +import Data.Coerce (coerce) +import Data.Foldable.Extra (allM) +import Data.Maybe (fromMaybe, isJust, mapMaybe) +import qualified Data.Set as S +import Development.IDE.GHC.Compat +import Development.IDE.GHC.Compat.Util +import Wingman.GHC (normalizeType) +import Wingman.Judgements.Theta +import Wingman.Types + +#if __GLASGOW_HASKELL__ >= 900 +import GHC.Tc.Utils.TcType +#endif + + +mkContext + :: Config + -> [(OccName, CType)] + -> TcGblEnv + -> HscEnv + -> ExternalPackageState + -> [Evidence] + -> Context +mkContext cfg locals tcg hscenv eps ev = fix $ \ctx -> + Context + { ctxDefiningFuncs + = fmap (second $ coerce $ normalizeType ctx) locals + , ctxModuleFuncs + = fmap (second (coerce $ normalizeType ctx) . splitId) + . mappend (locallyDefinedMethods tcg) + . (getFunBindId =<<) + . fmap unLoc + . bagToList + $ tcg_binds tcg + , ctxConfig = cfg + , ctxFamInstEnvs = + (eps_fam_inst_env eps, tcg_fam_inst_env tcg) + , ctxInstEnvs = + InstEnvs + (eps_inst_env eps) + (tcg_inst_env tcg) + (tcVisibleOrphanMods tcg) + , ctxTheta = evidenceToThetaType ev + , ctx_hscEnv = hscenv + , ctx_occEnv = tcg_rdr_env tcg + , ctx_module = extractModule tcg + } + + +locallyDefinedMethods :: TcGblEnv -> [Id] +locallyDefinedMethods + = foldMap classMethods + . mapMaybe tyConClass_maybe + . tcg_tcs + + + +splitId :: Id -> (OccName, CType) +splitId = occName &&& CType . idType + + +getFunBindId :: HsBindLR GhcTc GhcTc -> [Id] +getFunBindId (AbsBinds _ _ _ abes _ _ _) + = abes >>= \case + ABE _ poly _ _ _ -> pure poly + _ -> [] +getFunBindId _ = [] + + +------------------------------------------------------------------------------ +-- | Determine if there is an instance that exists for the given 'Class' at the +-- specified types. Deeply checks contexts to ensure the instance is actually +-- real. +-- +-- If so, this returns a 'PredType' that corresponds to the type of the +-- dictionary. +getInstance :: MonadReader Context m => Class -> [Type] -> m (Maybe (Class, PredType)) +getInstance cls tys = do + env <- asks ctxInstEnvs + let (mres, _, _) = lookupInstEnv False env cls tys + case mres of + ((inst, mapps) : _) -> do + -- Get the instantiated type of the dictionary + let df = piResultTys (idType $ is_dfun inst) $ zipWith fromMaybe alphaTys mapps + -- pull off its resulting arguments + let (theta, df') = tcSplitPhiTy df + allM hasClassInstance theta >>= \case + True -> pure $ Just (cls, df') + False -> pure Nothing + _ -> pure Nothing + + +------------------------------------------------------------------------------ +-- | Like 'getInstance', but only returns whether or not it succeeded. Can fail +-- fast, and uses a cached Theta from the context. +hasClassInstance :: MonadReader Context m => PredType -> m Bool +hasClassInstance predty = do + theta <- asks ctxTheta + case S.member (CType predty) theta of + True -> pure True + False -> do + let (con, apps) = tcSplitTyConApp predty + case tyConClass_maybe con of + Nothing -> pure False + Just cls -> fmap isJust $ getInstance cls apps + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Debug.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Debug.hs new file mode 100644 index 0000000000..e637779824 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Debug.hs @@ -0,0 +1,65 @@ +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE TypeApplications #-} +{-# OPTIONS_GHC -Wno-redundant-constraints #-} +{-# OPTIONS_GHC -Wno-unused-imports #-} +module Wingman.Debug + ( unsafeRender + , unsafeRender' + , traceM + , traceShowId + , trace + , traceX + , traceIdX + , traceMX + , traceFX + ) where + +import Control.DeepSeq +import Control.Exception +import Data.Either (fromRight) +import qualified Data.Text as T +import qualified Debug.Trace +import Development.IDE.GHC.Compat (PlainGhcException, Outputable(..), SDoc) +import Development.IDE.GHC.Util (printOutputable) +import System.IO.Unsafe (unsafePerformIO) + +------------------------------------------------------------------------------ +-- | Print something +unsafeRender :: Outputable a => a -> String +unsafeRender = unsafeRender' . ppr + + +unsafeRender' :: SDoc -> String +unsafeRender' sdoc = unsafePerformIO $ do + let z = T.unpack $ printOutputable sdoc + -- We might not have unsafeGlobalDynFlags (like during testing), in which + -- case GHC panics. Instead of crashing, let's just fail to print. + !res <- try @PlainGhcException $ evaluate $ deepseq z z + pure $ fromRight "" res +{-# NOINLINE unsafeRender' #-} + +traceMX :: (Monad m, Show a) => String -> a -> m () +traceMX str a = traceM $ mappend ("!!!" <> str <> ": ") $ show a + +traceX :: (Show a) => String -> a -> b -> b +traceX str a = trace (mappend ("!!!" <> str <> ": ") $ show a) + +traceIdX :: (Show a) => String -> a -> a +traceIdX str a = trace (mappend ("!!!" <> str <> ": ") $ show a) a + +traceFX :: String -> (a -> String) -> a -> a +traceFX str f a = trace (mappend ("!!!" <> str <> ": ") $ f a) a + +traceM :: Applicative f => String -> f () +trace :: String -> a -> a +traceShowId :: Show a => a -> a +#ifdef DEBUG +traceM = Debug.Trace.traceM +trace = Debug.Trace.trace +traceShowId = Debug.Trace.traceShowId +#else +traceM _ = pure () +trace _ = id +traceShowId = id +#endif diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/EmptyCase.hs b/plugins/hls-tactics-plugin/new/src/Wingman/EmptyCase.hs new file mode 100644 index 0000000000..a13d7c1a65 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/EmptyCase.hs @@ -0,0 +1,170 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TypeFamilies #-} + +{-# LANGUAGE NoMonoLocalBinds #-} + +module Wingman.EmptyCase where + +import Control.Applicative (empty) +import Control.Monad +import Control.Monad.Except (runExcept) +import Control.Monad.Trans +import Control.Monad.Trans.Maybe +import Data.Generics.Aliases (mkQ, GenericQ) +import Data.Generics.Schemes (everything) +import Data.Maybe +import Data.Monoid +import qualified Data.Text as T +import Data.Traversable +import Development.IDE (hscEnv, realSrcSpanToRange) +import Development.IDE.Core.RuleTypes +import Development.IDE.Core.Shake (IdeState (..)) +import Development.IDE.Core.UseStale +import Development.IDE.GHC.Compat hiding (empty, EmptyCase) +import Development.IDE.GHC.ExactPrint +import Development.IDE.Spans.LocalBindings (getLocalScope) +import Ide.Types +import Language.LSP.Server +import Language.LSP.Types +import Prelude hiding (span) +import Wingman.AbstractLSP.Types +import Wingman.CodeGen (destructionFor) +import Wingman.GHC +import Wingman.Judgements +import Wingman.LanguageServer +import Wingman.Types + + +data EmptyCaseT = EmptyCaseT + +instance IsContinuationSort EmptyCaseT where + toCommandId _ = CommandId "wingman.emptyCase" + +instance IsTarget EmptyCaseT where + type TargetArgs EmptyCaseT = () + fetchTargetArgs _ = pure () + +emptyCaseInteraction :: Interaction +emptyCaseInteraction = Interaction $ + Continuation @EmptyCaseT @EmptyCaseT @WorkspaceEdit EmptyCaseT + (SynthesizeCodeLens $ \LspEnv{..} _ -> do + let FileContext{..} = le_fileContext + nfp <- getNfp fc_uri + + let stale a = runStaleIde "codeLensProvider" le_ideState nfp a + + ccs <- lift getClientCapabilities + TrackedStale pm _ <- mapMaybeT liftIO $ stale GetAnnotatedParsedSource + TrackedStale binds bind_map <- mapMaybeT liftIO $ stale GetBindings + holes <- mapMaybeT liftIO $ emptyCaseScrutinees le_ideState nfp + + for holes $ \(ss, ty) -> do + binds_ss <- liftMaybe $ mapAgeFrom bind_map ss + let bindings = getLocalScope (unTrack binds) $ unTrack binds_ss + range = realSrcSpanToRange $ unTrack ss + matches <- + liftMaybe $ + destructionFor + (foldMap (hySingleton . occName . fst) bindings) + ty + edits <- liftMaybe $ hush $ + mkWorkspaceEdits le_dflags ccs fc_uri (unTrack pm) $ + graftMatchGroup (RealSrcSpan (unTrack ss) Nothing) $ + noLoc matches + pure + ( range + , Metadata + (mkEmptyCaseLensDesc ty) + (CodeActionUnknown "refactor.wingman.completeEmptyCase") + False + , edits + ) + ) + (\ _ _ _ we -> pure $ pure $ RawEdit we) + + +scrutinzedType :: EmptyCaseSort Type -> Maybe Type +scrutinzedType (EmptyCase ty) = pure ty +scrutinzedType (EmptyLamCase ty) = + case tacticsSplitFunTy ty of + (_, _, tys, _) -> listToMaybe $ fmap scaledThing tys + + +------------------------------------------------------------------------------ +-- | The description for the empty case lens. +mkEmptyCaseLensDesc :: Type -> T.Text +mkEmptyCaseLensDesc ty = + "Wingman: Complete case constructors (" <> T.pack (unsafeRender ty) <> ")" + + +------------------------------------------------------------------------------ +-- | Silence an error. +hush :: Either e a -> Maybe a +hush (Left _) = Nothing +hush (Right a) = Just a + + +------------------------------------------------------------------------------ +-- | Graft a 'RunTacticResults' into the correct place in an AST. Correctly +-- deals with top-level holes, in which we might need to fiddle with the +-- 'Match's that bind variables. +graftMatchGroup + :: SrcSpan + -> Located [LMatch GhcPs (LHsExpr GhcPs)] + -> Graft (Either String) ParsedSource +graftMatchGroup ss l = + hoistGraft (runExcept . runExceptString) $ graftExprWithM ss $ \case + L span (HsCase ext scrut mg) -> do + pure $ Just $ L span $ HsCase ext scrut $ mg { mg_alts = l } + L span (HsLamCase ext mg) -> do + pure $ Just $ L span $ HsLamCase ext $ mg { mg_alts = l } + (_ :: LHsExpr GhcPs) -> pure Nothing + + +fromMaybeT :: Functor m => a -> MaybeT m a -> m a +fromMaybeT def = fmap (fromMaybe def) . runMaybeT + + +------------------------------------------------------------------------------ +-- | Find the last typechecked module, and find the most specific span, as well +-- as the judgement at the given range. +emptyCaseScrutinees + :: IdeState + -> NormalizedFilePath + -> MaybeT IO [(Tracked 'Current RealSrcSpan, Type)] +emptyCaseScrutinees state nfp = do + let stale a = runStaleIde "emptyCaseScrutinees" state nfp a + + TrackedStale tcg tcg_map <- fmap (fmap tmrTypechecked) $ stale TypeCheck + let tcg' = unTrack tcg + hscenv <- stale GhcSessionDeps + + let scrutinees = traverse (emptyCaseQ . tcg_binds) tcg + fmap catMaybes $ for scrutinees $ \aged@(unTrack -> (ss, scrutinee)) -> do + ty <- MaybeT + . fmap (scrutinzedType <=< sequence) + . traverse (typeCheck (hscEnv $ untrackedStaleValue hscenv) tcg') + $ scrutinee + case null $ tacticsGetDataCons ty of + True -> pure empty + False -> + case ss of + RealSrcSpan r _ -> do + rss' <- liftMaybe $ mapAgeTo tcg_map $ unsafeCopyAge aged r + pure $ Just (rss', ty) + UnhelpfulSpan _ -> empty + +data EmptyCaseSort a + = EmptyCase a + | EmptyLamCase a + deriving (Eq, Ord, Show, Functor, Foldable, Traversable) + +------------------------------------------------------------------------------ +-- | Get the 'SrcSpan' and scrutinee of every empty case. +emptyCaseQ :: GenericQ [(SrcSpan, EmptyCaseSort (HsExpr GhcTc))] +emptyCaseQ = everything (<>) $ mkQ mempty $ \case + L new_span (Case scrutinee []) -> pure (new_span, EmptyCase scrutinee) + L new_span expr@(LamCase []) -> pure (new_span, EmptyLamCase expr) + (_ :: LHsExpr GhcTc) -> mempty + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/GHC.hs b/plugins/hls-tactics-plugin/new/src/Wingman/GHC.hs new file mode 100644 index 0000000000..13562a6ef8 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/GHC.hs @@ -0,0 +1,369 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE OverloadedStrings #-} + +module Wingman.GHC where + +import Control.Monad.State +import Control.Monad.Trans.Maybe (MaybeT(..)) +import Data.Bool (bool) +import Data.Coerce (coerce) +import Data.Function (on) +import Data.Functor ((<&>)) +import Data.List (isPrefixOf) +import qualified Data.Map as M +import Data.Maybe (isJust) +import Data.Set (Set) +import qualified Data.Set as S +import Data.Traversable +import Development.IDE.GHC.Compat +import Development.IDE.GHC.Compat.Util +import GHC.SourceGen (lambda) +import Generics.SYB (Data, everything, everywhere, listify, mkQ, mkT) +import Wingman.StaticPlugin (pattern MetaprogramSyntax) +import Wingman.Types + +#if __GLASGOW_HASKELL__ >= 900 +import GHC.Tc.Utils.TcType +#endif + + +tcTyVar_maybe :: Type -> Maybe Var +tcTyVar_maybe ty | Just ty' <- tcView ty = tcTyVar_maybe ty' +tcTyVar_maybe (CastTy ty _) = tcTyVar_maybe ty -- look through casts, as + -- this is only used for + -- e.g., FlexibleContexts +tcTyVar_maybe (TyVarTy v) = Just v +tcTyVar_maybe _ = Nothing + + +instantiateType :: Type -> ([TyVar], Type) +instantiateType t = do + let vs = tyCoVarsOfTypeList t + vs' = fmap cloneTyVar vs + subst = foldr (\(v,t) a -> extendTCvSubst a v $ TyVarTy t) emptyTCvSubst + $ zip vs vs' + in (vs', substTy subst t) + + +cloneTyVar :: TyVar -> TyVar +cloneTyVar t = + let uniq = getUnique t + some_magic_char = 'w' -- 'w' for wingman ;D + in setVarUnique t $ newTagUnique uniq some_magic_char + + +------------------------------------------------------------------------------ +-- | Is this a function type? +isFunction :: Type -> Bool +isFunction (tacticsSplitFunTy -> (_, _, [], _)) = False +isFunction _ = True + + +------------------------------------------------------------------------------ +-- | Split a function, also splitting out its quantified variables and theta +-- context. +tacticsSplitFunTy :: Type -> ([TyVar], ThetaType, [Scaled Type], Type) +tacticsSplitFunTy t + = let (vars, theta, t') = tcSplitNestedSigmaTys t + (args, res) = tcSplitFunTys t' + in (vars, theta, args, res) + + +------------------------------------------------------------------------------ +-- | Rip the theta context out of a regular type. +tacticsThetaTy :: Type -> ThetaType +tacticsThetaTy (tcSplitSigmaTy -> (_, theta, _)) = theta + + +------------------------------------------------------------------------------ +-- | Get the data cons of a type, if it has any. +tacticsGetDataCons :: Type -> Maybe ([DataCon], [Type]) +tacticsGetDataCons ty + | Just (_, ty') <- tcSplitForAllTyVarBinder_maybe ty + = tacticsGetDataCons ty' +tacticsGetDataCons ty + | Just _ <- algebraicTyCon ty + = splitTyConApp_maybe ty <&> \(tc, apps) -> + ( filter (not . dataConCannotMatch apps) $ tyConDataCons tc + , apps + ) +tacticsGetDataCons _ = Nothing + +------------------------------------------------------------------------------ +-- | Instantiate all of the quantified type variables in a type with fresh +-- skolems. +freshTyvars :: MonadState TacticState m => Type -> m Type +freshTyvars t = do + let (tvs, _, _, _) = tacticsSplitFunTy t + reps <- fmap M.fromList + $ for tvs $ \tv -> do + uniq <- freshUnique + pure (tv, setTyVarUnique tv uniq) + pure $ + everywhere + (mkT $ \tv -> M.findWithDefault tv tv reps + ) $ snd $ tcSplitForAllTyVars t + + +------------------------------------------------------------------------------ +-- | Given a datacon, extract its record fields' names and types. Returns +-- nothing if the datacon is not a record. +getRecordFields :: ConLike -> Maybe [(OccName, CType)] +getRecordFields dc = + case conLikeFieldLabels dc of + [] -> Nothing + lbls -> for lbls $ \lbl -> do + let ty = conLikeFieldType dc $ flLabel lbl + pure (mkVarOccFS $ flLabel lbl, CType ty) + + +------------------------------------------------------------------------------ +-- | Is this an algebraic type? +algebraicTyCon :: Type -> Maybe TyCon +algebraicTyCon ty + | Just (_, ty') <- tcSplitForAllTyVarBinder_maybe ty + = algebraicTyCon ty' +algebraicTyCon (splitTyConApp_maybe -> Just (tycon, _)) + | tycon == intTyCon = Nothing + | tycon == floatTyCon = Nothing + | tycon == doubleTyCon = Nothing + | tycon == charTyCon = Nothing + | tycon == funTyCon = Nothing + | otherwise = Just tycon +algebraicTyCon _ = Nothing + + +------------------------------------------------------------------------------ +-- | We can't compare 'RdrName' for equality directly. Instead, sloppily +-- compare them by their 'OccName's. +eqRdrName :: RdrName -> RdrName -> Bool +eqRdrName = (==) `on` occNameString . occName + + +------------------------------------------------------------------------------ +-- | Compare two 'OccName's for unqualified equality. +sloppyEqOccName :: OccName -> OccName -> Bool +sloppyEqOccName = (==) `on` occNameString + + +------------------------------------------------------------------------------ +-- | Does this thing contain any references to 'HsVar's with the given +-- 'RdrName'? +containsHsVar :: Data a => RdrName -> a -> Bool +containsHsVar name x = not $ null $ listify ( + \case + ((HsVar _ (L _ a)) :: HsExpr GhcPs) | eqRdrName a name -> True + _ -> False + ) x + + +------------------------------------------------------------------------------ +-- | Does this thing contain any holes? +containsHole :: Data a => a -> Bool +containsHole x = not $ null $ listify ( + \case + ((HsVar _ (L _ name)) :: HsExpr GhcPs) -> isHole $ occName name + MetaprogramSyntax _ -> True + _ -> False + ) x + + +------------------------------------------------------------------------------ +-- | Check if an 'OccName' is a hole +isHole :: OccName -> Bool +-- TODO(sandy): Make this more robust +isHole = isPrefixOf "_" . occNameString + + +------------------------------------------------------------------------------ +-- | Get all of the referenced occnames. +allOccNames :: Data a => a -> Set OccName +allOccNames = everything (<>) $ mkQ mempty $ \case + a -> S.singleton a + + +------------------------------------------------------------------------------ +-- | Unpack the relevant parts of a 'Match' +#if __GLASGOW_HASKELL__ >= 900 +pattern AMatch :: HsMatchContext (NoGhcTc GhcPs) -> [Pat GhcPs] -> HsExpr GhcPs -> Match GhcPs (LHsExpr GhcPs) +#else +pattern AMatch :: HsMatchContext (NameOrRdrName (IdP GhcPs)) -> [Pat GhcPs] -> HsExpr GhcPs -> Match GhcPs (LHsExpr GhcPs) +#endif +pattern AMatch ctx pats body <- + Match { m_ctxt = ctx + , m_pats = fmap fromPatCompat -> pats + , m_grhss = UnguardedRHSs (unLoc -> body) + } + + +pattern SingleLet :: IdP GhcPs -> [Pat GhcPs] -> HsExpr GhcPs -> HsExpr GhcPs -> HsExpr GhcPs +pattern SingleLet bind pats val expr <- + HsLet _ + (HsValBinds _ + (ValBinds _ (bagToList -> + [L _ (FunBind {fun_id = (L _ bind), fun_matches = (MG _ (L _ [L _ (AMatch _ pats val)]) _)})]) _)) + (L _ expr) + + +------------------------------------------------------------------------------ +-- | A pattern over the otherwise (extremely) messy AST for lambdas. +pattern Lambda :: [Pat GhcPs] -> HsExpr GhcPs -> HsExpr GhcPs +pattern Lambda pats body <- + HsLam _ + MG {mg_alts = L _ [L _ (AMatch _ pats body) ]} + where + -- If there are no patterns to bind, just stick in the body + Lambda [] body = body + Lambda pats body = lambda pats body + + +------------------------------------------------------------------------------ +-- | A GRHS that contains no guards. +pattern UnguardedRHSs :: LHsExpr p -> GRHSs p (LHsExpr p) +pattern UnguardedRHSs body <- + GRHSs {grhssGRHSs = [L _ (GRHS _ [] body)]} + + +------------------------------------------------------------------------------ +-- | A match with a single pattern. Case matches are always 'SinglePatMatch'es. +pattern SinglePatMatch :: PatCompattable p => Pat p -> LHsExpr p -> Match p (LHsExpr p) +pattern SinglePatMatch pat body <- + Match { m_pats = [fromPatCompat -> pat] + , m_grhss = UnguardedRHSs body + } + + +------------------------------------------------------------------------------ +-- | Helper function for defining the 'Case' pattern. +unpackMatches :: PatCompattable p => [Match p (LHsExpr p)] -> Maybe [(Pat p, LHsExpr p)] +unpackMatches [] = Just [] +unpackMatches (SinglePatMatch pat body : matches) = + ((pat, body):) <$> unpackMatches matches +unpackMatches _ = Nothing + + +------------------------------------------------------------------------------ +-- | A pattern over the otherwise (extremely) messy AST for lambdas. +pattern Case :: PatCompattable p => HsExpr p -> [(Pat p, LHsExpr p)] -> HsExpr p +pattern Case scrutinee matches <- + HsCase _ (L _ scrutinee) + MG {mg_alts = L _ (fmap unLoc -> unpackMatches -> Just matches)} + +------------------------------------------------------------------------------ +-- | Like 'Case', but for lambda cases. +pattern LamCase :: PatCompattable p => [(Pat p, LHsExpr p)] -> HsExpr p +pattern LamCase matches <- + HsLamCase _ + MG {mg_alts = L _ (fmap unLoc -> unpackMatches -> Just matches)} + + +------------------------------------------------------------------------------ +-- | Can ths type be lambda-cased? +-- +-- Return: 'Nothing' if no +-- @Just False@ if it can't be homomorphic +-- @Just True@ if it can +lambdaCaseable :: Type -> Maybe Bool +#if __GLASGOW_HASKELL__ >= 900 +lambdaCaseable (splitFunTy_maybe -> Just (_multiplicity, arg, res)) +#else +lambdaCaseable (splitFunTy_maybe -> Just (arg, res)) +#endif + | isJust (algebraicTyCon arg) + = Just $ isJust $ algebraicTyCon res +lambdaCaseable _ = Nothing + +class PatCompattable p where + fromPatCompat :: PatCompat p -> Pat p + toPatCompat :: Pat p -> PatCompat p + +instance PatCompattable GhcTc where + fromPatCompat = unLoc + toPatCompat = noLoc + +instance PatCompattable GhcPs where + fromPatCompat = unLoc + toPatCompat = noLoc + +type PatCompat pass = LPat pass + +------------------------------------------------------------------------------ +-- | Should make sure it's a fun bind +pattern TopLevelRHS + :: OccName + -> [PatCompat GhcTc] + -> LHsExpr GhcTc + -> HsLocalBindsLR GhcTc GhcTc + -> Match GhcTc (LHsExpr GhcTc) +pattern TopLevelRHS name ps body where_binds <- + Match _ + (FunRhs (L _ (occName -> name)) _ _) + ps + (GRHSs _ + [L _ (GRHS _ [] body)] (L _ where_binds)) + +liftMaybe :: Monad m => Maybe a -> MaybeT m a +liftMaybe a = MaybeT $ pure a + + +------------------------------------------------------------------------------ +-- | Get the type of an @HsExpr GhcTc@. This is slow and you should prefer to +-- not use it, but sometimes it can't be helped. +typeCheck :: HscEnv -> TcGblEnv -> HsExpr GhcTc -> IO (Maybe Type) +typeCheck hscenv tcg = fmap snd . initDs hscenv tcg . fmap exprType . dsExpr + +------------------------------------------------------------------------------ +-- | Expand type and data families +normalizeType :: Context -> Type -> Type +normalizeType ctx ty = + let ty' = expandTyFam ctx ty + in case tcSplitTyConApp_maybe ty' of + Just (tc, tys) -> + -- try to expand any data families + case tcLookupDataFamInst_maybe (ctxFamInstEnvs ctx) tc tys of + Just (dtc, dtys, _) -> mkAppTys (mkTyConTy dtc) dtys + Nothing -> ty' + Nothing -> ty' + +------------------------------------------------------------------------------ +-- | Expand type families +expandTyFam :: Context -> Type -> Type +expandTyFam ctx = snd . normaliseType (ctxFamInstEnvs ctx) Nominal + + +------------------------------------------------------------------------------ +-- | Like 'tcUnifyTy', but takes a list of skolems to prevent unification of. +tryUnifyUnivarsButNotSkolems :: Set TyVar -> CType -> CType -> Maybe TCvSubst +tryUnifyUnivarsButNotSkolems skolems goal inst = + tryUnifyUnivarsButNotSkolemsMany skolems $ coerce [(goal, inst)] + +------------------------------------------------------------------------------ +-- | Like 'tryUnifyUnivarsButNotSkolems', but takes a list +-- of pairs of types to unify. +tryUnifyUnivarsButNotSkolemsMany :: Set TyVar -> [(Type, Type)] -> Maybe TCvSubst +tryUnifyUnivarsButNotSkolemsMany skolems (unzip -> (goal, inst)) = + tcUnifyTys + (bool BindMe Skolem . flip S.member skolems) + inst + goal + + +updateSubst :: TCvSubst -> TacticState -> TacticState +updateSubst subst s = s { ts_unifier = unionTCvSubst subst (ts_unifier s) } + + +------------------------------------------------------------------------------ +-- | Get the class methods of a 'PredType', correctly dealing with +-- instantiation of quantified class types. +methodHypothesis :: PredType -> Maybe [HyInfo CType] +methodHypothesis ty = do + (tc, apps) <- splitTyConApp_maybe ty + cls <- tyConClass_maybe tc + let methods = classMethods cls + tvs = classTyVars cls + subst = zipTvSubst tvs apps + pure $ methods <&> \method -> + let (_, _, ty) = tcSplitSigmaTy $ idType method + in ( HyInfo (occName method) (ClassMethodPrv $ Uniquely cls) $ CType $ substTy subst ty + ) + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Judgements.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Judgements.hs new file mode 100644 index 0000000000..0ff03e60ee --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Judgements.hs @@ -0,0 +1,474 @@ +module Wingman.Judgements where + +import Control.Arrow +import Control.Lens hiding (Context) +import Data.Bool +import Data.Char +import Data.Coerce +import Data.Generics.Product (field) +import Data.Map (Map) +import qualified Data.Map as M +import Data.Maybe +import Data.Set (Set) +import qualified Data.Set as S +import Development.IDE.Core.UseStale (Tracked, unTrack) +import Development.IDE.GHC.Compat hiding (isTopLevel) +import Development.IDE.Spans.LocalBindings +import Wingman.GHC (algebraicTyCon, normalizeType) +import Wingman.Judgements.Theta +import Wingman.Types + + +------------------------------------------------------------------------------ +-- | Given a 'SrcSpan' and a 'Bindings', create a hypothesis. +hypothesisFromBindings :: Tracked age RealSrcSpan -> Tracked age Bindings -> Hypothesis CType +hypothesisFromBindings (unTrack -> span) (unTrack -> bs) = buildHypothesis $ getLocalScope bs span + + +------------------------------------------------------------------------------ +-- | Convert a @Set Id@ into a hypothesis. +buildHypothesis :: [(Name, Maybe Type)] -> Hypothesis CType +buildHypothesis + = Hypothesis + . mapMaybe go + where + go (occName -> occ, t) + | Just ty <- t + , (h:_) <- occNameString occ + , isAlpha h = Just $ HyInfo occ UserPrv $ CType ty + | otherwise = Nothing + + +------------------------------------------------------------------------------ +-- | Build a trivial hypothesis containing only a single name. The corresponding +-- HyInfo has no provenance or type. +hySingleton :: OccName -> Hypothesis () +hySingleton n = Hypothesis . pure $ HyInfo n UserPrv () + + +blacklistingDestruct :: Judgement -> Judgement +blacklistingDestruct = + field @"_jBlacklistDestruct" .~ True + + +unwhitelistingSplit :: Judgement -> Judgement +unwhitelistingSplit = + field @"_jWhitelistSplit" .~ False + + +isDestructBlacklisted :: Judgement -> Bool +isDestructBlacklisted = _jBlacklistDestruct + + +isSplitWhitelisted :: Judgement -> Bool +isSplitWhitelisted = _jWhitelistSplit + + +withNewGoal :: a -> Judgement' a -> Judgement' a +withNewGoal t = field @"_jGoal" .~ t + +------------------------------------------------------------------------------ +-- | Like 'withNewGoal' but allows you to modify the goal rather than replacing +-- it. +withModifiedGoal :: (a -> a) -> Judgement' a -> Judgement' a +withModifiedGoal f = field @"_jGoal" %~ f + + +------------------------------------------------------------------------------ +-- | Add some new type equalities to the local judgement. +withNewCoercions :: [(CType, CType)] -> Judgement -> Judgement +withNewCoercions ev j = + let subst = allEvidenceToSubst mempty $ coerce ev + in fmap (CType . substTyAddInScope subst . unCType) j + & field @"j_coercion" %~ unionTCvSubst subst + + +normalizeHypothesis :: Functor f => Context -> f CType -> f CType +normalizeHypothesis = fmap . coerce . normalizeType + +normalizeJudgement :: Functor f => Context -> f CType -> f CType +normalizeJudgement = normalizeHypothesis + + +introduce :: Context -> Hypothesis CType -> Judgement' CType -> Judgement' CType +-- NOTE(sandy): It's important that we put the new hypothesis terms first, +-- since 'jAcceptableDestructTargets' will never destruct a pattern that occurs +-- after a previously-destructed term. +introduce ctx hy = + field @"_jHypothesis" %~ mappend (normalizeHypothesis ctx hy) + + +------------------------------------------------------------------------------ +-- | Helper function for implementing functions which introduce new hypotheses. +introduceHypothesis + :: (Int -> Int -> Provenance) + -- ^ A function from the total number of args and position of this arg + -- to its provenance. + -> [(OccName, a)] + -> Hypothesis a +introduceHypothesis f ns = + Hypothesis $ zip [0..] ns <&> \(pos, (name, ty)) -> + HyInfo name (f (length ns) pos) ty + + +------------------------------------------------------------------------------ +-- | Introduce bindings in the context of a lambda. +lambdaHypothesis + :: Maybe OccName -- ^ The name of the top level function. For any other + -- function, this should be 'Nothing'. + -> [(OccName, a)] + -> Hypothesis a +lambdaHypothesis func = + introduceHypothesis $ \count pos -> + maybe UserPrv (\x -> TopLevelArgPrv x pos count) func + + +------------------------------------------------------------------------------ +-- | Introduce a binding in a recursive context. +recursiveHypothesis :: [(OccName, a)] -> Hypothesis a +recursiveHypothesis = introduceHypothesis $ const $ const RecursivePrv + + +------------------------------------------------------------------------------ +-- | Introduce a binding in a recursive context. +userHypothesis :: [(OccName, a)] -> Hypothesis a +userHypothesis = introduceHypothesis $ const $ const UserPrv + + +------------------------------------------------------------------------------ +-- | Check whether any of the given occnames are an ancestor of the term. +hasPositionalAncestry + :: Foldable t + => t OccName -- ^ Desired ancestors. + -> Judgement + -> OccName -- ^ Potential child + -> Maybe Bool -- ^ Just True if the result is the oldest positional ancestor + -- just false if it's a descendent + -- otherwise nothing +hasPositionalAncestry ancestors jdg name + | not $ null ancestors + = case name `elem` ancestors of + True -> Just True + False -> + case M.lookup name $ jAncestryMap jdg of + Just ancestry -> + bool Nothing (Just False) $ any (flip S.member ancestry) ancestors + Nothing -> Nothing + | otherwise = Nothing + + +------------------------------------------------------------------------------ +-- | Helper function for disallowing hypotheses that have the wrong ancestry. +filterAncestry + :: Foldable t + => t OccName + -> DisallowReason + -> Judgement + -> Judgement +filterAncestry ancestry reason jdg = + disallowing reason (M.keysSet $ M.filterWithKey go $ hyByName $ jHypothesis jdg) jdg + where + go name _ + = isNothing + $ hasPositionalAncestry ancestry jdg name + + +------------------------------------------------------------------------------ +-- | @filter defn pos@ removes any hypotheses which are bound in @defn@ to +-- a position other than @pos@. Any terms whose ancestry doesn't include @defn@ +-- remain. +filterPosition :: OccName -> Int -> Judgement -> Judgement +filterPosition defn pos jdg = + filterAncestry (findPositionVal jdg defn pos) (WrongBranch pos) jdg + + +------------------------------------------------------------------------------ +-- | Helper function for determining the ancestry list for 'filterPosition'. +findPositionVal :: Judgement' a -> OccName -> Int -> Maybe OccName +findPositionVal jdg defn pos = listToMaybe $ do + -- It's important to inspect the entire hypothesis here, as we need to trace + -- ancestry through potentially disallowed terms in the hypothesis. + (name, hi) <- M.toList + $ M.map (overProvenance expandDisallowed) + $ hyByName + $ jEntireHypothesis jdg + case hi_provenance hi of + TopLevelArgPrv defn' pos' _ + | defn == defn' + , pos == pos' -> pure name + PatternMatchPrv pv + | pv_scrutinee pv == Just defn + , pv_position pv == pos -> pure name + _ -> [] + + +------------------------------------------------------------------------------ +-- | Helper function for determining the ancestry list for +-- 'filterSameTypeFromOtherPositions'. +findDconPositionVals :: Judgement' a -> ConLike -> Int -> [OccName] +findDconPositionVals jdg dcon pos = do + (name, hi) <- M.toList $ hyByName $ jHypothesis jdg + case hi_provenance hi of + PatternMatchPrv pv + | pv_datacon pv == Uniquely dcon + , pv_position pv == pos -> pure name + _ -> [] + + +------------------------------------------------------------------------------ +-- | Disallow any hypotheses who have the same type as anything bound by the +-- given position for the datacon. Used to ensure recursive functions like +-- 'fmap' preserve the relative ordering of their arguments by eliminating any +-- other term which might match. +filterSameTypeFromOtherPositions :: ConLike -> Int -> Judgement -> Judgement +filterSameTypeFromOtherPositions dcon pos jdg = + let hy = hyByName + . jHypothesis + $ filterAncestry + (findDconPositionVals jdg dcon pos) + (WrongBranch pos) + jdg + tys = S.fromList $ hi_type <$> M.elems hy + to_remove = + M.filter (flip S.member tys . hi_type) (hyByName $ jHypothesis jdg) + M.\\ hy + in disallowing Shadowed (M.keysSet to_remove) jdg + + +------------------------------------------------------------------------------ +-- | Return the ancestry of a 'PatVal', or 'mempty' otherwise. +getAncestry :: Judgement' a -> OccName -> Set OccName +getAncestry jdg name = + maybe mempty pv_ancestry . M.lookup name $ jPatHypothesis jdg + + +jAncestryMap :: Judgement' a -> Map OccName (Set OccName) +jAncestryMap jdg = + M.map pv_ancestry (jPatHypothesis jdg) + + +provAncestryOf :: Provenance -> Set OccName +provAncestryOf (TopLevelArgPrv o _ _) = S.singleton o +provAncestryOf (PatternMatchPrv (PatVal mo so _ _)) = + maybe mempty S.singleton mo <> so +provAncestryOf (ClassMethodPrv _) = mempty +provAncestryOf UserPrv = mempty +provAncestryOf RecursivePrv = mempty +provAncestryOf ImportPrv = mempty +provAncestryOf (DisallowedPrv _ p2) = provAncestryOf p2 + + +------------------------------------------------------------------------------ +-- TODO(sandy): THIS THING IS A BIG BIG HACK +-- +-- Why? 'ctxDefiningFuncs' is _all_ of the functions currently being defined +-- (eg, we might be in a where block). The head of this list is not guaranteed +-- to be the one we're interested in. +extremelyStupid__definingFunction :: Context -> OccName +extremelyStupid__definingFunction = + fst . head . ctxDefiningFuncs + + +patternHypothesis + :: Maybe OccName + -> ConLike + -> Judgement' a + -> [(OccName, a)] + -> Hypothesis a +patternHypothesis scrutinee dc jdg + = introduceHypothesis $ \_ pos -> + PatternMatchPrv $ + PatVal + scrutinee + (maybe + mempty + (\scrut -> S.singleton scrut <> getAncestry jdg scrut) + scrutinee) + (Uniquely dc) + pos + + +------------------------------------------------------------------------------ +-- | Prevent some occnames from being used in the hypothesis. This will hide +-- them from 'jHypothesis', but not from 'jEntireHypothesis'. +disallowing :: DisallowReason -> S.Set OccName -> Judgement' a -> Judgement' a +disallowing reason ns = + field @"_jHypothesis" %~ (\z -> Hypothesis . flip fmap (unHypothesis z) $ \hi -> + case S.member (hi_name hi) ns of + True -> overProvenance (DisallowedPrv reason) hi + False -> hi + ) + + +------------------------------------------------------------------------------ +-- | The hypothesis, consisting of local terms and the ambient environment +-- (imports and class methods.) Hides disallowed values. +jHypothesis :: Judgement' a -> Hypothesis a +jHypothesis + = Hypothesis + . filter (not . isDisallowed . hi_provenance) + . unHypothesis + . jEntireHypothesis + + +------------------------------------------------------------------------------ +-- | The whole hypothesis, including things disallowed. +jEntireHypothesis :: Judgement' a -> Hypothesis a +jEntireHypothesis = _jHypothesis + + +------------------------------------------------------------------------------ +-- | Just the local hypothesis. +jLocalHypothesis :: Judgement' a -> Hypothesis a +jLocalHypothesis + = Hypothesis + . filter (isLocalHypothesis . hi_provenance) + . unHypothesis + . jHypothesis + + +------------------------------------------------------------------------------ +-- | Filter elements from the hypothesis +hyFilter :: (HyInfo a -> Bool) -> Hypothesis a -> Hypothesis a +hyFilter f = Hypothesis . filter f . unHypothesis + + +------------------------------------------------------------------------------ +-- | Given a judgment, return the hypotheses that are acceptable to destruct. +-- +-- We use the ordering of the hypothesis for this purpose. Since new bindings +-- are always inserted at the beginning, we can impose a canonical ordering on +-- which order to try destructs by what order they are introduced --- stopping +-- at the first one we've already destructed. +jAcceptableDestructTargets :: Judgement' CType -> [HyInfo CType] +jAcceptableDestructTargets + = filter (isJust . algebraicTyCon . unCType . hi_type) + . takeWhile (not . isAlreadyDestructed . hi_provenance) + . unHypothesis + . jEntireHypothesis + + +------------------------------------------------------------------------------ +-- | If we're in a top hole, the name of the defining function. +isTopHole :: Context -> Judgement' a -> Maybe OccName +isTopHole ctx = + bool Nothing (Just $ extremelyStupid__definingFunction ctx) . _jIsTopHole + + +unsetIsTopHole :: Judgement' a -> Judgement' a +unsetIsTopHole = field @"_jIsTopHole" .~ False + + +------------------------------------------------------------------------------ +-- | What names are currently in scope in the hypothesis? +hyNamesInScope :: Hypothesis a -> Set OccName +hyNamesInScope = M.keysSet . hyByName + + +------------------------------------------------------------------------------ +-- | Are there any top-level function argument bindings in this judgement? +jHasBoundArgs :: Judgement' a -> Bool +jHasBoundArgs + = any (isTopLevel . hi_provenance) + . unHypothesis + . jLocalHypothesis + + +jNeedsToBindArgs :: Judgement' CType -> Bool +jNeedsToBindArgs = isFunTy . unCType . jGoal + + +------------------------------------------------------------------------------ +-- | Fold a hypothesis into a single mapping from name to info. This +-- unavoidably will cause duplicate names (things like methods) to shadow one +-- another. +hyByName :: Hypothesis a -> Map OccName (HyInfo a) +hyByName + = M.fromList + . fmap (hi_name &&& id) + . unHypothesis + + +------------------------------------------------------------------------------ +-- | Only the hypothesis members which are pattern vals +jPatHypothesis :: Judgement' a -> Map OccName PatVal +jPatHypothesis + = M.mapMaybe (getPatVal . hi_provenance) + . hyByName + . jHypothesis + + +getPatVal :: Provenance-> Maybe PatVal +getPatVal prov = + case prov of + PatternMatchPrv pv -> Just pv + _ -> Nothing + + +jGoal :: Judgement' a -> a +jGoal = _jGoal + + +substJdg :: TCvSubst -> Judgement -> Judgement +substJdg subst = fmap $ coerce . substTy subst . coerce + + +mkFirstJudgement + :: Context + -> Hypothesis CType + -> Bool -- ^ are we in the top level rhs hole? + -> Type + -> Judgement' CType +mkFirstJudgement ctx hy top goal = + normalizeJudgement ctx $ + Judgement + { _jHypothesis = hy + , _jBlacklistDestruct = False + , _jWhitelistSplit = True + , _jIsTopHole = top + , _jGoal = CType goal + , j_coercion = emptyTCvSubst + } + + +------------------------------------------------------------------------------ +-- | Is this a top level function binding? +isTopLevel :: Provenance -> Bool +isTopLevel TopLevelArgPrv{} = True +isTopLevel _ = False + + +------------------------------------------------------------------------------ +-- | Is this a local function argument, pattern match or user val? +isLocalHypothesis :: Provenance -> Bool +isLocalHypothesis UserPrv{} = True +isLocalHypothesis PatternMatchPrv{} = True +isLocalHypothesis TopLevelArgPrv{} = True +isLocalHypothesis _ = False + + +------------------------------------------------------------------------------ +-- | Is this a pattern match? +isPatternMatch :: Provenance -> Bool +isPatternMatch PatternMatchPrv{} = True +isPatternMatch _ = False + + +------------------------------------------------------------------------------ +-- | Was this term ever disallowed? +isDisallowed :: Provenance -> Bool +isDisallowed DisallowedPrv{} = True +isDisallowed _ = False + +------------------------------------------------------------------------------ +-- | Has this term already been disallowed? +isAlreadyDestructed :: Provenance -> Bool +isAlreadyDestructed (DisallowedPrv AlreadyDestructed _) = True +isAlreadyDestructed _ = False + + +------------------------------------------------------------------------------ +-- | Eliminates 'DisallowedPrv' provenances. +expandDisallowed :: Provenance -> Provenance +expandDisallowed (DisallowedPrv _ prv) = expandDisallowed prv +expandDisallowed prv = prv diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Judgements/SYB.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Judgements/SYB.hs new file mode 100644 index 0000000000..8cd6130eb3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Judgements/SYB.hs @@ -0,0 +1,98 @@ +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE RankNTypes #-} + +-- | Custom SYB traversals +module Wingman.Judgements.SYB where + +import Data.Foldable (foldl') +import Data.Generics hiding (typeRep) +import qualified Data.Text as T +import Development.IDE.GHC.Compat +import Development.IDE.GHC.Compat.Util (unpackFS) +import GHC.Exts (Any) +import Type.Reflection +import Unsafe.Coerce (unsafeCoerce) +import Wingman.StaticPlugin (pattern WingmanMetaprogram) + + +------------------------------------------------------------------------------ +-- | Like 'everything', but only looks inside 'Located' terms that contain the +-- given 'SrcSpan'. +everythingContaining + :: forall r + . Monoid r + => SrcSpan + -> GenericQ r + -> GenericQ r +everythingContaining dst f = go + where + go :: GenericQ r + go x = + case genericIsSubspan dst x of + Just False -> mempty + _ -> foldl' (<>) (f x) (gmapQ go x) + + +------------------------------------------------------------------------------ +-- | Helper function for implementing 'everythingWithin' +-- +-- NOTE(sandy): Subtly broken. In an ideal world, this function should return +-- @Just False@ for nodes of /any type/ which do not contain the span. But if +-- this functionality exists anywhere within the SYB machinery, I have yet to +-- find it. +genericIsSubspan + :: SrcSpan + -> GenericQ (Maybe Bool) +genericIsSubspan dst = mkQ1 (L noSrcSpan ()) Nothing $ \case + L span _ -> Just $ dst `isSubspanOf` span + + +------------------------------------------------------------------------------ +-- | Like 'mkQ', but allows for polymorphic instantiation of its specific case. +-- This instantiation matches whenever the dynamic value has the same +-- constructor as the proxy @f ()@ value. +mkQ1 :: forall a r f + . (Data a, Data (f ())) + => f () -- ^ Polymorphic constructor to match on + -> r -- ^ Default value + -> (forall b. f b -> r) -- ^ Polymorphic match + -> a + -> r +mkQ1 proxy r br a = + case l_con == a_con && sameTypeModuloLastApp @a @(f ()) of + -- We have proven that the two values share the same constructor, and + -- that they have the same type if you ignore the final application. + -- Therefore, it is safe to coerce @a@ to @f b@, since @br@ is universal + -- over @b@ and can't inspect it. + True -> br $ unsafeCoerce @_ @(f Any) a + False -> r + where + l_con = toConstr proxy + a_con = toConstr a + + +------------------------------------------------------------------------------ +-- | Given @a ~ f1 a1@ and @b ~ f2 b2@, returns true if @f1 ~ f2@. +sameTypeModuloLastApp :: forall a b. (Typeable a, Typeable b) => Bool +sameTypeModuloLastApp = + let tyrep1 = typeRep @a + tyrep2 = typeRep @b + in case (tyrep1 , tyrep2) of + (App a _, App b _) -> + case eqTypeRep a b of + Just HRefl -> True + Nothing -> False + _ -> False + + +metaprogramAtQ :: SrcSpan -> GenericQ [(SrcSpan, T.Text)] +metaprogramAtQ ss = everythingContaining ss $ mkQ mempty $ \case + L new_span (WingmanMetaprogram program) -> pure (new_span, T.pack $ unpackFS program) + (_ :: LHsExpr GhcTc) -> mempty + + +metaprogramQ :: GenericQ [(SrcSpan, T.Text)] +metaprogramQ = everything (<>) $ mkQ mempty $ \case + L new_span (WingmanMetaprogram program) -> pure (new_span, T.pack $ unpackFS program) + (_ :: LHsExpr GhcTc) -> mempty + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Judgements/Theta.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Judgements/Theta.hs new file mode 100644 index 0000000000..25bf5a3a21 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Judgements/Theta.hs @@ -0,0 +1,235 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE ViewPatterns #-} + +module Wingman.Judgements.Theta + ( Evidence + , getEvidenceAtHole + , mkEvidence + , evidenceToCoercions + , evidenceToSubst + , evidenceToHypothesis + , evidenceToThetaType + , allEvidenceToSubst + ) where + +import Control.Applicative (empty) +import Control.Lens (preview) +import Data.Coerce (coerce) +import Data.Maybe (fromMaybe, mapMaybe, maybeToList) +import Data.Generics.Sum (_Ctor) +import Data.Set (Set) +import qualified Data.Set as S +import Development.IDE.Core.UseStale +import Development.IDE.GHC.Compat hiding (empty) +import Generics.SYB hiding (tyConName, empty, Generic) +import GHC.Generics +import Wingman.GHC +import Wingman.Types + +#if __GLASGOW_HASKELL__ >= 900 +import GHC.Tc.Utils.TcType +#endif + + +------------------------------------------------------------------------------ +-- | Something we've learned about the type environment. +data Evidence + -- | The two types are equal, via a @a ~ b@ relationship + = EqualityOfTypes Type Type + -- | We have an instance in scope + | HasInstance PredType + deriving (Show, Generic) + + +------------------------------------------------------------------------------ +-- | Given a 'PredType', pull an 'Evidence' out of it. +mkEvidence :: PredType -> [Evidence] +mkEvidence (getEqualityTheta -> Just (a, b)) + = pure $ EqualityOfTypes a b +mkEvidence inst@(tcTyConAppTyCon_maybe -> Just (tyConClass_maybe -> Just cls)) = do + (_, apps) <- maybeToList $ splitTyConApp_maybe inst + let tvs = classTyVars cls + subst = zipTvSubst tvs apps + sc_ev <- traverse (mkEvidence . substTy subst) $ classSCTheta cls + HasInstance inst : sc_ev +mkEvidence _ = empty + + +------------------------------------------------------------------------------ +-- | Build a set of 'PredType's from the evidence. +evidenceToThetaType :: [Evidence] -> Set CType +evidenceToThetaType evs = S.fromList $ do + HasInstance t <- evs + pure $ CType t + + +------------------------------------------------------------------------------ +-- | Compute all the 'Evidence' implicitly bound at the given 'SrcSpan'. +getEvidenceAtHole :: Tracked age SrcSpan -> Tracked age (LHsBinds GhcTc) -> [Evidence] +getEvidenceAtHole (unTrack -> dst) + = concatMap mkEvidence + . (everything (<>) $ + mkQ mempty (absBinds dst) `extQ` wrapperBinds dst `extQ` matchBinds dst) + . unTrack + + +mkSubst :: Set TyVar -> Type -> Type -> TCvSubst +mkSubst skolems a b = + let tyvars = S.fromList $ mapMaybe getTyVar_maybe [a, b] + -- If we can unify our skolems, at least one is no longer a skolem. + -- Removing them from this set ensures we can get a substitution between + -- the two. But it's okay to leave them in 'ts_skolems' in general, since + -- they won't exist after running this substitution. + skolems' = skolems S.\\ tyvars + in + case tryUnifyUnivarsButNotSkolems skolems' (CType a) (CType b) of + Just subst -> subst + Nothing -> emptyTCvSubst + + +substPair :: TCvSubst -> (Type, Type) -> (Type, Type) +substPair subst (ty, ty') = (substTy subst ty, substTy subst ty') + + +------------------------------------------------------------------------------ +-- | Construct a substitution given a list of types that are equal to one +-- another. This is more subtle than it seems, since there might be several +-- equalities for the same type. We must be careful to push the accumulating +-- substitution through each pair of types before adding their equalities. +allEvidenceToSubst :: Set TyVar -> [(Type, Type)] -> TCvSubst +allEvidenceToSubst _ [] = emptyTCvSubst +allEvidenceToSubst skolems ((a, b) : evs) = + let subst = mkSubst skolems a b + in unionTCvSubst subst + $ allEvidenceToSubst skolems + $ fmap (substPair subst) evs + +------------------------------------------------------------------------------ +-- | Given some 'Evidence', get a list of which types are now equal. +evidenceToCoercions :: [Evidence] -> [(CType, CType)] +evidenceToCoercions = coerce . mapMaybe (preview $ _Ctor @"EqualityOfTypes") + +------------------------------------------------------------------------------ +-- | Update our knowledge of which types are equal. +evidenceToSubst :: [Evidence] -> TacticState -> TacticState +evidenceToSubst evs ts = + updateSubst + (allEvidenceToSubst (ts_skolems ts) . coerce $ evidenceToCoercions evs) + ts + + +------------------------------------------------------------------------------ +-- | Get all of the methods that are in scope from this piece of 'Evidence'. +evidenceToHypothesis :: Evidence -> Hypothesis CType +evidenceToHypothesis EqualityOfTypes{} = mempty +evidenceToHypothesis (HasInstance t) = + Hypothesis . excludeForbiddenMethods . fromMaybe [] $ methodHypothesis t + + +------------------------------------------------------------------------------ +-- | Given @a ~ b@ or @a ~# b@, returns @Just (a, b)@, otherwise @Nothing@. +getEqualityTheta :: PredType -> Maybe (Type, Type) +getEqualityTheta (splitTyConApp_maybe -> Just (tc, [_k, a, b])) +#if __GLASGOW_HASKELL__ > 806 + | tc == eqTyCon +#else + | nameRdrName (tyConName tc) == eqTyCon_RDR +#endif + = Just (a, b) +getEqualityTheta (splitTyConApp_maybe -> Just (tc, [_k1, _k2, a, b])) + | tc == eqPrimTyCon = Just (a, b) +getEqualityTheta _ = Nothing + + +------------------------------------------------------------------------------ +-- | Many operations are defined in typeclasses for performance reasons, rather +-- than being a true part of the class. This function filters out those, in +-- order to keep our hypothesis space small. +excludeForbiddenMethods :: [HyInfo a] -> [HyInfo a] +excludeForbiddenMethods = filter (not . flip S.member forbiddenMethods . hi_name) + where + forbiddenMethods :: Set OccName + forbiddenMethods = S.map mkVarOcc $ S.fromList + [ -- monadfail + "fail" + -- show + , "showsPrec", "showList" + -- functor + , "<$" + -- applicative + , "liftA2", "<*", "*>" + -- monad + , "return", ">>" + -- alternative + , "some", "many" + -- foldable + , "foldr1", "foldl1", "elem", "maximum", "minimum", "sum", "product" + -- traversable + , "sequenceA", "mapM", "sequence" + -- semigroup + , "sconcat", "stimes" + -- monoid + , "mconcat" + ] + + +------------------------------------------------------------------------------ +-- | Extract evidence from 'AbsBinds' in scope. +absBinds :: SrcSpan -> LHsBindLR GhcTc GhcTc -> [PredType] +#if __GLASGOW_HASKELL__ >= 900 +absBinds dst (L src (FunBind w _ _ _)) + | dst `isSubspanOf` src + = wrapper w +absBinds dst (L src (AbsBinds _ _ h _ _ z _)) +#else +absBinds dst (L src (AbsBinds _ _ h _ _ _ _)) +#endif + | dst `isSubspanOf` src + = fmap idType h +#if __GLASGOW_HASKELL__ >= 900 + <> foldMap (absBinds dst) z +#endif +absBinds _ _ = [] + + +------------------------------------------------------------------------------ +-- | Extract evidence from 'HsWrapper's in scope +wrapperBinds :: SrcSpan -> LHsExpr GhcTc -> [PredType] +#if __GLASGOW_HASKELL__ >= 900 +wrapperBinds dst (L src (XExpr (WrapExpr (HsWrap h _)))) +#else +wrapperBinds dst (L src (HsWrap _ h _)) +#endif + | dst `isSubspanOf` src + = wrapper h +wrapperBinds _ _ = [] + + +------------------------------------------------------------------------------ +-- | Extract evidence from the 'ConPatOut's bound in this 'Match'. +matchBinds :: SrcSpan -> LMatch GhcTc (LHsExpr GhcTc) -> [PredType] +matchBinds dst (L src (Match _ _ pats _)) + | dst `isSubspanOf` src + = everything (<>) (mkQ mempty patBinds) pats +matchBinds _ _ = [] + + +------------------------------------------------------------------------------ +-- | Extract evidence from a 'ConPatOut'. +patBinds :: Pat GhcTc -> [PredType] +#if __GLASGOW_HASKELL__ >= 900 +patBinds (ConPat{ pat_con_ext = ConPatTc { cpt_dicts = dicts }}) +#else +patBinds (ConPatOut { pat_dicts = dicts }) +#endif + = fmap idType dicts +patBinds _ = [] + + +------------------------------------------------------------------------------ +-- | Extract the types of the evidence bindings in scope. +wrapper :: HsWrapper -> [PredType] +wrapper (WpCompose h h2) = wrapper h <> wrapper h2 +wrapper (WpEvLam v) = [idType v] +wrapper _ = [] + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/KnownStrategies.hs b/plugins/hls-tactics-plugin/new/src/Wingman/KnownStrategies.hs new file mode 100644 index 0000000000..e898358c49 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/KnownStrategies.hs @@ -0,0 +1,82 @@ +module Wingman.KnownStrategies where + +import Data.Foldable (for_) +import Development.IDE.GHC.Compat.Core +import Refinery.Tactic +import Wingman.Judgements (jGoal) +import Wingman.KnownStrategies.QuickCheck (deriveArbitrary) +import Wingman.Machinery (tracing, getKnownInstance, getCurrentDefinitions) +import Wingman.Tactics +import Wingman.Types + + +knownStrategies :: TacticsM () +knownStrategies = choice + [ known "fmap" deriveFmap + , known "mempty" deriveMempty + , known "arbitrary" deriveArbitrary + , known "<>" deriveMappend + , known "mappend" deriveMappend + ] + + +known :: String -> TacticsM () -> TacticsM () +known name t = do + getCurrentDefinitions >>= \case + [(def, _)] | def == mkVarOcc name -> + tracing ("known " <> name) t + _ -> failure NoApplicableTactic + + +deriveFmap :: TacticsM () +deriveFmap = do + try intros + overAlgebraicTerms homo + choice + [ overFunctions (apply Saturated) >> auto' 2 + , assumption + , recursion + ] + + +------------------------------------------------------------------------------ +-- | We derive mappend by binding the arguments, introducing the constructor, +-- and then calling mappend recursively. At each recursive call, we filter away +-- any binding that isn't in an analogous position. +-- +-- The recursive call first attempts to use an instance in scope. If that fails, +-- it falls back to trying a theta method from the hypothesis with the correct +-- name. +deriveMappend :: TacticsM () +deriveMappend = do + try intros + destructAll + split + g <- goal + minst <- getKnownInstance (mkClsOcc "Semigroup") + . pure + . unCType + $ jGoal g + for_ minst $ \(cls, df) -> do + restrictPositionForApplication + (applyMethod cls df $ mkVarOcc "<>") + assumption + try $ + restrictPositionForApplication + (applyByName $ mkVarOcc "<>") + assumption + + +------------------------------------------------------------------------------ +-- | We derive mempty by introducing the constructor, and then trying to +-- 'mempty' everywhere. This smaller 'mempty' might come from an instance in +-- scope, or it might come from the hypothesis theta. +deriveMempty :: TacticsM () +deriveMempty = do + split + g <- goal + minst <- getKnownInstance (mkClsOcc "Monoid") [unCType $ jGoal g] + for_ minst $ \(cls, df) -> do + applyMethod cls df $ mkVarOcc "mempty" + try assumption + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/KnownStrategies/QuickCheck.hs b/plugins/hls-tactics-plugin/new/src/Wingman/KnownStrategies/QuickCheck.hs new file mode 100644 index 0000000000..b14e4b8348 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/KnownStrategies/QuickCheck.hs @@ -0,0 +1,109 @@ +module Wingman.KnownStrategies.QuickCheck where + +import Data.Bool (bool) +import Data.Generics (everything, mkQ) +import Data.List (partition) +import Development.IDE.GHC.Compat +import GHC.Exts (IsString (fromString)) +import GHC.List (foldl') +import GHC.SourceGen (int) +import GHC.SourceGen.Binds (match, valBind) +import GHC.SourceGen.Expr (case', lambda, let') +import GHC.SourceGen.Overloaded (App ((@@)), HasList (list)) +import GHC.SourceGen.Pat (conP) +import Refinery.Tactic (goal, rule, failure) +import Wingman.CodeGen +import Wingman.Judgements (jGoal) +import Wingman.Machinery (tracePrim) +import Wingman.Types + + +------------------------------------------------------------------------------ +-- | Known tactic for deriving @arbitrary :: Gen a@. This tactic splits the +-- type's data cons into terminal and inductive cases, and generates code that +-- produces a terminal if the QuickCheck size parameter is <=1, or any data con +-- otherwise. It correctly scales recursive parameters, ensuring termination. +deriveArbitrary :: TacticsM () +deriveArbitrary = do + ty <- jGoal <$> goal + case splitTyConApp_maybe $ unCType ty of + Just (gen_tc, [splitTyConApp_maybe -> Just (tc, apps)]) + | occNameString (occName $ tyConName gen_tc) == "Gen" -> do + rule $ \_ -> do + let dcs = tyConDataCons tc + (terminal, big) = partition ((== 0) . genRecursiveCount) + $ fmap (mkGenerator tc apps) dcs + terminal_expr = mkVal "terminal" + oneof_expr = mkVal "oneof" + pure + $ Synthesized (tracePrim "deriveArbitrary") + -- TODO(sandy): This thing is not actually empty! We produced + -- a bespoke binding "terminal", and a not-so-bespoke "n". + -- But maybe it's fine for known rules? + mempty + mempty + mempty + $ noLoc $ case terminal of + [onlyCon] -> genExpr onlyCon -- See #1879 + _ -> let' [valBind (fromString "terminal") $ list $ fmap genExpr terminal] $ + appDollar (mkFunc "sized") $ lambda [bvar' (mkVarOcc "n")] $ + case' (infixCall "<=" (mkVal "n") (int 1)) + [ match [conP (fromString "True") []] $ + oneof_expr @@ terminal_expr + , match [conP (fromString "False") []] $ + appDollar oneof_expr $ + infixCall "<>" + (list $ fmap genExpr big) + terminal_expr + ] + _ -> failure $ GoalMismatch "deriveArbitrary" ty + + +------------------------------------------------------------------------------ +-- | Helper data type for the generator of a specific data con. +data Generator = Generator + { genRecursiveCount :: Integer + , genExpr :: HsExpr GhcPs + } + + +------------------------------------------------------------------------------ +-- | Make a 'Generator' for a given tycon instantiated with the given @[Type]@. +mkGenerator :: TyCon -> [Type] -> DataCon -> Generator +mkGenerator tc apps dc = do + let dc_expr = var' $ occName $ dataConName dc + args = conLikeInstOrigArgTys' (RealDataCon dc) apps + num_recursive_calls = sum $ fmap (bool 0 1 . doesTypeContain tc) args + mkArbitrary = mkArbitraryCall tc num_recursive_calls + Generator num_recursive_calls $ case args of + [] -> mkFunc "pure" @@ dc_expr + (a : as) -> + foldl' + (infixCall "<*>") + (infixCall "<$>" dc_expr $ mkArbitrary a) + (fmap mkArbitrary as) + + +------------------------------------------------------------------------------ +-- | Check if the given 'TyCon' exists anywhere in the 'Type'. +doesTypeContain :: TyCon -> Type -> Bool +doesTypeContain recursive_tc = + everything (||) $ mkQ False (== recursive_tc) + + +------------------------------------------------------------------------------ +-- | Generate the correct sort of call to @arbitrary@. For recursive calls, we +-- need to scale down the size parameter, either by a constant factor of 1 if +-- it's the only recursive parameter, or by @`div` n@ where n is the number of +-- recursive parameters. For all other types, just call @arbitrary@ directly. +mkArbitraryCall :: TyCon -> Integer -> Type -> HsExpr GhcPs +mkArbitraryCall recursive_tc n ty = + let arbitrary = mkFunc "arbitrary" + in case doesTypeContain recursive_tc ty of + True -> + mkFunc "scale" + @@ bool (mkFunc "flip" @@ mkFunc "div" @@ int n) + (mkFunc "subtract" @@ int 1) + (n == 1) + @@ arbitrary + False -> arbitrary diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/LanguageServer.hs b/plugins/hls-tactics-plugin/new/src/Wingman/LanguageServer.hs new file mode 100644 index 0000000000..c0bba854ff --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/LanguageServer.hs @@ -0,0 +1,662 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE TypeFamilies #-} + +{-# LANGUAGE NoMonoLocalBinds #-} + +module Wingman.LanguageServer where + +import Control.Arrow ((***)) +import Control.Monad +import Control.Monad.IO.Class +import Control.Monad.RWS +import Control.Monad.State (State, evalState) +import Control.Monad.Trans.Maybe +import Data.Bifunctor (first) +import Data.Coerce +import Data.Functor ((<&>)) +import Data.Functor.Identity (runIdentity) +import qualified Data.HashMap.Strict as Map +import Data.IORef (readIORef) +import qualified Data.Map as M +import Data.Maybe +import Data.Set (Set) +import qualified Data.Set as S +import qualified Data.Text as T +import Data.Traversable +import Development.IDE (hscEnv, getFilesOfInterestUntracked, ShowDiagnostic (ShowDiag), srcSpanToRange, defineNoDiagnostics, IdeAction) +import Development.IDE.Core.PositionMapping (idDelta) +import Development.IDE.Core.RuleTypes +import Development.IDE.Core.Rules (usePropertyAction) +import Development.IDE.Core.Service (runAction) +import Development.IDE.Core.Shake (IdeState (..), uses, define, use, addPersistentRule) +import qualified Development.IDE.Core.Shake as IDE +import Development.IDE.Core.UseStale +import Development.IDE.GHC.Compat hiding (empty) +import Development.IDE.GHC.Compat.ExactPrint +import qualified Development.IDE.GHC.Compat.Util as FastString +import Development.IDE.GHC.Error (realSrcSpanToRange) +import Development.IDE.GHC.ExactPrint hiding (LogShake, Log) +import Development.IDE.Graph (Action, RuleResult, Rules, action) +import Development.IDE.Graph.Classes (Hashable, NFData) +import Development.IDE.Spans.LocalBindings (Bindings, getDefiningBindings) +import GHC.Generics (Generic) +import Generics.SYB hiding (Generic) +import qualified Ide.Plugin.Config as Plugin +import Ide.Plugin.Properties +import Ide.PluginUtils (usePropertyLsp) +import Ide.Types (PluginId) +import Language.Haskell.GHC.ExactPrint (Transform, modifyAnnsT, addAnnotationsForPretty) +import Language.LSP.Server (MonadLsp, sendNotification) +import Language.LSP.Types hiding + (SemanticTokenAbsolute (length, line), + SemanticTokenRelative (length), + SemanticTokensEdit (_start)) +import Language.LSP.Types.Capabilities +import Prelude hiding (span) +import Retrie (transformA) +import Wingman.Context +import Wingman.GHC +import Wingman.Judgements +import Wingman.Judgements.SYB (everythingContaining, metaprogramQ, metaprogramAtQ) +import Wingman.Judgements.Theta +import Wingman.Range +import Wingman.StaticPlugin (pattern WingmanMetaprogram, pattern MetaprogramSyntax) +import Wingman.Types +import Development.IDE.Types.Logger (Recorder, cmapWithPrio, WithPriority, Pretty (pretty)) +import qualified Development.IDE.Core.Shake as Shake + + +newtype Log + = LogShake Shake.Log + deriving Show + +instance Pretty Log where + pretty = \case + LogShake shakeLog -> pretty shakeLog + +tacticDesc :: T.Text -> T.Text +tacticDesc name = "fill the hole using the " <> name <> " tactic" + + +------------------------------------------------------------------------------ +-- | The name of the command for the LS. +tcCommandName :: TacticCommand -> T.Text +tcCommandName = T.pack . show + + +runIde :: String -> String -> IdeState -> Action a -> IO a +runIde herald action state = runAction ("Wingman." <> herald <> "." <> action) state + +runIdeAction :: String -> String -> IdeState -> IdeAction a -> IO a +runIdeAction herald action state = IDE.runIdeAction ("Wingman." <> herald <> "." <> action) (shakeExtras state) + + +runCurrentIde + :: forall a r + . ( r ~ RuleResult a + , Eq a , Hashable a , Show a , Typeable a , NFData a + , Show r, Typeable r, NFData r + ) + => String + -> IdeState + -> NormalizedFilePath + -> a + -> MaybeT IO (Tracked 'Current r) +runCurrentIde herald state nfp a = + MaybeT $ fmap (fmap unsafeMkCurrent) $ runIde herald (show a) state $ use a nfp + + +runStaleIde + :: forall a r + . ( r ~ RuleResult a + , Eq a , Hashable a , Show a , Typeable a , NFData a + , Show r, Typeable r, NFData r + ) + => String + -> IdeState + -> NormalizedFilePath + -> a + -> MaybeT IO (TrackedStale r) +runStaleIde herald state nfp a = + MaybeT $ runIde herald (show a) state $ useWithStale a nfp + + +unsafeRunStaleIde + :: forall a r + . ( r ~ RuleResult a + , Eq a , Hashable a , Show a , Typeable a , NFData a + , Show r, Typeable r, NFData r + ) + => String + -> IdeState + -> NormalizedFilePath + -> a + -> MaybeT IO r +unsafeRunStaleIde herald state nfp a = do + (r, _) <- MaybeT $ runIde herald (show a) state $ IDE.useWithStale a nfp + pure r + +unsafeRunStaleIdeFast + :: forall a r + . ( r ~ RuleResult a + , Eq a , Hashable a , Show a , Typeable a , NFData a + , Show r, Typeable r, NFData r + ) + => String + -> IdeState + -> NormalizedFilePath + -> a + -> MaybeT IO r +unsafeRunStaleIdeFast herald state nfp a = do + (r, _) <- MaybeT $ runIdeAction herald (show a) state $ IDE.useWithStaleFast a nfp + pure r + + +------------------------------------------------------------------------------ + +properties :: Properties + '[ 'PropertyKey "hole_severity" ('TEnum (Maybe DiagnosticSeverity)) + , 'PropertyKey "max_use_ctor_actions" 'TInteger + , 'PropertyKey "timeout_duration" 'TInteger + , 'PropertyKey "auto_gas" 'TInteger + , 'PropertyKey "proofstate_styling" 'TBoolean + ] +properties = emptyProperties + & defineBooleanProperty #proofstate_styling + "Should Wingman emit styling markup when showing metaprogram proof states?" True + & defineIntegerProperty #auto_gas + "The depth of the search tree when performing \"Attempt to fill hole\". Bigger values will be able to derive more solutions, but will take exponentially more time." 4 + & defineIntegerProperty #timeout_duration + "The timeout for Wingman actions, in seconds" 2 + & defineIntegerProperty #max_use_ctor_actions + "Maximum number of `Use constructor ` code actions that can appear" 5 + & defineEnumProperty #hole_severity + "The severity to use when showing hole diagnostics. These are noisy, but some editors don't allow jumping to all severities." + [ (Just DsError, "error") + , (Just DsWarning, "warning") + , (Just DsInfo, "info") + , (Just DsHint, "hint") + , (Nothing, "none") + ] + Nothing + + +-- | Get the the plugin config +getTacticConfig :: MonadLsp Plugin.Config m => PluginId -> m Config +getTacticConfig pId = + Config + <$> usePropertyLsp #max_use_ctor_actions pId properties + <*> usePropertyLsp #timeout_duration pId properties + <*> usePropertyLsp #auto_gas pId properties + <*> usePropertyLsp #proofstate_styling pId properties + + +getIdeDynflags + :: IdeState + -> NormalizedFilePath + -> MaybeT IO DynFlags +getIdeDynflags state nfp = do + -- Ok to use the stale 'ModIface', since all we need is its 'DynFlags' + -- which don't change very often. + msr <- unsafeRunStaleIde "getIdeDynflags" state nfp GetModSummaryWithoutTimestamps + pure $ ms_hspp_opts $ msrModSummary msr + +getAllMetaprograms :: Data a => a -> [String] +getAllMetaprograms = everything (<>) $ mkQ mempty $ \case + WingmanMetaprogram fs -> [ FastString.unpackFS fs ] + (_ :: HsExpr GhcTc) -> mempty + + +------------------------------------------------------------------------------ +-- | Find the last typechecked module, and find the most specific span, as well +-- as the judgement at the given range. +judgementForHole + :: IdeState + -> NormalizedFilePath + -> Tracked 'Current Range + -> Config + -> MaybeT IO HoleJudgment +judgementForHole state nfp range cfg = do + let stale a = runStaleIde "judgementForHole" state nfp a + + TrackedStale asts amapping <- stale GetHieAst + case unTrack asts of + HAR _ _ _ _ (HieFromDisk _) -> fail "Need a fresh hie file" + HAR _ (unsafeCopyAge asts -> hf) _ _ HieFresh -> do + range' <- liftMaybe $ mapAgeFrom amapping range + binds <- stale GetBindings + tcg@(TrackedStale tcg_t tcg_map) + <- fmap (fmap tmrTypechecked) + $ stale TypeCheck + + hscenv <- stale GhcSessionDeps + + (rss, g) <- liftMaybe $ getSpanAndTypeAtHole range' hf + + new_rss <- liftMaybe $ mapAgeTo amapping rss + tcg_rss <- liftMaybe $ mapAgeFrom tcg_map new_rss + + -- KnownThings is just the instances in scope. There are no ranges + -- involved, so it's not crucial to track ages. + let henv = untrackedStaleValue hscenv + eps <- liftIO $ readIORef $ hsc_EPS $ hscEnv henv + + (jdg, ctx) <- liftMaybe $ mkJudgementAndContext cfg g binds new_rss tcg (hscEnv henv) eps + let mp = getMetaprogramAtSpan (fmap (`RealSrcSpan` Nothing) tcg_rss) tcg_t + + dflags <- getIdeDynflags state nfp + pure $ HoleJudgment + { hj_range = fmap realSrcSpanToRange new_rss + , hj_jdg = jdg + , hj_ctx = ctx + , hj_dflags = dflags + , hj_hole_sort = holeSortFor mp + } + + +holeSortFor :: Maybe T.Text -> HoleSort +holeSortFor = maybe Hole Metaprogram + + +mkJudgementAndContext + :: Config + -> Type + -> TrackedStale Bindings + -> Tracked 'Current RealSrcSpan + -> TrackedStale TcGblEnv + -> HscEnv + -> ExternalPackageState + -> Maybe (Judgement, Context) +mkJudgementAndContext cfg g (TrackedStale binds bmap) rss (TrackedStale tcg tcgmap) hscenv eps = do + binds_rss <- mapAgeFrom bmap rss + tcg_rss <- mapAgeFrom tcgmap rss + + let tcs = fmap tcg_binds tcg + ctx = mkContext cfg + (mapMaybe (sequenceA . (occName *** coerce)) + $ unTrack + $ getDefiningBindings <$> binds <*> binds_rss) + (unTrack tcg) + hscenv + eps + evidence + top_provs = getRhsPosVals tcg_rss tcs + already_destructed = getAlreadyDestructed (fmap (`RealSrcSpan` Nothing) tcg_rss) tcs + local_hy = spliceProvenance top_provs + $ hypothesisFromBindings binds_rss binds + evidence = getEvidenceAtHole (fmap (`RealSrcSpan` Nothing) tcg_rss) tcs + cls_hy = foldMap evidenceToHypothesis evidence + subst = ts_unifier $ evidenceToSubst evidence defaultTacticState + pure + ( disallowing AlreadyDestructed already_destructed + $ fmap (CType . substTyAddInScope subst . unCType) $ + mkFirstJudgement + ctx + (local_hy <> cls_hy) + (isRhsHoleWithoutWhere tcg_rss tcs) + g + , ctx + ) + + +------------------------------------------------------------------------------ +-- | Determine which bindings have already been destructed by the location of +-- the hole. +getAlreadyDestructed + :: Tracked age SrcSpan + -> Tracked age (LHsBinds GhcTc) + -> Set OccName +getAlreadyDestructed (unTrack -> span) (unTrack -> binds) = + everythingContaining span + (mkQ mempty $ \case + Case (HsVar _ (L _ (occName -> var))) _ -> + S.singleton var + (_ :: HsExpr GhcTc) -> mempty + ) binds + + +getSpanAndTypeAtHole + :: Tracked age Range + -> Tracked age (HieASTs Type) + -> Maybe (Tracked age RealSrcSpan, Type) +getSpanAndTypeAtHole r@(unTrack -> range) (unTrack -> hf) = do + join $ listToMaybe $ M.elems $ flip M.mapWithKey (getAsts hf) $ \fs ast -> + case selectSmallestContaining (rangeToRealSrcSpan (FastString.unpackFS fs) range) ast of + Nothing -> Nothing + Just ast' -> do + let info = nodeInfo ast' + ty <- listToMaybe $ nodeType info + guard $ ("HsUnboundVar","HsExpr") `S.member` nodeAnnotations info + -- Ensure we're actually looking at a hole here + occ <- (either (const Nothing) (Just . occName) =<<) + . listToMaybe + . S.toList + . M.keysSet + $ nodeIdentifiers info + guard $ isHole occ + pure (unsafeCopyAge r $ nodeSpan ast', ty) + + + +------------------------------------------------------------------------------ +-- | Combine two (possibly-overlapping) hypotheses; using the provenance from +-- the first hypothesis if the bindings overlap. +spliceProvenance + :: Hypothesis a -- ^ Bindings to keep + -> Hypothesis a -- ^ Bindings to keep if they don't overlap with the first set + -> Hypothesis a +spliceProvenance top x = + let bound = S.fromList $ fmap hi_name $ unHypothesis top + in mappend top $ Hypothesis . filter (flip S.notMember bound . hi_name) $ unHypothesis x + + +------------------------------------------------------------------------------ +-- | Compute top-level position vals of a function +getRhsPosVals + :: Tracked age RealSrcSpan + -> Tracked age TypecheckedSource + -> Hypothesis CType +getRhsPosVals (unTrack -> rss) (unTrack -> tcs) + = everything (<>) (mkQ mempty $ \case + TopLevelRHS name ps + (L (RealSrcSpan span _) -- body with no guards and a single defn + (HsVar _ (L _ hole))) + _ + | containsSpan rss span -- which contains our span + , isHole $ occName hole -- and the span is a hole + -> flip evalState 0 $ buildTopLevelHypothesis name ps + _ -> mempty + ) tcs + + +------------------------------------------------------------------------------ +-- | Construct a hypothesis given the patterns from the left side of a HsMatch. +-- These correspond to things that the user put in scope before running +-- tactics. +buildTopLevelHypothesis + :: OccName -- ^ Function name + -> [PatCompat GhcTc] + -> State Int (Hypothesis CType) +buildTopLevelHypothesis name ps = do + fmap mconcat $ + for (zip [0..] ps) $ \(ix, p) -> + buildPatHy (TopLevelArgPrv name ix $ length ps) p + + +------------------------------------------------------------------------------ +-- | Construct a hypothesis for a single pattern, including building +-- sub-hypotheses for constructor pattern matches. +buildPatHy :: Provenance -> PatCompat GhcTc -> State Int (Hypothesis CType) +buildPatHy prov (fromPatCompat -> p0) = + case p0 of + VarPat _ x -> pure $ mkIdHypothesis (unLoc x) prov + LazyPat _ p -> buildPatHy prov p + AsPat _ x p -> do + hy' <- buildPatHy prov p + pure $ mkIdHypothesis (unLoc x) prov <> hy' + ParPat _ p -> buildPatHy prov p + BangPat _ p -> buildPatHy prov p + ViewPat _ _ p -> buildPatHy prov p + -- Desugar lists into cons + ListPat _ [] -> pure mempty + ListPat x@(ListPatTc ty _) (p : ps) -> + mkDerivedConHypothesis prov (RealDataCon consDataCon) [ty] + [ (0, p) + , (1, toPatCompat $ ListPat x ps) + ] + -- Desugar tuples into an explicit constructor + TuplePat tys pats boxity -> + mkDerivedConHypothesis + prov + (RealDataCon $ tupleDataCon boxity $ length pats) + tys + $ zip [0.. ] pats +#if __GLASGOW_HASKELL__ >= 900 + ConPat {pat_con = (L _ con), pat_con_ext = ConPatTc {cpt_arg_tys = args}, pat_args = f} -> +#else + ConPatOut {pat_con = (L _ con), pat_arg_tys = args, pat_args = f} -> +#endif + case f of + PrefixCon l_pgt -> + mkDerivedConHypothesis prov con args $ zip [0..] l_pgt + InfixCon pgt pgt5 -> + mkDerivedConHypothesis prov con args $ zip [0..] [pgt, pgt5] + RecCon r -> + mkDerivedRecordHypothesis prov con args r + SigPat _ p _ -> buildPatHy prov p + _ -> pure mempty + + +------------------------------------------------------------------------------ +-- | Like 'mkDerivedConHypothesis', but for record patterns. +mkDerivedRecordHypothesis + :: Provenance + -> ConLike -- ^ Destructing constructor + -> [Type] -- ^ Applied type variables + -> HsRecFields GhcTc (PatCompat GhcTc) + -> State Int (Hypothesis CType) +mkDerivedRecordHypothesis prov dc args (HsRecFields (fmap unLoc -> fs) _) + | Just rec_fields <- getRecordFields dc + = do + let field_lookup = M.fromList $ zip (fmap (occNameFS . fst) rec_fields) [0..] + mkDerivedConHypothesis prov dc args $ fs <&> \(HsRecField (L _ rec_occ) p _) -> + ( field_lookup M.! (occNameFS $ occName $ unLoc $ rdrNameFieldOcc rec_occ) + , p + ) +mkDerivedRecordHypothesis _ _ _ _ = + error "impossible! using record pattern on something that isn't a record" + + +------------------------------------------------------------------------------ +-- | Construct a fake variable name. Used to track the provenance of top-level +-- pattern matches which otherwise wouldn't have anything to attach their +-- 'TopLevelArgPrv' to. +mkFakeVar :: State Int OccName +mkFakeVar = do + i <- get + put $ i + 1 + pure $ mkVarOcc $ "_" <> show i + + +------------------------------------------------------------------------------ +-- | Construct a fake variable to attach the current 'Provenance' to, and then +-- build a sub-hypothesis for the pattern match. +mkDerivedConHypothesis + :: Provenance + -> ConLike -- ^ Destructing constructor + -> [Type] -- ^ Applied type variables + -> [(Int, PatCompat GhcTc)] -- ^ Patterns, and their order in the data con + -> State Int (Hypothesis CType) +mkDerivedConHypothesis prov dc args ps = do + var <- mkFakeVar + hy' <- fmap mconcat $ + for ps $ \(ix, p) -> do + let prov' = PatternMatchPrv + $ PatVal (Just var) + (S.singleton var <> provAncestryOf prov) + (Uniquely dc) + ix + buildPatHy prov' p + pure + $ mappend hy' + $ Hypothesis + $ pure + $ HyInfo var (DisallowedPrv AlreadyDestructed prov) + $ CType + -- TODO(sandy): This is the completely wrong type, but we don't have a good + -- way to get the real one. It's probably OK though, since we're generating + -- this term with a disallowed provenance, and it doesn't actually exist + -- anyway. + $ conLikeResTy dc args + + +------------------------------------------------------------------------------ +-- | Build a 'Hypothesis' given an 'Id'. +mkIdHypothesis :: Id -> Provenance -> Hypothesis CType +mkIdHypothesis (splitId -> (name, ty)) prov = + Hypothesis $ pure $ HyInfo name prov ty + + +------------------------------------------------------------------------------ +-- | Is this hole immediately to the right of an equals sign --- and is there +-- no where clause attached to it? +-- +-- It's important that there is no where clause because otherwise it gets +-- clobbered. See #2183 for an example. +-- +-- This isn't a perfect check, and produces some ugly code. But it's much much +-- better than the alternative, which is to destructively modify the user's +-- AST. +isRhsHoleWithoutWhere + :: Tracked age RealSrcSpan + -> Tracked age TypecheckedSource + -> Bool +isRhsHoleWithoutWhere (unTrack -> rss) (unTrack -> tcs) = + everything (||) (mkQ False $ \case + TopLevelRHS _ _ + (L (RealSrcSpan span _) _) + (EmptyLocalBinds _) -> containsSpan rss span + _ -> False + ) tcs + + +ufmSeverity :: UserFacingMessage -> MessageType +ufmSeverity NotEnoughGas = MtInfo +ufmSeverity TacticErrors = MtError +ufmSeverity TimedOut = MtInfo +ufmSeverity NothingToDo = MtInfo +ufmSeverity (InfrastructureError _) = MtError + + +mkShowMessageParams :: UserFacingMessage -> ShowMessageParams +mkShowMessageParams ufm = ShowMessageParams (ufmSeverity ufm) $ T.pack $ show ufm + + +showLspMessage :: MonadLsp cfg m => ShowMessageParams -> m () +showLspMessage = sendNotification SWindowShowMessage + + +-- This rule only exists for generating file diagnostics +-- so the RuleResult is empty +data WriteDiagnostics = WriteDiagnostics + deriving (Eq, Show, Typeable, Generic) + +instance Hashable WriteDiagnostics +instance NFData WriteDiagnostics + +type instance RuleResult WriteDiagnostics = () + +data GetMetaprograms = GetMetaprograms + deriving (Eq, Show, Typeable, Generic) + +instance Hashable GetMetaprograms +instance NFData GetMetaprograms + +type instance RuleResult GetMetaprograms = [(Tracked 'Current RealSrcSpan, T.Text)] + +wingmanRules :: Recorder (WithPriority Log) -> PluginId -> Rules () +wingmanRules recorder plId = do + define (cmapWithPrio LogShake recorder) $ \WriteDiagnostics nfp -> + usePropertyAction #hole_severity plId properties >>= \case + Nothing -> pure (mempty, Just ()) + Just severity -> + use GetParsedModule nfp >>= \case + Nothing -> + pure ([], Nothing) + Just pm -> do + let holes :: [Range] + holes = + everything (<>) + (mkQ mempty $ \case + L span (HsVar _ (L _ name)) + | isHole (occName name) -> + maybeToList $ srcSpanToRange span +#if __GLASGOW_HASKELL__ >= 900 + L span (HsUnboundVar _ occ) +#else + L span (HsUnboundVar _ (TrueExprHole occ)) +#endif + | isHole occ -> + maybeToList $ srcSpanToRange span + (_ :: LHsExpr GhcPs) -> mempty + ) $ pm_parsed_source pm + pure + ( fmap (\r -> (nfp, ShowDiag, mkDiagnostic severity r)) holes + , Just () + ) + + defineNoDiagnostics (cmapWithPrio LogShake recorder) $ \GetMetaprograms nfp -> do + TrackedStale tcg tcg_map <- fmap tmrTypechecked <$> useWithStale_ TypeCheck nfp + let scrutinees = traverse (metaprogramQ . tcg_binds) tcg + return $ Just $ flip mapMaybe scrutinees $ \aged@(unTrack -> (ss, program)) -> do + case ss of + RealSrcSpan r _ -> do + rss' <- mapAgeTo tcg_map $ unsafeCopyAge aged r + pure (rss', program) + UnhelpfulSpan _ -> Nothing + + -- This persistent rule helps to avoid blocking HLS hover providers at startup + -- Without it, the GetMetaprograms rule blocks on typecheck and prevents other + -- hover providers from being used to produce a response + addPersistentRule GetMetaprograms $ \_ -> return $ Just ([], idDelta, Nothing) + + action $ do + files <- getFilesOfInterestUntracked + void $ uses WriteDiagnostics $ Map.keys files + + +mkDiagnostic :: DiagnosticSeverity -> Range -> Diagnostic +mkDiagnostic severity r = + Diagnostic r + (Just severity) + (Just $ InR "hole") + (Just "wingman") + "Hole" + (Just $ List [DtUnnecessary]) + Nothing + + +------------------------------------------------------------------------------ +-- | Transform a 'Graft' over the AST into a 'WorkspaceEdit'. +mkWorkspaceEdits + :: DynFlags + -> ClientCapabilities + -> Uri + -> Annotated ParsedSource + -> Graft (Either String) ParsedSource + -> Either UserFacingMessage WorkspaceEdit +mkWorkspaceEdits dflags ccs uri pm g = do + let pm' = runIdentity $ transformA pm annotateMetaprograms + let response = transform dflags ccs uri g pm' + in first (InfrastructureError . T.pack) response + + +------------------------------------------------------------------------------ +-- | Add ExactPrint annotations to every metaprogram in the source tree. +-- Usually the ExactPrint module can do this for us, but we've enabled +-- QuasiQuotes, so the round-trip print/parse journey will crash. +annotateMetaprograms :: Data a => a -> Transform a +annotateMetaprograms = everywhereM $ mkM $ \case + L ss (WingmanMetaprogram mp) -> do + let x = L ss $ MetaprogramSyntax mp + let anns = addAnnotationsForPretty [] x mempty + modifyAnnsT $ mappend anns + pure x + (x :: LHsExpr GhcPs) -> pure x + + +------------------------------------------------------------------------------ +-- | Find the source of a tactic metaprogram at the given span. +getMetaprogramAtSpan + :: Tracked age SrcSpan + -> Tracked age TcGblEnv + -> Maybe T.Text +getMetaprogramAtSpan (unTrack -> ss) + = fmap snd + . listToMaybe + . metaprogramAtQ ss + . tcg_binds + . unTrack + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/LanguageServer/Metaprogram.hs b/plugins/hls-tactics-plugin/new/src/Wingman/LanguageServer/Metaprogram.hs new file mode 100644 index 0000000000..e853831a32 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/LanguageServer/Metaprogram.hs @@ -0,0 +1,59 @@ +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE OverloadedStrings #-} + +{-# LANGUAGE NoMonoLocalBinds #-} +{-# LANGUAGE RankNTypes #-} + +module Wingman.LanguageServer.Metaprogram + ( hoverProvider + ) where + +import Control.Applicative (empty) +import Control.Monad +import Control.Monad.Trans +import Control.Monad.Trans.Maybe +import Data.List (find) +import Data.Maybe +import qualified Data.Text as T +import Development.IDE (positionToRealSrcLoc, realSrcSpanToRange) +import Development.IDE.Core.Shake (IdeState (..)) +import Development.IDE.Core.UseStale +import Development.IDE.GHC.Compat hiding (empty) +import Ide.Types +import Language.LSP.Types +import Prelude hiding (span) +import Wingman.LanguageServer +import Wingman.Metaprogramming.Parser (attempt_it) +import Wingman.Types + + +------------------------------------------------------------------------------ +-- | Provide the "empty case completion" code lens +hoverProvider :: PluginMethodHandler IdeState TextDocumentHover +hoverProvider state plId (HoverParams (TextDocumentIdentifier uri) (unsafeMkCurrent -> pos) _) + | Just nfp <- uriToNormalizedFilePath $ toNormalizedUri uri = do + let loc = fmap (realSrcLocSpan . positionToRealSrcLoc nfp) pos + stale = unsafeRunStaleIdeFast "hoverProvider" state nfp + + cfg <- getTacticConfig plId + liftIO $ fromMaybeT (Right Nothing) $ do + holes <- stale GetMetaprograms + + fmap (Right . Just) $ + case find (flip containsSpan (unTrack loc) . unTrack . fst) holes of + Just (trss, program) -> do + let tr_range = fmap realSrcSpanToRange trss + rsl = realSrcSpanStart $ unTrack trss + HoleJudgment{hj_jdg=jdg, hj_ctx=ctx} <- judgementForHole state nfp tr_range cfg + z <- liftIO $ attempt_it rsl ctx jdg $ T.unpack program + pure $ Hover + { _contents = HoverContents + $ MarkupContent MkMarkdown + $ either T.pack T.pack z + , _range = Just $ unTrack tr_range + } + Nothing -> empty +hoverProvider _ _ _ = pure $ Right Nothing + +fromMaybeT :: Functor m => a -> MaybeT m a -> m a +fromMaybeT def = fmap (fromMaybe def) . runMaybeT diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/LanguageServer/TacticProviders.hs b/plugins/hls-tactics-plugin/new/src/Wingman/LanguageServer/TacticProviders.hs new file mode 100644 index 0000000000..b5a6521b7e --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/LanguageServer/TacticProviders.hs @@ -0,0 +1,309 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE OverloadedStrings #-} +{-# OPTIONS_GHC -Wno-orphans #-} + +module Wingman.LanguageServer.TacticProviders + ( commandProvider + , commandTactic + , TacticProviderData (..) + ) where + +import Control.Monad +import Data.Bool (bool) +import Data.Coerce +import Data.Maybe +import Data.Monoid +import qualified Data.Set as S +import qualified Data.Text as T +import Development.IDE.GHC.Compat +import Ide.Types +import Language.LSP.Types hiding (SemanticTokenAbsolute (..), SemanticTokenRelative (..)) +import Prelude hiding (span) +import Wingman.AbstractLSP.Types +import Wingman.Auto +import Wingman.GHC +import Wingman.Judgements +import Wingman.Machinery (useNameFromHypothesis, uncoveredDataCons) +import Wingman.Metaprogramming.Parser (parseMetaprogram) +import Wingman.Tactics +import Wingman.Types + + +------------------------------------------------------------------------------ +-- | A mapping from tactic commands to actual tactics for refinery. +commandTactic :: TacticCommand -> T.Text -> TacticsM () +commandTactic Auto = const auto +commandTactic Intros = const intros +commandTactic IntroAndDestruct = const introAndDestruct +commandTactic Destruct = useNameFromHypothesis destruct . mkVarOcc . T.unpack +commandTactic DestructPun = useNameFromHypothesis destructPun . mkVarOcc . T.unpack +commandTactic Homomorphism = useNameFromHypothesis homo . mkVarOcc . T.unpack +commandTactic DestructLambdaCase = const destructLambdaCase +commandTactic HomomorphismLambdaCase = const homoLambdaCase +commandTactic DestructAll = const destructAll +commandTactic UseDataCon = userSplit . mkVarOcc . T.unpack +commandTactic Refine = const refine +commandTactic BeginMetaprogram = const metaprogram +commandTactic RunMetaprogram = parseMetaprogram + + +------------------------------------------------------------------------------ +-- | The LSP kind +tacticKind :: TacticCommand -> T.Text +tacticKind Auto = "fillHole" +tacticKind Intros = "introduceLambda" +tacticKind IntroAndDestruct = "introduceAndDestruct" +tacticKind Destruct = "caseSplit" +tacticKind DestructPun = "caseSplitPun" +tacticKind Homomorphism = "homomorphicCaseSplit" +tacticKind DestructLambdaCase = "lambdaCase" +tacticKind HomomorphismLambdaCase = "homomorphicLambdaCase" +tacticKind DestructAll = "splitFuncArgs" +tacticKind UseDataCon = "useConstructor" +tacticKind Refine = "refine" +tacticKind BeginMetaprogram = "beginMetaprogram" +tacticKind RunMetaprogram = "runMetaprogram" + + +------------------------------------------------------------------------------ +-- | Whether or not this code action is preferred -- ostensibly refers to +-- whether or not we can bind it to a key in vs code? +tacticPreferred :: TacticCommand -> Bool +tacticPreferred Auto = True +tacticPreferred Intros = True +tacticPreferred IntroAndDestruct = True +tacticPreferred Destruct = True +tacticPreferred DestructPun = False +tacticPreferred Homomorphism = True +tacticPreferred DestructLambdaCase = False +tacticPreferred HomomorphismLambdaCase = False +tacticPreferred DestructAll = True +tacticPreferred UseDataCon = True +tacticPreferred Refine = True +tacticPreferred BeginMetaprogram = False +tacticPreferred RunMetaprogram = True + + +mkTacticKind :: TacticCommand -> CodeActionKind +mkTacticKind = + CodeActionUnknown . mappend "refactor.wingman." . tacticKind + + +------------------------------------------------------------------------------ +-- | Mapping from tactic commands to their contextual providers. See 'provide', +-- 'filterGoalType' and 'filterBindingType' for the nitty gritty. +commandProvider :: TacticCommand -> TacticProvider +commandProvider Auto = + requireHoleSort (== Hole) $ + provide Auto "" +commandProvider Intros = + requireHoleSort (== Hole) $ + filterGoalType isFunction $ + provide Intros "" +commandProvider IntroAndDestruct = + requireHoleSort (== Hole) $ + filterGoalType (liftLambdaCase False (\_ -> isJust . algebraicTyCon)) $ + provide IntroAndDestruct "" +commandProvider Destruct = + requireHoleSort (== Hole) $ + filterBindingType destructFilter $ \occ _ -> + provide Destruct $ T.pack $ occNameString occ +commandProvider DestructPun = + requireHoleSort (== Hole) $ + filterBindingType destructPunFilter $ \occ _ -> + provide DestructPun $ T.pack $ occNameString occ +commandProvider Homomorphism = + requireHoleSort (== Hole) $ + filterBindingType homoFilter $ \occ _ -> + provide Homomorphism $ T.pack $ occNameString occ +commandProvider DestructLambdaCase = + requireHoleSort (== Hole) $ + requireExtension LambdaCase $ + filterGoalType (isJust . lambdaCaseable) $ + provide DestructLambdaCase "" +commandProvider HomomorphismLambdaCase = + requireHoleSort (== Hole) $ + requireExtension LambdaCase $ + filterGoalType (liftLambdaCase False homoFilter) $ + provide HomomorphismLambdaCase "" +commandProvider DestructAll = + requireHoleSort (== Hole) $ + withJudgement $ \jdg -> + case _jIsTopHole jdg && jHasBoundArgs jdg of + True -> provide DestructAll "" + False -> mempty +commandProvider UseDataCon = + requireHoleSort (== Hole) $ + withConfig $ \cfg -> + filterTypeProjection + ( guardLength (<= cfg_max_use_ctor_actions cfg) + . maybe [] fst + . tacticsGetDataCons + ) $ \dcon -> + provide UseDataCon + . T.pack + . occNameString + . occName + $ dataConName dcon +commandProvider Refine = + requireHoleSort (== Hole) $ + provide Refine "" +commandProvider BeginMetaprogram = + requireHoleSort (== Hole) $ + provide BeginMetaprogram "" +commandProvider RunMetaprogram = + withMetaprogram $ \mp -> + provide RunMetaprogram mp + + +------------------------------------------------------------------------------ +-- | Return an empty list if the given predicate doesn't hold over the length +guardLength :: (Int -> Bool) -> [a] -> [a] +guardLength f as = bool [] as $ f $ length as + + +------------------------------------------------------------------------------ +-- | A 'TacticProvider' is a way of giving context-sensitive actions to the LS +-- UI. +type TacticProvider + = TacticProviderData + -> [(Metadata, T.Text)] + + +data TacticProviderData = TacticProviderData + { tpd_lspEnv :: LspEnv + , tpd_jdg :: Judgement + , tpd_hole_sort :: HoleSort + } + + +requireHoleSort :: (HoleSort -> Bool) -> TacticProvider -> TacticProvider +requireHoleSort p tp tpd = + case p $ tpd_hole_sort tpd of + True -> tp tpd + False -> [] + +withMetaprogram :: (T.Text -> TacticProvider) -> TacticProvider +withMetaprogram tp tpd = + case tpd_hole_sort tpd of + Metaprogram mp -> tp mp tpd + _ -> [] + + +------------------------------------------------------------------------------ +-- | Restrict a 'TacticProvider', making sure it appears only when the given +-- predicate holds for the goal. +requireExtension :: Extension -> TacticProvider -> TacticProvider +requireExtension ext tp tpd = + case xopt ext $ le_dflags $ tpd_lspEnv tpd of + True -> tp tpd + False -> [] + + +------------------------------------------------------------------------------ +-- | Restrict a 'TacticProvider', making sure it appears only when the given +-- predicate holds for the goal. +filterGoalType :: (Type -> Bool) -> TacticProvider -> TacticProvider +filterGoalType p tp tpd = + case p $ unCType $ jGoal $ tpd_jdg tpd of + True -> tp tpd + False -> [] + + +------------------------------------------------------------------------------ +-- | Restrict a 'TacticProvider', making sure it appears only when the given +-- predicate holds for the goal. +withJudgement :: (Judgement -> TacticProvider) -> TacticProvider +withJudgement tp tpd = tp (tpd_jdg tpd) tpd + + +------------------------------------------------------------------------------ +-- | Multiply a 'TacticProvider' for each binding, making sure it appears only +-- when the given predicate holds over the goal and binding types. +filterBindingType + :: (Type -> Type -> Bool) -- ^ Goal and then binding types. + -> (OccName -> Type -> TacticProvider) + -> TacticProvider +filterBindingType p tp tpd = + let jdg = tpd_jdg tpd + hy = jLocalHypothesis jdg + g = jGoal jdg + in unHypothesis hy >>= \hi -> + let ty = unCType $ hi_type hi + in case p (unCType g) ty of + True -> tp (hi_name hi) ty tpd + False -> [] + + +------------------------------------------------------------------------------ +-- | Multiply a 'TacticProvider' by some feature projection out of the goal +-- type. Used e.g. to crete a code action for every data constructor. +filterTypeProjection + :: (Type -> [a]) -- ^ Features of the goal to look into further + -> (a -> TacticProvider) + -> TacticProvider +filterTypeProjection p tp tpd = + (p $ unCType $ jGoal $ tpd_jdg tpd) >>= \a -> + tp a tpd + + +------------------------------------------------------------------------------ +-- | Get access to the 'Config' when building a 'TacticProvider'. +withConfig :: (Config -> TacticProvider) -> TacticProvider +withConfig tp tpd = tp (le_config $ tpd_lspEnv tpd) tpd + + +------------------------------------------------------------------------------ +-- | Terminal constructor for providing context-sensitive tactics. Tactics +-- given by 'provide' are always available. +provide :: TacticCommand -> T.Text -> TacticProvider +provide tc name _ = + pure (Metadata (tacticTitle tc name) (mkTacticKind tc) (tacticPreferred tc), name) + + +------------------------------------------------------------------------------ +-- | Construct a 'CommandId' +tcCommandId :: TacticCommand -> CommandId +tcCommandId c = coerce $ T.pack $ "tactics" <> show c <> "Command" + + +------------------------------------------------------------------------------ +-- | We should show homos only when the goal type is the same as the binding +-- type, and that both are usual algebraic types. +homoFilter :: Type -> Type -> Bool +homoFilter codomain domain = + case uncoveredDataCons domain codomain of + Just s -> S.null s + _ -> False + + +------------------------------------------------------------------------------ +-- | Lift a function of (codomain, domain) over a lambda case. +liftLambdaCase :: r -> (Type -> Type -> r) -> Type -> r +liftLambdaCase nil f t = + case tacticsSplitFunTy t of + (_, _, arg : _, res) -> f res $ scaledThing arg + _ -> nil + + + +------------------------------------------------------------------------------ +-- | We should show destruct for bindings only when those bindings have usual +-- algebraic types. +destructFilter :: Type -> Type -> Bool +destructFilter _ (algebraicTyCon -> Just _) = True +destructFilter _ _ = False + + +------------------------------------------------------------------------------ +-- | We should show destruct punning for bindings only when those bindings have +-- usual algebraic types, and when any of their data constructors are records. +destructPunFilter :: Type -> Type -> Bool +destructPunFilter _ (algebraicTyCon -> Just tc) = + not . all (null . dataConFieldLabels) $ tyConDataCons tc +destructPunFilter _ _ = False + + +instance IsContinuationSort TacticCommand where + toCommandId = tcCommandId + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Machinery.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Machinery.hs new file mode 100644 index 0000000000..ca082ec65e --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Machinery.hs @@ -0,0 +1,450 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE TupleSections #-} + +module Wingman.Machinery where + +import Control.Applicative (empty) +import Control.Concurrent.Chan.Unagi.NoBlocking (newChan, writeChan, OutChan, tryRead, tryReadChan) +import Control.Lens ((<>~)) +import Control.Monad.Reader +import Control.Monad.State.Class (gets, modify, MonadState) +import Control.Monad.State.Strict (StateT (..), execStateT) +import Control.Monad.Trans.Maybe +import Data.Coerce +import Data.Foldable +import Data.Functor ((<&>)) +import Data.Generics (everything, gcount, mkQ) +import Data.Generics.Product (field') +import Data.List (sortBy) +import qualified Data.Map as M +import Data.Maybe (mapMaybe, isNothing) +import Data.Monoid (getSum) +import Data.Ord (Down (..), comparing) +import qualified Data.Set as S +import Data.Traversable (for) +import Development.IDE.Core.Compile (lookupName) +import Development.IDE.GHC.Compat hiding (isTopLevel, empty) +import Refinery.Future +import Refinery.ProofState +import Refinery.Tactic +import Refinery.Tactic.Internal +import System.Timeout (timeout) +import Wingman.Context (getInstance) +import Wingman.GHC (tryUnifyUnivarsButNotSkolems, updateSubst, tacticsGetDataCons, freshTyvars, tryUnifyUnivarsButNotSkolemsMany) +import Wingman.Judgements +import Wingman.Simplify (simplify) +import Wingman.Types + +#if __GLASGOW_HASKELL__ < 900 +import FunDeps (fd_eqs, improveFromInstEnv) +import Pair (unPair) +#else +import GHC.Tc.Instance.FunDeps (fd_eqs, improveFromInstEnv) +import GHC.Data.Pair (unPair) +#endif + + +substCTy :: TCvSubst -> CType -> CType +substCTy subst = coerce . substTy subst . coerce + + +getSubstForJudgement + :: MonadState TacticState m + => Judgement + -> m TCvSubst +getSubstForJudgement j = do + -- NOTE(sandy): It's OK to use mempty here, because coercions _can_ give us + -- substitutions for skolems. + let coercions = j_coercion j + unifier <- gets ts_unifier + pure $ unionTCvSubst unifier coercions + +------------------------------------------------------------------------------ +-- | Produce a subgoal that must be solved before we can solve the original +-- goal. +newSubgoal + :: Judgement + -> Rule +newSubgoal j = do + ctx <- ask + unifier <- getSubstForJudgement j + subgoal + $ normalizeJudgement ctx + $ substJdg unifier + $ unsetIsTopHole + $ normalizeJudgement ctx j + + +tacticToRule :: Judgement -> TacticsM () -> Rule +tacticToRule jdg (TacticT tt) = RuleT $ execStateT tt jdg >>= flip Subgoal Axiom + + +consumeChan :: OutChan (Maybe a) -> IO [a] +consumeChan chan = do + tryReadChan chan >>= tryRead >>= \case + Nothing -> pure [] + Just (Just a) -> (a:) <$> consumeChan chan + Just Nothing -> pure [] + + +------------------------------------------------------------------------------ +-- | Attempt to generate a term of the right type using in-scope bindings, and +-- a given tactic. +runTactic + :: Int -- ^ Timeout + -> Context + -> Judgement + -> TacticsM () -- ^ Tactic to use + -> IO (Either [TacticError] RunTacticResults) +runTactic duration ctx jdg t = do + let skolems = S.fromList + $ foldMap (tyCoVarsOfTypeWellScoped . unCType) + $ (:) (jGoal jdg) + $ fmap hi_type + $ toList + $ hyByName + $ jHypothesis jdg + tacticState = + defaultTacticState + { ts_skolems = skolems + } + + let stream = hoistListT (flip runReaderT ctx . unExtractM) + $ runStreamingTacticT t jdg tacticState + (in_proofs, out_proofs) <- newChan + (in_errs, out_errs) <- newChan + timed_out <- + fmap isNothing $ timeout duration $ consume stream $ \case + Left err -> writeChan in_errs $ Just err + Right proof -> writeChan in_proofs $ Just proof + writeChan in_proofs Nothing + + solns <- consumeChan out_proofs + let sorted = + flip sortBy solns $ comparing $ \(Proof ext _ holes) -> + Down $ scoreSolution ext jdg $ fmap snd holes + case sorted of + ((Proof syn _ subgoals) : _) -> + pure $ Right $ + RunTacticResults + { rtr_trace = syn_trace syn + , rtr_extract = simplify $ syn_val syn + , rtr_subgoals = fmap snd subgoals + , rtr_other_solns = reverse . fmap pf_extract $ sorted + , rtr_jdg = jdg + , rtr_ctx = ctx + , rtr_timed_out = timed_out + } + _ -> fmap Left $ consumeChan out_errs + + +tracePrim :: String -> Trace +tracePrim = flip rose [] + + +------------------------------------------------------------------------------ +-- | Mark that a tactic used the given string in its extract derivation. Mainly +-- used for debugging the search when things go terribly wrong. +tracing + :: Functor m + => String + -> TacticT jdg (Synthesized ext) err s m a + -> TacticT jdg (Synthesized ext) err s m a +tracing s = mappingExtract (mapTrace $ rose s . pure) + + +------------------------------------------------------------------------------ +-- | Mark that a tactic performed recursion. Doing so incurs a small penalty in +-- the score. +markRecursion + :: Functor m + => TacticT jdg (Synthesized ext) err s m a + -> TacticT jdg (Synthesized ext) err s m a +markRecursion = mappingExtract (field' @"syn_recursion_count" <>~ 1) + + +------------------------------------------------------------------------------ +-- | Map a function over the extract created by a tactic. +mappingExtract + :: Functor m + => (ext -> ext) + -> TacticT jdg ext err s m a + -> TacticT jdg ext err s m a +mappingExtract f (TacticT m) + = TacticT $ StateT $ \jdg -> + mapExtract id f $ runStateT m jdg + + +------------------------------------------------------------------------------ +-- | Given the results of running a tactic, score the solutions by +-- desirability. +-- +-- NOTE: This function is completely unprincipled and was just hacked together +-- to produce the right test results. +scoreSolution + :: Synthesized (LHsExpr GhcPs) + -> Judgement + -> [Judgement] + -> ( Penalize Int -- number of holes + , Reward Bool -- all bindings used + , Penalize Int -- unused top-level bindings + , Penalize Int -- number of introduced bindings + , Reward Int -- number used bindings + , Penalize Int -- number of recursive calls + , Penalize Int -- size of extract + ) +scoreSolution ext goal holes + = ( Penalize $ length holes + , Reward $ S.null $ intro_vals S.\\ used_vals + , Penalize $ S.size unused_top_vals + , Penalize $ S.size intro_vals + , Reward $ S.size used_vals + length used_user_vals + , Penalize $ getSum $ syn_recursion_count ext + , Penalize $ solutionSize $ syn_val ext + ) + where + initial_scope = hyByName $ jEntireHypothesis goal + intro_vals = M.keysSet $ hyByName $ syn_scoped ext + used_vals = S.intersection intro_vals $ syn_used_vals ext + used_user_vals = filter (isLocalHypothesis . hi_provenance) + $ mapMaybe (flip M.lookup initial_scope) + $ S.toList + $ syn_used_vals ext + top_vals = S.fromList + . fmap hi_name + . filter (isTopLevel . hi_provenance) + . unHypothesis + $ syn_scoped ext + unused_top_vals = top_vals S.\\ used_vals + + +------------------------------------------------------------------------------ +-- | Compute the number of 'LHsExpr' nodes; used as a rough metric for code +-- size. +solutionSize :: LHsExpr GhcPs -> Int +solutionSize = everything (+) $ gcount $ mkQ False $ \case + (_ :: LHsExpr GhcPs) -> True + + +newtype Penalize a = Penalize a + deriving (Eq, Ord, Show) via (Down a) + +newtype Reward a = Reward a + deriving (Eq, Ord, Show) via a + + +------------------------------------------------------------------------------ +-- | Generate a unique unification variable. +newUnivar :: MonadState TacticState m => m Type +newUnivar = do + freshTyvars $ + mkInfForAllTys [alphaTyVar] alphaTy + + +------------------------------------------------------------------------------ +-- | Attempt to unify two types. +unify :: CType -- ^ The goal type + -> CType -- ^ The type we are trying unify the goal type with + -> RuleM () +unify goal inst = do + skolems <- gets ts_skolems + case tryUnifyUnivarsButNotSkolems skolems goal inst of + Just subst -> + modify $ updateSubst subst + Nothing -> cut + +------------------------------------------------------------------------------ +-- | Get a substitution out of a theta's fundeps +learnFromFundeps + :: ThetaType + -> RuleM () +learnFromFundeps theta = do + inst_envs <- asks ctxInstEnvs + skolems <- gets ts_skolems + subst <- gets ts_unifier + let theta' = substTheta subst theta + fundeps = foldMap (foldMap fd_eqs . improveFromInstEnv inst_envs (\_ _ -> ())) theta' + case tryUnifyUnivarsButNotSkolemsMany skolems $ fmap unPair fundeps of + Just subst -> + modify $ updateSubst subst + Nothing -> cut + + +cut :: RuleT jdg ext err s m a +cut = RuleT Empty + + +------------------------------------------------------------------------------ +-- | Attempt to unify two types. +canUnify + :: MonadState TacticState m + => CType -- ^ The goal type + -> CType -- ^ The type we are trying unify the goal type with + -> m Bool +canUnify goal inst = do + skolems <- gets ts_skolems + case tryUnifyUnivarsButNotSkolems skolems goal inst of + Just _ -> pure True + Nothing -> pure False + + +------------------------------------------------------------------------------ +-- | Prefer the first tactic to the second, if the bool is true. Otherwise, just run the second tactic. +-- +-- This is useful when you have a clever pruning solution that isn't always +-- applicable. +attemptWhen :: TacticsM a -> TacticsM a -> Bool -> TacticsM a +attemptWhen _ t2 False = t2 +attemptWhen t1 t2 True = commit t1 t2 + + +------------------------------------------------------------------------------ +-- | Run the given tactic iff the current hole contains no univars. Skolems and +-- already decided univars are OK though. +requireConcreteHole :: TacticsM a -> TacticsM a +requireConcreteHole m = do + jdg <- goal + skolems <- gets ts_skolems + let vars = S.fromList $ tyCoVarsOfTypeWellScoped $ unCType $ jGoal jdg + case S.size $ vars S.\\ skolems of + 0 -> m + _ -> failure TooPolymorphic + + +------------------------------------------------------------------------------ +-- | The 'try' that comes in refinery 0.3 causes unnecessary backtracking and +-- balloons the search space. This thing just tries it, but doesn't backtrack +-- if it fails. +-- +-- NOTE(sandy): But there's a bug! Or at least, something not understood here. +-- Using this everywhere breaks te tests, and neither I nor TOTBWF are sure +-- why. Prefer 'try' if you can, and only try this as a last resort. +-- +-- TODO(sandy): Remove this when we upgrade to 0.4 +try' + :: Functor m + => TacticT jdg ext err s m () + -> TacticT jdg ext err s m () +try' t = commit t $ pure () + + +------------------------------------------------------------------------------ +-- | Sorry leaves a hole in its extract +exact :: HsExpr GhcPs -> TacticsM () +exact = rule . const . pure . pure . noLoc + +------------------------------------------------------------------------------ +-- | Lift a function over 'HyInfo's to one that takes an 'OccName' and tries to +-- look it up in the hypothesis. +useNameFromHypothesis :: (HyInfo CType -> TacticsM a) -> OccName -> TacticsM a +useNameFromHypothesis f name = do + hy <- jHypothesis <$> goal + case M.lookup name $ hyByName hy of + Just hi -> f hi + Nothing -> failure $ NotInScope name + +------------------------------------------------------------------------------ +-- | Lift a function over 'HyInfo's to one that takes an 'OccName' and tries to +-- look it up in the hypothesis. +useNameFromContext :: (HyInfo CType -> TacticsM a) -> OccName -> TacticsM a +useNameFromContext f name = do + lookupNameInContext name >>= \case + Just ty -> f $ createImportedHyInfo name ty + Nothing -> failure $ NotInScope name + + +------------------------------------------------------------------------------ +-- | Find the type of an 'OccName' that is defined in the current module. +lookupNameInContext :: MonadReader Context m => OccName -> m (Maybe CType) +lookupNameInContext name = do + ctx <- asks ctxModuleFuncs + pure $ case find ((== name) . fst) ctx of + Just (_, ty) -> pure ty + Nothing -> empty + + +getDefiningType + :: TacticsM CType +getDefiningType = do + calling_fun_name <- asks (fst . head . ctxDefiningFuncs) + maybe + (failure $ NotInScope calling_fun_name) + pure + =<< lookupNameInContext calling_fun_name + + +------------------------------------------------------------------------------ +-- | Build a 'HyInfo' for an imported term. +createImportedHyInfo :: OccName -> CType -> HyInfo CType +createImportedHyInfo on ty = HyInfo + { hi_name = on + , hi_provenance = ImportPrv + , hi_type = ty + } + + +getTyThing + :: OccName + -> TacticsM (Maybe TyThing) +getTyThing occ = do + ctx <- ask + case lookupOccEnv (ctx_occEnv ctx) occ of + Just (elt : _) -> do + mvar <- lift + $ ExtractM + $ lift + $ lookupName (ctx_hscEnv ctx) (ctx_module ctx) + $ gre_name elt + pure mvar + _ -> pure Nothing + + +------------------------------------------------------------------------------ +-- | Like 'getTyThing' but specialized to classes. +knownClass :: OccName -> TacticsM (Maybe Class) +knownClass occ = + getTyThing occ <&> \case + Just (ATyCon tc) -> tyConClass_maybe tc + _ -> Nothing + + +------------------------------------------------------------------------------ +-- | Like 'getInstance', but uses a class that it just looked up. +getKnownInstance :: OccName -> [Type] -> TacticsM (Maybe (Class, PredType)) +getKnownInstance f tys = runMaybeT $ do + cls <- MaybeT $ knownClass f + MaybeT $ getInstance cls tys + + +------------------------------------------------------------------------------ +-- | Lookup the type of any 'OccName' that was imported. Necessarily done in +-- IO, so we only expose this functionality to the parser. Internal Haskell +-- code that wants to lookup terms should do it via 'KnownThings'. +getOccNameType + :: OccName + -> TacticsM Type +getOccNameType occ = do + getTyThing occ >>= \case + Just (AnId v) -> pure $ varType v + _ -> failure $ NotInScope occ + + +getCurrentDefinitions :: TacticsM [(OccName, CType)] +getCurrentDefinitions = do + ctx_funcs <- asks ctxDefiningFuncs + for ctx_funcs $ \res@(occ, _) -> + pure . maybe res (occ,) =<< lookupNameInContext occ + + +------------------------------------------------------------------------------ +-- | Given two types, see if we can construct a homomorphism by mapping every +-- data constructor in the domain to the same in the codomain. This function +-- returns 'Just' when all the lookups succeeded, and a non-empty value if the +-- homomorphism *is not* possible. +uncoveredDataCons :: Type -> Type -> Maybe (S.Set (Uniquely DataCon)) +uncoveredDataCons domain codomain = do + (g_dcs, _) <- tacticsGetDataCons codomain + (hi_dcs, _) <- tacticsGetDataCons domain + pure $ S.fromList (coerce hi_dcs) S.\\ S.fromList (coerce g_dcs) + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/Lexer.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/Lexer.hs new file mode 100644 index 0000000000..fed7e91bbd --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/Lexer.hs @@ -0,0 +1,99 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE OverloadedStrings #-} + +module Wingman.Metaprogramming.Lexer where + +import Control.Applicative +import Control.Monad +import Data.Foldable (asum) +import Data.Text (Text) +import qualified Data.Text as T +import Data.Void +import Development.IDE.GHC.Compat.Core (OccName, mkVarOcc) +import qualified Text.Megaparsec as P +import qualified Text.Megaparsec.Char as P +import qualified Text.Megaparsec.Char.Lexer as L + +type Parser = P.Parsec Void Text + + + +lineComment :: Parser () +lineComment = L.skipLineComment "--" + +blockComment :: Parser () +blockComment = L.skipBlockComment "{-" "-}" + +sc :: Parser () +sc = L.space P.space1 lineComment blockComment + +ichar :: Parser Char +ichar = P.alphaNumChar <|> P.char '_' <|> P.char '\'' + +symchar :: Parser Char +symchar = asum + [ P.symbolChar + , P.char '!' + , P.char '#' + , P.char '$' + , P.char '%' + , P.char '^' + , P.char '&' + , P.char '*' + , P.char '-' + , P.char '=' + , P.char '+' + , P.char ':' + , P.char '<' + , P.char '>' + , P.char ',' + , P.char '.' + , P.char '/' + , P.char '?' + , P.char '~' + , P.char '|' + , P.char '\\' + ] + +lexeme :: Parser a -> Parser a +lexeme = L.lexeme sc + +symbol :: Text -> Parser Text +symbol = L.symbol sc + +symbol_ :: Text -> Parser () +symbol_ = void . symbol + +brackets :: Parser a -> Parser a +brackets = P.between (symbol "[") (symbol "]") + +braces :: Parser a -> Parser a +braces = P.between (symbol "{") (symbol "}") + +parens :: Parser a -> Parser a +parens = P.between (symbol "(") (symbol ")") + +identifier :: Text -> Parser () +identifier i = lexeme (P.string i *> P.notFollowedBy ichar) + +variable :: Parser OccName +variable = lexeme $ do + c <- P.alphaNumChar <|> P.char '(' + fmap mkVarOcc $ case c of + '(' -> do + cs <- P.many symchar + void $ P.char ')' + pure cs + _ -> do + cs <- P.many ichar + pure $ c : cs + +name :: Parser Text +name = lexeme $ do + c <- P.alphaNumChar + cs <- P.many (ichar <|> P.char '-') + pure $ T.pack (c:cs) + +keyword :: Text -> Parser () +keyword = identifier + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/Parser.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/Parser.hs new file mode 100644 index 0000000000..a1d4eca4d4 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/Parser.hs @@ -0,0 +1,501 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeApplications #-} + +module Wingman.Metaprogramming.Parser where + +import qualified Control.Monad.Combinators.Expr as P +import Data.Either (fromRight) +import Data.Functor +import Data.Maybe (listToMaybe) +import qualified Data.Text as T +import Development.IDE.GHC.Compat (RealSrcLoc, srcLocLine, srcLocCol, srcLocFile) +import Development.IDE.GHC.Compat.Util (unpackFS) +import Refinery.Tactic (failure) +import qualified Refinery.Tactic as R +import qualified Text.Megaparsec as P +import Wingman.Auto +import Wingman.Machinery (useNameFromHypothesis, useNameFromContext, getCurrentDefinitions) +import Wingman.Metaprogramming.Lexer +import Wingman.Metaprogramming.Parser.Documentation +import Wingman.Metaprogramming.ProofState (proofState, layout) +import Wingman.Tactics +import Wingman.Types + + +nullary :: T.Text -> TacticsM () -> Parser (TacticsM ()) +nullary name tac = identifier name $> tac + + +unary_occ :: T.Text -> (OccName -> TacticsM ()) -> Parser (TacticsM ()) +unary_occ name tac = tac <$> (identifier name *> variable) + + +------------------------------------------------------------------------------ +-- | Like 'unary_occ', but runs directly in the 'Parser' monad. +unary_occM :: T.Text -> (OccName -> Parser (TacticsM ())) -> Parser (TacticsM ()) +unary_occM name tac = tac =<< (identifier name *> variable) + + +variadic_occ :: T.Text -> ([OccName] -> TacticsM ()) -> Parser (TacticsM ()) +variadic_occ name tac = tac <$> (identifier name *> P.many variable) + + +commands :: [SomeMetaprogramCommand] +commands = + [ command "assumption" Nondeterministic Nullary + "Use any term in the hypothesis that can unify with the current goal." + (pure assumption) + [ Example + Nothing + [] + [EHI "some_a_val" "a"] + (Just "a") + "some_a_val" + ] + + , command "assume" Deterministic (Ref One) + "Use the given term from the hypothesis, unifying it with the current goal" + (pure . assume) + [ Example + Nothing + ["some_a_val"] + [EHI "some_a_val" "a"] + (Just "a") + "some_a_val" + ] + + , command "intros" Deterministic (Bind Many) + ( mconcat + [ "Construct a lambda expression, using the specific names if given, " + , "generating unique names otherwise. When no arguments are given, " + , "all of the function arguments will be bound; otherwise, this " + , "tactic will bind only enough to saturate the given names. Extra " + , "names are ignored." + ]) + (pure . \case + [] -> intros + names -> intros' $ IntroduceOnlyNamed names + ) + [ Example + Nothing + [] + [] + (Just "a -> b -> c -> d") + "\\a b c -> (_ :: d)" + , Example + Nothing + ["aye"] + [] + (Just "a -> b -> c -> d") + "\\aye -> (_ :: b -> c -> d)" + , Example + Nothing + ["x", "y", "z", "w"] + [] + (Just "a -> b -> c -> d") + "\\x y z -> (_ :: d)" + ] + + , command "idiom" Deterministic Tactic + "Lift a tactic into idiom brackets." + (pure . idiom) + [ Example + Nothing + ["(apply f)"] + [EHI "f" "a -> b -> Int"] + (Just "Maybe Int") + "f <$> (_ :: Maybe a) <*> (_ :: Maybe b)" + ] + + , command "intro" Deterministic (Bind One) + "Construct a lambda expression, binding an argument with the given name." + (pure . intros' . IntroduceOnlyNamed . pure) + [ Example + Nothing + ["aye"] + [] + (Just "a -> b -> c -> d") + "\\aye -> (_ :: b -> c -> d)" + ] + + , command "destruct_all" Deterministic Nullary + "Pattern match on every function paramater, in original binding order." + (pure destructAll) + [ Example + (Just "Assume `a` and `b` were bound via `f a b = _`.") + [] + [EHI "a" "Bool", EHI "b" "Maybe Int"] + Nothing $ + T.pack $ init $ unlines + [ "case a of" + , " False -> case b of" + , " Nothing -> _" + , " Just i -> _" + , " True -> case b of" + , " Nothing -> _" + , " Just i -> _" + ] + ] + + , command "destruct" Deterministic (Ref One) + "Pattern match on the argument." + (pure . useNameFromHypothesis destruct) + [ Example + Nothing + ["a"] + [EHI "a" "Bool"] + Nothing $ + T.pack $ init $ unlines + [ "case a of" + , " False -> _" + , " True -> _" + ] + ] + + , command "homo" Deterministic (Ref One) + ( mconcat + [ "Pattern match on the argument, and fill the resulting hole in with " + , "the same data constructor." + ]) + (pure . useNameFromHypothesis homo) + [ Example + (Just $ mconcat + [ "Only applicable when the type constructor of the argument is " + , "the same as that of the hole." + ]) + ["e"] + [EHI "e" "Either a b"] + (Just "Either x y") $ + T.pack $ init $ unlines + [ "case e of" + , " Left a -> Left (_ :: x)" + , " Right b -> Right (_ :: y)" + ] + ] + + , command "application" Nondeterministic Nullary + "Apply any function in the hypothesis that returns the correct type." + (pure application) + [ Example + Nothing + [] + [EHI "f" "a -> b"] + (Just "b") + "f (_ :: a)" + ] + + , command "pointwise" Deterministic Tactic + "Restrict the hypothesis in the holes of the given tactic to align up with the top-level bindings. This will ensure, eg, that the first hole can see only terms that came from the first position in any terms destructed from the top-level bindings." + (pure . flip restrictPositionForApplication (pure ())) + [ Example + (Just "In the context of `f (a1, b1) (a2, b2) = _`. The resulting first hole can see only 'a1' and 'a2', and the second, only 'b1' and 'b2'.") + ["(use mappend)"] + [] + Nothing + "mappend _ _" + ] + + , command "apply" Deterministic (Ref One) + "Apply the given function from *local* scope." + (pure . useNameFromHypothesis (apply Saturated)) + [ Example + Nothing + ["f"] + [EHI "f" "a -> b"] + (Just "b") + "f (_ :: a)" + ] + + , command "split" Nondeterministic Nullary + "Produce a data constructor for the current goal." + (pure split) + [ Example + Nothing + [] + [] + (Just "Either a b") + "Right (_ :: b)" + ] + + , command "ctor" Deterministic (Ref One) + "Use the given data cosntructor." + (pure . userSplit) + [ Example + Nothing + ["Just"] + [] + (Just "Maybe a") + "Just (_ :: a)" + ] + + , command "obvious" Nondeterministic Nullary + "Produce a nullary data constructor for the current goal." + (pure obvious) + [ Example + Nothing + [] + [] + (Just "[a]") + "[]" + ] + + , command "auto" Nondeterministic Nullary + ( mconcat + [ "Repeatedly attempt to split, destruct, apply functions, and " + , "recurse in an attempt to fill the hole." + ]) + (pure auto) + [ Example + Nothing + [] + [EHI "f" "a -> b", EHI "g" "b -> c"] + (Just "a -> c") + "g . f" + ] + + , command "sorry" Deterministic Nullary + "\"Solve\" the goal by leaving a hole." + (pure sorry) + [ Example + Nothing + [] + [] + (Just "b") + "_ :: b" + ] + + , command "unary" Deterministic Nullary + ( mconcat + [ "Produce a hole for a single-parameter function, as well as a hole for " + , "its argument. The argument holes are completely unconstrained, and " + , "will be solved before the function." + ]) + (pure $ nary 1) + [ Example + (Just $ mconcat + [ "In the example below, the variable `a` is free, and will unify " + , "to the resulting extract from any subsequent tactic." + ]) + [] + [] + (Just "Int") + "(_2 :: a -> Int) (_1 :: a)" + ] + + , command "binary" Deterministic Nullary + ( mconcat + [ "Produce a hole for a two-parameter function, as well as holes for " + , "its arguments. The argument holes have the same type but are " + , "otherwise unconstrained, and will be solved before the function." + ]) + (pure $ nary 2) + [ Example + (Just $ mconcat + [ "In the example below, the variable `a` is free, and will unify " + , "to the resulting extract from any subsequent tactic." + ]) + [] + [] + (Just "Int") + "(_3 :: a -> a -> Int) (_1 :: a) (_2 :: a)" + ] + + , command "recursion" Deterministic Nullary + "Fill the current hole with a call to the defining function." + ( pure $ + fmap listToMaybe getCurrentDefinitions >>= \case + Just (self, _) -> useNameFromContext (apply Saturated) self + Nothing -> failure $ TacticPanic "no defining function" + ) + [ Example + (Just "In the context of `foo (a :: Int) (b :: b) = _`:") + [] + [] + Nothing + "foo (_ :: Int) (_ :: b)" + ] + + , command "use" Deterministic (Ref One) + "Apply the given function from *module* scope." + (pure . use Saturated) + [ Example + (Just "`import Data.Char (isSpace)`") + ["isSpace"] + [] + (Just "Bool") + "isSpace (_ :: Char)" + ] + + , command "cata" Deterministic (Ref One) + "Destruct the given term, recursing on every resulting binding." + (pure . useNameFromHypothesis cata) + [ Example + (Just "Assume we're called in the context of a function `f.`") + ["x"] + [EHI "x" "(a, a)"] + Nothing $ + T.pack $ init $ unlines + [ "case x of" + , " (a1, a2) ->" + , " let a1_c = f a1" + , " a2_c = f a2" + , " in _" + ] + ] + + , command "collapse" Deterministic Nullary + "Collapse every term in scope with the same type as the goal." + (pure collapse) + [ Example + Nothing + [] + [ EHI "a1" "a" + , EHI "a2" "a" + , EHI "a3" "a" + ] + (Just "a") + "(_ :: a -> a -> a -> a) a1 a2 a3" + ] + + , command "let" Deterministic (Bind Many) + "Create let-bindings for each binder given to this tactic." + (pure . letBind) + [ Example + Nothing + ["a", "b", "c"] + [ ] + (Just "x") + $ T.pack $ unlines + [ "let a = _1 :: a" + , " b = _2 :: b" + , " c = _3 :: c" + , " in (_4 :: x)" + ] + ] + + , command "try" Nondeterministic Tactic + "Simultaneously run and do not run a tactic. Subsequent tactics will bind on both states." + (pure . R.try) + [ Example + Nothing + ["(apply f)"] + [ EHI "f" "a -> b" + ] + (Just "b") + $ T.pack $ unlines + [ "-- BOTH of:\n" + , "f (_ :: a)" + , "\n-- and\n" + , "_ :: b" + ] + ] + + , command "nested" Nondeterministic (Ref One) + "Nest the given function (in module scope) with itself arbitrarily many times. NOTE: The resulting function is necessarily unsaturated, so you will likely need `with_arg` to use this tactic in a saturated context." + (pure . nested) + [ Example + Nothing + ["fmap"] + [] + (Just "[(Int, Either Bool a)] -> [(Int, Either Bool b)]") + "fmap (fmap (fmap _))" + ] + + , command "with_arg" Deterministic Nullary + "Fill the current goal with a function application. This can be useful when you'd like to fill in the argument before the function, or when you'd like to use a non-saturated function in a saturated context." + (pure with_arg) + [ Example + (Just "Where `a` is a new unifiable type variable.") + [] + [] + (Just "r") + "(_2 :: a -> r) (_1 :: a)" + ] + ] + + + +oneTactic :: Parser (TacticsM ()) +oneTactic = + P.choice + [ parens tactic + , makeParser commands + ] + + +tactic :: Parser (TacticsM ()) +tactic = P.makeExprParser oneTactic operators + +operators :: [[P.Operator Parser (TacticsM ())]] +operators = + [ [ P.InfixR (symbol "|" $> (R.<%>) )] + , [ P.InfixL (symbol ";" $> (>>)) + , P.InfixL (symbol "," $> bindOne) + ] + ] + + +tacticProgram :: Parser (TacticsM ()) +tacticProgram = do + sc + r <- tactic P.<|> pure (pure ()) + P.eof + pure r + + +wrapError :: String -> String +wrapError err = "```\n" <> err <> "\n```\n" + + +fixErrorOffset :: RealSrcLoc -> P.ParseErrorBundle a b -> P.ParseErrorBundle a b +fixErrorOffset rsl (P.ParseErrorBundle ne (P.PosState a n (P.SourcePos _ line col) pos s)) + = P.ParseErrorBundle ne + $ P.PosState a n + (P.SourcePos + (unpackFS $ srcLocFile rsl) + ((<>) line $ P.mkPos $ srcLocLine rsl - 1) + ((<>) col $ P.mkPos $ srcLocCol rsl - 1 + length @[] "[wingman|") + ) + pos + s + +------------------------------------------------------------------------------ +-- | Attempt to run a metaprogram tactic, returning the proof state, or the +-- errors. +attempt_it + :: RealSrcLoc + -> Context + -> Judgement + -> String + -> IO (Either String String) +attempt_it rsl ctx jdg program = + case P.runParser tacticProgram "" (T.pack program) of + Left peb -> pure $ Left $ wrapError $ P.errorBundlePretty $ fixErrorOffset rsl peb + Right tt -> do + res <- runTactic 2e6 ctx jdg tt + pure $ case res of + Left tes -> Left $ wrapError $ show tes + Right rtr -> Right + $ layout (cfg_proofstate_styling $ ctxConfig ctx) + $ proofState rtr + + +parseMetaprogram :: T.Text -> TacticsM () +parseMetaprogram + = fromRight (pure ()) + . P.runParser tacticProgram "" + + +------------------------------------------------------------------------------ +-- | Automatically generate the metaprogram command reference. +writeDocumentation :: IO () +writeDocumentation = + writeFile "COMMANDS.md" $ + unlines + [ "# Wingman Metaprogram Command Reference" + , "" + , prettyReadme commands + ] + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/Parser.hs-boot b/plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/Parser.hs-boot new file mode 100644 index 0000000000..607db0e6f3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/Parser.hs-boot @@ -0,0 +1,7 @@ +module Wingman.Metaprogramming.Parser where + +import Wingman.Metaprogramming.Lexer +import Wingman.Types + +tactic :: Parser (TacticsM ()) + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/Parser/Documentation.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/Parser/Documentation.hs new file mode 100644 index 0000000000..44071a5ae7 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/Parser/Documentation.hs @@ -0,0 +1,237 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# OPTIONS_GHC -Wno-deprecations #-} + +module Wingman.Metaprogramming.Parser.Documentation where + +import Data.Functor ((<&>)) +import Data.List (sortOn) +import Data.String (IsString) +import Data.Text (Text) +import Data.Text.Prettyprint.Doc hiding (parens) +import Data.Text.Prettyprint.Doc.Render.String (renderString) +import Development.IDE.GHC.Compat (OccName) +import qualified Text.Megaparsec as P +import Wingman.Metaprogramming.Lexer (Parser, identifier, variable, parens) +import Wingman.Types (TacticsM) + +import {-# SOURCE #-} Wingman.Metaprogramming.Parser (tactic) + + +------------------------------------------------------------------------------ +-- | Is a tactic deterministic or not? +data Determinism + = Deterministic + | Nondeterministic + +prettyDeterminism :: Determinism -> Doc b +prettyDeterminism Deterministic = "deterministic" +prettyDeterminism Nondeterministic = "non-deterministic" + + +------------------------------------------------------------------------------ +-- | How many arguments does the tactic take? +data Count a where + One :: Count OccName + Many :: Count [OccName] + +prettyCount :: Count a -> Doc b +prettyCount One = "single" +prettyCount Many = "variadic" + + +------------------------------------------------------------------------------ +-- | What sorts of arguments does the tactic take? Currently there is no +-- distinction between 'Ref' and 'Bind', other than documentation. +-- +-- The type index here is used for the shape of the function the parser should +-- take. +data Syntax a where + Nullary :: Syntax (Parser (TacticsM ())) + Ref :: Count a -> Syntax (a -> Parser (TacticsM ())) + Bind :: Count a -> Syntax (a -> Parser (TacticsM ())) + Tactic :: Syntax (TacticsM () -> Parser (TacticsM ())) + +prettySyntax :: Syntax a -> Doc b +prettySyntax Nullary = "none" +prettySyntax (Ref co) = prettyCount co <+> "reference" +prettySyntax (Bind co) = prettyCount co <+> "binding" +prettySyntax Tactic = "tactic" + + +------------------------------------------------------------------------------ +-- | An example for the documentation. +data Example = Example + { ex_ctx :: Maybe Text -- ^ Specific context information about when the tactic is applicable + , ex_args :: [Var] -- ^ Arguments the tactic was called with + , ex_hyp :: [ExampleHyInfo] -- ^ The hypothesis + , ex_goal :: Maybe ExampleType -- ^ Current goal. Nothing indicates it's uninteresting. + , ex_result :: Text -- ^ Resulting extract. + } + + +------------------------------------------------------------------------------ +-- | An example 'HyInfo'. +data ExampleHyInfo = EHI + { ehi_name :: Var -- ^ Name of the variable + , ehi_type :: ExampleType -- ^ Type of the variable + } + + +------------------------------------------------------------------------------ +-- | A variable +newtype Var = Var + { getVar :: Text + } + deriving newtype (IsString, Pretty) + + +------------------------------------------------------------------------------ +-- | A type +newtype ExampleType = ExampleType + { getExampleType :: Text + } + deriving newtype (IsString, Pretty) + + +------------------------------------------------------------------------------ +-- | A command to expose to the parser +data MetaprogramCommand a = MC + { mpc_name :: Text -- ^ Name of the command. This is the token necessary to run the command. + , mpc_syntax :: Syntax a -- ^ The command's arguments + , mpc_det :: Determinism -- ^ Determinism of the command + , mpc_description :: Text -- ^ User-facing description + , mpc_tactic :: a -- ^ Tactic to run + , mpc_examples :: [Example] -- ^ Collection of documentation examples + } + +------------------------------------------------------------------------------ +-- | Existentialize the pain away +data SomeMetaprogramCommand where + SMC :: MetaprogramCommand a -> SomeMetaprogramCommand + + +------------------------------------------------------------------------------ +-- | Run the 'Parser' of a 'MetaprogramCommand' +makeMPParser :: MetaprogramCommand a -> Parser (TacticsM ()) +makeMPParser (MC name Nullary _ _ t _) = do + identifier name + t +makeMPParser (MC name (Ref One) _ _ t _) = do + identifier name + variable >>= t +makeMPParser (MC name (Ref Many) _ _ t _) = do + identifier name + P.many variable >>= t +makeMPParser (MC name (Bind One) _ _ t _) = do + identifier name + variable >>= t +makeMPParser (MC name (Bind Many) _ _ t _) = do + identifier name + P.many variable >>= t +makeMPParser (MC name Tactic _ _ t _) = do + identifier name + parens tactic >>= t + + +------------------------------------------------------------------------------ +-- | Compile a collection of metaprogram commands into a parser. +makeParser :: [SomeMetaprogramCommand] -> Parser (TacticsM ()) +makeParser ps = P.choice $ ps <&> \(SMC mp) -> makeMPParser mp + + +------------------------------------------------------------------------------ +-- | Pretty print a command. +prettyCommand :: MetaprogramCommand a -> Doc b +prettyCommand (MC name syn det desc _ exs) = vsep + [ "##" <+> pretty name + , mempty + , "arguments:" <+> prettySyntax syn <> ". " + , prettyDeterminism det <> "." + , mempty + , ">" <+> align (pretty desc) + , mempty + , vsep $ fmap (prettyExample name) exs + , mempty + ] + + +------------------------------------------------------------------------------ +-- | Pretty print a hypothesis. +prettyHyInfo :: ExampleHyInfo -> Doc a +prettyHyInfo hi = pretty (ehi_name hi) <+> "::" <+> pretty (ehi_type hi) + + +------------------------------------------------------------------------------ +-- | Append the given term only if the first argument has elements. +mappendIfNotNull :: [a] -> a -> [a] +mappendIfNotNull [] _ = [] +mappendIfNotNull as a = as <> [a] + + +------------------------------------------------------------------------------ +-- | Pretty print an example. +prettyExample :: Text -> Example -> Doc a +prettyExample name (Example m_txt args hys goal res) = + align $ vsep + [ mempty + , "### Example" + , maybe mempty ((line <>) . (<> line) . (">" <+>) . align . pretty) m_txt + , "Given:" + , mempty + , codeFence $ vsep + $ mappendIfNotNull (fmap prettyHyInfo hys) mempty + <> [ "_" <+> maybe mempty (("::" <+>). pretty) goal + ] + , mempty + , hsep + [ "running " + , enclose "`" "`" $ pretty name <> hsep (mempty : fmap pretty args) + , "will produce:" + ] + , mempty + , codeFence $ align $ pretty res + ] + + +------------------------------------------------------------------------------ +-- | Make a haskell code fence. +codeFence :: Doc a -> Doc a +codeFence d = align $ vsep + [ "```haskell" + , d + , "```" + ] + + +------------------------------------------------------------------------------ +-- | Render all of the commands. +prettyReadme :: [SomeMetaprogramCommand] -> String +prettyReadme + = renderString + . layoutPretty defaultLayoutOptions + . vsep + . fmap (\case SMC c -> prettyCommand c) + . sortOn (\case SMC c -> mpc_name c) + + + +------------------------------------------------------------------------------ +-- | Helper function to build a 'SomeMetaprogramCommand'. +command + :: Text + -> Determinism + -> Syntax a + -> Text + -> a + -> [Example] + -> SomeMetaprogramCommand +command txt det syn txt' a exs = SMC $ + MC + { mpc_name = txt + , mpc_det = det + , mpc_syntax = syn + , mpc_description = txt' + , mpc_tactic = a + , mpc_examples = exs + } + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/ProofState.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/ProofState.hs new file mode 100644 index 0000000000..02e203a1d3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Metaprogramming/ProofState.hs @@ -0,0 +1,117 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# OPTIONS_GHC -Wno-deprecations #-} + +module Wingman.Metaprogramming.ProofState where + +import Data.Bool (bool) +import Data.Functor ((<&>)) +import qualified Data.Text as T +import Data.Text.Prettyprint.Doc +import Data.Text.Prettyprint.Doc.Render.Util.Panic +import Language.LSP.Types (sectionSeparator) +import Wingman.Judgements (jHypothesis) +import Wingman.Types + +renderSimplyDecorated + :: Monoid out + => (T.Text -> out) -- ^ Render plain 'Text' + -> (ann -> out) -- ^ How to render an annotation + -> (ann -> out) -- ^ How to render the removed annotation + -> SimpleDocStream ann + -> out +renderSimplyDecorated text push pop = go [] + where + go _ SFail = panicUncaughtFail + go [] SEmpty = mempty + go (_:_) SEmpty = panicInputNotFullyConsumed + go st (SChar c rest) = text (T.singleton c) <> go st rest + go st (SText _l t rest) = text t <> go st rest + go st (SLine i rest) = + text (T.singleton '\n') <> text (textSpaces i) <> go st rest + go st (SAnnPush ann rest) = push ann <> go (ann : st) rest + go (ann:st) (SAnnPop rest) = pop ann <> go st rest + go [] SAnnPop{} = panicUnpairedPop +{-# INLINE renderSimplyDecorated #-} + + +data Ann + = Goal + | Hypoth + | Status + deriving (Eq, Ord, Show, Enum, Bounded) + +forceMarkdownNewlines :: String -> String +forceMarkdownNewlines = unlines . fmap (<> " ") . lines + +layout :: Bool -> Doc Ann -> String +layout use_styling + = forceMarkdownNewlines + . T.unpack + . renderSimplyDecorated id + (renderAnn use_styling) + (renderUnann use_styling) + . layoutPretty (LayoutOptions $ AvailablePerLine 80 0.6) + +renderAnn :: Bool -> Ann -> T.Text +renderAnn False _ = "" +renderAnn _ Goal = "" +renderAnn _ Hypoth = "```haskell\n" +renderAnn _ Status = "" + +renderUnann :: Bool -> Ann -> T.Text +renderUnann False _ = "" +renderUnann _ Goal = "" +renderUnann _ Hypoth = "\n```\n" +renderUnann _ Status = "" + +proofState :: RunTacticResults -> Doc Ann +proofState RunTacticResults{rtr_subgoals} = + vsep + $ ( annotate Status + . countFinished "goals accomplished 🎉" "goal" + $ length rtr_subgoals + ) + : pretty sectionSeparator + : fmap prettySubgoal rtr_subgoals + + +prettySubgoal :: Judgement -> Doc Ann +prettySubgoal jdg = + vsep $ + [ mempty | has_hy] <> + [ annotate Hypoth $ prettyHypothesis hy | has_hy] <> + [ "⊢" <+> annotate Goal (prettyType (_jGoal jdg)) + , pretty sectionSeparator + ] + where + hy = jHypothesis jdg + has_hy = not $ null $ unHypothesis hy + + +prettyHypothesis :: Hypothesis CType -> Doc Ann +prettyHypothesis hy = + vsep $ unHypothesis hy <&> \hi -> + prettyHyInfo hi + +prettyHyInfo :: HyInfo CType -> Doc Ann +prettyHyInfo hi = viaShow (hi_name hi) <+> "::" <+> prettyType (hi_type hi) + + +prettyType :: CType -> Doc Ann +prettyType (CType ty) = viaShow ty + + +countFinished :: Doc Ann -> Doc Ann -> Int -> Doc Ann +countFinished finished _ 0 = finished +countFinished _ thing n = count thing n + +count :: Doc Ann -> Int -> Doc Ann +count thing n = + pretty n <+> thing <> bool "" "s" (n /= 1) + +textSpaces :: Int -> T.Text +textSpaces n = T.replicate n $ T.singleton ' ' + + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Naming.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Naming.hs new file mode 100644 index 0000000000..832fa117e1 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Naming.hs @@ -0,0 +1,276 @@ +{-# LANGUAGE CPP #-} + +module Wingman.Naming where + +import Control.Arrow +import Control.Monad.State.Strict +import Data.Aeson (camelTo2) +import Data.Bool (bool) +import Data.Char +import Data.List (isPrefixOf) +import Data.List.Extra (split) +import Data.Map (Map) +import qualified Data.Map as M +import Data.Maybe (listToMaybe, fromMaybe) +import Data.Monoid +import Data.Set (Set) +import qualified Data.Set as S +import Data.Traversable +import Development.IDE.GHC.Compat.Core hiding (IsFunction) +import Text.Hyphenation (hyphenate, english_US) +import Wingman.GHC (tcTyVar_maybe) + +#if __GLASGOW_HASKELL__ >= 900 +import GHC.Tc.Utils.TcType +#endif + + +------------------------------------------------------------------------------ +-- | A classification of a variable, for which we have specific naming rules. +-- A variable can have multiple purposes simultaneously. +data Purpose + = Function [Type] Type + | Predicate + | Continuation + | Integral + | Number + | String + | List Type + | Maybe Type + | TyConned TyCon [Type] + -- ^ Something of the form @TC a b c@ + | TyVarred TyVar [Type] + -- ^ Something of the form @m a b c@ + +pattern IsPredicate :: Type +pattern IsPredicate <- + (tcSplitFunTys -> ([isFunTy . scaledThing -> False], isBoolTy -> True)) + +pattern IsFunction :: [Type] -> Type -> Type +pattern IsFunction args res <- + (first (map scaledThing) . tcSplitFunTys -> (args@(_:_), res)) + +pattern IsString :: Type +pattern IsString <- + (splitTyConApp_maybe -> Just ((== listTyCon) -> True, [eqType charTy -> True])) + +pattern IsMaybe :: Type -> Type +pattern IsMaybe a <- + (splitTyConApp_maybe -> Just ((== maybeTyCon) -> True, [a])) + +pattern IsList :: Type -> Type +pattern IsList a <- + (splitTyConApp_maybe -> Just ((== listTyCon) -> True, [a])) + +pattern IsTyConned :: TyCon -> [Type] -> Type +pattern IsTyConned tc args <- + (splitTyConApp_maybe -> Just (id &&& isSymOcc . getOccName -> (tc, False), args)) + +pattern IsTyVarred :: TyVar -> [Type] -> Type +pattern IsTyVarred v args <- + (tcSplitAppTys -> (tcTyVar_maybe -> Just v, args)) + + +------------------------------------------------------------------------------ +-- | Get the 'Purpose's of a type. A type can have multiple purposes +-- simultaneously, so the order of purposes in this function corresponds to the +-- precedence of that naming rule. Which means, eg, that if a type is both +-- a 'Predicate' and a 'Function', we should prefer to use the predicate naming +-- rules, since they come first. +getPurposes :: Type -> [Purpose] +getPurposes ty = mconcat + [ [ Predicate | IsPredicate <- [ty] ] + , [ Function args res | IsFunction args res <- [ty] ] + , with (isIntegerTy ty) [ Integral, Number ] + , with (isIntTy ty) [ Integral, Number ] + , [ Number | isFloatingTy ty ] + , [ String | isStringTy ty ] + , [ Maybe a | IsMaybe a <- [ty] ] + , [ List a | IsList a <- [ty] ] + , [ TyVarred v args | IsTyVarred v args <- [ty] ] + , [ TyConned tc args | IsTyConned tc args <- [ty] + , not (isTupleTyCon tc) + , tc /= listTyCon ] + ] + + +------------------------------------------------------------------------------ +-- | Return 'mempty' if the give bool is false. +with :: Monoid a => Bool -> a -> a +with False _ = mempty +with True a = a + + +------------------------------------------------------------------------------ +-- | Names we can give functions +functionNames :: [String] +functionNames = ["f", "g", "h"] + + +------------------------------------------------------------------------------ +-- | Get a ranked ordering of names for a given purpose. +purposeToName :: Purpose -> [String] +purposeToName (Function args res) + | Just tv_args <- traverse tcTyVar_maybe $ args <> pure res + = fmap (<> foldMap (occNameString . occName) tv_args) functionNames +purposeToName (Function _ _) = functionNames +purposeToName Predicate = pure "p" +purposeToName Continuation = pure "k" +purposeToName Integral = ["n", "i", "j"] +purposeToName Number = ["x", "y", "z", "w"] +purposeToName String = ["s", "str"] +purposeToName (List t) = fmap (<> "s") $ purposeToName =<< getPurposes t +purposeToName (Maybe t) = fmap ("m_" <>) $ purposeToName =<< getPurposes t +purposeToName (TyVarred tv args) + | Just tv_args <- traverse tcTyVar_maybe args + = pure $ foldMap (occNameString . occName) $ tv : tv_args +purposeToName (TyVarred tv _) = pure $ occNameString $ occName tv +purposeToName (TyConned tc args@(_:_)) + | Just tv_args <- traverse tcTyVar_maybe args + = [ mkTyConName tc + -- We insert primes to everything later, but it gets the lowest + -- precedence. Here we'd like to prefer it over the more specific type + -- name. + , mkTyConName tc <> "'" + , mconcat + [ mkTyConName tc + , bool mempty "_" $ length (mkTyConName tc) > 1 + , foldMap (occNameString . occName) tv_args + ] + ] +purposeToName (TyConned tc _) + = pure + $ mkTyConName tc + + +mkTyName :: Type -> [String] +mkTyName = purposeToName <=< getPurposes + + +------------------------------------------------------------------------------ +-- | Get a good name for a type constructor. +mkTyConName :: TyCon -> String +mkTyConName tc + | tc == unitTyCon = "u" + | isSymOcc occ + = take 1 + . fmap toLower + . filterReplace isSymbol 's' + . filterReplace isPunctuation 'p' + $ name + | camels@(_:_:_) <- camelTerms name + = foldMap (fmap toLower . take 1) camels + | otherwise + = getStem + $ fmap toLower name + where + occ = getOccName tc + name = occNameString occ + + +------------------------------------------------------------------------------ +-- | Split a string into its camel case components. +camelTerms :: String -> [String] +camelTerms = split (== '@') . camelTo2 '@' + + +------------------------------------------------------------------------------ +-- | A stem of a string is either a special-case shortened form, or a shortened +-- first syllable. If the string is one syllable, we take the full word if it's +-- short, or just the first two characters if it's long. Otherwise, just take +-- the first syllable. +-- +-- NOTE: There's no rhyme or reason here, I just experimented until I got +-- results that were reasonably consistent with the names I would give things. +getStem :: String -> String +getStem str = + let s = stem str + in case (s == str, length str) of + (False, _) -> s + (True, (<= 3) -> True) -> str + _ -> take 2 str + +------------------------------------------------------------------------------ +-- | Get a special-case stem, or, failing that, give back the first syllable. +stem :: String -> String +stem "char" = "c" +stem "function" = "func" +stem "bool" = "b" +stem "either" = "e" +stem "text" = "txt" +stem s = join $ take 1 $ hyphenate english_US s + + +------------------------------------------------------------------------------ +-- | Maybe replace an element in the list if the predicate matches +filterReplace :: (a -> Bool) -> a -> [a] -> [a] +filterReplace f r = fmap (\a -> bool a r $ f a) + + +------------------------------------------------------------------------------ +-- | Produce a unique, good name for a type. +mkGoodName + :: Set OccName -- ^ Bindings in scope; used to ensure we don't shadow anything + -> Type -- ^ The type to produce a name for + -> OccName +mkGoodName in_scope (mkTyName -> tn) + = mkVarOcc + . fromMaybe (mkNumericSuffix in_scope $ fromMaybe "x" $ listToMaybe tn) + . getFirst + . foldMap (\n -> bool (pure n) mempty $ check n) + $ tn <> fmap (<> "'") tn + where + check n = S.member (mkVarOcc n) $ illegalNames <> in_scope + + +illegalNames :: Set OccName +illegalNames = S.fromList $ fmap mkVarOcc + [ "case" + , "of" + , "class" + , "data" + , "do" + , "type" + , "if" + , "then" + , "else" + , "let" + , "in" + , "mdo" + , "newtype" + , "proc" + , "rec" + , "where" + ] + + + +------------------------------------------------------------------------------ +-- | Given a desired name, compute a new name for it based on how many names in +-- scope conflict with it. Eg, if we want to name something @x@, but already +-- have @x@, @x'@ and @x2@ in scope, we will give back @x3@. +mkNumericSuffix :: Set OccName -> String -> String +mkNumericSuffix s nm = + mappend nm . show . length . filter (isPrefixOf nm . occNameString) $ S.toList s + + +------------------------------------------------------------------------------ +-- | Like 'mkGoodName' but creates several apart names. +mkManyGoodNames + :: (Traversable t) + => Set OccName + -> t Type + -> t OccName +mkManyGoodNames in_scope args = + flip evalState in_scope $ for args $ \at -> do + in_scope <- get + let n = mkGoodName in_scope at + modify $ S.insert n + pure n + + +------------------------------------------------------------------------------ +-- | Which names are in scope? +getInScope :: Map OccName a -> [OccName] +getInScope = M.keys + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Plugin.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Plugin.hs new file mode 100644 index 0000000000..b55ee31ae3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Plugin.hs @@ -0,0 +1,46 @@ +-- | A plugin that uses tactics to synthesize code +module Wingman.Plugin where + +import Control.Monad +import Development.IDE.Core.Shake (IdeState (..)) +import Development.IDE.Plugin.CodeAction +import qualified Development.IDE.GHC.ExactPrint as E +import Ide.Types +import Language.LSP.Types +import Prelude hiding (span) +import Wingman.AbstractLSP +import Wingman.AbstractLSP.TacticActions (makeTacticInteraction) +import Wingman.EmptyCase +import Wingman.LanguageServer hiding (Log) +import qualified Wingman.LanguageServer as WingmanLanguageServer +import Wingman.LanguageServer.Metaprogram (hoverProvider) +import Wingman.StaticPlugin +import Development.IDE.Types.Logger (Recorder, cmapWithPrio, WithPriority, Pretty (pretty)) + +data Log + = LogWingmanLanguageServer WingmanLanguageServer.Log + | LogExactPrint E.Log + deriving Show + +instance Pretty Log where + pretty = \case + LogWingmanLanguageServer log -> pretty log + LogExactPrint exactPrintLog -> pretty exactPrintLog + +descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState +descriptor recorder plId + = mkExactprintPluginDescriptor (cmapWithPrio LogExactPrint recorder) + $ installInteractions + ( emptyCaseInteraction + : fmap makeTacticInteraction [minBound .. maxBound] + ) + $ (defaultPluginDescriptor plId) + { pluginHandlers = mkPluginHandler STextDocumentHover hoverProvider + , pluginRules = wingmanRules (cmapWithPrio LogWingmanLanguageServer recorder) plId + , pluginConfigDescriptor = + defaultConfigDescriptor + { configCustomConfig = mkCustomConfig properties + } + , pluginModifyDynflags = staticPlugin + } + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Range.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Range.hs new file mode 100644 index 0000000000..ec61efc27f --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Range.hs @@ -0,0 +1,23 @@ +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE RankNTypes #-} + +module Wingman.Range where + +import Development.IDE hiding (rangeToRealSrcSpan, rangeToSrcSpan) +import Development.IDE.GHC.Compat.Core +import Development.IDE.GHC.Compat.Util as FS + + + +------------------------------------------------------------------------------ +-- | Convert a DAML compiler Range to a GHC SrcSpan +-- TODO(sandy): this doesn't belong here +rangeToSrcSpan :: String -> Range -> SrcSpan +rangeToSrcSpan file range = RealSrcSpan (rangeToRealSrcSpan file range) Nothing + + +rangeToRealSrcSpan :: String -> Range -> RealSrcSpan +rangeToRealSrcSpan file (Range (Position startLn startCh) (Position endLn endCh)) = + mkRealSrcSpan + (mkRealSrcLoc (FS.fsLit file) (fromIntegral $ startLn + 1) (fromIntegral $ startCh + 1)) + (mkRealSrcLoc (FS.fsLit file) (fromIntegral $ endLn + 1) (fromIntegral $ endCh + 1)) diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Simplify.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Simplify.hs new file mode 100644 index 0000000000..10eaae97c7 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Simplify.hs @@ -0,0 +1,113 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Wingman.Simplify + ( simplify + ) where + +import Data.Generics (GenericT, everywhere, mkT) +import Data.List.Extra (unsnoc) +import Data.Monoid (Endo (..)) +import Development.IDE.GHC.Compat +import GHC.SourceGen (var) +import GHC.SourceGen.Expr (lambda) +import Wingman.CodeGen.Utils +import Wingman.GHC (containsHsVar, fromPatCompat, pattern SingleLet) + + +------------------------------------------------------------------------------ +-- | A pattern over the otherwise (extremely) messy AST for lambdas. +pattern Lambda :: [Pat GhcPs] -> HsExpr GhcPs -> HsExpr GhcPs +pattern Lambda pats body <- + HsLam _ + MG {mg_alts = L _ [L _ + Match { m_pats = fmap fromPatCompat -> pats + , m_grhss = GRHSs {grhssGRHSs = [L _ ( + GRHS _ [] (L _ body))]} + }] + } + where + -- If there are no patterns to bind, just stick in the body + Lambda [] body = body + Lambda pats body = lambda pats body + + + +------------------------------------------------------------------------------ +-- | Simplify an expression. +simplify :: LHsExpr GhcPs -> LHsExpr GhcPs +simplify + = (!!3) -- Do three passes; this should be good enough for the limited + -- amount of gas we give to auto + . iterate (everywhere $ foldEndo + [ simplifyEtaReduce + , simplifyRemoveParens + , simplifyCompose + , simplifySingleLet + ]) + + +------------------------------------------------------------------------------ +-- | Like 'foldMap' but for endomorphisms. +foldEndo :: Foldable t => t (a -> a) -> a -> a +foldEndo = appEndo . foldMap Endo + + +------------------------------------------------------------------------------ +-- | Perform an eta reduction. For example, transforms @\x -> (f g) x@ into +-- @f g@. +simplifyEtaReduce :: GenericT +simplifyEtaReduce = mkT $ \case + Lambda + [VarPat _ (L _ pat)] + (HsVar _ (L _ a)) | pat == a -> + var "id" + Lambda + (unsnoc -> Just (pats, VarPat _ (L _ pat))) + (HsApp _ (L _ f) (L _ (HsVar _ (L _ a)))) + | pat == a + -- We can only perform this simplification if @pat@ is otherwise unused. + , not (containsHsVar pat f) -> + Lambda pats f + x -> x + +------------------------------------------------------------------------------ +-- | Eliminates the unnecessary binding in @let a = b in a@ +simplifySingleLet :: GenericT +simplifySingleLet = mkT $ \case + SingleLet bind [] val (HsVar _ (L _ a)) | a == bind -> val + x -> x + + +------------------------------------------------------------------------------ +-- | Perform an eta-reducing function composition. For example, transforms +-- @\x -> f (g (h x))@ into @f . g . h@. +simplifyCompose :: GenericT +simplifyCompose = mkT $ \case + Lambda + (unsnoc -> Just (pats, VarPat _ (L _ pat))) + (unroll -> (fs@(_:_), HsVar _ (L _ a))) + | pat == a + -- We can only perform this simplification if @pat@ is otherwise unused. + , not (containsHsVar pat fs) -> + Lambda pats (foldr1 (infixCall ".") fs) + x -> x + + +------------------------------------------------------------------------------ +-- | Removes unnecessary parentheses on any token that doesn't need them. +simplifyRemoveParens :: GenericT +simplifyRemoveParens = mkT $ \case + HsPar _ (L _ x) | isAtomicHsExpr x -> x + (x :: HsExpr GhcPs) -> x + + +------------------------------------------------------------------------------ +-- | Unrolls a right-associative function application of the form +-- @HsApp f (HsApp g (HsApp h x))@ into @([f, g, h], x)@. +unroll :: HsExpr GhcPs -> ([HsExpr GhcPs], HsExpr GhcPs) +unroll (HsPar _ (L _ x)) = unroll x +unroll (HsApp _ (L _ f) (L _ a)) = + let (fs, r) = unroll a + in (f : fs, r) +unroll x = ([], x) + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/StaticPlugin.hs b/plugins/hls-tactics-plugin/new/src/Wingman/StaticPlugin.hs new file mode 100644 index 0000000000..42065aa289 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/StaticPlugin.hs @@ -0,0 +1,111 @@ +{-# LANGUAGE CPP #-} + +module Wingman.StaticPlugin + ( staticPlugin + , metaprogramHoleName + , enableQuasiQuotes + , pattern WingmanMetaprogram + , pattern MetaprogramSyntax + ) where + +import Development.IDE.GHC.Compat +import Development.IDE.GHC.Compat.Util + +import Ide.Types + +import Data.Data +import Generics.SYB +#if __GLASGOW_HASKELL__ >= 900 +import GHC.Driver.Plugins (purePlugin) +#else +import Plugins (purePlugin) +#endif + +staticPlugin :: DynFlagsModifications +staticPlugin = mempty + { dynFlagsModifyGlobal = + \df -> allowEmptyCaseButWithWarning + $ flip gopt_unset Opt_SortBySubsumHoleFits + $ flip gopt_unset Opt_ShowValidHoleFits + $ df + { refLevelHoleFits = Just 0 + , maxRefHoleFits = Just 0 + , maxValidHoleFits = Just 0 + , staticPlugins = staticPlugins df <> [metaprogrammingPlugin] + } + , dynFlagsModifyParser = enableQuasiQuotes + } + + +pattern MetaprogramSourceText :: SourceText +pattern MetaprogramSourceText = SourceText "wingman-meta-program" + + +pattern WingmanMetaprogram :: FastString -> HsExpr p +pattern WingmanMetaprogram mp <- +#if __GLASGOW_HASKELL__ >= 900 + HsPragE _ (HsPragSCC _ MetaprogramSourceText (StringLiteral NoSourceText mp)) + (L _ ( HsVar _ _)) +#else + HsSCC _ MetaprogramSourceText (StringLiteral NoSourceText mp) + (L _ ( HsVar _ _)) +#endif + + +enableQuasiQuotes :: DynFlags -> DynFlags +enableQuasiQuotes = flip xopt_set QuasiQuotes + + +-- | Wingman wants to support destructing of empty cases, but these are a parse +-- error by default. So we want to enable 'EmptyCase', but then that leads to +-- silent errors without 'Opt_WarnIncompletePatterns'. +allowEmptyCaseButWithWarning :: DynFlags -> DynFlags +allowEmptyCaseButWithWarning = + flip xopt_set EmptyCase . flip wopt_set Opt_WarnIncompletePatterns + + +metaprogrammingPlugin :: StaticPlugin +metaprogrammingPlugin = + StaticPlugin $ PluginWithArgs pluginDefinition [] + where + pluginDefinition = defaultPlugin + { parsedResultAction = worker + , pluginRecompile = purePlugin + } + worker :: Monad m => [CommandLineOption] -> ModSummary -> HsParsedModule -> m HsParsedModule + worker _ _ pm = pure $ pm { hpm_module = addMetaprogrammingSyntax $ hpm_module pm } + +mkMetaprogram :: SrcSpan -> FastString -> HsExpr GhcPs +mkMetaprogram ss mp = +#if __GLASGOW_HASKELL__ >= 900 + HsPragE noExtField (HsPragSCC noExtField MetaprogramSourceText (StringLiteral NoSourceText mp)) +#else + HsSCC noExtField MetaprogramSourceText (StringLiteral NoSourceText mp) +#endif + $ L ss + $ HsVar noExtField + $ L ss + $ mkRdrUnqual metaprogramHoleName + +addMetaprogrammingSyntax :: Data a => a -> a +addMetaprogrammingSyntax = + everywhere $ mkT $ \case + L ss (MetaprogramSyntax mp) -> + L ss $ mkMetaprogram ss mp + (x :: LHsExpr GhcPs) -> x + +metaprogramHoleName :: OccName +metaprogramHoleName = mkVarOcc "_$metaprogram" + +pattern MetaprogramSyntax :: FastString -> HsExpr GhcPs +pattern MetaprogramSyntax mp <- + HsSpliceE _ (HsQuasiQuote _ _ (occNameString . rdrNameOcc -> "wingman") _ mp) + where + MetaprogramSyntax mp = + HsSpliceE noExtField $ + HsQuasiQuote + noExtField + (mkRdrUnqual $ mkVarOcc "splice") + (mkRdrUnqual $ mkVarOcc "wingman") + noSrcSpan + mp diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Tactics.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Tactics.hs new file mode 100644 index 0000000000..10d87722cd --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Tactics.hs @@ -0,0 +1,692 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} + +module Wingman.Tactics + ( module Wingman.Tactics + , runTactic + ) where + +import Control.Applicative (Alternative(empty), (<|>)) +import Control.Lens ((&), (%~), (<>~)) +import Control.Monad (filterM, unless) +import Control.Monad (when) +import Control.Monad.Extra (anyM) +import Control.Monad.Reader.Class (MonadReader (ask)) +import Control.Monad.State.Strict (StateT(..), runStateT, execStateT) +import Data.Bool (bool) +import Data.Foldable +import Data.Functor ((<&>)) +import Data.Generics.Labels () +import Data.List +import Data.List.Extra (dropEnd, takeEnd) +import qualified Data.Map as M +import Data.Maybe +import Data.Set (Set) +import qualified Data.Set as S +import Data.Traversable (for) +import Development.IDE.GHC.Compat hiding (empty) +import GHC.Exts +import GHC.SourceGen ((@@)) +import GHC.SourceGen.Expr +import Refinery.Tactic +import Refinery.Tactic.Internal +import Wingman.CodeGen +import Wingman.GHC +import Wingman.Judgements +import Wingman.Machinery +import Wingman.Naming +import Wingman.StaticPlugin (pattern MetaprogramSyntax) +import Wingman.Types + + +------------------------------------------------------------------------------ +-- | Use something in the hypothesis to fill the hole. +assumption :: TacticsM () +assumption = attemptOn (S.toList . allNames) assume + + +------------------------------------------------------------------------------ +-- | Use something named in the hypothesis to fill the hole. +assume :: OccName -> TacticsM () +assume name = rule $ \jdg -> do + case M.lookup name $ hyByName $ jHypothesis jdg of + Just (hi_type -> ty) -> do + unify ty $ jGoal jdg + pure $ + -- This slightly terrible construct is producing a mostly-empty + -- 'Synthesized'; but there is no monoid instance to do something more + -- reasonable for a default value. + (pure (noLoc $ var' name)) + { syn_trace = tracePrim $ "assume " <> occNameString name + , syn_used_vals = S.singleton name <> getAncestry jdg name + } + Nothing -> cut + + +------------------------------------------------------------------------------ +-- | Like 'apply', but uses an 'OccName' available in the context +-- or the module +use :: Saturation -> OccName -> TacticsM () +use sat occ = do + ctx <- ask + ty <- case lookupNameInContext occ ctx of + Just ty -> pure ty + Nothing -> CType <$> getOccNameType occ + apply sat $ createImportedHyInfo occ ty + + +recursion :: TacticsM () +-- TODO(sandy): This tactic doesn't fire for the @AutoThetaFix@ golden test, +-- presumably due to running afoul of 'requireConcreteHole'. Look into this! +recursion = requireConcreteHole $ tracing "recursion" $ do + defs <- getCurrentDefinitions + attemptOn (const defs) $ \(name, ty) -> markRecursion $ do + jdg <- goal + -- Peek allows us to look at the extract produced by this block. + peek + ( do + let hy' = recursiveHypothesis defs + ctx <- ask + localTactic (apply Saturated $ HyInfo name RecursivePrv ty) (introduce ctx hy') + <@> fmap (localTactic assumption . filterPosition name) [0..] + ) $ \ext -> do + let pat_vals = jPatHypothesis jdg + -- Make sure that the recursive call contains at least one already-bound + -- pattern value. This ensures it is structurally smaller, and thus + -- suggests termination. + case any (flip M.member pat_vals) $ syn_used_vals ext of + True -> Nothing + False -> Just UnhelpfulRecursion + + +restrictPositionForApplication :: TacticsM () -> TacticsM () -> TacticsM () +restrictPositionForApplication f app = do + -- NOTE(sandy): Safe use of head; context is guaranteed to have a defining + -- binding + name <- head . fmap fst <$> getCurrentDefinitions + f <@> + fmap + (localTactic app . filterPosition name) [0..] + + +------------------------------------------------------------------------------ +-- | Introduce a lambda binding every variable. +intros :: TacticsM () +intros = intros' IntroduceAllUnnamed + + +data IntroParams + = IntroduceAllUnnamed + | IntroduceOnlyNamed [OccName] + | IntroduceOnlyUnnamed Int + deriving stock (Eq, Ord, Show) + + +------------------------------------------------------------------------------ +-- | Introduce a lambda binding every variable. +intros' + :: IntroParams + -> TacticsM () +intros' params = rule $ \jdg -> do + let g = jGoal jdg + case tacticsSplitFunTy $ unCType g of + (_, _, [], _) -> cut -- failure $ GoalMismatch "intros" g + (_, _, scaledArgs, res) -> do + let args = fmap scaledThing scaledArgs + ctx <- ask + let gen_names = mkManyGoodNames (hyNamesInScope $ jEntireHypothesis jdg) args + occs = case params of + IntroduceAllUnnamed -> gen_names + IntroduceOnlyNamed names -> names + IntroduceOnlyUnnamed n -> take n gen_names + num_occs = length occs + top_hole = isTopHole ctx jdg + bindings = zip occs $ coerce args + bound_occs = fmap fst bindings + hy' = lambdaHypothesis top_hole bindings + jdg' = introduce ctx hy' + $ withNewGoal (CType $ mkVisFunTys (drop num_occs scaledArgs) res) jdg + ext <- newSubgoal jdg' + pure $ + ext + & #syn_trace %~ rose ("intros {" <> intercalate ", " (fmap show bound_occs) <> "}") + . pure + & #syn_scoped <>~ hy' + & #syn_val %~ noLoc . lambda (fmap bvar' bound_occs) . unLoc + + +------------------------------------------------------------------------------ +-- | Introduce a single lambda argument, and immediately destruct it. +introAndDestruct :: TacticsM () +introAndDestruct = do + hy <- fmap unHypothesis $ hyDiff $ intros' $ IntroduceOnlyUnnamed 1 + -- This case should never happen, but I'm validating instead of parsing. + -- Adding a log to be reminded if the invariant ever goes false. + -- + -- But note that this isn't a game-ending bug. In the worst case, we'll + -- accidentally bind too many variables, and incorrectly unify between them. + -- Which means some GADT cases that should be eliminated won't be --- not the + -- end of the world. + unless (length hy == 1) $ + traceMX "BUG: Introduced too many variables for introAndDestruct! Please report me if you see this! " hy + + for_ hy destruct + + +------------------------------------------------------------------------------ +-- | Case split, and leave holes in the matches. +destructAuto :: HyInfo CType -> TacticsM () +destructAuto hi = requireConcreteHole $ tracing "destruct(auto)" $ do + jdg <- goal + let subtactic = destructOrHomoAuto hi + case isPatternMatch $ hi_provenance hi of + True -> + pruning subtactic $ \jdgs -> + let getHyTypes = S.fromList . fmap hi_type . unHypothesis . jHypothesis + new_hy = foldMap getHyTypes jdgs + old_hy = getHyTypes jdg + in case S.null $ new_hy S.\\ old_hy of + True -> Just $ UnhelpfulDestruct $ hi_name hi + False -> Nothing + False -> subtactic + + +------------------------------------------------------------------------------ +-- | When running auto, in order to prune the auto search tree, we try +-- a homomorphic destruct whenever possible. If that produces any results, we +-- can probably just prune the other side. +destructOrHomoAuto :: HyInfo CType -> TacticsM () +destructOrHomoAuto hi = tracing "destructOrHomoAuto" $ do + jdg <- goal + let g = unCType $ jGoal jdg + ty = unCType $ hi_type hi + + attemptWhen + (rule $ destruct' False (\dc jdg -> + buildDataCon False jdg dc $ snd $ splitAppTys g) hi) + (rule $ destruct' False (const newSubgoal) hi) + $ case (splitTyConApp_maybe g, splitTyConApp_maybe ty) of + (Just (gtc, _), Just (tytc, _)) -> gtc == tytc + _ -> False + + +------------------------------------------------------------------------------ +-- | Case split, and leave holes in the matches. +destruct :: HyInfo CType -> TacticsM () +destruct hi = requireConcreteHole $ tracing "destruct(user)" $ + rule $ destruct' False (const newSubgoal) hi + + +------------------------------------------------------------------------------ +-- | Case split, and leave holes in the matches. Performs record punning. +destructPun :: HyInfo CType -> TacticsM () +destructPun hi = requireConcreteHole $ tracing "destructPun(user)" $ + rule $ destruct' True (const newSubgoal) hi + + +------------------------------------------------------------------------------ +-- | Case split, using the same data constructor in the matches. +homo :: HyInfo CType -> TacticsM () +homo hi = requireConcreteHole . tracing "homo" $ do + jdg <- goal + let g = jGoal jdg + + -- Ensure that every data constructor in the domain type is covered in the + -- codomain; otherwise 'homo' will produce an ill-typed program. + case uncoveredDataCons (coerce $ hi_type hi) (coerce g) of + Just uncovered_dcs -> + unless (S.null uncovered_dcs) $ + failure $ TacticPanic "Can't cover every datacon in domain" + _ -> failure $ TacticPanic "Unable to fetch datacons" + + rule + $ destruct' + False + (\dc jdg -> buildDataCon False jdg dc $ snd $ splitAppTys $ unCType $ jGoal jdg) + hi + + +------------------------------------------------------------------------------ +-- | LambdaCase split, and leave holes in the matches. +destructLambdaCase :: TacticsM () +destructLambdaCase = + tracing "destructLambdaCase" $ rule $ destructLambdaCase' False (const newSubgoal) + + +------------------------------------------------------------------------------ +-- | LambdaCase split, using the same data constructor in the matches. +homoLambdaCase :: TacticsM () +homoLambdaCase = + tracing "homoLambdaCase" $ + rule $ destructLambdaCase' False $ \dc jdg -> + buildDataCon False jdg dc + . snd + . splitAppTys + . unCType + $ jGoal jdg + + +newtype Saturation = Unsaturated Int + deriving (Eq, Ord, Show) + +pattern Saturated :: Saturation +pattern Saturated = Unsaturated 0 + + +apply :: Saturation -> HyInfo CType -> TacticsM () +apply (Unsaturated n) hi = tracing ("apply' " <> show (hi_name hi)) $ do + jdg <- goal + let g = jGoal jdg + ty = unCType $ hi_type hi + func = hi_name hi + ty' <- freshTyvars ty + let (_, theta, all_args, ret) = tacticsSplitFunTy ty' + saturated_args = dropEnd n all_args + unsaturated_args = takeEnd n all_args + rule $ \jdg -> do + unify g (CType $ mkVisFunTys unsaturated_args ret) + learnFromFundeps theta + ext + <- fmap unzipTrace + $ traverse ( newSubgoal + . blacklistingDestruct + . flip withNewGoal jdg + . CType + . scaledThing + ) saturated_args + pure $ + ext + & #syn_used_vals %~ (\x -> S.insert func x <> getAncestry jdg func) + & #syn_val %~ mkApply func . fmap unLoc + +application :: TacticsM () +application = overFunctions $ apply Saturated + + +------------------------------------------------------------------------------ +-- | Choose between each of the goal's data constructors. +split :: TacticsM () +split = tracing "split(user)" $ do + jdg <- goal + let g = jGoal jdg + case tacticsGetDataCons $ unCType g of + Nothing -> failure $ GoalMismatch "split" g + Just (dcs, _) -> choice $ fmap splitDataCon dcs + + +------------------------------------------------------------------------------ +-- | Choose between each of the goal's data constructors. Different than +-- 'split' because it won't split a data con if it doesn't result in any new +-- goals. +splitAuto :: TacticsM () +splitAuto = requireConcreteHole $ tracing "split(auto)" $ do + jdg <- goal + let g = jGoal jdg + case tacticsGetDataCons $ unCType g of + Nothing -> failure $ GoalMismatch "split" g + Just (dcs, _) -> do + case isSplitWhitelisted jdg of + True -> choice $ fmap splitDataCon dcs + False -> do + choice $ flip fmap dcs $ \dc -> requireNewHoles $ + splitDataCon dc + + +------------------------------------------------------------------------------ +-- | Like 'split', but only works if there is a single matching data +-- constructor for the goal. +splitSingle :: TacticsM () +splitSingle = tracing "splitSingle" $ do + jdg <- goal + let g = jGoal jdg + case tacticsGetDataCons $ unCType g of + Just ([dc], _) -> do + splitDataCon dc + _ -> failure $ GoalMismatch "splitSingle" g + +------------------------------------------------------------------------------ +-- | Like 'split', but prunes any data constructors which have holes. +obvious :: TacticsM () +obvious = tracing "obvious" $ do + pruning split $ bool (Just NoProgress) Nothing . null + + +------------------------------------------------------------------------------ +-- | Sorry leaves a hole in its extract +sorry :: TacticsM () +sorry = exact $ var' $ mkVarOcc "_" + + +------------------------------------------------------------------------------ +-- | Sorry leaves a hole in its extract +metaprogram :: TacticsM () +metaprogram = exact $ MetaprogramSyntax "" + + +------------------------------------------------------------------------------ +-- | Allow the given tactic to proceed if and only if it introduces holes that +-- have a different goal than current goal. +requireNewHoles :: TacticsM () -> TacticsM () +requireNewHoles m = do + jdg <- goal + pruning m $ \jdgs -> + case null jdgs || any (/= jGoal jdg) (fmap jGoal jdgs) of + True -> Nothing + False -> Just NoProgress + + +------------------------------------------------------------------------------ +-- | Attempt to instantiate the given ConLike to solve the goal. +-- +-- INVARIANT: Assumes the given ConLike is appropriate to construct the type +-- with. +splitConLike :: ConLike -> TacticsM () +splitConLike dc = + requireConcreteHole $ tracing ("splitDataCon:" <> show dc) $ rule $ \jdg -> do + let g = jGoal jdg + case splitTyConApp_maybe $ unCType g of + Just (_, apps) -> do + buildDataCon True (unwhitelistingSplit jdg) dc apps + Nothing -> cut -- failure $ GoalMismatch "splitDataCon" g + +------------------------------------------------------------------------------ +-- | Attempt to instantiate the given data constructor to solve the goal. +-- +-- INVARIANT: Assumes the given datacon is appropriate to construct the type +-- with. +splitDataCon :: DataCon -> TacticsM () +splitDataCon = splitConLike . RealDataCon + + +------------------------------------------------------------------------------ +-- | Perform a case split on each top-level argument. Used to implement the +-- "Destruct all function arguments" action. +destructAll :: TacticsM () +destructAll = do + jdg <- goal + let args = fmap fst + $ sortOn snd + $ mapMaybe (\(hi, prov) -> + case prov of + TopLevelArgPrv _ idx _ -> pure (hi, idx) + _ -> Nothing + ) + $ fmap (\hi -> (hi, hi_provenance hi)) + $ filter (isAlgType . unCType . hi_type) + $ unHypothesis + $ jHypothesis jdg + for_ args $ \arg -> do + subst <- getSubstForJudgement =<< goal + destruct $ fmap (coerce substTy subst) arg + +-------------------------------------------------------------------------------- +-- | User-facing tactic to implement "Use constructor " +userSplit :: OccName -> TacticsM () +userSplit occ = do + jdg <- goal + let g = jGoal jdg + -- TODO(sandy): It's smelly that we need to find the datacon to generate the + -- code action, send it as a string, and then look it up again. Can we push + -- this over LSP somehow instead? + case splitTyConApp_maybe $ unCType g of + Just (tc, _) -> do + case find (sloppyEqOccName occ . occName . dataConName) + $ tyConDataCons tc of + Just dc -> splitDataCon dc + Nothing -> failure $ NotInScope occ + Nothing -> failure $ NotInScope occ + + +------------------------------------------------------------------------------ +-- | @matching f@ takes a function from a judgement to a @Tactic@, and +-- then applies the resulting @Tactic@. +matching :: (Judgement -> TacticsM ()) -> TacticsM () +matching f = TacticT $ StateT $ \s -> runStateT (unTacticT $ f s) s + + +attemptOn :: (Judgement -> [a]) -> (a -> TacticsM ()) -> TacticsM () +attemptOn getNames tac = matching (choice . fmap tac . getNames) + + +localTactic :: TacticsM a -> (Judgement -> Judgement) -> TacticsM a +localTactic t f = do + TacticT $ StateT $ \jdg -> + runStateT (unTacticT t) $ f jdg + + +refine :: TacticsM () +refine = intros <%> splitSingle + + +auto' :: Int -> TacticsM () +auto' 0 = failure OutOfGas +auto' n = do + let loop = auto' (n - 1) + try intros + assumption <|> + choice + [ overFunctions $ \fname -> do + requireConcreteHole $ apply Saturated fname + loop + , overAlgebraicTerms $ \aname -> do + destructAuto aname + loop + , splitAuto >> loop + , recursion + ] + +overFunctions :: (HyInfo CType -> TacticsM ()) -> TacticsM () +overFunctions = + attemptOn $ filter (isFunction . unCType . hi_type) + . unHypothesis + . jHypothesis + +overAlgebraicTerms :: (HyInfo CType -> TacticsM ()) -> TacticsM () +overAlgebraicTerms = + attemptOn jAcceptableDestructTargets + + +allNames :: Judgement -> Set OccName +allNames = hyNamesInScope . jHypothesis + + +applyMethod :: Class -> PredType -> OccName -> TacticsM () +applyMethod cls df method_name = do + case find ((== method_name) . occName) $ classMethods cls of + Just method -> do + let (_, apps) = splitAppTys df + let ty = piResultTys (idType method) apps + apply Saturated $ HyInfo method_name (ClassMethodPrv $ Uniquely cls) $ CType ty + Nothing -> failure $ NotInScope method_name + + +applyByName :: OccName -> TacticsM () +applyByName name = do + g <- goal + choice $ unHypothesis (jHypothesis g) <&> \hi -> + case hi_name hi == name of + True -> apply Saturated hi + False -> empty + + +------------------------------------------------------------------------------ +-- | Make a function application where the function being applied itself is +-- a hole. +applyByType :: Type -> TacticsM () +applyByType ty = tracing ("applyByType " <> show ty) $ do + jdg <- goal + let g = jGoal jdg + ty' <- freshTyvars ty + let (_, _, args, ret) = tacticsSplitFunTy ty' + rule $ \jdg -> do + unify g (CType ret) + ext + <- fmap unzipTrace + $ traverse ( newSubgoal + . blacklistingDestruct + . flip withNewGoal jdg + . CType + . scaledThing + ) args + app <- newSubgoal . blacklistingDestruct $ withNewGoal (CType ty) jdg + pure $ + fmap noLoc $ + foldl' (@@) + <$> fmap unLoc app + <*> fmap (fmap unLoc) ext + + +------------------------------------------------------------------------------ +-- | Make an n-ary function call of the form +-- @(_ :: forall a b. a -> a -> b) _ _@. +nary :: Int -> TacticsM () +nary n = do + a <- newUnivar + b <- newUnivar + applyByType $ mkVisFunTys (replicate n $ unrestricted a) b + + +self :: TacticsM () +self = + fmap listToMaybe getCurrentDefinitions >>= \case + Just (self, _) -> useNameFromContext (apply Saturated) self + Nothing -> failure $ TacticPanic "no defining function" + + +------------------------------------------------------------------------------ +-- | Perform a catamorphism when destructing the given 'HyInfo'. This will +-- result in let binding, making values that call the defining function on each +-- destructed value. +cata :: HyInfo CType -> TacticsM () +cata hi = do + (_, _, calling_args, _) + <- tacticsSplitFunTy . unCType <$> getDefiningType + freshened_args <- traverse (freshTyvars . scaledThing) calling_args + diff <- hyDiff $ destruct hi + + -- For for every destructed term, check to see if it can unify with any of + -- the arguments to the calling function. If it doesn't, we don't try to + -- perform a cata on it. + unifiable_diff <- flip filterM (unHypothesis diff) $ \hi -> + flip anyM freshened_args $ \ty -> + canUnify (hi_type hi) $ CType ty + + rule $ + letForEach + (mkVarOcc . flip mappend "_c" . occNameString) + (\hi -> self >> commit (assume $ hi_name hi) assumption) + $ Hypothesis unifiable_diff + + +letBind :: [OccName] -> TacticsM () +letBind occs = do + jdg <- goal + occ_tys <- for occs + $ \occ + -> fmap (occ, ) + $ fmap (<$ jdg) + $ fmap CType newUnivar + rule $ nonrecLet occ_tys + + +------------------------------------------------------------------------------ +-- | Deeply nest an unsaturated function onto itself +nested :: OccName -> TacticsM () +nested = deepening . use (Unsaturated 1) + + +------------------------------------------------------------------------------ +-- | Repeatedly bind a tactic on its first hole +deep :: Int -> TacticsM () -> TacticsM () +deep 0 _ = pure () +deep n t = foldr1 bindOne $ replicate n t + + +------------------------------------------------------------------------------ +-- | Try 'deep' for arbitrary depths. +deepening :: TacticsM () -> TacticsM () +deepening t = + asum $ fmap (flip deep t) [0 .. 100] + + +bindOne :: TacticsM a -> TacticsM a -> TacticsM a +bindOne t t1 = t <@> [t1] + + +collapse :: TacticsM () +collapse = do + g <- goal + let terms = unHypothesis $ hyFilter ((jGoal g ==) . hi_type) $ jLocalHypothesis g + case terms of + [hi] -> assume $ hi_name hi + _ -> nary (length terms) <@> fmap (assume . hi_name) terms + + +with_arg :: TacticsM () +with_arg = rule $ \jdg -> do + let g = jGoal jdg + fresh_ty <- newUnivar + a <- newSubgoal $ withNewGoal (CType fresh_ty) jdg + f <- newSubgoal $ withNewGoal (coerce mkVisFunTys [unrestricted fresh_ty] g) jdg + pure $ fmap noLoc $ (@@) <$> fmap unLoc f <*> fmap unLoc a + + +------------------------------------------------------------------------------ +-- | Determine the difference in hypothesis due to running a tactic. Also, it +-- runs the tactic. +hyDiff :: TacticsM () -> TacticsM (Hypothesis CType) +hyDiff m = do + g <- unHypothesis . jEntireHypothesis <$> goal + let g_len = length g + m + g' <- unHypothesis . jEntireHypothesis <$> goal + pure $ Hypothesis $ take (length g' - g_len) g' + + +------------------------------------------------------------------------------ +-- | Attempt to run the given tactic in "idiom bracket" mode. For example, if +-- the current goal is +-- +-- (_ :: [r]) +-- +-- then @idiom apply@ will remove the applicative context, resulting in a hole: +-- +-- (_ :: r) +-- +-- and then use @apply@ to solve it. Let's say this results in: +-- +-- (f (_ :: a) (_ :: b)) +-- +-- Finally, @idiom@ lifts this back into the original applicative: +-- +-- (f <$> (_ :: [a]) <*> (_ :: [b])) +-- +-- Idiom will fail fast if the current goal doesn't have an applicative +-- instance. +idiom :: TacticsM () -> TacticsM () +idiom m = do + jdg <- goal + let hole = unCType $ jGoal jdg + when (isFunction hole) $ + failure $ GoalMismatch "idiom" $ jGoal jdg + case splitAppTy_maybe hole of + Just (applic, ty) -> do + minst <- getKnownInstance (mkClsOcc "Applicative") + . pure + $ applic + case minst of + Nothing -> failure $ GoalMismatch "idiom" $ CType applic + Just (_, _) -> do + rule $ \jdg -> do + expr <- subgoalWith (withNewGoal (CType ty) jdg) m + case unLoc $ syn_val expr of + HsApp{} -> pure $ fmap idiomize expr + RecordCon{} -> pure $ fmap idiomize expr + _ -> unsolvable $ GoalMismatch "idiom" $ jGoal jdg + rule $ newSubgoal . withModifiedGoal (CType . mkAppTy applic . unCType) + Nothing -> + failure $ GoalMismatch "idiom" $ jGoal jdg + +subgoalWith :: Judgement -> TacticsM () -> RuleM (Synthesized (LHsExpr GhcPs)) +subgoalWith jdg t = RuleT $ flip execStateT jdg $ unTacticT t + diff --git a/plugins/hls-tactics-plugin/new/src/Wingman/Types.hs b/plugins/hls-tactics-plugin/new/src/Wingman/Types.hs new file mode 100644 index 0000000000..621cc9752e --- /dev/null +++ b/plugins/hls-tactics-plugin/new/src/Wingman/Types.hs @@ -0,0 +1,562 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE UndecidableInstances #-} + +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module Wingman.Types + ( module Wingman.Types + , module Wingman.Debug + , OccName + , Name + , Type + , TyVar + , Span + ) where + +import Control.Lens hiding (Context) +import Control.Monad.Reader +import Control.Monad.State +import qualified Control.Monad.State.Strict as Strict +import Data.Coerce +import Data.Function +import Data.Generics (mkM, everywhereM, Data, Typeable) +import Data.Generics.Labels () +import Data.Generics.Product (field) +import Data.List.NonEmpty (NonEmpty (..)) +import Data.Semigroup +import Data.Set (Set) +import Data.Text (Text) +import qualified Data.Text as T +import Data.Tree +import Development.IDE (Range) +import Development.IDE.Core.UseStale +import Development.IDE.GHC.Compat hiding (Node) +import qualified Development.IDE.GHC.Compat.Util as Util +import Development.IDE.GHC.Orphans () +import GHC.Exts (fromString) +import GHC.Generics +import GHC.SourceGen (var) +import Refinery.ProofState +import Refinery.Tactic.Internal (TacticT(TacticT), RuleT (RuleT)) +import System.IO.Unsafe (unsafePerformIO) +import Wingman.Debug +import Data.IORef + + +------------------------------------------------------------------------------ +-- | The list of tactics exposed to the outside world. These are attached to +-- actual tactics via 'commandTactic' and are contextually provided to the +-- editor via 'commandProvider'. +data TacticCommand + = Auto + | Intros + | IntroAndDestruct + | Destruct + | DestructPun + | Homomorphism + | DestructLambdaCase + | HomomorphismLambdaCase + | DestructAll + | UseDataCon + | Refine + | BeginMetaprogram + | RunMetaprogram + deriving (Eq, Ord, Show, Enum, Bounded) + +-- | Generate a title for the command. +tacticTitle :: TacticCommand -> T.Text -> T.Text +tacticTitle = (mappend "Wingman: " .) . go + where + go Auto _ = "Attempt to fill hole" + go Intros _ = "Introduce lambda" + go IntroAndDestruct _ = "Introduce and destruct term" + go Destruct var = "Case split on " <> var + go DestructPun var = "Split on " <> var <> " with NamedFieldPuns" + go Homomorphism var = "Homomorphic case split on " <> var + go DestructLambdaCase _ = "Lambda case split" + go HomomorphismLambdaCase _ = "Homomorphic lambda case split" + go DestructAll _ = "Split all function arguments" + go UseDataCon dcon = "Use constructor " <> dcon + go Refine _ = "Refine hole" + go BeginMetaprogram _ = "Use custom tactic block" + go RunMetaprogram _ = "Run custom tactic" + + +------------------------------------------------------------------------------ +-- | Plugin configuration for tactics +data Config = Config + { cfg_max_use_ctor_actions :: Int + , cfg_timeout_seconds :: Int + , cfg_auto_gas :: Int + , cfg_proofstate_styling :: Bool + } + deriving (Eq, Ord, Show) + +emptyConfig :: Config +emptyConfig = Config + { cfg_max_use_ctor_actions = 5 + , cfg_timeout_seconds = 2 + , cfg_auto_gas = 4 + , cfg_proofstate_styling = True + } + +------------------------------------------------------------------------------ +-- | A wrapper around 'Type' which supports equality and ordering. +newtype CType = CType { unCType :: Type } + deriving stock (Data, Typeable) + +instance Eq CType where + (==) = eqType `on` unCType + +instance Ord CType where + compare = nonDetCmpType `on` unCType + +instance Show CType where + show = unsafeRender . unCType + +instance Show Name where + show = unsafeRender + +instance Show Type where + show = unsafeRender + +instance Show Var where + show = unsafeRender + +instance Show TCvSubst where + show = unsafeRender + +instance Show DataCon where + show = unsafeRender + +instance Show Class where + show = unsafeRender + +instance Show (HsExpr GhcPs) where + show = unsafeRender + +instance Show (HsExpr GhcTc) where + show = unsafeRender + +instance Show (HsDecl GhcPs) where + show = unsafeRender + +instance Show (Pat GhcPs) where + show = unsafeRender + +instance Show (LHsSigType GhcPs) where + show = unsafeRender + +instance Show TyCon where + show = unsafeRender + +instance Show ConLike where + show = unsafeRender + +instance Show LexicalFixity where + show = unsafeRender + + +------------------------------------------------------------------------------ +-- | The state that should be shared between subgoals. Extracts move towards +-- the root, judgments move towards the leaves, and the state moves *sideways*. +data TacticState = TacticState + { ts_skolems :: !(Set TyVar) + -- ^ The known skolems. + , ts_unifier :: !TCvSubst + , ts_unique_gen :: !UniqSupply + } deriving stock (Show, Generic) + +instance Show UniqSupply where + show _ = "" + + +------------------------------------------------------------------------------ +-- | A 'UniqSupply' to use in 'defaultTacticState' +unsafeDefaultUniqueSupply :: UniqSupply +unsafeDefaultUniqueSupply = + unsafePerformIO $ mkSplitUniqSupply 'w' +{-# NOINLINE unsafeDefaultUniqueSupply #-} + + +defaultTacticState :: TacticState +defaultTacticState = + TacticState + { ts_skolems = mempty + , ts_unifier = emptyTCvSubst + , ts_unique_gen = unsafeDefaultUniqueSupply + } + + +------------------------------------------------------------------------------ +-- | Generate a new 'Unique' +freshUnique :: MonadState TacticState m => m Util.Unique +freshUnique = do + (uniq, supply) <- gets $ takeUniqFromSupply . ts_unique_gen + modify' $! field @"ts_unique_gen" .~ supply + pure uniq + + +------------------------------------------------------------------------------ +-- | Describes where hypotheses came from. Used extensively to prune stupid +-- solutions from the search space. +data Provenance + = -- | An argument given to the topmost function that contains the current + -- hole. Recursive calls are restricted to values whose provenance lines up + -- with the same argument. + TopLevelArgPrv + OccName -- ^ Binding function + Int -- ^ Argument Position + Int -- ^ of how many arguments total? + -- | A binding created in a pattern match. + | PatternMatchPrv PatVal + -- | A class method from the given context. + | ClassMethodPrv + (Uniquely Class) -- ^ Class + -- | A binding explicitly written by the user. + | UserPrv + -- | A binding explicitly imported by the user. + | ImportPrv + -- | The recursive hypothesis. Present only in the context of the recursion + -- tactic. + | RecursivePrv + -- | A hypothesis which has been disallowed for some reason. It's important + -- to keep these in the hypothesis set, rather than filtering it, in order + -- to continue tracking downstream provenance. + | DisallowedPrv DisallowReason Provenance + deriving stock (Eq, Show, Generic, Ord, Data, Typeable) + + +------------------------------------------------------------------------------ +-- | Why was a hypothesis disallowed? +data DisallowReason + = WrongBranch Int + | Shadowed + | RecursiveCall + | AlreadyDestructed + deriving stock (Eq, Show, Generic, Ord, Data, Typeable) + + +------------------------------------------------------------------------------ +-- | Provenance of a pattern value. +data PatVal = PatVal + { pv_scrutinee :: Maybe OccName + -- ^ Original scrutinee which created this PatVal. Nothing, for lambda + -- case. + , pv_ancestry :: Set OccName + -- ^ The set of values which had to be destructed to discover this term. + -- Always contains the scrutinee. + , pv_datacon :: Uniquely ConLike + -- ^ The datacon which introduced this term. + , pv_position :: Int + -- ^ The position of this binding in the datacon's arguments. + } deriving stock (Eq, Show, Generic, Ord, Data, Typeable) + + +------------------------------------------------------------------------------ +-- | A wrapper which uses a 'Uniquable' constraint for providing 'Eq' and 'Ord' +-- instances. +newtype Uniquely a = Uniquely { getViaUnique :: a } + deriving Show via a + deriving stock (Data, Typeable) + +instance Util.Uniquable a => Eq (Uniquely a) where + (==) = (==) `on` Util.getUnique . getViaUnique + +instance Util.Uniquable a => Ord (Uniquely a) where + compare = Util.nonDetCmpUnique `on` Util.getUnique . getViaUnique + + +-- NOTE(sandy): The usage of list here is mostly for convenience, but if it's +-- ever changed, make sure to correspondingly update +-- 'jAcceptableDestructTargets' so that it correctly identifies newly +-- introduced terms. +newtype Hypothesis a = Hypothesis + { unHypothesis :: [HyInfo a] + } + deriving stock (Functor, Eq, Show, Generic, Ord, Data, Typeable) + deriving newtype (Semigroup, Monoid) + + +------------------------------------------------------------------------------ +-- | The provenance and type of a hypothesis term. +data HyInfo a = HyInfo + { hi_name :: OccName + , hi_provenance :: Provenance + , hi_type :: a + } + deriving stock (Functor, Eq, Show, Generic, Ord, Data, Typeable) + + +------------------------------------------------------------------------------ +-- | Map a function over the provenance. +overProvenance :: (Provenance -> Provenance) -> HyInfo a -> HyInfo a +overProvenance f (HyInfo name prv ty) = HyInfo name (f prv) ty + + +------------------------------------------------------------------------------ +-- | The current bindings and goal for a hole to be filled by refinery. +data Judgement' a = Judgement + { _jHypothesis :: !(Hypothesis a) + , _jBlacklistDestruct :: !Bool + , _jWhitelistSplit :: !Bool + , _jIsTopHole :: !Bool + , _jGoal :: !a + , j_coercion :: TCvSubst + } + deriving stock (Generic, Functor, Show) + +type Judgement = Judgement' CType + + +newtype ExtractM a = ExtractM { unExtractM :: ReaderT Context IO a } + deriving newtype (Functor, Applicative, Monad, MonadReader Context) + +------------------------------------------------------------------------------ +-- | Used to ensure hole names are unique across invocations of runTactic +globalHoleRef :: IORef Int +globalHoleRef = unsafePerformIO $ newIORef 10 +{-# NOINLINE globalHoleRef #-} + +instance MonadExtract Int (Synthesized (LHsExpr GhcPs)) TacticError TacticState ExtractM where + hole = do + u <- lift $ ExtractM $ lift $ + readIORef globalHoleRef <* modifyIORef' globalHoleRef (+ 1) + pure + ( u + , pure . noLoc $ var $ fromString $ occNameString $ occName $ mkMetaHoleName u + ) + + unsolvableHole _ = hole + + +instance MonadReader r m => MonadReader r (TacticT jdg ext err s m) where + ask = TacticT $ lift $ Effect $ asks pure + local f (TacticT m) = TacticT $ Strict.StateT $ \jdg -> + Effect $ local f $ pure $ Strict.runStateT m jdg + +instance MonadReader r m => MonadReader r (RuleT jdg ext err s m) where + ask = RuleT $ Effect $ asks Axiom + local f (RuleT m) = RuleT $ Effect $ local f $ pure m + +mkMetaHoleName :: Int -> RdrName +mkMetaHoleName u = mkRdrUnqual $ mkVarOcc $ "_" <> show (Util.mkUnique 'w' u) + +instance MetaSubst Int (Synthesized (LHsExpr GhcPs)) where + -- TODO(sandy): This join is to combine the synthesizeds + substMeta u val a = join $ a <&> + everywhereM (mkM $ \case + (L _ (HsVar _ (L _ name))) + | name == mkMetaHoleName u -> val + (t :: LHsExpr GhcPs) -> pure t) + + +------------------------------------------------------------------------------ +-- | Reasons a tactic might fail. +data TacticError + = OutOfGas + | GoalMismatch String CType + | NoProgress + | NoApplicableTactic + | UnhelpfulRecursion + | UnhelpfulDestruct OccName + | TooPolymorphic + | NotInScope OccName + | TacticPanic String + deriving (Eq) + +instance Show TacticError where + show OutOfGas = "Auto ran out of gas" + show (GoalMismatch tac (CType typ)) = + mconcat + [ "The tactic " + , tac + , " doesn't apply to goal type " + , unsafeRender typ + ] + show NoProgress = + "Unable to make progress" + show NoApplicableTactic = + "No tactic could be applied" + show UnhelpfulRecursion = + "Recursion wasn't productive" + show (UnhelpfulDestruct n) = + "Destructing patval " <> show n <> " leads to no new types" + show TooPolymorphic = + "The tactic isn't applicable because the goal is too polymorphic" + show (NotInScope name) = + "Tried to do something with the out of scope name " <> show name + show (TacticPanic err) = + "Tactic panic: " <> err + + +------------------------------------------------------------------------------ +type TacticsM = TacticT Judgement (Synthesized (LHsExpr GhcPs)) TacticError TacticState ExtractM +type RuleM = RuleT Judgement (Synthesized (LHsExpr GhcPs)) TacticError TacticState ExtractM +type Rule = RuleM (Synthesized (LHsExpr GhcPs)) + +type Trace = Rose String + +------------------------------------------------------------------------------ +-- | The extract for refinery. Represents a "synthesized attribute" in the +-- context of attribute grammars. In essence, 'Synthesized' describes +-- information we'd like to pass from leaves of the tactics search upwards. +-- This includes the actual AST we've generated (in 'syn_val'). +data Synthesized a = Synthesized + { syn_trace :: Trace + -- ^ A tree describing which tactics were used produce the 'syn_val'. + -- Mainly for debugging when you get the wrong answer, to see the other + -- things it tried. + , syn_scoped :: Hypothesis CType + -- ^ All of the bindings created to produce the 'syn_val'. + , syn_used_vals :: Set OccName + -- ^ The values used when synthesizing the 'syn_val'. + , syn_recursion_count :: Sum Int + -- ^ The number of recursive calls + , syn_val :: a + } + deriving stock (Eq, Show, Functor, Foldable, Traversable, Generic, Data, Typeable) + +instance Monad Synthesized where + return = pure + Synthesized tr1 sc1 uv1 rc1 a >>= f = + case f a of + Synthesized tr2 sc2 uv2 rc2 b -> + Synthesized + { syn_trace = tr1 <> tr2 + , syn_scoped = sc1 <> sc2 + , syn_used_vals = uv1 <> uv2 + , syn_recursion_count = rc1 <> rc2 + , syn_val = b + } + +mapTrace :: (Trace -> Trace) -> Synthesized a -> Synthesized a +mapTrace f (Synthesized tr sc uv rc a) = Synthesized (f tr) sc uv rc a + + +------------------------------------------------------------------------------ +-- | This might not be lawful, due to the semigroup on 'Trace' maybe not being +-- lawful. But that's only for debug output, so it's not anything I'm concerned +-- about. +instance Applicative Synthesized where + pure = Synthesized mempty mempty mempty mempty + Synthesized tr1 sc1 uv1 rc1 f <*> Synthesized tr2 sc2 uv2 rc2 a = + Synthesized (tr1 <> tr2) (sc1 <> sc2) (uv1 <> uv2) (rc1 <> rc2) $ f a + + +------------------------------------------------------------------------------ +-- | The Reader context of tactics and rules +data Context = Context + { ctxDefiningFuncs :: [(OccName, CType)] + -- ^ The functions currently being defined + , ctxModuleFuncs :: [(OccName, CType)] + -- ^ Everything defined in the current module + , ctxConfig :: Config + , ctxInstEnvs :: InstEnvs + , ctxFamInstEnvs :: FamInstEnvs + , ctxTheta :: Set CType + , ctx_hscEnv :: HscEnv + , ctx_occEnv :: OccEnv [GlobalRdrElt] + , ctx_module :: Module + } + +instance Show Context where + show Context{..} = mconcat + [ "Context " + , showsPrec 10 ctxDefiningFuncs "" + , showsPrec 10 ctxModuleFuncs "" + , showsPrec 10 ctxConfig "" + , showsPrec 10 ctxTheta "" + ] + + +------------------------------------------------------------------------------ +-- | An empty context +emptyContext :: Context +emptyContext + = Context + { ctxDefiningFuncs = mempty + , ctxModuleFuncs = mempty + , ctxConfig = emptyConfig + , ctxFamInstEnvs = mempty + , ctxInstEnvs = InstEnvs mempty mempty mempty + , ctxTheta = mempty + , ctx_hscEnv = error "empty hsc env from emptyContext" + , ctx_occEnv = emptyOccEnv + , ctx_module = error "empty module from emptyContext" + } + + +newtype Rose a = Rose (Tree a) + deriving stock (Eq, Functor, Generic, Data, Typeable) + +instance Show (Rose String) where + show = unlines . dropEveryOther . lines . drawTree . coerce + +dropEveryOther :: [a] -> [a] +dropEveryOther [] = [] +dropEveryOther [a] = [a] +dropEveryOther (a : _ : as) = a : dropEveryOther as + +------------------------------------------------------------------------------ +-- | This might not be lawful! I didn't check, and it feels sketchy. +instance (Eq a, Monoid a) => Semigroup (Rose a) where + Rose (Node a as) <> Rose (Node b bs) = Rose $ Node (a <> b) (as <> bs) + sconcat (a :| as) = rose mempty $ a : as + +instance (Eq a, Monoid a) => Monoid (Rose a) where + mempty = Rose $ Node mempty mempty + +rose :: (Eq a, Monoid a) => a -> [Rose a] -> Rose a +rose a [Rose (Node a' rs)] | a' == mempty = Rose $ Node a rs +rose a rs = Rose $ Node a $ coerce rs + + +------------------------------------------------------------------------------ +-- | The results of 'Wingman.Machinery.runTactic' +data RunTacticResults = RunTacticResults + { rtr_trace :: Trace + , rtr_extract :: LHsExpr GhcPs + , rtr_subgoals :: [Judgement] + , rtr_other_solns :: [Synthesized (LHsExpr GhcPs)] + , rtr_jdg :: Judgement + , rtr_ctx :: Context + , rtr_timed_out :: Bool + } deriving Show + + +data AgdaMatch = AgdaMatch + { amPats :: [Pat GhcPs] + , amBody :: HsExpr GhcPs + } + deriving (Show) + + +data UserFacingMessage + = NotEnoughGas + | TacticErrors + | TimedOut + | NothingToDo + | InfrastructureError Text + deriving Eq + +instance Show UserFacingMessage where + show NotEnoughGas = "Wingman ran out of gas when trying to find a solution. \nTry increasing the `auto_gas` setting." + show TacticErrors = "Wingman couldn't find a solution" + show TimedOut = "Wingman timed out while finding a solution. \nYou might get a better result if you increase the timeout duration." + show NothingToDo = "Nothing to do" + show (InfrastructureError t) = "Internal error: " <> T.unpack t + + +data HoleSort = Hole | Metaprogram T.Text + deriving (Eq, Ord, Show) + +data HoleJudgment = HoleJudgment + { hj_range :: Tracked 'Current Range + , hj_jdg :: Judgement + , hj_ctx :: Context + , hj_dflags :: DynFlags + , hj_hole_sort :: HoleSort + } + diff --git a/plugins/hls-tactics-plugin/new/test/AutoTupleSpec.hs b/plugins/hls-tactics-plugin/new/test/AutoTupleSpec.hs new file mode 100644 index 0000000000..11ba11e2ae --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/AutoTupleSpec.hs @@ -0,0 +1,57 @@ +{-# LANGUAGE NumDecimals #-} + +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module AutoTupleSpec where + +import Control.Monad (replicateM) +import Control.Monad.State (evalState) +import Data.Either (isRight) +import Development.IDE.GHC.Compat.Core ( mkVarOcc, mkBoxedTupleTy ) +import System.IO.Unsafe +import Test.Hspec +import Test.QuickCheck +import Wingman.Judgements (mkFirstJudgement) +import Wingman.Machinery +import Wingman.Tactics (auto') +import Wingman.Types + + +spec :: Spec +spec = describe "auto for tuple" $ do + it "should always be able to discover an auto solution" $ do + property $ do + -- Pick some number of variables + n <- choose (1, 7) + let vars = flip evalState defaultTacticState + $ replicateM n newUnivar + -- Pick a random ordering + in_vars <- shuffle vars + -- Randomly associate them into tuple types + in_type <- mkBoxedTupleTy + . fmap mkBoxedTupleTy + <$> randomGroups in_vars + out_type <- mkBoxedTupleTy + . fmap mkBoxedTupleTy + <$> randomGroups vars + pure $ + -- We should always be able to find a solution + unsafePerformIO + (runTactic + 2e6 + emptyContext + (mkFirstJudgement + emptyContext + (Hypothesis $ pure $ HyInfo (mkVarOcc "x") UserPrv $ CType in_type) + True + out_type) + (auto' $ n * 2)) `shouldSatisfy` isRight + + +randomGroups :: [a] -> Gen [[a]] +randomGroups [] = pure [] +randomGroups as = do + n <- choose (1, length as) + (:) <$> pure (take n as) + <*> randomGroups (drop n as) + diff --git a/plugins/hls-tactics-plugin/new/test/CodeAction/AutoSpec.hs b/plugins/hls-tactics-plugin/new/test/CodeAction/AutoSpec.hs new file mode 100644 index 0000000000..4075183ee6 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/CodeAction/AutoSpec.hs @@ -0,0 +1,92 @@ +{-# LANGUAGE OverloadedStrings #-} + +module CodeAction.AutoSpec where + +import Wingman.Types +import Test.Hspec +import Utils + + +spec :: Spec +spec = do + let autoTest = goldenTest Auto "" + autoTestNoWhitespace = goldenTestNoWhitespace Auto "" + + describe "golden" $ do + autoTest 11 8 "AutoSplitGADT" + autoTest 2 11 "GoldenEitherAuto" + autoTest 4 12 "GoldenJoinCont" + autoTest 3 11 "GoldenIdentityFunctor" + autoTest 7 11 "GoldenIdTypeFam" + autoTest 2 15 "GoldenEitherHomomorphic" + autoTest 2 8 "GoldenNote" + autoTest 2 12 "GoldenPureList" + autoTest 2 12 "GoldenListFmap" + autoTest 2 13 "GoldenFromMaybe" + autoTest 2 10 "GoldenFoldr" + autoTest 2 8 "GoldenSwap" + autoTest 4 11 "GoldenFmapTree" + autoTest 7 13 "GoldenGADTAuto" + autoTest 2 12 "GoldenSwapMany" + autoTest 4 12 "GoldenBigTuple" + autoTest 2 10 "GoldenShow" + autoTest 2 15 "GoldenShowCompose" + autoTest 2 8 "GoldenShowMapChar" + autoTest 7 8 "GoldenSuperclass" + autoTest 2 12 "GoldenSafeHead" + autoTest 2 12 "FmapBoth" + autoTest 7 8 "RecordCon" + autoTest 6 8 "NewtypeRecord" + autoTest 2 14 "FmapJoin" + autoTest 2 9 "Fgmap" + autoTest 4 19 "FmapJoinInLet" + autoTest 9 12 "AutoEndo" + autoTest 2 16 "AutoEmptyString" + autoTest 7 35 "AutoPatSynUse" + autoTest 2 28 "AutoZip" + autoTest 2 17 "AutoInfixApply" + autoTest 2 19 "AutoInfixApplyMany" + autoTest 2 25 "AutoInfixInfix" + autoTest 19 12 "AutoTypeLevel" + autoTest 11 9 "AutoForallClassMethod" + autoTest 2 8 "AutoUnusedPatternMatch" + + failing "flaky in CI" $ + autoTest 2 11 "GoldenApplicativeThen" + + failing "not enough auto gas" $ + autoTest 5 18 "GoldenFish" + + describe "theta" $ do + autoTest 12 10 "AutoThetaFix" + autoTest 7 27 "AutoThetaRankN" + autoTest 6 10 "AutoThetaGADT" + autoTest 6 8 "AutoThetaGADTDestruct" + autoTest 4 8 "AutoThetaEqCtx" + autoTest 6 10 "AutoThetaEqGADT" + autoTest 6 8 "AutoThetaEqGADTDestruct" + autoTest 6 10 "AutoThetaRefl" + autoTest 6 8 "AutoThetaReflDestruct" + autoTest 19 30 "AutoThetaMultipleUnification" + autoTest 16 9 "AutoThetaSplitUnification" + + describe "known" $ do + autoTest 25 13 "GoldenArbitrary" + autoTest 6 13 "GoldenArbitrarySingleConstructor" + autoTestNoWhitespace + 6 10 "KnownBigSemigroup" + autoTest 4 10 "KnownThetaSemigroup" + autoTest 6 10 "KnownCounterfactualSemigroup" + autoTest 10 10 "KnownModuleInstanceSemigroup" + autoTest 4 22 "KnownDestructedSemigroup" + autoTest 4 10 "KnownMissingSemigroup" + autoTest 7 12 "KnownMonoid" + autoTest 7 12 "KnownPolyMonoid" + autoTest 7 12 "KnownMissingMonoid" + + + describe "messages" $ do + mkShowMessageTest Auto "" 2 8 "MessageForallA" TacticErrors + mkShowMessageTest Auto "" 7 8 "MessageCantUnify" TacticErrors + mkShowMessageTest Auto "" 12 8 "MessageNotEnoughGas" NotEnoughGas + diff --git a/plugins/hls-tactics-plugin/new/test/CodeAction/DestructAllSpec.hs b/plugins/hls-tactics-plugin/new/test/CodeAction/DestructAllSpec.hs new file mode 100644 index 0000000000..488fb3ebad --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/CodeAction/DestructAllSpec.hs @@ -0,0 +1,38 @@ +{-# LANGUAGE OverloadedStrings #-} + +module CodeAction.DestructAllSpec where + +import Wingman.Types +import Test.Hspec +import Utils + + +spec :: Spec +spec = do + let destructAllTest = goldenTest DestructAll "" + describe "provider" $ do + mkTest + "Requires args on lhs of =" + "DestructAllProvider" 3 21 + [ (not, DestructAll, "") + ] + mkTest + "Can't be a non-top-hole" + "DestructAllProvider" 8 19 + [ (not, DestructAll, "") + , (id, Destruct, "a") + , (id, Destruct, "b") + ] + mkTest + "Provides a destruct all otherwise" + "DestructAllProvider" 12 22 + [ (id, DestructAll, "") + ] + + describe "golden" $ do + destructAllTest 2 11 "DestructAllAnd" + destructAllTest 4 23 "DestructAllMany" + destructAllTest 2 18 "DestructAllNonVarTopMatch" + destructAllTest 2 18 "DestructAllFunc" + destructAllTest 19 18 "DestructAllGADTEvidence" + diff --git a/plugins/hls-tactics-plugin/new/test/CodeAction/DestructPunSpec.hs b/plugins/hls-tactics-plugin/new/test/CodeAction/DestructPunSpec.hs new file mode 100644 index 0000000000..7d17aa1d2c --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/CodeAction/DestructPunSpec.hs @@ -0,0 +1,20 @@ +{-# LANGUAGE OverloadedStrings #-} + +module CodeAction.DestructPunSpec where + +import Wingman.Types +import Test.Hspec +import Utils + + +spec :: Spec +spec = do + let destructTest = goldenTest DestructPun + + describe "golden" $ do + destructTest "x" 4 9 "PunSimple" + destructTest "x" 6 10 "PunMany" + destructTest "x" 11 11 "PunGADT" + destructTest "x" 17 11 "PunManyGADT" + destructTest "x" 4 12 "PunShadowing" + diff --git a/plugins/hls-tactics-plugin/new/test/CodeAction/DestructSpec.hs b/plugins/hls-tactics-plugin/new/test/CodeAction/DestructSpec.hs new file mode 100644 index 0000000000..2251abfeb2 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/CodeAction/DestructSpec.hs @@ -0,0 +1,120 @@ +{-# LANGUAGE OverloadedStrings #-} + +module CodeAction.DestructSpec where + +import Wingman.Types +import Test.Hspec +import Utils + + +spec :: Spec +spec = do + let destructTest = goldenTest Destruct + + describe "golden" $ do + destructTest "gadt" 7 17 "GoldenGADTDestruct" + destructTest "gadt" 8 17 "GoldenGADTDestructCoercion" + destructTest "a" 7 25 "SplitPattern" + destructTest "a" 6 18 "DestructPun" + destructTest "fp" 31 14 "DestructCthulhu" + destructTest "b" 7 10 "DestructTyFam" + destructTest "b" 7 10 "DestructDataFam" + destructTest "b" 17 10 "DestructTyToDataFam" + destructTest "t" 6 10 "DestructInt" + + describe "layout" $ do + destructTest "b" 4 3 "LayoutBind" + destructTest "b" 2 15 "LayoutDollarApp" + destructTest "b" 2 18 "LayoutOpApp" + destructTest "b" 2 14 "LayoutLam" + destructTest "x" 11 15 "LayoutSplitWhere" + destructTest "x" 3 12 "LayoutSplitClass" + destructTest "b" 3 9 "LayoutSplitGuard" + destructTest "b" 4 13 "LayoutSplitLet" + destructTest "a" 4 7 "LayoutSplitIn" + destructTest "a" 4 31 "LayoutSplitViewPat" + destructTest "a" 7 17 "LayoutSplitPattern" + destructTest "a" 8 26 "LayoutSplitPatSyn" + + describe "providers" $ do + mkTest + "Produces destruct and homomorphism code actions" + "T2" 2 21 + [ (id, Destruct, "eab") + , (id, Homomorphism, "eab") + , (not, DestructPun, "eab") + ] + + mkTest + "Won't suggest homomorphism on the wrong type" + "T2" 8 8 + [ (not, Homomorphism, "global") + ] + + mkTest + "Produces (homomorphic) lambdacase code actions" + "T3" 4 24 + [ (id, HomomorphismLambdaCase, "") + , (id, DestructLambdaCase, "") + ] + + mkTest + "Produces lambdacase code actions" + "T3" 7 13 + [ (id, DestructLambdaCase, "") + ] + + mkTest + "Doesn't suggest lambdacase without -XLambdaCase" + "T2" 11 25 + [ (not, DestructLambdaCase, "") + ] + + mkTest + "Doesn't suggest destruct if already destructed" + "ProvideAlreadyDestructed" 6 18 + [ (not, Destruct, "x") + ] + + mkTest + "...but does suggest destruct if destructed in a different branch" + "ProvideAlreadyDestructed" 9 7 + [ (id, Destruct, "x") + ] + + mkTest + "Doesn't suggest destruct on class methods" + "ProvideLocalHyOnly" 2 12 + [ (not, Destruct, "mempty") + ] + + mkTest + "Suggests homomorphism if the domain is bigger than the codomain" + "ProviderHomomorphism" 12 13 + [ (id, Homomorphism, "g") + ] + + mkTest + "Doesn't suggest homomorphism if the domain is smaller than the codomain" + "ProviderHomomorphism" 15 14 + [ (not, Homomorphism, "g") + , (id, Destruct, "g") + ] + + mkTest + "Suggests lambda homomorphism if the domain is bigger than the codomain" + "ProviderHomomorphism" 18 14 + [ (id, HomomorphismLambdaCase, "") + ] + + mkTest + "Doesn't suggest lambda homomorphism if the domain is smaller than the codomain" + "ProviderHomomorphism" 21 15 + [ (not, HomomorphismLambdaCase, "") + , (id, DestructLambdaCase, "") + ] + + -- test layouts that maintain user-written fixities + destructTest "b" 3 13 "LayoutInfixKeep" + destructTest "b" 2 12 "LayoutPrefixKeep" + diff --git a/plugins/hls-tactics-plugin/new/test/CodeAction/IntroDestructSpec.hs b/plugins/hls-tactics-plugin/new/test/CodeAction/IntroDestructSpec.hs new file mode 100644 index 0000000000..5c3b809c1d --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/CodeAction/IntroDestructSpec.hs @@ -0,0 +1,36 @@ +{-# LANGUAGE OverloadedStrings #-} + +module CodeAction.IntroDestructSpec where + +import Wingman.Types +import Test.Hspec +import Utils + + +spec :: Spec +spec = do + let test l c = goldenTest IntroAndDestruct "" l c + . mappend "IntroDestruct" + + describe "golden" $ do + test 4 5 "One" + test 2 5 "Many" + test 4 11 "LetBinding" + + describe "provider" $ do + mkTest + "Can intro and destruct an algebraic ty" + "IntroDestructProvider" 2 12 + [ (id, IntroAndDestruct, "") + ] + mkTest + "Won't intro and destruct a non-algebraic ty" + "IntroDestructProvider" 5 12 + [ (not, IntroAndDestruct, "") + ] + mkTest + "Can't intro, so no option" + "IntroDestructProvider" 8 17 + [ (not, IntroAndDestruct, "") + ] + diff --git a/plugins/hls-tactics-plugin/new/test/CodeAction/IntrosSpec.hs b/plugins/hls-tactics-plugin/new/test/CodeAction/IntrosSpec.hs new file mode 100644 index 0000000000..da2aaaa273 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/CodeAction/IntrosSpec.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE OverloadedStrings #-} + +module CodeAction.IntrosSpec where + +import Wingman.Types +import Test.Hspec +import Utils + + +spec :: Spec +spec = do + let introsTest = goldenTest Intros "" + + describe "golden" $ do + introsTest 2 8 "GoldenIntros" + + describe "layout" $ do + introsTest 4 24 "LayoutRec" + diff --git a/plugins/hls-tactics-plugin/new/test/CodeAction/RefineSpec.hs b/plugins/hls-tactics-plugin/new/test/CodeAction/RefineSpec.hs new file mode 100644 index 0000000000..205054c652 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/CodeAction/RefineSpec.hs @@ -0,0 +1,23 @@ +{-# LANGUAGE OverloadedStrings #-} + +module CodeAction.RefineSpec where + +import Wingman.Types +import Test.Hspec +import Utils + + +spec :: Spec +spec = do + let refineTest = goldenTest Refine "" + + describe "golden" $ do + refineTest 2 8 "RefineIntro" + refineTest 2 8 "RefineCon" + refineTest 4 10 "RefineReader" + refineTest 8 10 "RefineGADT" + refineTest 2 8 "RefineIntroWhere" + + describe "messages" $ do + mkShowMessageTest Refine "" 2 8 "MessageForallA" TacticErrors + diff --git a/plugins/hls-tactics-plugin/new/test/CodeAction/RunMetaprogramSpec.hs b/plugins/hls-tactics-plugin/new/test/CodeAction/RunMetaprogramSpec.hs new file mode 100644 index 0000000000..e366c34efe --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/CodeAction/RunMetaprogramSpec.hs @@ -0,0 +1,43 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE OverloadedStrings #-} + +module CodeAction.RunMetaprogramSpec where + +import Utils +import Test.Hspec +import Wingman.Types + + +spec :: Spec +spec = do + let metaTest l c f = + goldenTest RunMetaprogram "" l c f + + describe "beginMetaprogram" $ do + goldenTest BeginMetaprogram "" 1 7 "MetaBegin" + goldenTest BeginMetaprogram "" 1 9 "MetaBeginNoWildify" + + describe "golden" $ do + metaTest 6 11 "MetaMaybeAp" + metaTest 2 32 "MetaBindOne" + metaTest 2 32 "MetaBindAll" + metaTest 2 13 "MetaTry" + metaTest 2 74 "MetaChoice" + metaTest 5 40 "MetaUseImport" + metaTest 6 31 "MetaUseLocal" + metaTest 11 11 "MetaUseMethod" + metaTest 9 38 "MetaCataCollapse" + metaTest 7 16 "MetaCataCollapseUnary" + metaTest 10 32 "MetaCataAST" + metaTest 6 46 "MetaPointwise" + metaTest 4 28 "MetaUseSymbol" + metaTest 7 53 "MetaDeepOf" + metaTest 2 34 "MetaWithArg" + metaTest 2 18 "MetaLetSimple" + metaTest 5 9 "MetaIdiom" + metaTest 7 9 "MetaIdiomRecord" + + metaTest 14 10 "MetaFundeps" + + metaTest 2 12 "IntrosTooMany" + diff --git a/plugins/hls-tactics-plugin/new/test/CodeAction/UseDataConSpec.hs b/plugins/hls-tactics-plugin/new/test/CodeAction/UseDataConSpec.hs new file mode 100644 index 0000000000..94a1d17550 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/CodeAction/UseDataConSpec.hs @@ -0,0 +1,42 @@ +{-# LANGUAGE OverloadedStrings #-} + +module CodeAction.UseDataConSpec where + +import qualified Data.Text as T +import Wingman.Types +import Test.Hspec +import Utils + + +spec :: Spec +spec = do + let useTest = goldenTest UseDataCon + + describe "provider" $ do + mkTest + "Suggests all data cons for Either" + "ConProviders" 5 6 + [ (id, UseDataCon, "Left") + , (id, UseDataCon, "Right") + , (not, UseDataCon, ":") + , (not, UseDataCon, "[]") + , (not, UseDataCon, "C1") + ] + mkTest + "Suggests no data cons for big types" + "ConProviders" 11 17 $ do + c <- [1 :: Int .. 10] + pure $ (not, UseDataCon, T.pack $ show c) + mkTest + "Suggests only matching data cons for GADT" + "ConProviders" 20 12 + [ (id, UseDataCon, "IntGADT") + , (id, UseDataCon, "VarGADT") + , (not, UseDataCon, "BoolGADT") + ] + + describe "golden" $ do + useTest "(,)" 2 8 "UseConPair" + useTest "Left" 2 8 "UseConLeft" + useTest "Right" 2 8 "UseConRight" + diff --git a/plugins/hls-tactics-plugin/new/test/CodeLens/EmptyCaseSpec.hs b/plugins/hls-tactics-plugin/new/test/CodeLens/EmptyCaseSpec.hs new file mode 100644 index 0000000000..9ebf7d5043 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/CodeLens/EmptyCaseSpec.hs @@ -0,0 +1,25 @@ +{-# LANGUAGE OverloadedStrings #-} + +module CodeLens.EmptyCaseSpec where + +import Test.Hspec +import Utils + + +spec :: Spec +spec = do + let test = mkCodeLensTest + noTest = mkNoCodeLensTest + + describe "golden" $ do + test "EmptyCaseADT" + test "EmptyCaseShadow" + test "EmptyCaseParens" + test "EmptyCaseNested" + test "EmptyCaseApply" + test "EmptyCaseGADT" + test "EmptyCaseLamCase" + + describe "no code lenses" $ do + noTest "EmptyCaseSpuriousGADT" + diff --git a/plugins/hls-tactics-plugin/new/test/Main.hs b/plugins/hls-tactics-plugin/new/test/Main.hs new file mode 100644 index 0000000000..00a71905e1 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/Main.hs @@ -0,0 +1,8 @@ +module Main where + +import qualified Spec +import Test.Hls +import Test.Tasty.Hspec + +main :: IO () +main = testSpecs Spec.spec >>= defaultTestRunner . testGroup "tactics" diff --git a/plugins/hls-tactics-plugin/new/test/ProviderSpec.hs b/plugins/hls-tactics-plugin/new/test/ProviderSpec.hs new file mode 100644 index 0000000000..4eea30f5b3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/ProviderSpec.hs @@ -0,0 +1,28 @@ +{-# LANGUAGE OverloadedStrings #-} + +module ProviderSpec where + +import Wingman.Types +import Test.Hspec +import Utils + + +spec :: Spec +spec = do + mkTest + "Produces intros code action" + "T1" 2 14 + [ (id, Intros, "") + ] + + mkTest + "Won't suggest intros on the wrong type" + "T2" 8 8 + [ (not, Intros, "") + ] + + goldenTestMany "SubsequentTactics" + [ InvokeTactic Intros "" 4 5 + , InvokeTactic Destruct "du" 4 8 + , InvokeTactic Auto "" 4 15 + ] diff --git a/plugins/hls-tactics-plugin/new/test/Spec.hs b/plugins/hls-tactics-plugin/new/test/Spec.hs new file mode 100644 index 0000000000..5416ef6a86 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/Spec.hs @@ -0,0 +1 @@ +{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=Spec #-} diff --git a/plugins/hls-tactics-plugin/new/test/UnificationSpec.hs b/plugins/hls-tactics-plugin/new/test/UnificationSpec.hs new file mode 100644 index 0000000000..148a40eaaa --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/UnificationSpec.hs @@ -0,0 +1,83 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE ViewPatterns #-} + +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module UnificationSpec where + +import Control.Arrow +import Control.Monad (replicateM, join) +import Control.Monad.State (evalState) +import Data.Bool (bool) +import Data.Functor ((<&>)) +import Data.Maybe (mapMaybe) +import qualified Data.Set as S +import Data.Traversable +import Data.Tuple (swap) +import Development.IDE.GHC.Compat.Core (substTy, mkBoxedTupleTy) +import Test.Hspec +import Test.QuickCheck +import Wingman.GHC +import Wingman.Machinery (newUnivar) +import Wingman.Types + +#if __GLASGOW_HASKELL__ >= 900 +import GHC.Tc.Utils.TcType (tcGetTyVar_maybe) +#else +import TcType (tcGetTyVar_maybe) +#endif + + +spec :: Spec +spec = describe "unification" $ do + it "should be able to unify univars with skolems on either side of the equality" $ do + property $ do + -- Pick some number of unification vars and skolem + n <- choose (1, 20) + let (skolems, take n -> univars) + = splitAt n + $ flip evalState defaultTacticState + $ replicateM (n * 2) newUnivar + -- Randomly pair them + skolem_uni_pairs <- + for (zip skolems univars) randomSwap + let (lhs, rhs) + = mkBoxedTupleTy *** mkBoxedTupleTy + $ unzip skolem_uni_pairs + pure $ + counterexample (show skolems) $ + counterexample (show lhs) $ + counterexample (show rhs) $ + case tryUnifyUnivarsButNotSkolems + (S.fromList $ mapMaybe tcGetTyVar_maybe skolems) + (CType lhs) + (CType rhs) of + Just subst -> + conjoin $ join $ + [ -- For each pair, running the unification over the univar should + -- result in the skolem + zip univars skolems <&> \(uni, skolem) -> + let substd = substTy subst uni + in counterexample (show substd) $ + counterexample (show skolem) $ + CType substd === CType skolem + + -- And also, no two univars should equal to one another + -- before or after substitution. + , zip univars (tail univars) <&> \(uni1, uni2) -> + let uni1_sub = substTy subst uni1 + uni2_sub = substTy subst uni2 + in counterexample (show uni1) $ + counterexample (show uni2) $ + CType uni1 =/= CType uni2 .&&. + CType uni1_sub =/= CType uni2_sub + ] + Nothing -> True === False + + +randomSwap :: (a, a) -> Gen (a, a) +randomSwap ab = do + which <- arbitrary + pure $ bool swap id which ab + + diff --git a/plugins/hls-tactics-plugin/new/test/Utils.hs b/plugins/hls-tactics-plugin/new/test/Utils.hs new file mode 100644 index 0000000000..db31d910cf --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/Utils.hs @@ -0,0 +1,263 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE RecordWildCards #-} + +module Utils where + +import Control.DeepSeq (deepseq) +import qualified Control.Exception as E +import Control.Lens hiding (List, failing, (<.>), (.=)) +import Control.Monad (unless, void) +import Control.Monad.IO.Class +import Data.Aeson +import Data.Foldable +import Data.Function (on) +import Data.IORef (writeIORef) +import Data.Maybe +import Data.Text (Text) +import qualified Data.Text as T +import qualified Data.Text.IO as T +import Ide.Plugin.Tactic as Tactic +import Language.LSP.Types +import Language.LSP.Types.Lens hiding (actions, applyEdit, capabilities, executeCommand, id, line, message, name, rename, title) +import qualified Language.LSP.Types.Lens as J +import System.Directory (doesFileExist) +import System.FilePath +import Test.Hls +import Test.Hspec +import Test.Hspec.Formatters (FailureReason(ExpectedButGot)) +import Wingman.LanguageServer (mkShowMessageParams) +import Wingman.Types + + +plugin :: PluginDescriptor IdeState +plugin = Tactic.descriptor mempty "tactics" + +------------------------------------------------------------------------------ +-- | Get a range at the given line and column corresponding to having nothing +-- selected. +-- +-- NB: These coordinates are in "file space", ie, 1-indexed. +pointRange :: Int -> Int -> Range +pointRange + (subtract 1 -> fromIntegral -> line) + (subtract 1 -> fromIntegral -> col) = + Range (Position line col) (Position line $ col + 1) + + +------------------------------------------------------------------------------ +-- | Get the title of a code action. +codeActionTitle :: (Command |? CodeAction) -> Maybe Text +codeActionTitle InL{} = Nothing +codeActionTitle (InR(CodeAction title _ _ _ _ _ _ _)) = Just title + + +resetGlobalHoleRef :: IO () +resetGlobalHoleRef = writeIORef globalHoleRef 0 + + +runSessionForTactics :: Session a -> IO a +runSessionForTactics = + runSessionWithServer' + [plugin] + def + (def { messageTimeout = 20 } ) + fullCaps + tacticPath + +------------------------------------------------------------------------------ +-- | Make a tactic unit test. +mkTest + :: Foldable t + => String -- ^ The test name + -> FilePath -- ^ The file name stem (without extension) to load + -> Int -- ^ Cursor line + -> Int -- ^ Cursor column + -> t ( Bool -> Bool -- Use 'not' for actions that shouldn't be present + , TacticCommand -- An expected command ... + , Text -- ... for this variable + ) -- ^ A collection of (un)expected code actions. + -> SpecWith (Arg Bool) +mkTest name fp line col ts = it name $ do + resetGlobalHoleRef + runSessionForTactics $ do + doc <- openDoc (fp <.> "hs") "haskell" + -- wait for diagnostics to start coming + void waitForDiagnostics + -- wait for the entire build to finish, so that Tactics code actions that + -- use stale data will get uptodate stuff + void $ waitForTypecheck doc + actions <- getCodeActions doc $ pointRange line col + let titles = mapMaybe codeActionTitle actions + for_ ts $ \(f, tc, var) -> do + let title = tacticTitle tc var + liftIO $ + (title `elem` titles) `shouldSatisfy` f + +data InvokeTactic = InvokeTactic + { it_command :: TacticCommand + , it_argument :: Text + , it_line :: Int + , it_col :: Int + } + +invokeTactic :: TextDocumentIdentifier -> InvokeTactic -> Session () +invokeTactic doc InvokeTactic{..} = do + -- wait for the entire build to finish, so that Tactics code actions that + -- use stale data will get uptodate stuff + void waitForDiagnostics + void $ waitForTypecheck doc + actions <- getCodeActions doc $ pointRange it_line it_col + case find ((== Just (tacticTitle it_command it_argument)) . codeActionTitle) actions of + Just (InR CodeAction {_command = Just c}) -> do + executeCommand c + void $ skipManyTill anyMessage $ message SWorkspaceApplyEdit + _ -> error $ show actions + + +mkGoldenTest + :: (Text -> Text -> Assertion) + -> [InvokeTactic] + -> FilePath + -> SpecWith () +mkGoldenTest eq invocations input = + it (input <> " (golden)") $ do + resetGlobalHoleRef + runSessionForTactics $ do + doc <- openDoc (input <.> "hs") "haskell" + traverse_ (invokeTactic doc) invocations + edited <- documentContents doc + let expected_name = input <.> "expected" <.> "hs" + -- Write golden tests if they don't already exist + liftIO $ (doesFileExist expected_name >>=) $ flip unless $ do + T.writeFile expected_name edited + expected <- liftIO $ T.readFile expected_name + liftIO $ edited `eq` expected + + +mkCodeLensTest + :: FilePath + -> SpecWith () +mkCodeLensTest input = + it (input <> " (golden)") $ do + resetGlobalHoleRef + runSessionForTactics $ do + doc <- openDoc (input <.> "hs") "haskell" + _ <- waitForDiagnostics + lenses <- fmap (reverse . filter isWingmanLens) $ getCodeLenses doc + for_ lenses $ \(CodeLens _ (Just cmd) _) -> + executeCommand cmd + _resp <- skipManyTill anyMessage (message SWorkspaceApplyEdit) + edited <- documentContents doc + let expected_name = input <.> "expected" <.> "hs" + -- Write golden tests if they don't already exist + liftIO $ (doesFileExist expected_name >>=) $ flip unless $ do + T.writeFile expected_name edited + expected <- liftIO $ T.readFile expected_name + liftIO $ edited `shouldBe` expected + + +------------------------------------------------------------------------------ +-- | A test that no code lenses can be run in the file +mkNoCodeLensTest + :: FilePath + -> SpecWith () +mkNoCodeLensTest input = + it (input <> " (no code lenses)") $ do + resetGlobalHoleRef + runSessionForTactics $ do + doc <- openDoc (input <.> "hs") "haskell" + _ <- waitForBuildQueue + lenses <- fmap (reverse . filter isWingmanLens) $ getCodeLenses doc + liftIO $ lenses `shouldBe` [] + + + +isWingmanLens :: CodeLens -> Bool +isWingmanLens (CodeLens _ (Just (Command _ cmd _)) _) + = T.isInfixOf ":tactics:" cmd +isWingmanLens _ = False + + +mkShowMessageTest + :: TacticCommand + -> Text + -> Int + -> Int + -> FilePath + -> UserFacingMessage + -> SpecWith () +mkShowMessageTest tc occ line col input ufm = + it (input <> " (golden)") $ do + resetGlobalHoleRef + runSessionForTactics $ do + doc <- openDoc (input <.> "hs") "haskell" + _ <- waitForDiagnostics + actions <- getCodeActions doc $ pointRange line col + Just (InR CodeAction {_command = Just c}) + <- pure $ find ((== Just (tacticTitle tc occ)) . codeActionTitle) actions + executeCommand c + NotificationMessage _ _ err <- skipManyTill anyMessage (message SWindowShowMessage) + liftIO $ err `shouldBe` mkShowMessageParams ufm + + +goldenTest :: TacticCommand -> Text -> Int -> Int -> FilePath -> SpecWith () +goldenTest tc occ line col = mkGoldenTest shouldBe [InvokeTactic tc occ line col] + +goldenTestMany :: FilePath -> [InvokeTactic] -> SpecWith () +goldenTestMany = flip $ mkGoldenTest shouldBe + +goldenTestNoWhitespace :: TacticCommand -> Text -> Int -> Int -> FilePath -> SpecWith () +goldenTestNoWhitespace tc occ line col = mkGoldenTest shouldBeIgnoringSpaces [InvokeTactic tc occ line col] + + +shouldBeIgnoringSpaces :: Text -> Text -> Assertion +shouldBeIgnoringSpaces = assertFun f "" + where + f = (==) `on` T.unwords . T.words + + +assertFun + :: Show a + => (a -> a -> Bool) + -> String -- ^ The message prefix + -> a -- ^ The expected value + -> a -- ^ The actual value + -> Assertion +assertFun eq preface expected actual = + unless (eq actual expected) $ do + (prefaceMsg + `deepseq` expectedMsg + `deepseq` actualMsg + `deepseq` + E.throwIO + (HUnitFailure Nothing $ show $ ExpectedButGot prefaceMsg expectedMsg actualMsg)) + where + prefaceMsg + | null preface = Nothing + | otherwise = Just preface + expectedMsg = show expected + actualMsg = show actual + + + +------------------------------------------------------------------------------ +-- | Don't run a test. +failing :: Applicative m => String -> b -> m () +failing _ _ = pure () + + +tacticPath :: FilePath +tacticPath = "old/test/golden" + + +executeCommandWithResp :: Command -> Session (ResponseMessage 'WorkspaceExecuteCommand) +executeCommandWithResp cmd = do + let args = decode $ encode $ fromJust $ cmd ^. arguments + execParams = ExecuteCommandParams Nothing (cmd ^. command) args + request SWorkspaceExecuteCommand execParams + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoEmptyString.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoEmptyString.expected.hs new file mode 100644 index 0000000000..8ccb9f083d --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoEmptyString.expected.hs @@ -0,0 +1,2 @@ +empty_string :: String +empty_string = "" diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoEmptyString.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoEmptyString.hs new file mode 100644 index 0000000000..f04451e24c --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoEmptyString.hs @@ -0,0 +1,2 @@ +empty_string :: String +empty_string = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoEndo.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoEndo.expected.hs new file mode 100644 index 0000000000..4b50c6c074 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoEndo.expected.hs @@ -0,0 +1,11 @@ +data Synthesized b a = Synthesized + { syn_trace :: b + , syn_val :: a + } + deriving (Eq, Show) + + +mapTrace :: (b -> b) -> Synthesized b a -> Synthesized b a +mapTrace fbb (Synthesized b a) + = Synthesized {syn_trace = fbb b, syn_val = a} + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoEndo.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoEndo.hs new file mode 100644 index 0000000000..c92e6adb5b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoEndo.hs @@ -0,0 +1,10 @@ +data Synthesized b a = Synthesized + { syn_trace :: b + , syn_val :: a + } + deriving (Eq, Show) + + +mapTrace :: (b -> b) -> Synthesized b a -> Synthesized b a +mapTrace = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoForallClassMethod.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoForallClassMethod.expected.hs new file mode 100644 index 0000000000..5846428ee7 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoForallClassMethod.expected.hs @@ -0,0 +1,12 @@ +{-# LANGUAGE ExplicitForAll #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE MultiParamTypeClasses #-} + +import Data.Functor.Contravariant + +class Semigroupal cat t1 t2 to f where + combine :: cat (to (f x y) (f x' y')) (f (t1 x x') (t2 y y')) + +comux :: forall p a b c d. Semigroupal Op (,) (,) (,) p => p (a, c) (b, d) -> (p a b, p c d) +comux = case combine of { (Op f) -> f } + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoForallClassMethod.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoForallClassMethod.hs new file mode 100644 index 0000000000..9ee00c9255 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoForallClassMethod.hs @@ -0,0 +1,12 @@ +{-# LANGUAGE ExplicitForAll #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE MultiParamTypeClasses #-} + +import Data.Functor.Contravariant + +class Semigroupal cat t1 t2 to f where + combine :: cat (to (f x y) (f x' y')) (f (t1 x x') (t2 y y')) + +comux :: forall p a b c d. Semigroupal Op (,) (,) (,) p => p (a, c) (b, d) -> (p a b, p c d) +comux = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoInfixApply.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoInfixApply.expected.hs new file mode 100644 index 0000000000..367f6e54d9 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoInfixApply.expected.hs @@ -0,0 +1,3 @@ +test :: (a -> b -> c) -> a -> (a -> b) -> c +test (/:) a f = a /: f a + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoInfixApply.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoInfixApply.hs new file mode 100644 index 0000000000..4675331aea --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoInfixApply.hs @@ -0,0 +1,3 @@ +test :: (a -> b -> c) -> a -> (a -> b) -> c +test (/:) a f = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoInfixApplyMany.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoInfixApplyMany.expected.hs new file mode 100644 index 0000000000..ce40bf0cd6 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoInfixApplyMany.expected.hs @@ -0,0 +1,3 @@ +test :: (a -> b -> x -> c) -> a -> (a -> b) -> x -> c +test (/:) a f x = (a /: f a) x + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoInfixApplyMany.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoInfixApplyMany.hs new file mode 100644 index 0000000000..55a706ab9b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoInfixApplyMany.hs @@ -0,0 +1,3 @@ +test :: (a -> b -> x -> c) -> a -> (a -> b) -> x -> c +test (/:) a f x = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoInfixInfix.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoInfixInfix.expected.hs new file mode 100644 index 0000000000..7adea169d1 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoInfixInfix.expected.hs @@ -0,0 +1,2 @@ +test :: (a -> b -> c) -> (c -> d -> e) -> a -> (a -> b) -> d -> e +test (/:) (-->) a f x = (a /: f a) --> x diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoInfixInfix.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoInfixInfix.hs new file mode 100644 index 0000000000..729e1a2227 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoInfixInfix.hs @@ -0,0 +1,2 @@ +test :: (a -> b -> c) -> (c -> d -> e) -> a -> (a -> b) -> d -> e +test (/:) (-->) a f x = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoPatSynUse.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoPatSynUse.expected.hs new file mode 100644 index 0000000000..8addba654f --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoPatSynUse.expected.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE PatternSynonyms #-} + +pattern JustSingleton :: a -> Maybe [a] +pattern JustSingleton a <- Just [a] + +amIASingleton :: Maybe [a] -> Maybe a +amIASingleton (JustSingleton a) = Just a + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoPatSynUse.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoPatSynUse.hs new file mode 100644 index 0000000000..25a44666e7 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoPatSynUse.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE PatternSynonyms #-} + +pattern JustSingleton :: a -> Maybe [a] +pattern JustSingleton a <- Just [a] + +amIASingleton :: Maybe [a] -> Maybe a +amIASingleton (JustSingleton a) = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoSplitGADT.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoSplitGADT.expected.hs new file mode 100644 index 0000000000..2521b651eb --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoSplitGADT.expected.hs @@ -0,0 +1,12 @@ +{-# LANGUAGE GADTs #-} + +data GADT b a where + GBool :: b -> GADT b Bool + GInt :: GADT b Int + +-- wingman would prefer to use GBool since then it can use its argument. But +-- that won't unify with GADT Int, so it is forced to pick GInt and ignore the +-- argument. +test :: b -> GADT b Int +test _ = GInt + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoSplitGADT.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoSplitGADT.hs new file mode 100644 index 0000000000..b15621e091 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoSplitGADT.hs @@ -0,0 +1,12 @@ +{-# LANGUAGE GADTs #-} + +data GADT b a where + GBool :: b -> GADT b Bool + GInt :: GADT b Int + +-- wingman would prefer to use GBool since then it can use its argument. But +-- that won't unify with GADT Int, so it is forced to pick GInt and ignore the +-- argument. +test :: b -> GADT b Int +test = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqCtx.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqCtx.expected.hs new file mode 100644 index 0000000000..cdb8506d01 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqCtx.expected.hs @@ -0,0 +1,5 @@ +{-# LANGUAGE GADTs #-} + +fun2 :: (a ~ b) => a -> b +fun2 = id -- id + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqCtx.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqCtx.hs new file mode 100644 index 0000000000..448a7f5de5 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqCtx.hs @@ -0,0 +1,5 @@ +{-# LANGUAGE GADTs #-} + +fun2 :: (a ~ b) => a -> b +fun2 = _ -- id + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqGADT.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqGADT.expected.hs new file mode 100644 index 0000000000..cea9517794 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqGADT.expected.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE GADTs #-} + +data Y a b = a ~ b => Y + +fun3 :: Y a b -> a -> b +fun3 Y = id + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqGADT.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqGADT.hs new file mode 100644 index 0000000000..eae2246722 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqGADT.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE GADTs #-} + +data Y a b = a ~ b => Y + +fun3 :: Y a b -> a -> b +fun3 Y = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqGADTDestruct.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqGADTDestruct.expected.hs new file mode 100644 index 0000000000..9f2b954867 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqGADTDestruct.expected.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE GADTs #-} + +data Y a b = a ~ b => Y + +fun3 :: Y a b -> a -> b +fun3 Y a = a + + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqGADTDestruct.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqGADTDestruct.hs new file mode 100644 index 0000000000..2292a3972f --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaEqGADTDestruct.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE GADTs #-} + +data Y a b = a ~ b => Y + +fun3 :: Y a b -> a -> b +fun3 = _ + + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaFix.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaFix.expected.hs new file mode 100644 index 0000000000..ba8df349e4 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaFix.expected.hs @@ -0,0 +1,13 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE UndecidableInstances #-} + +data Fix f a = Fix (f (Fix f a)) + +instance ( Functor f + -- FIXME(sandy): Unfortunately, the recursion tactic fails to fire + -- on this case. By explicitly adding the @Functor (Fix f)@ + -- dictionary, we can get Wingman to generate the right definition. + , Functor (Fix f) + ) => Functor (Fix f) where + fmap fab (Fix f) = Fix (fmap (fmap fab) f) + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaFix.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaFix.hs new file mode 100644 index 0000000000..014e6441da --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaFix.hs @@ -0,0 +1,13 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE UndecidableInstances #-} + +data Fix f a = Fix (f (Fix f a)) + +instance ( Functor f + -- FIXME(sandy): Unfortunately, the recursion tactic fails to fire + -- on this case. By explicitly adding the @Functor (Fix f)@ + -- dictionary, we can get Wingman to generate the right definition. + , Functor (Fix f) + ) => Functor (Fix f) where + fmap = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaGADT.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaGADT.expected.hs new file mode 100644 index 0000000000..e74f2aba40 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaGADT.expected.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE GADTs #-} + +data X f = Monad f => X + +fun1 :: X f -> a -> f a +fun1 X = pure + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaGADT.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaGADT.hs new file mode 100644 index 0000000000..e1b20a4b3b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaGADT.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE GADTs #-} + +data X f = Monad f => X + +fun1 :: X f -> a -> f a +fun1 X = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaGADTDestruct.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaGADTDestruct.expected.hs new file mode 100644 index 0000000000..4d4b1f9579 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaGADTDestruct.expected.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE GADTs #-} + +data X f = Monad f => X + +fun1 :: X f -> a -> f a +fun1 X a = pure a + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaGADTDestruct.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaGADTDestruct.hs new file mode 100644 index 0000000000..d92d0bd97d --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaGADTDestruct.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE GADTs #-} + +data X f = Monad f => X + +fun1 :: X f -> a -> f a +fun1 = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaMultipleUnification.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaMultipleUnification.expected.hs new file mode 100644 index 0000000000..446a4d73b3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaMultipleUnification.expected.hs @@ -0,0 +1,21 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE TypeOperators #-} + +import Data.Kind + +data Nat = Z | S Nat + +data HList (ls :: [Type]) where + HNil :: HList '[] + HCons :: t -> HList ts -> HList (t ': ts) + +data ElemAt (n :: Nat) t (ts :: [Type]) where + AtZ :: ElemAt 'Z t (t ': ts) + AtS :: ElemAt k t ts -> ElemAt ('S k) t (u ': ts) + +lookMeUp :: ElemAt i ty tys -> HList tys -> ty +lookMeUp AtZ (HCons t _) = t +lookMeUp (AtS ea') (HCons t hl') = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaMultipleUnification.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaMultipleUnification.hs new file mode 100644 index 0000000000..b0b520347d --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaMultipleUnification.hs @@ -0,0 +1,21 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE TypeOperators #-} + +import Data.Kind + +data Nat = Z | S Nat + +data HList (ls :: [Type]) where + HNil :: HList '[] + HCons :: t -> HList ts -> HList (t ': ts) + +data ElemAt (n :: Nat) t (ts :: [Type]) where + AtZ :: ElemAt 'Z t (t ': ts) + AtS :: ElemAt k t ts -> ElemAt ('S k) t (u ': ts) + +lookMeUp :: ElemAt i ty tys -> HList tys -> ty +lookMeUp AtZ (HCons t hl') = _ +lookMeUp (AtS ea') (HCons t hl') = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaRankN.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaRankN.expected.hs new file mode 100644 index 0000000000..23d96223f3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaRankN.expected.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE RankNTypes #-} + +showMe :: (forall x. Show x => x -> String) -> Int -> String +showMe f = f + +showedYou :: Int -> String +showedYou = showMe (\x -> show x) + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaRankN.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaRankN.hs new file mode 100644 index 0000000000..0e92ac35f3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaRankN.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE RankNTypes #-} + +showMe :: (forall x. Show x => x -> String) -> Int -> String +showMe f = f + +showedYou :: Int -> String +showedYou = showMe (\x -> _) + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaRefl.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaRefl.expected.hs new file mode 100644 index 0000000000..9e42bc946e --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaRefl.expected.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE GADTs #-} + +data Z a b where Z :: Z a a + +fun4 :: Z a b -> a -> b +fun4 Z = id -- id + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaRefl.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaRefl.hs new file mode 100644 index 0000000000..df15580ad2 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaRefl.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE GADTs #-} + +data Z a b where Z :: Z a a + +fun4 :: Z a b -> a -> b +fun4 Z = _ -- id + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaReflDestruct.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaReflDestruct.expected.hs new file mode 100644 index 0000000000..36aed1af65 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaReflDestruct.expected.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE GADTs #-} + +data Z a b where Z :: Z a a + +fun4 :: Z a b -> a -> b +fun4 Z a = a -- id + + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaReflDestruct.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaReflDestruct.hs new file mode 100644 index 0000000000..3beccba7a5 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaReflDestruct.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE GADTs #-} + +data Z a b where Z :: Z a a + +fun4 :: Z a b -> a -> b +fun4 = _ -- id + + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaSplitUnification.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaSplitUnification.expected.hs new file mode 100644 index 0000000000..e680f0265c --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaSplitUnification.expected.hs @@ -0,0 +1,17 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE TypeOperators #-} + +data A = A +data B = B +data X = X +data Y = Y + + +data Pairrow ax by where + Pairrow :: (a -> b) -> (x -> y) -> Pairrow '(a, x) '(b, y) + +test2 :: (A -> B) -> (X -> Y) -> Pairrow '(A, X) '(B, Y) +test2 = Pairrow + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoThetaSplitUnification.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaSplitUnification.hs new file mode 100644 index 0000000000..e6ceeb1bcd --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoThetaSplitUnification.hs @@ -0,0 +1,17 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE TypeOperators #-} + +data A = A +data B = B +data X = X +data Y = Y + + +data Pairrow ax by where + Pairrow :: (a -> b) -> (x -> y) -> Pairrow '(a, x) '(b, y) + +test2 :: (A -> B) -> (X -> Y) -> Pairrow '(A, X) '(B, Y) +test2 = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoTypeLevel.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoTypeLevel.expected.hs new file mode 100644 index 0000000000..3668830620 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoTypeLevel.expected.hs @@ -0,0 +1,21 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE TypeOperators #-} + +import Data.Kind + +data Nat = Z | S Nat + +data HList (ls :: [Type]) where + HNil :: HList '[] + HCons :: t -> HList ts -> HList (t ': ts) + +data ElemAt (n :: Nat) t (ts :: [Type]) where + AtZ :: ElemAt 'Z t (t ': ts) + AtS :: ElemAt k t ts -> ElemAt ('S k) t (u ': ts) + +lookMeUp :: ElemAt i ty tys -> HList tys -> ty +lookMeUp AtZ (HCons t _) = t +lookMeUp (AtS ea') (HCons _ hl') = lookMeUp ea' hl' + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoTypeLevel.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoTypeLevel.hs new file mode 100644 index 0000000000..40226739db --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoTypeLevel.hs @@ -0,0 +1,20 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE TypeOperators #-} + +import Data.Kind + +data Nat = Z | S Nat + +data HList (ls :: [Type]) where + HNil :: HList '[] + HCons :: t -> HList ts -> HList (t ': ts) + +data ElemAt (n :: Nat) t (ts :: [Type]) where + AtZ :: ElemAt 'Z t (t ': ts) + AtS :: ElemAt k t ts -> ElemAt ('S k) t (u ': ts) + +lookMeUp :: ElemAt i ty tys -> HList tys -> ty +lookMeUp = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoUnusedPatternMatch.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoUnusedPatternMatch.expected.hs new file mode 100644 index 0000000000..2885a1ca05 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoUnusedPatternMatch.expected.hs @@ -0,0 +1,2 @@ +test :: Bool -> () +test _ = () diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoUnusedPatternMatch.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoUnusedPatternMatch.hs new file mode 100644 index 0000000000..5345192969 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoUnusedPatternMatch.hs @@ -0,0 +1,2 @@ +test :: Bool -> () +test = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoZip.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoZip.expected.hs new file mode 100644 index 0000000000..997bc09a33 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoZip.expected.hs @@ -0,0 +1,6 @@ +zip_it_up_and_zip_it_out :: [a] -> [b] -> [(a, b)] +zip_it_up_and_zip_it_out _ [] = [] +zip_it_up_and_zip_it_out [] (_ : _) = [] +zip_it_up_and_zip_it_out (a : as') (b : bs') + = (a, b) : zip_it_up_and_zip_it_out as' bs' + diff --git a/plugins/hls-tactics-plugin/new/test/golden/AutoZip.hs b/plugins/hls-tactics-plugin/new/test/golden/AutoZip.hs new file mode 100644 index 0000000000..98d6335988 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/AutoZip.hs @@ -0,0 +1,3 @@ +zip_it_up_and_zip_it_out :: [a] -> [b] -> [(a, b)] +zip_it_up_and_zip_it_out = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/ConProviders.hs b/plugins/hls-tactics-plugin/new/test/golden/ConProviders.hs new file mode 100644 index 0000000000..19dbc3c6e5 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/ConProviders.hs @@ -0,0 +1,21 @@ +{-# LANGUAGE GADTs #-} + +-- Should suggest Left and Right, but not [] +t1 :: Either a b +t1 = _ + + +data ManyConstructors = C1 | C2 | C3 | C4 | C5 | C6 | C7 | C8 | C9 | C10 + +noCtorsIfMany :: ManyConstructors +noCtorsIfMany = _ + + +data GADT a where + IntGADT :: GADT Int + BoolGADT :: GADT Bool + VarGADT :: GADT a + +gadtCtor :: GADT Int +gadtCtor = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructAllAnd.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructAllAnd.expected.hs new file mode 100644 index 0000000000..392bd9d2cd --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructAllAnd.expected.hs @@ -0,0 +1,5 @@ +and :: Bool -> Bool -> Bool +and False False = _w0 +and False True = _w1 +and True False = _w2 +and True True = _w3 diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructAllAnd.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructAllAnd.hs new file mode 100644 index 0000000000..892eab679c --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructAllAnd.hs @@ -0,0 +1,2 @@ +and :: Bool -> Bool -> Bool +and x y = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructAllFunc.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructAllFunc.expected.hs new file mode 100644 index 0000000000..536d15b107 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructAllFunc.expected.hs @@ -0,0 +1,4 @@ +has_a_func :: Bool -> (a -> b) -> Bool +has_a_func False y = _w0 +has_a_func True y = _w1 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructAllFunc.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructAllFunc.hs new file mode 100644 index 0000000000..6996698400 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructAllFunc.hs @@ -0,0 +1,3 @@ +has_a_func :: Bool -> (a -> b) -> Bool +has_a_func x y = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructAllGADTEvidence.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructAllGADTEvidence.expected.hs new file mode 100644 index 0000000000..0e4c0985fa --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructAllGADTEvidence.expected.hs @@ -0,0 +1,21 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE TypeOperators #-} + +import Data.Kind + +data Nat = Z | S Nat + +data HList (ls :: [Type]) where + HNil :: HList '[] + HCons :: t -> HList ts -> HList (t ': ts) + +data ElemAt (n :: Nat) t (ts :: [Type]) where + AtZ :: ElemAt 'Z t (t ': ts) + AtS :: ElemAt k t ts -> ElemAt ('S k) t (u ': ts) + +lookMeUp :: ElemAt i ty tys -> HList tys -> ty +lookMeUp AtZ (HCons t hl') = _w0 +lookMeUp (AtS ea') (HCons t hl') = _w1 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructAllGADTEvidence.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructAllGADTEvidence.hs new file mode 100644 index 0000000000..3ac66d5444 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructAllGADTEvidence.hs @@ -0,0 +1,20 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE TypeOperators #-} + +import Data.Kind + +data Nat = Z | S Nat + +data HList (ls :: [Type]) where + HNil :: HList '[] + HCons :: t -> HList ts -> HList (t ': ts) + +data ElemAt (n :: Nat) t (ts :: [Type]) where + AtZ :: ElemAt 'Z t (t ': ts) + AtS :: ElemAt k t ts -> ElemAt ('S k) t (u ': ts) + +lookMeUp :: ElemAt i ty tys -> HList tys -> ty +lookMeUp ea hl = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructAllMany.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructAllMany.expected.hs new file mode 100644 index 0000000000..366a3eac70 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructAllMany.expected.hs @@ -0,0 +1,27 @@ +data ABC = A | B | C + +many :: () -> Either a b -> Bool -> Maybe ABC -> ABC -> () +many () (Left a) False Nothing A = _w0 +many () (Left a) False Nothing B = _w1 +many () (Left a) False Nothing C = _w2 +many () (Left a) False (Just abc') A = _w3 +many () (Left a) False (Just abc') B = _w4 +many () (Left a) False (Just abc') C = _w5 +many () (Left a) True Nothing A = _w6 +many () (Left a) True Nothing B = _w7 +many () (Left a) True Nothing C = _w8 +many () (Left a) True (Just abc') A = _w9 +many () (Left a) True (Just abc') B = _wa +many () (Left a) True (Just abc') C = _wb +many () (Right b') False Nothing A = _wc +many () (Right b') False Nothing B = _wd +many () (Right b') False Nothing C = _we +many () (Right b') False (Just abc') A = _wf +many () (Right b') False (Just abc') B = _wg +many () (Right b') False (Just abc') C = _wh +many () (Right b') True Nothing A = _wi +many () (Right b') True Nothing B = _wj +many () (Right b') True Nothing C = _wk +many () (Right b') True (Just abc') A = _wl +many () (Right b') True (Just abc') B = _wm +many () (Right b') True (Just abc') C = _wn diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructAllMany.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructAllMany.hs new file mode 100644 index 0000000000..ab0a4dccb9 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructAllMany.hs @@ -0,0 +1,4 @@ +data ABC = A | B | C + +many :: () -> Either a b -> Bool -> Maybe ABC -> ABC -> () +many u e b mabc abc = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructAllNonVarTopMatch.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructAllNonVarTopMatch.expected.hs new file mode 100644 index 0000000000..dc1ea66c51 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructAllNonVarTopMatch.expected.hs @@ -0,0 +1,6 @@ +and :: (a, b) -> Bool -> Bool -> Bool +and (a, b) False False = _w0 +and (a, b) False True = _w1 +and (a, b) True False = _w2 +and (a, b) True True = _w3 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructAllNonVarTopMatch.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructAllNonVarTopMatch.hs new file mode 100644 index 0000000000..358223ae67 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructAllNonVarTopMatch.hs @@ -0,0 +1,3 @@ +and :: (a, b) -> Bool -> Bool -> Bool +and (a, b) x y = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructAllProvider.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructAllProvider.hs new file mode 100644 index 0000000000..8d115e828d --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructAllProvider.hs @@ -0,0 +1,12 @@ +-- we need to name the args ourselves first +nothingToDestruct :: [a] -> [a] -> [a] +nothingToDestruct = _ + + +-- can't destruct all for non-top-level holes +notTop :: Bool -> Bool -> Bool +notTop a b = a && _ + +-- destruct all is ok +canDestructAll :: Bool -> Bool -> Bool +canDestructAll a b = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructCthulhu.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructCthulhu.expected.hs new file mode 100644 index 0000000000..e885b489a1 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructCthulhu.expected.hs @@ -0,0 +1,54 @@ +{-# LANGUAGE GADTs #-} + +data FreePro r c a b where + ID :: FreePro r c x x + Comp :: FreePro r c x y -> FreePro r c y z -> FreePro r c x z + Copy :: FreePro r c x (x, x) + Consume :: FreePro r c x () + Swap :: FreePro r c (a, b) (b, a) + SwapE :: FreePro r c (Either a b) (Either b a) + Fst :: FreePro r c (a, b) a + Snd :: FreePro r c (a, b) b + InjectL :: FreePro r c a (Either a b) + InjectR :: FreePro r c b (Either a b) + Unify :: FreePro r c (Either a a) a + First :: FreePro r c a b -> FreePro r c (a, m) (b, m) + Second :: FreePro r c a b -> FreePro r c (m, a) (m, b) + Alongside :: FreePro r c a b -> FreePro r c a' b' -> FreePro r c (a, a') (b, b') + Fanout :: FreePro r c a b -> FreePro r c a b' -> FreePro r c a (b, b') + Left' :: FreePro r c a b -> FreePro r c (Either a x) (Either b x) + Right' :: FreePro r c a b -> FreePro r c (Either x a) (Either x b) + EitherOf :: FreePro r c a b -> FreePro r c a' b' -> FreePro r c (Either a a') (Either b b') + Fanin :: FreePro r c a b -> FreePro r c a' b -> FreePro r c (Either a a') b + LiftC :: c a b -> FreePro r c a b + Zero :: FreePro r c x y + Plus :: FreePro r c x y -> FreePro r c x y -> FreePro r c x y + Unleft :: FreePro r c (Either a d) (Either b d) -> FreePro r c a b + Unright :: FreePro r c (Either d a) (Either d b) -> FreePro r c a b + + +cthulhu :: FreePro r c a b -> FreePro r c a b +cthulhu ID = _w0 +cthulhu (Comp fp' fp_rcyb) = _w1 +cthulhu Copy = _w2 +cthulhu Consume = _w3 +cthulhu Swap = _w4 +cthulhu SwapE = _w5 +cthulhu Fst = _w6 +cthulhu Snd = _w7 +cthulhu InjectL = _w8 +cthulhu InjectR = _w9 +cthulhu Unify = _wa +cthulhu (First fp') = _wb +cthulhu (Second fp') = _wc +cthulhu (Alongside fp' fp_rca'b') = _wd +cthulhu (Fanout fp' fp_rcab') = _we +cthulhu (Left' fp') = _wf +cthulhu (Right' fp') = _wg +cthulhu (EitherOf fp' fp_rca'b') = _wh +cthulhu (Fanin fp' fp_rca'b) = _wi +cthulhu (LiftC cab) = _wj +cthulhu Zero = _wk +cthulhu (Plus fp' fp_rcab) = _wl +cthulhu (Unleft fp') = _wm +cthulhu (Unright fp') = _wn diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructCthulhu.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructCthulhu.hs new file mode 100644 index 0000000000..a2d04bb6a2 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructCthulhu.hs @@ -0,0 +1,31 @@ +{-# LANGUAGE GADTs #-} + +data FreePro r c a b where + ID :: FreePro r c x x + Comp :: FreePro r c x y -> FreePro r c y z -> FreePro r c x z + Copy :: FreePro r c x (x, x) + Consume :: FreePro r c x () + Swap :: FreePro r c (a, b) (b, a) + SwapE :: FreePro r c (Either a b) (Either b a) + Fst :: FreePro r c (a, b) a + Snd :: FreePro r c (a, b) b + InjectL :: FreePro r c a (Either a b) + InjectR :: FreePro r c b (Either a b) + Unify :: FreePro r c (Either a a) a + First :: FreePro r c a b -> FreePro r c (a, m) (b, m) + Second :: FreePro r c a b -> FreePro r c (m, a) (m, b) + Alongside :: FreePro r c a b -> FreePro r c a' b' -> FreePro r c (a, a') (b, b') + Fanout :: FreePro r c a b -> FreePro r c a b' -> FreePro r c a (b, b') + Left' :: FreePro r c a b -> FreePro r c (Either a x) (Either b x) + Right' :: FreePro r c a b -> FreePro r c (Either x a) (Either x b) + EitherOf :: FreePro r c a b -> FreePro r c a' b' -> FreePro r c (Either a a') (Either b b') + Fanin :: FreePro r c a b -> FreePro r c a' b -> FreePro r c (Either a a') b + LiftC :: c a b -> FreePro r c a b + Zero :: FreePro r c x y + Plus :: FreePro r c x y -> FreePro r c x y -> FreePro r c x y + Unleft :: FreePro r c (Either a d) (Either b d) -> FreePro r c a b + Unright :: FreePro r c (Either d a) (Either d b) -> FreePro r c a b + + +cthulhu :: FreePro r c a b -> FreePro r c a b +cthulhu fp = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructDataFam.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructDataFam.expected.hs new file mode 100644 index 0000000000..e463935583 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructDataFam.expected.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE TypeFamilies #-} + +data family Yo +data instance Yo = Heya Int + +test :: Yo -> Int +test (Heya n) = _w0 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructDataFam.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructDataFam.hs new file mode 100644 index 0000000000..a93e1974fb --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructDataFam.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE TypeFamilies #-} + +data family Yo +data instance Yo = Heya Int + +test :: Yo -> Int +test b = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructInt.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructInt.expected.hs new file mode 100644 index 0000000000..0f14deef83 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructInt.expected.hs @@ -0,0 +1,7 @@ +import Data.Int + +data Test = Test Int32 + +test :: Test -> Int32 +test (Test in') = _w0 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructInt.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructInt.hs new file mode 100644 index 0000000000..432a6d4074 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructInt.hs @@ -0,0 +1,7 @@ +import Data.Int + +data Test = Test Int32 + +test :: Test -> Int32 +test t = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructPun.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructPun.expected.hs new file mode 100644 index 0000000000..bfd8d09074 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructPun.expected.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE NamedFieldPuns #-} + + +data Foo = Foo { a :: Bool, b :: Bool } + +foo Foo {a = False, b} = _w0 +foo Foo {a = True, b} = _w1 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructPun.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructPun.hs new file mode 100644 index 0000000000..c7b410c5e3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructPun.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE NamedFieldPuns #-} + + +data Foo = Foo { a :: Bool, b :: Bool } + +foo Foo {a, b} = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructTyFam.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructTyFam.expected.hs new file mode 100644 index 0000000000..eee4cbd587 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructTyFam.expected.hs @@ -0,0 +1,9 @@ +{-# LANGUAGE TypeFamilies #-} + +type family Yo where + Yo = Bool + +test :: Yo -> Int +test False = _w0 +test True = _w1 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructTyFam.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructTyFam.hs new file mode 100644 index 0000000000..30a9d884b7 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructTyFam.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE TypeFamilies #-} + +type family Yo where + Yo = Bool + +test :: Yo -> Int +test b = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructTyToDataFam.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructTyToDataFam.expected.hs new file mode 100644 index 0000000000..3016c4ef4e --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructTyToDataFam.expected.hs @@ -0,0 +1,18 @@ +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE UndecidableInstances #-} + +type family T1 a where + T1 a = T2 Int + +type family T2 a +type instance T2 Int = T3 + +type family T3 where + T3 = Yo + +data family Yo +data instance Yo = Heya Int + +test :: T1 Bool -> Int +test (Heya n) = _w0 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/DestructTyToDataFam.hs b/plugins/hls-tactics-plugin/new/test/golden/DestructTyToDataFam.hs new file mode 100644 index 0000000000..191fa7b044 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/DestructTyToDataFam.hs @@ -0,0 +1,18 @@ +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE UndecidableInstances #-} + +type family T1 a where + T1 a = T2 Int + +type family T2 a +type instance T2 Int = T3 + +type family T3 where + T3 = Yo + +data family Yo +data instance Yo = Heya Int + +test :: T1 Bool -> Int +test b = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseADT.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseADT.expected.hs new file mode 100644 index 0000000000..84d2b80d0e --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseADT.expected.hs @@ -0,0 +1,8 @@ +data Foo = A Int | B Bool | C + +foo :: Foo -> () +foo x = case x of + A n -> _ + B b -> _ + C -> _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseADT.hs b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseADT.hs new file mode 100644 index 0000000000..37d3b6c357 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseADT.hs @@ -0,0 +1,5 @@ +data Foo = A Int | B Bool | C + +foo :: Foo -> () +foo x = case x of + diff --git a/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseApply.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseApply.expected.hs new file mode 100644 index 0000000000..1895dd6256 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseApply.expected.hs @@ -0,0 +1,3 @@ +blah = case show 5 of + [] -> _ + c : s -> _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseApply.hs b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseApply.hs new file mode 100644 index 0000000000..29647e2cda --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseApply.hs @@ -0,0 +1 @@ +blah = case show 5 of diff --git a/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseGADT.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseGADT.expected.hs new file mode 100644 index 0000000000..409be2aa03 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseGADT.expected.hs @@ -0,0 +1,13 @@ +{-# LANGUAGE GADTs #-} + +data GADT a where + MyInt :: GADT Int + MyBool :: GADT Bool + MyVar :: GADT a + + +test :: GADT Int -> GADT Bool +test x = case x of + MyInt -> _ + MyVar -> _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseGADT.hs b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseGADT.hs new file mode 100644 index 0000000000..ba08ddae54 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseGADT.hs @@ -0,0 +1,11 @@ +{-# LANGUAGE GADTs #-} + +data GADT a where + MyInt :: GADT Int + MyBool :: GADT Bool + MyVar :: GADT a + + +test :: GADT Int -> GADT Bool +test x = case x of + diff --git a/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseLamCase.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseLamCase.expected.hs new file mode 100644 index 0000000000..048f437368 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseLamCase.expected.hs @@ -0,0 +1,6 @@ +{-# LANGUAGE LambdaCase #-} + +test :: Bool -> Bool +test = \case + False -> _ + True -> _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseLamCase.hs b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseLamCase.hs new file mode 100644 index 0000000000..ef490eb751 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseLamCase.hs @@ -0,0 +1,4 @@ +{-# LANGUAGE LambdaCase #-} + +test :: Bool -> Bool +test = \case diff --git a/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseNested.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseNested.expected.hs new file mode 100644 index 0000000000..ef873a7c41 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseNested.expected.hs @@ -0,0 +1,5 @@ +test = + case (case (Just "") of + Nothing -> _ + Just s -> _) of + True -> _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseNested.hs b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseNested.hs new file mode 100644 index 0000000000..a72781a7c6 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseNested.hs @@ -0,0 +1,3 @@ +test = + case (case (Just "") of) of + True -> _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseParens.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseParens.expected.hs new file mode 100644 index 0000000000..18aacf2ae2 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseParens.expected.hs @@ -0,0 +1,3 @@ +test = True && (case True of + False -> _ + True -> _) diff --git a/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseParens.hs b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseParens.hs new file mode 100644 index 0000000000..2ac71b042e --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseParens.hs @@ -0,0 +1 @@ +test = True && case True of diff --git a/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseShadow.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseShadow.expected.hs new file mode 100644 index 0000000000..2c5158b856 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseShadow.expected.hs @@ -0,0 +1,10 @@ +data Foo = A Int | B Bool | C + +-- Make sure we don't shadow the i and b bindings when we empty case +-- split +foo :: Int -> Bool -> Foo -> () +foo i b x = case x of + A n -> _ + B b' -> _ + C -> _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseShadow.hs b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseShadow.hs new file mode 100644 index 0000000000..c57af5b849 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseShadow.hs @@ -0,0 +1,7 @@ +data Foo = A Int | B Bool | C + +-- Make sure we don't shadow the i and b bindings when we empty case +-- split +foo :: Int -> Bool -> Foo -> () +foo i b x = case x of + diff --git a/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseSpuriousGADT.hs b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseSpuriousGADT.hs new file mode 100644 index 0000000000..25906fe536 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/EmptyCaseSpuriousGADT.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE GADTs #-} + +data Foo a where + Foo :: Foo Int + +foo :: Foo Bool -> () +foo x = case x of + diff --git a/plugins/hls-tactics-plugin/new/test/golden/Fgmap.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/Fgmap.expected.hs new file mode 100644 index 0000000000..4f4921fa05 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/Fgmap.expected.hs @@ -0,0 +1,2 @@ +fgmap :: (Functor f, Functor g) => (a -> b) -> (f (g a) -> f (g b)) +fgmap = fmap . fmap diff --git a/plugins/hls-tactics-plugin/new/test/golden/Fgmap.hs b/plugins/hls-tactics-plugin/new/test/golden/Fgmap.hs new file mode 100644 index 0000000000..de1968474e --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/Fgmap.hs @@ -0,0 +1,2 @@ +fgmap :: (Functor f, Functor g) => (a -> b) -> (f (g a) -> f (g b)) +fgmap = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/FmapBoth.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/FmapBoth.expected.hs new file mode 100644 index 0000000000..825b00ebea --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/FmapBoth.expected.hs @@ -0,0 +1,3 @@ +fmapBoth :: (Functor f, Functor g) => (a -> b) -> (f a, g a) -> (f b, g b) +fmapBoth fab (fa, ga) = (fmap fab fa, fmap fab ga) + diff --git a/plugins/hls-tactics-plugin/new/test/golden/FmapBoth.hs b/plugins/hls-tactics-plugin/new/test/golden/FmapBoth.hs new file mode 100644 index 0000000000..29d8ea62b2 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/FmapBoth.hs @@ -0,0 +1,3 @@ +fmapBoth :: (Functor f, Functor g) => (a -> b) -> (f a, g a) -> (f b, g b) +fmapBoth = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/FmapJoin.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/FmapJoin.expected.hs new file mode 100644 index 0000000000..5dc5026f8b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/FmapJoin.expected.hs @@ -0,0 +1,2 @@ +fJoin :: (Monad m, Monad f) => f (m (m a)) -> f (m a) +fJoin = fmap (\ m -> m >>= id) diff --git a/plugins/hls-tactics-plugin/new/test/golden/FmapJoin.hs b/plugins/hls-tactics-plugin/new/test/golden/FmapJoin.hs new file mode 100644 index 0000000000..98a40133ea --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/FmapJoin.hs @@ -0,0 +1,2 @@ +fJoin :: (Monad m, Monad f) => f (m (m a)) -> f (m a) +fJoin = fmap _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/FmapJoinInLet.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/FmapJoinInLet.expected.hs new file mode 100644 index 0000000000..ac4b54ae9d --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/FmapJoinInLet.expected.hs @@ -0,0 +1,4 @@ +{-# LANGUAGE ScopedTypeVariables #-} + +fJoin :: forall f m a. (Monad m, Monad f) => f (m (m a)) -> f (m a) +fJoin = let f = ( (\ m -> m >>= id) :: m (m a) -> m a) in fmap f diff --git a/plugins/hls-tactics-plugin/new/test/golden/FmapJoinInLet.hs b/plugins/hls-tactics-plugin/new/test/golden/FmapJoinInLet.hs new file mode 100644 index 0000000000..e6fe6cbd0d --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/FmapJoinInLet.hs @@ -0,0 +1,4 @@ +{-# LANGUAGE ScopedTypeVariables #-} + +fJoin :: forall f m a. (Monad m, Monad f) => f (m (m a)) -> f (m a) +fJoin = let f = (_ :: m (m a) -> m a) in fmap f diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenApplicativeThen.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenApplicativeThen.hs new file mode 100644 index 0000000000..29ce9f5132 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenApplicativeThen.hs @@ -0,0 +1,2 @@ +useThen :: Applicative f => f Int -> f a -> f a +useThen = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenArbitrary.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenArbitrary.expected.hs new file mode 100644 index 0000000000..6f7af5c3fd --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenArbitrary.expected.hs @@ -0,0 +1,53 @@ +-- Emulate a quickcheck import; deriveArbitrary works on any type with the +-- right name and kind +data Gen a + +data Obj + = Square Int Int + | Circle Int + | Polygon [(Int, Int)] + | Rotate2 Double Obj + | Empty + | Full + | Complement Obj + | UnionR Double [Obj] + | DifferenceR Double Obj [Obj] + | IntersectR Double [Obj] + | Translate Double Double Obj + | Scale Double Double Obj + | Mirror Double Double Obj + | Outset Double Obj + | Shell Double Obj + | WithRounding Double Obj + + +arbitrary :: Gen Obj +arbitrary + = let + terminal + = [(Square <$> arbitrary) <*> arbitrary, Circle <$> arbitrary, + Polygon <$> arbitrary, pure Empty, pure Full] + in + sized + $ (\ n + -> case n <= 1 of + True -> oneof terminal + False + -> oneof + $ ([(Rotate2 <$> arbitrary) <*> scale (subtract 1) arbitrary, + Complement <$> scale (subtract 1) arbitrary, + (UnionR <$> arbitrary) <*> scale (subtract 1) arbitrary, + ((DifferenceR <$> arbitrary) <*> scale (flip div 2) arbitrary) + <*> scale (flip div 2) arbitrary, + (IntersectR <$> arbitrary) <*> scale (subtract 1) arbitrary, + ((Translate <$> arbitrary) <*> arbitrary) + <*> scale (subtract 1) arbitrary, + ((Scale <$> arbitrary) <*> arbitrary) + <*> scale (subtract 1) arbitrary, + ((Mirror <$> arbitrary) <*> arbitrary) + <*> scale (subtract 1) arbitrary, + (Outset <$> arbitrary) <*> scale (subtract 1) arbitrary, + (Shell <$> arbitrary) <*> scale (subtract 1) arbitrary, + (WithRounding <$> arbitrary) <*> scale (subtract 1) arbitrary] + <> terminal)) + diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenArbitrary.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenArbitrary.hs new file mode 100644 index 0000000000..f45d2d1fea --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenArbitrary.hs @@ -0,0 +1,26 @@ +-- Emulate a quickcheck import; deriveArbitrary works on any type with the +-- right name and kind +data Gen a + +data Obj + = Square Int Int + | Circle Int + | Polygon [(Int, Int)] + | Rotate2 Double Obj + | Empty + | Full + | Complement Obj + | UnionR Double [Obj] + | DifferenceR Double Obj [Obj] + | IntersectR Double [Obj] + | Translate Double Double Obj + | Scale Double Double Obj + | Mirror Double Double Obj + | Outset Double Obj + | Shell Double Obj + | WithRounding Double Obj + + +arbitrary :: Gen Obj +arbitrary = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenArbitrarySingleConstructor.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenArbitrarySingleConstructor.expected.hs new file mode 100644 index 0000000000..786e381ca8 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenArbitrarySingleConstructor.expected.hs @@ -0,0 +1,7 @@ +data Gen a + +data Obj = Obj Int Bool Char String + +arbitrary :: Gen Obj +arbitrary + = (((Obj <$> arbitrary) <*> arbitrary) <*> arbitrary) <*> arbitrary \ No newline at end of file diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenArbitrarySingleConstructor.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenArbitrarySingleConstructor.hs new file mode 100644 index 0000000000..a6a7d171a3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenArbitrarySingleConstructor.hs @@ -0,0 +1,6 @@ +data Gen a + +data Obj = Obj Int Bool Char String + +arbitrary :: Gen Obj +arbitrary = _ \ No newline at end of file diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenBigTuple.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenBigTuple.expected.hs new file mode 100644 index 0000000000..1e7ccecde4 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenBigTuple.expected.hs @@ -0,0 +1,4 @@ +-- There used to be a bug where we were unable to perform a nested split. The +-- more serious regression test of this is 'AutoTupleSpec'. +bigTuple :: (a, b, c, d) -> (a, b, (c, d)) +bigTuple (a, b, c, d) = (a, b, (c, d)) diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenBigTuple.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenBigTuple.hs new file mode 100644 index 0000000000..1ede521a5f --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenBigTuple.hs @@ -0,0 +1,4 @@ +-- There used to be a bug where we were unable to perform a nested split. The +-- more serious regression test of this is 'AutoTupleSpec'. +bigTuple :: (a, b, c, d) -> (a, b, (c, d)) +bigTuple = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenEitherAuto.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenEitherAuto.expected.hs new file mode 100644 index 0000000000..f7756898e0 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenEitherAuto.expected.hs @@ -0,0 +1,3 @@ +either' :: (a -> c) -> (b -> c) -> Either a b -> c +either' fac _ (Left a) = fac a +either' _ fbc (Right b) = fbc b diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenEitherAuto.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenEitherAuto.hs new file mode 100644 index 0000000000..eb34cd8209 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenEitherAuto.hs @@ -0,0 +1,2 @@ +either' :: (a -> c) -> (b -> c) -> Either a b -> c +either' = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenEitherHomomorphic.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenEitherHomomorphic.expected.hs new file mode 100644 index 0000000000..c18f2ec476 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenEitherHomomorphic.expected.hs @@ -0,0 +1,3 @@ +eitherSplit :: a -> Either (a -> b) (a -> c) -> Either b c +eitherSplit a (Left fab) = Left (fab a) +eitherSplit a (Right fac) = Right (fac a) diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenEitherHomomorphic.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenEitherHomomorphic.hs new file mode 100644 index 0000000000..dee865d1a2 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenEitherHomomorphic.hs @@ -0,0 +1,2 @@ +eitherSplit :: a -> Either (a -> b) (a -> c) -> Either b c +eitherSplit = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenFish.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenFish.hs new file mode 100644 index 0000000000..ce38700b58 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenFish.hs @@ -0,0 +1,5 @@ +-- There was an old bug where we would only pull skolems from the hole, rather +-- than the entire hypothesis. Because of this, the 'b' here would be +-- considered a univar, which could then be unified with the skolem 'c'. +fish :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c +fish amb bmc a = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenFmapTree.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenFmapTree.expected.hs new file mode 100644 index 0000000000..2b32b3a9cd --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenFmapTree.expected.hs @@ -0,0 +1,5 @@ +data Tree a = Leaf a | Branch (Tree a) (Tree a) + +instance Functor Tree where + fmap fab (Leaf a) = Leaf (fab a) + fmap fab (Branch tr' tr_a) = Branch (fmap fab tr') (fmap fab tr_a) diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenFmapTree.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenFmapTree.hs new file mode 100644 index 0000000000..679e7902df --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenFmapTree.hs @@ -0,0 +1,4 @@ +data Tree a = Leaf a | Branch (Tree a) (Tree a) + +instance Functor Tree where + fmap = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenFoldr.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenFoldr.expected.hs new file mode 100644 index 0000000000..89db0adb76 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenFoldr.expected.hs @@ -0,0 +1,3 @@ +foldr2 :: (a -> b -> b) -> b -> [a] -> b +foldr2 _ b [] = b +foldr2 fabb b (a : as') = fabb a (foldr2 fabb b as') diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenFoldr.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenFoldr.hs new file mode 100644 index 0000000000..bade9c1e7a --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenFoldr.hs @@ -0,0 +1,2 @@ +foldr2 :: (a -> b -> b) -> b -> [a] -> b +foldr2 = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenFromMaybe.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenFromMaybe.expected.hs new file mode 100644 index 0000000000..5b39ea5a4b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenFromMaybe.expected.hs @@ -0,0 +1,3 @@ +fromMaybe :: a -> Maybe a -> a +fromMaybe a Nothing = a +fromMaybe _ (Just a') = a' diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenFromMaybe.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenFromMaybe.hs new file mode 100644 index 0000000000..e3046a29c3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenFromMaybe.hs @@ -0,0 +1,2 @@ +fromMaybe :: a -> Maybe a -> a +fromMaybe = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTAuto.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTAuto.expected.hs new file mode 100644 index 0000000000..88f33dd2da --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTAuto.expected.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE GADTs #-} +module GoldenGADTAuto where +data CtxGADT a where + MkCtxGADT :: (Show a, Eq a) => a -> CtxGADT a + +ctxGADT :: CtxGADT () +ctxGADT = MkCtxGADT () diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTAuto.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTAuto.hs new file mode 100644 index 0000000000..1c47dd0e07 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTAuto.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE GADTs #-} +module GoldenGADTAuto where +data CtxGADT a where + MkCtxGADT :: (Show a, Eq a) => a -> CtxGADT a + +ctxGADT :: CtxGADT () +ctxGADT = _auto diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTDestruct.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTDestruct.expected.hs new file mode 100644 index 0000000000..3f5f4fa157 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTDestruct.expected.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE GADTs #-} +module GoldenGADTDestruct where +data CtxGADT where + MkCtxGADT :: (Show a, Eq a) => a -> CtxGADT + +ctxGADT :: CtxGADT -> String +ctxGADT (MkCtxGADT a) = _w0 diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTDestruct.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTDestruct.hs new file mode 100644 index 0000000000..588cf362a2 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTDestruct.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE GADTs #-} +module GoldenGADTDestruct where +data CtxGADT where + MkCtxGADT :: (Show a, Eq a) => a -> CtxGADT + +ctxGADT :: CtxGADT -> String +ctxGADT gadt = _decons diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTDestructCoercion.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTDestructCoercion.expected.hs new file mode 100644 index 0000000000..4f4b2d3a4a --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTDestructCoercion.expected.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE GADTs #-} +module GoldenGADTDestruct where +data E a b where + E :: forall a b. (b ~ a, Ord a) => b -> E a [a] + +ctxGADT :: E a b -> String +ctxGADT (E b) = _w0 diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTDestructCoercion.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTDestructCoercion.hs new file mode 100644 index 0000000000..9eca759e85 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenGADTDestructCoercion.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE GADTs #-} +module GoldenGADTDestruct where +data E a b where + E :: forall a b. (b ~ a, Ord a) => b -> E a [a] + +ctxGADT :: E a b -> String +ctxGADT gadt = _decons diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenIdTypeFam.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenIdTypeFam.expected.hs new file mode 100644 index 0000000000..7b3d1beda0 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenIdTypeFam.expected.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE TypeFamilies #-} + +type family TyFam +type instance TyFam = Int + +tyblah' :: TyFam -> Int +tyblah' = id diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenIdTypeFam.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenIdTypeFam.hs new file mode 100644 index 0000000000..be8903fec0 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenIdTypeFam.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE TypeFamilies #-} + +type family TyFam +type instance TyFam = Int + +tyblah' :: TyFam -> Int +tyblah' = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenIdentityFunctor.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenIdentityFunctor.expected.hs new file mode 100644 index 0000000000..5c509d6507 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenIdentityFunctor.expected.hs @@ -0,0 +1,3 @@ +data Ident a = Ident a +instance Functor Ident where + fmap fab (Ident a) = Ident (fab a) diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenIdentityFunctor.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenIdentityFunctor.hs new file mode 100644 index 0000000000..6d1de50992 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenIdentityFunctor.hs @@ -0,0 +1,3 @@ +data Ident a = Ident a +instance Functor Ident where + fmap = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenIntros.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenIntros.expected.hs new file mode 100644 index 0000000000..0ae8c4bbac --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenIntros.expected.hs @@ -0,0 +1,2 @@ +blah :: Int -> Bool -> (a -> b) -> String -> Int +blah n b fab s = _w0 diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenIntros.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenIntros.hs new file mode 100644 index 0000000000..5b4e6e241f --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenIntros.hs @@ -0,0 +1,2 @@ +blah :: Int -> Bool -> (a -> b) -> String -> Int +blah = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenJoinCont.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenJoinCont.expected.hs new file mode 100644 index 0000000000..e941214796 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenJoinCont.expected.hs @@ -0,0 +1,4 @@ +type Cont r a = ((a -> r) -> r) + +joinCont :: Cont r (Cont r a) -> Cont r a +joinCont f far = f (\ g -> g far) diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenJoinCont.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenJoinCont.hs new file mode 100644 index 0000000000..f2c63714da --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenJoinCont.hs @@ -0,0 +1,4 @@ +type Cont r a = ((a -> r) -> r) + +joinCont :: Cont r (Cont r a) -> Cont r a +joinCont = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenListFmap.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenListFmap.expected.hs new file mode 100644 index 0000000000..ec44241736 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenListFmap.expected.hs @@ -0,0 +1,3 @@ +fmapList :: (a -> b) -> [a] -> [b] +fmapList _ [] = [] +fmapList fab (a : as') = fab a : fmapList fab as' diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenListFmap.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenListFmap.hs new file mode 100644 index 0000000000..85293daaf4 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenListFmap.hs @@ -0,0 +1,2 @@ +fmapList :: (a -> b) -> [a] -> [b] +fmapList = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenNote.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenNote.expected.hs new file mode 100644 index 0000000000..99bc0cd6d0 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenNote.expected.hs @@ -0,0 +1,3 @@ +note :: e -> Maybe a -> Either e a +note e Nothing = Left e +note _ (Just a) = Right a diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenNote.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenNote.hs new file mode 100644 index 0000000000..c9e0c820e4 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenNote.hs @@ -0,0 +1,2 @@ +note :: e -> Maybe a -> Either e a +note = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenPureList.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenPureList.expected.hs new file mode 100644 index 0000000000..8f2bc80ea7 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenPureList.expected.hs @@ -0,0 +1,2 @@ +pureList :: a -> [a] +pureList a = a : [] diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenPureList.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenPureList.hs new file mode 100644 index 0000000000..3a3293b4ec --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenPureList.hs @@ -0,0 +1,2 @@ +pureList :: a -> [a] +pureList = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenSafeHead.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenSafeHead.expected.hs new file mode 100644 index 0000000000..7f8f73e5b7 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenSafeHead.expected.hs @@ -0,0 +1,3 @@ +safeHead :: [x] -> Maybe x +safeHead [] = Nothing +safeHead (x : _) = Just x diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenSafeHead.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenSafeHead.hs new file mode 100644 index 0000000000..6a5d27c0d1 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenSafeHead.hs @@ -0,0 +1,2 @@ +safeHead :: [x] -> Maybe x +safeHead = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenShow.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenShow.expected.hs new file mode 100644 index 0000000000..05ba83e9fe --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenShow.expected.hs @@ -0,0 +1,2 @@ +showMe :: Show a => a -> String +showMe = show diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenShow.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenShow.hs new file mode 100644 index 0000000000..9ec5e27bcf --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenShow.hs @@ -0,0 +1,2 @@ +showMe :: Show a => a -> String +showMe = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenShowCompose.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenShowCompose.expected.hs new file mode 100644 index 0000000000..d8a78b3017 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenShowCompose.expected.hs @@ -0,0 +1,2 @@ +showCompose :: Show a => (b -> a) -> b -> String +showCompose fba = show . fba diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenShowCompose.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenShowCompose.hs new file mode 100644 index 0000000000..c99768e4e5 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenShowCompose.hs @@ -0,0 +1,2 @@ +showCompose :: Show a => (b -> a) -> b -> String +showCompose = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenShowMapChar.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenShowMapChar.expected.hs new file mode 100644 index 0000000000..c32357d1a9 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenShowMapChar.expected.hs @@ -0,0 +1,2 @@ +test :: Show a => a -> (String -> b) -> b +test a f = f (show a) diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenShowMapChar.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenShowMapChar.hs new file mode 100644 index 0000000000..8e6e5eae6b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenShowMapChar.hs @@ -0,0 +1,2 @@ +test :: Show a => a -> (String -> b) -> b +test = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenSuperclass.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenSuperclass.expected.hs new file mode 100644 index 0000000000..e0a5dbb565 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenSuperclass.expected.hs @@ -0,0 +1,8 @@ +class Super a where + super :: a + +class Super a => Sub a + +blah :: Sub a => a +blah = super + diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenSuperclass.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenSuperclass.hs new file mode 100644 index 0000000000..86a9fed7bc --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenSuperclass.hs @@ -0,0 +1,8 @@ +class Super a where + super :: a + +class Super a => Sub a + +blah :: Sub a => a +blah = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenSwap.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenSwap.expected.hs new file mode 100644 index 0000000000..e09cb3800a --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenSwap.expected.hs @@ -0,0 +1,2 @@ +swap :: (a, b) -> (b, a) +swap (a, b) = (b, a) diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenSwap.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenSwap.hs new file mode 100644 index 0000000000..9243955c54 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenSwap.hs @@ -0,0 +1,2 @@ +swap :: (a, b) -> (b, a) +swap = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenSwapMany.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenSwapMany.expected.hs new file mode 100644 index 0000000000..1d2bc0a605 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenSwapMany.expected.hs @@ -0,0 +1,2 @@ +swapMany :: (a, b, c, d, e) -> (e, d, c, b, a) +swapMany (a, b, c, d, e) = (e, d, c, b, a) diff --git a/plugins/hls-tactics-plugin/new/test/golden/GoldenSwapMany.hs b/plugins/hls-tactics-plugin/new/test/golden/GoldenSwapMany.hs new file mode 100644 index 0000000000..b1f6c0fb2a --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/GoldenSwapMany.hs @@ -0,0 +1,2 @@ +swapMany :: (a, b, c, d, e) -> (e, d, c, b, a) +swapMany = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/IntroDestructLetBinding.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/IntroDestructLetBinding.expected.hs new file mode 100644 index 0000000000..0039ab768e --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/IntroDestructLetBinding.expected.hs @@ -0,0 +1,6 @@ +test :: IO () +test = do + let x :: Bool -> Int + x False = _w0 + x True = _w1 + pure () diff --git a/plugins/hls-tactics-plugin/new/test/golden/IntroDestructLetBinding.hs b/plugins/hls-tactics-plugin/new/test/golden/IntroDestructLetBinding.hs new file mode 100644 index 0000000000..bf12200131 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/IntroDestructLetBinding.hs @@ -0,0 +1,5 @@ +test :: IO () +test = do + let x :: Bool -> Int + x = _ + pure () diff --git a/plugins/hls-tactics-plugin/new/test/golden/IntroDestructMany.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/IntroDestructMany.expected.hs new file mode 100644 index 0000000000..462e5edf99 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/IntroDestructMany.expected.hs @@ -0,0 +1,4 @@ +x :: Bool -> Maybe Int -> String -> Int +x False = _w0 +x True = _w1 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/IntroDestructMany.hs b/plugins/hls-tactics-plugin/new/test/golden/IntroDestructMany.hs new file mode 100644 index 0000000000..98a4bd552c --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/IntroDestructMany.hs @@ -0,0 +1,3 @@ +x :: Bool -> Maybe Int -> String -> Int +x = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/IntroDestructOne.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/IntroDestructOne.expected.hs new file mode 100644 index 0000000000..4ba80e2455 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/IntroDestructOne.expected.hs @@ -0,0 +1,6 @@ +module Test where + +x :: Bool -> Int +x False = _w0 +x True = _w1 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/IntroDestructOne.hs b/plugins/hls-tactics-plugin/new/test/golden/IntroDestructOne.hs new file mode 100644 index 0000000000..2afdc50ca5 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/IntroDestructOne.hs @@ -0,0 +1,5 @@ +module Test where + +x :: Bool -> Int +x = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/IntroDestructProvider.hs b/plugins/hls-tactics-plugin/new/test/golden/IntroDestructProvider.hs new file mode 100644 index 0000000000..f0d127dd50 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/IntroDestructProvider.hs @@ -0,0 +1,9 @@ +hasAlgTy :: Maybe Int -> Int +hasAlgTy = _ + +hasFunTy :: (Int -> Int) -> Int +hasFunTy = _ + +isSaturated :: Bool -> Int +isSaturated b = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/IntrosTooMany.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/IntrosTooMany.expected.hs new file mode 100644 index 0000000000..97668d8c90 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/IntrosTooMany.expected.hs @@ -0,0 +1,2 @@ +too_many :: a -> b -> c +too_many a b = _w0 diff --git a/plugins/hls-tactics-plugin/new/test/golden/IntrosTooMany.hs b/plugins/hls-tactics-plugin/new/test/golden/IntrosTooMany.hs new file mode 100644 index 0000000000..066f123a47 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/IntrosTooMany.hs @@ -0,0 +1,2 @@ +too_many :: a -> b -> c +too_many = [wingman| intros a b c d e f g h i j k l m n o p q r s t u v w x y z |] diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownBigSemigroup.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownBigSemigroup.expected.hs new file mode 100644 index 0000000000..c97ba98a6a --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownBigSemigroup.expected.hs @@ -0,0 +1,9 @@ +import Data.Monoid + +data Big a = Big [Bool] (Sum Int) String (Endo a) Any + +instance Semigroup (Big a) where + (Big bs sum s en any) <> (Big bs' sum' str en' any') + = Big + (bs <> bs') (sum <> sum') (s <> str) (en <> en') (any <> any') + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownBigSemigroup.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownBigSemigroup.hs new file mode 100644 index 0000000000..49ea10b8b4 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownBigSemigroup.hs @@ -0,0 +1,7 @@ +import Data.Monoid + +data Big a = Big [Bool] (Sum Int) String (Endo a) Any + +instance Semigroup (Big a) where + (<>) = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownCounterfactualSemigroup.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownCounterfactualSemigroup.expected.hs new file mode 100644 index 0000000000..8bef710c69 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownCounterfactualSemigroup.expected.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE UndecidableInstances #-} + +data Semi = Semi [String] Int + +instance Semigroup Int => Semigroup Semi where + (Semi ss n) <> (Semi strs i) = Semi (ss <> strs) (n <> i) + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownCounterfactualSemigroup.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownCounterfactualSemigroup.hs new file mode 100644 index 0000000000..11e53f4191 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownCounterfactualSemigroup.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE UndecidableInstances #-} + +data Semi = Semi [String] Int + +instance Semigroup Int => Semigroup Semi where + (<>) = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownDestructedSemigroup.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownDestructedSemigroup.expected.hs new file mode 100644 index 0000000000..179937cb6a --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownDestructedSemigroup.expected.hs @@ -0,0 +1,5 @@ +data Test a = Test [a] + +instance Semigroup (Test a) where + (Test a) <> (Test c) = Test (a <> c) + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownDestructedSemigroup.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownDestructedSemigroup.hs new file mode 100644 index 0000000000..ed4182c6d9 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownDestructedSemigroup.hs @@ -0,0 +1,5 @@ +data Test a = Test [a] + +instance Semigroup (Test a) where + Test a <> Test c = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownMissingMonoid.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownMissingMonoid.expected.hs new file mode 100644 index 0000000000..f64222977b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownMissingMonoid.expected.hs @@ -0,0 +1,8 @@ +data Mono a = Monoid [String] a + +instance Semigroup (Mono a) where + (<>) = undefined + +instance Monoid (Mono a) where + mempty = Monoid mempty _w0 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownMissingMonoid.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownMissingMonoid.hs new file mode 100644 index 0000000000..7c6bfc5ccd --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownMissingMonoid.hs @@ -0,0 +1,8 @@ +data Mono a = Monoid [String] a + +instance Semigroup (Mono a) where + (<>) = undefined + +instance Monoid (Mono a) where + mempty = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownMissingSemigroup.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownMissingSemigroup.expected.hs new file mode 100644 index 0000000000..3f18919e80 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownMissingSemigroup.expected.hs @@ -0,0 +1,5 @@ +data Semi = Semi [String] Int + +instance Semigroup Semi where + (Semi ss n) <> (Semi strs i) = Semi (ss <> strs) _w0 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownMissingSemigroup.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownMissingSemigroup.hs new file mode 100644 index 0000000000..1193c14a3b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownMissingSemigroup.hs @@ -0,0 +1,5 @@ +data Semi = Semi [String] Int + +instance Semigroup Semi where + (<>) = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownModuleInstanceSemigroup.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownModuleInstanceSemigroup.expected.hs new file mode 100644 index 0000000000..627217b285 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownModuleInstanceSemigroup.expected.hs @@ -0,0 +1,12 @@ +data Foo = Foo + +instance Semigroup Foo where + (<>) _ _ = Foo + + +data Bar = Bar Foo Foo + +instance Semigroup Bar where + (Bar foo foo') <> (Bar foo2 foo3) + = Bar (foo <> foo2) (foo' <> foo3) + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownModuleInstanceSemigroup.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownModuleInstanceSemigroup.hs new file mode 100644 index 0000000000..8a03a029af --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownModuleInstanceSemigroup.hs @@ -0,0 +1,11 @@ +data Foo = Foo + +instance Semigroup Foo where + (<>) _ _ = Foo + + +data Bar = Bar Foo Foo + +instance Semigroup Bar where + (<>) = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownMonoid.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownMonoid.expected.hs new file mode 100644 index 0000000000..6ad1e2bf92 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownMonoid.expected.hs @@ -0,0 +1,8 @@ +data Mono = Monoid [String] + +instance Semigroup Mono where + (<>) = undefined + +instance Monoid Mono where + mempty = Monoid mempty + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownMonoid.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownMonoid.hs new file mode 100644 index 0000000000..0667bee28c --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownMonoid.hs @@ -0,0 +1,8 @@ +data Mono = Monoid [String] + +instance Semigroup Mono where + (<>) = undefined + +instance Monoid Mono where + mempty = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownPolyMonoid.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownPolyMonoid.expected.hs new file mode 100644 index 0000000000..317f2e770b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownPolyMonoid.expected.hs @@ -0,0 +1,8 @@ +data Mono a = Monoid [String] a + +instance Semigroup (Mono a) where + (<>) = undefined + +instance Monoid a => Monoid (Mono a) where + mempty = Monoid mempty mempty + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownPolyMonoid.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownPolyMonoid.hs new file mode 100644 index 0000000000..8ba7bc6d98 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownPolyMonoid.hs @@ -0,0 +1,8 @@ +data Mono a = Monoid [String] a + +instance Semigroup (Mono a) where + (<>) = undefined + +instance Monoid a => Monoid (Mono a) where + mempty = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownThetaSemigroup.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownThetaSemigroup.expected.hs new file mode 100644 index 0000000000..3711af103a --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownThetaSemigroup.expected.hs @@ -0,0 +1,5 @@ +data Semi a = Semi a + +instance Semigroup a => Semigroup (Semi a) where + (Semi a) <> (Semi a') = Semi (a <> a') + diff --git a/plugins/hls-tactics-plugin/new/test/golden/KnownThetaSemigroup.hs b/plugins/hls-tactics-plugin/new/test/golden/KnownThetaSemigroup.hs new file mode 100644 index 0000000000..f5e38276fe --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/KnownThetaSemigroup.hs @@ -0,0 +1,5 @@ +data Semi a = Semi a + +instance Semigroup a => Semigroup (Semi a) where + (<>) = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutBind.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutBind.expected.hs new file mode 100644 index 0000000000..c65b7d07d0 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutBind.expected.hs @@ -0,0 +1,8 @@ +test :: Bool -> IO () +test b = do + putStrLn "hello" + case b of + False -> _w0 + True -> _w1 + pure () + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutBind.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutBind.hs new file mode 100644 index 0000000000..4598f0eba1 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutBind.hs @@ -0,0 +1,6 @@ +test :: Bool -> IO () +test b = do + putStrLn "hello" + _ + pure () + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutDollarApp.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutDollarApp.expected.hs new file mode 100644 index 0000000000..32e08c94a8 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutDollarApp.expected.hs @@ -0,0 +1,5 @@ +test :: Bool -> Bool +test b = id $ (case b of + False -> _w0 + True -> _w1) + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutDollarApp.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutDollarApp.hs new file mode 100644 index 0000000000..83a3e4785b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutDollarApp.hs @@ -0,0 +1,3 @@ +test :: Bool -> Bool +test b = id $ _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutInfixKeep.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutInfixKeep.expected.hs new file mode 100644 index 0000000000..b4d3ee6a0e --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutInfixKeep.expected.hs @@ -0,0 +1,5 @@ +-- keep layout that was written by the user in infix +foo :: Bool -> a -> a +False `foo` a = _w0 +True `foo` a = _w1 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutInfixKeep.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutInfixKeep.hs new file mode 100644 index 0000000000..60d198e5da --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutInfixKeep.hs @@ -0,0 +1,4 @@ +-- keep layout that was written by the user in infix +foo :: Bool -> a -> a +b `foo` a = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutLam.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutLam.expected.hs new file mode 100644 index 0000000000..d8b34c8939 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutLam.expected.hs @@ -0,0 +1,5 @@ +test :: Bool -> Bool +test = \b -> case b of + False -> _w0 + True -> _w1 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutLam.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutLam.hs new file mode 100644 index 0000000000..3fead2a25d --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutLam.hs @@ -0,0 +1,3 @@ +test :: Bool -> Bool +test = \b -> _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutOpApp.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutOpApp.expected.hs new file mode 100644 index 0000000000..e8bc6ccc87 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutOpApp.expected.hs @@ -0,0 +1,4 @@ +test :: Bool -> Bool +test b = True && (case b of + False -> _w0 + True -> _w1) diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutOpApp.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutOpApp.hs new file mode 100644 index 0000000000..a4c05b7539 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutOpApp.hs @@ -0,0 +1,2 @@ +test :: Bool -> Bool +test b = True && _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutPrefixKeep.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutPrefixKeep.expected.hs new file mode 100644 index 0000000000..bffe1b6852 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutPrefixKeep.expected.hs @@ -0,0 +1,4 @@ +(-/) :: Bool -> a -> a +(-/) False a = _w0 +(-/) True a = _w1 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutPrefixKeep.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutPrefixKeep.hs new file mode 100644 index 0000000000..bfe7bdafb3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutPrefixKeep.hs @@ -0,0 +1,3 @@ +(-/) :: Bool -> a -> a +(-/) b a = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutRec.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutRec.expected.hs new file mode 100644 index 0000000000..ef639a9839 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutRec.expected.hs @@ -0,0 +1,5 @@ +data Pair a b = Pair {pa :: a, pb :: b} + +p :: Pair (a -> a) (a -> b -> c -> b) +p = Pair {pa = _, pb = \ a b c -> _w0} + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutRec.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutRec.hs new file mode 100644 index 0000000000..47a9895c2e --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutRec.hs @@ -0,0 +1,5 @@ +data Pair a b = Pair {pa :: a, pb :: b} + +p :: Pair (a -> a) (a -> b -> c -> b) +p = Pair {pa = _, pb = _} + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitClass.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitClass.expected.hs new file mode 100644 index 0000000000..9bcb21c9e7 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitClass.expected.hs @@ -0,0 +1,5 @@ +class Test a where + test :: Bool -> a + test False = _w0 + test True = _w1 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitClass.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitClass.hs new file mode 100644 index 0000000000..c082169c7b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitClass.hs @@ -0,0 +1,4 @@ +class Test a where + test :: Bool -> a + test x = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitGuard.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitGuard.expected.hs new file mode 100644 index 0000000000..6b73dfb0ec --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitGuard.expected.hs @@ -0,0 +1,5 @@ +test :: Bool -> Bool -> Bool +test a b + | a = case b of + False -> _w0 + True -> _w1 diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitGuard.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitGuard.hs new file mode 100644 index 0000000000..be2d0d30f5 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitGuard.hs @@ -0,0 +1,3 @@ +test :: Bool -> Bool -> Bool +test a b + | a = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitIn.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitIn.expected.hs new file mode 100644 index 0000000000..8095217673 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitIn.expected.hs @@ -0,0 +1,5 @@ +test :: a +test = + let a = (1,"bbb") + in case a of { (n, s) -> _w0 } + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitIn.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitIn.hs new file mode 100644 index 0000000000..ce6e0341c4 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitIn.hs @@ -0,0 +1,5 @@ +test :: a +test = + let a = (1,"bbb") + in _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitLet.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitLet.expected.hs new file mode 100644 index 0000000000..ba63836df3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitLet.expected.hs @@ -0,0 +1,7 @@ +test :: a +test = + let t :: Bool -> a + t False = _w0 + t True = _w1 + in _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitLet.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitLet.hs new file mode 100644 index 0000000000..71529d7dd3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitLet.hs @@ -0,0 +1,6 @@ +test :: a +test = + let t :: Bool -> a + t b = _ + in _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitPatSyn.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitPatSyn.expected.hs new file mode 100644 index 0000000000..0f7ee4e388 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitPatSyn.expected.hs @@ -0,0 +1,11 @@ +{-# LANGUAGE PatternSynonyms #-} + +pattern JustSingleton :: a -> Maybe [a] +pattern JustSingleton a <- Just [a] + + +test :: Maybe [Bool] -> Maybe Bool +test (JustSingleton False) = _w0 +test (JustSingleton True) = _w1 + + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitPatSyn.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitPatSyn.hs new file mode 100644 index 0000000000..0497bb7244 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitPatSyn.hs @@ -0,0 +1,10 @@ +{-# LANGUAGE PatternSynonyms #-} + +pattern JustSingleton :: a -> Maybe [a] +pattern JustSingleton a <- Just [a] + + +test :: Maybe [Bool] -> Maybe Bool +test (JustSingleton a) = _ + + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitPattern.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitPattern.expected.hs new file mode 100644 index 0000000000..b92544f622 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitPattern.expected.hs @@ -0,0 +1,9 @@ +{-# LANGUAGE PatternSynonyms #-} + +pattern Blah :: a -> Maybe a +pattern Blah a = Just a + +test :: Maybe Bool -> a +test (Blah False) = _w0 +test (Blah True) = _w1 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitPattern.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitPattern.hs new file mode 100644 index 0000000000..3cabb3c64b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitPattern.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE PatternSynonyms #-} + +pattern Blah :: a -> Maybe a +pattern Blah a = Just a + +test :: Maybe Bool -> a +test (Blah a) = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitViewPat.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitViewPat.expected.hs new file mode 100644 index 0000000000..d123c652d7 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitViewPat.expected.hs @@ -0,0 +1,6 @@ +{-# LANGUAGE ViewPatterns #-} + +splitLookup :: [(Int, String)] -> String +splitLookup (lookup 5 -> Nothing) = _w0 +splitLookup (lookup 5 -> (Just s)) = _w1 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitViewPat.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitViewPat.hs new file mode 100644 index 0000000000..6baed55abd --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitViewPat.hs @@ -0,0 +1,5 @@ +{-# LANGUAGE ViewPatterns #-} + +splitLookup :: [(Int, String)] -> String +splitLookup (lookup 5 -> a) = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitWhere.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitWhere.expected.hs new file mode 100644 index 0000000000..28ad669007 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitWhere.expected.hs @@ -0,0 +1,14 @@ +data A = A | B | C + +some :: A -> IO () +some a = do + foo + bar a + where + foo = putStrLn "Hi" + + bar :: A -> IO () + bar A = _w0 + bar B = _w1 + bar C = _w2 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitWhere.hs b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitWhere.hs new file mode 100644 index 0000000000..5035df1b0c --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/LayoutSplitWhere.hs @@ -0,0 +1,12 @@ +data A = A | B | C + +some :: A -> IO () +some a = do + foo + bar a + where + foo = putStrLn "Hi" + + bar :: A -> IO () + bar x = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MessageCantUnify.hs b/plugins/hls-tactics-plugin/new/test/golden/MessageCantUnify.hs new file mode 100644 index 0000000000..713f686338 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MessageCantUnify.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE DataKinds, GADTs #-} + +data Z ab where + Z :: (a -> b) -> Z '(a, b) + +test :: Z ab +test = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MessageForallA.hs b/plugins/hls-tactics-plugin/new/test/golden/MessageForallA.hs new file mode 100644 index 0000000000..1498dfd8e4 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MessageForallA.hs @@ -0,0 +1,2 @@ +test :: a +test = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/MessageNotEnoughGas.hs b/plugins/hls-tactics-plugin/new/test/golden/MessageNotEnoughGas.hs new file mode 100644 index 0000000000..9156cc0053 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MessageNotEnoughGas.hs @@ -0,0 +1,13 @@ +test + :: (a1 -> a2) + -> (a2 -> a3) + -> (a3 -> a4) + -> (a4 -> a5) + -> (a5 -> a6) + -> (a6 -> a7) + -> (a7 -> a8) + -> (a8 -> a9) + -> (a9 -> a10) + -> a1 -> a10 +test = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaBegin.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaBegin.expected.hs new file mode 100644 index 0000000000..3c56bdbee9 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaBegin.expected.hs @@ -0,0 +1 @@ +foo = [wingman||] diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaBegin.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaBegin.hs new file mode 100644 index 0000000000..fdfbd7289d --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaBegin.hs @@ -0,0 +1 @@ +foo = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaBeginNoWildify.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaBeginNoWildify.expected.hs new file mode 100644 index 0000000000..c8aa76e837 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaBeginNoWildify.expected.hs @@ -0,0 +1,2 @@ +foo v = [wingman||] + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaBeginNoWildify.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaBeginNoWildify.hs new file mode 100644 index 0000000000..2aa2d1caa3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaBeginNoWildify.hs @@ -0,0 +1,2 @@ +foo v = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaBindAll.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaBindAll.expected.hs new file mode 100644 index 0000000000..00421ee479 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaBindAll.expected.hs @@ -0,0 +1,2 @@ +foo :: a -> (a, a) +foo a = (a, a) diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaBindAll.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaBindAll.hs new file mode 100644 index 0000000000..d25670bca1 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaBindAll.hs @@ -0,0 +1,2 @@ +foo :: a -> (a, a) +foo a = [wingman| split; assumption |] diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaBindOne.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaBindOne.expected.hs new file mode 100644 index 0000000000..05f86c9963 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaBindOne.expected.hs @@ -0,0 +1,2 @@ +foo :: a -> (a, a) +foo a = (a, _w0) diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaBindOne.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaBindOne.hs new file mode 100644 index 0000000000..fe6c118829 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaBindOne.hs @@ -0,0 +1,2 @@ +foo :: a -> (a, a) +foo a = [wingman| split, assumption |] diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaCataAST.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaCataAST.expected.hs new file mode 100644 index 0000000000..aac10101ec --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaCataAST.expected.hs @@ -0,0 +1,23 @@ +{-# LANGUAGE GADTs #-} + +data AST a where + BoolLit :: Bool -> AST Bool + IntLit :: Int -> AST Int + If :: AST Bool -> AST a -> AST a -> AST a + Equal :: AST a -> AST a -> AST Bool + +eval :: AST a -> a +eval (BoolLit b) = b +eval (IntLit n) = n +eval (If ast ast' ast_a) + = let + ast_c = eval ast + ast'_c = eval ast' + ast_a_c = eval ast_a + in _w0 ast_c ast'_c ast_a_c +eval (Equal ast ast') + = let + ast_c = eval ast + ast'_c = eval ast' + in _w1 ast_c ast'_c + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaCataAST.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaCataAST.hs new file mode 100644 index 0000000000..26e3a03cec --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaCataAST.hs @@ -0,0 +1,11 @@ +{-# LANGUAGE GADTs #-} + +data AST a where + BoolLit :: Bool -> AST Bool + IntLit :: Int -> AST Int + If :: AST Bool -> AST a -> AST a -> AST a + Equal :: AST a -> AST a -> AST Bool + +eval :: AST a -> a +eval = [wingman| intros x, cata x; collapse |] + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaCataCollapse.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaCataCollapse.expected.hs new file mode 100644 index 0000000000..58b4fb4ffc --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaCataCollapse.expected.hs @@ -0,0 +1,14 @@ +{-# LANGUAGE TypeOperators #-} + +import GHC.Generics + +class Yo f where + yo :: f x -> Int + +instance (Yo f, Yo g) => Yo (f :*: g) where + yo (fx :*: gx) + = let + fx_c = yo fx + gx_c = yo gx + in _w0 fx_c gx_c + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaCataCollapse.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaCataCollapse.hs new file mode 100644 index 0000000000..14dc163f4d --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaCataCollapse.hs @@ -0,0 +1,10 @@ +{-# LANGUAGE TypeOperators #-} + +import GHC.Generics + +class Yo f where + yo :: f x -> Int + +instance (Yo f, Yo g) => Yo (f :*: g) where + yo = [wingman| intros x, cata x, collapse |] + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaCataCollapseUnary.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaCataCollapseUnary.expected.hs new file mode 100644 index 0000000000..e9cef291a3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaCataCollapseUnary.expected.hs @@ -0,0 +1,8 @@ +import GHC.Generics + +class Yo f where + yo :: f x -> Int + +instance (Yo f) => Yo (M1 _1 _2 f) where + yo (M1 fx) = yo fx + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaCataCollapseUnary.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaCataCollapseUnary.hs new file mode 100644 index 0000000000..c1abb0acf2 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaCataCollapseUnary.hs @@ -0,0 +1,8 @@ +import GHC.Generics + +class Yo f where + yo :: f x -> Int + +instance (Yo f) => Yo (M1 _1 _2 f) where + yo = [wingman| intros x, cata x, collapse |] + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaChoice.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaChoice.expected.hs new file mode 100644 index 0000000000..c9d2f0cff9 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaChoice.expected.hs @@ -0,0 +1,2 @@ +reassoc :: (a, (b, c)) -> ((a, b), c) +reassoc (a, (b, c)) = ((a, b), c) diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaChoice.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaChoice.hs new file mode 100644 index 0000000000..97e5b424ba --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaChoice.hs @@ -0,0 +1,2 @@ +reassoc :: (a, (b, c)) -> ((a, b), c) +reassoc (a, (b, c)) = [wingman| split; split | assume c; assume a | assume b |] diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaDeepOf.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaDeepOf.expected.hs new file mode 100644 index 0000000000..90216da0a2 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaDeepOf.expected.hs @@ -0,0 +1,8 @@ +whats_it_deep_of + :: (a -> a) + -> [(Int, Either Bool (Maybe [a]))] + -> [(Int, Either Bool (Maybe [a]))] +-- The assumption here is necessary to tie-break in favor of the longest +-- nesting of fmaps. +whats_it_deep_of f = fmap (fmap (fmap (fmap (fmap f)))) + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaDeepOf.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaDeepOf.hs new file mode 100644 index 0000000000..3afcdcc4e1 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaDeepOf.hs @@ -0,0 +1,8 @@ +whats_it_deep_of + :: (a -> a) + -> [(Int, Either Bool (Maybe [a]))] + -> [(Int, Either Bool (Maybe [a]))] +-- The assumption here is necessary to tie-break in favor of the longest +-- nesting of fmaps. +whats_it_deep_of f = [wingman| nested fmap, assumption |] + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaFundeps.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaFundeps.expected.hs new file mode 100644 index 0000000000..f589d989f7 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaFundeps.expected.hs @@ -0,0 +1,16 @@ +{-# LANGUAGE FunctionalDependencies #-} +{-# LANGUAGE MultiParamTypeClasses #-} + +class Blah a b | a -> b, b -> a +instance Blah Int Bool + +foo :: Int +foo = 10 + +bar :: Blah a b => a -> b +bar = undefined + +qux :: Bool +qux = bar foo + + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaFundeps.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaFundeps.hs new file mode 100644 index 0000000000..36d0d4bf73 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaFundeps.hs @@ -0,0 +1,16 @@ +{-# LANGUAGE FunctionalDependencies #-} +{-# LANGUAGE MultiParamTypeClasses #-} + +class Blah a b | a -> b, b -> a +instance Blah Int Bool + +foo :: Int +foo = 10 + +bar :: Blah a b => a -> b +bar = undefined + +qux :: Bool +qux = [wingman| use bar, use foo |] + + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaIdiom.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaIdiom.expected.hs new file mode 100644 index 0000000000..21569c7c19 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaIdiom.expected.hs @@ -0,0 +1,6 @@ +foo :: Int -> Int -> Int +foo = undefined + +test :: Maybe Int +test = (foo <$> _w0) <*> _w1 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaIdiom.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaIdiom.hs new file mode 100644 index 0000000000..f9506cb03b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaIdiom.hs @@ -0,0 +1,6 @@ +foo :: Int -> Int -> Int +foo = undefined + +test :: Maybe Int +test = [wingman| idiom (use foo) |] + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaIdiomRecord.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaIdiomRecord.expected.hs new file mode 100644 index 0000000000..e39e9a9fab --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaIdiomRecord.expected.hs @@ -0,0 +1,8 @@ +data Rec = Rec + { a :: Int + , b :: Bool + } + +test :: Maybe Rec +test = (Rec <$> _w0) <*> _w1 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaIdiomRecord.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaIdiomRecord.hs new file mode 100644 index 0000000000..87397da160 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaIdiomRecord.hs @@ -0,0 +1,8 @@ +data Rec = Rec + { a :: Int + , b :: Bool + } + +test :: Maybe Rec +test = [wingman| idiom (ctor Rec) |] + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaLetSimple.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaLetSimple.expected.hs new file mode 100644 index 0000000000..54c3678c21 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaLetSimple.expected.hs @@ -0,0 +1,7 @@ +test :: Int +test + = let + a = _w0 + b = _w1 + c = _w2 + in _w3 diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaLetSimple.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaLetSimple.hs new file mode 100644 index 0000000000..ae570bae7b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaLetSimple.hs @@ -0,0 +1,2 @@ +test :: Int +test = [wingman| let a b c |] diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaMaybeAp.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaMaybeAp.expected.hs new file mode 100644 index 0000000000..e0b60b74fa --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaMaybeAp.expected.hs @@ -0,0 +1,5 @@ +maybeAp :: Maybe (a -> b) -> Maybe a -> Maybe b +maybeAp Nothing Nothing = Nothing +maybeAp Nothing (Just _) = Nothing +maybeAp (Just _) Nothing = Nothing +maybeAp (Just fab) (Just a) = Just (fab a) diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaMaybeAp.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaMaybeAp.hs new file mode 100644 index 0000000000..6159db4ecd --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaMaybeAp.hs @@ -0,0 +1,11 @@ +maybeAp :: Maybe (a -> b) -> Maybe a -> Maybe b +maybeAp = [wingman| + intros, + destruct_all, + obvious, + obvious, + obvious, + ctor Just, + application, + assumption + |] diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaPointwise.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaPointwise.expected.hs new file mode 100644 index 0000000000..f92e7d40af --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaPointwise.expected.hs @@ -0,0 +1,8 @@ +import Data.Monoid + +data Foo = Foo (Sum Int) (Sum Int) + +mappend2 :: Foo -> Foo -> Foo +mappend2 (Foo sum sum') (Foo sum2 sum3) + = Foo (mappend sum sum2) (mappend sum' sum3) + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaPointwise.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaPointwise.hs new file mode 100644 index 0000000000..77572569ff --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaPointwise.hs @@ -0,0 +1,7 @@ +import Data.Monoid + +data Foo = Foo (Sum Int) (Sum Int) + +mappend2 :: Foo -> Foo -> Foo +mappend2 = [wingman| intros f1 f2, destruct_all, ctor Foo; pointwise (use mappend); assumption|] + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaTry.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaTry.expected.hs new file mode 100644 index 0000000000..0940f9ea21 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaTry.expected.hs @@ -0,0 +1,2 @@ +foo :: a -> (b, a) +foo a = (_w0, a) diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaTry.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaTry.hs new file mode 100644 index 0000000000..582189bcbc --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaTry.hs @@ -0,0 +1,2 @@ +foo :: a -> (b, a) +foo a = [wingman| split; try (assumption) |] diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaUseImport.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaUseImport.expected.hs new file mode 100644 index 0000000000..c72f18589c --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaUseImport.expected.hs @@ -0,0 +1,6 @@ +import Data.Char + + +result :: Char -> Bool +result = isAlpha + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaUseImport.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaUseImport.hs new file mode 100644 index 0000000000..87ac26bbcb --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaUseImport.hs @@ -0,0 +1,6 @@ +import Data.Char + + +result :: Char -> Bool +result = [wingman| intro c, use isAlpha, assume c |] + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaUseLocal.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaUseLocal.expected.hs new file mode 100644 index 0000000000..1afee3471a --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaUseLocal.expected.hs @@ -0,0 +1,7 @@ +test :: Int +test = 0 + + +resolve :: Int +resolve = test + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaUseLocal.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaUseLocal.hs new file mode 100644 index 0000000000..0f791818d1 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaUseLocal.hs @@ -0,0 +1,7 @@ +test :: Int +test = 0 + + +resolve :: Int +resolve = [wingman| use test |] + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaUseMethod.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaUseMethod.expected.hs new file mode 100644 index 0000000000..acf46a75a0 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaUseMethod.expected.hs @@ -0,0 +1,12 @@ +{-# LANGUAGE MultiParamTypeClasses #-} + +class Test where + test :: Int + +instance Test where + test = 10 + + +resolve :: Int +resolve = test + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaUseMethod.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaUseMethod.hs new file mode 100644 index 0000000000..4723befd10 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaUseMethod.hs @@ -0,0 +1,12 @@ +{-# LANGUAGE MultiParamTypeClasses #-} + +class Test where + test :: Int + +instance Test where + test = 10 + + +resolve :: Int +resolve = [wingman| use test |] + diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaUseSymbol.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaUseSymbol.expected.hs new file mode 100644 index 0000000000..85012d7aaf --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaUseSymbol.expected.hs @@ -0,0 +1,4 @@ +import Data.Monoid + +resolve :: Sum Int +resolve = _w0 <> _w1 diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaUseSymbol.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaUseSymbol.hs new file mode 100644 index 0000000000..4afe5f572d --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaUseSymbol.hs @@ -0,0 +1,4 @@ +import Data.Monoid + +resolve :: Sum Int +resolve = [wingman| use (<>) |] diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaWithArg.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaWithArg.expected.hs new file mode 100644 index 0000000000..895e9333c0 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaWithArg.expected.hs @@ -0,0 +1,2 @@ +wat :: a -> b +wat a = _w0 a diff --git a/plugins/hls-tactics-plugin/new/test/golden/MetaWithArg.hs b/plugins/hls-tactics-plugin/new/test/golden/MetaWithArg.hs new file mode 100644 index 0000000000..75c6ab0445 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/MetaWithArg.hs @@ -0,0 +1,2 @@ +wat :: a -> b +wat a = [wingman| with_arg, assumption |] diff --git a/plugins/hls-tactics-plugin/new/test/golden/NewtypeRecord.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/NewtypeRecord.expected.hs new file mode 100644 index 0000000000..4bbd4d283a --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/NewtypeRecord.expected.hs @@ -0,0 +1,7 @@ +newtype MyRecord a = Record + { field1 :: a + } + +blah :: (a -> Int) -> a -> MyRecord a +blah _ = Record + diff --git a/plugins/hls-tactics-plugin/new/test/golden/NewtypeRecord.hs b/plugins/hls-tactics-plugin/new/test/golden/NewtypeRecord.hs new file mode 100644 index 0000000000..82b994b936 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/NewtypeRecord.hs @@ -0,0 +1,7 @@ +newtype MyRecord a = Record + { field1 :: a + } + +blah :: (a -> Int) -> a -> MyRecord a +blah = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/ProvideAlreadyDestructed.hs b/plugins/hls-tactics-plugin/new/test/golden/ProvideAlreadyDestructed.hs new file mode 100644 index 0000000000..2da53afbf5 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/ProvideAlreadyDestructed.hs @@ -0,0 +1,9 @@ +foo :: Bool -> () +foo x = + if True + then + case x of + True -> _ + False -> () + else + _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/ProvideLocalHyOnly.hs b/plugins/hls-tactics-plugin/new/test/golden/ProvideLocalHyOnly.hs new file mode 100644 index 0000000000..6a15b198dd --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/ProvideLocalHyOnly.hs @@ -0,0 +1,2 @@ +basilisk :: Monoid Bool => a +basilisk = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/ProviderHomomorphism.hs b/plugins/hls-tactics-plugin/new/test/golden/ProviderHomomorphism.hs new file mode 100644 index 0000000000..dc096f38f1 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/ProviderHomomorphism.hs @@ -0,0 +1,22 @@ +{-# LANGUAGE GADTs #-} +{-# LANGUAGE LambdaCase #-} + +data GADT a where + B1 :: GADT Bool + B2 :: GADT Bool + Int :: GADT Int + Var :: GADT a + + +hasHomo :: GADT Bool -> GADT a +hasHomo g = _ + +cantHomo :: GADT a -> GADT Int +cantHomo g = _ + +hasHomoLam :: GADT Bool -> GADT a +hasHomoLam = _ + +cantHomoLam :: GADT a -> GADT Int +cantHomoLam = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/PunGADT.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/PunGADT.expected.hs new file mode 100644 index 0000000000..9bdcd61516 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/PunGADT.expected.hs @@ -0,0 +1,12 @@ +{-# LANGUAGE GADTs #-} + +data GADT a where + GADT :: + { blah :: Int + , bar :: a + } -> GADT a + + +split :: GADT a -> a +split GADT {blah, bar} = _w0 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/PunGADT.hs b/plugins/hls-tactics-plugin/new/test/golden/PunGADT.hs new file mode 100644 index 0000000000..250479e758 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/PunGADT.hs @@ -0,0 +1,12 @@ +{-# LANGUAGE GADTs #-} + +data GADT a where + GADT :: + { blah :: Int + , bar :: a + } -> GADT a + + +split :: GADT a -> a +split x = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/PunMany.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/PunMany.expected.hs new file mode 100644 index 0000000000..7b661c2ee5 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/PunMany.expected.hs @@ -0,0 +1,8 @@ +data Many + = Hello { world :: String } + | Goodbye { a :: Int, b :: Bool, c :: Many } + +test :: Many -> Many +test Hello {world} = _w0 +test Goodbye {a, b, c} = _w1 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/PunMany.hs b/plugins/hls-tactics-plugin/new/test/golden/PunMany.hs new file mode 100644 index 0000000000..77234a7359 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/PunMany.hs @@ -0,0 +1,7 @@ +data Many + = Hello { world :: String } + | Goodbye { a :: Int, b :: Bool, c :: Many } + +test :: Many -> Many +test x = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/PunManyGADT.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/PunManyGADT.expected.hs new file mode 100644 index 0000000000..5b3eaf2559 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/PunManyGADT.expected.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE GADTs #-} + +data GADT a where + GADT :: + { blah :: Int + , bar :: a + } -> GADT a + Bar :: + { zoo :: Bool + , baxter :: a + , another :: a + } -> GADT Bool + Baz :: GADT Int + + +split :: GADT Bool -> a +split GADT {blah, bar} = _w0 +split Bar {zoo, baxter, another} = _w1 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/PunManyGADT.hs b/plugins/hls-tactics-plugin/new/test/golden/PunManyGADT.hs new file mode 100644 index 0000000000..70badb7ae2 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/PunManyGADT.hs @@ -0,0 +1,18 @@ +{-# LANGUAGE GADTs #-} + +data GADT a where + GADT :: + { blah :: Int + , bar :: a + } -> GADT a + Bar :: + { zoo :: Bool + , baxter :: a + , another :: a + } -> GADT Bool + Baz :: GADT Int + + +split :: GADT Bool -> a +split x = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/PunShadowing.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/PunShadowing.expected.hs new file mode 100644 index 0000000000..d3cc689a04 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/PunShadowing.expected.hs @@ -0,0 +1,5 @@ +data Bar = Bar { ax :: Int, bax :: Bool } + +bar :: () -> Bar -> Int +bar ax Bar {ax = n, bax} = _w0 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/PunShadowing.hs b/plugins/hls-tactics-plugin/new/test/golden/PunShadowing.hs new file mode 100644 index 0000000000..f2cce07cbc --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/PunShadowing.hs @@ -0,0 +1,5 @@ +data Bar = Bar { ax :: Int, bax :: Bool } + +bar :: () -> Bar -> Int +bar ax x = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/PunSimple.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/PunSimple.expected.hs new file mode 100644 index 0000000000..65bc2d28d0 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/PunSimple.expected.hs @@ -0,0 +1,5 @@ +data Bar = Bar { ax :: Int, bax :: Bool } + +bar :: Bar -> Int +bar Bar {ax, bax} = _w0 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/PunSimple.hs b/plugins/hls-tactics-plugin/new/test/golden/PunSimple.hs new file mode 100644 index 0000000000..6707399c28 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/PunSimple.hs @@ -0,0 +1,5 @@ +data Bar = Bar { ax :: Int, bax :: Bool } + +bar :: Bar -> Int +bar x = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/RecordCon.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/RecordCon.expected.hs new file mode 100644 index 0000000000..cfc2235bfb --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/RecordCon.expected.hs @@ -0,0 +1,9 @@ +data MyRecord a = Record + { field1 :: a + , field2 :: Int + } + +blah :: (a -> Int) -> a -> MyRecord a +blah f a = Record {field1 = a, field2 = f a} + + diff --git a/plugins/hls-tactics-plugin/new/test/golden/RecordCon.hs b/plugins/hls-tactics-plugin/new/test/golden/RecordCon.hs new file mode 100644 index 0000000000..651983e8a3 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/RecordCon.hs @@ -0,0 +1,9 @@ +data MyRecord a = Record + { field1 :: a + , field2 :: Int + } + +blah :: (a -> Int) -> a -> MyRecord a +blah = _ + + diff --git a/plugins/hls-tactics-plugin/new/test/golden/RefineCon.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/RefineCon.expected.hs new file mode 100644 index 0000000000..7110f637da --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/RefineCon.expected.hs @@ -0,0 +1,3 @@ +test :: ((), (b, c), d) +test = (_w0, _w1, _w2) + diff --git a/plugins/hls-tactics-plugin/new/test/golden/RefineCon.hs b/plugins/hls-tactics-plugin/new/test/golden/RefineCon.hs new file mode 100644 index 0000000000..dc611f6e93 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/RefineCon.hs @@ -0,0 +1,3 @@ +test :: ((), (b, c), d) +test = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/RefineGADT.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/RefineGADT.expected.hs new file mode 100644 index 0000000000..605f5e0a5c --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/RefineGADT.expected.hs @@ -0,0 +1,9 @@ +{-# LANGUAGE GADTs #-} + +data GADT a where + One :: (b -> Int) -> GADT Int + Two :: GADT Bool + +test :: z -> GADT Int +test z = One _w0 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/RefineGADT.hs b/plugins/hls-tactics-plugin/new/test/golden/RefineGADT.hs new file mode 100644 index 0000000000..6ac2853173 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/RefineGADT.hs @@ -0,0 +1,9 @@ +{-# LANGUAGE GADTs #-} + +data GADT a where + One :: (b -> Int) -> GADT Int + Two :: GADT Bool + +test :: z -> GADT Int +test z = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/RefineIntro.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/RefineIntro.expected.hs new file mode 100644 index 0000000000..5c99dfc3a1 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/RefineIntro.expected.hs @@ -0,0 +1,2 @@ +test :: a -> Either a b +test a = _w0 diff --git a/plugins/hls-tactics-plugin/new/test/golden/RefineIntro.hs b/plugins/hls-tactics-plugin/new/test/golden/RefineIntro.hs new file mode 100644 index 0000000000..afe7524957 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/RefineIntro.hs @@ -0,0 +1,2 @@ +test :: a -> Either a b +test = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/RefineIntroWhere.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/RefineIntroWhere.expected.hs new file mode 100644 index 0000000000..2d72de4c9b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/RefineIntroWhere.expected.hs @@ -0,0 +1,6 @@ +test :: Maybe Int -> Int +test = \ m_n -> _w0 + where + -- Don't delete me! + blah = undefined + diff --git a/plugins/hls-tactics-plugin/new/test/golden/RefineIntroWhere.hs b/plugins/hls-tactics-plugin/new/test/golden/RefineIntroWhere.hs new file mode 100644 index 0000000000..a9e4ca1db7 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/RefineIntroWhere.hs @@ -0,0 +1,6 @@ +test :: Maybe Int -> Int +test = _ + where + -- Don't delete me! + blah = undefined + diff --git a/plugins/hls-tactics-plugin/new/test/golden/RefineReader.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/RefineReader.expected.hs new file mode 100644 index 0000000000..267e6b8015 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/RefineReader.expected.hs @@ -0,0 +1,5 @@ +newtype Reader r a = Reader (r -> a) + +test :: b -> Reader r a +test b = Reader _w0 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/RefineReader.hs b/plugins/hls-tactics-plugin/new/test/golden/RefineReader.hs new file mode 100644 index 0000000000..9e68e115e9 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/RefineReader.hs @@ -0,0 +1,5 @@ +newtype Reader r a = Reader (r -> a) + +test :: b -> Reader r a +test b = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/SplitPattern.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/SplitPattern.expected.hs new file mode 100644 index 0000000000..c76acc0d31 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/SplitPattern.expected.hs @@ -0,0 +1,12 @@ +data ADT = One | Two Int | Three | Four Bool ADT | Five + +case_split :: ADT -> Int +case_split One = _ +case_split (Two i) = _ +case_split Three = _ +case_split (Four b One) = _w0 +case_split (Four b (Two n)) = _w1 +case_split (Four b Three) = _w2 +case_split (Four b (Four b' adt)) = _w3 +case_split (Four b Five) = _w4 +case_split Five = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/SplitPattern.hs b/plugins/hls-tactics-plugin/new/test/golden/SplitPattern.hs new file mode 100644 index 0000000000..ba66257007 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/SplitPattern.hs @@ -0,0 +1,8 @@ +data ADT = One | Two Int | Three | Four Bool ADT | Five + +case_split :: ADT -> Int +case_split One = _ +case_split (Two i) = _ +case_split Three = _ +case_split (Four b a) = _ +case_split Five = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/SubsequentTactics.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/SubsequentTactics.expected.hs new file mode 100644 index 0000000000..e638fa311c --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/SubsequentTactics.expected.hs @@ -0,0 +1,5 @@ +data Dummy a = Dummy a + +f :: Dummy Int -> Int +f (Dummy n) = n + diff --git a/plugins/hls-tactics-plugin/new/test/golden/SubsequentTactics.hs b/plugins/hls-tactics-plugin/new/test/golden/SubsequentTactics.hs new file mode 100644 index 0000000000..7487adf038 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/SubsequentTactics.hs @@ -0,0 +1,5 @@ +data Dummy a = Dummy a + +f :: Dummy Int -> Int +f = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/T1.hs b/plugins/hls-tactics-plugin/new/test/golden/T1.hs new file mode 100644 index 0000000000..7ab382d69f --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/T1.hs @@ -0,0 +1,3 @@ +fmapEither :: (a -> b) -> Either c a -> Either c b +fmapEither = _lalala + diff --git a/plugins/hls-tactics-plugin/new/test/golden/T2.hs b/plugins/hls-tactics-plugin/new/test/golden/T2.hs new file mode 100644 index 0000000000..20b1644a8f --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/T2.hs @@ -0,0 +1,12 @@ +eitherFmap :: (a -> b) -> Either e a -> Either e b +eitherFmap fa eab = _ + +global :: Bool +global = True + +foo :: Int +foo = _ + +dontSuggestLambdaCase :: Either a b -> Int +dontSuggestLambdaCase = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/T3.hs b/plugins/hls-tactics-plugin/new/test/golden/T3.hs new file mode 100644 index 0000000000..1bb42a9b02 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/T3.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE LambdaCase #-} + +suggestHomomorphicLC :: Either a b -> Either a b +suggestHomomorphicLC = _ + +suggestLC :: Either a b -> Int +suggestLC = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/UseConLeft.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/UseConLeft.expected.hs new file mode 100644 index 0000000000..26d6d77b8b --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/UseConLeft.expected.hs @@ -0,0 +1,3 @@ +test :: Either a b +test = Left _w0 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/UseConLeft.hs b/plugins/hls-tactics-plugin/new/test/golden/UseConLeft.hs new file mode 100644 index 0000000000..59d03ae7d0 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/UseConLeft.hs @@ -0,0 +1,3 @@ +test :: Either a b +test = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/UseConPair.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/UseConPair.expected.hs new file mode 100644 index 0000000000..1a5caad890 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/UseConPair.expected.hs @@ -0,0 +1,2 @@ +test :: (a, b) +test = (_w0, _w1) diff --git a/plugins/hls-tactics-plugin/new/test/golden/UseConPair.hs b/plugins/hls-tactics-plugin/new/test/golden/UseConPair.hs new file mode 100644 index 0000000000..2d15fe3500 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/UseConPair.hs @@ -0,0 +1,2 @@ +test :: (a, b) +test = _ diff --git a/plugins/hls-tactics-plugin/new/test/golden/UseConRight.expected.hs b/plugins/hls-tactics-plugin/new/test/golden/UseConRight.expected.hs new file mode 100644 index 0000000000..f36809804c --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/UseConRight.expected.hs @@ -0,0 +1,3 @@ +test :: Either a b +test = Right _w0 + diff --git a/plugins/hls-tactics-plugin/new/test/golden/UseConRight.hs b/plugins/hls-tactics-plugin/new/test/golden/UseConRight.hs new file mode 100644 index 0000000000..59d03ae7d0 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/UseConRight.hs @@ -0,0 +1,3 @@ +test :: Either a b +test = _ + diff --git a/plugins/hls-tactics-plugin/new/test/golden/hie.yaml b/plugins/hls-tactics-plugin/new/test/golden/hie.yaml new file mode 100644 index 0000000000..7aa4f9e0ad --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/hie.yaml @@ -0,0 +1 @@ +cradle: {direct: {arguments: ["T1", "T2", "T3"]}} diff --git a/plugins/hls-tactics-plugin/new/test/golden/test.cabal b/plugins/hls-tactics-plugin/new/test/golden/test.cabal new file mode 100644 index 0000000000..845edafa26 --- /dev/null +++ b/plugins/hls-tactics-plugin/new/test/golden/test.cabal @@ -0,0 +1,17 @@ +name: test +version: 0.1.0.0 +-- synopsis: +-- description: +license: BSD3 +author: Author name here +maintainer: example@example.com +copyright: 2017 Author name here +category: Web +build-type: Simple +cabal-version: >=1.10 + +library + exposed-modules: T1, T2 + build-depends: base >= 4.7 && < 5 + default-language: Haskell2010 + ghc-options: -Wall -fwarn-unused-imports From 47e04f8b7e414a94c52c87878b36c05d72f1b816 Mon Sep 17 00:00:00 2001 From: fendor Date: Wed, 7 Dec 2022 11:55:18 +0100 Subject: [PATCH 208/213] Bump hlint version CI flow (#3384) --- .github/workflows/hlint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/hlint.yml b/.github/workflows/hlint.yml index 600d9ff669..62d8742039 100644 --- a/.github/workflows/hlint.yml +++ b/.github/workflows/hlint.yml @@ -15,7 +15,7 @@ jobs: - name: 'Installing' uses: rwe/actions-hlint-setup@v1 with: - version: '3.4' + version: '3.5' - name: 'Checking code' uses: rwe/actions-hlint-run@v2 From 9cb3485326213a057e0341ae691498b8fb9f6054 Mon Sep 17 00:00:00 2001 From: fendor Date: Wed, 7 Dec 2022 13:49:59 +0100 Subject: [PATCH 209/213] Add CI flows for GHC 9.2.5 (#3376) * Add CI flows for GHC 9.2.5 * Prefer GHC 9.2.5 over 9.2.4 * Update list of tested-with GHC versions, as we only *test* 9.2.5 * Add GHC 9.2.5 as a supported GHC version * Update comment in cabal-plugin about disabled test-case * Disable GHC 9.2.5 on windows * Fix CI for windows * Run tests on windows for GHC 9.2.4 Co-authored-by: Michael Peyton Jones Co-authored-by: Pepe Iborra --- .github/workflows/bench.yml | 4 ++-- .github/workflows/caching.yml | 5 +++++ .github/workflows/flags.yml | 6 +++--- .github/workflows/test.yml | 17 ++++++++++++----- docs/support/ghc-version-support.md | 2 ++ ghcide-bench/ghcide-bench.cabal | 3 +-- ghcide/ghcide.cabal | 2 +- haskell-language-server.cabal | 2 +- plugins/hls-cabal-plugin/test/Main.hs | 2 +- 9 files changed, 28 insertions(+), 15 deletions(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index e70be7ba13..6b35610f86 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -46,7 +46,7 @@ jobs: strategy: fail-fast: false matrix: - ghc: ['8.10.7', '9.2.4'] + ghc: ['8.10.7', '9.2.5'] os: [ubuntu-latest] # This code is fitted to the strategy: assumes Linux is used ... etc, @@ -104,7 +104,7 @@ jobs: strategy: fail-fast: false matrix: - ghc: ['8.10.7', '9.2.4'] + ghc: ['8.10.7', '9.2.5'] os: [ubuntu-latest] cabal: ['3.6'] example: ['cabal', 'lsp-types'] diff --git a/.github/workflows/caching.yml b/.github/workflows/caching.yml index b75a0cb71b..1f84a1c2ca 100644 --- a/.github/workflows/caching.yml +++ b/.github/workflows/caching.yml @@ -84,6 +84,7 @@ jobs: # specified in 'test.yml' ghc: [ "9.4.2" , "9.4.1" + , "9.2.5" , "9.2.4" , "9.2.3" , "9.0.2" @@ -96,6 +97,10 @@ jobs: exclude: - os: windows-latest ghc: '9.4.1' + # Exclude until https://github.com/haskell/actions/issues/129 + # is resolved. + - os: windows-latest + ghc: '9.2.5' steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/flags.yml b/.github/workflows/flags.yml index 44b329a833..f1fdb09a1a 100644 --- a/.github/workflows/flags.yml +++ b/.github/workflows/flags.yml @@ -44,7 +44,7 @@ jobs: strategy: fail-fast: true matrix: - ghc: [ "9.2.4" + ghc: [ "9.2.5" , "9.0.2" , "8.10.7" ] @@ -65,8 +65,8 @@ jobs: - name: Build `ghcide` with flags run: cabal v2-build ghcide --flags="ghc-patched-unboxed-bytecode test-exe executable bench-exe ekg" - # we have to clean up warnings for 9.0 and 9.2 before enable -WAll - - if: matrix.ghc != '9.0.2' && matrix.ghc != '9.2.4' + # we have to clean up warnings for 9.0 and 9.2 before enable -Wall + - if: matrix.ghc != '9.0.2' && matrix.ghc != '9.2.5' name: Build with pedantic (-WError) run: cabal v2-build --flags="pedantic" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9385f5af36..64c4715f48 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -62,6 +62,7 @@ jobs: matrix: ghc: [ "9.4.2" , "9.4.1" + , "9.2.5" , "9.2.4" , "9.2.3" , "9.0.2" @@ -77,6 +78,10 @@ jobs: exclude: - os: windows-latest ghc: '9.4.1' + # Exclude until https://github.com/haskell/actions/issues/129 + # is resolved. + - os: windows-latest + ghc: '9.2.5' # Mark which GHC versions on which platform we want to test. include: # only test supported ghc major versions @@ -84,7 +89,7 @@ jobs: ghc: '9.4.2' test: true - os: ubuntu-latest - ghc: '9.2.4' + ghc: '9.2.5' test: true - os: ubuntu-latest ghc: '9.0.2' @@ -95,6 +100,8 @@ jobs: - os: windows-latest ghc: '9.4.2' test: true + # Test on 9.2.4 until https://github.com/haskell/actions/issues/129 + # is resolved. Then switch to 9.2.5 - os: windows-latest ghc: '9.2.4' test: true @@ -158,7 +165,7 @@ jobs: HLS_WRAPPER_TEST_EXE: hls-wrapper run: cabal test wrapper-test --test-options="$TEST_OPTS --rerun-log-file .tasty-rerun-log-wrapper" - - if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2' + - if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.2.5' && matrix.ghc != '9.4.2' name: Test hls-brittany-plugin run: cabal test hls-brittany-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-brittany-plugin --test-options="$TEST_OPTS" @@ -182,7 +189,7 @@ jobs: name: Test hls-eval-plugin run: cabal test hls-eval-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-eval-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2' + - if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.2.5' && matrix.ghc != '9.4.2' name: Test hls-haddock-comments-plugin run: cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-haddock-comments-plugin --test-options="$TEST_OPTS" @@ -202,7 +209,7 @@ jobs: name: Test hls-fourmolu-plugin run: cabal test hls-fourmolu-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-fourmolu-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2' + - if: matrix.test && matrix.ghc != '9.2.4' && matrix.ghc != '9.2.5' && matrix.ghc != '9.4.2' name: Test hls-tactics-plugin test suite run: cabal test hls-tactics-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-tactics-plugin --test-options="$TEST_OPTS" @@ -226,7 +233,7 @@ jobs: name: Test hls-hlint-plugin test suite run: cabal test hls-hlint-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-hlint-plugin --test-options="$TEST_OPTS" - - if: matrix.test && matrix.ghc != '9.0.1' && matrix.ghc != '9.0.2' && matrix.ghc != '9.2.4' && matrix.ghc != '9.4.2' + - if: matrix.test && matrix.ghc != '9.0.1' && matrix.ghc != '9.0.2' && matrix.ghc != '9.2.4' && matrix.ghc != '9.2.5' && matrix.ghc != '9.4.2' name: Test hls-stan-plugin test suite run: cabal test hls-stan-plugin --test-options="$TEST_OPTS" || LSP_TEST_LOG_COLOR=0 LSP_TEST_LOG_MESSAGES=true LSP_TEST_LOG_STDERR=true cabal test hls-stan-plugin --test-options="$TEST_OPTS" diff --git a/docs/support/ghc-version-support.md b/docs/support/ghc-version-support.md index 9af9d7ed46..481ff191be 100644 --- a/docs/support/ghc-version-support.md +++ b/docs/support/ghc-version-support.md @@ -19,6 +19,8 @@ Support status (see the support policy below for more details): |--------------|------------------------------------------------------------------------------------|-----------------------------------------------------------------------------| | 9.4.2 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | basic support | | 9.4.1 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | basic support | +| 9.2.5 | unreleased | full support | + | 9.2.4 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | full support | | 9.2.3 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | full support | | 9.2.(1,2) | [1.7.0.0](https://github.com/haskell/haskell-language-server/releases/tag/1.7.0.0) | deprecated | diff --git a/ghcide-bench/ghcide-bench.cabal b/ghcide-bench/ghcide-bench.cabal index 24e9ee2c80..3db1217049 100644 --- a/ghcide-bench/ghcide-bench.cabal +++ b/ghcide-bench/ghcide-bench.cabal @@ -12,7 +12,7 @@ synopsis: An LSP client for running performance experiments on HLS description: An LSP client for running performance experiments on HLS homepage: https://github.com/haskell/haskell-language-server/tree/master/ghcide#readme bug-reports: https://github.com/haskell/haskell-language-server/issues -tested-with: GHC == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 +tested-with: GHC == 8.10.7 || == 9.0.2 || == 9.2.5 source-repository head type: git @@ -137,4 +137,3 @@ test-suite test TupleSections TypeApplications ViewPatterns - diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index 5d0b50b921..9d898bbef9 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -13,7 +13,7 @@ description: A library for building Haskell IDE's on top of the GHC API. homepage: https://github.com/haskell/haskell-language-server/tree/master/ghcide#readme bug-reports: https://github.com/haskell/haskell-language-server/issues -tested-with: GHC == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 +tested-with: GHC == 8.10.7 || == 9.0.2 || == 9.2.5 extra-source-files: README.md CHANGELOG.md test/data/**/*.project test/data/**/*.cabal diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 6926f53318..5dd57d7ef8 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -14,7 +14,7 @@ copyright: The Haskell IDE Team license: Apache-2.0 license-file: LICENSE build-type: Simple -tested-with: GHC == 8.10.7 || == 9.0.2 || == 9.2.3 || == 9.2.4 +tested-with: GHC == 8.10.7 || == 9.0.2 || ==9.2.5 extra-source-files: README.md ChangeLog.md diff --git a/plugins/hls-cabal-plugin/test/Main.hs b/plugins/hls-cabal-plugin/test/Main.hs index 03a8976bb1..9fa843347d 100644 --- a/plugins/hls-cabal-plugin/test/Main.hs +++ b/plugins/hls-cabal-plugin/test/Main.hs @@ -97,7 +97,7 @@ pluginTests = testGroup "Plugin Tests" expectNoMoreDiagnostics 1 hsDoc "typechecking" cabalDoc <- openDoc "simple-cabal.cabal" "cabal" expectNoMoreDiagnostics 1 cabalDoc "parsing" - , ignoreTestBecause "Testcase is flaky for certain GHC versions (e.g. 9.2.4). See #3333 for details." $ do + , ignoreTestBecause "Testcase is flaky for certain GHC versions (e.g. 9.2.5). See #3333 for details." $ do runCabalTestCaseSession "Diagnostics in .hs files from invalid .cabal file" "simple-cabal" $ do hsDoc <- openDoc "A.hs" "haskell" expectNoMoreDiagnostics 1 hsDoc "typechecking" From f7d02197a0a42c167f69e748a325c2c36307cc70 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Dec 2022 17:14:23 +0000 Subject: [PATCH 210/213] Bump fkirc/skip-duplicate-actions from 5.2.0 to 5.3.0 (#3381) Bumps [fkirc/skip-duplicate-actions](https://github.com/fkirc/skip-duplicate-actions) from 5.2.0 to 5.3.0. - [Release notes](https://github.com/fkirc/skip-duplicate-actions/releases) - [Commits](https://github.com/fkirc/skip-duplicate-actions/compare/v5.2.0...v5.3.0) --- updated-dependencies: - dependency-name: fkirc/skip-duplicate-actions dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Michael Peyton Jones --- .github/workflows/bench.yml | 2 +- .github/workflows/caching.yml | 2 +- .github/workflows/flags.yml | 2 +- .github/workflows/nix.yml | 4 ++-- .github/workflows/test.yml | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 6b35610f86..02e05fb924 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -21,7 +21,7 @@ jobs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check - uses: fkirc/skip-duplicate-actions@v5.2.0 + uses: fkirc/skip-duplicate-actions@v5.3.0 with: cancel_others: false paths_ignore: '[ "**/docs/**" diff --git a/.github/workflows/caching.yml b/.github/workflows/caching.yml index 1f84a1c2ca..8a125ee92d 100644 --- a/.github/workflows/caching.yml +++ b/.github/workflows/caching.yml @@ -57,7 +57,7 @@ jobs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check - uses: fkirc/skip-duplicate-actions@v5.2.0 + uses: fkirc/skip-duplicate-actions@v5.3.0 with: cancel_others: false paths_ignore: '["**/docs/**" diff --git a/.github/workflows/flags.yml b/.github/workflows/flags.yml index f1fdb09a1a..775c4037ff 100644 --- a/.github/workflows/flags.yml +++ b/.github/workflows/flags.yml @@ -21,7 +21,7 @@ jobs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check - uses: fkirc/skip-duplicate-actions@v5.2.0 + uses: fkirc/skip-duplicate-actions@v5.3.0 with: cancel_others: false paths_ignore: '[ "**/docs/**" diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 96ba533133..631e34d12d 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -21,7 +21,7 @@ jobs: should_skip_build: ${{ steps.skip_check_no_nix.outputs.should_skip }} steps: - id: skip_check - uses: fkirc/skip-duplicate-actions@v5.2.0 + uses: fkirc/skip-duplicate-actions@v5.3.0 with: cancel_others: false paths_ignore: '[ "**/docs/**" @@ -36,7 +36,7 @@ jobs: , ".gitlab/**" ]' - id: skip_check_no_nix - uses: fkirc/skip-duplicate-actions@v5.2.0 + uses: fkirc/skip-duplicate-actions@v5.3.0 with: cancel_others: false paths: '[ "**.nix" ]' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 64c4715f48..461a99f5e5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ jobs: should_skip_ghcide: ${{ steps.skip_ghcide_check.outputs.should_skip }} steps: - id: skip_check - uses: fkirc/skip-duplicate-actions@v5.2.0 + uses: fkirc/skip-duplicate-actions@v5.3.0 with: cancel_others: false paths_ignore: '[ "**/docs/**" @@ -39,7 +39,7 @@ jobs: ]' # If we only change ghcide downstream packages we have not test ghcide itself - id: skip_ghcide_check - uses: fkirc/skip-duplicate-actions@v5.2.0 + uses: fkirc/skip-duplicate-actions@v5.3.0 with: cancel_others: false paths_ignore: '[ "hls-test-utils/**" From cc4f9986d2f932ecd0f2f8167c1ecdb28a2ee9e2 Mon Sep 17 00:00:00 2001 From: k4z4n0v4 <46030560+k4z4n0v4@users.noreply.github.com> Date: Thu, 8 Dec 2022 13:50:56 +0400 Subject: [PATCH 211/213] Correct markdown table (#3389) The table is skewed after the change that adds 9.2.5 --- docs/support/ghc-version-support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/support/ghc-version-support.md b/docs/support/ghc-version-support.md index 481ff191be..abfc01a389 100644 --- a/docs/support/ghc-version-support.md +++ b/docs/support/ghc-version-support.md @@ -20,7 +20,6 @@ Support status (see the support policy below for more details): | 9.4.2 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | basic support | | 9.4.1 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | basic support | | 9.2.5 | unreleased | full support | - | 9.2.4 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | full support | | 9.2.3 | [latest](https://github.com/haskell/haskell-language-server/releases/latest) | full support | | 9.2.(1,2) | [1.7.0.0](https://github.com/haskell/haskell-language-server/releases/tag/1.7.0.0) | deprecated | @@ -37,6 +36,7 @@ Support status (see the support policy below for more details): | 8.6.5 | [1.8.0.0](https://github.com/haskell/haskell-language-server/releases/tag/1.8.0.0) | deprecated | | 8.6.4 | [1.4.0](https://github.com/haskell/haskell-language-server/releases/tag/1.4.0) | deprecated | + GHC versions not in the list have never been supported by HLS. LTS stands for [Stackage](https://www.stackage.org/) Long Term Support. From 5cc2155a3cc245549ff2118bb974286c9f6e409b Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Thu, 8 Dec 2022 04:04:21 -0800 Subject: [PATCH 212/213] Delete a weird nix version override (#3388) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .github/workflows/nix.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 631e34d12d..fc9f300315 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -57,8 +57,6 @@ jobs: - uses: cachix/install-nix-action@v18 with: - install_url: https://nixos-nix-install-tests.cachix.org/serve/i6laym9jw3wg9mw6ncyrk6gjx4l34vvx/install - install_options: '--tarball-url-prefix https://nixos-nix-install-tests.cachix.org/serve' extra_nix_config: | experimental-features = nix-command flakes nix_path: nixpkgs=channel:nixos-unstable From f6296524b8c65deff4fcd49fcf3af5dfe4aa253d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Dec 2022 14:25:56 +0000 Subject: [PATCH 213/213] Bump technote-space/get-diff-action from 6.1.0 to 6.1.1 (#3293) Bumps [technote-space/get-diff-action](https://github.com/technote-space/get-diff-action) from 6.1.0 to 6.1.1. - [Release notes](https://github.com/technote-space/get-diff-action/releases) - [Changelog](https://github.com/technote-space/get-diff-action/blob/main/.releasegarc) - [Commits](https://github.com/technote-space/get-diff-action/compare/v6.1.0...v6.1.1) --- updated-dependencies: - dependency-name: technote-space/get-diff-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michael Peyton Jones Co-authored-by: Kobayashi Co-authored-by: Pepe Iborra Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .github/workflows/pre-commit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 0136d48441..b7c4ec3e61 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -14,7 +14,7 @@ jobs: - name: Checkout code uses: actions/checkout@v3 - name: Find changed files - uses: technote-space/get-diff-action@v6.1.0 + uses: technote-space/get-diff-action@v6.1.1 id: git-diff with: PATTERNS: |