{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Feedback.Loop.Filter where

import Data.ByteString (ByteString)
import qualified Data.ByteString.Lazy as LB
import qualified Data.ByteString.Lazy.Char8 as LB8
import Data.Conduit
import qualified Data.Conduit.Combinators as C
import qualified Data.Conduit.List as CL
import Data.List
import Data.Set
import qualified Data.Set as S
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import Feedback.Common.OptParse
import Path
import Path.IO
import System.Exit
import System.Process.Typed as Typed
#ifdef MIN_VERSION_Win32
import System.Win32.MinTTY (isMinTTYHandle)
import System.Win32.Types (withHandleToHANDLE)
#endif
import UnliftIO

#ifdef MIN_VERSION_Win32
getMinTTY :: IO Bool
getMinTTY = withHandleToHANDLE stdin isMinTTYHandle
#else
getMinTTY :: IO Bool
getMinTTY :: IO Bool
getMinTTY = Bool -> IO Bool
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
False
#endif

data Filter = Filter
  { Filter -> Path Abs Dir -> Bool
filterDirFilter :: Path Abs Dir -> Bool,
    Filter -> Path Abs File -> Bool
filterFileFilter :: Path Abs File -> Bool
  }

instance Semigroup Filter where
  Filter
f1 <> :: Filter -> Filter -> Filter
<> Filter
f2 =
    Filter
      { filterDirFilter :: Path Abs Dir -> Bool
filterDirFilter = \Path Abs Dir
d -> Filter -> Path Abs Dir -> Bool
filterDirFilter Filter
f1 Path Abs Dir
d Bool -> Bool -> Bool
&& Filter -> Path Abs Dir -> Bool
filterDirFilter Filter
f2 Path Abs Dir
d,
        filterFileFilter :: Path Abs File -> Bool
filterFileFilter = \Path Abs File
f -> Filter -> Path Abs File -> Bool
filterFileFilter Filter
f1 Path Abs File
f Bool -> Bool -> Bool
&& Filter -> Path Abs File -> Bool
filterFileFilter Filter
f2 Path Abs File
f
      }

instance Monoid Filter where
  mempty :: Filter
mempty = Filter {filterDirFilter :: Path Abs Dir -> Bool
filterDirFilter = Bool -> Path Abs Dir -> Bool
forall a b. a -> b -> a
const Bool
True, filterFileFilter :: Path Abs File -> Bool
filterFileFilter = Bool -> Path Abs File -> Bool
forall a b. a -> b -> a
const Bool
True}
  mappend :: Filter -> Filter -> Filter
mappend = Filter -> Filter -> Filter
forall a. Semigroup a => a -> a -> a
(<>)

fileSetFilter :: Set (Path Abs File) -> Filter
fileSetFilter :: Set (Path Abs File) -> Filter
fileSetFilter Set (Path Abs File)
fileSet =
  let dirSet :: Set (Path Abs Dir)
dirSet = (Path Abs File -> Path Abs Dir)
-> Set (Path Abs File) -> Set (Path Abs Dir)
forall b a. Ord b => (a -> b) -> Set a -> Set b
S.map Path Abs File -> Path Abs Dir
forall b t. Path b t -> Path b Dir
parent Set (Path Abs File)
fileSet
   in Filter
        { filterDirFilter :: Path Abs Dir -> Bool
filterDirFilter = (Path Abs Dir -> Set (Path Abs Dir) -> Bool
forall a. Ord a => a -> Set a -> Bool
`S.member` Set (Path Abs Dir)
dirSet),
          filterFileFilter :: Path Abs File -> Bool
filterFileFilter = (Path Abs File -> Set (Path Abs File) -> Bool
forall a. Ord a => a -> Set a -> Bool
`S.member` Set (Path Abs File)
fileSet)
        }

mkCombinedFilter :: Path Abs Dir -> FilterSettings -> IO Filter
mkCombinedFilter :: Path Abs Dir -> FilterSettings -> IO Filter
mkCombinedFilter Path Abs Dir
here FilterSettings
filterSettings =
  [Filter] -> Filter
forall a. Monoid a => [a] -> a
mconcat
    ([Filter] -> Filter) -> IO [Filter] -> IO Filter
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [IO Filter] -> IO [Filter]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
forall (m :: * -> *) a. Monad m => [m a] -> m [a]
sequence
      [ Path Abs Dir -> FilterSettings -> IO Filter
mkGitFilter Path Abs Dir
here FilterSettings
filterSettings,
        Path Abs Dir -> FilterSettings -> IO Filter
mkFindFilter Path Abs Dir
here FilterSettings
filterSettings,
        Filter -> IO Filter
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Filter -> IO Filter) -> Filter -> IO Filter
forall a b. (a -> b) -> a -> b
$ Path Abs Dir -> Filter
standardFilter Path Abs Dir
here
      ]

