Skip to content

Commit 76351d7

Browse files
committed
feat: Add syntax highlight toggle
This commit introduces a new feature that allows users to toggle syntax highlighting on and off. The following changes were made: - Added a `DiffViewMenu` composable that includes a switch to toggle syntax highlighting. - Updated `DiffCheckerScreen` and `DiffViewerScreen` to use the new `DiffViewMenu`. - Modified `InlineCharDiffText`, `TwoSideCharDiffText`, `SeparateCharDiffText`, and `UnifiedCharDiffText` to conditionally apply syntax highlighting based on the `isSyntaxHighlightEnabled` state. - Added `isSyntaxHighlightEnabled` state and a corresponding setter function to `DiffCheckerViewModel`. - Updated `OutlinedTextField` in `DiffCheckerScreen` to conditionally apply syntax highlighting to the input fields.
1 parent b81c9bb commit 76351d7

File tree

8 files changed

+313
-244
lines changed

8 files changed

+313
-244
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package dev.jahidhasanco.diffly.presentation.component
2+
3+
import androidx.compose.foundation.layout.Box
4+
import androidx.compose.foundation.layout.Row
5+
import androidx.compose.foundation.layout.fillMaxWidth
6+
import androidx.compose.foundation.layout.padding
7+
import androidx.compose.material.icons.Icons
8+
import androidx.compose.material.icons.filled.Check
9+
import androidx.compose.material.icons.filled.MoreVert
10+
import androidx.compose.material3.DropdownMenu
11+
import androidx.compose.material3.DropdownMenuItem
12+
import androidx.compose.material3.HorizontalDivider
13+
import androidx.compose.material3.Icon
14+
import androidx.compose.material3.IconButton
15+
import androidx.compose.material3.Switch
16+
import androidx.compose.material3.SwitchDefaults
17+
import androidx.compose.material3.Text
18+
import androidx.compose.runtime.Composable
19+
import androidx.compose.runtime.getValue
20+
import androidx.compose.runtime.mutableStateOf
21+
import androidx.compose.runtime.remember
22+
import androidx.compose.runtime.setValue
23+
import androidx.compose.ui.Alignment
24+
import androidx.compose.ui.Modifier
25+
import androidx.compose.ui.draw.scale
26+
import androidx.compose.ui.graphics.Color
27+
import androidx.compose.ui.unit.dp
28+
import dev.jahidhasanco.diffly.domain.model.DiffViewType
29+
import dev.jahidhasanco.diffly.presentation.theme.background
30+
import dev.jahidhasanco.diffly.presentation.theme.primary
31+
32+
@Composable
33+
fun DiffViewMenu(
34+
selectedViewType: DiffViewType,
35+
onViewTypeSelected: (DiffViewType) -> Unit,
36+
isSyntaxHighlightEnabled: Boolean,
37+
onSyntaxHighlightToggle: (Boolean) -> Unit,
38+
) {
39+
var expanded by remember { mutableStateOf(false) }
40+
41+
Box {
42+
IconButton(onClick = { expanded = !expanded }) {
43+
Icon(
44+
Icons.Default.MoreVert, contentDescription = "Menu"
45+
)
46+
}
47+
48+
DropdownMenu(
49+
containerColor = background,
50+
expanded = expanded,
51+
onDismissRequest = { expanded = false }) {
52+
DropdownMenuItem(
53+
text = { Text("Two Side View") },
54+
trailingIcon = if (selectedViewType == DiffViewType.TWO_SIDE) {
55+
{ Icon(Icons.Default.Check, contentDescription = null) }
56+
} else null,
57+
onClick = {
58+
onViewTypeSelected(DiffViewType.TWO_SIDE)
59+
expanded = false
60+
})
61+
DropdownMenuItem(
62+
text = { Text("Separate View") },
63+
trailingIcon = if (selectedViewType == DiffViewType.SEPARATE) {
64+
{ Icon(Icons.Default.Check, contentDescription = null) }
65+
} else null,
66+
onClick = {
67+
onViewTypeSelected(DiffViewType.SEPARATE)
68+
expanded = false
69+
})
70+
DropdownMenuItem(
71+
text = { Text("Unified View") },
72+
trailingIcon = if (selectedViewType == DiffViewType.UNIFIED) {
73+
{ Icon(Icons.Default.Check, contentDescription = null) }
74+
} else null,
75+
onClick = {
76+
onViewTypeSelected(DiffViewType.UNIFIED)
77+
expanded = false
78+
})
79+
HorizontalDivider()
80+
DropdownMenuItem(text = {
81+
Row(
82+
modifier = Modifier.fillMaxWidth(),
83+
verticalAlignment = Alignment.CenterVertically
84+
) {
85+
86+
Text(
87+
"Syntax Highlight",
88+
modifier = Modifier.padding(end = 2.dp)
89+
)
90+
Switch(
91+
checked = isSyntaxHighlightEnabled,
92+
onCheckedChange = { checked ->
93+
onSyntaxHighlightToggle(checked)
94+
expanded = false
95+
},
96+
modifier = Modifier.scale(0.7f),
97+
colors = SwitchDefaults.colors(
98+
checkedThumbColor = primary,
99+
uncheckedThumbColor = Color.Gray,
100+
checkedTrackColor = primary.copy(alpha = 0.3f),
101+
uncheckedTrackColor = Color.LightGray
102+
)
103+
)
104+
}
105+
}, onClick = {
106+
onSyntaxHighlightToggle(!isSyntaxHighlightEnabled)
107+
expanded = false
108+
})
109+
}
110+
}
111+
}

