So a few days ago, I wrote about adding pass
support into an XMonad prompt. Today,
I spent some time (read: most of my afternoon) trying to improve it. So here are
some of the improvements:
1
2
3
4
5
6
7
8
passwordStoreEnvVar :: String
passwordStoreEnvVar = "PASSWORD_STORE_DIR"
getPasswordDir :: IO FilePath
getPasswordDir = do
envDir <- lookupEnv passwordStoreEnvVar
home <- getEnv "HOME"
return $ fromMaybe (home </> ".password_store") envDir
The first is the resolution of the location of the directory structure. The original
expected that it would be defined in the PASSWORD_STORE_DIR
environment variable
(which is the most significant definition, but not de facto), which I do use, but if
this is not defined, the application will default to $HOME/.password_store/
. I
also cleaned up the environment variable to be a global constant.
The new function getPasswordDir
performs the same logic, first using
lookupEnv
to find the primary value, lookupEnv
returns Nothing
if the
environment variable is not defined (getEnv
would throw an exception). If it is
Nothing
, it will build the default $HOME/.password_store
path).
1
2
3
4
5
passwordPrompt :: XPConfig -> X ()
passwordPrompt config = do
li <- io getPasswords
let compl = \s -> filter (\x -> s `isInfixOf` x) li
mkXPrompt Pass config (return . compl) (selectPassword li)
The second improvement I made was making the completion function more “fuzzy”. This
meaning that if I want the password for “amazon” it would match amazon
,
web/amazon
, or something like amazon/aws
. Before it was only testing the prefix
which wasn’t as easy. I fixed this by replacing the use of mkComplFunFromList
with
my own function (which I just took what it would give from the source and modified
it) which uses isInfixOf
as the comparison. Having seen this in
action, I am tempted to override other prompts to be as helpful.
1
2
3
4
5
6
7
8
passwordLength :: Int
passwordLength = 24
selectPassword :: [String] -> String -> X ()
selectPassword passwords ps = spawn $ "pass " ++ args
where
args | ps `elem` passwords = "show -c " ++ ps
| otherwise = "generate -c " ++ ps ++ " " ++ (show passwordLength)
The final improvement is how it handles undefined entries. Originally, nothing would
happen, but it seems useful to be able to generate new passwords as well. So I
expanded the selectPassword
function to detect this. Now the arguments to pass
are generated with a where clause. The condition is if the password is an element
of the generated password list, then it will perform as before, copying
the password to the clipboard. If this fails, it will have pass
generate a
password instead, copying it to the clipboard. The length of this
password is defined in the passwordLength
definition.
After having played with this enough, I am tempted to try some other utility prompts,
the one that caught my eye is the XMonad.Prompt.Window
stuff to act as a sort
of super “Alt-Tab” where it would take you to the window you search for. The current
behavior only uses the window name, so searching for “firefox” doesn’t find anything
due to how it seeds the completion list. I am trying to populate the list with a
more verbose description of the windows and using either the infix filter or one of
the more advanced distance comparisons.