diff --git a/ChangeLog.md b/ChangeLog.md index 2a97b514a2..df563242f5 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -64,6 +64,9 @@ Bug fixes: * Use Hoogle from the snapshot used and not the latest version. See [#4905](https://github.com/commercialhaskell/stack/issues/4905) +* Resolve "'stty' is not recognized". See + [#4901](https://github.com/commercialhaskell/stack/issues/4901) + ## v2.1.3.1 Hackage-only release: diff --git a/package.yaml b/package.yaml index db70fa4ba8..915c57171b 100644 --- a/package.yaml +++ b/package.yaml @@ -116,7 +116,6 @@ dependencies: - tar - template-haskell - temporary -- terminal-size - text - text-metrics - th-reify-many diff --git a/src/Stack/Runners.hs b/src/Stack/Runners.hs index 0c6ce14495..d40bab60ea 100644 --- a/src/Stack/Runners.hs +++ b/src/Stack/Runners.hs @@ -35,7 +35,7 @@ import Stack.Types.Docker (dockerEnable) import Stack.Types.Nix (nixEnable) import Stack.Types.Version (stackMinorVersion, stackVersion, minorVersion) import System.Console.ANSI (hSupportsANSIWithoutEmulation) -import System.Console.Terminal.Size (size, width) +import System.Terminal (getTerminalWidth) -- | Ensure that no project settings are used when running 'withConfig'. withGlobalProject :: RIO Runner a -> RIO Runner a @@ -145,7 +145,7 @@ withRunnerGlobal go inner = do ColorAuto -> fromMaybe True <$> hSupportsANSIWithoutEmulation stderr termWidth <- clipWidth <$> maybe (fromMaybe defaultTerminalWidth - <$> fmap (fmap width) size) + <$> getTerminalWidth) pure (globalTermWidth go) menv <- mkDefaultProcessContext logOptions0 <- logOptionsHandle stderr False diff --git a/src/unix/System/Terminal.hsc b/src/unix/System/Terminal.hsc index 2f1acc58f5..e17b35ae5d 100644 --- a/src/unix/System/Terminal.hsc +++ b/src/unix/System/Terminal.hsc @@ -1,10 +1,41 @@ +{-# LANGUAGE ForeignFunctionInterface #-} module System.Terminal ( fixCodePage +, getTerminalWidth , hIsTerminalDeviceOrMinTTY ) where +import Foreign +import Foreign.C.Types import RIO (MonadIO, Handle, hIsTerminalDevice) +#include +#include + + +newtype WindowWidth = WindowWidth CUShort + deriving (Eq, Ord, Show) + +instance Storable WindowWidth where + sizeOf _ = (#size struct winsize) + alignment _ = (#alignment struct winsize) + peek p = WindowWidth <$> (#peek struct winsize, ws_col) p + poke p (WindowWidth w) = do + (#poke struct winsize, ws_col) p w + +foreign import ccall "sys/ioctl.h ioctl" + ioctl :: CInt -> CInt -> Ptr WindowWidth -> IO CInt + +getTerminalWidth :: IO (Maybe Int) +getTerminalWidth = + alloca $ \p -> do + errno <- ioctl (#const STDOUT_FILENO) (#const TIOCGWINSZ) p + if errno < 0 + then return Nothing + else do + WindowWidth w <- peek p + return . Just . fromIntegral $ w + fixCodePage :: x -> y -> a -> a fixCodePage _ _ = id diff --git a/src/windows/System/Terminal.hs b/src/windows/System/Terminal.hs index 5b93d99a53..fe34c570b3 100644 --- a/src/windows/System/Terminal.hs +++ b/src/windows/System/Terminal.hs @@ -1,14 +1,73 @@ +{-# LANGUAGE ForeignFunctionInterface #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} module System.Terminal ( fixCodePage +, getTerminalWidth , hIsTerminalDeviceOrMinTTY ) where import Distribution.Types.Version (mkVersion) +import Foreign.Ptr +import Foreign.Storable +import Foreign.Marshal.Alloc import Stack.Prelude +import System.IO hiding (hIsTerminalDevice) +import System.Process import System.Win32 (isMinTTYHandle, withHandleToHANDLE) import System.Win32.Console (setConsoleCP, setConsoleOutputCP, getConsoleCP, getConsoleOutputCP) +import RIO.Partial (read) + +type HANDLE = Ptr () + +data CONSOLE_SCREEN_BUFFER_INFO + +sizeCONSOLE_SCREEN_BUFFER_INFO :: Int +sizeCONSOLE_SCREEN_BUFFER_INFO = 22 + +posCONSOLE_SCREEN_BUFFER_INFO_srWindow :: Int +posCONSOLE_SCREEN_BUFFER_INFO_srWindow = 10 -- 4 x Word16 Left,Top,Right,Bottom + +c_STD_OUTPUT_HANDLE :: Int +c_STD_OUTPUT_HANDLE = -11 + +foreign import ccall unsafe "windows.h GetConsoleScreenBufferInfo" + c_GetConsoleScreenBufferInfo :: HANDLE -> Ptr CONSOLE_SCREEN_BUFFER_INFO -> IO Bool + +foreign import ccall unsafe "windows.h GetStdHandle" + c_GetStdHandle :: Int -> IO HANDLE + + +getTerminalWidth :: IO (Maybe Int) +getTerminalWidth = do + hdl <- c_GetStdHandle c_STD_OUTPUT_HANDLE + allocaBytes sizeCONSOLE_SCREEN_BUFFER_INFO $ \p -> do + b <- c_GetConsoleScreenBufferInfo hdl p + if not b + then do -- This could happen on Cygwin or MSYS + let stty = (shell "stty size") { + std_in = UseHandle stdin + , std_out = CreatePipe + , std_err = CreatePipe + } + (_, mbStdout, _, rStty) <- createProcess stty + exStty <- waitForProcess rStty + case exStty of + ExitFailure _ -> return Nothing + ExitSuccess -> + maybe (return Nothing) + (\hSize -> do + sizeStr <- hGetContents hSize + case map read $ words sizeStr :: [Int] of + [_r, c] -> return $ Just c + _ -> return Nothing + ) + mbStdout + else do + [left,_top,right,_bottom] <- forM [0..3] $ \i -> do + v <- peekByteOff p ((i*2) + posCONSOLE_SCREEN_BUFFER_INFO_srWindow) + return $ fromIntegral (v :: Word16) + return $ Just (1+right-left) -- | Set the code page for this process as necessary. Only applies to Windows. -- See: https://github.com/commercialhaskell/stack/issues/738