All checks were successful
Run Test Suite / test (push) Successful in 39s
There are some odd things being done in the testing files, that should get reviewed.
236 lines
6.8 KiB
Go
Executable File
236 lines
6.8 KiB
Go
Executable File
package style
|
|
|
|
import (
|
|
"git.gophernest.net/azpect/TextEditor/internal/core"
|
|
"github.com/alecthomas/chroma/v2"
|
|
"github.com/charmbracelet/lipgloss"
|
|
)
|
|
|
|
// Styles holds all the visual styling for the editor.
|
|
type Styles struct {
|
|
// Cursor styles by mode
|
|
CursorNormal lipgloss.Style
|
|
CursorInsert lipgloss.Style
|
|
CursorCommand lipgloss.Style
|
|
|
|
// Gutter (line numbers)
|
|
Gutter lipgloss.Style
|
|
GutterCurrentLine lipgloss.Style
|
|
|
|
// Visual mode
|
|
VisualHighlight lipgloss.Style
|
|
VisualAnchor lipgloss.Style // debugging
|
|
|
|
// Status bar
|
|
StatusBar lipgloss.Style
|
|
StatusBarActive lipgloss.Style
|
|
|
|
// Command line
|
|
CommandError lipgloss.Style
|
|
CommandOutputBorder lipgloss.Style
|
|
CommandContinueMessage lipgloss.Style
|
|
|
|
// General Styles
|
|
LineStyle lipgloss.Style // This is a simple background with no text coloring
|
|
BackgroundStyle lipgloss.Style // This is just the background
|
|
|
|
// Chroma data
|
|
ChromaStyle *chroma.Style
|
|
}
|
|
|
|
// DefaultStyles: Returns the default editor color scheme.
|
|
func DefaultStyles() Styles {
|
|
return Styles{
|
|
CursorNormal: lipgloss.NewStyle().Reverse(true),
|
|
CursorInsert: lipgloss.NewStyle().Underline(true),
|
|
CursorCommand: lipgloss.NewStyle().Reverse(true),
|
|
|
|
Gutter: lipgloss.NewStyle().
|
|
Background(lipgloss.Color("236")).
|
|
Foreground(lipgloss.Color("243")),
|
|
|
|
GutterCurrentLine: lipgloss.NewStyle().
|
|
Background(lipgloss.Color("236")).
|
|
Foreground(lipgloss.Color("#d69d00")),
|
|
|
|
VisualHighlight: lipgloss.NewStyle().
|
|
Background(lipgloss.Color("#7a6a00")),
|
|
|
|
VisualAnchor: lipgloss.NewStyle().
|
|
Background(lipgloss.Color("#a89020")),
|
|
|
|
StatusBar: lipgloss.NewStyle().
|
|
Background(lipgloss.Color("236")).
|
|
Foreground(lipgloss.Color("243")),
|
|
|
|
StatusBarActive: lipgloss.NewStyle().
|
|
Background(lipgloss.Color("62")).
|
|
Foreground(lipgloss.Color("230")),
|
|
|
|
CommandError: lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("#e3203a")),
|
|
|
|
CommandOutputBorder: lipgloss.NewStyle().
|
|
Background(lipgloss.Color("#000000")),
|
|
|
|
CommandContinueMessage: lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("#546fba")),
|
|
|
|
ChromaStyle: nil,
|
|
}
|
|
}
|
|
|
|
func ChromaStyles(chromaStyle *chroma.Style) Styles {
|
|
bgString := chromaStyle.Get(chroma.Background).Background.String()
|
|
lineNumbers := chromaStyle.Get(chroma.LineTableTD)
|
|
lineHighlight := chromaStyle.Get(chroma.LineHighlight)
|
|
|
|
return Styles{
|
|
CursorNormal: lipgloss.NewStyle().
|
|
Background(lipgloss.Color(bgString)).
|
|
Reverse(true),
|
|
|
|
CursorInsert: lipgloss.NewStyle().
|
|
Background(lipgloss.Color(bgString)).
|
|
Underline(true),
|
|
|
|
CursorCommand: lipgloss.NewStyle().
|
|
Background(lipgloss.Color(bgString)).
|
|
Reverse(true),
|
|
|
|
Gutter: lipgloss.NewStyle().
|
|
Background(lipgloss.Color(
|
|
darkenColor(lineNumbers.Background, 0.9).String()),
|
|
).
|
|
Foreground(lipgloss.Color(lineNumbers.Colour.String())),
|
|
|
|
GutterCurrentLine: lipgloss.NewStyle().
|
|
Background(lipgloss.Color(
|
|
darkenColor(lineNumbers.Background, 0.9).String()),
|
|
).
|
|
Foreground(lipgloss.Color(lineNumbers.Colour.String())),
|
|
|
|
VisualHighlight: lipgloss.NewStyle().
|
|
Background(lipgloss.Color(lineHighlight.Background.String())).
|
|
Foreground(lipgloss.Color(lineHighlight.Colour.String())),
|
|
|
|
VisualAnchor: lipgloss.NewStyle().
|
|
Background(lipgloss.Color(lineHighlight.Background.String())).
|
|
Foreground(lipgloss.Color(lineHighlight.Colour.String())),
|
|
|
|
StatusBar: lipgloss.NewStyle().
|
|
Background(lipgloss.Color(bgString)).
|
|
Foreground(lipgloss.Color("243")),
|
|
|
|
StatusBarActive: lipgloss.NewStyle().
|
|
Background(lipgloss.Color(bgString)).
|
|
Foreground(lipgloss.Color("230")),
|
|
|
|
CommandError: lipgloss.NewStyle().
|
|
Background(lipgloss.Color(bgString)).
|
|
Foreground(lipgloss.Color("#e3203a")),
|
|
|
|
CommandOutputBorder: lipgloss.NewStyle().
|
|
Background(
|
|
lipgloss.Color(
|
|
darkenColor(
|
|
chromaStyle.Get(chroma.Background).Background, 0.5).
|
|
String(),
|
|
),
|
|
),
|
|
|
|
CommandContinueMessage: lipgloss.NewStyle().
|
|
Background(lipgloss.Color(bgString)).
|
|
Foreground(lipgloss.Color("#546fba")),
|
|
|
|
LineStyle: lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color(chromaStyle.Get(chroma.Line).Colour.String())).
|
|
Background(lipgloss.Color(bgString)),
|
|
|
|
BackgroundStyle: lipgloss.NewStyle().Background(lipgloss.Color(bgString)),
|
|
|
|
ChromaStyle: chromaStyle,
|
|
}
|
|
}
|
|
|
|
// Styles.DefaultCursorStyle: Returns the appropriate cursor style for the given mode.
|
|
func (s Styles) DefaultCursorStyle(mode core.Mode) lipgloss.Style {
|
|
switch mode {
|
|
case core.InsertMode:
|
|
return s.CursorInsert
|
|
case core.CommandMode:
|
|
return s.CursorCommand
|
|
default:
|
|
return s.CursorNormal
|
|
}
|
|
}
|
|
|
|
// Styles.CursorStyle: Returns a cursor style derived from a chroma style. This function should preferred
|
|
// over the DefaultCursorStyle, but in cases where there is no style to apply, the DefaultCursorStyle
|
|
// will always work.
|
|
func (s Styles) CursorStyle(mode core.Mode, style lipgloss.Style) lipgloss.Style {
|
|
switch mode {
|
|
case core.NormalMode, core.VisualLineMode, core.VisualBlockMode, core.VisualMode:
|
|
return lipgloss.NewStyle().
|
|
Background(style.GetForeground()).
|
|
Foreground(style.GetBackground())
|
|
default:
|
|
return lipgloss.NewStyle().
|
|
Background(s.BackgroundStyle.GetBackground()).
|
|
Foreground(style.GetForeground()).
|
|
Underline(true)
|
|
}
|
|
}
|
|
|
|
// Styles.VisualHighlightWithTextColor: Works analogously to CursorStyle vs DefaultCursorStyle. When a
|
|
// style is available, this function should be used, so the text color will be rendered in front
|
|
// of the background. Otherwise, the VisualHighlight property will always work.
|
|
func (s Styles) VisualHighlightWithTextColor(style lipgloss.Style) lipgloss.Style {
|
|
return lipgloss.NewStyle().
|
|
Background(s.VisualHighlight.GetBackground()).
|
|
Foreground(style.GetForeground())
|
|
}
|
|
|
|
// Styles.MakeStyleMap: Generates a style map for a single line. A style map is a mapping from
|
|
// column a lipgloss style. Cursor styles are not handled by this map, but they can be derived
|
|
// by inverting the background and foreground (and rolling back to the default).
|
|
func (s Styles) MakeStyleMap(lexer chroma.Lexer, line string) []lipgloss.Style {
|
|
m := make([]lipgloss.Style, len(line))
|
|
|
|
if s.ChromaStyle == nil {
|
|
return m
|
|
}
|
|
|
|
iter, err := lexer.Tokenise(nil, line)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
col := 0
|
|
for _, token := range iter.Tokens() {
|
|
entry := s.ChromaStyle.Get(token.Type)
|
|
s := lipgloss.NewStyle().
|
|
Background(lipgloss.Color(entry.Background.String())).
|
|
Foreground(lipgloss.Color(entry.Colour.String()))
|
|
for _, char := range token.Value {
|
|
if char == '\n' {
|
|
continue
|
|
}
|
|
if col < len(m) {
|
|
m[col] = s
|
|
}
|
|
col++
|
|
}
|
|
}
|
|
|
|
return m
|
|
}
|
|
|
|
// darkenColor: Uses a factor (0.0 to 1.0) to darken a color using its opacity.
|
|
func darkenColor(c chroma.Colour, factor float64) chroma.Colour {
|
|
r := uint8(float64(c.Red()) * factor)
|
|
g := uint8(float64(c.Green()) * factor)
|
|
b := uint8(float64(c.Blue()) * factor)
|
|
return chroma.NewColour(r, g, b)
|
|
}
|