snippet-0.0.0.0: src/Snippet/Lines.hs
-- |
--
-- Module : Snippet.Lines
-- Copyright : (c) 2026 Patrick Brisbin
-- License : AGPL-3
-- Maintainer : pbrisbin@gmail.com
-- Stability : experimental
-- Portability : POSIX
module Snippet.Lines
( makeLines
, makeLines1
, makeLinesN
) where
import Prelude
import Data.List.NonEmpty (NonEmpty (..))
import Data.List.NonEmpty qualified as NE
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import Data.Text qualified as T
import Snippet.Line
import Snippet.Region
makeLines :: Region -> NonEmpty Text -> NonEmpty Line
makeLines region input = case NE.length input of
1 -> makeLines1 region input
_ -> makeLinesN region input
-- | Make 'Line's for the one-line case:
--
-- @
-- 85 │ , ((annotate AnnIcon "=" \<+\>) . pretty) \<$\> r.fullDescription
-- │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- @
makeLines1 :: Region -> NonEmpty Text -> NonEmpty Line
makeLines1 region input = headLine :| [underbarLine]
where
headLine =
Line
{ number = Just sl
, pre
, focused
, post
}
underbarLine =
Line
{ number = Nothing
, pre = T.replicate (sc - 1) " "
, focused = T.replicate (ec - sc) "^"
, post = ""
}
(pre, focused, post) = focusLine sc (Just ec) $ NE.head input
sl = region.startLine
sc = fromMaybe 1 region.startColumn
ec = fromMaybe (defaultEndColumn input) region.endColumn
-- | Make 'Line's for multi-line case
--
-- If on column 1:
--
-- @
-- 15 │ ╭ data ArtifactLocation = ArtifactLocation
-- 16 │ │ { uri :: Text
-- 17 │ │ }
-- 18 │ │ deriving stock (Eq)
-- │ ╰─────────────────────^
-- @
--
-- If not on column 1:
--
-- @
-- 15 │ data ArtifactLocation = ArtifactLocation
-- │ ╭────^
-- 16 │ │ { uri :: Text
-- 17 │ │ }
-- 18 │ │ deriving stock (Eq)
-- │ ╰─────────────────────^
-- @
makeLinesN :: Region -> NonEmpty Text -> NonEmpty Line
makeLinesN region input
| sc == 1 =
Line
{ number = Just sl
, pre = ""
, focused = "╭ " <> NE.head input
, post = ""
}
:| tailLines
| otherwise =
let (pre, focused, post) = focusLine sc Nothing $ NE.head input
in Line
{ number = Just sl
, pre = " " <> pre
, focused
, post
}
:| Line
{ number = Nothing
, pre = ""
, focused = "╭" <> T.replicate sc "─" <> "^"
, post = ""
}
: tailLines
where
tailLines = zipWith tailLine [sl + 1 ..] (NE.tail input) <> [underbarLine]
tailLine n x =
Line
{ number = Just n
, pre = ""
, focused = "│ " <> x
, post = ""
}
underbarLine =
Line
{ number = Nothing
, pre = ""
, focused = "╰─" <> T.replicate (ec - 2) "─" <> "^"
, post = ""
}
sl = region.startLine
sc = fromMaybe 1 region.startColumn
ec = fromMaybe (defaultEndColumn input) region.endColumn
focusLine :: Int -> Maybe Int -> Text -> (Text, Text, Text)
focusLine sc mec t = (pre, marked, post)
where
(pre, rest) = T.splitAt (sc - 1) t
(marked, post) = case mec of
Nothing -> (rest, "")
Just ec -> T.splitAt (ec - sc) rest
defaultEndColumn :: NonEmpty Text -> Int
defaultEndColumn = (+ 1) . T.length . NE.last