Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ go install github.com/dlvhdr/diffnav@latest
git config --global pager.diff diffnav
```

## Flags

| Flag | Description |
|------|-------------|
| `--side-by-side, -s` | Force side-by-side diff view |
| `--unified, -u` | Force unified diff view |

Example:

```sh
git diff | diffnav --unified
git diff | diffnav -u
```

## Configuration

The config file is searched in this order:
Expand Down Expand Up @@ -76,6 +90,9 @@ ui:

# Color filenames by git status (default: true)
colorFileNames: false

# Use side-by-side diff view (default: true, set false for unified)
sideBySide: true
```

| Option | Type | Default | Description |
Expand All @@ -87,6 +104,7 @@ ui:
| `ui.searchTreeWidth`| int | `50` | Width of the search panel |
| `ui.icons` | string | `nerd-fonts` | Icon style: `nerd-fonts`, `nerd-fonts-alt`, `unicode`, or `ascii` |
| `ui.colorFileNames` | bool | `true` | Color filenames by git status |
| `ui.sideBySide` | bool | `true` | Use side-by-side diff view (false for unified) |

### Delta

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

Expand Down
16 changes: 16 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"bufio"
"flag"
"fmt"
"io"
"os"
Expand All @@ -19,6 +20,13 @@ import (
)

