mirror of
https://gitlab.com/EternalWanderer/voidcruiser.nl
synced 2024-11-28 20:03:50 +01:00
XMonad prompt article
This commit is contained in:
parent
d15425526a
commit
8ee89fe117
|
@ -16,6 +16,10 @@ standard XMonad Contrib modules (`XMonad.Prompt.Shell`, `XMonad.Prompt.Ssh` and
|
|||
it came to my universal/external Qutebrowser bookmarks menu and
|
||||
`yt-dlp`-and-`pipe-viewer` wrapper.
|
||||
|
||||
This tutorial of sorts with assume _some_ Haskell knowledge or not being afraid
|
||||
of diving straight into how Haskell works. I'm not going into great detail on
|
||||
how everything works here.
|
||||
|
||||
# Bookmarks menu
|
||||
|
||||
The first one I decided to tackle was the bookmarks menu, as it is by far the
|
||||
|
@ -129,17 +133,15 @@ Lets see if there is anything in the
|
|||
module that looks like it could help us in creating a prompt.
|
||||
|
||||
|
||||
---
|
||||
```haskell
|
||||
mkXPrompt :: XPrompt p => p -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
|
||||
```
|
||||
Creates a prompt given:
|
||||
- a prompt type, instance of the `XPrompt` class.
|
||||
- a prompt configuration (`def` can be used as a starting point)
|
||||
- a completion function (`mkComplFunFromList` can be used to create a completions function given a list of possible completions)
|
||||
- an action to be run: the action must take a string and return `X ()`
|
||||
|
||||
---
|
||||
> ```haskell
|
||||
> mkXPrompt :: XPrompt p => p -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
|
||||
> ```
|
||||
> ---
|
||||
> Creates a prompt given:
|
||||
> - a prompt type, instance of the `XPrompt` class.
|
||||
> - a prompt configuration (`def` can be used as a starting point)
|
||||
> - a completion function (`mkComplFunFromList` can be used to create a completions function given a list of possible completions)
|
||||
> - an action to be run: the action must take a string and return `X ()`
|
||||
|
||||
This looks like it could serve as the basis for our prompt. The description and
|
||||
type signature tell us that it is going to require an instance of the `XPrompt`
|
||||
|
@ -170,25 +172,128 @@ Now for the completion function, that will handle the list given to our prompt.
|
|||
Lets mostly follow the suggestion in the description of `mkXPrompt` and lets
|
||||
take a look at:
|
||||
|
||||
---
|
||||
```haskell
|
||||
mkComplFunFromList' :: XPConfig -> [String] -> String -> IO [String]
|
||||
```
|
||||
This function takes a list of possible completions and returns a completions
|
||||
function to be used with mkXPrompt. If the string is null it will return all
|
||||
completions.
|
||||
|
||||
---
|
||||
> ```haskell
|
||||
> mkComplFunFromList' :: XPConfig -> [String] -> String -> IO [String]
|
||||
> ```
|
||||
> ---
|
||||
> This function takes a list of possible completions and returns a completions
|
||||
> function to be used with mkXPrompt. If the string is null it will return all
|
||||
> completions.
|
||||
|
||||
This is how Qutebrowser and `dmenu` act by default with a given list of possible
|
||||
options.
|
||||
|
||||
So it takes an instance of `XPConfig` -- that will be our `c` argument, and a
|
||||
list of strings. Here is where we feed it the contents of our file using our
|
||||
`fileContentList` function.
|
||||
```haskell
|
||||
bookmarksFile = ".config/qutebrowser/bookmarks/urls" :: String
|
||||
```
|
||||
> I didn't know where to put this, but I created a string to hold the path to my
|
||||
> bookmarks
|
||||
|
||||
So it takes an instance of `XPConfig` -- that will again be our `c` argument,
|
||||
and a list of strings. Here is where we feed it the contents of our file using
|
||||
our `fileContentList` function. We will do this by binding the output to, say
|
||||
`bl` for "bookmark list" with `<-`. Since `fileContentList` is a member of the
|
||||
`IO` monad and we're working in, we have to call it using the `io` function,
|
||||
which is an alias for the `liftIO` function.
|
||||
|
||||
```haskell
|
||||
bookmarkPrompt c = do
|
||||
bl <- io fileContentList
|
||||
mkXPrompt Bookmark c (mkComplFunFromList' c bl)
|
||||
bookmarkPrompt :: XPConfig -> (String -> X ()) -> X ()
|
||||
bookmarkPrompt c f = do
|
||||
bl <- io fileContentList bookmarksFile
|
||||
mkXPrompt Bookmark c (mkComplFunFromList' c bl) f
|
||||
```
|
||||
|
||||
You'll see that I've also added argument `f`, this is the function we're going
|
||||
to use to actually do something with our prompt output. Considering we're
|
||||
working with bookmarks, opening them in a browser would make sense.
|
||||
|
||||
```haskell
|
||||
openBookmark :: String -> X ()
|
||||
openBookmark bookmark = do
|
||||
browser <- io getBrowser
|
||||
spawn $ browser ++ " '" ++ getUrl bookmark ++ "'"
|
||||
where getUrl = head . words
|
||||
```
|
||||
`openBookmark` is a function that takes a string and returns something in the
|
||||
context of the `X` monad (hence the name "XMonad", it's a monad that interacts
|
||||
with Xorg). Lets go through it line by line.
|
||||
|
||||
```haskell
|
||||
browser <- io getBrowser
|
||||
```
|
||||
First we get user's browser using the `getBrowser` function from the
|
||||
`XMonad.Prompt.Shell` module and bind that to `browser`.
|
||||
|
||||
This function checks the `$BROWSER` environment variable and if it isn't set, it
|
||||
defaults to "firefox".
|
||||
|
||||
```haskell
|
||||
spawn $ browser ++ " '" ++ getUrl bookmark ++ "'"
|
||||
```
|
||||
Since `getBrowser` returns a string, we can append things to it and feed that to
|
||||
`spawn`. In this case, we get the URL portion of the bookmark entry surrounded by
|
||||
single quotes in case a given bookmark contains any symbols that mess up our
|
||||
shell. After all, what `spawn` ultimately does is feed a given string to
|
||||
`/bin/sh` as a command to execute.
|
||||
|
||||
```haskell
|
||||
where getUrl = head . words
|
||||
```
|
||||
For get `getUrl`, we take the given string, split it into a list of strings
|
||||
based on space characters, pipe that into head, thus retrieving the first item.
|
||||
|
||||
## Keybinding
|
||||
|
||||
We now have a set of functions that create a prompt populated with our
|
||||
Qutebrowser bookmarks file (any other list of URLs will also work) which will
|
||||
open our browser when choosing one.
|
||||
|
||||
Now all we have to do is bind it to a key. Personally I use the
|
||||
`XMonad.Util.EZConfig` so I have the following in my keybindings:
|
||||
|
||||
```haskell
|
||||
, ("M-M1-C-b", bookmarkPrompt (myXPConfig {autoComplete = Just 200000}) openBookmark Bookmark)
|
||||
```
|
||||
If you use the default way of defining keybindings you can use something like
|
||||
the following:
|
||||
|
||||
```haskell
|
||||
, ((modm .|. controlMask, xK_b), bookmarkPrompt def openBookmark)
|
||||
|
||||
```
|
||||
`def` is a reference to the default implementation of `XPConfig`.
|
||||
|
||||
# Everything Together
|
||||
|
||||
Everything put together, your config should have something like the following
|
||||
added.
|
||||
|
||||
```haskell
|
||||
data Bookmark = Bookmark
|
||||
|
||||
instance XPrompt Bookmark where
|
||||
showXPrompt Bookmark = "Bookmark: "
|
||||
|
||||
bookmarksFile = ".config/qutebrowser/bookmarks/urls" :: String
|
||||
|
||||
fileContentList :: FilePath -> IO [String]
|
||||
fileContentList f = do
|
||||
homeDir <- getEnv "HOME"
|
||||
file <- readFile (homeDir ++ "/" ++ f)
|
||||
return . uniqSort . lines $ file
|
||||
|
||||
bookmarkPrompt :: XPConfig -> (String -> X ()) -> X ()
|
||||
bookmarkPrompt c f = do
|
||||
bl <- io fileContentList bookmarksFile
|
||||
mkXPrompt Bookmark c (mkComplFunFromList' c bl) f
|
||||
|
||||
openBookmark :: String -> X ()
|
||||
openBookmark bookmark = do
|
||||
browser <- io getBrowser
|
||||
spawn $ browser ++ " '" ++ getUrl bookmark ++ "'"
|
||||
where getUrl = head . words
|
||||
|
||||
-- ... keybindings
|
||||
, ((modm .|. controlMask, xK_b), bookmarkPrompt def openBookmark)
|
||||
-- more keybindings ...
|
||||
```
|
||||
|
|
Loading…
Reference in a new issue