Semantic diff
1
fork

Configure Feed

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

feat: rename sdiff to adiff

+92 -92
+1 -1
.gitignore
··· 1 1 .direnv/ 2 2 result 3 - sdiff 3 + adiff
+24 -24
README.md
··· 1 - # sdiff 1 + # adiff 2 2 3 3 A semantic, side-by-side diff viewer for the terminal with `$EDITOR` support. 4 4 5 - Unlike traditional diff tools, sdiff uses [tree-sitter](https://tree-sitter.github.io/tree-sitter/) to parse source files into ASTs and diffs at the token level. This means pure formatting changes — re-indented blocks, reformatted function signatures, whitespace normalization — are shown as equal, while genuine logic changes are clearly highlighted. 5 + Unlike traditional diff tools, adiff uses [tree-sitter](https://tree-sitter.github.io/tree-sitter/) to parse source files into ASTs and diffs at the token level. This means pure formatting changes — re-indented blocks, reformatted function signatures, whitespace normalization — are shown as equal, while genuine logic changes are clearly highlighted. 6 6 7 - ![sdiff screenshot](docs/screenshot.png) 7 + ![adiff screenshot](docs/screenshot.png) 8 8 9 9 ## Features 10 10 11 11 - **Semantic diff** — tree-sitter token-level diffing ignores style-only changes 12 12 - **Syntax highlighting** — per-language color via [Chroma](https://github.com/alecthomas/chroma) 13 - - **Live file watching** — press `e` to open the new file in `$EDITOR`; sdiff watches for saves and reloads automatically 13 + - **Live file watching** — press `e` to open the new file in `$EDITOR`; adiff watches for saves and reloads automatically 14 14 - **Vim keybindings** — `j`/`k`, `g`/`G`, `n`/`p` for change navigation 15 15 - **Themeable** — TOML theme files with full colour control; `nord` built-in 16 16 ··· 27 27 ### Nix (flake) 28 28 29 29 ```nix 30 - inputs.sdiff.url = "git+https://tangled.org/adriano.tngl.sh/sdiff"; 30 + inputs.adiff.url = "git+https://tangled.org/adriano.tngl.sh/adiff"; 31 31 ``` 32 32 33 - Then add `inputs.sdiff.packages.${system}.default` to your packages, or use the provided overlay. 33 + Then add `inputs.adiff.packages.${system}.default` to your packages, or use the provided overlay. 34 34 35 35 **Direct package reference** (home-manager / NixOS `environment.systemPackages`): 36 36 37 37 ```nix 38 - home.packages = [ inputs.sdiff.packages.${pkgs.system}.default ]; 38 + home.packages = [ inputs.adiff.packages.${pkgs.system}.default ]; 39 39 ``` 40 40 41 - **Overlay** — adds `pkgs.sdiff` to your package set: 41 + **Overlay** — adds `pkgs.adiff` to your package set: 42 42 43 43 ```nix 44 - nixpkgs.overlays = [ inputs.sdiff.overlays.default ]; 44 + nixpkgs.overlays = [ inputs.adiff.overlays.default ]; 45 45 46 46 # Then reference it like any nixpkgs package: 47 - home.packages = [ pkgs.sdiff ]; 47 + home.packages = [ pkgs.adiff ]; 48 48 ``` 49 49 50 50 In a NixOS or home-manager flake this typically looks like: ··· 54 54 inputs = { 55 55 nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 56 56 home-manager.url = "github:nix-community/home-manager"; 57 - sdiff.url = "git+https://tangled.org/adriano.tngl.sh/sdiff"; 57 + adiff.url = "git+https://tangled.org/adriano.tngl.sh/adiff"; 58 58 }; 59 59 60 - outputs = { nixpkgs, home-manager, sdiff, ... }: { 60 + outputs = { nixpkgs, home-manager, adiff, ... }: { 61 61 homeConfigurations.yourname = home-manager.lib.homeManagerConfiguration { 62 62 pkgs = import nixpkgs { 63 63 system = "x86_64-linux"; 64 - overlays = [ sdiff.overlays.default ]; 64 + overlays = [ adiff.overlays.default ]; 65 65 }; 66 66 modules = [ 67 - ({ pkgs, ... }: { home.packages = [ pkgs.sdiff ]; }) 67 + ({ pkgs, ... }: { home.packages = [ pkgs.adiff ]; }) 68 68 ]; 69 69 }; 70 70 }; ··· 74 74 ## Usage 75 75 76 76 ``` 77 - sdiff [flags] <old-file> <new-file> 77 + adiff [flags] <old-file> <new-file> 78 78 ``` 79 79 80 80 | Flag | Default | Description | ··· 100 100 101 101 ## Themes 102 102 103 - Themes are TOML files. sdiff searches for themes in this order: 103 + Themes are TOML files. adiff searches for themes in this order: 104 104 105 105 1. Literal path (if the value contains `/` or ends in `.toml`) 106 - 2. `~/.config/sdiff/themes/<name>.toml` 106 + 2. `~/.config/adiff/themes/<name>.toml` 107 107 3. Built-in `nord` 108 108 109 109 ### Theme format ··· 130 130 cursor_symbol = "▶" 131 131 ``` 132 132 133 - Place custom themes at `~/.config/sdiff/themes/<name>.toml` and reference them with `-theme <name>`. 133 + Place custom themes at `~/.config/adiff/themes/<name>.toml` and reference them with `-theme <name>`. 134 134 135 135 ## hookable integration 136 136 137 - [hookable](https://tangled.org/adriano.tngl.sh/hookable) is a Claude Code hook runner that exposes tool call inputs as environment variables and forwards them to an arbitrary command. sdiff reads these variables natively, so it can be dropped in as the `--cmd` to preview file changes before Claude applies them. 137 + [hookable](https://tangled.org/adriano.tngl.sh/hookable) is a Claude Code hook runner that exposes tool call inputs as environment variables and forwards them to an arbitrary command. adiff reads these variables natively, so it can be dropped in as the `--cmd` to preview file changes before Claude applies them. 138 138 139 - When invoked with no file arguments and `HOOKABLE_TOOL_NAME` is set, sdiff constructs the before/after diff automatically: 139 + When invoked with no file arguments and `HOOKABLE_TOOL_NAME` is set, adiff constructs the before/after diff automatically: 140 140 141 141 | Tool | Before | After | 142 142 |------|--------|-------| ··· 153 153 "PreToolUse": [ 154 154 { 155 155 "matcher": "Edit", 156 - "hooks": [{"type": "command", "command": "hookable --interactive --no-exit-code --cmd 'sdiff -i'"}] 156 + "hooks": [{"type": "command", "command": "hookable --interactive --no-exit-code --cmd 'adiff -i'"}] 157 157 }, 158 158 { 159 159 "matcher": "Write", 160 - "hooks": [{"type": "command", "command": "hookable --interactive --no-exit-code --cmd 'sdiff -i'"}] 160 + "hooks": [{"type": "command", "command": "hookable --interactive --no-exit-code --cmd 'adiff -i'"}] 161 161 } 162 162 ] 163 163 } 164 164 } 165 165 ``` 166 166 167 - `--interactive` runs sdiff under a PTY so the full TUI renders. `--no-exit-code` tells hookable to ignore sdiff's exit code and always wait for a keypress — press `y` to allow the change or `n` to deny it. 167 + `--interactive` runs adiff under a PTY so the full TUI renders. `--no-exit-code` tells hookable to ignore adiff's exit code and always wait for a keypress — press `y` to allow the change or `n` to deny it. 168 168 169 169 ## Development 170 170 171 171 ```sh 172 172 nix develop # CGO-aware dev shell 173 173 go test ./... # run tests 174 - nix build # hermetic build → ./result/bin/sdiff 174 + nix build # hermetic build → ./result/bin/adiff 175 175 ```
+1 -1
docs/demo_new.go
··· 11 11 // processRequest handles an incoming HTTP request. 12 12 // 13 13 // The signature below is reformatted across multiple lines in the new 14 - // version. sdiff recognises the tokens are identical and shows those 14 + // version. adiff recognises the tokens are identical and shows those 15 15 // lines as equal — no highlight despite the whitespace change. 16 16 func processRequest( 17 17 w http.ResponseWriter,
+1 -1
docs/demo_old.go
··· 10 10 // processRequest handles an incoming HTTP request. 11 11 // 12 12 // The signature below is reformatted across multiple lines in the new 13 - // version. sdiff recognises the tokens are identical and shows those 13 + // version. adiff recognises the tokens are identical and shows those 14 14 // lines as equal — no highlight despite the whitespace change. 15 15 func processRequest(w http.ResponseWriter, r *http.Request) { 16 16 name := r.URL.Query().Get("name")
docs/screenshot.png

