Skip to content

Commit e381a78

Browse files
committed
Refactor: Introduce different diff view types
This commit introduces three different ways to view the differences: Two-Side, Column (Separate), and Unified. Key changes: - Added `DiffViewType.kt` enum to represent the different view options. - Added `DiffCheckerScreen.kt`: - Implemented a dropdown menu in the `TopAppBar` to select the `DiffViewType`. - Real-time diff calculation is now triggered immediately when the "Real Time Diff" switch is enabled. - Conditionally renders different composables based on the `selectedViewType`: - `TwoSideCharDiffText` for `DiffViewType.TWO_SIDE`. - `ColumCharDiffText` for `DiffViewType.SEPARATE`. - `UnifiedCharDiffText` for `DiffViewType.UNIFIED`. - Renamed `CharDiffText.kt` to `TwoSIdeCharDiffText.kt` and updated its composable name to `TwoSideCharDiffText`. - Introduced `ColumCharDiffText.kt`: - Displays original and changed text in two separate `LazyColumn`s stacked vertically. - Shows deletion/addition counts for each section. - Filters character diffs to only show relevant changes for each text section (e.g., no insertions in original, no deletions in changed). - Introduced `UnifiedCharDiffText.kt`: - Displays a unified diff view in a single `LazyColumn`. - Shows line numbers for both old and new text. - Prefixes lines with `+` (added), `-` (deleted), ` ` (unchanged), or `~` (changed). - Renders character-level differences for changed lines using `InlineCharDiffText`. - Tracks and displays old and new line numbers correctly.
1 parent 04475fe commit e381a78

5 files changed

