xmonad-contrib 提示:在特定工作区执行终端提示?

xmonad-contrib Prompt: Execute terminal prompt in a particular workspace?

我最近决定通过 Stack 从源代码构建 XMonad 以进行自定义配置。先声明一下,我对 Haskell 没有太多经验。 我的 OS 是 Linux - Arch.

设置

我正在尝试使用 xmonad-contrib 中的 XMonad.Prompt 包来启动一些应用程序。 (https://hackage.haskell.org/package/xmonad-contrib-0.13/docs/XMonad-Prompt.html)

目前,我使用的一个提示是 XMonad.Prompt.Man,它在默认终端(我默认使用的终端是 Alacritty)中使用提供的参数启动 man 程序。 (文档:https://hackage.haskell.org/package/xmonad-contrib-0.13/docs/XMonad-Prompt-Man.html) (来源:https://hackage.haskell.org/package/xmonad-contrib-0.13/docs/src/XMonad-Prompt-Man.html

我不明白的是如何使用此提示将终端启动到工作区 2,我希望在其中打开所有联机帮助页。

提示目前的工作方式:

我在 xmonad.hs 中设置了一个键绑定,如下所示:

...
, ("M-C-m", manPrompt myPromptConfig)
...

这按文档说明的方式工作,但联机帮助页始终在当前工作区中打开,而不是在我希望它们打开的工作区 2 中打开。

我试过的

因为 man 在现有终端中打开并且不提供设置 window 名称的选项,所以我最初无法使用 ManageHook 检测 window 命名并将其转移到正确的工作区,就像我对 Mozilla Firefox 等应用程序所做的那样:(https://hackage.haskell.org/package/xmonad-0.15/docs/XMonad-ManageHook.html)

myManageHook = composeAll
    [
    ...
    , title =? "Mozilla Firefox" -> (doShift !! myWorkspaces 1)
    ...
    ]

我已经尝试将 window 名称设置为使用 Alacritty 的 --title 选项进行硬编码的名称,但这只会在终端生成并且 man 已经生成后移动终端在当前布局上调用,导致当终端转移到具有不同布局的不同工作区时文本对齐不正确。

我认为一个合适的解决方案可能需要修改 XMonad.Prompt.Man 的源代码。

查看 XMonad.Prompt.Man 的源代码,我发现这一行可能对更改有用(第 60-63 行):(https://hackage.haskell.org/package/xmonad-contrib-0.13/docs/src/XMonad-Prompt-Man.html)

manPrompt :: XPConfig -> X()
manPrompt c = do         -- NOTE FROM ME: getMans and manCompl are functions defined elsewhere in the file that are not relevant here
    mans <- io getMans
    mkXPrompt Man c (manCompl c mans) $ runInTerm "" . (++) "man "
    

查看 runInTerm 的源代码(来自 XMonad.Util.Run),在我看来它需要 2 个字符串参数:第一个是选项列表,"" 在在 manPrompt 的情况下(即默认情况下为空白),第二个是 运行 的命令,在 manPrompt 的情况下是 "man "。这些都在默认终端中得到 运行(对我来说很敏捷)。 (文档:https://hackage.haskell.org/package/xmonad-contrib-0.16/docs/XMonad-Util-Run.html) (来源:https://hackage.haskell.org/package/xmonad-contrib-0.16/docs/src/XMonad.Util.Run.html#runInTerm

我可以做些什么来修改 manPromptrunInTerm 以便我可以指定 manPrompt 生成的终端在哪个工作区中生成?或者有没有其他任何人知道实现我不知道的期望行为的方法?

编辑#1

根据最近回答的建议,我正在尝试将 manPrompt 的代码从 XMonad.Actions.SpawnOn 修补为 运行 spawnOn,而不是 runInTerm. (https://hackage.haskell.org/package/xmonad-contrib-0.13/docs/XMonad-Actions-SpawnOn.html)

这是我现在拥有的:

manPrompt :: XPConfig -> WorkspaceId -> String -> X ()
manPrompt promptConfig workspaceId terminalArgs = do
    mans <- io getMans
    mkXPrompt (showXPrompt "man ") promptConfig (manCompl promptConfig mans) $ spawnOn (workspaceId) $ (myTerminal) ++ " " ++ (terminalArgs) ++ " -e man "

然而,这不会编译,导致错误:

 xmonad.hs:248:78: error:
    * Couldn't match expected type `String -> X ()'
                  with actual type `X ()'
    * In the second argument of `($)', namely
        `spawnOn (workspaceId)
           $ (myTerminal) ++ " " ++ (terminalArgs) ++ " -e man "'
      In a stmt of a 'do' block:
        mkXPrompt
          (showXPrompt "man ") promptConfig (manCompl promptConfig mans)
          $ spawnOn (workspaceId)
              $ (myTerminal) ++ " " ++ (terminalArgs) ++ " -e man "
      In the expression:
        do mans <- io getMans
           mkXPrompt
             (showXPrompt "man ") promptConfig (manCompl promptConfig mans)
             $ spawnOn (workspaceId)
                 $ (myTerminal) ++ " " ++ (terminalArgs) ++ " -e man "
    |
248 |   mkXPrompt (showXPrompt "man ") promptConfig (manCompl promptConfig mans) $ spawnOn (workspaceId) $ (myTerminal) ++ " " ++ (terminalArgs) ++ " -e man "
    |                                                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                        

我认为这个问题是因为我没有正确掌握 Haskell 数据类型,但我不明白...如果调用 spawnOn 的正确方法是 spawnOn <workspaceId> <command>,如何我可以像上面那样将 String bash 命令作为 <command> 传递给函数吗?

编辑 #2 -- 解决方案 #1

我目前的解决方案是使用 XMonad.Prompt.Man 的自定义补丁,manPrompt 如下所示:

manPrompt :: XPConfig -> WorkspaceId -> String -> X ()
manPrompt promptConfig workspaceId terminalArgs = do
    mans <- io getMans
    mkXPrompt Man promptConfig (manCompl promptConfig mans) $
    \input -> spawnOn workspaceId $ myTerminal ++ " " ++ terminalArgs ++ " -e man " ++ input

我猜这可以编译并工作,但似乎使用 spawnOn 仍然会导致与 doShift 相同的文本对齐问题,当在 windows 之间移动终端时具有不同布局的工作区。 (我在我尝试过的下的原始post中提到了这些。)

为了更好地解释“文本对齐问题”的含义,我在此处提供了一个屏幕截图:https://ibb.co/vxjcKZh (这张截图是在一个布局被压扁的工作区上调用提示的结果。然后我将注意力转移到产生联机帮助页的工作区,它应该是全屏的,但你可以看到,文本没有填满整个终端 window;这是因为 man 命令 运行 当 window 大小被压缩时。)

有什么方法可以在终端 window 生成后以某种方式刷新文本?

runInTerm 的调整版本确实很适合这里。 runInTerm is defined as:

unsafeRunInTerm, runInTerm :: String -> String -> X ()
unsafeRunInTerm options command = asks (terminal . config) >>= \t ->
    unsafeSpawn $ t ++ " " ++ options ++ " -e " ++ command
runInTerm = unsafeRunInTerm

unsafeSpawn 又是 XMonad.Actions.SpawnOnspawn from XMonad.Core. There is a spawnOn 变体的别名,除了命令 [=53] 外,它还需要一个 WorkspaceId =].似乎使用 spawnOn 定义自定义 runInTerm 就足以得到你想要的东西。


在您的编辑尝试中,mkXPrompt 的最后一个参数应该是 String -> X () 函数,而不是 X () 动作。 String 参数代表用户在提示中选择的完成,在您的情况下,这将是 man 的参数。在manPrompt的原始实现中,String -> X ()函数是:

runInTerm "" . (++) "man "

或者,以一种可以说更清晰的方式改写它:

\completion -> runInTerm "" ("man " ++ completion)

你的manPrompt可以相应调整:

manPromptOn :: XPConfig -> WorkspaceId -> String -> X ()
manPromptOn promptConfig workspaceId terminalArgs = do
    mans <- io getMans
    mkXPrompt Man promptConfig (manCompl promptConfig mans) $
        \completion -> spawnOn workspaceId $
            myTerminal ++ " " ++ terminalArgs ++ " -e man " ++ completion

(请注意,我另外将第一个参数更改为 mkXPrompt,因为传递字符串将不起作用。我还假设此函数被添加到 [= 的修改副本之类的东西中39=],因为 Man 构造函数和 getMans 函数均未由模块的原始版本导出。)