232 lines
6.8 KiB
Go
Executable File
232 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))
|
|
|
|
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)
|
|
}
|