@@ -8,145 +8,105 @@ module Skylighting.Format.Typst (
8
8
) where
9
9
10
10
import Control.Monad (mplus )
11
- import Data.Char (isSpace )
12
11
import Data.List (sort )
13
12
import qualified Data.Map as Map
14
13
import Data.Text (Text )
15
14
import qualified Data.Text as Text
16
15
import Skylighting.Types
17
- import Text.Printf
16
+ import qualified Data.Map as M
17
+ import Data.Maybe (fromMaybe )
18
18
#if !MIN_VERSION_base(4,11,0)
19
19
import Data.Semigroup
20
20
#endif
21
21
22
- formatTypst :: Bool -> [SourceLine ] -> Text
23
- formatTypst inline = Text. intercalate (Text. singleton ' \n ' )
24
- . map (sourceLineToTypst inline)
25
-
26
22
-- | Formats tokens as Typst using custom commands inside
27
23
-- @|@ characters. Assumes that @|@ is defined as a short verbatim
28
24
-- command by the macros produced by 'styleToTypst'.
29
25
-- A @KeywordTok@ is rendered using @\\KeywordTok{..}@, and so on.
30
26
formatTypstInline :: FormatOptions -> [SourceLine ] -> Text
31
- formatTypstInline _opts ls = " \\ VERB|" <> formatTypst True ls <> " |"
27
+ formatTypstInline _opts = Text. intercalate newline . map sourceLineToTypst
28
+
29
+ newline :: Text
30
+ newline = " #EndLine()\n "
32
31
33
- sourceLineToTypst :: Bool -> SourceLine -> Text
34
- sourceLineToTypst inline = mconcat . map ( tokenToTypst inline)
32
+ sourceLineToTypst :: SourceLine -> Text
33
+ sourceLineToTypst = mconcat . map tokenToTypst
35
34
36
- tokenToTypst :: Bool -> Token -> Text
37
- tokenToTypst inline (NormalTok , txt)
38
- | Text. all isSpace txt = escapeTypst inline txt
39
- tokenToTypst inline (toktype, txt) = Text. cons ' \\ '
40
- (Text. pack (show toktype) <> " {" <> escapeTypst inline txt <> " }" )
35
+ tokenToTypst :: Token -> Text
36
+ tokenToTypst (toktype, txt) =
37
+ " #" <> Text. pack (show toktype) <> " (" <> doubleQuoted txt <> " );"
41
38
42
- escapeTypst :: Bool -> Text -> Text
43
- escapeTypst inline = Text. concatMap escapeTypstChar
44
- where escapeTypstChar c =
45
- case c of
46
- ' \\ ' -> " \\ textbackslash{}"
47
- ' {' -> " \\ {"
48
- ' }' -> " \\ }"
49
- ' |' | inline -> " \\ VerbBar{}" -- used in inline verbatim
50
- ' _' -> " \\ _"
51
- ' &' -> " \\ &"
52
- ' %' -> " \\ %"
53
- ' #' -> " \\ #"
54
- ' `' -> " \\ textasciigrave{}"
55
- ' \' ' -> " \\ textquotesingle{}"
56
- ' -' -> " {-}" -- prevent ligatures
57
- ' ~' -> " \\ textasciitilde{}"
58
- ' ^' -> " \\ ^{}"
59
- ' >' -> " \\ textgreater{}"
60
- ' <' -> " \\ textless{}"
61
- _ -> Text. singleton c
39
+ doubleQuoted :: Text -> Text
40
+ doubleQuoted t = " \" " <> escape t <> " \" "
41
+ where
42
+ escape = Text. concatMap escapeChar
43
+ escapeChar ' \\ ' = " \\\\ "
44
+ escapeChar ' "' = " \\\" "
45
+ escapeChar c = Text. singleton c
62
46
63
47
-- Typst
64
48
65
49
-- | Format tokens as a Typst @Highlighting@ environment inside a
66
- -- @Shaded@ environment. @Highlighting@ and @Shaded@ are
67
- -- defined by the macros produced by 'styleToTypst'. @Highlighting@
68
- -- is a verbatim environment using @fancyvrb@; @\\@, @{@, and @}@
69
- -- have their normal meanings inside this environment, so that
70
- -- formatting commands work. @Shaded@ is either nothing
71
- -- (if the style's background color is default) or a @snugshade@
72
- -- environment from @framed@, providing a background color
73
- -- for the whole code block, even if it spans multiple pages.
50
+ -- Skylighting block that can be styled. @Skylighting@ is
51
+ -- defined by the macros produced by 'styleToTypst'.
74
52
formatTypstBlock :: FormatOptions -> [SourceLine ] -> Text
75
- formatTypstBlock opts ls = Text. unlines
76
- [" \\ begin{Shaded}"
77
- ," \\ begin{Highlighting}[" <>
78
- (if numberLines opts
79
- then " numbers=left," <>
80
- (if startNumber opts == 1
81
- then " "
82
- else " ,firstnumber=" <>
83
- Text. pack (show (startNumber opts))) <> " ,"
84
- else Text. empty) <> " ]"
85
- ,formatTypst False ls
86
- ," \\ end{Highlighting}"
87
- ," \\ end{Shaded}" ]
53
+ formatTypstBlock opts ls =
54
+ " #Skylighting(" <>
55
+ (if numberLines opts
56
+ then " number: true, start: " <> Text. pack (show (startNumber opts)) <> " , "
57
+ else " " ) <>
58
+ " (" <> -- an array
59
+ Text. intercalate " \n " (map (\ ln -> " [" <> formatTypstInline opts [ln] <> " ]," ) ls)
60
+ <> " ));"
88
61
89
62
-- | Converts a 'Style' to a set of Typst macro definitions,
90
63
-- which should be placed in the document's preamble.
91
- -- Note: default Typst setup doesn't allow boldface typewriter font.
92
- -- To make boldface work in styles, you need to use a different typewriter
93
- -- font. This will work for computer modern:
94
- --
95
- -- > \DeclareFontShape{OT1}{cmtt}{bx}{n}{<5><6><7><8><9><10><10.95><12><14.4><17.28><20.74><24.88>cmttb10}{}
96
- --
97
- -- Or, with xelatex:
98
- --
99
- -- > \usepackage{fontspec}
100
- -- > \setmainfont[SmallCapsFont={* Caps}]{Latin Modern Roman}
101
- -- > \setsansfont{Latin Modern Sans}
102
- -- > \setmonofont[SmallCapsFont={Latin Modern Mono Caps}]{Latin Modern Mono Light}
103
- --
104
64
styleToTypst :: Style -> Text
105
- styleToTypst f = Text. unlines $
106
- [ " \\ usepackage{color}"
107
- , " \\ usepackage{fancyvrb}"
108
- , " \\ newcommand{\\ VerbBar}{|}"
109
- , " \\ newcommand{\\ VERB}{\\ Verb[commandchars=\\\\\\ {\\ }]}"
110
- , " \\ DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\\\\ {\\ }}"
111
- , " % Add ',fontsize=\\ small' for more characters per line"
112
- ] ++
113
- (case backgroundColor f of
114
- Nothing -> [" \\ newenvironment{Shaded}{}{}" ]
115
- Just (RGB r g b) -> [" \\ usepackage{framed}"
116
- ,Text. pack
117
- (printf " \\ definecolor{shadecolor}{RGB}{%d,%d,%d}" r g b)
118
- ," \\ newenvironment{Shaded}{\\ begin{snugshade}}{\\ end{snugshade}}" ])
119
- ++ sort (map (macrodef (defaultColor f) (Map. toList (tokenStyles f)))
120
- (enumFromTo KeywordTok NormalTok ))
65
+ styleToTypst f =
66
+ Text. unlines $
67
+ [ " /* Function definitions for syntax highlighting generated by skylighting: */"
68
+ , " #let EndLine() = raw(\"\\ n\" )"
69
+ , " #let Skylighting(fill: none, number: false, start: 1, sourcelines) = {"
70
+ , " let blocks = []"
71
+ , " let lnum = start - 1"
72
+ , " let bgcolor = " <> maybe " none" toTypstColor (backgroundColor f)
73
+ , " for ln in sourcelines {"
74
+ , " if number {"
75
+ , " lnum = lnum + 1"
76
+ , " blocks = blocks + box(width: if start + sourcelines.len() > 999 { 30pt } else { 24pt }, text(" <> lineNumberFill <> " [ #lnum ]))"
77
+ , " }"
78
+ , " blocks = blocks + ln + EndLine()"
79
+ , " }"
80
+ , " block(fill: bgcolor, blocks)"
81
+ , " }"
82
+ ] <>
83
+ sort (map (macrodef (defaultColor f) (Map. toList (tokenStyles f)))
84
+ (enumFromTo KeywordTok NormalTok ))
85
+ where
86
+ toTypstColor c = " rgb(" <> Text. pack (show (fromColor c :: String )) <> " )"
87
+ lineNumberFill = case lineNumberColor f of
88
+ Nothing -> " "
89
+ Just c -> " fill: " <> toTypstColor c <> " , "
121
90
122
91
macrodef :: Maybe Color -> [(TokenType , TokenStyle )] -> TokenType -> Text
123
- macrodef defaultcol tokstyles tokt = " \\ newcommand{\\ "
124
- <> Text. pack (show tokt)
125
- <> " }[1]{"
126
- <> Text. pack (co . ul . bf . it . bg $ " #1" )
127
- <> " }"
128
- where tokf = case lookup tokt tokstyles of
129
- Nothing -> defStyle
130
- Just x -> x
131
- ul x = if tokenUnderline tokf
132
- then " \\ underline{" <> x <> " }"
133
- else x
134
- it x = if tokenItalic tokf
135
- then " \\ textit{" <> x <> " }"
136
- else x
137
- bf x = if tokenBold tokf
138
- then " \\ textbf{" <> x <> " }"
139
- else x
140
- bcol = fromColor `fmap` tokenBackground tokf
141
- :: Maybe (Double , Double , Double )
142
- bg x = case bcol of
143
- Nothing -> x
144
- Just (r, g, b) ->
145
- printf " \\ colorbox[rgb]{%0.2f,%0.2f,%0.2f}{%s}" r g b x
146
- col = fromColor `fmap` (tokenColor tokf `mplus` defaultcol)
147
- :: Maybe (Double , Double , Double )
148
- co x = case col of
149
- Nothing -> x
150
- Just (r, g, b) ->
151
- printf " \\ textcolor[rgb]{%0.2f,%0.2f,%0.2f}{%s}" r g b x
152
-
92
+ macrodef defaultcol tokstyles' tokt =
93
+ " #let " <> Text. pack (show tokt) <> " (s) = " <> (ul . bg . textstyle) (" raw(s)" )
94
+ where tokstyles = M. fromList tokstyles'
95
+ tokf = fromMaybe defStyle $ M. lookup tokt tokstyles
96
+ ul x = if tokenUnderline tokf
97
+ then " underline(" <> x <> " )"
98
+ else x
99
+ bg x = case tokenBackground tokf of
100
+ Nothing -> x
101
+ Just _c -> x -- TODO?
102
+ textstyle x = " text(" <> bf x <> it x <> co x <> x <> " )"
103
+ it x = if tokenItalic tokf
104
+ then " style: \" italic\" ,"
105
+ else " "
106
+ bf x = if tokenBold tokf
107
+ then " weight: \" bold\" ,"
108
+ else " "
109
+ co x = case tokenColor tokf `mplus` defaultcol of
110
+ Just c -> " fill: rgb(" <>
111
+ Text. pack (show (fromColor c :: String )) <> " ),"
112
+ Nothing -> " "
0 commit comments