1
- {- | Wrap text in Markdown format without converting tabs to spaces.
1
+ {-# LANGUAGE OverloadedStrings #-}
2
+ {-# LANGUAGE FlexibleInstances #-}
3
+ {-# LANGUAGE MultiParamTypeClasses #-}
4
+ {-# LANGUAGE GeneralizedNewtypeDeriving #-}
2
5
3
- This module has two problems because of the hacky solution:
4
-
5
- 1. Gopher links have the potential of being wrapped in the middle of the link, which would
6
- break the link.
7
- 2. The tab character is preserved by using a placeholder, which is not ideal.
8
-
9
- -}
10
- module Bore.Text.Wrap (wrapMarkdown ) where
6
+ module Bore.Text.Wrap where
11
7
8
+ import Commonmark
12
9
import Data.Text (Text )
13
10
import qualified Data.Text as T
14
- import Text.Pandoc
11
+ import Text.Wrap (wrapText , defaultWrapSettings )
12
+ import Control.Monad.Reader (Reader , asks , runReader )
13
+
14
+ -- Configuration for the renderer
15
+ data WrapConfig = WrapConfig
16
+ { lineWidth :: Int -- Configurable line width
17
+ }
18
+
19
+ -- Custom Renderer for Wrapping Text
20
+ newtype WrapRenderer = WrapRenderer { getWrappedText :: Reader WrapConfig Text }
21
+
22
+ instance Show WrapRenderer where
23
+ show (WrapRenderer t) = show (runReader t (WrapConfig 80 ))
24
+ instance Rangeable WrapRenderer where
25
+ ranged _ (WrapRenderer t) = WrapRenderer t
26
+ instance HasAttributes WrapRenderer where
27
+ addAttributes _ (WrapRenderer t) = WrapRenderer t
28
+
29
+ instance Semigroup WrapRenderer where
30
+ WrapRenderer a <> WrapRenderer b = WrapRenderer $ (<>) <$> a <*> b
31
+
32
+ instance Monoid WrapRenderer where
33
+ mempty = WrapRenderer $ pure " "
15
34
16
- -- Define the placeholder
17
- tabPlaceholder :: Text
18
- tabPlaceholder = " \FF FF"
35
+ -- Handle Paragraphs
36
+ instance IsBlock WrapRenderer WrapRenderer where
37
+ paragraph (WrapRenderer content) = WrapRenderer $ do
38
+ width <- asks lineWidth
39
+ wrapped <- content
40
+ if ' \t ' `T.elem` wrapped
41
+ then return $ wrapped <> " \n\n "
42
+ else return $ wrapText defaultWrapSettings width wrapped <> " \n\n "
43
+ plain = id
44
+ thematicBreak = WrapRenderer $ return " ------------------------------\n "
45
+ blockQuote = id -- need to wrap this too
46
+ heading _ = id
47
+ codeBlock _ t = WrapRenderer $ return $ " ```\n " <> t <> " \n ```\n\n "
48
+ rawBlock _ t = WrapRenderer $ return t
49
+ list _ _ items = mconcat items -- wrap this too
50
+ referenceLinkDefinition _ _ = mempty
19
51
20
- -- Preprocess: Replace tabs with placeholder
21
- preprocess :: Text -> Text
22
- preprocess = T. replace " \t " tabPlaceholder
52
+ -- Handle Inline Elements
53
+ instance IsInline WrapRenderer where
54
+ str t = WrapRenderer $ return t
55
+ softBreak = WrapRenderer $ return " "
56
+ lineBreak = WrapRenderer $ return " \n "
57
+ emph (WrapRenderer t) = WrapRenderer $ fmap (" *" <> ) (fmap (<> " *" ) t)
58
+ strong (WrapRenderer t) = WrapRenderer $ fmap (" **" <> ) (fmap (<> " **" ) t)
59
+ code t = WrapRenderer $ return $ " `" <> t <> " `"
60
+ link _ _ (WrapRenderer t) = WrapRenderer t
61
+ image _ _ (WrapRenderer t) = WrapRenderer t
62
+ escapedChar c = WrapRenderer $ return $ T. singleton c
63
+ entity t = WrapRenderer $ return t
64
+ rawInline _ rawContent = WrapRenderer $ return rawContent
23
65
24
- -- Postprocess: Replace placeholder back to tabs
25
- postprocess :: Text -> Text
26
- postprocess = T. replace tabPlaceholder " \t "
66
+ -- Parse and Render Markdown
67
+ wrapMarkdownParagraphs :: Int -> Text -> Either String Text
68
+ wrapMarkdownParagraphs width input =
69
+ case commonmark " source" input of
70
+ Left err -> Left $ show err
71
+ Right (WrapRenderer result) ->
72
+ Right $ runReader result (WrapConfig width)
27
73
28
- -- | Word wrap a Markdown document without converting tabs to spaces
29
74
wrapMarkdown :: Int -> Text -> Text
30
75
wrapMarkdown width input =
31
- let
32
- preprocessedInput = preprocess input
33
-
34
- result = runPure $ do
35
- -- Parse the preprocessed Markdown input into Pandoc AST
36
- pandoc <- readMarkdown def { readerExtensions = pandocExtensions } preprocessedInput
37
-
38
- -- Render the Pandoc AST back to Markdown with wrapping settings
39
- writeMarkdown def
40
- { writerWrapText = WrapAuto
41
- , writerColumns = width
42
- }
43
- pandoc
44
-
45
- wrappedOutput = case result of
46
- Left err -> error $ show err
47
- Right output -> postprocess output
48
- in
49
- wrappedOutput
76
+ case wrapMarkdownParagraphs width input of
77
+ Left err -> " Error: " <> T. pack err
78
+ Right output -> output
0 commit comments