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),
|
hoverScrollbar = Color(0xFF0070D8),
|
||||||
diffLineAdded = Color(0xAAd7ebd0),
|
diffLineAdded = Color(0xAAd7ebd0),
|
||||||
diffLineRemoved = Color(0xAAf0d4d4),
|
diffLineRemoved = Color(0xAAf0d4d4),
|
||||||
|
diffKeyword = Color(0xff3b83cf),
|
||||||
|
diffAnnotation = Color(0xff9f9927),
|
||||||
|
diffComment = Color(0xff0d9141),
|
||||||
isLight = true,
|
isLight = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -52,6 +55,9 @@ val darkBlueTheme = ColorsScheme(
|
|||||||
hoverScrollbar = Color(0xFFCCCCCC),
|
hoverScrollbar = Color(0xFFCCCCCC),
|
||||||
diffLineAdded = Color(0xAA566f5a),
|
diffLineAdded = Color(0xAA566f5a),
|
||||||
diffLineRemoved = Color(0xAA6f585e),
|
diffLineRemoved = Color(0xAA6f585e),
|
||||||
|
diffKeyword = Color(0xFF90c0f0),
|
||||||
|
diffAnnotation = Color(0xFFB3AE5F),
|
||||||
|
diffComment = Color(0xFF70C290),
|
||||||
isLight = false,
|
isLight = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -78,5 +84,8 @@ val darkGrayTheme = ColorsScheme(
|
|||||||
hoverScrollbar = Color(0xFFCCCCCC),
|
hoverScrollbar = Color(0xFFCCCCCC),
|
||||||
diffLineAdded = Color(0xAA5b7059),
|
diffLineAdded = Color(0xAA5b7059),
|
||||||
diffLineRemoved = Color(0xAA74595c),
|
diffLineRemoved = Color(0xAA74595c),
|
||||||
|
diffKeyword = Color(0xFF90c0f0),
|
||||||
|
diffAnnotation = Color(0xFFB3AE5F),
|
||||||
|
diffComment = Color(0xFF70C290),
|
||||||
isLight = false,
|
isLight = false,
|
||||||
)
|
)
|
@ -38,6 +38,9 @@ data class ColorsScheme(
|
|||||||
val hoverScrollbar: Color,
|
val hoverScrollbar: Color,
|
||||||
val diffLineAdded: Color,
|
val diffLineAdded: Color,
|
||||||
val diffLineRemoved: Color,
|
val diffLineRemoved: Color,
|
||||||
|
val diffKeyword: Color,
|
||||||
|
val diffAnnotation: Color,
|
||||||
|
val diffComment: Color,
|
||||||
val isLight: Boolean,
|
val isLight: Boolean,
|
||||||
) {
|
) {
|
||||||
fun toComposeColors(): Colors {
|
fun toComposeColors(): Colors {
|
||||||
|
@ -123,6 +123,15 @@ val Colors.diffLineAdded: Color
|
|||||||
val Colors.diffLineRemoved: Color
|
val Colors.diffLineRemoved: Color
|
||||||
get() = appTheme.diffLineRemoved
|
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
|
val Colors.isDark: Boolean
|
||||||
get() = !this.isLight
|
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.ContextMenu
|
||||||
import com.jetpackduba.gitnuro.ui.context_menu.ContextMenuElement
|
import com.jetpackduba.gitnuro.ui.context_menu.ContextMenuElement
|
||||||
import com.jetpackduba.gitnuro.ui.context_menu.SelectionAwareTextContextMenu
|
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.DiffViewModel
|
||||||
import com.jetpackduba.gitnuro.viewmodels.TextDiffType
|
import com.jetpackduba.gitnuro.viewmodels.TextDiffType
|
||||||
import com.jetpackduba.gitnuro.viewmodels.ViewDiffResult
|
import com.jetpackduba.gitnuro.viewmodels.ViewDiffResult
|
||||||
@ -1137,6 +1138,8 @@ fun SplitDiffLine(
|
|||||||
@Composable
|
@Composable
|
||||||
fun DiffLineText(line: Line, diffType: DiffType, onActionTriggered: () -> Unit) {
|
fun DiffLineText(line: Line, diffType: DiffType, onActionTriggered: () -> Unit) {
|
||||||
val fileExtension = diffType.filePath.split(".").lastOrNull()
|
val fileExtension = diffType.filePath.split(".").lastOrNull()
|
||||||
|
val syntaxHighlighter = getSyntaxHighlighterFromExtension(fileExtension)
|
||||||
|
|
||||||
val text = line.text
|
val text = line.text
|
||||||
val hoverInteraction = remember { MutableInteractionSource() }
|
val hoverInteraction = remember { MutableInteractionSource() }
|
||||||
val isHovered by hoverInteraction.collectIsHoveredAsState()
|
val isHovered by hoverInteraction.collectIsHoveredAsState()
|
||||||
@ -1171,7 +1174,7 @@ fun DiffLineText(line: Line, diffType: DiffType, onActionTriggered: () -> Unit)
|
|||||||
|
|
||||||
Row {
|
Row {
|
||||||
Text(
|
Text(
|
||||||
text = syntaxHighlight(fileExtension, text),
|
text = syntaxHighlighter.syntaxHighlight(text),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(start = 16.dp)
|
.padding(start = 16.dp)
|
||||||
.fillMaxSize(),
|
.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
|
@Composable
|
||||||
fun LineNumber(text: String, remarked: Boolean) {
|
fun LineNumber(text: String, remarked: Boolean) {
|
||||||
Text(
|
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