snippet-0.1.0.3: src/Snippet/Pretty.hs
-- |
--
-- Module : Snippet.Pretty
-- Copyright : (c) 2026 Patrick Brisbin
-- License : AGPL-3
-- Maintainer : pbrisbin@gmail.com
-- Stability : experimental
-- Portability : POSIX
module Snippet.Pretty
( Ann (..)
, annToAnsi
, prettySnippet
, prettySnippetFill
, snippetGutterWidth
, snippetInsetWidth
) where
import Prelude
import Data.Maybe (fromMaybe)
import Data.Text qualified as T
import Prettyprinter hiding (line)
import Snippet.Line
import Snippet.Pretty.Ann
import Snippet.Region
import Snippet.Snippet
-- | A 'Snippet's gutter fits the digits of its maximum (i.e. end) line number,
-- plus 1 character of whitespace.
snippetGutterWidth :: Snippet -> Int
snippetGutterWidth snippet = length (show @Int el) + 1
where
sl = snippet.region.startLine
el = fromMaybe sl snippet.region.endLine
-- | A 'Snippet's inset matches how far the code is indented to support region
-- marking.
snippetInsetWidth :: Snippet -> Int
snippetInsetWidth = regionInsetWidth . (.region)
regionInsetWidth :: Region -> Int
regionInsetWidth region =
go region + maybe 0 go region.subRegion
where
go r
| maybe True (== r.startLine) r.endLine = 0
| otherwise = 2
prettySnippet :: Snippet -> Doc Ann
prettySnippet snippet = prettySnippetFill w snippet
where
w = length (show @Int el) + 1
sl = snippet.region.startLine
el = fromMaybe sl snippet.region.endLine
-- | 'prettySnippet' but fill the left gutter to the given width
--
--
-- This is useful if multiple snippets are being rendered together and their
-- line-numbers are different numbers of digits. You can compute the maximum
-- width on the outside (see 'snippetGutterWidth') and pass it so they all
-- align.
--
-- For example:
--
-- @
-- >>> vsep [prettySnippet a, prettySnippet b]
-- ┌─ a
-- │
-- 1 │ ...
-- │
-- ┌─ b
-- │
-- 15 │ ...
-- │
-- @
--
--
-- @
-- >>> let w = max `on` snippetGutterWidth a b
-- >>> vsep [prettySnippetFill w a, prettySnippetFill w b]
-- ┌─
-- │
-- 1 │ ...
-- │
-- ┌─ b
-- │
-- 15 │ ...
-- │
-- @
prettySnippetFill :: Int -> Snippet -> Doc Ann
prettySnippetFill w snippet =
vsep
[ indent w
$ annotate
AnnDecoration
(pretty $ "┌" <> T.replicate (snippetInsetWidth snippet) "─")
<+> pretty (snippet.uri)
<> ":"
<> pretty snippet.region.startLine
<> ":"
<> pretty snippet.region.startColumn
, indent w $ annotate AnnDecoration "│"
, prettyLines w snippet.lines
]
prettyLines :: Int -> [Line] -> Doc Ann
prettyLines w = vsep . map (prettyLine w)
prettyLine :: Int -> Line -> Doc Ann
prettyLine w line = annotate AnnDecoration (number <> "│") <+> lineContent line
where
-- TODO: I'd prefer right-align
number = fill w (maybe "" pretty $ lineNumber line)