packages feed

hydra-0.5.0: src/main/haskell/Hydra/Tools/Formatting.hs

-- | String formatting utilities

module Hydra.Tools.Formatting where

import Hydra.Basics
import qualified Hydra.Lib.Strings as Strings

import qualified Data.Char as C
import qualified Data.List as L
import qualified Data.Map as M
import qualified Data.Set as S
import qualified Data.Maybe as Y


data CaseConvention = CaseConventionCamel | CaseConventionPascal | CaseConventionLowerSnake | CaseConventionUpperSnake

convertCase :: CaseConvention -> CaseConvention -> String -> String
convertCase from to original = case to of
    CaseConventionCamel -> decapitalize $ L.concat (capitalize . Strings.toLower <$> parts)
    CaseConventionPascal -> L.concat (capitalize . Strings.toLower <$> parts)
    CaseConventionLowerSnake -> L.intercalate "_" (Strings.toLower <$> parts)
    CaseConventionUpperSnake -> L.intercalate "_" (Strings.toUpper <$> parts)
  where
    parts = case from of
      CaseConventionCamel -> byCaps
      CaseConventionPascal -> byCaps
      CaseConventionLowerSnake -> byUnderscores
      CaseConventionUpperSnake -> byUnderscores
    byUnderscores = Strings.splitOn "_" original
    byCaps = L.foldl helper [""] $ L.reverse $ decapitalize original
      where
        helper (h:r) c = ["" | C.isUpper c] ++ ((c:h):r)

convertCaseCamelToLowerSnake :: String -> String
convertCaseCamelToLowerSnake = convertCase CaseConventionCamel CaseConventionLowerSnake

convertCaseCamelToUpperSnake :: String -> String
convertCaseCamelToUpperSnake = convertCase CaseConventionCamel CaseConventionUpperSnake

convertCasePascalToUpperSnake :: String -> String
convertCasePascalToUpperSnake = convertCase CaseConventionPascal CaseConventionUpperSnake

escapeWithUnderscore :: S.Set String -> String -> String
escapeWithUnderscore reserved s = if S.member s reserved then s ++ "_" else s

indentLines :: String -> String
indentLines s = unlines (indent <$> lines s)
  where
    indent l = "    " ++ l

javaStyleComment :: String -> String
javaStyleComment s = "/**\n" ++ " * " ++ s ++ "\n */"

nonAlnumToUnderscores :: String -> String
nonAlnumToUnderscores = L.reverse . fst . L.foldl replace ([], False)
  where
    replace (s, b) c = if isAlnum c
      then (c:s, False)
      else if b
        then (s, True)
        else ('_':s, True)
    isAlnum c = (c >= 'A' && c <= 'Z')
      || (c >= 'a' && c <= 'z')
      || (c >= '0' && c <= '9')

sanitizeWithUnderscores :: S.Set String -> String -> String
sanitizeWithUnderscores reserved = escapeWithUnderscore reserved . nonAlnumToUnderscores

withCharacterAliases :: String -> String
withCharacterAliases original = L.filter C.isAlphaNum $ L.concat $ alias <$> original
  where
    alias c = Y.maybe [c] capitalize $ M.lookup (C.ord c) aliases

    -- Taken from: https://cs.stanford.edu/people/miles/iso8859.html
    aliases = M.fromList [
      (32, "sp"),
      (33, "excl"),
      (34, "quot"),
      (35, "num"),
      (36, "dollar"),
      (37, "percnt"),
      (38, "amp"),
      (39, "apos"),
      (40, "lpar"),
      (41, "rpar"),
      (42, "ast"),
      (43, "plus"),
      (44, "comma"),
      (45, "minus"),
      (46, "period"),
      (47, "sol"),
      (58, "colon"),
      (59, "semi"),
      (60, "lt"),
      (61, "equals"),
      (62, "gt"),
      (63, "quest"),
      (64, "commat"),
      (91, "lsqb"),
      (92, "bsol"),
      (93, "rsqb"),
      (94, "circ"),
      (95, "lowbar"),
      (96, "grave"),
      (123, "lcub"),
      (124, "verbar"),
      (125, "rcub"),
      (126, "tilde")]

-- A simple soft line wrap which is suitable for code comments
wrapLine :: Int -> String -> String
wrapLine maxlen = L.intercalate "\n" . helper []
  where
    helper prev rem = if L.length rem <= maxlen
        then L.reverse (rem:prev)
        else if L.null prefix
        then helper (trunc:prev) $ L.drop maxlen rem
        else helper ((init prefix):prev) $ suffix ++ L.drop maxlen rem
      where
        trunc = L.take maxlen rem
        (prefix, suffix) = case span (\c -> c /= ' ' && c /= '\t') (reverse trunc) of
                                      (restRev, firstRev) -> (reverse firstRev, reverse restRev)