Skip to content

Commit a070e6e

Browse files
authored
Merge pull request #198 from batrachianai/diff-diff
Diff diff
2 parents 67986f5 + c9d166c commit a070e6e

File tree

4 files changed

+52
-50
lines changed

4 files changed

+52
-50
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.5.33] - 2027-01-16
9+
10+
### Fixed
11+
12+
- Fixed character level diff highlights
813

914
## [0.5.32] - 2027-01-15
1015

@@ -242,6 +247,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
242247

243248
- First release. This document will be updated for subsequent releases.
244249

250+
[0.5.33]: https://github.com/batrachianai/toad/compare/v0.5.32...v0.5.33
245251
[0.5.32]: https://github.com/batrachianai/toad/compare/v0.5.31...v0.5.32
246252
[0.5.31]: https://github.com/batrachianai/toad/compare/v0.5.30...v0.5.31
247253
[0.5.30]: https://github.com/batrachianai/toad/compare/v0.5.29...v0.5.30

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "batrachian-toad"
3-
version = "0.5.32"
3+
version = "0.5.33"
44
description = "A unified experience for AI in your terminal."
55
readme = "README.md"
66
requires-python = ">=3.14"

src/toad/data/agents/opencode.ai.toml

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ run_command."*" = "opencode acp"
1818
help = '''
1919
# OpenCode
2020
21-
**The AI coding agent built for the terminal**
22-
2321
OpenCode is an open source agent that helps you write and run code directly from the terminal with a flexible client/server architecture.
2422
2523
## Key Features
@@ -29,31 +27,11 @@ OpenCode is an open source agent that helps you write and run code directly from
2927
- **Multi-LLM Support**: Works with various AI providers
3028
- **GitHub Integration**: Deep integration with GitHub workflows
3129
32-
## ACP Integration
33-
34-
OpenCode supports the Agent Client Protocol via the **opencode-acp** adapter by josephschmitt. This adapter:
35-
- Launches a per-session MCP HTTP server
36-
- Proxies file and terminal actions back to ACP
37-
- Translates OpenCode streaming updates to ACP notifications
38-
- Enables OpenCode to work with ACP-compatible editors
39-
40-
The ACP adapter allows standardized communication between OpenCode and various development tools.
41-
42-
## Installation
43-
44-
Install OpenCode globally:
45-
```bash
46-
npm install -g opencode
47-
```
48-
49-
For ACP support, install the adapter from:
50-
https://github.com/josephschmitt/opencode-acp
51-
5230
---
5331
5432
**Website**: https://opencode.ai/
5533
**GitHub**: https://github.com/sst/opencode
56-
**ACP Adapter**: https://github.com/josephschmitt/opencode-acp
34+
5735
'''
5836

5937
[actions."*".install]

src/toad/widgets/diff_view.py

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,38 @@ def counts(self) -> tuple[int, int]:
310310
additions += 1
311311
return additions, removals
312312

313+
@classmethod
314+
def _highlight_diff_lines(
315+
cls, lines_a: list[Content], lines_b: list[Content]
316+
) -> tuple[list[Content], list[Content]]:
317+
"""Diff two groups of lines.
318+
319+
Args:
320+
lines_a: Lines before.
321+
lines_b: Lines after
322+
323+
Returns:
324+
A pair of highlighted lists of lines.
325+
"""
326+
code_a = Content("\n").join(content for content in lines_a)
327+
code_b = Content("\n").join(content for content in lines_b)
328+
sequence_matcher = difflib.SequenceMatcher(
329+
lambda character: character in " \t",
330+
code_a.plain,
331+
code_b.plain,
332+
autojunk=True,
333+
)
334+
spans_a: list[Span] = []
335+
spans_b: list[Span] = []
336+
for tag, i1, i2, j1, j2 in sequence_matcher.get_opcodes():
337+
if tag in {"delete", "replace"}:
338+
spans_a.append(Span(i1, i2, "on $error 40%"))
339+
if tag in {"insert", "replace"}:
340+
spans_b.append(Span(j1, j2, "on $success 40%"))
341+
diffed_lines_a = code_a.add_spans(spans_a).split("\n")
342+
diffed_lines_b = code_b.add_spans(spans_b).split("\n")
343+
return diffed_lines_a, diffed_lines_b
344+
313345
@property
314346
def highlighted_code_lines(self) -> tuple[list[Content], list[Content]]:
315347
"""Get syntax highlighted code for both files, as a list of lines.
@@ -331,35 +363,21 @@ def highlighted_code_lines(self) -> tuple[list[Content], list[Content]]:
331363
"\n".join(text_lines_b), language=language2, path=self.path2
332364
)
333365

334-
if self.code_before:
335-
336-
sequence_matcher = difflib.SequenceMatcher(
337-
lambda character: character in " \t",
338-
code_a.plain,
339-
code_b.plain,
340-
autojunk=True,
341-
)
342-
code_a_spans: list[Span] = []
343-
code_b_spans: list[Span] = []
344-
345-
for tag, i1, i2, j1, j2 in sequence_matcher.get_opcodes():
346-
if (
347-
tag in {"delete", "replace"}
348-
and "\n" not in code_a.plain[i1 : i2 + 1]
349-
):
350-
code_a_spans.append(Span(i1, i2, "on $error 40%"))
351-
if (
352-
tag in {"insert", "replace"}
353-
and "\n" not in code_b.plain[j1 : j2 + 1]
354-
):
355-
code_b_spans.append(Span(j1, j2, "on $success 40%"))
356-
357-
code_a = code_a.add_spans(code_a_spans)
358-
code_b = code_b.add_spans(code_b_spans)
359-
360366
lines_a = code_a.split("\n")
361367
lines_b = code_b.split("\n")
368+
369+
if self.code_before:
370+
for group in self.grouped_opcodes:
371+
for tag, i1, i2, j1, j2 in group:
372+
if tag == "replace" and (j2 - j1) / (i2 - i1) <= 1.5:
373+
diff_lines_a, diff_lines_b = self._highlight_diff_lines(
374+
lines_a[i1:i2], lines_b[j1:j2]
375+
)
376+
lines_a[i1:i2] = diff_lines_a
377+
lines_b[j1:j2] = diff_lines_b
378+
362379
self._highlighted_code_lines = (lines_a, lines_b)
380+
363381
return self._highlighted_code_lines
364382

365383
def get_title(self) -> Content:

0 commit comments

Comments
 (0)