judah / haskeline

A Haskell library for line input in command-line programs.
https://hackage.haskell.org/package/haskeline
BSD 3-Clause "New" or "Revised" License
221 stars 75 forks source link

Using Haskeline with a PseudoTerminal #147

Open NorfairKing opened 3 years ago

NorfairKing commented 3 years ago

I'd like to use haskeline for a browser-based terminal interface. (See smos.online.)

I got something hacky working like this:

    -- TODO try to use the special TTY handles instead so history works too.
    customRunInputT :: InputT IO a -> IO a
    customRunInputT inputT = do
      historyRef <- newIORef Haskeline.emptyHistory
      withBehavior (useFileHandle inputH) $ \rt -> do
        let runTerm =
              rt
                { putStrOut = \s -> do
                    hPutStr outputH s
                    hFlush outputH
                }
        runReaderT
          ( runReaderT
              ( Haskeline.runKillRing
                  ( runReaderT
                      ( runReaderT (unInputT inputT) runTerm
                      )
                      historyRef
                  )
              )
              Haskeline.defaultPrefs
          )
          Haskeline.defaultSettings

However, pressing the up arrow key doesn't show me the history but a control sequence instead. I've also tried to write this:

givenTtyHandles :: Handle -> Handle -> MaybeT IO Handles
givenTtyHandles inh outh = do
    outEH <- inCodingMode outh
    return Handles
            { hIn = externalHandle inh
            , hOut = outEH
            , closeHandles = pure () -- Don't close them
            }

and then the history works but the input is no longer echoed, even with hSetExcho inh and hSetBuffering inh NoBuffering.

Is this a use-case that you would like to support?

goertzenator commented 3 years ago

I would very much like to see pseudo terminal support. My use case is in an embedded system where a daemon needs to open and service pseudo terminals and serial terminals.

goertzenator commented 3 years ago

@NorfairKing , if you have a branch of your work pushed somewhere I would love to look at it. Also, did you manage the TERM environment variable at all? I wonder if this contributed to some of your problems.

I am thinking of a top level lib function like the one below that lets you provide input handle, output handle, and Maybe a terminal type. If Nothing is specified for term type then the environment is queried as usual. When using something besides the controlling terminal it will be common to not have the client's TERM variable available so it will need to be provided manually.

useTermHandles :: Handle -> Handle -> Maybe String -> Behavior
NorfairKing commented 3 years ago

@goertzenator https://github.com/NorfairKing/smos/blob/69226687bf7030b05a9ca0c6eacdcc03701c791c/smos-shell/src/Smos/Shell.hs#L34 here's what I do.