    -- | String formatting helpers, starting to get a bit out of control.

    module Hledger.Utils.String (
     takeEnd,
     -- * misc
     lowercase,
     uppercase,
     underline,
     stripbrackets,
     -- quoting
     quoteIfNeeded,
     singleQuoteIfNeeded,
     quoteForCommandLine,
     -- quotechars,
     -- whitespacechars,
     words',
     unwords',
     stripAnsi,
     -- * single-line layout
     strip,
     lstrip,
     rstrip,
     strip1Char,
     stripBy,
     strip1By,
     chomp,
     chomp1,
     singleline,
     elideLeft,
     elideRight,
     formatString,
     -- * wide-character-aware layout
     charWidth,
     strWidth,
     strWidthAnsi,
     takeWidth,
     ) where

    import Data.Char (isSpace, toLower, toUpper)
    import Data.List (intercalate, dropWhileEnd)
    import qualified Data.Text as T
    import Text.Megaparsec ((<|>), between, many, noneOf, sepBy)
    import Text.Megaparsec.Char (char)
    import Text.Printf (printf)

    import Hledger.Utils.Parse
    import Hledger.Utils.Regex (toRegex', regexReplace)
    import Text.DocLayout (charWidth, realLength)

    -- | Take elements from the end of a list.
    takeEnd :: Int -> [a] -> [a]
    takeEnd Int
    n [a]
    l = [a] -> [a] -> [a]
    forall {a} {a}. [a] -> [a] -> [a]
    go (Int -> [a] -> [a]
    forall a. Int -> [a] -> [a]
    drop Int
    n [a]
    l) [a]
    l
      where
        go :: [a] -> [a] -> [a]
    go (a
    _:[a]
    xs) (a
    _:[a]
    ys) = [a] -> [a] -> [a]
    go [a]
    xs [a]
    ys
        go []     [a]
    r      = [a]
    r
        go [a]
    _      []     = []

    lowercase, uppercase :: String -> String
    lowercase :: String -> String
    lowercase = (Char -> Char) -> String -> String
    forall a b. (a -> b) -> [a] -> [b]
    map Char -> Char
    toLower
    uppercase :: String -> String
    uppercase = (Char -> Char) -> String -> String
    forall a b. (a -> b) -> [a] -> [b]
    map Char -> Char
    toUpper

    -- | Remove leading and trailing whitespace.
    strip :: String -> String
    strip :: String -> String
    strip = String -> String
    lstrip (String -> String) -> (String -> String) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> String
    rstrip

    -- | Remove leading whitespace.
    lstrip :: String -> String
    lstrip :: String -> String
    lstrip = (Char -> Bool) -> String -> String
    forall a. (a -> Bool) -> [a] -> [a]
    dropWhile Char -> Bool
    isSpace

    -- | Remove trailing whitespace.
    rstrip :: String -> String
    rstrip :: String -> String
    rstrip = String -> String
    forall a. [a] -> [a]
    reverse (String -> String) -> (String -> String) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> String
    lstrip (String -> String) -> (String -> String) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> String
    forall a. [a] -> [a]
    reverse

    -- | Strip the given starting and ending character
    -- from the start and end of a string if both are present.
    strip1Char :: Char -> Char -> String -> String
    strip1Char :: Char -> Char -> String -> String
    strip1Char Char
    b Char
    e String
    s = case String
    s of
      (Char
    c:String
    cs) | Char
    cChar -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    ==Char
    b, Bool -> Bool
    not (Bool -> Bool) -> Bool -> Bool
    forall a b. (a -> b) -> a -> b
    $ String -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null String
    cs, String -> Char
    forall a. HasCallStack => [a] -> a
    last String
    csChar -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    ==Char
    e -> String -> String
    forall a. HasCallStack => [a] -> [a]
    init String
    cs
      String
    _ -> String
    s

    -- | Strip a run of zero or more characters matching the predicate
    -- from the start and end of a string.
    stripBy :: (Char -> Bool) -> String -> String
    stripBy :: (Char -> Bool) -> String -> String
    stripBy Char -> Bool
    f = (Char -> Bool) -> String -> String
    forall a. (a -> Bool) -> [a] -> [a]
    dropWhileEnd Char -> Bool
    f (String -> String) -> (String -> String) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Char -> Bool) -> String -> String
    forall a. (a -> Bool) -> [a] -> [a]
    dropWhile Char -> Bool
    f

    -- | Strip a single balanced enclosing pair of a character matching the predicate
    -- from the start and end of a string.
    strip1By :: (Char -> Bool) -> String -> String
    strip1By :: (Char -> Bool) -> String -> String
    strip1By Char -> Bool
    f String
    s = case String
    s of
      (Char
    c:String
    cs) | Char -> Bool
    f Char
    c, Bool -> Bool
    not (Bool -> Bool) -> Bool -> Bool
    forall a b. (a -> b) -> a -> b
    $ String -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null String
    cs, String -> Char
    forall a. HasCallStack => [a] -> a
    last String
    csChar -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    ==Char
    c -> String -> String
    forall a. HasCallStack => [a] -> [a]
    init String
    cs
      String
    _ -> String
    s

    -- | Remove all trailing newlines/carriage returns.
    chomp :: String -> String
    chomp :: String -> String
    chomp = String -> String
    forall a. [a] -> [a]
    reverse (String -> String) -> (String -> String) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Char -> Bool) -> String -> String
    forall a. (a -> Bool) -> [a] -> [a]
    dropWhile (Char -> String -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` String
    "\r\n") (String -> String) -> (String -> String) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> String
    forall a. [a] -> [a]
    reverse

    -- | Remove all trailing newline/carriage returns, leaving just one trailing newline.
    chomp1 :: String -> String
    chomp1 :: String -> String
    chomp1 = (String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++String
    "\n") (String -> String) -> (String -> String) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> String
    chomp

    -- | Remove consecutive line breaks, replacing them with single space
    singleline :: String -> String
    singleline :: String -> String
    singleline = [String] -> String
    unwords ([String] -> String) -> (String -> [String]) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (String -> Bool) -> [String] -> [String]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (String -> String -> Bool
    forall a. Eq a => a -> a -> Bool
    /=String
    "") ([String] -> [String])
    -> (String -> [String]) -> String -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ((String -> String) -> [String] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map String -> String
    strip) ([String] -> [String])
    -> (String -> [String]) -> String -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> [String]
    lines

    stripbrackets :: String -> String
    stripbrackets :: String -> String
    stripbrackets = (Char -> Bool) -> String -> String
    forall a. (a -> Bool) -> [a] -> [a]
    dropWhile (Char -> String -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` String
    "([") (String -> String) -> (String -> String) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> String
    forall a. [a] -> [a]
    reverse (String -> String) -> (String -> String) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Char -> Bool) -> String -> String
    forall a. (a -> Bool) -> [a] -> [a]
    dropWhile (Char -> String -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` String
    "])") (String -> String) -> (String -> String) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> String
    forall a. [a] -> [a]
    reverse :: String -> String

    elideLeft :: Int -> String -> String
    elideLeft :: Int -> String -> String
    elideLeft Int
    width String
    s =
        if String -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length String
    s Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    width then String
    ".." String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ Int -> String -> String
    forall a. Int -> [a] -> [a]
    takeEnd (Int
    width Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    2) String
    s else String
    s

    elideRight :: Int -> String -> String
    elideRight :: Int -> String -> String
    elideRight Int
    width String
    s =
        if String -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length String
    s Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    width then Int -> String -> String
    forall a. Int -> [a] -> [a]
    take (Int
    width Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    2) String
    s String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    ".." else String
    s

    -- | Clip and pad a string to a minimum & maximum width, and/or left/right justify it.
    -- Works on multi-line strings too (but will rewrite non-unix line endings).
    formatString :: Bool -> Maybe Int -> Maybe Int -> String -> String
    formatString :: Bool -> Maybe Int -> Maybe Int -> String -> String
    formatString Bool
    leftJustified Maybe Int
    minwidth Maybe Int
    maxwidth String
    s = String -> [String] -> String
    forall a. [a] -> [[a]] -> [a]
    intercalate String
    "\n" ([String] -> String) -> [String] -> String
    forall a b. (a -> b) -> a -> b
    $ (String -> String) -> [String] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map (String -> String -> String
    forall r. PrintfType r => String -> r
    printf String
    fmt) ([String] -> [String]) -> [String] -> [String]
    forall a b. (a -> b) -> a -> b
    $ String -> [String]
    lines String
    s
        where
          justify :: String
    justify = if Bool
    leftJustified then String
    "-" else String
    ""
          minwidth' :: String
    minwidth' = String -> (Int -> String) -> Maybe Int -> String
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe String
    "" Int -> String
    forall a. Show a => a -> String
    show Maybe Int
    minwidth
          maxwidth' :: String
    maxwidth' = String -> (Int -> String) -> Maybe Int -> String
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe String
    "" ((String
    "."String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++)(String -> String) -> (Int -> String) -> Int -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    .Int -> String
    forall a. Show a => a -> String
    show) Maybe Int
    maxwidth
          fmt :: String
    fmt = String
    "%" String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    justify String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    minwidth' String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    maxwidth' String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    "s"

    underline :: String -> String
    underline :: String -> String
    underline String
    s = String
    s' String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ Int -> Char -> String
    forall a. Int -> a -> [a]
    replicate (String -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length String
    s) Char
    '-' String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    "\n"
        where s' :: String
    s'
                | String -> Char
    forall a. HasCallStack => [a] -> a
    last String
    s Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == Char
    '\n' = String
    s
                | Bool
    otherwise = String
    s String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    "\n"

    -- | Double-quote this string if it contains whitespace, single quotes
    -- or double-quotes, escaping the quotes as needed.
    quoteIfNeeded :: String -> String
    quoteIfNeeded :: String -> String
    quoteIfNeeded String
    s | (Char -> Bool) -> String -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    any (Char -> String -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` String
    s) (String
    quotecharsString -> String -> String
    forall a. [a] -> [a] -> [a]
    ++String
    whitespacecharsString -> String -> String
    forall a. [a] -> [a] -> [a]
    ++String
    redirectchars) = Char -> String -> String
    showChar Char
    '"' (String -> String) -> String -> String
    forall a b. (a -> b) -> a -> b
    $ String -> String -> String
    escapeQuotes String
    s String
    "\""
                    | Bool
    otherwise = String
    s
      where
        escapeQuotes :: String -> String -> String
    escapeQuotes []       String
    x = String
    x
        escapeQuotes (Char
    '"':String
    cs) String
    x = String -> String -> String
    showString String
    "\\\"" (String -> String) -> String -> String
    forall a b. (a -> b) -> a -> b
    $ String -> String -> String
    escapeQuotes String
    cs String
    x
        escapeQuotes (Char
    c:String
    cs)   String
    x = Char -> String -> String
    showChar Char
    c        (String -> String) -> String -> String
    forall a b. (a -> b) -> a -> b
    $ String -> String -> String
    escapeQuotes String
    cs String
    x

    -- | Single-quote this string if it contains whitespace or double-quotes.
    -- Does not work for strings containing single quotes.
    singleQuoteIfNeeded :: String -> String
    singleQuoteIfNeeded :: String -> String
    singleQuoteIfNeeded String
    s | (Char -> Bool) -> String -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    any (Char -> String -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` String
    s) (String
    quotecharsString -> String -> String
    forall a. [a] -> [a] -> [a]
    ++String
    whitespacechars) = String -> String
    singleQuote String
    s
                          | Bool
    otherwise = String
    s

    -- | Prepend and append single quotes to a string.
    singleQuote :: String -> String
    singleQuote :: String -> String
    singleQuote String
    s = String
    "'"String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++String
    sString -> String -> String
    forall a. [a] -> [a] -> [a]
    ++String
    "'"

    -- | Try to single- and backslash-quote a string as needed to make it usable
    -- as an argument on a (sh/bash) shell command line. At least, well enough
    -- to handle common currency symbols, like $. Probably broken in many ways.
    --
    -- >>> quoteForCommandLine "a"
    -- "a"
    -- >>> quoteForCommandLine "\""
    -- "'\"'"
    -- >>> quoteForCommandLine "$"
    -- "'\\$'"
    --
    quoteForCommandLine :: String -> String
    quoteForCommandLine :: String -> String
    quoteForCommandLine String
    s
      | (Char -> Bool) -> String -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    any (Char -> String -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` String
    s) (String
    quotecharsString -> String -> String
    forall a. [a] -> [a] -> [a]
    ++String
    whitespacecharsString -> String -> String
    forall a. [a] -> [a] -> [a]
    ++String
    shellchars) = String -> String
    singleQuote (String -> String) -> String -> String
    forall a b. (a -> b) -> a -> b
    $ String -> String
    quoteShellChars String
    s
      | Bool
    otherwise = String
    s

    -- | Try to backslash-quote common shell-significant characters in this string.
    -- Doesn't handle single quotes, & probably others.
    quoteShellChars :: String -> String
    quoteShellChars :: String -> String
    quoteShellChars = (Char -> String) -> String -> String
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap Char -> String
    escapeShellChar
      where
        escapeShellChar :: Char -> String
    escapeShellChar Char
    c | Char
    c Char -> String -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` String
    shellchars = [Char
    '\\',Char
    c]
        escapeShellChar Char
    c = [Char
    c]

    quotechars, whitespacechars, redirectchars, shellchars :: [Char]
    quotechars :: String
    quotechars      = String
    "'\""
    whitespacechars :: String
    whitespacechars = String
    " \t\n\r"
    redirectchars :: String
    redirectchars   = String
    "<>"
    shellchars :: String
    shellchars      = String
    "<>(){}[]$7?#!~`"

    -- | Quote-aware version of words - don't split on spaces which are inside quotes.
    -- NB correctly handles "a'b" but not "''a''". Can raise an error if parsing fails.
    words' :: String -> [String]
    words' :: String -> [String]
    words' String
    "" = []
    words' String
    s  = (String -> String) -> [String] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map String -> String
    stripquotes ([String] -> [String]) -> [String] -> [String]
    forall a b. (a -> b) -> a -> b
    $ Either (ParseErrorBundle String HledgerParseErrorData) [String]
    -> [String]
    forall t e a.
    (Show t, Show (Token t), Show e) =>
    Either (ParseErrorBundle t e) a -> a
    fromparse (Either (ParseErrorBundle String HledgerParseErrorData) [String]
     -> [String])
    -> Either (ParseErrorBundle String HledgerParseErrorData) [String]
    -> [String]
    forall a b. (a -> b) -> a -> b
    $ Parsec HledgerParseErrorData String [String]
    -> String
    -> Either (ParseErrorBundle String HledgerParseErrorData) [String]
    forall e a.
    Parsec e String a -> String -> Either (ParseErrorBundle String e) a
    parsewithString Parsec HledgerParseErrorData String [String]
    ParsecT HledgerParseErrorData String Identity [[Token String]]
    p String
    s
        where
          p :: ParsecT HledgerParseErrorData String Identity [[Token String]]
    p = (ParsecT HledgerParseErrorData String Identity [Token String]
    singleQuotedPattern ParsecT HledgerParseErrorData String Identity [Token String]
    -> ParsecT HledgerParseErrorData String Identity [Token String]
    -> ParsecT HledgerParseErrorData String Identity [Token String]
    forall a.
    ParsecT HledgerParseErrorData String Identity a
    -> ParsecT HledgerParseErrorData String Identity a
    -> ParsecT HledgerParseErrorData String Identity a
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
    <|> ParsecT HledgerParseErrorData String Identity [Token String]
    doubleQuotedPattern ParsecT HledgerParseErrorData String Identity [Token String]
    -> ParsecT HledgerParseErrorData String Identity [Token String]
    -> ParsecT HledgerParseErrorData String Identity [Token String]
    forall a.
    ParsecT HledgerParseErrorData String Identity a
    -> ParsecT HledgerParseErrorData String Identity a
    -> ParsecT HledgerParseErrorData String Identity a
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
    <|> ParsecT HledgerParseErrorData String Identity [Token String]
    patterns) ParsecT HledgerParseErrorData String Identity [Token String]
    -> ParsecT HledgerParseErrorData String Identity ()
    -> ParsecT HledgerParseErrorData String Identity [[Token String]]
    forall (m :: * -> *) a sep. MonadPlus m => m a -> m sep -> m [a]
    `sepBy` ParsecT HledgerParseErrorData String Identity ()
    forall s (m :: * -> *).
    (Stream s, Token s ~ Char) =>
    ParsecT HledgerParseErrorData s m ()
    skipNonNewlineSpaces1
              -- eof
          patterns :: ParsecT HledgerParseErrorData String Identity [Token String]
    patterns = ParsecT HledgerParseErrorData String Identity (Token String)
    -> ParsecT HledgerParseErrorData String Identity [Token String]
    forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
    many ([Token String]
    -> ParsecT HledgerParseErrorData String Identity (Token String)
    forall (f :: * -> *) e s (m :: * -> *).
    (Foldable f, MonadParsec e s m) =>
    f (Token s) -> m (Token s)
    noneOf String
    [Token String]
    whitespacechars)
          singleQuotedPattern :: ParsecT HledgerParseErrorData String Identity [Token String]
    singleQuotedPattern = ParsecT HledgerParseErrorData String Identity Char
    -> ParsecT HledgerParseErrorData String Identity Char
    -> ParsecT HledgerParseErrorData String Identity [Token String]
    -> ParsecT HledgerParseErrorData String Identity [Token String]
    forall (m :: * -> *) open close a.
    Applicative m =>
    m open -> m close -> m a -> m a
    between (Token String
    -> ParsecT HledgerParseErrorData String Identity (Token String)
    forall e s (m :: * -> *).
    (MonadParsec e s m, Token s ~ Char) =>
    Token s -> m (Token s)
    char Char
    Token String
    '\'') (Token String
    -> ParsecT HledgerParseErrorData String Identity (Token String)
    forall e s (m :: * -> *).
    (MonadParsec e s m, Token s ~ Char) =>
    Token s -> m (Token s)
    char Char
    Token String
    '\'') (ParsecT HledgerParseErrorData String Identity (Token String)
    -> ParsecT HledgerParseErrorData String Identity [Token String]
    forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
    many (ParsecT HledgerParseErrorData String Identity (Token String)
     -> ParsecT HledgerParseErrorData String Identity [Token String])
    -> ParsecT HledgerParseErrorData String Identity (Token String)
    -> ParsecT HledgerParseErrorData String Identity [Token String]
    forall a b. (a -> b) -> a -> b
    $ [Token String]
    -> ParsecT HledgerParseErrorData String Identity (Token String)
    forall (f :: * -> *) e s (m :: * -> *).
    (Foldable f, MonadParsec e s m) =>
    f (Token s) -> m (Token s)
    noneOf String
    [Token String]
    "'")
          doubleQuotedPattern :: ParsecT HledgerParseErrorData String Identity [Token String]
    doubleQuotedPattern = ParsecT HledgerParseErrorData String Identity Char
    -> ParsecT HledgerParseErrorData String Identity Char
    -> ParsecT HledgerParseErrorData String Identity [Token String]
    -> ParsecT HledgerParseErrorData String Identity [Token String]
    forall (m :: * -> *) open close a.
    Applicative m =>
    m open -> m close -> m a -> m a
    between (Token String
    -> ParsecT HledgerParseErrorData String Identity (Token String)
    forall e s (m :: * -> *).
    (MonadParsec e s m, Token s ~ Char) =>
    Token s -> m (Token s)
    char Char
    Token String
    '"') (Token String
    -> ParsecT HledgerParseErrorData String Identity (Token String)
    forall e s (m :: * -> *).
    (MonadParsec e s m, Token s ~ Char) =>
    Token s -> m (Token s)
    char Char
    Token String
    '"') (ParsecT HledgerParseErrorData String Identity (Token String)
    -> ParsecT HledgerParseErrorData String Identity [Token String]
    forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
    many (ParsecT HledgerParseErrorData String Identity (Token String)
     -> ParsecT HledgerParseErrorData String Identity [Token String])
    -> ParsecT HledgerParseErrorData String Identity (Token String)
    -> ParsecT HledgerParseErrorData String Identity [Token String]
    forall a b. (a -> b) -> a -> b
    $ [Token String]
    -> ParsecT HledgerParseErrorData String Identity (Token String)
    forall (f :: * -> *) e s (m :: * -> *).
    (Foldable f, MonadParsec e s m) =>
    f (Token s) -> m (Token s)
    noneOf String
    [Token String]
    "\"")

    -- | Quote-aware version of unwords - single-quote strings which contain whitespace
    unwords' :: [String] -> String
    unwords' :: [String] -> String
    unwords' = [String] -> String
    unwords ([String] -> String)
    -> ([String] -> [String]) -> [String] -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (String -> String) -> [String] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map String -> String
    quoteIfNeeded

    -- | Strip one matching pair of single or double quotes on the ends of a string.
    stripquotes :: String -> String
    stripquotes :: String -> String
    stripquotes String
    s = if String -> Bool
    isSingleQuoted String
    s Bool -> Bool -> Bool
    || String -> Bool
    isDoubleQuoted String
    s then String -> String
    forall a. HasCallStack => [a] -> [a]
    init (String -> String) -> String -> String
    forall a b. (a -> b) -> a -> b
    $ String -> String
    forall a. HasCallStack => [a] -> [a]
    tail String
    s else String
    s

    isSingleQuoted :: String -> Bool
    isSingleQuoted s :: String
    s@(Char
    _:Char
    _:String
    _) = String -> Char
    forall a. HasCallStack => [a] -> a
    head String
    s Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == Char
    '\'' Bool -> Bool -> Bool
    && String -> Char
    forall a. HasCallStack => [a] -> a
    last String
    s Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == Char
    '\''
    isSingleQuoted String
    _ = Bool
    False

    isDoubleQuoted :: String -> Bool
    isDoubleQuoted s :: String
    s@(Char
    _:Char
    _:String
    _) = String -> Char
    forall a. HasCallStack => [a] -> a
    head String
    s Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == Char
    '"' Bool -> Bool -> Bool
    && String -> Char
    forall a. HasCallStack => [a] -> a
    last String
    s Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == Char
    '"'
    isDoubleQuoted String
    _ = Bool
    False

    -- Functions below treat wide (eg CJK) characters as double-width.

    -- | Double-width-character-aware string truncation. Take as many
    -- characters as possible from a string without exceeding the
    -- specified width. Eg takeWidth 3 "りんご" = "り".
    takeWidth :: Int -> String -> String
    takeWidth :: Int -> String -> String
    takeWidth Int
    _ String
    ""     = String
    ""
    takeWidth Int
    0 String
    _      = String
    ""
    takeWidth Int
    w (Char
    c:String
    cs) | Int
    cw Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    w   = Char
    cChar -> String -> String
    forall a. a -> [a] -> [a]
    :Int -> String -> String
    takeWidth (Int
    wInt -> Int -> Int
    forall a. Num a => a -> a -> a
    -Int
    cw) String
    cs
                       | Bool
    otherwise = String
    ""
      where cw :: Int
    cw = Char -> Int
    charWidth Char
    c

    -- | Like strWidth, but also strips ANSI escape sequences before
    -- calculating the width.
    --
    -- This is no longer used in code, as widths are calculated before
    -- adding ANSI escape sequences, but is being kept around for now.
    strWidthAnsi :: String -> Int
    strWidthAnsi :: String -> Int
    strWidthAnsi = String -> Int
    strWidth (String -> Int) -> (String -> String) -> String -> Int
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> String
    stripAnsi

    -- | Alias for 'realLength'.
    strWidth :: String -> Int
    strWidth :: String -> Int
    strWidth = String -> Int
    forall a. HasChars a => a -> Int
    realLength

    -- | Strip ANSI escape sequences from a string.
    --
    -- >>> stripAnsi "\ESC[31m-1\ESC[m"
    -- "-1"
    stripAnsi :: String -> String
    stripAnsi :: String -> String
    stripAnsi String
    s = (String -> String)
    -> (String -> String) -> Either String String -> String
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
    either String -> String
    forall {a}. a
    err String -> String
    forall a. a -> a
    id (Either String String -> String) -> Either String String -> String
    forall a b. (a -> b) -> a -> b
    $ Regexp -> String -> String -> Either String String
    regexReplace Regexp
    ansire String
    "" String
    s
     where
       err :: a
    err    = String -> a
    forall a. HasCallStack => String -> a
    error String
    "stripAnsi: invalid replacement pattern"      -- PARTIAL, shouldn't happen
       ansire :: Regexp
    ansire = Text -> Regexp
    toRegex' (Text -> Regexp) -> Text -> Regexp
    forall a b. (a -> b) -> a -> b
    $ String -> Text
    T.pack String
    "\ESC\\[([0-9]+;)*([0-9]+)?[ABCDHJKfmsu]"  -- PARTIAL, should succeed