既存の[[クレート]]を調査してみたが、いずれもライブラリ利用として対応しているものはなさそう。[[Similar]]を使って表示をカスタマイズしてみる。
## 実装
幅が違う問題は等幅フォントが利用される前提で、[[ASCII]]を1スペース、日本語を2スペースで数えることにしている。
```rust
use similar::{ChangeTag, TextDiff};
use unicode_width::UnicodeWidthStr;
fn create_side_by_side_diff(text1: &str, text2: &str, max_width: usize) -> String {
TextDiff::from_lines(text1, text2)
.iter_all_changes()
.map(|change| {
let content = change.to_string().trim_end_matches('\n').to_string();
let width = max_width - (content.width() - content.chars().count());
match change.tag() {
ChangeTag::Delete => format!(
"{:>6} | {:<width$} | {:>6} | {:<blank_width$} |",
change.old_index().unwrap() + 1,
content,
"",
"",
blank_width = max_width,
width = width,
),
ChangeTag::Insert => format!(
"{:>6} | {:<blank_width$} | {:>6} | {:<width$} |",
"",
"",
change.new_index().unwrap() + 1,
content,
blank_width = max_width,
width = width
),
ChangeTag::Equal => format!(
"{:>6} | {token:<width$} | {:>6} | {token:<width$} |",
change.old_index().unwrap() + 1,
change.new_index().unwrap() + 1,
token = content,
width = width
),
}
})
.collect::<Vec<_>>()
.join("\n")
}
```
## テスト
```txt
あああ
bbb
ccc
ddd
いいいいいーeee
かかお
FFFFFFFFFF
ggg
```
と
```txt
あああ
bbb
いいいいいーeee
かかか
ffffffffff
ggg
```
を比較してみる。以下のように呼び出す。
```rust
fn main() {
let diff_text = create_side_by_side_diff(
"
あああ
bbb
ccc
ddd
いいいいいーeee
かかお
FFFFFFFFFF
ggg"
.trim(),
"
あああ
bbb
いいいいいーeee
かかか
ffffffffff
ggg"
.trim(),
20,
);
println!("{diff_text}");
}
```
結果は以下のようになる。
```txt
1 | あああ | 1 | あああ |
2 | bbb | 2 | bbb |
3 | ccc | | |
4 | ddd | | |
5 | いいいいいーeee | 3 | いいいいいーeee |
6 | かかお | | |
7 | FFFFFFFFFF | | |
| | 4 | かかか |
| | 5 | ffffffffff |
8 | ggg | 6 | ggg |
```
となる。
[[Minerva]]は等幅フォントを利用していないので、上記の結果は若干ずれて見えるかもしれないが、等幅フォントだと以下画像のように表示される。
![[Pasted image 20230115172450.png]]