    {-|

    The @accounts@ command lists account names:

    - in flat mode (default), it lists the full names of accounts posted to by matched postings,
      clipped to the specified depth, possibly with leading components dropped.

    - in tree mode, it shows the indented short names of accounts posted to by matched postings,
      and their parents, to the specified depth.

    -}

    {-# LANGUAGE MultiWayIf #-}
    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE ScopedTypeVariables #-}
    {-# LANGUAGE TemplateHaskell #-}

    module Hledger.Cli.Commands.Accounts (
      accountsmode
     ,accounts
    ) where

    import Control.Monad (forM_)
    import Data.List
    import Data.Maybe (fromMaybe)
    import Data.Text qualified as T
    import Data.Text.IO qualified as T
    import Safe (headDef)
    import System.Console.CmdArgs.Explicit as C

    import Hledger
    import Hledger.Cli.CliOptions

    -- | Command line options for this command.
    accountsmode :: Mode RawOpts
    accountsmode = CommandHelpStr
    -> [Flag RawOpts]
    -> [(CommandHelpStr, [Flag RawOpts])]
    -> [Flag RawOpts]
    -> ([Arg RawOpts], Maybe (Arg RawOpts))
    -> Mode RawOpts
    hledgerCommandMode
      $(embedFileRelative "Hledger/Cli/Commands/Accounts.txt")
      (
      [[CommandHelpStr]
    -> (RawOpts -> RawOpts) -> CommandHelpStr -> Flag RawOpts
    forall a. [CommandHelpStr] -> (a -> a) -> CommandHelpStr -> Flag a
    flagNone [CommandHelpStr
    "used",CommandHelpStr
    "u"]     (CommandHelpStr -> RawOpts -> RawOpts
    setboolopt CommandHelpStr
    "used")       CommandHelpStr
    "list accounts used"
      ,[CommandHelpStr]
    -> (RawOpts -> RawOpts) -> CommandHelpStr -> Flag RawOpts
    forall a. [CommandHelpStr] -> (a -> a) -> CommandHelpStr -> Flag a
    flagNone [CommandHelpStr
    "declared",CommandHelpStr
    "d"] (CommandHelpStr -> RawOpts -> RawOpts
    setboolopt CommandHelpStr
    "declared")   CommandHelpStr
    "list accounts declared"
      ,[CommandHelpStr]
    -> (RawOpts -> RawOpts) -> CommandHelpStr -> Flag RawOpts
    forall a. [CommandHelpStr] -> (a -> a) -> CommandHelpStr -> Flag a
    flagNone [CommandHelpStr
    "undeclared"]   (CommandHelpStr -> RawOpts -> RawOpts
    setboolopt CommandHelpStr
    "undeclared") CommandHelpStr
    "list accounts used but not declared"
      ,[CommandHelpStr]
    -> (RawOpts -> RawOpts) -> CommandHelpStr -> Flag RawOpts
    forall a. [CommandHelpStr] -> (a -> a) -> CommandHelpStr -> Flag a
    flagNone [CommandHelpStr
    "unused"]       (CommandHelpStr -> RawOpts -> RawOpts
    setboolopt CommandHelpStr
    "unused")     CommandHelpStr
    "list accounts declared but not used"
      ,[CommandHelpStr]
    -> (RawOpts -> RawOpts) -> CommandHelpStr -> Flag RawOpts
    forall a. [CommandHelpStr] -> (a -> a) -> CommandHelpStr -> Flag a
    flagNone [CommandHelpStr
    "find"]         (CommandHelpStr -> RawOpts -> RawOpts
    setboolopt CommandHelpStr
    "find")       CommandHelpStr
    "list the first account matched by the first argument (a case-insensitive infix regexp)"

      ,[CommandHelpStr]
    -> (RawOpts -> RawOpts) -> CommandHelpStr -> Flag RawOpts
    forall a. [CommandHelpStr] -> (a -> a) -> CommandHelpStr -> Flag a
    flagNone [CommandHelpStr
    "types"]        (CommandHelpStr -> RawOpts -> RawOpts
    setboolopt CommandHelpStr
    "types")      CommandHelpStr
    "also show account types when known"
      ,[CommandHelpStr]
    -> (RawOpts -> RawOpts) -> CommandHelpStr -> Flag RawOpts
    forall a. [CommandHelpStr] -> (a -> a) -> CommandHelpStr -> Flag a
    flagNone [CommandHelpStr
    "positions"]    (CommandHelpStr -> RawOpts -> RawOpts
    setboolopt CommandHelpStr
    "positions")  CommandHelpStr
    "also show where accounts were declared"
      ,[CommandHelpStr]
    -> (RawOpts -> RawOpts) -> CommandHelpStr -> Flag RawOpts
    forall a. [CommandHelpStr] -> (a -> a) -> CommandHelpStr -> Flag a
    flagNone [CommandHelpStr
    "directives"]   (CommandHelpStr -> RawOpts -> RawOpts
    setboolopt CommandHelpStr
    "directives") CommandHelpStr
    "show as account directives, for use in journals"
      ]
      [Flag RawOpts] -> [Flag RawOpts] -> [Flag RawOpts]
    forall a. [a] -> [a] -> [a]
    ++ Bool -> [Flag RawOpts]
    flattreeflags Bool
    False [Flag RawOpts] -> [Flag RawOpts] -> [Flag RawOpts]
    forall a. [a] -> [a] -> [a]
    ++
      [[CommandHelpStr]
    -> Update RawOpts
    -> CommandHelpStr
    -> CommandHelpStr
    -> Flag RawOpts
    forall a.
    [CommandHelpStr]
    -> Update a -> CommandHelpStr -> CommandHelpStr -> Flag a
    flagReq  [CommandHelpStr
    "drop"] (\CommandHelpStr
    s RawOpts
    opts -> RawOpts -> Either CommandHelpStr RawOpts
    forall a b. b -> Either a b
    Right (RawOpts -> Either CommandHelpStr RawOpts)
    -> RawOpts -> Either CommandHelpStr RawOpts
    forall a b. (a -> b) -> a -> b
    $ CommandHelpStr -> CommandHelpStr -> RawOpts -> RawOpts
    setopt CommandHelpStr
    "drop" CommandHelpStr
    s RawOpts
    opts) CommandHelpStr
    "N" CommandHelpStr
    "flat mode: omit N leading account name parts"]
      )
      [(CommandHelpStr, [Flag RawOpts])]
    cligeneralflagsgroups1
      [Flag RawOpts]
    hiddenflags
      ([], Arg RawOpts -> Maybe (Arg RawOpts)
    forall a. a -> Maybe a
    Just (Arg RawOpts -> Maybe (Arg RawOpts))
    -> Arg RawOpts -> Maybe (Arg RawOpts)
    forall a b. (a -> b) -> a -> b
    $ CommandHelpStr -> Arg RawOpts
    argsFlag CommandHelpStr
    "[QUERY..]")

    -- | The accounts command.
    accounts :: CliOpts -> Journal -> IO ()
    accounts :: CliOpts -> Journal -> IO ()
    accounts opts :: CliOpts
    opts@CliOpts{rawopts_ :: CliOpts -> RawOpts
    rawopts_=RawOpts
    rawopts, reportspec_ :: CliOpts -> ReportSpec
    reportspec_=ReportSpec{_rsQuery :: ReportSpec -> Query
    _rsQuery=Query
    query,_rsReportOpts :: ReportSpec -> ReportOpts
    _rsReportOpts=ReportOpts
    ropts}} Journal
    j = do

      -- 1. identify the accounts we'll show
      let tree :: Bool
    tree     = ReportOpts -> Bool
    tree_ ReportOpts
    ropts
          types :: Bool
    types = CommandHelpStr -> RawOpts -> Bool
    boolopt CommandHelpStr
    "types"    RawOpts
    rawopts
          positions :: Bool
    positions = CommandHelpStr -> RawOpts -> Bool
    boolopt CommandHelpStr
    "positions" RawOpts
    rawopts
          directives :: Bool
    directives = CommandHelpStr -> RawOpts -> Bool
    boolopt CommandHelpStr
    "directives" RawOpts
    rawopts
          -- Modified queries. These may not work with boolean queries (#2371).
          -- a depth limit will clip and exclude account names later, but we don't want to exclude accounts at this stage
          nodepthq :: Query
    nodepthq = CommandHelpStr -> Query -> Query
    forall a. Show a => CommandHelpStr -> a -> a
    dbg4 CommandHelpStr
    "nodepthq" (Query -> Query) -> Query -> Query
    forall a b. (a -> b) -> a -> b
    $ (Query -> Bool) -> Query -> Query
    filterQuery (Bool -> Bool
    not (Bool -> Bool) -> (Query -> Bool) -> Query -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Query -> Bool
    queryIsDepth) Query
    query
          -- just the acct: part of the query will be reapplied later, after clipping
          acctq :: Query
    acctq = CommandHelpStr -> Query -> Query
    forall a. Show a => CommandHelpStr -> a -> a
    dbg4 CommandHelpStr
    "acctq" (Query -> Query) -> Query -> Query
    forall a b. (a -> b) -> a -> b
    $ (Query -> Bool) -> Query -> Query
    filterQuery Query -> Bool
    queryIsAcct Query
    query
          dep :: DepthSpec
    dep = CommandHelpStr -> DepthSpec -> DepthSpec
    forall a. Show a => CommandHelpStr -> a -> a
    dbg4 CommandHelpStr
    "depth" (DepthSpec -> DepthSpec) -> DepthSpec -> DepthSpec
    forall a b. (a -> b) -> a -> b
    $ Query -> DepthSpec
    queryDepth (Query -> DepthSpec) -> Query -> DepthSpec
    forall a b. (a -> b) -> a -> b
    $ (Query -> Bool) -> Query -> Query
    filterQuery Query -> Bool
    queryIsDepth Query
    query
          -- when finding accounts used by postings, we remove tags that were declared on the posting,
          -- so that a tag: query will match account tags and not posting tags.
          matchedused :: [Text]
    matchedused = CommandHelpStr -> [Text] -> [Text]
    forall a. Show a => CommandHelpStr -> a -> a
    dbg5 CommandHelpStr
    "matchedused" ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ [Text] -> [Text]
    forall a. Eq a => [a] -> [a]
    nub ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ (Posting -> Text) -> [Posting] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map Posting -> Text
    paccount ([Posting] -> [Text]) -> [Posting] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ Journal -> [Posting]
    journalPostings (Journal -> [Posting]) -> Journal -> [Posting]
    forall a b. (a -> b) -> a -> b
    $
            Query -> Journal -> Journal
    filterJournalPostings Query
    nodepthq (Journal -> Journal) -> Journal -> Journal
    forall a b. (a -> b) -> a -> b
    $ Journal -> Journal
    journalPostingsKeepAccountTagsOnly Journal
    j
          matcheddeclared :: [Text]
    matcheddeclared = CommandHelpStr -> [Text] -> [Text]
    forall a. Show a => CommandHelpStr -> a -> a
    dbg5 CommandHelpStr
    "matcheddeclared" ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $
            [Text] -> [Text]
    forall a. Eq a => [a] -> [a]
    nub ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $
            (Text -> Bool) -> [Text] -> [Text]
    forall a. (a -> Bool) -> [a] -> [a]
    filter ((Text -> Maybe AccountType)
    -> (Text -> [Tag]) -> Query -> Text -> Bool
    matchesAccountExtra (Journal -> Text -> Maybe AccountType
    journalAccountType Journal
    j) (Journal -> Text -> [Tag]
    journalInheritedAccountTags Journal
    j) Query
    nodepthq) ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $
            ((Text, AccountDeclarationInfo) -> Text)
    -> [(Text, AccountDeclarationInfo)] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map (Text, AccountDeclarationInfo) -> Text
    forall a b. (a, b) -> a
    fst ([(Text, AccountDeclarationInfo)] -> [Text])
    -> [(Text, AccountDeclarationInfo)] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ Journal -> [(Text, AccountDeclarationInfo)]
    jdeclaredaccounts Journal
    j
          matchedundeclared :: [Text]
    matchedundeclared = CommandHelpStr -> [Text] -> [Text]
    forall a. Show a => CommandHelpStr -> a -> a
    dbg5 CommandHelpStr
    "matchedundeclared" ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ [Text] -> [Text]
    forall a. Eq a => [a] -> [a]
    nub ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ [Text]
    matchedused [Text] -> [Text] -> [Text]
    forall a. Eq a => [a] -> [a] -> [a]
    \\ [Text]
    matcheddeclared
          matchedunused :: [Text]
    matchedunused = CommandHelpStr -> [Text] -> [Text]
    forall a. Show a => CommandHelpStr -> a -> a
    dbg5 CommandHelpStr
    "matchedunused" ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ [Text] -> [Text]
    forall a. Eq a => [a] -> [a]
    nub ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ [Text]
    matcheddeclared [Text] -> [Text] -> [Text]
    forall a. Eq a => [a] -> [a] -> [a]
    \\ [Text]
    matchedused
          -- keep synced with aregister
          matchedacct :: Text
    matchedacct = CommandHelpStr -> Text -> Text
    forall a. Show a => CommandHelpStr -> a -> a
    dbg5 CommandHelpStr
    "matchedacct" (Text -> Text) -> Text -> Text
    forall a b. (a -> b) -> a -> b
    $
            Text -> Maybe Text -> Text
    forall a. a -> Maybe a -> a
    fromMaybe (CommandHelpStr -> Text
    forall a. CommandHelpStr -> a
    error' (CommandHelpStr -> Text) -> CommandHelpStr -> Text
    forall a b. (a -> b) -> a -> b
    $ CommandHelpStr -> CommandHelpStr
    forall a. Show a => a -> CommandHelpStr
    show CommandHelpStr
    apat CommandHelpStr -> CommandHelpStr -> CommandHelpStr
    forall a. [a] -> [a] -> [a]
    ++ CommandHelpStr
    " did not match any account.")   -- PARTIAL:
                (Maybe Text -> Text) -> ([Text] -> Maybe Text) -> [Text] -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Text] -> Maybe Text
    firstMatch ([Text] -> Text) -> [Text] -> Text
    forall a b. (a -> b) -> a -> b
    $ Journal -> [Text]
    journalAccountNamesDeclaredOrImplied Journal
    j
            where
              firstMatch :: [Text] -> Maybe Text
    firstMatch = case Text -> Either CommandHelpStr Regexp
    toRegexCI (Text -> Either CommandHelpStr Regexp)
    -> Text -> Either CommandHelpStr Regexp
    forall a b. (a -> b) -> a -> b
    $ CommandHelpStr -> Text
    T.pack CommandHelpStr
    apat of
                  Right Regexp
    re -> (Text -> Bool) -> [Text] -> Maybe Text
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
    find (Regexp -> Text -> Bool
    regexMatchText Regexp
    re)
                  Left  CommandHelpStr
    _  -> Maybe Text -> [Text] -> Maybe Text
    forall a b. a -> b -> a
    const Maybe Text
    forall a. Maybe a
    Nothing
              apat :: CommandHelpStr
    apat = CommandHelpStr -> [CommandHelpStr] -> CommandHelpStr
    forall a. a -> [a] -> a
    headDef
                (CommandHelpStr -> CommandHelpStr
    forall a. CommandHelpStr -> a
    error' CommandHelpStr
    "With --find, please provide an account name or\naccount pattern (case-insensitive, infix, regexp) as first command argument.")
                ([CommandHelpStr] -> CommandHelpStr)
    -> [CommandHelpStr] -> CommandHelpStr
    forall a b. (a -> b) -> a -> b
    $ CommandHelpStr -> RawOpts -> [CommandHelpStr]
    listofstringopt CommandHelpStr
    "args" RawOpts
    rawopts
          matchedall :: [Text]
    matchedall = [Text]
    matcheddeclared [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ [Text]
    matchedused
          accts :: [Text]
    accts = CommandHelpStr -> [Text] -> [Text]
    forall a. Show a => CommandHelpStr -> a -> a
    dbg5 CommandHelpStr
    "accts to show" ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $
            case (CliOpts -> Maybe UsedOrDeclared
    usedOrDeclaredFromOpts CliOpts
    opts, CommandHelpStr -> RawOpts -> Bool
    boolopt CommandHelpStr
    "find" RawOpts
    rawopts) of
              (Maybe UsedOrDeclared
    Nothing,         Bool
    False) -> [Text]
    matchedall
              (Maybe UsedOrDeclared
    Nothing,         Bool
    True)  -> [Text
    matchedacct]
              (Just UsedOrDeclared
    Used,       Bool
    False) -> [Text]
    matchedused
              (Just UsedOrDeclared
    Declared,   Bool
    False) -> [Text]
    matcheddeclared
              (Just UsedOrDeclared
    Undeclared, Bool
    False) -> [Text]
    matchedundeclared
              (Just UsedOrDeclared
    Unused,     Bool
    False) -> [Text]
    matchedunused
              (Maybe UsedOrDeclared, Bool)
    _ -> CommandHelpStr -> [Text]
    forall a. CommandHelpStr -> a
    error' CommandHelpStr
    "please pick at most one of --used, --declared, --undeclared, --unused, --find"

      -- 2. sort them by declaration order (then undeclared accounts alphabetically)
      -- within each group of siblings
          sortedaccts :: [Text]
    sortedaccts = Journal -> Bool -> [Text] -> [Text]
    sortAccountNamesByDeclaration Journal
    j Bool
    tree [Text]
    accts

      -- 3. if there's a depth limit, depth-clip and remove any no longer useful items
          clippedaccts :: [Text]
    clippedaccts =
            CommandHelpStr -> [Text] -> [Text]
    forall a. Show a => CommandHelpStr -> a -> a
    dbg4 CommandHelpStr
    "clippedaccts" ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $
            (Text -> Bool) -> [Text] -> [Text]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Query -> Text -> Bool
    matchesAccount Query
    acctq) ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $  -- clipping can leave accounts that no longer match the query, remove such
            [Text] -> [Text]
    forall a. Eq a => [a] -> [a]
    nub ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $                            -- clipping can leave duplicates (adjacent, hopefully)
            (Text -> Bool) -> [Text] -> [Text]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Bool -> Bool
    not (Bool -> Bool) -> (Text -> Bool) -> Text -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> Bool
    T.null) ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $          -- depth:0 can leave nulls
            (Text -> Text) -> [Text] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map (DepthSpec -> Text -> Text
    clipAccountName DepthSpec
    dep) ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $      -- clip at depth if specified
            [Text]
    sortedaccts

      -- 4. print what remains as a list or tree, maybe applying --drop in the former case.
      -- Add various bits of info if enabled.
      let
        showKeyword :: Text
    showKeyword = if Bool
    directives then Text
    "account " else Text
    ""
        -- some contortions here to show types nicely aligned
        showName :: Text -> Text
    showName Text
    a = case ReportOpts -> AccountListMode
    accountlistmode_ ReportOpts
    ropts of
          AccountListMode
    ALTree -> Text
    indent Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text -> Text
    accountLeafName Text
    droppedName
          AccountListMode
    ALFlat -> Text
    droppedName
          where
            indent :: Text
    indent      = Int -> Text -> Text
    T.replicate (Int
    2 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    * (Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    max Int
    0 (Text -> Int
    accountNameLevel Text
    a Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - ReportOpts -> Int
    drop_ ReportOpts
    ropts) Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1)) Text
    " "
            droppedName :: Text
    droppedName = Int -> Text -> Text
    accountNameDrop (ReportOpts -> Int
    drop_ ReportOpts
    ropts) Text
    a
        showType :: Text -> Text
    showType Text
    a
          | Bool
    types     = Text -> Text
    pad Text
    a Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "    ; type: " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text -> (AccountType -> Text) -> Maybe AccountType -> Text
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe Text
    "" (CommandHelpStr -> Text
    T.pack (CommandHelpStr -> Text)
    -> (AccountType -> CommandHelpStr) -> AccountType -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . AccountType -> CommandHelpStr
    forall a. Show a => a -> CommandHelpStr
    show) (Journal -> Text -> Maybe AccountType
    journalAccountType Journal
    j Text
    a)
          | Bool
    otherwise = Text
    ""
        showAcctDeclOrder :: Text -> Text
    showAcctDeclOrder Text
    a
          | Bool
    positions =
            (if Bool
    types then Text
    "," else Text -> Text
    pad Text
    a Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "    ;") Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <>
            case Text
    -> [(Text, AccountDeclarationInfo)] -> Maybe AccountDeclarationInfo
    forall a b. Eq a => a -> [(a, b)] -> Maybe b
    lookup Text
    a ([(Text, AccountDeclarationInfo)] -> Maybe AccountDeclarationInfo)
    -> [(Text, AccountDeclarationInfo)] -> Maybe AccountDeclarationInfo
    forall a b. (a -> b) -> a -> b
    $ Journal -> [(Text, AccountDeclarationInfo)]
    jdeclaredaccounts Journal
    j of
              Just AccountDeclarationInfo
    adi ->
                Text
    " declared at " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> (CommandHelpStr -> Text
    T.pack (CommandHelpStr -> Text) -> CommandHelpStr -> Text
    forall a b. (a -> b) -> a -> b
    $ SourcePos -> CommandHelpStr
    sourcePosPretty (SourcePos -> CommandHelpStr) -> SourcePos -> CommandHelpStr
    forall a b. (a -> b) -> a -> b
    $ AccountDeclarationInfo -> SourcePos
    adisourcepos AccountDeclarationInfo
    adi) Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <>  -- TODO: hide the column number
                Text
    ", overall declaration order " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> (CommandHelpStr -> Text
    T.pack (CommandHelpStr -> Text) -> CommandHelpStr -> Text
    forall a b. (a -> b) -> a -> b
    $ Int -> CommandHelpStr
    forall a. Show a => a -> CommandHelpStr
    show (Int -> CommandHelpStr) -> Int -> CommandHelpStr
    forall a b. (a -> b) -> a -> b
    $ AccountDeclarationInfo -> Int
    adideclarationorder AccountDeclarationInfo
    adi)
              Maybe AccountDeclarationInfo
    Nothing -> Text
    " undeclared"
          | Bool
    otherwise = Text
    ""
        pad :: Text -> Text
    pad Text
    a = Int -> Text -> Text
    T.replicate (Int
    maxwidth Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Text -> Int
    T.length (Text -> Text
    showName Text
    a)) Text
    " "
        maxwidth :: Int
    maxwidth = [Int] -> Int
    forall a. Ord a => [a] -> a
    forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
    maximum ([Int] -> Int) -> [Int] -> Int
    forall a b. (a -> b) -> a -> b
    $ (Text -> Int) -> [Text] -> [Int]
    forall a b. (a -> b) -> [a] -> [b]
    map (Text -> Int
    T.length (Text -> Int) -> (Text -> Text) -> Text -> Int
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> Text
    showName) [Text]
    clippedaccts

      [Text] -> (Text -> IO ()) -> IO ()
    forall (t :: * -> *) (m :: * -> *) a b.
    (Foldable t, Monad m) =>
    t a -> (a -> m b) -> m ()
    forM_ [Text]
    clippedaccts ((Text -> IO ()) -> IO ()) -> (Text -> IO ()) -> IO ()
    forall a b. (a -> b) -> a -> b
    $ \Text
    a -> Text -> IO ()
    T.putStrLn (Text -> IO ()) -> Text -> IO ()
    forall a b. (a -> b) -> a -> b
    $ Text
    showKeyword Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text -> Text
    showName Text
    a Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text -> Text
    showType Text
    a Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text -> Text
    showAcctDeclOrder Text
    a