首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Haskeline运行时更改选项卡完成的内容?

如何在Haskeline运行时更改选项卡完成的内容?
EN

Stack Overflow用户
提问于 2021-03-29 18:29:58
回答 1查看 152关注 0票数 4

我想编写一个文本接口,它提供了一些默认命令。此程序支持这些命令的选项卡完成。

该程序还记录用户输入并将其存储在StateData中。现在我希望这个程序支持那些用户输入的选项卡完成。例如:

代码语言:javascript
复制
*Main > main
> read a<tab> -- press tab and no suggestions (read is a default command)
> read abcde
...
> read a<tab> -- press tab
abcde         -- suggestions

在不使用像IORef这样的不安全机制的情况下能够做到这一点吗?有没有一种方法可以将更新的stloop (在repl中)传递给replSettings startState (在repl中)?

我是新来的Haskeline,谢谢您的时间。

代码语言:javascript
复制
repl :: StateData -> IO()
repl startState                    =  runInputT (replSettings startState) $ loop startState
  where
    loop :: StateData              -> InputT IO ()
    loop st                        =  do
      inputL                       <- getInputLine "> "
      case inputL                  of
           Nothing                 -> return ()
           Just "quit"             -> outputStrLn "--Exited--" >> return ()
           Just ipt                -> do (opt, st')     <- process ipt `runStateT` st
                                         ...
                                         loop st'

replSettings :: StateData -> Settings IO
replSettings st =
  Settings
    { complete       = replCompletion st,
      historyFile    = Just "history.txt",
      autoAddHistory = True
    }

replCompletion :: StateData -> CompletionFunc IO
replCompletion st = completeWordWithPrev Nothing [' '] st (\x y -> return $ completionGenerator x y)

completionGenerator :: String -> String -> StateData -> [Completion]
completionGenerator "" c st = 
  commandSuggestion c (updatesSuggestions st) -- I wish to update it at run time
completionGenerator p  c st = ...
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-03-29 19:31:20

IORef并不不安全;您已经进入了IO,因此在这里添加可变状态是非常合理的方法。

但是,如果您想要避免IO,可以简单地使用StateT StateData IO作为InputT的底层monad,从而在Settings中使用完成函数。看来您已经在尝试使用StateT了。下面是一个完整的示例,它只是将每个条目添加到列表中,然后天真地自动完成它们:

代码语言:javascript
复制
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.State (StateT, evalStateT, get, modify)
import Data.List (isPrefixOf)
import System.Console.Haskeline

type StateData = [String]

main :: IO ()
main = repl []

repl :: StateData -> IO ()
repl startState
  = flip evalStateT startState
  $ runInputT replSettings loop
  where
    loop :: InputT (StateT StateData IO) ()
    loop = do
      inputL <- getInputLine "> "
      case inputL of
        Nothing -> pure ()
        Just "quit" -> outputStrLn "--Exited--"
        Just ipt -> do
          -- Just add each entry to the state directly.
          lift $ modify (ipt :)
          loop

replSettings :: Settings (StateT StateData IO)
replSettings = Settings
  { complete       = replCompletion
  , historyFile    = Just "history.txt"
  , autoAddHistory = True
  }

replCompletion :: CompletionFunc (StateT StateData IO)
replCompletion = completeWordWithPrev Nothing " " completionGenerator

completionGenerator :: String -> String -> StateT StateData IO [Completion]
completionGenerator prefix suffix = do
  st <- get
  -- Trivial completion that just ignores the suffix.
  pure $ fmap (\ s -> Completion s s True)
    $ filter (prefix `isPrefixOf`) st

还可以使用MonadState (来自mtl)编写完成生成器,以防止它访问IO,其他代码也可以使用这种纯状态,而对IO不知情。但否则,由于您已经在本代码中的IO中,StateT StateData IO / get / modifyReaderT (IORef StateData) IO / readIORef / modifyIORef没有什么不同。

实际上,如果您将IORef放在StateData中,假设它是代码中一个更复杂的记录类型,那么后者是使它的某些部分不可变和其他部分不可变的好方法。

代码语言:javascript
复制
data StateData = StateData
  { mutableThing   :: !(IORef Thing)
  , immutableStuff :: !Stuff
  …
  }
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66859656

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档