Lines changed: 319 additions & 25 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package dev.jahidhasanco.diffly.domain.model
2+
3+
enum class DiffViewType {
4+
TWO_SIDE, SEPARATE, UNIFIED
5+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package dev.jahidhasanco.diffly.presentation.component
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.layout.Box
5+
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.Spacer
7+
import androidx.compose.foundation.layout.fillMaxSize
8+
import androidx.compose.foundation.layout.fillMaxWidth
9+
import androidx.compose.foundation.layout.height
10+
import androidx.compose.foundation.layout.padding
11+
import androidx.compose.foundation.lazy.LazyColumn
12+
import androidx.compose.foundation.lazy.items
13+
import androidx.compose.material3.MaterialTheme
14+
import androidx.compose.material3.Text
15+
import androidx.compose.runtime.Composable
16+
import androidx.compose.runtime.remember
17+
import androidx.compose.ui.Modifier
18+
import androidx.compose.ui.graphics.Color
19+
import androidx.compose.ui.unit.dp
20+
import dev.jahidhasanco.diffly.domain.model.CharDiffType
21+
import dev.jahidhasanco.diffly.domain.model.DiffEntry
22+
import dev.jahidhasanco.diffly.domain.model.DiffType
23+
import dev.jahidhasanco.diffly.presentation.theme.added
24+
import dev.jahidhasanco.diffly.presentation.theme.delete
25+
26+
@Composable
27+
fun ColumCharDiffText(diffResult: List<DiffEntry>) {
28+
Column(modifier = Modifier.fillMaxSize()) {
29+
30+
// Original Text Column
31+
Text(
32+
"Original Text",
33+
modifier = Modifier.padding(8.dp),
34+
style = MaterialTheme.typography.titleMedium
35+
)
36+
val deletedCount = diffResult.count { it.type == DiffType.CHANGED || it.type == DiffType.DELETED }
37+
if (deletedCount > 0) {
38+
Text(
39+
"-$deletedCount",
40+
modifier = Modifier.padding(start = 8.dp, bottom = 8.dp),
41+
style = MaterialTheme.typography.bodyMedium.copy(
42+
color = delete,
43+
fontWeight = MaterialTheme.typography.titleMedium.fontWeight
44+
)
45+
)
46+
}
47+
48+
LazyColumn(
49+
modifier = Modifier
50+
.fillMaxWidth()
51+
.weight(1f)
52+
) {
53+
items(diffResult) { entry ->
54+
entry.oldLine?.let { line ->
55+
val charDiffs = remember(entry.charDiffs) {
56+
entry.charDiffs?.filter { it.type != CharDiffType.INSERTED }
57+
}
58+
val color = when (entry.type) {
59+
DiffType.ADDED, DiffType.DELETED, DiffType.CHANGED -> delete.copy(alpha = 0.3f)
60+
else -> Color.Unspecified
61+
}
62+
63+
Box(
64+
modifier = Modifier
65+
.fillMaxWidth()
66+
.padding(horizontal = 2.dp, vertical = 1.dp)
67+
.background(color)
68+
) {
69+
if (!charDiffs.isNullOrEmpty()) {
70+
InlineCharDiffText(charDiffs = charDiffs)
71+
} else {
72+
Text(line)
73+
}
74+
}
75+
}
76+
}
77+
}
78+
79+
// Spacer between sections
80+
Spacer(modifier = Modifier.height(8.dp))
81+
82+
// Changed Text Column
83+
Text(
84+
"Changed Text",
85+
modifier = Modifier.padding(8.dp),
86+
style = MaterialTheme.typography.titleMedium
87+
)
88+
val addedCount = diffResult.count { it.type == DiffType.CHANGED || it.type == DiffType.ADDED }
89+
if (addedCount > 0) {
90+
Text(
91+
"+$addedCount",
92+
modifier = Modifier.padding(start = 8.dp, bottom = 8.dp),
93+
style = MaterialTheme.typography.bodyMedium.copy(
94+
color = added,
95+
fontWeight = MaterialTheme.typography.titleMedium.fontWeight
96+
)
97+
)
98+
}
99+
100+
LazyColumn(
101+
modifier = Modifier
102+
.fillMaxWidth()
103+
.weight(1f)
104+
) {
105+
items(diffResult) { entry ->
106+
entry.newLine?.let { line ->
107+
val charDiffs = remember(entry.charDiffs) {
108+
entry.charDiffs?.filter { it.type != CharDiffType.DELETED }
109+
}
110+
val color = when (entry.type) {
111+
DiffType.ADDED, DiffType.DELETED, DiffType.CHANGED -> added.copy(alpha = 0.3f)
112+
else -> Color.Unspecified
113+
}
114+
115+
Box(
116+
modifier = Modifier
117+
.fillMaxWidth()
118+
.padding(horizontal = 2.dp, vertical = 1.dp)
119+
.background(color)
120+
) {
121+
if (!charDiffs.isNullOrEmpty()) {
122+
InlineCharDiffText(charDiffs = charDiffs)
123+
} else {
124+
Text(line)
125+
}
126+
}
127+
}
128+
}
129+
}
130+
}
131+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import dev.jahidhasanco.diffly.presentation.theme.added
2525
import dev.jahidhasanco.diffly.presentation.theme.delete
2626

2727
@Composable
28-
fun CharDiffText(diffResult: List<DiffEntry>) {
28+
fun TwoSideCharDiffText(diffResult: List<DiffEntry>) {
2929
Row(modifier = Modifier.fillMaxSize()) {
3030
LazyColumn(
3131
modifier = Modifier
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package dev.jahidhasanco.diffly.presentation.component
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.layout.Box
5+
import androidx.compose.foundation.layout.Row
6+
import androidx.compose.foundation.layout.fillMaxWidth
7+
import androidx.compose.foundation.layout.padding
8+
import androidx.compose.foundation.layout.width
9+
import androidx.compose.foundation.lazy.LazyColumn
10+
import androidx.compose.foundation.lazy.items
11+
import androidx.compose.material3.MaterialTheme
12+
import androidx.compose.material3.Text
13+
import androidx.compose.runtime.Composable
14+
import androidx.compose.ui.Alignment
15+
import androidx.compose.ui.Modifier
16+
import androidx.compose.ui.graphics.Color
17+
import androidx.compose.ui.unit.dp
18+
import dev.jahidhasanco.diffly.domain.model.DiffEntry
19+
import dev.jahidhasanco.diffly.domain.model.DiffType
20+
import dev.jahidhasanco.diffly.presentation.theme.added
21+
import dev.jahidhasanco.diffly.presentation.theme.delete
22+
23+
@Composable
24+
fun UnifiedCharDiffText(diffResult: List<DiffEntry>) {
25+
var oldLineNumber = 1
26+
var newLineNumber = 1
27+
28+
LazyColumn(
29+
modifier = Modifier
30+
.fillMaxWidth()
31+
) {
32+
item {
33+
Text(
34+
"Unified Diff",
35+
modifier = Modifier.padding(8.dp),
36+
style = MaterialTheme.typography.titleMedium
37+
)
38+
}
39+
40+
items(diffResult) { entry ->
41+
val prefix = when (entry.type) {
42+
DiffType.ADDED -> "+"
43+
DiffType.DELETED -> "-"
44+
DiffType.UNCHANGED -> " "
45+
DiffType.CHANGED -> "~"
46+
}
47+
48+
val line = when (entry.type) {
49+
DiffType.ADDED -> entry.newLine
50+
DiffType.DELETED -> entry.oldLine
51+
DiffType.UNCHANGED -> entry.oldLine
52+
DiffType.CHANGED -> entry.newLine
53+
}
54+
55+
val color = when (entry.type) {
56+
DiffType.ADDED -> added.copy(alpha = 0.3f)
57+
DiffType.DELETED -> delete.copy(alpha = 0.3f)
58+
DiffType.CHANGED -> delete.copy(alpha = 0.2f)
59+
else -> Color.Transparent
60+
}
61+
62+
line?.let {
63+
Box(
64+
modifier = Modifier
65+
.fillMaxWidth()
66+
.padding(horizontal = 2.dp, vertical = 1.dp)
67+
.background(color)
68+
) {
69+
Row(
70+
verticalAlignment = Alignment.Top
71+
) {
72+
// Old line number
73+
Text(
74+
text = entry.oldLine?.let { oldLineNumber.toString() } ?: "",
75+
modifier = Modifier
76+
.width(40.dp)
77+
.padding(end = 2.dp),
78+
color = Color.Gray
79+
)
80+
81+
// New line number
82+
Text(
83+
text = entry.newLine?.let { newLineNumber.toString() } ?: "",
84+
modifier = Modifier
85+
.width(40.dp)
86+
.padding(end = 2.dp),
87+
color = Color.Gray
88+
)
89+
90+
// Prefix
91+
Text(
92+
text = prefix,
93+
modifier = Modifier.padding(end = 4.dp),
94+
color = Color.Gray
95+
)
96+
97+
// Line content or char-level diff
98+
if (entry.type == DiffType.CHANGED && !entry.charDiffs.isNullOrEmpty()) {
99+
InlineCharDiffText(charDiffs = entry.charDiffs)
100+
} else {
101+
Text(
102+
text = it,
103+
color = Color.Black
104+
)
105+
}
106+
}
107+
}
108+
}
109+
110+
when (entry.type) {
111+
DiffType.ADDED -> newLineNumber++
112+
DiffType.DELETED -> oldLineNumber++
113+
DiffType.UNCHANGED, DiffType.CHANGED -> {
114+
oldLineNumber++
115+
newLineNumber++
116+
}
117+
}
118+
}
119+
}
120+
}
121+

0 commit comments

Comments
 (0)