mkStdinFilter :: Path Abs Dir -> IO Filter
mkStdinFilter :: Path Abs Dir -> IO Filter
mkStdinFilter Path Abs Dir
here = Filter
-> (Set (Path Abs File) -> Filter)
-> Maybe (Set (Path Abs File))
-> Filter
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Filter
forall a. Monoid a => a
mempty Set (Path Abs File) -> Filter
fileSetFilter (Maybe (Set (Path Abs File)) -> Filter)
-> IO (Maybe (Set (Path Abs File))) -> IO Filter
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Path Abs Dir -> IO (Maybe (Set (Path Abs File)))
getStdinFiles Path Abs Dir
here

getStdinFiles :: Path Abs Dir -> IO (Maybe (Set (Path Abs File)))
getStdinFiles :: Path Abs Dir -> IO (Maybe (Set (Path Abs File)))
getStdinFiles Path Abs Dir
here = do
  Bool
isTerminal <- Handle -> IO Bool
forall (m :: * -> *). MonadIO m => Handle -> m Bool
hIsTerminalDevice Handle
stdin
  Bool
isMinTTY <- IO Bool
getMinTTY
  if Bool
isTerminal Bool -> Bool -> Bool
|| Bool
isMinTTY
    then Maybe (Set (Path Abs File)) -> IO (Maybe (Set (Path Abs File)))
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe (Set (Path Abs File))
forall a. Maybe a
Nothing
    else
      (Set (Path Abs File) -> Maybe (Set (Path Abs File))
forall a. a -> Maybe a
Just (Set (Path Abs File) -> Maybe (Set (Path Abs File)))
-> IO (Set (Path Abs File)) -> IO (Maybe (Set (Path Abs File)))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Path Abs Dir -> Handle -> IO (Set (Path Abs File))
handleFileSet Path Abs Dir
here Handle
stdin)
        IO (Maybe (Set (Path Abs File)))
-> (IOException -> IO (Maybe (Set (Path Abs File))))
-> IO (Maybe (Set (Path Abs File)))
forall (m :: * -> *) e a.
(MonadUnliftIO m, Exception e) =>
m a -> (e -> m a) -> m a
`catch` (\(IOException
_ :: IOException) -> Maybe (Set (Path Abs File)) -> IO (Maybe (Set (Path Abs File)))
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe (Set (Path Abs File))
forall a. Maybe a
Nothing)

mkGitFilter :: Path Abs Dir -> FilterSettings -> IO Filter
mkGitFilter :: Path Abs Dir -> FilterSettings -> IO Filter
mkGitFilter Path Abs Dir
here FilterSettings {Bool
Maybe [Char]
filterSettingGitignore :: Bool
filterSettingFind :: Maybe [Char]
filterSettingGitignore :: FilterSettings -> Bool
filterSettingFind :: FilterSettings -> Maybe [Char]
..} = do
  if Bool
filterSettingGitignore
    then do
      Maybe (Set (Path Abs File))
