Refactored syntax highlight to support multiple languages
This commit is contained in:
parent
6011b620f9
commit
284597cdeb
@ -25,6 +25,9 @@ val lightTheme = ColorsScheme(
|
||||
hoverScrollbar = Color(0xFF0070D8),
|
||||
diffLineAdded = Color(0xAAd7ebd0),
|
||||
diffLineRemoved = Color(0xAAf0d4d4),
|
||||
diffKeyword = Color(0xff3b83cf),
|
||||
diffAnnotation = Color(0xff9f9927),
|
||||
diffComment = Color(0xff0d9141),
|
||||
isLight = true,
|
||||
)
|
||||
|
||||
@ -52,6 +55,9 @@ val darkBlueTheme = ColorsScheme(
|
||||
hoverScrollbar = Color(0xFFCCCCCC),
|
||||
diffLineAdded = Color(0xAA566f5a),
|
||||
diffLineRemoved = Color(0xAA6f585e),
|
||||
diffKeyword = Color(0xFF90c0f0),
|
||||
diffAnnotation = Color(0xFFB3AE5F),
|
||||
diffComment = Color(0xFF70C290),
|
||||
isLight = false,
|
||||
)
|
||||
|
||||
@ -78,5 +84,8 @@ val darkGrayTheme = ColorsScheme(
|
||||
hoverScrollbar = Color(0xFFCCCCCC),
|
||||
diffLineAdded = Color(0xAA5b7059),
|
||||
diffLineRemoved = Color(0xAA74595c),
|
||||
diffKeyword = Color(0xFF90c0f0),
|
||||
diffAnnotation = Color(0xFFB3AE5F),
|
||||
diffComment = Color(0xFF70C290),
|
||||
isLight = false,
|
||||
)
|
@ -38,6 +38,9 @@ data class ColorsScheme(
|
||||
val hoverScrollbar: Color,
|
||||
val diffLineAdded: Color,
|
||||
val diffLineRemoved: Color,
|
||||
val diffKeyword: Color,
|
||||
val diffAnnotation: Color,
|
||||
val diffComment: Color,
|
||||
val isLight: Boolean,
|
||||
) {
|
||||
fun toComposeColors(): Colors {
|
||||
|
@ -123,6 +123,15 @@ val Colors.diffLineAdded: Color
|
||||
val Colors.diffLineRemoved: Color
|
||||
get() = appTheme.diffLineRemoved
|
||||
|
||||
val Colors.diffKeyword: Color
|
||||
get() = appTheme.diffKeyword
|
||||
|
||||
val Colors.diffAnnotation: Color
|
||||
get() = appTheme.diffAnnotation
|
||||
|
||||
val Colors.diffComment: Color
|
||||
get() = appTheme.diffComment
|
||||
|
||||
val Colors.isDark: Boolean
|
||||
get() = !this.isLight
|
||||
|
||||
|
@ -57,6 +57,7 @@ import com.jetpackduba.gitnuro.ui.components.tooltip.DelayedTooltip
|
||||
import com.jetpackduba.gitnuro.ui.context_menu.ContextMenu
|
||||
import com.jetpackduba.gitnuro.ui.context_menu.ContextMenuElement
|
||||
import com.jetpackduba.gitnuro.ui.context_menu.SelectionAwareTextContextMenu
|
||||
import com.jetpackduba.gitnuro.ui.diff.syntax_highlighter.getSyntaxHighlighterFromExtension
|
||||
import com.jetpackduba.gitnuro.viewmodels.DiffViewModel
|
||||
import com.jetpackduba.gitnuro.viewmodels.TextDiffType
|
||||
import com.jetpackduba.gitnuro.viewmodels.ViewDiffResult
|
||||
@ -1137,6 +1138,8 @@ fun SplitDiffLine(
|
||||
@Composable
|
||||
fun DiffLineText(line: Line, diffType: DiffType, onActionTriggered: () -> Unit) {
|
||||
val fileExtension = diffType.filePath.split(".").lastOrNull()
|
||||
val syntaxHighlighter = getSyntaxHighlighterFromExtension(fileExtension)
|
||||
|
||||
val text = line.text
|
||||
val hoverInteraction = remember { MutableInteractionSource() }
|
||||
val isHovered by hoverInteraction.collectIsHoveredAsState()
|
||||
@ -1171,7 +1174,7 @@ fun DiffLineText(line: Line, diffType: DiffType, onActionTriggered: () -> Unit)
|
||||
|
||||
Row {
|
||||
Text(
|
||||
text = syntaxHighlight(fileExtension, text),
|
||||
text = syntaxHighlighter.syntaxHighlight(text),
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.fillMaxSize(),
|
||||
@ -1197,140 +1200,6 @@ fun DiffLineText(line: Line, diffType: DiffType, onActionTriggered: () -> Unit)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun syntaxHighlight(fileExtension: String?, text: String): AnnotatedString {
|
||||
val cleanText = text.replace(
|
||||
"\t",
|
||||
" "
|
||||
).removeLineDelimiters()
|
||||
|
||||
return if (cleanText.trimStart().startsWith("//")) {
|
||||
AnnotatedString(cleanText, spanStyle = SpanStyle(color = Color(0xFF70C290)))
|
||||
} else {
|
||||
val words = cleanText.split(" ")
|
||||
|
||||
val builder = AnnotatedString.Builder()
|
||||
val keywords = listOf(
|
||||
"as",
|
||||
"as?",
|
||||
"break",
|
||||
"by",
|
||||
"catch",
|
||||
"class",
|
||||
"constructor",
|
||||
"continue",
|
||||
"do",
|
||||
"dynamic",
|
||||
"else",
|
||||
"false",
|
||||
"finally",
|
||||
"for",
|
||||
"fun",
|
||||
"if",
|
||||
"import",
|
||||
"in",
|
||||
"!in",
|
||||
"interface",
|
||||
"is",
|
||||
"!is",
|
||||
"null",
|
||||
"object",
|
||||
"package",
|
||||
"return",
|
||||
"super",
|
||||
"this",
|
||||
"throw",
|
||||
"true",
|
||||
"try",
|
||||
"val",
|
||||
"var",
|
||||
"when",
|
||||
"where",
|
||||
"while",
|
||||
|
||||
// Modifiers
|
||||
"actual",
|
||||
"abstract",
|
||||
"annotation",
|
||||
"companion",
|
||||
"const",
|
||||
"crossinline",
|
||||
"data",
|
||||
"enum",
|
||||
"expect",
|
||||
"external",
|
||||
"final",
|
||||
"infix",
|
||||
"inline",
|
||||
"inner",
|
||||
"internal",
|
||||
"lateinit",
|
||||
"noinline",
|
||||
"open",
|
||||
"operator",
|
||||
"out",
|
||||
"override",
|
||||
"private",
|
||||
"protected",
|
||||
"public",
|
||||
"reified",
|
||||
"sealed",
|
||||
"suspend",
|
||||
"tailrec",
|
||||
"vararg",
|
||||
)
|
||||
|
||||
fun isAnnotation(word: String): Boolean = word.startsWith("@")
|
||||
|
||||
words.forEachIndexed { index, word ->
|
||||
if (keywords.contains(word)) {
|
||||
builder.append(
|
||||
AnnotatedString(
|
||||
word,
|
||||
spanStyle = SpanStyle(
|
||||
// color = Color(0xFF669ACD)
|
||||
color = Color(0xFF90c0f0)
|
||||
)
|
||||
)
|
||||
)
|
||||
} else if (isAnnotation(word)) {
|
||||
builder.append(
|
||||
AnnotatedString(
|
||||
word,
|
||||
spanStyle = SpanStyle(
|
||||
color = Color(0xFFB3AE5F)
|
||||
)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
builder.append(word)
|
||||
}
|
||||
|
||||
if (index < words.lastIndex) {
|
||||
builder.append(" ")
|
||||
}
|
||||
}
|
||||
|
||||
builder.toAnnotatedString()
|
||||
}
|
||||
}
|
||||
|
||||
class Testtt {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
external fun test1()
|
||||
}
|
||||
}
|
||||
//
|
||||
//class Pancakes private constructor(val pointer: Long) {
|
||||
// constructor(message: String, num: Int) : this(initialize(message, num))
|
||||
// companion object {
|
||||
// fun initialize(message: String, num: Int): Long {
|
||||
// return 0L
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
@Composable
|
||||
fun LineNumber(text: String, remarked: Boolean) {
|
||||
Text(
|
||||
|
@ -0,0 +1,7 @@
|
||||
package com.jetpackduba.gitnuro.ui.diff.syntax_highlighter
|
||||
|
||||
class DefaultSyntaxHighlighter : SyntaxHighlighter() {
|
||||
override fun loadKeywords(): List<String> = emptyList()
|
||||
override fun isAnnotation(word: String): Boolean = false
|
||||
override fun isComment(line: String): Boolean = false
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package com.jetpackduba.gitnuro.ui.diff.syntax_highlighter
|
||||
|
||||
class KotlinSyntaxHighlighter : SyntaxHighlighter() {
|
||||
override fun loadKeywords(): List<String> = listOf(
|
||||
"as",
|
||||
"as?",
|
||||
"break",
|
||||
"by",
|
||||
"catch",
|
||||
"class",
|
||||
"constructor",
|
||||
"continue",
|
||||
"do",
|
||||
"dynamic",
|
||||
"else",
|
||||
"false",
|
||||
"finally",
|
||||
"for",
|
||||
"fun",
|
||||
"if",
|
||||
"import",
|
||||
"in",
|
||||
"!in",
|
||||
"interface",
|
||||
"is",
|
||||
"!is",
|
||||
"null",
|
||||
"object",
|
||||
"package",
|
||||
"return",
|
||||
"super",
|
||||
"this",
|
||||
"throw",
|
||||
"true",
|
||||
"try",
|
||||
"val",
|
||||
"var",
|
||||
"when",
|
||||
"where",
|
||||
"while",
|
||||
"actual",
|
||||
"abstract",
|
||||
"annotation",
|
||||
"companion",
|
||||
"const",
|
||||
"crossinline",
|
||||
"data",
|
||||
"enum",
|
||||
"expect",
|
||||
"external",
|
||||
"final",
|
||||
"infix",
|
||||
"inline",
|
||||
"inner",
|
||||
"internal",
|
||||
"lateinit",
|
||||
"noinline",
|
||||
"open",
|
||||
"operator",
|
||||
"out",
|
||||
"override",
|
||||
"private",
|
||||
"protected",
|
||||
"public",
|
||||
"reified",
|
||||
"sealed",
|
||||
"suspend",
|
||||
"tailrec",
|
||||
"vararg",
|
||||
)
|
||||
|
||||
override fun isAnnotation(word: String): Boolean = word.startsWith("@")
|
||||
override fun isComment(line: String): Boolean = line.startsWith("//")
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.jetpackduba.gitnuro.ui.diff.syntax_highlighter
|
||||
|
||||
class RustSyntaxHighlighter : SyntaxHighlighter() {
|
||||
override fun loadKeywords(): List<String> = listOf(
|
||||
"as",
|
||||
"async",
|
||||
"await",
|
||||
"break",
|
||||
"const",
|
||||
"continue",
|
||||
"crate",
|
||||
"dyn",
|
||||
"else",
|
||||
"enum",
|
||||
"extern",
|
||||
"false",
|
||||
"fn",
|
||||
"for",
|
||||
"if",
|
||||
"impl",
|
||||
"in",
|
||||
"let",
|
||||
"loop",
|
||||
"match",
|
||||
"mod",
|
||||
"move",
|
||||
"mut",
|
||||
"pub",
|
||||
"ref",
|
||||
"return",
|
||||
"Self",
|
||||
"self",
|
||||
"static",
|
||||
"struct",
|
||||
"super",
|
||||
"trait",
|
||||
"true",
|
||||
"type",
|
||||
"union",
|
||||
"unsafe",
|
||||
"use",
|
||||
"where",
|
||||
"while",
|
||||
)
|
||||
|
||||
override fun isAnnotation(word: String): Boolean = word.startsWith("#[") && word.endsWith("]")
|
||||
|
||||
override fun isComment(line: String): Boolean = line.startsWith("//")
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package com.jetpackduba.gitnuro.ui.diff.syntax_highlighter
|
||||
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import com.jetpackduba.gitnuro.extensions.removeLineDelimiters
|
||||
import com.jetpackduba.gitnuro.theme.diffAnnotation
|
||||
import com.jetpackduba.gitnuro.theme.diffComment
|
||||
import com.jetpackduba.gitnuro.theme.diffKeyword
|
||||
|
||||
abstract class SyntaxHighlighter {
|
||||
private val keywords: List<String> by lazy {
|
||||
loadKeywords()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun syntaxHighlight(text: String): AnnotatedString {
|
||||
val cleanText = text.replace(
|
||||
"\t",
|
||||
" "
|
||||
).removeLineDelimiters()
|
||||
|
||||
return if (isComment(cleanText.trimStart())) {
|
||||
AnnotatedString(cleanText, spanStyle = SpanStyle(color = MaterialTheme.colors.diffComment))
|
||||
} else {
|
||||
val words = cleanText.split(" ")
|
||||
|
||||
val builder = AnnotatedString.Builder()
|
||||
|
||||
words.forEachIndexed { index, word ->
|
||||
if (keywords.contains(word)) {
|
||||
builder.append(
|
||||
AnnotatedString(
|
||||
word,
|
||||
spanStyle = SpanStyle(
|
||||
color = MaterialTheme.colors.diffKeyword
|
||||
)
|
||||
)
|
||||
)
|
||||
} else if (isAnnotation(word)) {
|
||||
builder.append(
|
||||
AnnotatedString(
|
||||
word,
|
||||
spanStyle = SpanStyle(
|
||||
color = MaterialTheme.colors.diffAnnotation
|
||||
)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
builder.append(word)
|
||||
}
|
||||
|
||||
if (index < words.lastIndex) {
|
||||
builder.append(" ")
|
||||
}
|
||||
}
|
||||
|
||||
builder.toAnnotatedString()
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun isAnnotation(word: String): Boolean
|
||||
abstract fun isComment(line: String): Boolean
|
||||
abstract fun loadKeywords(): List<String>
|
||||
}
|
||||
|
||||
fun getSyntaxHighlighterFromExtension(extension: String?): SyntaxHighlighter {
|
||||
val matchingHighlightLanguage = HighlightLanguagesSupported.entries.firstOrNull { language ->
|
||||
language.extensions.contains(extension)
|
||||
}
|
||||
|
||||
return matchingHighlightLanguage?.highlighter?.invoke() ?: DefaultSyntaxHighlighter()
|
||||
}
|
||||
|
||||
private enum class HighlightLanguagesSupported(val extensions: List<String>, val highlighter: () -> SyntaxHighlighter) {
|
||||
Kotlin(listOf("kt", "kts"), { KotlinSyntaxHighlighter() }),
|
||||
Rust(listOf("rs"), { RustSyntaxHighlighter() }),
|
||||
TypeScript(listOf("js", "jsx", "ts", "tsx", "vue", "astro"), { TypeScriptSyntaxHighlighter() }),
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package com.jetpackduba.gitnuro.ui.diff.syntax_highlighter
|
||||
|
||||
class TypeScriptSyntaxHighlighter : SyntaxHighlighter() {
|
||||
override fun loadKeywords(): List<String> = listOf(
|
||||
"break",
|
||||
"case",
|
||||
"catch",
|
||||
"class",
|
||||
"const",
|
||||
"continue",
|
||||
"debugger",
|
||||
"default",
|
||||
"delete",
|
||||
"do",
|
||||
"else",
|
||||
"enum",
|
||||
"export",
|
||||
"extends",
|
||||
"false",
|
||||
"finally",
|
||||
"for",
|
||||
"function",
|
||||
"if",
|
||||
"import",
|
||||
"in",
|
||||
"instanceof",
|
||||
"new",
|
||||
"null",
|
||||
"return",
|
||||
"super",
|
||||
"switch",
|
||||
"this",
|
||||
"throw",
|
||||
"true",
|
||||
"try",
|
||||
"typeof",
|
||||
"var",
|
||||
"void",
|
||||
"while",
|
||||
"with",
|
||||
"as",
|
||||
"implements",
|
||||
"interface",
|
||||
"let",
|
||||
"package",
|
||||
"private",
|
||||
"protected",
|
||||
"public",
|
||||
"static",
|
||||
"yield",
|
||||
"any",
|
||||
"boolean",
|
||||
"constructor",
|
||||
"declare",
|
||||
"get",
|
||||
"module",
|
||||
"require",
|
||||
"number",
|
||||
"set",
|
||||
"string",
|
||||
"symbol",
|
||||
"type",
|
||||
"from",
|
||||
"of",
|
||||
)
|
||||
|
||||
override fun isAnnotation(word: String): Boolean = word.startsWith("@")
|
||||
override fun isComment(line: String): Boolean = line.startsWith("//")
|
||||
}
|
Loading…
Reference in New Issue
Block a user