mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 11:01:25 +01:00
134 lines
3.7 KiB
Go
134 lines
3.7 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package terminal
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/mattn/go-runewidth"
|
|
)
|
|
|
|
// Field is one labeled fact in a View.
|
|
type Field struct {
|
|
Label string
|
|
// Value is plain text. Cells, when set, replace it with styled values joined by commas, such
|
|
// as colored labels.
|
|
Value string
|
|
Cells []Cell
|
|
// TTYOnly and PipedOnly restrict the field to one kind of output, like table columns.
|
|
TTYOnly, PipedOnly bool
|
|
}
|
|
|
|
// text returns the field's safe value, styled for d when it is made of cells.
|
|
func (f Field) text(d Display) string {
|
|
if len(f.Cells) == 0 {
|
|
return SafeLine(f.Value)
|
|
}
|
|
values := make([]string, len(f.Cells))
|
|
for i, cell := range f.Cells {
|
|
values[i] = SafeLine(cell.Text)
|
|
if d.TTY {
|
|
values[i] = cell.style(values[i], d, false)
|
|
}
|
|
}
|
|
return strings.Join(values, ", ")
|
|
}
|
|
|
|
// View is one resource for human readers, such as an issue. On a TTY it shows the title, badges,
|
|
// fields, body and a "View this <kind> on Gitea" footer; piped, "key:\tvalue" lines, "--" and the
|
|
// raw body.
|
|
type View struct {
|
|
Title, ID string
|
|
// Badges, such as the state, start the line under the title; Meta completes it, for example
|
|
// "alice opened about 2 hours ago • 2 comments".
|
|
Badges []Cell
|
|
Meta string
|
|
Fields []Field
|
|
Body string
|
|
// Plain marks the body as plain text, such as a commit message, which is never rendered as markdown.
|
|
Plain bool
|
|
// Bodyless resources, such as webhooks, have no body: the view ends with the fields and URL.
|
|
Bodyless bool
|
|
// Kind names the resource in the footer, such as "issue".
|
|
Kind, URL string
|
|
}
|
|
|
|
// Render formats the view for d. All server text is sanitized.
|
|
func (v View) Render(d Display) string {
|
|
var b strings.Builder
|
|
if !d.TTY {
|
|
if v.Title != "" {
|
|
b.WriteString("title:\t" + SafeLine(v.Title) + "\n")
|
|
}
|
|
for _, field := range v.Fields {
|
|
if !field.TTYOnly {
|
|
b.WriteString(strings.ToLower(SafeLine(field.Label)) + ":\t" + field.text(d) + "\n")
|
|
}
|
|
}
|
|
if v.URL != "" {
|
|
b.WriteString("url:\t" + SafeLine(v.URL) + "\n")
|
|
}
|
|
if v.Bodyless {
|
|
return b.String()
|
|
}
|
|
body := SafeText(v.Body)
|
|
b.WriteString("--\n" + body)
|
|
if !strings.HasSuffix(body, "\n") {
|
|
b.WriteByte('\n')
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
muted := func(text string) string { return Cell{Muted: true}.style(text, d, false) }
|
|
b.WriteString(Cell{Bold: true}.style(SafeLine(v.Title), d, false))
|
|
if v.ID != "" {
|
|
b.WriteString(" " + muted(SafeLine(v.ID)))
|
|
}
|
|
b.WriteByte('\n')
|
|
var summary []string
|
|
for _, badge := range v.Badges {
|
|
summary = append(summary, badge.style(SafeLine(badge.Text), d, false))
|
|
}
|
|
if meta := SafeLine(v.Meta); meta != "" {
|
|
summary = append(summary, muted(meta))
|
|
}
|
|
if len(summary) > 0 {
|
|
b.WriteString(strings.Join(summary, " • ") + "\n")
|
|
}
|
|
|
|
var fields []Field
|
|
labelWidth := 0
|
|
for _, field := range v.Fields {
|
|
if !field.PipedOnly && (field.Value != "" || len(field.Cells) > 0) {
|
|
fields = append(fields, field)
|
|
labelWidth = max(labelWidth, runewidth.StringWidth(SafeLine(field.Label)))
|
|
}
|
|
}
|
|
for _, field := range fields {
|
|
label := SafeLine(field.Label) + ":"
|
|
b.WriteString(Cell{Bold: true}.style(label, d, false) + strings.Repeat(" ", labelWidth+2-runewidth.StringWidth(label)) + field.text(d) + "\n")
|
|
}
|
|
|
|
if !v.Bodyless {
|
|
body := strings.TrimSpace(v.Body)
|
|
switch {
|
|
case body == "":
|
|
body = muted("No description provided")
|
|
case d.Markdown && !v.Plain:
|
|
body = Markdown(body, d)
|
|
default:
|
|
body = SafeText(body)
|
|
}
|
|
b.WriteString("\n" + body + "\n")
|
|
}
|
|
if v.URL != "" {
|
|
kind := "View on Gitea: "
|
|
if v.Kind != "" {
|
|
kind = "View this " + v.Kind + " on Gitea: "
|
|
}
|
|
b.WriteString("\n" + muted(kind+SafeLine(v.URL)) + "\n")
|
|
}
|
|
return b.String()
|
|
}
|