summaryrefslogtreecommitdiff
path: root/src/Erebos
diff options
context:
space:
mode:
authorRoman Smrž <roman.smrz@seznam.cz>2026-01-31 17:48:55 +0100
committerRoman Smrž <roman.smrz@seznam.cz>2026-01-31 20:42:22 +0100
commit0c36f490719f613dc23b6632a4098bd17fb0ab7c (patch)
tree895014981fe22b57b73fe63aba6918520341e5aa /src/Erebos
parent0c1f276f3bfc2f25653d1cd7a75b59d9f6afd522 (diff)
Data structure for formatted text
Diffstat (limited to 'src/Erebos')
-rw-r--r--src/Erebos/Conversation.hs21
-rw-r--r--src/Erebos/TextFormat.hs16
-rw-r--r--src/Erebos/TextFormat/Ansi.hs59
-rw-r--r--src/Erebos/TextFormat/Types.hs57
4 files changed, 146 insertions, 7 deletions
diff --git a/src/Erebos/Conversation.hs b/src/Erebos/Conversation.hs
index 2c6f967..a48daf7 100644
--- a/src/Erebos/Conversation.hs
+++ b/src/Erebos/Conversation.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE OverloadedStrings #-}
+
module Erebos.Conversation (
Message,
messageFrom,
@@ -5,6 +7,7 @@ module Erebos.Conversation (
messageText,
messageUnread,
formatMessage,
+ formatMessageFT,
Conversation,
isSameConversation,
@@ -38,6 +41,8 @@ import Erebos.DirectMessage
import Erebos.Identity
import Erebos.State
import Erebos.Storable
+import Erebos.TextFormat
+import Erebos.TextFormat.Types
data Message = forall conv msg. ConversationType conv msg => Message msg Bool
@@ -58,13 +63,15 @@ messageUnread :: Message -> Bool
messageUnread (Message _ unread) = unread
formatMessage :: TimeZone -> Message -> String
-formatMessage tzone msg = concat
- [ if messageUnread msg then "\ESC[93m" else ""
- , formatTime defaultTimeLocale "[%H:%M] " $ utcToLocalTime tzone $ zonedTimeToUTC $ messageTime msg
- , maybe "<unnamed>" T.unpack $ idName $ messageFrom msg
- , maybe "" ((": "<>) . T.unpack) $ messageText msg
- , if messageUnread msg then "\ESC[0m" else ""
- ]
+formatMessage tzone = T.unpack . renderPlainText . formatMessageFT tzone
+
+formatMessageFT :: TimeZone -> Message -> FormattedText
+formatMessageFT tzone msg =
+ (if messageUnread msg then FormattedText (CustomTextColor (Just BrightYellow) Nothing) else id) $ mconcat
+ [ PlainText $ T.pack $ formatTime defaultTimeLocale "[%H:%M] " $ utcToLocalTime tzone $ zonedTimeToUTC $ messageTime msg
+ , maybe "<unnamed>" PlainText $ idName $ messageFrom msg
+ , maybe "" ((": " <>) . PlainText) $ messageText msg
+ ]
data Conversation
diff --git a/src/Erebos/TextFormat.hs b/src/Erebos/TextFormat.hs
new file mode 100644
index 0000000..0bfad75
--- /dev/null
+++ b/src/Erebos/TextFormat.hs
@@ -0,0 +1,16 @@
+module Erebos.TextFormat (
+ FormattedText,
+
+ renderPlainText,
+) where
+
+import Data.Text (Text)
+
+import Erebos.TextFormat.Types
+
+
+renderPlainText :: FormattedText -> Text
+renderPlainText = \case
+ PlainText text -> text
+ ConcatenatedText ftexts -> mconcat $ map renderPlainText ftexts
+ FormattedText _ ftext -> renderPlainText ftext
diff --git a/src/Erebos/TextFormat/Ansi.hs b/src/Erebos/TextFormat/Ansi.hs
new file mode 100644
index 0000000..504e098
--- /dev/null
+++ b/src/Erebos/TextFormat/Ansi.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Erebos.TextFormat.Ansi (
+ FormattedText,
+
+ AnsiText(..),
+ renderAnsiText,
+) where
+
+import Control.Applicative
+
+import Data.Text (Text)
+import Data.Text qualified as T
+
+import Erebos.TextFormat.Types
+
+
+newtype AnsiText = AnsiText { fromAnsiText :: Text }
+
+renderAnsiText :: FormattedText -> AnsiText
+renderAnsiText = AnsiText . go ( Nothing, Nothing )
+ where
+ go cur@( cfg, cbg ) = \case
+ PlainText text -> text
+ ConcatenatedText ftexts -> mconcat $ map (go cur) ftexts
+ FormattedText (CustomTextColor fg bg) ftext -> mconcat
+ [ ansiColor fg bg
+ , go ( fg <|> cfg, bg <|> cbg ) ftext
+ , ansiColor
+ (if fg /= cfg then cfg <|> Just DefaultColor else Nothing)
+ (if bg /= cbg then cbg <|> Just DefaultColor else Nothing)
+ ]
+
+
+ansiColor :: Maybe Color -> Maybe Color -> Text
+ansiColor Nothing Nothing = ""
+ansiColor (Just fg) Nothing = "\ESC[" <> T.pack (show (colorNum fg)) <> "m"
+ansiColor Nothing (Just bg) = "\ESC[" <> T.pack (show (colorNum bg + 10)) <> "m"
+ansiColor (Just fg) (Just bg) = "\ESC[" <> T.pack (show (colorNum fg)) <> ";" <> T.pack (show (colorNum bg + 10)) <> "m"
+
+colorNum :: Color -> Int
+colorNum = \case
+ DefaultColor -> 39
+ Black -> 30
+ Red -> 31
+ Green -> 32
+ Yellow -> 33
+ Blue -> 34
+ Magenta -> 35
+ Cyan -> 36
+ White -> 37
+ BrightBlack -> 90
+ BrightRed -> 91
+ BrightGreen -> 92
+ BrightYellow -> 93
+ BrightBlue -> 94
+ BrightMagenta -> 95
+ BrightCyan -> 96
+ BrightWhite -> 97
diff --git a/src/Erebos/TextFormat/Types.hs b/src/Erebos/TextFormat/Types.hs
new file mode 100644
index 0000000..a03bc71
--- /dev/null
+++ b/src/Erebos/TextFormat/Types.hs
@@ -0,0 +1,57 @@
+module Erebos.TextFormat.Types (
+ FormattedText(..),
+ TextStyle(..),
+ Color(..),
+) where
+
+import Data.String
+import Data.Text (Text)
+
+
+data FormattedText
+ = PlainText Text
+ | ConcatenatedText [ FormattedText ]
+ | FormattedText TextStyle FormattedText
+
+instance IsString FormattedText where
+ fromString = PlainText . fromString
+
+instance Semigroup FormattedText where
+ ConcatenatedText xs <> ConcatenatedText ys = ConcatenatedText (xs ++ ys)
+ x <> ConcatenatedText ys = ConcatenatedText (x : ys)
+ ConcatenatedText xs <> y = ConcatenatedText (xs ++ [ y ])
+ x <> y = ConcatenatedText [ x, y ]
+
+instance Monoid FormattedText where
+ mempty = ConcatenatedText []
+ mconcat [] = ConcatenatedText []
+ mconcat [ x ] = x
+ mconcat xs = ConcatenatedText $ concatMap flatten xs
+ where
+ flatten (ConcatenatedText ys) = ys
+ flatten y = [ y ]
+
+
+data TextStyle
+ = CustomTextColor (Maybe Color) (Maybe Color)
+
+
+data Color
+ = DefaultColor
+ | Black
+ | Red
+ | Green
+ | Yellow
+ | Blue
+ | Magenta
+ | Cyan
+ | White
+ | BrightBlack
+ | BrightRed
+ | BrightGreen
+ | BrightYellow
+ | BrightBlue
+ | BrightMagenta
+ | BrightCyan
+ | BrightWhite
+ deriving (Eq)