X.P.RunOrRaise: Disambiguate directories and executables

When there is a directory and a file in $PATH of the same name `foo`, we
display `foo`, as well as `foo/` in the prompt, but selecting either one
of them will invariably run or raise the directory, as this test is done
beforehand.

Since directories already carry a trailing forward slash from the shell
prompt's `getShellCompl`, this is easily disambiguated by incorporating
an additional check.

It is duly noted that the same problem also exists for files in the
current directory.  This could be fixed in a similar way (adding a
unique suffix to files and checking for that), but since this would
involve changing `getShellCompl` and no-one has complained about this so
far, it was deemed "not worth it" for now.

Fixes: https://github.com/xmonad/xmonad-contrib/issues/616
This commit is contained in:
slotThe
2021-10-09 09:03:50 +02:00
parent 9ff7f89664
commit 5bdc5993f9

View File

@@ -22,14 +22,14 @@ module XMonad.Prompt.RunOrRaise
) where ) where
import XMonad hiding (config) import XMonad hiding (config)
import XMonad.Prelude (liftA2) import XMonad.Prelude (isNothing, isSuffixOf, liftA2)
import XMonad.Prompt import XMonad.Prompt
import XMonad.Prompt.Shell import XMonad.Prompt.Shell
import XMonad.Actions.WindowGo (runOrRaise) import XMonad.Actions.WindowGo (runOrRaise)
import XMonad.Util.Run (runProcessWithInput) import XMonad.Util.Run (runProcessWithInput)
import Control.Exception as E import Control.Exception as E
import System.Directory (doesDirectoryExist, doesFileExist, executable, getPermissions) import System.Directory (doesDirectoryExist, doesFileExist, executable, findExecutable, getPermissions)
econst :: Monad m => a -> IOException -> m a econst :: Monad m => a -> IOException -> m a
econst = const . return econst = const . return
@@ -60,9 +60,14 @@ open path = io (isNormalFile path) >>= \b ->
then spawn $ "xdg-open \"" ++ path ++ "\"" then spawn $ "xdg-open \"" ++ path ++ "\""
else uncurry runOrRaise . getTarget $ path else uncurry runOrRaise . getTarget $ path
where where
isNormalFile f = exists f >>= \e -> if e then notExecutable f else return False isNormalFile f = do
exists f = or <$> sequence [doesFileExist f,doesDirectoryExist f] notCommand <- isNothing <$> findExecutable f -- not a command (executable in $PATH)
exists <- or <$> sequence [doesDirExist f, doesFileExist f]
case (notCommand, exists) of
(True, True) -> notExecutable f -- not executable as a file in current dir
_ -> pure False
notExecutable = fmap (not . executable) . getPermissions notExecutable = fmap (not . executable) . getPermissions
doesDirExist f = ("/" `isSuffixOf` f &&) <$> doesDirectoryExist f
getTarget x = (x,isApp x) getTarget x = (x,isApp x)
isApp :: String -> Query Bool isApp :: String -> Query Bool