app/src/main/java/dev/jahidhasanco/diffly/presentation/component/InlineCharDiffText.kt

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,56 +18,77 @@ import dev.jahidhasanco.diffly.presentation.theme.delete
1818

1919
@Composable
2020
fun InlineCharDiffText(
21+
isSyntaxHighlightEnabled: Boolean,
2122
code: String,
2223
charDiffs: List<CharDiff>,
2324
language: CodeLang,
2425
parser: PrettifyParser,
2526
theme: CodeTheme
2627
) {
27-
val syntaxAnnotatedString = remember(code, language, theme) {
28-
parseCodeAsAnnotatedString(parser, theme, language, code)
29-
}
28+
if (isSyntaxHighlightEnabled) {
29+
val syntaxAnnotatedString = remember(code, language, theme) {
30+
parseCodeAsAnnotatedString(parser, theme, language, code)
31+
}
3032

31-
val combinedAnnotatedString = buildAnnotatedString {
32-
for (i in code.indices) {
33-
val char = code[i]
33+
val combinedAnnotatedString = buildAnnotatedString {
34+
for (i in code.indices) {
35+
val char = code[i]
3436

3537

36-
// get all syntax span styles at index i
37-
val spanStylesAtIndex =
38-
syntaxAnnotatedString.spanStyles.filter { it.start <= i && i < it.end }
39-
.map { it.item }
38+
// get all syntax span styles at index i
39+
val spanStylesAtIndex =
40+
syntaxAnnotatedString.spanStyles.filter { it.start <= i && i < it.end }
41+
.map { it.item }
4042

41-
val charStyle = spanStylesAtIndex.fold(SpanStyle()) { acc, style ->
42-
acc.merge(style)
43-
}
43+
val charStyle =
44+
spanStylesAtIndex.fold(SpanStyle()) { acc, style ->
45+
acc.merge(style)
46+
}
4447

45-
val backgroundColor = when (charDiffs.getOrNull(i)?.type) {
46-
CharDiffType.INSERTED -> added.copy(alpha = 0.2f)
47-
CharDiffType.DELETED -> delete.copy(alpha = 0.2f)
48-
CharDiffType.UNCHANGED -> Color.Transparent
49-
else -> Color.Transparent
50-
}
48+
val backgroundColor = when (charDiffs.getOrNull(i)?.type) {
49+
CharDiffType.INSERTED -> added.copy(alpha = 0.2f)
50+
CharDiffType.DELETED -> delete.copy(alpha = 0.2f)
51+
CharDiffType.UNCHANGED -> Color.Transparent
52+
else -> Color.Transparent
53+
}
5154

52-
val finalColor = if (charStyle.color == Color.Unspecified) {
53-
Color.Black
54-
} else {
55-
charStyle.color
56-
}
55+
val finalColor = if (charStyle.color == Color.Unspecified) {
56+
Color.Black
57+
} else {
58+
charStyle.color
59+
}
5760

58-
val mergedStyle = charStyle.merge(
59-
SpanStyle(
60-
color = finalColor,
61-
background = backgroundColor
61+
val mergedStyle = charStyle.merge(
62+
SpanStyle(
63+
color = finalColor,
64+
background = backgroundColor
65+
)
6266
)
63-
)
6467

6568

66-
withStyle(mergedStyle) {
67-
append(char)
69+
withStyle(mergedStyle) {
70+
append(char)
71+
}
6872
}
6973
}
70-
}
7174

72-
Text(text = combinedAnnotatedString)
75+
Text(text = combinedAnnotatedString)
76+
} else {
77+
val annotatedString = buildAnnotatedString {
78+
charDiffs.forEach { cd ->
79+
80+
val backgroundColor = when (cd.type) {
81+
CharDiffType.UNCHANGED -> Color.Unspecified
82+
CharDiffType.INSERTED -> added
83+
CharDiffType.DELETED -> delete
84+
}
85+
withStyle(
86+
SpanStyle(background = backgroundColor)
87+
) {
88+
append(cd.char)
89+
}
90+
}
91+
}
92+
Text(annotatedString)
93+
}
7394
}

app/src/main/java/dev/jahidhasanco/diffly/presentation/component/SeparateCharDiffText.kt

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import dev.jahidhasanco.diffly.presentation.theme.delete
3030

3131
@Composable
3232
fun SeparateCharDiffText(
33+
isSyntaxHighlightEnabled: Boolean,
3334
language: CodeLang,
3435
parser: PrettifyParser,
3536
theme: CodeTheme,
@@ -84,20 +85,28 @@ fun SeparateCharDiffText(
8485
) {
8586
if (!charDiffs.isNullOrEmpty()) {
8687
InlineCharDiffText(
88+
isSyntaxHighlightEnabled,
8789
line,
8890
charDiffs = charDiffs,
8991
language,
9092
parser,
9193
theme
9294
)
9395
} else {
94-
val syntaxAnnotatedString =
95-
remember(line, language, theme) {
96-
parseCodeAsAnnotatedString(
97-
parser, theme, language, line
98-
)
99-
}
100-
Text(syntaxAnnotatedString)
96+
if (isSyntaxHighlightEnabled) {
97+
val syntaxAnnotatedString =
98+
remember(line, language, theme) {
99+
parseCodeAsAnnotatedString(
100+
parser,
101+
theme,
102+
language,
103+
line
104+
)
105+
}
106+
Text(syntaxAnnotatedString)
107+
} else {
108+
Text(line)
109+
}
101110
}
102111
}
103112
}
@@ -151,20 +160,28 @@ fun SeparateCharDiffText(
151160
) {
152161
if (!charDiffs.isNullOrEmpty()) {
153162
InlineCharDiffText(
163+
isSyntaxHighlightEnabled,
154164
line,
155165
charDiffs = charDiffs,
156166
language,
157167
parser,
158168
theme
159169
)
160170
} else {
161-
val syntaxAnnotatedString =
162-
remember(line, language, theme) {
163-
parseCodeAsAnnotatedString(
164-
parser, theme, language, line
165-
)
166-
}
167-
Text(syntaxAnnotatedString)
171+
if (isSyntaxHighlightEnabled) {
172+
val syntaxAnnotatedString =
173+
remember(line, language, theme) {
174+
parseCodeAsAnnotatedString(
175+
parser,
176+
theme,
177+
language,
178+
line
179+
)
180+
}
181+
Text(syntaxAnnotatedString)
182+
} else {
183+
Text(line)
184+
}
168185
}
169186
}
170187
}

app/src/main/java/dev/jahidhasanco/diffly/presentation/component/TwoSIdeCharDiffText.kt

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import dev.jahidhasanco.diffly.presentation.theme.delete
3030

3131
@Composable
3232
fun TwoSideCharDiffText(
33+
isSyntaxHighlightEnabled: Boolean,
3334
language: CodeLang,
3435
parser: PrettifyParser,
3536
theme: CodeTheme,
@@ -82,23 +83,28 @@ fun TwoSideCharDiffText(
8283
) {
8384
if (!charDiffs.isNullOrEmpty()) {
8485
InlineCharDiffText(
86+
isSyntaxHighlightEnabled,
8587
line,
8688
charDiffs = charDiffs,
8789
language,
8890
parser,
8991
theme
9092
)
9193
} else {
92-
val syntaxAnnotatedString =
93-
remember(line, language, theme) {
94-
parseCodeAsAnnotatedString(
95-
parser,
96-
theme,
97-
language,
98-
line
99-
)
100-
}
101-
Text(syntaxAnnotatedString)
94+
if (isSyntaxHighlightEnabled) {
95+
val syntaxAnnotatedString =
96+
remember(line, language, theme) {
97+
parseCodeAsAnnotatedString(
98+
parser,
99+
theme,
100+
language,
101+
line
102+
)
103+
}
104+
Text(syntaxAnnotatedString)
105+
} else {
106+
Text(line)
107+
}
102108
}
103109
}
104110
}
@@ -155,23 +161,28 @@ fun TwoSideCharDiffText(
155161
) {
156162
if (!charDiffs.isNullOrEmpty()) {
157163
InlineCharDiffText(
164+
isSyntaxHighlightEnabled,
158165
line,
159166
charDiffs = charDiffs,
160167
language,
161168
parser,
162169
theme
163170
)
164171
} else {
165-
val syntaxAnnotatedString =
166-
remember(line, language, theme) {
167-
parseCodeAsAnnotatedString(
168-
parser,
169-
theme,
170-
language,
171-
line
172-
)
173-
}
174-
Text(syntaxAnnotatedString)
172+
if (isSyntaxHighlightEnabled) {
173+
val syntaxAnnotatedString =
174+
remember(line, language, theme) {
175+
parseCodeAsAnnotatedString(
176+
parser,
177+
theme,
178+
language,
179+
line
180+
)
181+
}
182+
Text(syntaxAnnotatedString)
183+
} else {
184+
Text(line)
185+
}
175186
}
176187
}
177188
}

0 commit comments

Comments
 (0)