Repository navigation
Highlight
Module: com.github.re-ovo.bettertext:highlight, package me.rerere.bettertext.highlight.
A pure Kotlin syntax highlighter: highlight.js 11.11.1's parser and 30 of its grammars, ported to Kotlin. It turns code into an AnnotatedString, which BetterText (or Compose's Text) draws. It has no dependency on the other modules.
@Composable
fun Code(code: String, language: String) {
BetterText(
text = rememberHighlightedCode(code, language),
modifier = Modifier.horizontalScroll(rememberScrollState()),
style = TextStyle(
color = HighlightTextColorPalette.Default.fallback,
fontFamily = FontFamily.Monospace,
fontSize = 13.sp,
fontFeatureSettings = CODE_FONT_FEATURES,
),
)
}@Composable
fun rememberHighlightedCode(
code: String,
language: String,
colors: HighlightTextColorPalette = HighlightTextColorPalette.Default,
): AnnotatedString-
languageis a highlight.js name or alias, case-insensitive (kotlin,kt,py,ts…). An unsupported or empty language returns the code unhighlighted. - Code longer than
MAX_HIGHLIGHTED_LENGTH(4096 characters) is returned unhighlighted, since it's highlighted again on every change, for example while a reply streams in. - Only token colors (and italic comments, bold
strong) are set. Give the text style the palette'sfallbackcolor for plain code, and a monospace font. -
CODE_FONT_FEATURESturns off ligatures, so fonts such as JetBrains Mono show the characters as typed.
It uses the CodeHighlighter from LocalCodeHighlighter, one shared instance by default.
HighlightTextColorPalette maps highlight.js scopes to 15 color slots. Two palettes are built in:
| Palette | For |
|---|---|
HighlightTextColorPalette.AtomOneDark (also Default) |
dark backgrounds, e.g. Color(0xFF282C34)
|
HighlightTextColorPalette.AtomOneLight |
light backgrounds, e.g. Color(0xFFFAFAFA)
|
Pick one by the background:
val background = MaterialTheme.colorScheme.surfaceContainerHigh
val colors = if (background.luminance() < 0.5f) {
HighlightTextColorPalette.AtomOneDark
} else {
HighlightTextColorPalette.AtomOneLight
}
val text = rememberHighlightedCode(code, language, colors)Make your own with copy, or by passing every slot:
val colors = HighlightTextColorPalette.AtomOneDark.copy(
keyword = Color(0xFFFF79C6),
string = Color(0xFFF1FA8C),
)| Slot | Scopes |
|---|---|
keyword |
keyword, doctag, formula
|
string |
string, regexp, addition
|
number |
number, type, selector-class, selector-attr, selector-pseudo
|
comment |
comment, quote (italic) |
function |
title, function, symbol, bullet, link, meta, selector-id
|
operator |
literal, char, operator
|
punctuation |
punctuation, params
|
className |
built_in |
property |
section, name, selector-tag, deletion, subst, property
|
variable |
variable |
tag |
tag |
attrName |
attr, template-variable
|
attrValue |
attribute |
fallback |
emphasis (italic), strong (bold), and any unknown scope |
boolean is part of the palette but no scope uses it at the moment. A tiered scope such as title.function or char.escape falls back to its parent scope when it has no entry of its own, as highlight.js's CSS themes do.
val highlighter = CodeHighlighter()
highlighter.supports("kotlin") // true
val text: AnnotatedString = highlighter.highlightToAnnotatedString(code, "kotlin", colors)
val tokens: List<HighlightToken> = highlighter.highlight(code, "kotlin")highlight returns tokens that concatenate to the code: HighlightToken.Plain(content) and HighlightToken.Styled(content, type), where type is the highlight.js scope (keyword, title.function…). Use them to build your own styling; AnnotatedString.Builder.buildHighlightText(token, colors) appends one token with the palette's style.
Each language is compiled the first time it's used. A CodeHighlighter isn't thread-safe: compiled languages keep matcher state, so call it from one thread at a time, or use one instance per thread. To highlight in the background, give the worker its own instance:
private val backgroundHighlighter = CodeHighlighter()
private val highlightDispatcher = Dispatchers.Default.limitedParallelism(1)
suspend fun highlight(code: String, language: String) = withContext(highlightDispatcher) {
backgroundHighlighter.highlightToAnnotatedString(code, language)
}| Language | Names and aliases |
|---|---|
| Bash |
bash, sh, zsh, shell
|
| C |
c, h
|
| C++ |
cpp, cc, c++, h++, hpp, hh, hxx, cxx
|
| C# |
csharp, cs, c#
|
| CMake |
cmake, cmake.in
|
| CSS | css |
| Dart | dart |
| Diff |
diff, patch
|
| Dockerfile |
dockerfile, docker
|
| GLSL | glsl |
| Go |
go, golang
|
| INI / TOML |
ini, toml
|
| Java |
java, jsp
|
| JavaScript |
javascript, js, jsx, mjs, cjs
|
| JSON |
json, jsonc, json5
|
| Kotlin |
kotlin, kt, kts
|
| LaTeX |
latex, tex
|
| Lua |
lua, pluto
|
| Markdown |
markdown, md, mkdown, mkd
|
| PHP | php |
| PowerShell |
powershell, pwsh, ps, ps1
|
| Properties | properties |
| Python |
python, py, gyp, ipython
|
| Ruby |
ruby, rb, gemspec, podspec, thor, irb
|
| Rust |
rust, rs
|
| SQL | sql |
| Swift | swift |
| TypeScript |
typescript, ts, tsx, mts, cts
|
| XML / HTML |
xml, html, xhtml, rss, atom, xjb, xsd, xsl, plist, wsf, svg
|
| YAML |
yaml, yml
|
Embedded languages are highlighted too where highlight.js does it, such as CSS and JavaScript inside HTML.
Register a code block component that uses the block's language; see Markdown › Code blocks.