OXYGENHUB
# b610bc6eeab7 oxygenhub-android / app / src / main / java / io / oxygenhub / android / ui / code / Highlight.kt
2.9 KiB · last changed A code browser: files at any bookmark or tag, highlighted, the README rendered, history and changes (refs #3) 10 days ago source blame history raw
1
package io.oxygenhub.android.ui.code
2
3
import androidx.compose.ui.graphics.Color
4
import androidx.compose.ui.text.AnnotatedString
5
import androidx.compose.ui.text.SpanStyle
6
import androidx.compose.ui.text.font.FontWeight
7
import dev.snipme.highlights.Highlights
8
import dev.snipme.highlights.model.BoldHighlight
9
import dev.snipme.highlights.model.ColorHighlight
10
import dev.snipme.highlights.model.SyntaxLanguage
11
import dev.snipme.highlights.model.SyntaxThemes
12
13
/** The language to highlight a file as, from its name; plain text when unknown. */
14
fun languageOf(path: String): SyntaxLanguage {
15
    val name = path.substringAfterLast('/').lowercase()
16
    return when (name.substringAfterLast('.', "")) {
17
        "rs" -> SyntaxLanguage.RUST
18
        "kt", "kts" -> SyntaxLanguage.KOTLIN
19
        "java" -> SyntaxLanguage.JAVA
20
        "js", "mjs", "cjs", "jsx" -> SyntaxLanguage.JAVASCRIPT
21
        "ts", "tsx" -> SyntaxLanguage.TYPESCRIPT
22
        "py" -> SyntaxLanguage.PYTHON
23
        "sh", "bash", "zsh" -> SyntaxLanguage.SHELL
24
        "c", "h" -> SyntaxLanguage.C
25
        "cc", "cpp", "cxx", "hpp", "hh" -> SyntaxLanguage.CPP
26
        "go" -> SyntaxLanguage.GO
27
        "rb" -> SyntaxLanguage.RUBY
28
        "swift" -> SyntaxLanguage.SWIFT
29
        "dart" -> SyntaxLanguage.DART
30
        "cs" -> SyntaxLanguage.CSHARP
31
        "php" -> SyntaxLanguage.PHP
32
        "pl", "pm" -> SyntaxLanguage.PERL
33
        else -> if (name == "makefile" || name == "dockerfile") SyntaxLanguage.SHELL else SyntaxLanguage.DEFAULT
34
    }
35
}
36
37
/**
38
 * [code] highlighted for [path], one AnnotatedString per line, so a long
39
 * file is laid out a line at a time. Plain lines when the language is not
40
 * known or the file is too long to highlight quickly.
41
 */
42
fun highlightLines(code: String, path: String, dark: Boolean): List<AnnotatedString> {
43
    val lines = code.split('\n')
44
    val language = languageOf(path)
45
    if (language == SyntaxLanguage.DEFAULT || code.length > MAX_HIGHLIGHT) return lines.map { AnnotatedString(it) }
46
    val whole = AnnotatedString.Builder(code)
47
    val spans = runCatching {
48
        Highlights.Builder().code(code).language(language).theme(SyntaxThemes.default(dark)).build().getHighlights()
49
    }.getOrDefault(emptyList())
50
    for (h in spans) {
51
        val (start, end) = h.location.start to h.location.end
52
        if (start < 0 || end > code.length || start >= end) continue
53
        when (h) {
54
            is ColorHighlight -> whole.addStyle(SpanStyle(color = Color(0xFF000000.toInt() or h.rgb)), start, end)
55
            is BoldHighlight -> whole.addStyle(SpanStyle(fontWeight = FontWeight.Bold), start, end)
56
        }
57
    }
58
    val text = whole.toAnnotatedString()
59
    var at = 0
60
    return lines.map { line ->
61
        val piece = text.subSequence(at, at + line.length)
62
        at += line.length + 1
63
        piece
64
    }
65
}
66
67
/** Beyond this many characters a file is shown plain: highlighting it would stall the screen. */
68
const val MAX_HIGHLIGHT = 200_000