Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(chroma): export charm themes #195

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ jobs:
build:
strategy:
matrix:
go-version: [~1.13, ^1]
go-version: [~1.16, ^1]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
env:
Expand Down
104 changes: 104 additions & 0 deletions ansi/chroma.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package ansi

import (
"sync"

"github.com/alecthomas/chroma/v2"
"github.com/alecthomas/chroma/v2/styles"
)

var (
// mutex for synchronizing access to the chroma style registry.
// Related https://github.com/alecthomas/chroma/pull/650
mutex = sync.Mutex{}
)

func chromaStyle(style StylePrimitive) string {
var s string

if style.Color != nil {
s = *style.Color
}
if style.BackgroundColor != nil {
if s != "" {
s += " "
}
s += "bg:" + *style.BackgroundColor
}
if style.Italic != nil && *style.Italic {
if s != "" {
s += " "
}
s += "italic"
}
if style.Bold != nil && *style.Bold {
if s != "" {
s += " "
}
s += "bold"
}
if style.Underline != nil && *style.Underline {
if s != "" {
s += " "
}
s += "underline"
}

return s
}

// ChromaRegister registers a chroma style from a glamour style.
func ChromaRegister(cfg *StyleConfig) *chroma.Style {
var style *chroma.Style
rules := cfg.CodeBlock
theme := rules.Theme
if theme == "" && rules.Chroma == nil {
return nil
}

mutex.Lock()
// Register the theme if it doesn't already exist.
_, ok := styles.Registry[theme]
if !ok && rules.Chroma != nil {
style = styles.Register(
chroma.MustNewStyle(theme,
chroma.StyleEntries{
chroma.Text: chromaStyle(rules.Chroma.Text),
chroma.Error: chromaStyle(rules.Chroma.Error),
chroma.Comment: chromaStyle(rules.Chroma.Comment),
chroma.CommentPreproc: chromaStyle(rules.Chroma.CommentPreproc),
chroma.Keyword: chromaStyle(rules.Chroma.Keyword),
chroma.KeywordReserved: chromaStyle(rules.Chroma.KeywordReserved),
chroma.KeywordNamespace: chromaStyle(rules.Chroma.KeywordNamespace),
chroma.KeywordType: chromaStyle(rules.Chroma.KeywordType),
chroma.Operator: chromaStyle(rules.Chroma.Operator),
chroma.Punctuation: chromaStyle(rules.Chroma.Punctuation),
chroma.Name: chromaStyle(rules.Chroma.Name),
chroma.NameBuiltin: chromaStyle(rules.Chroma.NameBuiltin),
chroma.NameTag: chromaStyle(rules.Chroma.NameTag),
chroma.NameAttribute: chromaStyle(rules.Chroma.NameAttribute),
chroma.NameClass: chromaStyle(rules.Chroma.NameClass),
chroma.NameConstant: chromaStyle(rules.Chroma.NameConstant),
chroma.NameDecorator: chromaStyle(rules.Chroma.NameDecorator),
chroma.NameException: chromaStyle(rules.Chroma.NameException),
chroma.NameFunction: chromaStyle(rules.Chroma.NameFunction),
chroma.NameOther: chromaStyle(rules.Chroma.NameOther),
chroma.Literal: chromaStyle(rules.Chroma.Literal),
chroma.LiteralNumber: chromaStyle(rules.Chroma.LiteralNumber),
chroma.LiteralDate: chromaStyle(rules.Chroma.LiteralDate),
chroma.LiteralString: chromaStyle(rules.Chroma.LiteralString),
chroma.LiteralStringEscape: chromaStyle(rules.Chroma.LiteralStringEscape),
chroma.GenericDeleted: chromaStyle(rules.Chroma.GenericDeleted),
chroma.GenericEmph: chromaStyle(rules.Chroma.GenericEmph),
chroma.GenericInserted: chromaStyle(rules.Chroma.GenericInserted),
chroma.GenericStrong: chromaStyle(rules.Chroma.GenericStrong),
chroma.GenericSubheading: chromaStyle(rules.Chroma.GenericSubheading),
chroma.Background: chromaStyle(rules.Chroma.Background),
},
),
)
}
mutex.Unlock()

return style
}
108 changes: 13 additions & 95 deletions ansi/codeblock.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,131 +2,49 @@ package ansi

import (
"io"
"sync"

"github.com/alecthomas/chroma"
"github.com/alecthomas/chroma/quick"
"github.com/alecthomas/chroma/styles"
"github.com/alecthomas/chroma/v2/quick"
"github.com/muesli/reflow/indent"
"github.com/muesli/termenv"
)