mGitFiles <- Path Abs Dir -> IO (Maybe (Set (Path Abs File)))
gitLsFiles Path Abs Dir
here
      Filter -> IO Filter
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Filter -> IO Filter) -> Filter -> IO Filter
forall a b. (a -> b) -> a -> b
$ Filter
-> (Set (Path Abs File) -> Filter)
-> Maybe (Set (Path Abs File))
-> Filter
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Filter
forall a. Monoid a => a
mempty Set (Path Abs File) -> Filter
fileSetFilter Maybe (Set (Path Abs File))
mGitFiles
    else Filter -> IO Filter
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Filter
forall a. Monoid a => a
mempty

gitLsFiles :: Path Abs Dir -> IO (Maybe (Set (Path Abs File)))
gitLsFiles :: Path Abs Dir -> IO (Maybe (Set (Path Abs File)))
gitLsFiles Path Abs Dir
here = do
  -- If there is no git directory, we'll get a 'fatal' message on stderr.
  -- We don't need the user to see this, so we setStderr nullStream.
  let processConfig :: ProcessConfig () () ()
processConfig = StreamSpec 'STOutput ()
-> ProcessConfig () () () -> ProcessConfig () () ()
forall stderr stdin stdout stderr0.
StreamSpec 'STOutput stderr
-> ProcessConfig stdin stdout stderr0
-> ProcessConfig stdin stdout stderr
setStderr StreamSpec 'STOutput ()
forall (anyStreamType :: StreamType). StreamSpec anyStreamType ()
nullStream (ProcessConfig () () () -> ProcessConfig () () ())
-> ProcessConfig () () () -> ProcessConfig () () ()
forall a b. (a -> b) -> a -> b
$ [Char] -> ProcessConfig () () ()
shell [Char]
"git ls-files"
  (ExitCode
ec, ByteString
out) <- ProcessConfig () () () -> IO (ExitCode, ByteString)
forall (m :: * -> *) stdin stdoutIgnored stderr.
MonadIO m =>
ProcessConfig stdin stdoutIgnored stderr
-> m (ExitCode, ByteString)
readProcessStdout ProcessConfig () () ()
processConfig
  Set (Path Abs File)
set <- Path Abs Dir -> ByteString -> IO (Set (Path Abs File))
bytesFileSet Path Abs Dir
here ByteString
out
  Maybe (Set (Path Abs File)) -> IO (Maybe (Set (Path Abs File)))
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe (Set (Path Abs File)) -> IO (Maybe (Set (Path Abs File))))
-> Maybe (Set (Path Abs File)) -> IO (Maybe (Set (Path Abs File)))
forall a b. (a -> b) -> a -> b
$ case ExitCode
ec of
    ExitFailure Int
_ -> Maybe (Set (Path Abs File))
forall a. Maybe a
Nothing
    ExitCode
ExitSuccess -> Set (Path Abs File) -> Maybe (Set (Path Abs File))
forall a. a -> Maybe a
Just Set (Path Abs File)
set

mkFindFilter :: Path Abs Dir -> FilterSettings -> IO Filter
mkFindFilter :: Path Abs Dir -> FilterSettings -> IO Filter
mkFindFilter Path Abs Dir
here FilterSettings {Bool
Maybe [Char]
filterSettingGitignore :: FilterSettings -> Bool
filterSettingFind :: FilterSettings -> Maybe [Char]
filterSettingGitignore :: Bool
filterSettingFind :: Maybe [Char]
..} = case Maybe [Char]
filterSettingFind of
  Maybe [Char]
Nothing -> Filter -> IO Filter
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Filter
forall a. Monoid a => a
mempty
  Just [Char]
args -> Set (Path Abs File) -> Filter
fileSetFilter (Set (Path Abs File) -> Filter)
-> IO (Set (Path Abs File)) -> IO Filter
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Path Abs Dir -> [Char] -> IO (Set (Path Abs File))
filesFromFindArgs Path Abs Dir
here [Char]
args

