mirror of
https://github.com/alecthomas/chroma.git
synced 2025-03-17 20:58:08 +02:00
Pager friendly terminal formatting (#1006)
Let's say a pager, like [moar](https://github.com/walles/moar) (uses Chroma for syntax highlighting) or `less`, shows a line in the middle of a file. Unless that line starts with the correct formatting for the line, the pager would have to scan the whole file from the start to get the coloring of this single line right. Before this PR, lines were not guaranteed to start with formatting, but could sometimes rely on formatting from the preceding lines. With this change in place, lines can now stand by themselves, and paging will work better on Chroma's output. Missing formatting at the start of the line would happen when a token had a linefeed in the middle.
This commit is contained in:
parent
a56e228d32
commit
876fb612a7
@ -1,7 +1,6 @@
|
||||
package formatters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
@ -257,13 +256,7 @@ func (c *indexedTTYFormatter) Format(w io.Writer, style *chroma.Style, it chroma
|
||||
}
|
||||
}
|
||||
|
||||
if clr != "" {
|
||||
fmt.Fprint(w, clr)
|
||||
}
|
||||
fmt.Fprint(w, token.Value)
|
||||
if clr != "" {
|
||||
fmt.Fprintf(w, "\033[0m")
|
||||
}
|
||||
writeToken(w, clr, token.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package formatters
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
)
|
||||
@ -10,33 +11,66 @@ import (
|
||||
// TTY16m is a true-colour terminal formatter.
|
||||
var TTY16m = Register("terminal16m", chroma.FormatterFunc(trueColourFormatter))
|
||||
|
||||
var crOrCrLf = regexp.MustCompile(`\r?\n`)
|
||||
|
||||
// Print the text with the given formatting, resetting the formatting at the end
|
||||
// of each line and resuming it on the next line.
|
||||
//
|
||||
// This way, a pager (like https://github.com/walles/moar for example) can show
|
||||
// any line in the output by itself, and it will get the right formatting.
|
||||
func writeToken(w io.Writer, formatting string, text string) {
|
||||
if formatting == "" {
|
||||
fmt.Fprint(w, text)
|
||||
return
|
||||
}
|
||||
|
||||
newlineIndices := crOrCrLf.FindAllStringIndex(text, -1)
|
||||
|
||||
afterLastNewline := 0
|
||||
for _, indices := range newlineIndices {
|
||||
newlineStart, afterNewline := indices[0], indices[1]
|
||||
fmt.Fprint(w, formatting)
|
||||
fmt.Fprint(w, text[afterLastNewline:newlineStart])
|
||||
fmt.Fprint(w, "\033[0m")
|
||||
fmt.Fprint(w, text[newlineStart:afterNewline])
|
||||
afterLastNewline = afterNewline
|
||||
}
|
||||
|
||||
if afterLastNewline < len(text) {
|
||||
// Print whatever is left after the last newline
|
||||
fmt.Fprint(w, formatting)
|
||||
fmt.Fprint(w, text[afterLastNewline:])
|
||||
fmt.Fprint(w, "\033[0m")
|
||||
}
|
||||
}
|
||||
|
||||
func trueColourFormatter(w io.Writer, style *chroma.Style, it chroma.Iterator) error {
|
||||
style = clearBackground(style)
|
||||
for token := it(); token != chroma.EOF; token = it() {
|
||||
entry := style.Get(token.Type)
|
||||
if !entry.IsZero() {
|
||||
out := ""
|
||||
if entry.Bold == chroma.Yes {
|
||||
out += "\033[1m"
|
||||
}
|
||||
if entry.Underline == chroma.Yes {
|
||||
out += "\033[4m"
|
||||
}
|
||||
if entry.Italic == chroma.Yes {
|
||||
out += "\033[3m"
|
||||
}
|
||||
if entry.Colour.IsSet() {
|
||||
out += fmt.Sprintf("\033[38;2;%d;%d;%dm", entry.Colour.Red(), entry.Colour.Green(), entry.Colour.Blue())
|
||||
}
|
||||
if entry.Background.IsSet() {
|
||||
out += fmt.Sprintf("\033[48;2;%d;%d;%dm", entry.Background.Red(), entry.Background.Green(), entry.Background.Blue())
|
||||
}
|
||||
fmt.Fprint(w, out)
|
||||
if entry.IsZero() {
|
||||
fmt.Fprint(w, token.Value)
|
||||
continue
|
||||
}
|
||||
fmt.Fprint(w, token.Value)
|
||||
if !entry.IsZero() {
|
||||
fmt.Fprint(w, "\033[0m")
|
||||
|
||||
formatting := ""
|
||||
if entry.Bold == chroma.Yes {
|
||||
formatting += "\033[1m"
|
||||
}
|
||||
if entry.Underline == chroma.Yes {
|
||||
formatting += "\033[4m"
|
||||
}
|
||||
if entry.Italic == chroma.Yes {
|
||||
formatting += "\033[3m"
|
||||
}
|
||||
if entry.Colour.IsSet() {
|
||||
formatting += fmt.Sprintf("\033[38;2;%d;%d;%dm", entry.Colour.Red(), entry.Colour.Green(), entry.Colour.Blue())
|
||||
}
|
||||
if entry.Background.IsSet() {
|
||||
formatting += fmt.Sprintf("\033[48;2;%d;%d;%dm", entry.Background.Red(), entry.Background.Green(), entry.Background.Blue())
|
||||
}
|
||||
|
||||
writeToken(w, formatting, token.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user