Make code suggestions work aswell
This commit is contained in:
141
tui.go
141
tui.go
@@ -437,7 +437,15 @@ func (m App) threadDetail(width, height int) string {
|
||||
rendered := make([]string, 0, len(visible))
|
||||
innerWidth := max(1, width-2)
|
||||
for _, line := range visible {
|
||||
renderedLine := ansi.Truncate(line.fixed+line.text, innerWidth, "…")
|
||||
railWidth := ansi.StringWidth(line.rail)
|
||||
contentWidth := max(1, innerWidth-railWidth)
|
||||
var renderedLine string
|
||||
if line.suggestionChange != 0 {
|
||||
renderedLine = suggestionHighlight(line.fixed, line.text, contentWidth, line.suggestionChange)
|
||||
} else {
|
||||
renderedLine = ansi.Truncate(line.fixed+line.text, contentWidth, "…")
|
||||
}
|
||||
renderedLine = line.rail + renderedLine
|
||||
if line.selected {
|
||||
renderedLine = selectedBackground(renderedLine, innerWidth)
|
||||
}
|
||||
@@ -447,9 +455,11 @@ func (m App) threadDetail(width, height int) string {
|
||||
}
|
||||
|
||||
type detailLine struct {
|
||||
text string
|
||||
fixed string
|
||||
selected bool
|
||||
text string
|
||||
fixed string
|
||||
rail string
|
||||
selected bool
|
||||
suggestionChange byte
|
||||
}
|
||||
|
||||
func (m App) detailLines(width int) []detailLine {
|
||||
@@ -481,9 +491,40 @@ func (m App) detailLines(width int) []detailLine {
|
||||
}
|
||||
}
|
||||
for _, comment := range thread.Comments {
|
||||
lines = append(lines, detailLine{}, detailLine{text: authorStyle(comment.Author).Render("@"+comment.Author) + " " + dimStyle.Render(comment.CreatedAt.Local().Format("2006-01-02 15:04"))})
|
||||
for _, commentLine := range strings.Split(wrap(comment.Body, max(10, width-4)), "\n") {
|
||||
lines = append(lines, detailLine{text: commentLine})
|
||||
content := parseCommentBody(comment.Body)
|
||||
rail := lipgloss.NewStyle().Foreground(authorColor(comment.Author)).Render("│ ")
|
||||
lines = append(lines, detailLine{}, detailLine{
|
||||
rail: rail,
|
||||
text: authorStyle(comment.Author).Render("@"+comment.Author) + " " +
|
||||
dimStyle.Render(comment.CreatedAt.Local().Format("2006-01-02 15:04")),
|
||||
})
|
||||
if content.Prose != "" {
|
||||
for _, commentLine := range strings.Split(wrap(content.Prose, max(10, width-4)), "\n") {
|
||||
lines = append(lines, detailLine{rail: rail, text: commentLine})
|
||||
}
|
||||
}
|
||||
for suggestionIndex, suggestion := range content.Suggestions {
|
||||
removed, added := normalizeSuggestion(reviewedSourceLines(thread, comment), suggestion)
|
||||
removedRanges, addedRanges := suggestionChangedRanges(removed, added)
|
||||
label := "suggested change"
|
||||
if len(content.Suggestions) > 1 {
|
||||
label = fmt.Sprintf("suggested change %d/%d", suggestionIndex+1, len(content.Suggestions))
|
||||
}
|
||||
lines = append(lines, detailLine{rail: rail}, detailLine{rail: rail, text: dimStyle.Render(label)})
|
||||
for sourceIndex, source := range removed {
|
||||
lines = append(lines, addCommentRail(
|
||||
wrapSuggestionLine(
|
||||
thread.Path, source, '-', removedRanges[sourceIndex], max(1, width-4),
|
||||
), rail,
|
||||
)...)
|
||||
}
|
||||
for sourceIndex, source := range added {
|
||||
lines = append(lines, addCommentRail(
|
||||
wrapSuggestionLine(
|
||||
thread.Path, source, '+', addedRanges[sourceIndex], max(1, width-4),
|
||||
), rail,
|
||||
)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
if thread.IsTruncated {
|
||||
@@ -520,6 +561,53 @@ func wrapDiffLine(line highlightedDiffLine, width int) []detailLine {
|
||||
return result
|
||||
}
|
||||
|
||||
func wrapSuggestionLine(path, source string, change byte, changed codeRange, width int) []detailLine {
|
||||
marker := badStyle.Render(string(change))
|
||||
if change == '+' {
|
||||
marker = okStyle.Render(string(change))
|
||||
}
|
||||
code := highlightedSource(lexerForPath(path), source)
|
||||
code = highlightCodeRange(code, changed, change)
|
||||
lines := wrapDiffLine(highlightedDiffLine{
|
||||
gutter: marker + " ",
|
||||
code: code,
|
||||
}, width)
|
||||
for i := range lines {
|
||||
lines[i].suggestionChange = change
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
func highlightCodeRange(code string, changed codeRange, change byte) string {
|
||||
if changed.End <= changed.Start {
|
||||
return code
|
||||
}
|
||||
totalWidth := ansi.StringWidth(code)
|
||||
start := clamp(changed.Start, 0, totalWidth)
|
||||
end := clamp(changed.End, start, totalWidth)
|
||||
if end <= start {
|
||||
return code
|
||||
}
|
||||
|
||||
background := "\x1b[48;2;55;0;0m"
|
||||
if change == '+' {
|
||||
background = "\x1b[48;2;0;55;0m"
|
||||
}
|
||||
const reset = "\x1b[0m"
|
||||
before := ansi.Cut(code, 0, start)
|
||||
middle := ansi.Cut(code, start, end)
|
||||
after := ansi.Cut(code, end, totalWidth)
|
||||
middle = strings.ReplaceAll(middle, reset, reset+background)
|
||||
return before + background + middle + reset + after
|
||||
}
|
||||
|
||||
func addCommentRail(lines []detailLine, rail string) []detailLine {
|
||||
for i := range lines {
|
||||
lines[i].rail = rail
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
func wrapCodeWithIndent(code string, width int) []string {
|
||||
if width <= 0 || ansi.StringWidth(code) <= width {
|
||||
return []string{code}
|
||||
@@ -580,25 +668,9 @@ func isCodeBreakpoint(r rune) bool {
|
||||
|
||||
func reviewAnchor(thread ReviewThread) (int, int) {
|
||||
if len(thread.Comments) > 0 {
|
||||
comment := thread.Comments[0]
|
||||
end := comment.OriginalLine
|
||||
start := comment.OriginalStartLine
|
||||
if end == 0 {
|
||||
end = comment.Line
|
||||
start = comment.StartLine
|
||||
}
|
||||
if end > 0 {
|
||||
if start == 0 {
|
||||
start = end
|
||||
}
|
||||
return start, end
|
||||
}
|
||||
return commentReviewAnchor(thread, thread.Comments[0])
|
||||
}
|
||||
start := thread.StartLine
|
||||
if start == 0 {
|
||||
start = thread.Line
|
||||
}
|
||||
return start, thread.Line
|
||||
return commentReviewAnchor(thread, ReviewComment{})
|
||||
}
|
||||
|
||||
func shortOID(oid string) string {
|
||||
@@ -618,6 +690,25 @@ func selectedBackground(line string, width int) string {
|
||||
return background + line + reset
|
||||
}
|
||||
|
||||
func suggestionHighlight(gutter, code string, width int, change byte) string {
|
||||
background := "\x1b[48;5;52m"
|
||||
if change == '+' {
|
||||
background = "\x1b[48;5;22m"
|
||||
}
|
||||
const reset = "\x1b[0m"
|
||||
gutter = ansi.Truncate(gutter, width, "")
|
||||
gutter = strings.ReplaceAll(gutter, reset, reset+background)
|
||||
codeWidth := max(0, width-ansi.StringWidth(gutter))
|
||||
code = ansi.Truncate(code, codeWidth, "")
|
||||
code = strings.ReplaceAll(code, reset, reset+background)
|
||||
content := background + gutter + code + reset
|
||||
padding := max(0, width-ansi.StringWidth(gutter)-ansi.StringWidth(code))
|
||||
if padding > 0 {
|
||||
content += background + strings.Repeat(" ", padding) + reset
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
var authorPalette = []lipgloss.Color{
|
||||
"#61AFEF", "#C678DD", "#56B6C2", "#E5C07B",
|
||||
"#E06C75", "#98C379", "#D19A66", "#7FC8FF",
|
||||
|
||||
Reference in New Issue
Block a user