const (
// The chroma style theme name used for rendering.
chromaStyleTheme = "charm"
)

var (
// mutex for synchronizing access to the chroma style registry.
// Related https://github.com/alecthomas/chroma/pull/650
mutex = sync.Mutex{}
)

// A CodeBlockElement is used to render code blocks.
type CodeBlockElement struct {
Code string
Language string
}

func chromaStyle(style StylePrimitive) string {
var s string

if style.Color != nil {
s = *style.Color
}
if style.BackgroundColor != nil {
if s != "" {
s += " "
}
s += "bg:" + *style.BackgroundColor
}
if style.Italic != nil && *style.Italic {
if s != "" {
s += " "
}
s += "italic"
}
if style.Bold != nil && *style.Bold {
if s != "" {
s += " "
}
s += "bold"
}
if style.Underline != nil && *style.Underline {
if s != "" {
s += " "
}
s += "underline"
}

return s
}

func (e *CodeBlockElement) Render(w io.Writer, ctx RenderContext) error {
bs := ctx.blockStack

var indentation uint
var margin uint
rules := ctx.options.Styles.CodeBlock
theme := rules.Theme

if rules.Indent != nil {
indentation = *rules.Indent
}
if rules.Margin != nil {
margin = *rules.Margin
}
theme := rules.Theme

if rules.Chroma != nil && ctx.options.ColorProfile != termenv.Ascii {
theme = chromaStyleTheme
mutex.Lock()
// Don't register the style if it's already registered.
_, ok := styles.Registry[theme]
if !ok {
styles.Register(chroma.MustNewStyle(theme,
chroma.StyleEntries{
chroma.Text: chromaStyle(rules.Chroma.Text),
chroma.Error: chromaStyle(rules.Chroma.Error),
chroma.Comment: chromaStyle(rules.Chroma.Comment),
chroma.CommentPreproc: chromaStyle(rules.Chroma.CommentPreproc),
chroma.Keyword: chromaStyle(rules.Chroma.Keyword),
chroma.KeywordReserved: chromaStyle(rules.Chroma.KeywordReserved),
chroma.KeywordNamespace: chromaStyle(rules.Chroma.KeywordNamespace),
chroma.KeywordType: chromaStyle(rules.Chroma.KeywordType),
chroma.Operator: chromaStyle(rules.Chroma.Operator),
chroma.Punctuation: chromaStyle(rules.Chroma.Punctuation),
chroma.Name: chromaStyle(rules.Chroma.Name),
chroma.NameBuiltin: chromaStyle(rules.Chroma.NameBuiltin),
chroma.NameTag: chromaStyle(rules.Chroma.NameTag),
chroma.NameAttribute: chromaStyle(rules.Chroma.NameAttribute),
chroma.NameClass: chromaStyle(rules.Chroma.NameClass),
chroma.NameConstant: chromaStyle(rules.Chroma.NameConstant),
chroma.NameDecorator: chromaStyle(rules.Chroma.NameDecorator),
chroma.NameException: chromaStyle(rules.Chroma.NameException),
chroma.NameFunction: chromaStyle(rules.Chroma.NameFunction),
chroma.NameOther: chromaStyle(rules.Chroma.NameOther),
chroma.Literal: chromaStyle(rules.Chroma.Literal),
chroma.LiteralNumber: chromaStyle(rules.Chroma.LiteralNumber),
chroma.LiteralDate: chromaStyle(rules.Chroma.LiteralDate),
chroma.LiteralString: chromaStyle(rules.Chroma.LiteralString),
chroma.LiteralStringEscape: chromaStyle(rules.Chroma.LiteralStringEscape),
chroma.GenericDeleted: chromaStyle(rules.Chroma.GenericDeleted),
chroma.GenericEmph: chromaStyle(rules.Chroma.GenericEmph),
chroma.GenericInserted: chromaStyle(rules.Chroma.GenericInserted),
chroma.GenericStrong: chromaStyle(rules.Chroma.GenericStrong),
chroma.GenericSubheading: chromaStyle(rules.Chroma.GenericSubheading),
chroma.Background: chromaStyle(rules.Chroma.Background),
}))
}
mutex.Unlock()
}

iw := indent.NewWriterPipe(w, indentation+margin, func(wr io.Writer) {
renderText(w, ctx.options.ColorProfile, bs.Current().Style.StylePrimitive, " ")
})

if len(theme) > 0 {
var formatter string
switch ctx.options.ColorProfile {
case termenv.TrueColor, termenv.ANSI256:
formatter = "terminal256"
case termenv.ANSI:
formatter = "terminal16"
default:
formatter = "terminal"
}
renderText(iw, ctx.options.ColorProfile, bs.Current().Style.StylePrimitive, rules.BlockPrefix)
err := quick.Highlight(iw, e.Code, e.Language, "terminal256", theme)
err := quick.Highlight(iw, e.Code, e.Language, formatter, theme)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions ansi/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type RenderContext struct {

// NewRenderContext returns a new RenderContext.
func NewRenderContext(options Options) RenderContext {
ChromaRegister(&options.Styles)
return RenderContext{
options: options,
blockStack: &BlockStack{},
Expand Down
1 change: 1 addition & 0 deletions ansi/style.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ type StyleBlock struct {
// StyleCodeBlock holds the style settings for a code block.
type StyleCodeBlock struct {
StyleBlock
// Theme is a chroma theme name.
Theme string `json:"theme,omitempty"`
Chroma *Chroma `json:"chroma,omitempty"`
}
Expand Down
9 changes: 9 additions & 0 deletions chroma.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package glamour

import "github.com/charmbracelet/glamour/ansi"

func init() {
for _, style := range DefaultStyles {
ansi.ChromaRegister(style)
}
}
87 changes: 1 addition & 86 deletions dracula.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,98 +111,13 @@ var DraculaStyleConfig = ansi.StyleConfig{
},
},
CodeBlock: ansi.StyleCodeBlock{
Theme: "dracula",
StyleBlock: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{
Color: stringPtr("#ffb86c"),
},
Margin: uintPtr(2),
},
Chroma: &ansi.Chroma{
Text: ansi.StylePrimitive{
Color: stringPtr("#f8f8f2"),
},
Error: ansi.StylePrimitive{
Color: stringPtr("#f8f8f2"),
BackgroundColor: stringPtr("#ff5555"),
},
Comment: ansi.StylePrimitive{
Color: stringPtr("#6272A4"),
},
CommentPreproc: ansi.StylePrimitive{
Color: stringPtr("#ff79c6"),
},
Keyword: ansi.StylePrimitive{
Color: stringPtr("#ff79c6"),
},
KeywordReserved: ansi.StylePrimitive{
Color: stringPtr("#ff79c6"),
},
KeywordNamespace: ansi.StylePrimitive{
Color: stringPtr("#ff79c6"),
},
KeywordType: ansi.StylePrimitive{
Color: stringPtr("#8be9fd"),
},
Operator: ansi.StylePrimitive{
Color: stringPtr("#ff79c6"),
},
Punctuation: ansi.StylePrimitive{
Color: stringPtr("#f8f8f2"),
},
Name: ansi.StylePrimitive{
Color: stringPtr("#8be9fd"),
},
NameBuiltin: ansi.StylePrimitive{
Color: stringPtr("#8be9fd"),
},
NameTag: ansi.StylePrimitive{
Color: stringPtr("#ff79c6"),
},
NameAttribute: ansi.StylePrimitive{
Color: stringPtr("#50fa7b"),
},
NameClass: ansi.StylePrimitive{
Color: stringPtr("#8be9fd"),
},
NameConstant: ansi.StylePrimitive{
Color: stringPtr("#bd93f9"),
},
NameDecorator: ansi.StylePrimitive{
Color: stringPtr("#50fa7b"),
},
NameFunction: ansi.StylePrimitive{
Color: stringPtr("#50fa7b"),
},
LiteralNumber: ansi.StylePrimitive{
Color: stringPtr("#6EEFC0"),
},
LiteralString: ansi.StylePrimitive{
Color: stringPtr("#f1fa8c"),
},
LiteralStringEscape: ansi.StylePrimitive{
Color: stringPtr("#ff79c6"),
},
GenericDeleted: ansi.StylePrimitive{
Color: stringPtr("#ff5555"),
},
GenericEmph: ansi.StylePrimitive{
Color: stringPtr("#f1fa8c"),
Italic: boolPtr(true),
},
GenericInserted: ansi.StylePrimitive{
Color: stringPtr("#50fa7b"),
},
GenericStrong: ansi.StylePrimitive{
Color: stringPtr("#ffb86c"),
Bold: boolPtr(true),
},
GenericSubheading: ansi.StylePrimitive{
Color: stringPtr("#bd93f9"),
},
Background: ansi.StylePrimitive{
BackgroundColor: stringPtr("#282a36"),
},
},
},
Table: ansi.StyleTable{
StyleBlock: ansi.StyleBlock{
Expand Down
Loading