align-equal-1.0.1.0: src/Data/Text/AlignEqual.hs
{-# LANGUAGE OverloadedStrings #-}
-- | A module providing functions for text alignment and padding.
module Data.Text.AlignEqual where
import Data.Maybe (catMaybes)
import Data.Text (Text)
import qualified Data.Text as T
import Safe (maximumMay)
-- | Calculates the number of characters preceding the first '=' sign in a text line.
-- If no '=' is found, returns `Nothing`. If '=' is found, returns `Just` the length of the prefix before it.
--
-- >>> prefixLength "key=value"
-- Just 3
-- >>> prefixLength "a=b"
-- Just 1
-- >>> prefixLength "noequals"
-- Nothing
prefixLength
:: Text
-- ^ The input text line
-> Maybe Int
-- ^ The number of characters before the first '=' sign, or `Nothing` if no '=' is found, or `Just` the length otherwise
prefixLength line =
if T.null suffix
then Nothing
else Just (T.length prefix)
where
(prefix, suffix) = T.breakOn "=" line
-- | Adjusts the prefix of a text line to a desired length by adding padding spaces.
--
-- >>> adjustLine 5 "key=value"
-- "key =value"
-- >>> adjustLine 3 "a=b"
-- "a =b"
adjustLine
:: Int
-- ^ The desired prefix length
-> Text
-- ^ The text line to pad
-> Text
-- ^ The padded text line
adjustLine desiredPrefixLength oldLine = newLine
where
(prefix, suffix) = T.breakOn "=" oldLine
actualPrefixLength = T.length prefix
additionalSpaces = desiredPrefixLength - actualPrefixLength
spaces = T.replicate additionalSpaces " "
newLine = T.concat [ prefix, spaces, suffix ]
-- | Processes multi-line text to align all '=' signs across lines.
-- It adjusts the prefix length of each line to match the maximum prefix length.
-- If a line does not contain '=', it will be left as-is.
-- If the input is empty or contains no lines with '=', it is returned unchanged.
--
-- >>> adjustText "key=value\na=b"
-- "key=value\na =b"
-- >>> adjustText "x=y\nlong=var"
-- "x =y\nlong=var"
-- >>> adjustText ""
-- ""
adjustText
:: Text
-- ^ The input text (possibly multi-line)
-> Text
-- ^ The aligned text, or the original text if no '=' is found
adjustText oldText
| T.null oldText = oldText
| null newLines = oldText
| otherwise = T.intercalate "\n" newLines
where
oldLines = T.lines oldText
prefixLengths = map prefixLength oldLines
prefixLengths' = catMaybes prefixLengths
newLines =
case maximumMay prefixLengths' of
Nothing ->
[]
Just desiredPrefixLength ->
map (adjustLine desiredPrefixLength) oldLines