Files
diple/highlight.go
2026-07-28 09:52:23 +02:00

241 lines
5.8 KiB
Go

package main
import (
"bytes"
"fmt"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/alecthomas/chroma/v2/quick"
)
const reviewContextLines = 3
var codeHighlightTheme = "github-dark"
var hunkHeaderPattern = regexp.MustCompile(`^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@`)
type highlightedDiffLine struct {
gutter string
code string
selected bool
}
type parsedDiffLine struct {
raw string
oldLine int
newLine int
header bool
notice bool
selected bool
}
func highlightDiff(path, hunk string, startLine, endLine int, side string) []highlightedDiffLine {
if hunk == "" {
return []highlightedDiffLine{{code: "(GitHub did not return a diff hunk)"}}
}
if endLine <= 0 {
endLine = startLine
}
if startLine <= 0 {
startLine = endLine
}
if startLine > endLine {
startLine, endLine = endLine, startLine
}
parsed := parseDiff(hunk, startLine, endLine, side)
visible := reviewedWindow(parsed)
padding := commonIndent(visible)
lexer := lexerForPath(path)
out := make([]highlightedDiffLine, 0, len(visible))
for _, line := range visible {
if line.raw == "⋯" {
out = append(out, highlightedDiffLine{code: "\x1b[38;5;245m⋯\x1b[0m"})
continue
}
if line.notice {
out = append(out, highlightedDiffLine{code: "\x1b[38;5;245m" + line.raw + "\x1b[0m"})
continue
}
if line.header {
out = append(out, highlightedDiffLine{code: "\x1b[38;5;141m" + line.raw + "\x1b[0m"})
continue
}
out = append(out, renderDiffLine(lexer, line, padding))
}
return out
}
func parseDiff(hunk string, startLine, endLine int, side string) []parsedDiffLine {
oldLine, newLine := 0, 0
lines := make([]parsedDiffLine, 0, strings.Count(hunk, "\n")+1)
for _, raw := range strings.Split(hunk, "\n") {
if match := hunkHeaderPattern.FindStringSubmatch(raw); match != nil {
oldLine, _ = strconv.Atoi(match[1])
newLine, _ = strconv.Atoi(match[2])
lines = append(lines, parsedDiffLine{raw: raw, header: true})
continue
}
line := parsedDiffLine{raw: raw}
switch {
case strings.HasPrefix(raw, "+"):
line.newLine = newLine
newLine++
case strings.HasPrefix(raw, "-"):
line.oldLine = oldLine
oldLine++
case strings.HasPrefix(raw, `\`):
// "\ No newline at end of file" has no source coordinate.
default:
line.oldLine, line.newLine = oldLine, newLine
oldLine++
newLine++
}
coordinate := line.newLine
if side == "LEFT" {
coordinate = line.oldLine
}
line.selected = coordinate > 0 && coordinate >= startLine && coordinate <= endLine
lines = append(lines, line)
}
return lines
}
func reviewedWindow(lines []parsedDiffLine) []parsedDiffLine {
first, last := -1, -1
for i, line := range lines {
if line.selected {
if first == -1 {
first = i
}
last = i
}
}
if first == -1 {
out := make([]parsedDiffLine, 0, 2)
for _, line := range lines {
if line.header {
out = append(out, line)
break
}
}
return append(out, parsedDiffLine{
raw: "(original reviewed lines unavailable in GitHub's historical diff)",
notice: true,
})
}
start := max(0, first-reviewContextLines)
end := min(len(lines), last+reviewContextLines+1)
header := -1
for i := first; i >= 0; i-- {
if lines[i].header {
header = i
break
}
}
out := make([]parsedDiffLine, 0, end-start+3)
if header >= 0 && header < start {
out = append(out, lines[header])
}
if start > 0 && start != header {
out = append(out, parsedDiffLine{raw: "⋯"})
}
out = append(out, lines[start:end]...)
if end < len(lines) {
out = append(out, parsedDiffLine{raw: "⋯"})
}
return out
}
func renderDiffLine(lexer string, line parsedDiffLine, padding int) highlightedDiffLine {
marker, source := " ", line.raw
lineNumber := ""
markerStyle := "\x1b[38;5;245m"
switch {
case strings.HasPrefix(line.raw, "+"):
marker, source = "+", strings.TrimPrefix(line.raw, "+")
lineNumber = coordinateText(line.newLine)
markerStyle = "\x1b[38;5;114m"
case strings.HasPrefix(line.raw, "-"):
marker, source = "-", strings.TrimPrefix(line.raw, "-")
lineNumber = coordinateText(line.oldLine)
markerStyle = "\x1b[38;5;203m"
default:
source = strings.TrimPrefix(line.raw, " ")
lineNumber = coordinateText(line.newLine)
if lineNumber == "" {
lineNumber = coordinateText(line.oldLine)
}
}
source = strings.ReplaceAll(source, "\t", " ")
source = trimIndent(source, padding)
return highlightedDiffLine{
gutter: fmt.Sprintf(
"\x1b[38;5;245m%5s\x1b[0m %s%s\x1b[0m ",
lineNumber, markerStyle, marker,
),
code: highlightedSource(lexer, source),
selected: line.selected,
}
}
func commonIndent(lines []parsedDiffLine) int {
padding := -1
for _, line := range lines {
if line.header || line.notice || line.raw == "⋯" || strings.HasPrefix(line.raw, `\`) {
continue
}
source := line.raw
if strings.HasPrefix(source, "+") || strings.HasPrefix(source, "-") || strings.HasPrefix(source, " ") {
source = source[1:]
}
source = strings.ReplaceAll(source, "\t", " ")
if strings.TrimSpace(source) == "" {
continue
}
indent := len(source) - len(strings.TrimLeft(source, " "))
if padding == -1 || indent < padding {
padding = indent
}
}
return max(0, padding)
}
func trimIndent(source string, padding int) string {
for padding > 0 && strings.HasPrefix(source, " ") {
source = source[1:]
padding--
}
return source
}
func coordinateText(line int) string {
if line <= 0 {
return ""
}
return strconv.Itoa(line)
}
func highlightedSource(lexer, source string) string {
var highlighted bytes.Buffer
if err := quick.Highlight(&highlighted, source, lexer, "terminal16m", codeHighlightTheme); err != nil {
return source
}
return strings.TrimSuffix(highlighted.String(), "\n")
}
func lexerForPath(path string) string {
ext := strings.TrimPrefix(filepath.Ext(path), ".")
if ext == "" {
return "plaintext"
}
return ext
}