module Language.PureScript.Docs.Tags
  ( tags
  , dumpCtags
  , dumpEtags
  ) where

import Prelude

import Control.Arrow (first)
import Data.List (sort)
import Data.Maybe (mapMaybe)
import Data.Text qualified as T
import Language.PureScript.AST (SourceSpan, sourcePosLine, spanStart)
import Language.PureScript.Docs.Types (ChildDeclaration(..), Declaration(..), Module(..))

tags :: Module -> [(String, Int)]
tags = map (first T.unpack) . concatMap dtags . modDeclarations
  where
    dtags :: Declaration -> [(T.Text, Int)]
    dtags decl = case declSourceSpan decl of
      Just ss -> (declTitle decl, pos ss):mapMaybe subtag (declChildren decl)
      Nothing -> mapMaybe subtag $ declChildren decl

    subtag :: ChildDeclaration -> Maybe (T.Text, Int)
    subtag cdecl = case cdeclSourceSpan cdecl of
      Just ss -> Just (cdeclTitle cdecl, pos ss)
      Nothing -> Nothing

    pos :: SourceSpan -> Int
    pos = sourcePosLine . spanStart

-- etags files appear to be sorted on module file name:
-- from emacs source, `emacs/lib-src/etags.c`:
-- "In etags mode, sort by file name."
dumpEtags :: [(String, Module)] -> [String]
dumpEtags = concatMap renderModEtags . sort

renderModEtags :: (String, Module) -> [String]
renderModEtags (path, mdl) = ["\x0c", path ++ "," ++ show tagsLen] ++ tagLines
  where tagsLen = sum $ map length tagLines
        tagLines = map tagLine $ tags mdl
        tagLine (name, line) = "\x7f" ++ name ++ "\x01" ++ show line ++ ","

-- ctags files are required to be sorted: http://ctags.sourceforge.net/FORMAT
-- "The tags file is sorted on {tagname}.  This allows for a binary search in
--  the file."
dumpCtags :: [(String, Module)] -> [String]
dumpCtags = sort . concatMap renderModCtags

renderModCtags :: (String, Module) -> [String]
renderModCtags (path, mdl) = sort tagLines
  where tagLines = map tagLine $ tags mdl
        tagLine (name, line) = name ++ "\t" ++ path ++ "\t" ++ show line