This is a binary file and will not be displayed.

+5 -5
flake.nix
··· 1 1 { 2 - description = "sdiff — semantic side-by-side diff TUI"; 2 + description = "adiff — semantic side-by-side diff TUI"; 3 3 4 4 inputs = { 5 5 nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; ··· 12 12 ... 13 13 }: 14 14 flake-parts.lib.mkFlake {inherit inputs;} { 15 - flake.overlays.default = final: _prev: {sdiff = self.packages.${final.system}.default;}; 15 + flake.overlays.default = final: _prev: {adiff = self.packages.${final.system}.default;}; 16 16 17 17 systems = ["x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin"]; 18 18 ··· 95 95 }; 96 96 in { 97 97 packages.default = pkgs.buildGoModule { 98 - pname = "sdiff"; 98 + pname = "adiff"; 99 99 version = "0.1.0"; 100 100 src = ./.; 101 101 ··· 143 143 }; 144 144 145 145 apps.update-screenshot = let 146 - tape = pkgs.writeText "sdiff-screenshot.tape" '' 146 + tape = pkgs.writeText "adiff-screenshot.tape" '' 147 147 Set Shell "bash" 148 148 Set FontSize 14 149 149 Set Width 1600 ··· 153 153 Env TERM "xterm-256color" 154 154 Env PS1 "" 155 155 156 - Type "result/bin/sdiff docs/demo_old.go docs/demo_new.go" 156 + Type "result/bin/adiff docs/demo_old.go docs/demo_new.go" 157 157 Enter 158 158 Sleep 2s 159 159 Screenshot docs/screenshot.png
+1 -1
go.mod
··· 1 - module sdiff 1 + module adiff 2 2 3 3 go 1.26.1 4 4
+1 -1
internal/diff.go
··· 1 - package sdiff 1 + package adiff 2 2 3 3 import ( 4 4 "strings"
+1 -1
internal/highlight.go
··· 1 - package sdiff 1 + package adiff 2 2 3 3 import ( 4 4 "fmt"
+3 -3
internal/theme.go
··· 1 - package sdiff 1 + package adiff 2 2 3 3 import ( 4 4 _ "embed" ··· 61 61 // LoadTheme loads a theme by name or file path. 62 62 // Search order: 63 63 // 1. Looks like a path (contains "/" or ends in ".toml") → load directly 64 - // 2. ~/.config/sdiff/themes/<name>.toml 64 + // 2. ~/.config/adiff/themes/<name>.toml 65 65 // 3. Built-in nord theme 66 66 func LoadTheme(nameOrPath string) (Theme, error) { 67 67 var data []byte ··· 74 74 } 75 75 } else { 76 76 cfgDir, _ := os.UserConfigDir() 77 - candidate := filepath.Join(cfgDir, "sdiff", "themes", nameOrPath+".toml") 77 + candidate := filepath.Join(cfgDir, "adiff", "themes", nameOrPath+".toml") 78 78 data, err = os.ReadFile(candidate) 79 79 if err != nil { 80 80 // Fall back to built-in
+1 -1
internal/themes/nord.toml
··· 1 1 [meta] 2 2 name = "nord" 3 3 description = "Nord color scheme" 4 - author = "sdiff" 4 + author = "adiff" 5 5 6 6 [chroma] 7 7 style = "nord"
+1 -1
internal/treesitter.go
··· 1 - package sdiff 1 + package adiff 2 2 3 3 import ( 4 4 "errors"
+26 -26
main.go
··· 12 12 13 13 xterm "github.com/charmbracelet/x/term" 14 14 tea "github.com/charmbracelet/bubbletea" 15 - sdiff "sdiff/internal" 15 + adiff "adiff/internal" 16 16 ) 17 17 18 18 // ── bubbletea model ─────────────────────────────────────────────────────────── ··· 22 22 newFile string 23 23 oldText string 24 24 newText string 25 - styles sdiff.ThemeStyles 25 + styles adiff.ThemeStyles 26 26 diffMode string // "semantic" or "text" 27 - lines []sdiff.DiffLine 27 + lines []adiff.DiffLine 28 28 oldHL []string // highlighted lines for old file 29 29 newHL []string // highlighted lines for new file 30 30 mtimes [2]time.Time // [0]=oldFile, [1]=newFile — for change detection ··· 46 46 return model{}, fmt.Errorf("reading %s: %w", newFile, err) 47 47 } 48 48 49 - theme, err := sdiff.LoadTheme(themeNameOrPath) 49 + theme, err := adiff.LoadTheme(themeNameOrPath) 50 50 if err != nil { 51 51 fmt.Fprintf(os.Stderr, "warning: %v; using built-in nord theme\n", err) 52 - theme, _ = sdiff.LoadTheme("nord") 52 + theme, _ = adiff.LoadTheme("nord") 53 53 } 54 54 styles := theme.ToStyles() 55 55 56 56 oldText := string(oldBytes) 57 57 newText := string(newBytes) 58 58 59 - lines, diffMode := sdiff.ComputeSemanticDiff(oldBytes, newBytes, oldFile, newFile) 60 - oldHL := sdiff.HighlightLines(oldText, oldFile, styles.ChromaStyle) 61 - newHL := sdiff.HighlightLines(newText, newFile, styles.ChromaStyle) 59 + lines, diffMode := adiff.ComputeSemanticDiff(oldBytes, newBytes, oldFile, newFile) 60 + oldHL := adiff.HighlightLines(oldText, oldFile, styles.ChromaStyle) 61 + newHL := adiff.HighlightLines(newText, newFile, styles.ChromaStyle) 62 62 63 63 mtimes := [2]time.Time{mtime(oldFile), mtime(newFile)} 64 64 65 65 cursor := 0 66 66 for i, l := range lines { 67 - if l.Kind != sdiff.KindEqual { 67 + if l.Kind != adiff.KindEqual { 68 68 cursor = i 69 69 break 70 70 } ··· 195 195 196 196 case "s": 197 197 if m.diffMode == "semantic" { 198 - m.lines = sdiff.ComputeDiff(m.oldText, m.newText) 198 + m.lines = adiff.ComputeDiff(m.oldText, m.newText) 199 199 m.diffMode = "text" 200 200 } else { 201 - lines, mode := sdiff.ComputeSemanticDiff([]byte(m.oldText), []byte(m.newText), m.oldFile, m.newFile) 201 + lines, mode := adiff.ComputeSemanticDiff([]byte(m.oldText), []byte(m.newText), m.oldFile, m.newFile) 202 202 m.lines = lines 203 203 m.diffMode = mode 204 204 if mode == "text" { ··· 241 241 } 242 242 m.oldText = string(oldBytes) 243 243 m.newText = string(newBytes) 244 - m.lines, m.diffMode = sdiff.ComputeSemanticDiff(oldBytes, newBytes, m.oldFile, m.newFile) 245 - m.oldHL = sdiff.HighlightLines(m.oldText, m.oldFile, m.styles.ChromaStyle) 246 - m.newHL = sdiff.HighlightLines(m.newText, m.newFile, m.styles.ChromaStyle) 244 + m.lines, m.diffMode = adiff.ComputeSemanticDiff(oldBytes, newBytes, m.oldFile, m.newFile) 245 + m.oldHL = adiff.HighlightLines(m.oldText, m.oldFile, m.styles.ChromaStyle) 246 + m.newHL = adiff.HighlightLines(m.newText, m.newFile, m.styles.ChromaStyle) 247 247 if m.cursor >= len(m.lines) { 248 248 m.cursor = len(m.lines) - 1 249 249 } ··· 262 262 263 263 func (m *model) jumpNext() { 264 264 for i := m.cursor + 1; i < len(m.lines); i++ { 265 - if m.lines[i].Kind != sdiff.KindEqual { 265 + if m.lines[i].Kind != adiff.KindEqual { 266 266 m.cursor = i 267 267 m.scrollToCursor() 268 268 return ··· 272 272 273 273 func (m *model) jumpPrev() { 274 274 for i := m.cursor - 1; i >= 0; i-- { 275 - if m.lines[i].Kind != sdiff.KindEqual { 275 + if m.lines[i].Kind != adiff.KindEqual { 276 276 m.cursor = i 277 277 m.scrollToCursor() 278 278 return ··· 326 326 327 327 var lBgHex, rBgHex string 328 328 switch dl.Kind { 329 - case sdiff.KindRemoved: 329 + case adiff.KindRemoved: 330 330 lBgHex = st.RemovedBgHex 331 331 rightLine = "" 332 - case sdiff.KindAdded: 332 + case adiff.KindAdded: 333 333 rBgHex = st.AddedBgHex 334 334 leftLine = "" 335 335 } ··· 348 348 rNum = st.LineSt.Render(fmt.Sprintf("%4d", dl.NewLine)) 349 349 } 350 350 351 - leftCell := sdiff.RenderCell(leftLine, colWidth-6, lBgHex, false) 352 - rightCell := sdiff.RenderCell(rightLine, colWidth-6, rBgHex, true) 351 + leftCell := adiff.RenderCell(leftLine, colWidth-6, lBgHex, false) 352 + rightCell := adiff.RenderCell(rightLine, colWidth-6, rBgHex, true) 353 353 354 354 row := prefix + lNum + " " + leftCell + " │ " + rNum + " " + rightCell 355 355 sb.WriteString(row) ··· 360 360 total := len(m.lines) 361 361 changed := 0 362 362 for _, l := range m.lines { 363 - if l.Kind != sdiff.KindEqual { 363 + if l.Kind != adiff.KindEqual { 364 364 changed++ 365 365 } 366 366 } ··· 424 424 425 425 var lBgHex, rBgHex string 426 426 switch dl.Kind { 427 - case sdiff.KindRemoved: 427 + case adiff.KindRemoved: 428 428 lBgHex = st.RemovedBgHex 429 429 rightLine = "" 430 - case sdiff.KindAdded: 430 + case adiff.KindAdded: 431 431 rBgHex = st.AddedBgHex 432 432 leftLine = "" 433 433 } ··· 441 441 rNum = st.LineSt.Render(fmt.Sprintf("%4d", dl.NewLine)) 442 442 } 443 443 444 - leftCell := sdiff.RenderCell(leftLine, colWidth-6, lBgHex, false) 445 - rightCell := sdiff.RenderCell(rightLine, colWidth-6, rBgHex, true) 444 + leftCell := adiff.RenderCell(leftLine, colWidth-6, lBgHex, false) 445 + rightCell := adiff.RenderCell(rightLine, colWidth-6, rBgHex, true) 446 446 447 447 fmt.Println(" " + lNum + " " + leftCell + " │ " + rNum + " " + rightCell) 448 448 } 449 449 450 450 changed := 0 451 451 for _, l := range m.lines { 452 - if l.Kind != sdiff.KindEqual { 452 + if l.Kind != adiff.KindEqual { 453 453 changed++ 454 454 } 455 455 }
+16 -16
test/diff_test.go
··· 1 - package sdiff_test 1 + package adiff_test 2 2 3 3 import ( 4 4 "os" 5 5 "strings" 6 6 "testing" 7 7 8 - sdiff "sdiff/internal" 8 + adiff "adiff/internal" 9 9 ) 10 10 11 11 // TestSemanticDiffDetectsNewStructField is a regression test for the bug where ··· 14 14 oldSrc := readFile(t, "testdata/logic_old.go") 15 15 newSrc := readFile(t, "testdata/logic_new.go") 16 16 17 - lines, mode := sdiff.ComputeSemanticDiff(oldSrc, newSrc, "logic_old.go", "logic_new.go") 17 + lines, mode := adiff.ComputeSemanticDiff(oldSrc, newSrc, "logic_old.go", "logic_new.go") 18 18 if mode != "semantic" { 19 19 t.Fatalf("expected semantic mode, got %q", mode) 20 20 } 21 21 22 - newLines := sdiff.SplitLines(string(newSrc)) 22 + newLines := adiff.SplitLines(string(newSrc)) 23 23 foundSum := false 24 24 for _, dl := range lines { 25 - if dl.Kind == sdiff.KindAdded && dl.NewLine > 0 { 25 + if dl.Kind == adiff.KindAdded && dl.NewLine > 0 { 26 26 idx := dl.NewLine - 1 27 27 if idx < len(newLines) && containsWord(newLines[idx], "Sum") { 28 28 foundSum = true ··· 42 42 oldSrc := readFile(t, "testdata/style_old.go") 43 43 newSrc := readFile(t, "testdata/style_new.go") 44 44 45 - lines, mode := sdiff.ComputeSemanticDiff(oldSrc, newSrc, "testdata/style_old.go", "testdata/style_new.go") 45 + lines, mode := adiff.ComputeSemanticDiff(oldSrc, newSrc, "testdata/style_old.go", "testdata/style_new.go") 46 46 if mode != "semantic" { 47 47 t.Fatalf("expected semantic mode, got %q", mode) 48 48 } 49 49 50 50 for _, dl := range lines { 51 - if dl.Kind == sdiff.KindRemoved { 51 + if dl.Kind == adiff.KindRemoved { 52 52 t.Errorf("line %d marked as removed; expected no code removals", dl.OldLine) 53 53 } 54 - if dl.Kind == sdiff.KindAdded { 54 + if dl.Kind == adiff.KindAdded { 55 55 text := strings.TrimSpace(dl.Text) 56 56 if !strings.HasPrefix(text, "//") { 57 57 t.Errorf("non-comment line %d marked as added: %q", dl.NewLine, dl.Text) ··· 66 66 oldSrc := readFile(t, "testdata/logic_old.rb") 67 67 newSrc := readFile(t, "testdata/logic_new.rb") 68 68 69 - lines, mode := sdiff.ComputeSemanticDiff(oldSrc, newSrc, "logic_old.rb", "logic_new.rb") 69 + lines, mode := adiff.ComputeSemanticDiff(oldSrc, newSrc, "logic_old.rb", "logic_new.rb") 70 70 if mode != "semantic" { 71 71 t.Fatalf("expected semantic mode, got %q", mode) 72 72 } 73 73 74 - newLines := sdiff.SplitLines(string(newSrc)) 74 + newLines := adiff.SplitLines(string(newSrc)) 75 75 foundMultiply := false 76 76 for _, dl := range lines { 77 - if dl.Kind == sdiff.KindAdded && dl.NewLine > 0 { 77 + if dl.Kind == adiff.KindAdded && dl.NewLine > 0 { 78 78 idx := dl.NewLine - 1 79 79 if idx < len(newLines) && containsWord(newLines[idx], "multiply") { 80 80 foundMultiply = true ··· 99 99 oldSrc := readFile(t, "testdata/logic_old.nix") 100 100 newSrc := readFile(t, "testdata/logic_new.nix") 101 101 102 - lines, mode := sdiff.ComputeSemanticDiff(oldSrc, newSrc, "logic_old.nix", "logic_new.nix") 102 + lines, mode := adiff.ComputeSemanticDiff(oldSrc, newSrc, "logic_old.nix", "logic_new.nix") 103 103 if mode != "semantic" { 104 104 t.Fatalf("expected semantic mode, got %q", mode) 105 105 } 106 106 107 - newLines := sdiff.SplitLines(string(newSrc)) 107 + newLines := adiff.SplitLines(string(newSrc)) 108 108 foundFd := false 109 109 for _, dl := range lines { 110 - if dl.Kind == sdiff.KindAdded && dl.NewLine > 0 { 110 + if dl.Kind == adiff.KindAdded && dl.NewLine > 0 { 111 111 idx := dl.NewLine - 1 112 112 if idx < len(newLines) && containsWord(newLines[idx], "fd") { 113 113 foundFd = true ··· 144 144 oldSrc := readFile(t, oldPath) 145 145 newSrc := readFile(t, newPath) 146 146 147 - lines, mode := sdiff.ComputeSemanticDiff(oldSrc, newSrc, oldPath, newPath) 147 + lines, mode := adiff.ComputeSemanticDiff(oldSrc, newSrc, oldPath, newPath) 148 148 if mode != "semantic" { 149 149 t.Fatalf("expected semantic mode, got %q", mode) 150 150 } 151 151 152 152 for _, dl := range lines { 153 - if dl.Kind != sdiff.KindEqual { 153 + if dl.Kind != adiff.KindEqual { 154 154 t.Errorf("line %d/%d marked as %v but files are semantically equal", 155 155 dl.OldLine, dl.NewLine, dl.Kind) 156 156 }
+9 -9
test/highlight_test.go
··· 1 - package sdiff_test 1 + package adiff_test 2 2 3 3 import ( 4 4 "strings" 5 5 "testing" 6 6 7 - sdiff "sdiff/internal" 7 + adiff "adiff/internal" 8 8 ) 9 9 10 10 // TestHighlightLinesNoTabs verifies that HighlightLines expands tab characters 11 11 // so that rune-count width calculations on the output are accurate. 12 12 func TestHighlightLinesNoTabs(t *testing.T) { 13 13 data := readFile(t, "testdata/style_old.go") 14 - lines := sdiff.HighlightLines(string(data), "style_old.go", "nord") 14 + lines := adiff.HighlightLines(string(data), "style_old.go", "nord") 15 15 for i, line := range lines { 16 - plain := sdiff.StripANSI(line) 16 + plain := adiff.StripANSI(line) 17 17 if strings.ContainsRune(plain, '\t') { 18 18 t.Errorf("line %d contains raw tab after highlighting: %q", i+1, plain) 19 19 } ··· 41 41 } 42 42 for _, tc := range cases { 43 43 t.Run(tc.name, func(t *testing.T) { 44 - cell := sdiff.RenderCell(tc.input, tc.width, tc.bgHex, false) 45 - plain := sdiff.StripANSI(cell) 44 + cell := adiff.RenderCell(tc.input, tc.width, tc.bgHex, false) 45 + plain := adiff.StripANSI(cell) 46 46 got := len([]rune(plain)) 47 47 if got != tc.width { 48 48 t.Errorf("visual width = %d, want %d; plain=%q", got, tc.width, plain) ··· 64 64 {"testdata/style_new.go", "nord"}, 65 65 } { 66 66 data := readFile(t, tc.file) 67 - lines := sdiff.HighlightLines(string(data), tc.file, tc.theme) 67 + lines := adiff.HighlightLines(string(data), tc.file, tc.theme) 68 68 for i, hl := range lines { 69 - cell := sdiff.RenderCell(hl, cellWidth, "", false) 70 - plain := sdiff.StripANSI(cell) 69 + cell := adiff.RenderCell(hl, cellWidth, "", false) 70 + plain := adiff.StripANSI(cell) 71 71 got := len([]rune(plain)) 72 72 if got != cellWidth { 73 73 t.Errorf("%s line %d: cell width = %d, want %d", tc.file, i+1, got, cellWidth)
+1 -1
themes/nord.toml
··· 1 1 [meta] 2 2 name = "nord" 3 3 description = "Nord color scheme" 4 - author = "sdiff" 4 + author = "adiff" 5 5 6 6 [chroma] 7 7 style = "nord"