我最近决定通过 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)
有吗我可以做些什么来修改 manPrompt
或 runInTerm
以便我可以指定由 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?
发布评论
评论(1)
runInTerm
的调整版本确实很适合这里。runInTerm
定义为:unsafeSpawn
,反过来,是spawn
来自 <代码>XMonad.Core。有一个 <XMonad.Actions.SpawnOn
中的 code>spawnOn 变体,它采用WorkspaceId
除了要运行的命令之外。似乎使用spawnOn
定义自定义runInTerm
足以获得您想要的东西。在您编辑的尝试中,
mkXPrompt
应该是String -> X()
函数,而不是X()
操作。String
参数代表用户在提示中选择的完成,在您的情况下,这将是man
的参数。在manPrompt
的原始实现中,String -> X ()
函数是:或者,以一种可以说更清晰的风格重新表述它:
您的
manPrompt
可以进行相应调整:(请注意,我另外将第一个参数更改为
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:unsafeSpawn
, in turn, is an alias forspawn
fromXMonad.Core
. There is aspawnOn
variation on it inXMonad.Actions.SpawnOn
which takes aWorkspaceId
in addition to the command to be ran. It seems like usingspawnOn
to define a customrunInTerm
would be enough to get what you want.On the attempt in your edit, the final argument to
mkXPrompt
should be aString -> X ()
function, rather than aX ()
action. TheString
argument stands for the completion chosen by the user in the prompt, which, in your case, would be the argument toman
. In the original implementation ofmanPrompt
, theString -> X ()
function is:Or, rephrasing it in an arguably clearer style:
Your
manPrompt
can be adjusted accordingly:(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 theXMonad.Prompt.Man
module, as neither theMan
constructor nor thegetMans
function are exported by the original version of the module.)