{-# LANGUAGE CPP                #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric      #-}
{-# LANGUAGE FlexibleContexts   #-}
{-# LANGUAGE LambdaCase         #-}
{-# LANGUAGE OverloadedStrings  #-}
{-# LANGUAGE TemplateHaskell    #-}
{-# OPTIONS_HADDOCK show-extensions #-}

-- |
-- Module      :  Yi.Keymap.Vim.Tag
-- License     :  GPL-2
-- Maintainer  :  yi-devel@googlegroups.com
-- Stability   :  experimental
-- Portability :  portable

module Yi.Keymap.Vim.Tag
    ( completeVimTag
    , gotoTag
    , nextTag
    , popTag
    , unpopTag
    ) where

import           GHC.Generics (Generic)

import           Lens.Micro.Platform        (view)
import           Control.Monad       (foldM, void)
import           Data.Binary         (Binary (..))
import           Data.Default        (Default (..))
import           Data.Maybe          (maybeToList)
import           Data.Monoid         ((<>))
import qualified Data.Text           as T (Text)
import           Data.Typeable       (Typeable)
import           System.Directory    (doesFileExist)
import           System.FilePath     (takeDirectory, (</>))
import           System.FriendlyPath (userToCanonPath)
import           Yi.Buffer
import           Yi.Core             (errorEditor)
import           Yi.Editor
import           Yi.File             (openingNewFile)
import           Yi.Keymap           (YiM)
import           Yi.Tag
import           Yi.Types            (YiVariable)
import           Yi.Utils            (io)

-- | List of tags and the file/line/char that they originate from.
-- (the location that :tag or Ctrl-[ was called from).
data VimTagStack = VimTagStack
    { VimTagStack -> [(Tag, Int, [Char], Int, Int)]
tagStackList :: [(Tag, Int, FilePath, Int, Int)]
    , VimTagStack -> Int
tagStackIndex :: Int
    } deriving (Typeable, (forall x. VimTagStack -> Rep VimTagStack x)
-> (forall x. Rep VimTagStack x -> VimTagStack)
-> Generic VimTagStack
forall x. Rep VimTagStack x -> VimTagStack
forall x. VimTagStack -> Rep VimTagStack x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. VimTagStack -> Rep VimTagStack x
from :: forall x. VimTagStack -> Rep VimTagStack x
$cto :: forall x. Rep VimTagStack x -> VimTagStack
to :: forall x. Rep VimTagStack x -> VimTagStack
Generic)

instance Default VimTagStack where
    def :: VimTagStack
def = [(Tag, Int, [Char], Int, Int)] -> Int -> VimTagStack
VimTagStack [] Int
0

instance YiVariable VimTagStack

instance Binary VimTagStack

-- | Returns tag, tag index, filepath, line number, char number
getTagList :: EditorM [(Tag, Int, FilePath, Int, Int)]
getTagList :: EditorM [(Tag, Int, [Char], Int, Int)]
getTagList = do
    VimTagStack [(Tag, Int, [Char], Int, Int)]
ts Int
_ <- EditorM VimTagStack
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Default a, Functor m) =>
m a
getEditorDyn
    [(Tag, Int, [Char], Int, Int)]
-> EditorM [(Tag, Int, [Char], Int, Int)]
forall a. a -> EditorM a
forall (m :: * -> *) a. Monad m => a -> m a
return [(Tag, Int, [Char], Int, Int)]
ts

getTagIndex :: EditorM Int
getTagIndex :: EditorM Int
getTagIndex = do
    VimTagStack [(Tag, Int, [Char], Int, Int)]
_ Int
ti <- EditorM VimTagStack
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Default a, Functor m) =>
m a
getEditorDyn
    Int -> EditorM Int
forall a. a -> EditorM a
forall (m :: * -> *) a. Monad m => a -> m a
return Int
ti

setTagList :: [(Tag, Int, FilePath, Int, Int)] -> EditorM ()
setTagList :: [(Tag, Int, [Char], Int, Int)] -> EditorM ()
setTagList [(Tag, Int, [Char], Int, Int)]
tl =  do
    t :: VimTagStack
t@(VimTagStack [(Tag, Int, [Char], Int, Int)]
_ Int
_) <- EditorM VimTagStack
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Default a, Functor m) =>
m a
getEditorDyn
    VimTagStack -> EditorM ()
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Functor m) =>
a -> m ()
putEditorDyn (VimTagStack -> EditorM ()) -> VimTagStack -> EditorM ()
forall a b. (a -> b) -> a -> b
$ VimTagStack
t { tagStackList = tl }

setTagIndex :: Int -> EditorM ()
setTagIndex :: Int -> EditorM ()
setTagIndex Int
ti = do
    t :: VimTagStack
t@(VimTagStack [(Tag, Int, [Char], Int, Int)]
_ Int
_) <- EditorM VimTagStack
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Default a, Functor m) =>
m a
getEditorDyn
    VimTagStack -> EditorM ()
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Functor m) =>
a -> m ()
putEditorDyn (VimTagStack -> EditorM ()) -> VimTagStack -> EditorM ()
forall a b. (a -> b) -> a -> b
$ VimTagStack
t { tagStackIndex = ti }

-- | Push tag at index.
pushTagStack :: Tag -> Int -> FilePath -> Int -> Int -> EditorM ()
pushTagStack :: Tag -> Int -> [Char] -> Int -> Int -> EditorM ()
pushTagStack Tag
tag Int
ind [Char]
fp Int
ln Int
cn = do
    [(Tag, Int, [Char], Int, Int)]
tl <- EditorM [(Tag, Int, [Char], Int, Int)]
getTagList
    Int
ti <- EditorM Int
getTagIndex
    [(Tag, Int, [Char], Int, Int)] -> EditorM ()
setTagList ([(Tag, Int, [Char], Int, Int)] -> EditorM ())
-> [(Tag, Int, [Char], Int, Int)] -> EditorM ()
forall a b. (a -> b) -> a -> b
$ (Int
-> [(Tag, Int, [Char], Int, Int)] -> [(Tag, Int, [Char], Int, Int)]
forall a. Int -> [a] -> [a]
take Int
ti [(Tag, Int, [Char], Int, Int)]
tl) [(Tag, Int, [Char], Int, Int)]
-> [(Tag, Int, [Char], Int, Int)] -> [(Tag, Int, [Char], Int, Int)]
forall a. [a] -> [a] -> [a]
++ [(Tag
tag, Int
ind, [Char]
fp, Int
ln, Int
cn)]
    Int -> EditorM ()
setTagIndex (Int -> EditorM ()) -> Int -> EditorM ()
forall a b. (a -> b) -> a -> b
$ Int
ti Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1

-- | Get tag and decrement index (so that when a new push is done, the current
-- tag is popped)
popTagStack :: EditorM (Maybe (Tag, Int, FilePath, Int, Int))
popTagStack :: EditorM (Maybe (Tag, Int, [Char], Int, Int))
popTagStack = do
    [(Tag, Int, [Char], Int, Int)]
tl <- EditorM [(Tag, Int, [Char], Int, Int)]
getTagList
    Int
ti <- EditorM Int
getTagIndex
    case [(Tag, Int, [Char], Int, Int)]
tl of
        [] -> Maybe (Tag, Int, [Char], Int, Int)
-> EditorM (Maybe (Tag, Int, [Char], Int, Int))
forall a. a -> EditorM a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (Tag, Int, [Char], Int, Int)
forall a. Maybe a
Nothing
        [(Tag, Int, [Char], Int, Int)]
_  -> case Int
ti of
                Int
0 -> Maybe (Tag, Int, [Char], Int, Int)
-> EditorM (Maybe (Tag, Int, [Char], Int, Int))
forall a. a -> EditorM a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (Tag, Int, [Char], Int, Int)
forall a. Maybe a
Nothing
                Int
_ -> Int -> EditorM ()
setTagIndex (Int
ti Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) EditorM ()
-> EditorM (Maybe (Tag, Int, [Char], Int, Int))
-> EditorM (Maybe (Tag, Int, [Char], Int, Int))
forall a b. EditorM a -> EditorM b -> EditorM b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Maybe (Tag, Int, [Char], Int, Int)
-> EditorM (Maybe (Tag, Int, [Char], Int, Int))
forall a. a -> EditorM a
forall (m :: * -> *) a. Monad m => a -> m a
return ((Tag, Int, [Char], Int, Int) -> Maybe (Tag, Int, [Char], Int, Int)
forall a. a -> Maybe a
Just ((Tag, Int, [Char], Int, Int)
 -> Maybe (Tag, Int, [Char], Int, Int))
-> (Tag, Int, [Char], Int, Int)
-> Maybe (Tag, Int, [Char], Int, Int)
forall a b. (a -> b) -> a -> b
$ [(Tag, Int, [Char], Int, Int)]
tl [(Tag, Int, [Char], Int, Int)]
-> Int -> (Tag, Int, [Char], Int, Int)
forall a. HasCallStack => [a] -> Int -> a
!! (Int
ti Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1))

-- | Opens the file that contains @tag@. Uses the global tag table or uses
-- the first valid tag file in @TagsFileList@.
gotoTag :: Tag -> Int -> Maybe (FilePath, Int, Int) -> YiM ()
gotoTag :: Tag -> Int -> Maybe ([Char], Int, Int) -> YiM ()
gotoTag Tag
tag Int
ind Maybe ([Char], Int, Int)
ret =
    YiM (Maybe ()) -> YiM ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (YiM (Maybe ()) -> YiM ())
-> ((TagTable -> YiM ()) -> YiM (Maybe ()))
-> (TagTable -> YiM ())
-> YiM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (TagTable -> YiM ()) -> YiM (Maybe ())
forall a. (TagTable -> YiM a) -> YiM (Maybe a)
visitTagTable ((TagTable -> YiM ()) -> YiM ()) -> (TagTable -> YiM ()) -> YiM ()
forall a b. (a -> b) -> a -> b
$ \TagTable
tagTable -> do
        let lis :: [([Char], Int)]
lis = Tag -> TagTable -> [([Char], Int)]
lookupTag Tag
tag TagTable
tagTable
        if ([([Char], Int)] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [([Char], Int)]
lis) Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
ind
          then Text -> YiM ()
errorEditor (Text -> YiM ()) -> Text -> YiM ()
forall a b. (a -> b) -> a -> b
$ Text
"tag not found: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Tag -> Text
_unTag Tag
tag
          else do
            BufferFileInfo
bufinf <- BufferM BufferFileInfo -> YiM BufferFileInfo
forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
withCurrentBuffer BufferM BufferFileInfo
bufInfoB

            let ([Char]
filename, Int
line) = [([Char], Int)]
lis [([Char], Int)] -> Int -> ([Char], Int)
forall a. HasCallStack => [a] -> Int -> a
!! Int
ind
                ([Char]
fn, Int
ln, Int
cn) = case Maybe ([Char], Int, Int)
ret of
                   Just ([Char], Int, Int)
ret' -> ([Char], Int, Int)
ret'
                   Maybe ([Char], Int, Int)
Nothing -> (BufferFileInfo -> [Char]
bufInfoFileName BufferFileInfo
bufinf, 
                               BufferFileInfo -> Int
bufInfoLineNo BufferFileInfo
bufinf, 
                               BufferFileInfo -> Int
bufInfoColNo BufferFileInfo
bufinf)
            EditorM () -> YiM ()
forall a. EditorM a -> YiM a
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor (EditorM () -> YiM ()) -> EditorM () -> YiM ()
forall a b. (a -> b) -> a -> b
$ Tag -> Int -> [Char] -> Int -> Int -> EditorM ()
pushTagStack Tag
tag Int
ind [Char]
fn Int
ln Int
cn
            [Char] -> BufferM Int -> YiM ()
forall a. [Char] -> BufferM a -> YiM ()
openingNewFile [Char]
filename (BufferM Int -> YiM ()) -> BufferM Int -> YiM ()
forall a b. (a -> b) -> a -> b
$ Int -> BufferM Int
gotoLn Int
line

-- | Goes to the next tag. (:tnext)
nextTag :: YiM ()
nextTag :: YiM ()
nextTag = do
    Maybe (Tag, Int, [Char], Int, Int)
prev <- EditorM (Maybe (Tag, Int, [Char], Int, Int))
-> YiM (Maybe (Tag, Int, [Char], Int, Int))
forall a. EditorM a -> YiM a
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor EditorM (Maybe (Tag, Int, [Char], Int, Int))
popTagStack 
    case Maybe (Tag, Int, [Char], Int, Int)
prev of
        Maybe (Tag, Int, [Char], Int, Int)
Nothing -> Text -> YiM ()
errorEditor (Text -> YiM ()) -> Text -> YiM ()
forall a b. (a -> b) -> a -> b
$ Text
"tag stack empty"
        Just (Tag
tag, Int
ind, [Char]
fn, Int
ln, Int
cn) -> Tag -> Int -> Maybe ([Char], Int, Int) -> YiM ()
gotoTag Tag
tag (Int
ind Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (([Char], Int, Int) -> Maybe ([Char], Int, Int)
forall a. a -> Maybe a
Just ([Char]
fn, Int
ln, Int
cn))

-- | Return to location from before last tag jump.
popTag :: YiM ()
popTag :: YiM ()
popTag = do
    [(Tag, Int, [Char], Int, Int)]
tl <- EditorM [(Tag, Int, [Char], Int, Int)]
-> YiM [(Tag, Int, [Char], Int, Int)]
forall a. EditorM a -> YiM a
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor EditorM [(Tag, Int, [Char], Int, Int)]
getTagList
    case [(Tag, Int, [Char], Int, Int)]
tl of
        [] -> Text -> YiM ()
errorEditor Text
"tag stack empty"
        [(Tag, Int, [Char], Int, Int)]
_ -> do
            Maybe (Tag, Int, [Char], Int, Int)
posloc <- EditorM (Maybe (Tag, Int, [Char], Int, Int))
-> YiM (Maybe (Tag, Int, [Char], Int, Int))
forall a. EditorM a -> YiM a
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor EditorM (Maybe (Tag, Int, [Char], Int, Int))
popTagStack
            case Maybe (Tag, Int, [Char], Int, Int)
posloc of
                Maybe (Tag, Int, [Char], Int, Int)
Nothing -> Text -> YiM ()
errorEditor Text
"at bottom of tag stack"
                Just (Tag
_, Int
_, [Char]
fn, Int
ln, Int
cn) -> [Char] -> BufferM () -> YiM ()
forall a. [Char] -> BufferM a -> YiM ()
openingNewFile [Char]
fn (BufferM () -> YiM ()) -> BufferM () -> YiM ()
forall a b. (a -> b) -> a -> b
$ Int -> Int -> BufferM ()
moveToLineColB Int
ln Int
cn

-- | Go to next tag in the tag stack. Represents :tag without any
-- specified tag.
unpopTag :: YiM ()
unpopTag :: YiM ()
unpopTag = do
  [(Tag, Int, [Char], Int, Int)]
tl <- EditorM [(Tag, Int, [Char], Int, Int)]
-> YiM [(Tag, Int, [Char], Int, Int)]
forall a. EditorM a -> YiM a
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor EditorM [(Tag, Int, [Char], Int, Int)]
getTagList
  Int
ti <- EditorM Int -> YiM Int
forall a. EditorM a -> YiM a
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor EditorM Int
getTagIndex
  if Int
ti Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= [(Tag, Int, [Char], Int, Int)] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(Tag, Int, [Char], Int, Int)]
tl
    then case [(Tag, Int, [Char], Int, Int)]
tl of
            [] -> Text -> YiM ()
errorEditor Text
"tag stack empty"
            [(Tag, Int, [Char], Int, Int)]
_ -> Text -> YiM ()
errorEditor Text
"at top of tag stack"
    else let (Tag
tag, Int
ind, [Char]
_, Int
_, Int
_) = [(Tag, Int, [Char], Int, Int)]
tl [(Tag, Int, [Char], Int, Int)]
-> Int -> (Tag, Int, [Char], Int, Int)
forall a. HasCallStack => [a] -> Int -> a
!! Int
ti
         in YiM (Maybe ()) -> YiM ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (YiM (Maybe ()) -> YiM ())
-> ((TagTable -> YiM ()) -> YiM (Maybe ()))
-> (TagTable -> YiM ())
-> YiM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (TagTable -> YiM ()) -> YiM (Maybe ())
forall a. (TagTable -> YiM a) -> YiM (Maybe a)
visitTagTable ((TagTable -> YiM ()) -> YiM ()) -> (TagTable -> YiM ()) -> YiM ()
forall a b. (a -> b) -> a -> b
$ \TagTable
tagTable -> do
             let lis :: [([Char], Int)]
lis =  Tag -> TagTable -> [([Char], Int)]
lookupTag Tag
tag TagTable
tagTable
             if ([([Char], Int)] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [([Char], Int)]
lis) Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
ind
               then Text -> YiM ()
errorEditor (Text -> YiM ()) -> Text -> YiM ()
forall a b. (a -> b) -> a -> b
$ Text
"tag not found: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Tag -> Text
_unTag Tag
tag
               else do
                   BufferFileInfo
bufinf <- BufferM BufferFileInfo -> YiM BufferFileInfo
forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
withCurrentBuffer BufferM BufferFileInfo
bufInfoB
                   let ([Char]
filename, Int
line) = [([Char], Int)]
lis [([Char], Int)] -> Int -> ([Char], Int)
forall a. HasCallStack => [a] -> Int -> a
!! Int
ind
                       ln :: Int
ln = BufferFileInfo -> Int
bufInfoLineNo BufferFileInfo
bufinf
                       cn :: Int
cn = BufferFileInfo -> Int
bufInfoColNo BufferFileInfo
bufinf
                       fn :: [Char]
fn = BufferFileInfo -> [Char]
bufInfoFileName BufferFileInfo
bufinf
                       tl' :: [(Tag, Int, [Char], Int, Int)]
tl' = Int
-> [(Tag, Int, [Char], Int, Int)] -> [(Tag, Int, [Char], Int, Int)]
forall a. Int -> [a] -> [a]
take Int
ti [(Tag, Int, [Char], Int, Int)]
tl
                               [(Tag, Int, [Char], Int, Int)]
-> [(Tag, Int, [Char], Int, Int)] -> [(Tag, Int, [Char], Int, Int)]
forall a. [a] -> [a] -> [a]
++ (Tag
tag, Int
ind, [Char]
fn, Int
ln, Int
cn)(Tag, Int, [Char], Int, Int)
-> [(Tag, Int, [Char], Int, Int)] -> [(Tag, Int, [Char], Int, Int)]
forall a. a -> [a] -> [a]
:(Int
-> [(Tag, Int, [Char], Int, Int)] -> [(Tag, Int, [Char], Int, Int)]
forall a. Int -> [a] -> [a]
drop (Int
ti Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) [(Tag, Int, [Char], Int, Int)]
tl)
                   EditorM () -> YiM ()
forall a. EditorM a -> YiM a
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor (EditorM () -> YiM ()) -> EditorM () -> YiM ()
forall a b. (a -> b) -> a -> b
$ [(Tag, Int, [Char], Int, Int)] -> EditorM ()
setTagList [(Tag, Int, [Char], Int, Int)]
tl'
                   [Char] -> BufferM Int -> YiM ()
forall a. [Char] -> BufferM a -> YiM ()
openingNewFile [Char]
filename (BufferM Int -> YiM ()) -> BufferM Int -> YiM ()
forall a b. (a -> b) -> a -> b
$ Int -> BufferM Int
gotoLn Int
line

completeVimTag :: T.Text -> YiM [T.Text]
completeVimTag :: Text -> YiM [Text]
completeVimTag Text
s =
  (Maybe Text -> [Text]) -> YiM (Maybe Text) -> YiM [Text]
forall a b. (a -> b) -> YiM a -> YiM b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Maybe Text -> [Text]
forall a. Maybe a -> [a]
maybeToList (YiM (Maybe Text) -> YiM [Text])
-> ((TagTable -> YiM Text) -> YiM (Maybe Text))
-> (TagTable -> YiM Text)
-> YiM [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (TagTable -> YiM Text) -> YiM (Maybe Text)
forall a. (TagTable -> YiM a) -> YiM (Maybe a)
visitTagTable ((TagTable -> YiM Text) -> YiM [Text])
-> (TagTable -> YiM Text) -> YiM [Text]
forall a b. (a -> b) -> a -> b
$ Text -> YiM Text
forall a. a -> YiM a
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> YiM Text) -> (TagTable -> Text) -> TagTable -> YiM Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (TagTable -> Text -> Text) -> Text -> TagTable -> Text
forall a b c. (a -> b -> c) -> b -> a -> c
flip TagTable -> Text -> Text
completeTag Text
s

-- | Gets the first valid tags file in @TagsFileList@, if such a valid
-- file exists.
tagsFile :: YiM (Maybe FilePath)
tagsFile :: YiM (Maybe [Char])
tagsFile = do
    [[Char]]
fs <- Getting [[Char]] Config [[Char]] -> Config -> [[Char]]
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting [[Char]] Config [[Char]]
Field [[Char]]
tagsFileList (Config -> [[Char]]) -> YiM Config -> YiM [[Char]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> YiM Config
forall (m :: * -> *). MonadEditor m => m Config
askCfg
    let g :: Maybe [Char] -> [Char] -> YiM (Maybe [Char])
g Maybe [Char]
f' [Char]
f = case Maybe [Char]
f' of
            Just [Char]
_ -> Maybe [Char] -> YiM (Maybe [Char])
forall a. a -> YiM a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe [Char]
f'
            Maybe [Char]
Nothing -> [Char] -> YiM (Maybe [Char])
tagsFileLocation [Char]
f
    (Maybe [Char] -> [Char] -> YiM (Maybe [Char]))
-> Maybe [Char] -> [[Char]] -> YiM (Maybe [Char])
forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM Maybe [Char] -> [Char] -> YiM (Maybe [Char])
g Maybe [Char]
forall a. Maybe a
Nothing [[Char]]
fs

-- | Handles paths of the form ./[path], which represents a tags file relative
-- to the path of the current directory of a file rather than the directory
-- of the process.
tagsFileLocation :: String -> YiM (Maybe FilePath)
tagsFileLocation :: [Char] -> YiM (Maybe [Char])
tagsFileLocation [Char]
s
    | [Char] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Char]
s Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
2 Bool -> Bool -> Bool
|| Int -> [Char] -> [Char]
forall a. Int -> [a] -> [a]
take Int
2 [Char]
s [Char] -> [Char] -> Bool
forall a. Eq a => a -> a -> Bool
/= [Char]
"./" = [Char] -> YiM (Maybe [Char])
forall {m :: * -> *}. MonadBase IO m => [Char] -> m (Maybe [Char])
check [Char]
s
    | Bool
otherwise = do
       let s' :: [Char]
s' = Int -> [Char] -> [Char]
forall a. Int -> [a] -> [a]
drop Int
2 [Char]
s
       [Char]
dir <- [Char] -> [Char]
takeDirectory ([Char] -> [Char]) -> YiM [Char] -> YiM [Char]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>
                 (BufferM [Char] -> YiM [Char]
forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
withCurrentBuffer (BufferM [Char] -> YiM [Char]) -> BufferM [Char] -> YiM [Char]
forall a b. (a -> b) -> a -> b
$ BufferM BufferFileInfo
bufInfoB BufferM BufferFileInfo
-> (BufferFileInfo -> BufferM [Char]) -> BufferM [Char]
forall a b. BufferM a -> (a -> BufferM b) -> BufferM b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= [Char] -> BufferM [Char]
forall a. a -> BufferM a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Char] -> BufferM [Char])
-> (BufferFileInfo -> [Char]) -> BufferFileInfo -> BufferM [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BufferFileInfo -> [Char]
bufInfoFileName)
       [Char] -> YiM (Maybe [Char])
