Semantic diff
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add non-interactive mode (default), -i for TUI

+64 -3
+1
.gitignore
··· 1 1 .direnv/ 2 2 result 3 + sdiff
+3 -3
go.mod
··· 8 8 github.com/charmbracelet/bubbletea v1.3.10 9 9 github.com/charmbracelet/lipgloss v1.1.0 10 10 github.com/charmbracelet/x/ansi v0.10.1 11 + github.com/charmbracelet/x/term v0.2.1 12 + github.com/nix-community/tree-sitter-nix v0.3.0 11 13 github.com/sergi/go-diff v1.4.0 12 14 github.com/tree-sitter/go-tree-sitter v0.25.0 13 15 github.com/tree-sitter/tree-sitter-bash v0.25.1 ··· 17 19 github.com/tree-sitter/tree-sitter-javascript v0.25.0 18 20 github.com/tree-sitter/tree-sitter-json v0.24.8 19 21 github.com/tree-sitter/tree-sitter-python v0.25.0 22 + github.com/tree-sitter/tree-sitter-ruby v0.23.1 20 23 github.com/tree-sitter/tree-sitter-rust v0.24.2 21 24 github.com/tree-sitter/tree-sitter-typescript v0.23.2 22 - github.com/nix-community/tree-sitter-nix v0.3.0 23 - github.com/tree-sitter/tree-sitter-ruby v0.23.1 24 25 ) 25 26 26 27 require ( 27 28 github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 28 29 github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect 29 30 github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect 30 - github.com/charmbracelet/x/term v0.2.1 // indirect 31 31 github.com/dlclark/regexp2 v1.11.5 // indirect 32 32 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect 33 33 github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
+60
main.go
··· 8 8 "strings" 9 9 "time" 10 10 11 + xterm "github.com/charmbracelet/x/term" 11 12 tea "github.com/charmbracelet/bubbletea" 12 13 sdiff "sdiff/internal" 13 14 ) ··· 343 344 return hl[idx] 344 345 } 345 346 347 + // printDiff renders the diff to stdout without the TUI. 348 + func printDiff(m model) { 349 + width, _, err := xterm.GetSize(os.Stdout.Fd()) 350 + if err != nil || width < 20 { 351 + width = 200 352 + } 353 + 354 + st := m.styles 355 + colWidth := width / 2 356 + if colWidth < 10 { 357 + colWidth = 10 358 + } 359 + 360 + fmt.Println(st.HeaderSt.Render(fmt.Sprintf(" %s → %s", m.oldFile, m.newFile))) 361 + 362 + for _, dl := range m.lines { 363 + leftLine := m.getHighlightedLine(m.oldHL, dl.OldLine-1) 364 + rightLine := m.getHighlightedLine(m.newHL, dl.NewLine-1) 365 + 366 + var lBgHex, rBgHex string 367 + switch dl.Kind { 368 + case sdiff.KindRemoved: 369 + lBgHex = st.RemovedBgHex 370 + rightLine = "" 371 + case sdiff.KindAdded: 372 + rBgHex = st.AddedBgHex 373 + leftLine = "" 374 + } 375 + 376 + lNum := " " 377 + rNum := " " 378 + if dl.OldLine > 0 { 379 + lNum = st.LineSt.Render(fmt.Sprintf("%4d", dl.OldLine)) 380 + } 381 + if dl.NewLine > 0 { 382 + rNum = st.LineSt.Render(fmt.Sprintf("%4d", dl.NewLine)) 383 + } 384 + 385 + leftCell := sdiff.RenderCell(leftLine, colWidth-6, lBgHex) 386 + rightCell := sdiff.RenderCell(rightLine, colWidth-6, rBgHex) 387 + 388 + fmt.Println(" " + lNum + " " + leftCell + " │ " + rNum + " " + rightCell) 389 + } 390 + 391 + changed := 0 392 + for _, l := range m.lines { 393 + if l.Kind != sdiff.KindEqual { 394 + changed++ 395 + } 396 + } 397 + fmt.Println(st.StatusSt.Render(fmt.Sprintf(" %d lines %d changed mode:%s", len(m.lines), changed, m.diffMode))) 398 + } 399 + 346 400 // ── main ────────────────────────────────────────────────────────────────────── 347 401 348 402 func main() { 349 403 theme := flag.String("theme", "nord", "theme name or path to a .toml theme file") 404 + interactive := flag.Bool("i", false, "interactive TUI mode") 350 405 flag.Usage = func() { 351 406 fmt.Fprintf(os.Stderr, "Usage: sdiff [flags] <old-file> <new-file>\n\nFlags:\n") 352 407 flag.PrintDefaults() ··· 362 417 if err != nil { 363 418 fmt.Fprintln(os.Stderr, err) 364 419 os.Exit(1) 420 + } 421 + 422 + if !*interactive { 423 + printDiff(m) 424 + return 365 425 } 366 426 367 427 p := tea.NewProgram(m, tea.WithAltScreen())