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

发布于 2025-01-11 15:43:02 字数 6222 浏览 0 评论 0 原文

我最近决定通过 Stack 从源代码构建 XMonad 来进行自定义配置。首先我要说的是我对 Haskell 没有太多的经验。 我的操作系统是 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) (src: 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 在现有终端中打开,并且不提供用于设置窗口名称的选项,所以我最初无法使用 ManageHook 来检测窗口名称和将其转移到正确的工作区,就像我对 Mozilla Firefox 等应用程序所做的那样:(https://hackage.haskell.org/package/xmonad-0.15/docs/XMonad-ManageHook.html

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

我尝试使用 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(即默认情况下为空),第二个是要运行的命令,“man”(如果是manPrompt)。它们都在默认终端(对我来说是 Alacritty)中运行。 (文档:https://hackage.haskell .org/package/xmonad-contrib-0.16/docs/XMonad-Util-Run.html) (src: 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 <工作空间ID> ,如何将 String bash 命令作为 传递给函数,如上面所示?

编辑 #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 相同的文本对齐问题。 (我在我尝试过的内容下的原始帖子中提到了这些。)

为了更好地解释“文本对齐问题”的含义,我在此处添加了一个屏幕截图:https://ibb.co/vxjcKZh (此屏幕截图是在具有压缩布局的工作区上调用提示的结果。然后,我将焦点切换到生成手册页的工作区,该工作区应该是全屏的,但您可以看到,文本没有填充整个终端窗口;这是因为 man 命令在窗口大小被压扁时运行。)

有没有办法可以在终端窗口生成后以某种方式刷新它?

I recently decided to build XMonad from source via Stack to make a custom configuration. Let me preface by saying I do not have a ton of experience with Haskell.
My OS is Linux - Arch.

Setup

I am attempting to make use of the XMonad.Prompt package from xmonad-contrib in order to launch some applications.
(https://hackage.haskell.org/package/xmonad-contrib-0.13/docs/XMonad-Prompt.html)

Currently, one prompt I am using is XMonad.Prompt.Man, which launches the man program with a provided argument in the default terminal (the terminal I default to is Alacritty).
(docs: https://hackage.haskell.org/package/xmonad-contrib-0.13/docs/XMonad-Prompt-Man.html)
(src: https://hackage.haskell.org/package/xmonad-contrib-0.13/docs/src/XMonad-Prompt-Man.html)

What I do not understand is how to use this prompt to launch the terminal to workspace 2, where I would like all my manpages to open.

The way the prompt works as of right now:

I have a keybinding set up in xmonad.hs, something like the following:

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

This works how the documentation says it should, but the manpages always open in the current workspace, not in workspace 2 where I want them to.

What I have tried

Because man opens in an existing terminal and does not provide options for setting the window name, I was initially not able to use ManageHook to detect the window name and shift it over to the correct workspace, like I do for applications like Mozilla Firefox: (https://hackage.haskell.org/package/xmonad-0.15/docs/XMonad-ManageHook.html)

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

I have tried setting the window name to something hard-coded using Alacritty's --title option, but that only shifts the terminal after it has been spawned and man has already been invoked on the current layout, resulting in incorrect text alignment when the terminal gets shifted to a different workspace with a different layout.

I thought a proper solution might require modifying the source code of XMonad.Prompt.Man.

Looking at the source code for XMonad.Prompt.Man, I saw that this line might be useful to change (line 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 "
    

Looking at the source code for runInTerm (from XMonad.Util.Run), it looks to me like it takes 2 string args: the first being a list of options, "" in the case of manPrompt (i.e. blank by default), and the second being the command to run, "man " in the case of manPrompt. These both get run in the default terminal (Alacritty for me).
(docs: https://hackage.haskell.org/package/xmonad-contrib-0.16/docs/XMonad-Util-Run.html)
(src: https://hackage.haskell.org/package/xmonad-contrib-0.16/docs/src/XMonad.Util.Run.html#runInTerm)

Is there something I can do to modify manPrompt or runInTerm so that I can specify which workspace the terminal spawned by manPrompt spawns in? Or is there any other way anyone knows of achieving my desired behavior that I do not know about?

EDIT #1

At the suggestion of a recent answer, I am attempting to patch the code for manPrompt to run spawnOn from XMonad.Actions.SpawnOn, rather than runInTerm.
(https://hackage.haskell.org/package/xmonad-contrib-0.13/docs/XMonad-Actions-SpawnOn.html)

Here is what I have right now:

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 "

However, this will not compile, resulting in the error:

 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 "
    |                                                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                        

I assume this issue is with my failure to grasp Haskell datatypes properly, but I do not understand...if the proper way to invoke spawnOn is spawnOn <workspaceId> <command>, how can I pass a String bash command to the function as <command>, like above?

EDIT #2 -- SOLUTION #1

My current solution is to use a custom patch of XMonad.Prompt.Man, with a manPrompt that looks like the following:

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

This compiles and works, I guess, but it seems that the use of spawnOn still results in the same text alignment issues as with doShift when moving the terminal windows between workspaces with different layouts. (I mentioned these in the original post under What I have tried.)

To better explain what I mean by "text alignment issues," I included a screenshot here: https://ibb.co/vxjcKZh
(This screenshot is the result of summoning the prompt on a workspace with a squished layout. I then switched my focus over to the workspace that the manpages got spawned on, which should be full-screen, but you can see, the text does not fill the entire terminal window; this is because the man command ran when the window size was squished.)

Is there a way I can refresh the text in terminal window somehow after it spawns?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一袭白衣梦中忆 2025-01-18 15:43:03

runInTerm 的调整版本确实很适合这里。 runInTerm 定义为

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

unsafeSpawn,反过来,是spawn 来自 <代码>XMonad.Core。有一个 < XMonad.Actions.SpawnOn 中的 code>spawnOn 变体,它采用 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< /code>,因为传递字符串是行不通的,我还假设这个函数被添加到类似 XMonad.Prompt.Man< /code> module,因为 Man 构造函数和 getMans 函数都不是由该模块的原始版本导出的。)

A tweaked version of runInTerm would indeed be a good fit here. runInTerm is defined as:

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

unsafeSpawn, in turn, is an alias for spawn from XMonad.Core. There is a spawnOn variation on it in XMonad.Actions.SpawnOn which takes a WorkspaceId in addition to the command to be ran. It seems like using spawnOn to define a custom runInTerm would be enough to get what you want.


On the attempt in your edit, the final argument to mkXPrompt should be a String -> X () function, rather than a X () action. The String argument stands for the completion chosen by the user in the prompt, which, in your case, would be the argument to man. In the original implementation of manPrompt, the String -> X () function is:

runInTerm "" . (++) "man "

Or, rephrasing it in an arguably clearer style:

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

Your manPrompt can be adjusted accordingly:

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

(Note that I have additionally changed the first argument to mkXPrompt, as passing a string won't work. I'm also assuming this function is being added to something like a modified copy of the XMonad.Prompt.Man module, as neither the Man constructor nor the getMans function are exported by the original version of the module.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文