func main() {
// Parse CLI flags
sideBySideFlag := flag.Bool("side-by-side", false, "Force side-by-side diff view")
flag.BoolVar(sideBySideFlag, "s", false, "Force side-by-side diff view (shorthand)")
unifiedFlag := flag.Bool("unified", false, "Force unified diff view")
flag.BoolVar(unifiedFlag, "u", false, "Force unified diff view (shorthand)")
flag.Parse()

zone.NewGlobal()

stat, err := os.Stdin.Stat()
Expand Down Expand Up @@ -78,6 +86,14 @@ func main() {
os.Exit(0)
}
cfg := config.Load()

// Override config with CLI flags if specified
if *unifiedFlag {
cfg.UI.SideBySide = false
} else if *sideBySideFlag {
cfg.UI.SideBySide = true
}

p := tea.NewProgram(ui.New(input, cfg), tea.WithMouseAllMotion())

if _, err := p.Run(); err != nil {
Expand Down
2 changes: 2 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type UIConfig struct {
SearchTreeWidth int `yaml:"searchTreeWidth"`
Icons string `yaml:"icons"` // "nerd-fonts" (default), "nerd-fonts-alt", "unicode", "ascii"
ColorFileNames bool `yaml:"colorFileNames"` // Color filenames by git status (default: true)
SideBySide bool `yaml:"sideBySide"` // Side-by-side diff view (default: true)
}

type Config struct {
Expand All @@ -32,6 +33,7 @@ func DefaultConfig() Config {
SearchTreeWidth: 50,
Icons: "nerd-fonts",
ColorFileNames: true,
SideBySide: true,
},
}
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/ui/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type KeyMap struct {
Copy key.Binding
TogglePanel key.Binding
OpenInEditor key.Binding
ToggleDiffView key.Binding
}

var keys = &KeyMap{
Expand Down Expand Up @@ -56,6 +57,10 @@ var keys = &KeyMap{
key.WithKeys("o"),
key.WithHelp("o", "open"),
),
ToggleDiffView: key.NewBinding(
key.WithKeys("s"),
key.WithHelp("s", "toggle side-by-side"),
),
}

func getKeys() []key.Binding {
Expand All @@ -69,6 +74,7 @@ func getKeys() []key.Binding {
keys.Search,
keys.Copy,
keys.OpenInEditor,
keys.ToggleDiffView,
keys.Quit,
}
}
11 changes: 8 additions & 3 deletions pkg/ui/mainModel.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,13 @@ type mainModel struct {
draggingSidebar bool
customSidebarWidth int
iconStyle string
sideBySide bool
}

func New(input string, cfg config.Config) mainModel {
m := mainModel{input: input, isShowingFileTree: cfg.UI.ShowFileTree, activePanel: FileTreePanel, config: cfg, iconStyle: cfg.UI.Icons}
m := mainModel{input: input, isShowingFileTree: cfg.UI.ShowFileTree, activePanel: FileTreePanel, config: cfg, iconStyle: cfg.UI.Icons, sideBySide: cfg.UI.SideBySide}
m.fileTree = filetree.New(cfg.UI.Icons, cfg.UI.ColorFileNames)
m.diffViewer = diffviewer.New()
m.diffViewer = diffviewer.New(cfg.UI.SideBySide)

m.help = help.New()
helpSt := lipgloss.NewStyle()
Expand Down Expand Up @@ -148,6 +149,10 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds = append(cmds, dfCmd)
case "i":
m.cycleIconStyle()
case "s":
m.sideBySide = !m.sideBySide
cmd = m.diffViewer.SetSideBySide(m.sideBySide)
cmds = append(cmds, cmd)
case "tab":
if m.isShowingFileTree {
if m.activePanel == FileTreePanel {
Expand Down Expand Up @@ -371,7 +376,7 @@ func (m mainModel) View() string {
// Width(0) means only the border is rendered (1 char).
grabLine := lipgloss.NewStyle().
Width(0).
Height(m.height - m.footerHeight() - m.headerHeight() - 1).
Height(m.height-m.footerHeight()-m.headerHeight()-1).
Border(lipgloss.NormalBorder(), false, true, false, false).
BorderForeground(lipgloss.Color("8")).
Render("")
Expand Down
30 changes: 19 additions & 11 deletions pkg/ui/panes/diffviewer/diffviewer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ const dirHeaderHeight = 3

type Model struct {
common.Common
vp viewport.Model
buffer *bytes.Buffer
file *gitdiff.File
vp viewport.Model
buffer *bytes.Buffer
file *gitdiff.File
sideBySide bool
}

func New() Model {
func New(sideBySide bool) Model {
return Model{
vp: viewport.Model{},
vp: viewport.Model{},
sideBySide: sideBySide,
}
}

Expand Down Expand Up @@ -76,7 +78,7 @@ func (m *Model) SetSize(width, height int) tea.Cmd {
m.Height = height
m.vp.Width = m.Width
m.vp.Height = m.Height - dirHeaderHeight
return diff(m.file, m.Width)
return diff(m.file, m.Width, m.sideBySide)
}

func (m Model) headerView() string {
Expand Down Expand Up @@ -117,13 +119,19 @@ func (m Model) headerView() string {
func (m Model) SetFilePatch(file *gitdiff.File) (Model, tea.Cmd) {
m.buffer = new(bytes.Buffer)
m.file = file
return m, diff(m.file, m.Width)
return m, diff(m.file, m.Width, m.sideBySide)
}

func (m *Model) GoToTop() {
m.vp.GotoTop()
}

// SetSideBySide updates the diff view mode and re-renders.
func (m *Model) SetSideBySide(sideBySide bool) tea.Cmd {
m.sideBySide = sideBySide
return diff(m.file, m.Width, m.sideBySide)
}

func (m *Model) LineUp(n int) {
m.vp.LineUp(n)
}
Expand All @@ -142,19 +150,19 @@ func (m *Model) ScrollDown(lines int) {
m.vp.LineDown(lines)
}


func diff(file *gitdiff.File, width int) tea.Cmd {
func diff(file *gitdiff.File, width int, sideBySidePreference bool) tea.Cmd {
if width == 0 || file == nil {
return nil
}
return func() tea.Msg {
sideBySide := !file.IsNew && !file.IsDelete
// Only use side-by-side if preference is true AND file is not new/deleted
useSideBySide := sideBySidePreference && !file.IsNew && !file.IsDelete
args := []string{
"--paging=never",
fmt.Sprintf("-w=%d", width),
fmt.Sprintf("--max-line-length=%d", width),
}
if sideBySide {
if useSideBySide {
args = append(args, "--side-by-side")
}
deltac := exec.Command("delta", args...)
Expand Down