forall {m :: * -> *}. MonadBase IO m => [Char] -> m (Maybe [Char])
check ([Char] -> YiM (Maybe [Char])) -> [Char] -> YiM (Maybe [Char])
forall a b. (a -> b) -> a -> b
$ [Char]
dir [Char] -> [Char] -> [Char]
</> [Char]
s'
    where check :: [Char] -> m (Maybe [Char])
check [Char]
f = do
            [Char]
f' <- IO [Char] -> m [Char]
forall (m :: * -> *) a. MonadBase IO m => IO a -> m a
io (IO [Char] -> m [Char]) -> IO [Char] -> m [Char]
forall a b. (a -> b) -> a -> b
$ [Char] -> IO [Char]
userToCanonPath [Char]
f
            Bool
fileExists <- IO Bool -> m Bool
forall (m :: * -> *) a. MonadBase IO m => IO a -> m a
io (IO Bool -> m Bool) -> IO Bool -> m Bool
forall a b. (a -> b) -> a -> b
$ [Char] -> IO Bool
doesFileExist [Char]
f'
            if Bool
fileExists
                then Maybe [Char] -> m (Maybe [Char])
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe [Char] -> m (Maybe [Char]))
-> Maybe [Char] -> m (Maybe [Char])
forall a b. (a -> b) -> a -> b
$ [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
f'
                else Maybe [Char] -> m (Maybe [Char])
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe [Char]
forall a. Maybe a
Nothing

-- | Call continuation @act@ with the TagTable. Uses the global table
-- or, if it doesn't exist, uses the first valid tag file in
-- @TagsFileList@.
visitTagTable :: (TagTable -> YiM a) -> YiM (Maybe a)
visitTagTable :: forall a. (TagTable -> YiM a) -> YiM (Maybe a)
visitTagTable TagTable -> YiM a
act = do
    Maybe TagTable
posTagTable <- EditorM (Maybe TagTable) -> YiM (Maybe TagTable)
forall a. EditorM a -> YiM a
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor EditorM (Maybe TagTable)
getTags
    case Maybe TagTable
posTagTable of
        Just TagTable
tagTable -> a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> YiM a -> YiM (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TagTable -> YiM a
act TagTable
tagTable
        Maybe TagTable
Nothing -> do
            Maybe [Char]
f <- YiM (Maybe [Char])
tagsFile
            case Maybe [Char]
f of
                Maybe [Char]
Nothing -> Text -> YiM ()
errorEditor Text
"No tags file" YiM () -> YiM (Maybe a) -> YiM (Maybe a)
forall a b. YiM a -> YiM b -> YiM b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Maybe a -> YiM (Maybe a)
forall a. a -> YiM a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
forall a. Maybe a
Nothing
                Just [Char]
f' -> do
                    TagTable
tagTable <- IO TagTable -> YiM TagTable
forall (m :: * -> *) a. MonadBase IO m => IO a -> m a
io (IO TagTable -> YiM TagTable) -> IO TagTable -> YiM TagTable
forall a b. (a -> b) -> a -> b
$ [Char] -> IO TagTable
importTagTable [Char]
f'
                    EditorM () -> YiM ()
forall a. EditorM a -> YiM a
forall (m :: * -> *) a. MonadEditor m => EditorM a -> m a
withEditor (EditorM () -> YiM ()) -> EditorM () -> YiM ()
forall a b. (a -> b) -> a -> b
$ TagTable -> EditorM ()
setTags TagTable
tagTable
                    a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> YiM a -> YiM (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TagTable -> YiM a
act TagTable
tagTable