Skip to content

Commit 4bfcf87

Browse files
authored
feat: add unified diff view option (#63)
1 parent c8a8f8e commit 4bfcf87

6 files changed

Lines changed: 70 additions & 14 deletions

File tree

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@ go install github.com/dlvhdr/diffnav@latest
4343
git config --global pager.diff diffnav
4444
```
4545

46+
## Flags
47+
48+
| Flag | Description |
49+
|------|-------------|
50+
| `--side-by-side, -s` | Force side-by-side diff view |
51+
| `--unified, -u` | Force unified diff view |
52+
53+
Example:
54+
55+
```sh
56+
git diff | diffnav --unified
57+
git diff | diffnav -u
58+
```
59+
4660
## Configuration
4761

4862
The config file is searched in this order:
@@ -76,6 +90,9 @@ ui:
7690

7791
# Color filenames by git status (default: true)
7892
colorFileNames: false
93+
94+
# Use side-by-side diff view (default: true, set false for unified)
95+
sideBySide: true
7996
```
8097
8198
| Option | Type | Default | Description |
@@ -87,6 +104,7 @@ ui:
87104
| `ui.searchTreeWidth`| int | `50` | Width of the search panel |
88105
| `ui.icons` | string | `nerd-fonts` | Icon style: `nerd-fonts`, `nerd-fonts-alt`, `unicode`, or `ascii` |
89106
| `ui.colorFileNames` | bool | `true` | Color filenames by git status |
107+
| `ui.sideBySide` | bool | `true` | Use side-by-side diff view (false for unified) |
90108

91109
### Delta
92110

@@ -107,6 +125,7 @@ If you want the exact delta configuration I'm using - [it can be found here](htt
107125
| <kbd>y</kbd> | Copy file path |
108126
| <kbd>i</kbd> | Cycle icon style |
109127
| <kbd>o</kbd> | Open file in $EDITOR |
128+
| <kbd>s</kbd> | Toggle side-by-side/unified view |
110129
| <kbd>Tab</kbd> | Switch focus between the panes |
111130
| <kbd>q</kbd> | Quit |
112131

main.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"bufio"
5+
"flag"
56
"fmt"
67
"io"
78
"os"
@@ -19,6 +20,13 @@ import (
1920
)
2021

2122
func main() {
23+
// Parse CLI flags
24+
sideBySideFlag := flag.Bool("side-by-side", false, "Force side-by-side diff view")
25+
flag.BoolVar(sideBySideFlag, "s", false, "Force side-by-side diff view (shorthand)")
26+
unifiedFlag := flag.Bool("unified", false, "Force unified diff view")
27+
flag.BoolVar(unifiedFlag, "u", false, "Force unified diff view (shorthand)")
28+
flag.Parse()
29+
2230
zone.NewGlobal()
2331

2432
stat, err := os.Stdin.Stat()
@@ -78,6 +86,14 @@ func main() {
7886
os.Exit(0)
7987
}
8088
cfg := config.Load()
89+
90+
// Override config with CLI flags if specified
91+
if *unifiedFlag {
92+
cfg.UI.SideBySide = false
93+
} else if *sideBySideFlag {
94+
cfg.UI.SideBySide = true
95+
}
96+
8197
p := tea.NewProgram(ui.New(input, cfg), tea.WithMouseAllMotion())
8298

8399
if _, err := p.Run(); err != nil {

pkg/config/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type UIConfig struct {
1616
SearchTreeWidth int `yaml:"searchTreeWidth"`
1717
Icons string `yaml:"icons"` // "nerd-fonts" (default), "nerd-fonts-alt", "unicode", "ascii"
1818
ColorFileNames bool `yaml:"colorFileNames"` // Color filenames by git status (default: true)
19+
SideBySide bool `yaml:"sideBySide"` // Side-by-side diff view (default: true)
1920
}
2021

2122
type Config struct {
@@ -32,6 +33,7 @@ func DefaultConfig() Config {
3233
SearchTreeWidth: 50,
3334
Icons: "nerd-fonts",
3435
ColorFileNames: true,
36+
SideBySide: true,
3537
},
3638
}
3739
}

pkg/ui/keys.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type KeyMap struct {
1313
Copy key.Binding
1414
TogglePanel key.Binding
1515
OpenInEditor key.Binding
16+
ToggleDiffView key.Binding
1617
}
1718

1819
var keys = &KeyMap{
@@ -56,6 +57,10 @@ var keys = &KeyMap{
5657
key.WithKeys("o"),
5758
key.WithHelp("o", "open"),
5859
),
60+
ToggleDiffView: key.NewBinding(
61+
key.WithKeys("s"),
62+
key.WithHelp("s", "toggle side-by-side"),
63+
),
5964
}
6065

6166
func getKeys() []key.Binding {
@@ -69,6 +74,7 @@ func getKeys() []key.Binding {
6974
keys.Search,
7075
keys.Copy,
7176
keys.OpenInEditor,
77+
keys.ToggleDiffView,
7278
keys.Quit,
7379
}
7480
}

pkg/ui/mainModel.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,13 @@ type mainModel struct {
7373
draggingSidebar bool
7474
customSidebarWidth int
7575
iconStyle string
76+
sideBySide bool
7677
}
7778

7879
func New(input string, cfg config.Config) mainModel {
79-
m := mainModel{input: input, isShowingFileTree: cfg.UI.ShowFileTree, activePanel: FileTreePanel, config: cfg, iconStyle: cfg.UI.Icons}
80+
m := mainModel{input: input, isShowingFileTree: cfg.UI.ShowFileTree, activePanel: FileTreePanel, config: cfg, iconStyle: cfg.UI.Icons, sideBySide: cfg.UI.SideBySide}
8081
m.fileTree = filetree.New(cfg.UI.Icons, cfg.UI.ColorFileNames)
81-
m.diffViewer = diffviewer.New()
82+
m.diffViewer = diffviewer.New(cfg.UI.SideBySide)
8283

8384
m.help = help.New()
8485
helpSt := lipgloss.NewStyle()
@@ -148,6 +149,10 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
148149
cmds = append(cmds, dfCmd)
149150
case "i":
150151
m.cycleIconStyle()
152+
case "s":
153+
m.sideBySide = !m.sideBySide
154+
cmd = m.diffViewer.SetSideBySide(m.sideBySide)
155+
cmds = append(cmds, cmd)
151156
case "tab":
152157
if m.isShowingFileTree {
153158
if m.activePanel == FileTreePanel {
@@ -371,7 +376,7 @@ func (m mainModel) View() string {
371376
// Width(0) means only the border is rendered (1 char).
372377
grabLine := lipgloss.NewStyle().
373378
Width(0).
374-
Height(m.height - m.footerHeight() - m.headerHeight() - 1).
379+
Height(m.height-m.footerHeight()-m.headerHeight()-1).
375380
Border(lipgloss.NormalBorder(), false, true, false, false).
376381
BorderForeground(lipgloss.Color("8")).
377382
Render("")

pkg/ui/panes/diffviewer/diffviewer.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@ const dirHeaderHeight = 3
2020

2121
type Model struct {
2222
common.Common
23-
vp viewport.Model
24-
buffer *bytes.Buffer
25-
file *gitdiff.File
23+
vp viewport.Model
24+
buffer *bytes.Buffer
25+
file *gitdiff.File
26+
sideBySide bool
2627
}
2728

28-
func New() Model {
29+
func New(sideBySide bool) Model {
2930
return Model{
30-
vp: viewport.Model{},
31+
vp: viewport.Model{},
32+
sideBySide: sideBySide,
3133
}
3234
}
3335

@@ -76,7 +78,7 @@ func (m *Model) SetSize(width, height int) tea.Cmd {
7678
m.Height = height
7779
m.vp.Width = m.Width
7880
m.vp.Height = m.Height - dirHeaderHeight
79-
return diff(m.file, m.Width)
81+
return diff(m.file, m.Width, m.sideBySide)
8082
}
8183

8284
func (m Model) headerView() string {
@@ -117,13 +119,19 @@ func (m Model) headerView() string {
117119
func (m Model) SetFilePatch(file *gitdiff.File) (Model, tea.Cmd) {
118120
m.buffer = new(bytes.Buffer)
119121
m.file = file
120-
return m, diff(m.file, m.Width)
122+
return m, diff(m.file, m.Width, m.sideBySide)
121123
}
122124

123125
func (m *Model) GoToTop() {
124126
m.vp.GotoTop()
125127
}
126128

129+
// SetSideBySide updates the diff view mode and re-renders.
130+
func (m *Model) SetSideBySide(sideBySide bool) tea.Cmd {
131+
m.sideBySide = sideBySide
132+
return diff(m.file, m.Width, m.sideBySide)
133+
}
134+
127135
func (m *Model) LineUp(n int) {
128136
m.vp.LineUp(n)
129137
}
@@ -142,19 +150,19 @@ func (m *Model) ScrollDown(lines int) {
142150
m.vp.LineDown(lines)
143151
}
144152

145-
146-
func diff(file *gitdiff.File, width int) tea.Cmd {
153+
func diff(file *gitdiff.File, width int, sideBySidePreference bool) tea.Cmd {
147154
if width == 0 || file == nil {
148155
return nil
149156
}
150157
return func() tea.Msg {
151-
sideBySide := !file.IsNew && !file.IsDelete
158+
// Only use side-by-side if preference is true AND file is not new/deleted
159+
useSideBySide := sideBySidePreference && !file.IsNew && !file.IsDelete
152160
args := []string{
153161
"--paging=never",
154162
fmt.Sprintf("-w=%d", width),
155163
fmt.Sprintf("--max-line-length=%d", width),
156164
}
157-
if sideBySide {
165+
if useSideBySide {
158166
args = append(args, "--side-by-side")
159167
}
160168
deltac := exec.Command("delta", args...)

0 commit comments

Comments
 (0)