@@ -13,47 +13,32 @@ import java.util.*
1313 */
1414object CodeHighlighter {
1515
16- private val LT_BRACE = " <" .toRegex()
17- private const val LT_REGULAR = " <"
18- private const val LT_TMP = " ^"
19-
20- private val parser = PrettifyParser ()
21-
2216 /* *
2317 * Highlight code content.
2418 *
25- * @param codeLanguage Programming language
26- * @param rawSource Code source by one string
27- * @param colorTheme Color theme (see below)
19+ * @param language Programming language
20+ * @param source Source code as single string
21+ * @param theme Color theme (see below)
2822 * @return Highlighted code, string with necessary inserted color tags
2923 */
30- fun highlight (codeLanguage : String , rawSource : String , colorTheme : ColorThemeData ): String {
31- val source = rawSource.escapeLT()
32- val results = parser.parse(codeLanguage, source)
33- val colorsMap = buildColorsMap(colorTheme)
34- val highlighted = StringBuilder ()
35-
36- results.forEach {
37- val color = colorsMap.getColor(it)
38- val content = parseContent(source, it)
39- highlighted.append(content.withFontParams(color))
40- }
41- return highlighted.toString()
24+ fun highlight (language : String , source : String , theme : ColorThemeData ): String {
25+ val colors = buildColorsMap(theme)
26+
27+ return PrettifyParser ().parse(language, source)
28+ .map { source highlight it applyFontParams colors[it] }
29+ .reduce(String ::plus)
4230 }
4331
4432 // - Helpers
4533
4634 /* *
47- * Parse user input by extracting highlighted content.
35+ * Parse input by extracting highlighted content.
4836 *
49- * @param codeContent Code content
5037 * @param result Syntax unit
51- * @return Parsed content to highlight
38+ * @return Content to highlight
5239 */
53- private fun parseContent (codeContent : String , result : ParseResult ): String {
54- val length = result.offset + result.length
55- val content = codeContent.substring(result.offset, length)
56- return content.expandLT()
40+ private infix fun String.highlight (result : ParseResult ) = safeLT {
41+ substring(result.offset, result.offset + result.length)
5742 }
5843
5944 /* *
@@ -62,7 +47,7 @@ object CodeHighlighter {
6247 * @param result Syntax unit
6348 * @return Color for syntax unit
6449 */
65- private fun HashMap <String , String >.getColor (result : ParseResult ) =
50+ private operator fun HashMap <String , String >.get (result : ParseResult ) =
6651 this [result.styleKeys[0 ]] ? : this [" pln" ]
6752
6853 /* *
@@ -71,31 +56,30 @@ object CodeHighlighter {
7156 * @param colorTheme Color theme
7257 * @return Colors map built from color theme
7358 */
74- private fun buildColorsMap (colorTheme : ColorThemeData ) =
75- object : HashMap <String , String >() {
76- init {
77- val syntaxColors = colorTheme.syntaxColors
78-
79- put(" typ" , syntaxColors.type.hex())
80- put(" kwd" , syntaxColors.keyword.hex())
81- put(" lit" , syntaxColors.literal.hex())
82- put(" com" , syntaxColors.comment.hex())
83- put(" str" , syntaxColors.string.hex())
84- put(" pun" , syntaxColors.punctuation.hex())
85- put(" pln" , syntaxColors.plain.hex())
86- put(" tag" , syntaxColors.tag.hex())
87- put(" dec" , syntaxColors.declaration.hex())
88- put(" src" , syntaxColors.plain.hex())
89- put(" atn" , syntaxColors.attrName.hex())
90- put(" atv" , syntaxColors.attrValue.hex())
91- put(" nocode" , syntaxColors.plain.hex())
92- }
93- }
94-
95- // - Escaping/extracting "lower then" symbol
96-
97- private fun String.escapeLT () = replace(LT_BRACE , LT_TMP )
98- private fun String.expandLT () = replace(LT_TMP , LT_REGULAR )
59+ private fun buildColorsMap (theme : ColorThemeData ): HashMap <String , String > {
60+ fun color (body : SyntaxColors .() -> Int ) =
61+ theme.syntaxColors.let { body(it).hex() }
62+ return hashMapOf(
63+ " typ" to color { type },
64+ " kwd" to color { keyword },
65+ " lit" to color { literal },
66+ " com" to color { comment },
67+ " str" to color { string },
68+ " pun" to color { punctuation },
69+ " pln" to color { plain },
70+ " tag" to color { tag },
71+ " dec" to color { declaration },
72+ " src" to color { plain },
73+ " atn" to color { attrName },
74+ " atv" to color { attrValue },
75+ " nocode" to color { plain })
76+ }
77+
78+ // - Escaping/extracting "less then" symbol
79+
80+ private fun String.safeLT (op : String .() -> String ) = escapeLT().op().expandLT()
81+ private fun String.escapeLT () = replace(" <" , " ^" )
82+ private fun String.expandLT () = replace(" ^" , " <" )
9983}
10084
10185/* *
@@ -222,8 +206,6 @@ enum class Font {
222206
223207// - Helpers
224208
225- const val transparent = " #00000000"
226-
227209/* *
228210 * @return Converted hex int to color by adding alpha-channel
229211 */
@@ -242,50 +224,47 @@ fun Int.hex() = "#${Integer.toHexString(this)}"
242224 * @return Is value equals to found or not condition
243225 */
244226fun Int.isFound () = this >= 0
245-
246227fun Int.notFound () = this == - 1
247228
248229/* *
249230 * Apply font params to string.
250231 *
251- * @param color Color as formatter string
252- * @return Formatted string
232+ * @param color Color
233+ * @return Parametrized string
253234 */
254- fun String.withFontParams (color : String? ): String {
255- val parametrizedString = StringBuilder ()
256-
235+ infix fun String.applyFontParams (color : String? ): String {
236+ var parametrizedString = " "
257237 var idx = 0
258238 var newIdx = indexOf(" \n " )
259239
260240 if (newIdx.notFound()) // covers expected tag coverage (within only one line)
261- parametrizedString.append( inFontTag(color) )
241+ parametrizedString + = inFontTag(color)
262242 else { // may contain multiple lines with line breaks
263243
264244 // put tag on the borders (end & start of line, ..., end of tag)
265245 do { // until closing tag is reached
266- val part = substring(idx .. newIdx - 1 ).inFontTag(color).plus(" \n " )
267- parametrizedString.append(part)
246+ parametrizedString + = (substring(idx .. newIdx - 1 ) inFontTag color) + " \n "
268247
269248 idx = newIdx + 1
270249 newIdx = indexOf(" \n " , idx)
271250 } while (newIdx.isFound())
272251
273252 if (idx != indexOf(" \n " )) // if not replaced only once (for multiline tag coverage)
274- parametrizedString.append( substring(idx). inFontTag( color))
253+ parametrizedString + = substring(idx) inFontTag color
275254 }
276- return parametrizedString.toString()
255+ return parametrizedString
277256}
278257
258+ /* *
259+ * @return String wrapped in font tag
260+ */
261+ private infix fun String.inFontTag (color : String? ) =
262+ " <font color=\" $color \" >${escLineBreakAtStart()} </font>"
263+
279264/* *
280265 * @return String with escaped line break at start
281266 */
282267fun String.escLineBreakAtStart () =
283268 if (startsWith(" \n " ) && length >= 1 )
284269 substring(1 )
285270 else this
286-
287- /* *
288- * @return String surrounded by font tag
289- */
290- fun String.inFontTag (color : String? ) =
291- " <font color=\" $color \" >${escLineBreakAtStart()} </font>"
0 commit comments