filesFromFindArgs :: Path Abs Dir -> String -> IO (Set (Path Abs File))
filesFromFindArgs :: Path Abs Dir -> [Char] -> IO (Set (Path Abs File))
filesFromFindArgs Path Abs Dir
here [Char]
args = do
  let processConfig :: ProcessConfig () Handle ()
processConfig = StreamSpec 'STOutput Handle
-> ProcessConfig () () () -> ProcessConfig () Handle ()
forall stdout stdin stdout0 stderr.
StreamSpec 'STOutput stdout
-> ProcessConfig stdin stdout0 stderr
-> ProcessConfig stdin stdout stderr
setStdout StreamSpec 'STOutput Handle
forall (anyStreamType :: StreamType).
StreamSpec anyStreamType Handle
createPipe (ProcessConfig () () () -> ProcessConfig () Handle ())
-> ProcessConfig () () () -> ProcessConfig () Handle ()
forall a b. (a -> b) -> a -> b
$ [Char] -> ProcessConfig () () ()
shell ([Char] -> ProcessConfig () () ())
-> [Char] -> ProcessConfig () () ()
forall a b. (a -> b) -> a -> b
$ [Char]
"find " [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
args
  (ExitCode
ec, ByteString
out) <- ProcessConfig () Handle () -> IO (ExitCode, ByteString)
forall (m :: * -> *) stdin stdoutIgnored stderr.
MonadIO m =>
ProcessConfig stdin stdoutIgnored stderr
-> m (ExitCode, ByteString)
readProcessStdout ProcessConfig () Handle ()
processConfig
  Set (Path Abs File)
set <- Path Abs Dir -> ByteString -> IO (Set (Path Abs File))
bytesFileSet Path Abs Dir
here ByteString
out
  case ExitCode
ec of
    ExitFailure Int
_ -> [Char] -> IO (Set (Path Abs File))
forall a. [Char] -> IO a
die ([Char] -> IO (Set (Path Abs File)))
-> [Char] -> IO (Set (Path Abs File))
forall a b. (a -> b) -> a -> b
$ [Char]
"Find failed: " [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> ExitCode -> [Char]
forall a. Show a => a -> [Char]
show ExitCode
ec
    ExitCode
ExitSuccess -> Set (Path Abs File) -> IO (Set (Path Abs File))
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Set (Path Abs File)
set

bytesFileSet :: Path Abs Dir -> LB.ByteString -> IO (Set (Path Abs File))
bytesFileSet :: Path Abs Dir -> ByteString -> IO (Set (Path Abs File))
bytesFileSet Path Abs Dir
here ByteString
lb =
  ConduitT () Void IO (Set (Path Abs File))
-> IO (Set (Path Abs File))
forall (m :: * -> *) r. Monad m => ConduitT () Void m r -> m r
runConduit (ConduitT () Void IO (Set (Path Abs File))
 -> IO (Set (Path Abs File)))
-> ConduitT () Void IO (Set (Path Abs File))
-> IO (Set (Path Abs File))
forall a b. (a -> b) -> a -> b
$
    [ByteString] -> ConduitT () ByteString IO ()
forall (m :: * -> *) a i. Monad m => [a] -> ConduitT i a m ()
CL.sourceList (ByteString -> [ByteString]
LB8.lines ByteString
lb)
      ConduitT () ByteString IO ()
-> ConduitT ByteString Void IO (Set (Path Abs File))
-> ConduitT () Void IO (Set (Path Abs File))
forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
.| (ByteString -> ByteString) -> ConduitT ByteString ByteString IO ()
forall (m :: * -> *) a b. Monad m => (a -> b) -> ConduitT a b m ()
C.map ByteString -> ByteString
LB.toStrict
      ConduitT ByteString ByteString IO ()
-> ConduitT ByteString Void IO (Set (Path Abs File))
-> ConduitT ByteString Void IO (Set (Path Abs File))
forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
.| Path Abs Dir -> ConduitT ByteString Void IO (Set (Path Abs File))
fileSetBuilder Path Abs Dir
here

handleFileSet :: Path Abs Dir -> Handle -> IO (Set (Path Abs File))
handleFileSet :: Path Abs Dir -> Handle -> IO (Set (Path Abs File))
handleFileSet Path Abs Dir
here Handle
h =
  ConduitT () Void IO (Set (Path Abs File))
-> IO (Set (Path Abs File))
forall (m :: * -> *) r. Monad m => ConduitT () Void m r -> m r
runConduit (ConduitT () Void IO (Set (Path Abs File))
 -> IO (Set (Path Abs File)))
-> ConduitT () Void IO (Set (Path Abs File))
-> IO (Set (Path Abs File))
forall a b. (a -> b) -> a -> b
$
    Handle -> ConduitT () ByteString IO ()
forall (m :: * -> *) i.
MonadIO m =>
Handle -> ConduitT i ByteString m ()
C.sourceHandle Handle
h
      ConduitT () ByteString IO ()
-> ConduitT ByteString Void IO (Set (Path Abs File))
-> ConduitT () Void IO (Set (Path Abs File))
forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
.| ConduitT ByteString ByteString IO ()
forall (m :: * -> *) seq.
(Monad m, IsSequence seq, Element seq ~ Word8) =>
ConduitT seq seq m ()
C.linesUnboundedAscii
      ConduitT ByteString ByteString IO ()
-> ConduitT ByteString Void IO (Set (Path Abs File))
-> ConduitT ByteString Void IO (Set (Path Abs File))
forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
.| Path Abs Dir -> ConduitT ByteString Void IO (Set (Path Abs File))
fileSetBuilder Path Abs Dir
here

fileSetBuilder :: Path Abs Dir -> ConduitT ByteString Void IO (Set (Path Abs File))
fileSetBuilder :: Path Abs Dir -> ConduitT ByteString Void IO (Set (Path Abs File))
fileSetBuilder Path Abs Dir
here =
  (ByteString -> Either UnicodeException Text)
-> ConduitT
     ByteString (Element (Either UnicodeException Text)) IO ()
forall (m :: * -> *) mono a.
(Monad m, MonoFoldable mono) =>
(a -> mono) -> ConduitT a (Element mono) m ()
C.concatMap ByteString -> Either UnicodeException Text
TE.decodeUtf8'
    ConduitT ByteString Text IO ()
-> ConduitT Text Void IO (Set (Path Abs File))
-> ConduitT ByteString Void IO (Set (Path Abs File))
forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
.| (Text -> [Char]) -> ConduitT Text [Char] IO ()
forall (m :: * -> *) a b. Monad m => (a -> b) -> ConduitT a b m ()
C.map Text -> [Char]
T.unpack
    ConduitT Text [Char] IO ()
-> ConduitT [Char] Void IO (Set (Path Abs File))
-> ConduitT Text Void IO (Set (Path Abs File))
forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
.| ([Char] -> IO (Path Abs File))
-> ConduitT [Char] (Path Abs File) IO ()
forall (m :: * -> *) a b.
Monad m =>
(a -> m b) -> ConduitT a b m ()
C.mapM (Path Abs Dir -> [Char] -> IO (Path Abs File)
forall (m :: * -> *).
MonadIO m =>
Path Abs Dir -> [Char] -> m (Path Abs File)
resolveFile Path Abs Dir
here)
    ConduitT [Char] (Path Abs File) IO ()
-> ConduitT (Path Abs File) Void IO (Set (Path Abs File))
-> ConduitT [Char] Void IO (Set (Path Abs File))
forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
.| (Path Abs File -> Set (Path Abs File))
-> ConduitT (Path Abs File) Void IO (Set (Path Abs File))
forall (m :: * -> *) b a o.
(Monad m, Monoid b) =>
(a -> b) -> ConduitT a o m b
C.foldMap Path Abs File -> Set (Path Abs File)
forall a. a -> Set a
S.singleton

standardFilter :: Path Abs Dir -> Filter
standardFilter :: Path Abs Dir -> Filter
standardFilter Path Abs Dir
here =
  Filter
    { filterDirFilter :: Path Abs Dir -> Bool
filterDirFilter = Bool -> Bool
not (Bool -> Bool) -> (Path Abs Dir -> Bool) -> Path Abs Dir -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Path Abs Dir -> Path Abs Dir -> Bool
forall b t. Path b Dir -> Path b t -> Bool
isHiddenIn Path Abs Dir
here,
      filterFileFilter :: Path Abs File -> Bool
filterFileFilter = \Path Abs File
f ->
        [Bool] -> Bool
forall (t :: * -> *). Foldable t => t Bool -> Bool
and
          [ Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Path Abs Dir -> Path Abs File -> Bool
forall b t. Path b Dir -> Path b t -> Bool
isHiddenIn Path Abs Dir
here Path Abs File
f,
            -- It's not one of those files that vim makes
            Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [Char]
"~" [Char] -> [Char] -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` Path Abs File -> [Char]
fromAbsFile Path Abs File
f,
            Path Abs File -> Path Rel File
forall b. Path b File -> Path Rel File
filename Path Abs File
f Path Rel File -> Path Rel File -> Bool
forall a. Eq a => a -> a -> Bool
/= [relfile|4913|]
          ]
    }

hidden :: Path Rel File -> Bool
hidden :: Path Rel File -> Bool
hidden = Path Rel File -> Bool
goFile
  where
    goFile :: Path Rel File -> Bool
    goFile :: Path Rel File -> Bool
goFile Path Rel File
f = Path Rel Dir -> Path Rel File -> Bool
forall b t. Path b Dir -> Path b t -> Bool
isHiddenIn (Path Rel File -> Path Rel Dir
forall b t. Path b t -> Path b Dir
parent Path Rel File
f) Path Rel File
f Bool -> Bool -> Bool
|| Path Rel Dir -> Bool
goDir (Path Rel File -> Path Rel Dir
forall b t. Path b t -> Path b Dir
parent Path Rel File
f)
    goDir :: Path Rel Dir -> Bool
    goDir :: Path Rel Dir -> Bool
goDir Path Rel Dir
f
      | Path Rel Dir -> Path Rel Dir
forall b t. Path b t -> Path b Dir
parent Path Rel Dir
f Path Rel Dir -> Path Rel Dir -> Bool
forall a. Eq a => a -> a -> Bool
== Path Rel Dir
f = Bool
False
      | Bool
otherwise = Path Rel Dir -> Path Rel Dir -> Bool
forall b t. Path b Dir -> Path b t -> Bool
isHiddenIn (Path Rel Dir -> Path Rel Dir
forall b t. Path b t -> Path b Dir
parent Path Rel Dir
f) Path Rel Dir
f Bool -> Bool -> Bool
|| Path Rel Dir -> Bool
goDir (Path Rel Dir -> Path Rel Dir
forall b t. Path b t -> Path b Dir
parent Path Rel Dir
f)

isHiddenIn :: Path b Dir -> Path b t -> Bool
isHiddenIn :: forall b t. Path b Dir -> Path b t -> Bool
isHiddenIn Path b Dir
curdir Path b t
ad =
  case Path b Dir -> Path b t -> Maybe (Path Rel t)
forall (m :: * -> *) b t.
MonadThrow m =>
Path b Dir -> Path b t -> m (Path Rel t)
stripProperPrefix Path b Dir
curdir Path b t
ad of
    Maybe (Path Rel t)
Nothing -> Bool
False
    Just Path Rel t
rp -> [Char]
"." [Char] -> [Char] -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` Path Rel t -> [Char]
forall b t. Path b t -> [Char]
toFilePath Path Rel t
rp