diff --git a/.gitignore b/.gitignore index 53e1c2b7..bf2e81ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ ssh_example_ed25519* +.idea/ diff --git a/get.go b/get.go index c8527a2a..0b036a81 100644 --- a/get.go +++ b/get.go @@ -24,6 +24,30 @@ func (s Style) GetUnderline() bool { return s.getAsBool(underlineKey, false) } +// GetDoubleUnderline returns the style's double underline value. If no value is set false is +// returned. +func (s Style) GetDoubleUnderline() bool { + return s.getAsBool(doubleUnderlineKey, false) +} + +// GetCurlyUnderline returns the style's curly underline value. If no value is set false is +// returned. +func (s Style) GetCurlyUnderline() bool { + return s.getAsBool(curlyUnderlineKey, false) +} + +// GetDottedUnderline returns the style's dotted underline value. If no value is set false is +// returned. +func (s Style) GetDottedUnderline() bool { + return s.getAsBool(dottedUnderlineKey, false) +} + +// GetDashedUnderline returns the style's dashed underline value. If no value is set false is +// returned. +func (s Style) GetDashedUnderline() bool { + return s.getAsBool(dashedUnderlineKey, false) +} + // GetStrikethrough returns the style's strikethrough value. If no value is set false // is returned. func (s Style) GetStrikethrough() bool { @@ -60,6 +84,12 @@ func (s Style) GetBackground() color.Color { return s.getAsColor(backgroundKey) } +// GetUnderlineColor returns the style's underline color. If no value is set +// NoColor{} is returned. +func (s Style) GetUnderlineColor() color.Color { + return s.getAsColor(underlineColorKey) +} + // GetWidth returns the style's width setting. If no width is set 0 is // returned. func (s Style) GetWidth() int { @@ -438,6 +468,8 @@ func (s Style) getAsColor(k propKey) color.Color { c = s.fgColor case backgroundKey: c = s.bgColor + case underlineColorKey: + c = s.underlineColor case marginBackgroundKey: c = s.marginBgColor case borderTopForegroundKey: diff --git a/hyperlink.go b/hyperlink.go new file mode 100644 index 00000000..64990cab --- /dev/null +++ b/hyperlink.go @@ -0,0 +1,9 @@ +package lipgloss + +import ( + "github.com/charmbracelet/x/ansi" +) + +func Hyperlink(uri string, name string, params ...string) string { + return ansi.SetHyperlink(uri, params...) + name + ansi.ResetHyperlink(params...) + ansi.ResetStyle +} diff --git a/set.go b/set.go index 60937513..3365d1fa 100644 --- a/set.go +++ b/set.go @@ -13,6 +13,8 @@ func (s *Style) set(key propKey, value interface{}) { s.fgColor = colorOrNil(value) case backgroundKey: s.bgColor = colorOrNil(value) + case underlineColorKey: + s.underlineColor = colorOrNil(value) case widthKey: s.width = max(0, value.(int)) case heightKey: @@ -95,6 +97,8 @@ func (s *Style) setFrom(key propKey, i Style) { s.set(foregroundKey, i.fgColor) case backgroundKey: s.set(backgroundKey, i.bgColor) + case underlineColorKey: + s.set(underlineColorKey, i.underlineColor) case widthKey: s.set(widthKey, i.width) case heightKey: @@ -181,6 +185,38 @@ func (s Style) Underline(v bool) Style { return s } +// DoubleUnderline sets a double underline rule. By default, underlines will not +// be drawn on whitespace like margins and padding. To change this behavior set +// UnderlineSpaces. +func (s Style) DoubleUnderline(v bool) Style { + s.set(doubleUnderlineKey, v) + return s +} + +// CurlyUnderline sets a curly underline rule. By default, underlines will not +// be drawn on whitespace like margins and padding. To change this behavior set +// UnderlineSpaces. +func (s Style) CurlyUnderline(v bool) Style { + s.set(curlyUnderlineKey, v) + return s +} + +// DottedUnderline sets a dotted underline rule. By default, underlines will not +// be drawn on whitespace like margins and padding. To change this behavior set +// UnderlineSpaces. +func (s Style) DottedUnderline(v bool) Style { + s.set(dottedUnderlineKey, v) + return s +} + +// DashedUnderline sets a dashed underline rule. By default, underlines will not +// be drawn on whitespace like margins and padding. To change this behavior set +// UnderlineSpaces. +func (s Style) DashedUnderline(v bool) Style { + s.set(dashedUnderlineKey, v) + return s +} + // Strikethrough sets a strikethrough rule. By default, strikes will not be // drawn on whitespace like margins and padding. To change this behavior set // StrikethroughSpaces. @@ -225,6 +261,13 @@ func (s Style) Background(c color.Color) Style { return s } +// UnderlineColor sets an underline color rule. This can be used to change the +// underline color to a different color than the foreground color. +func (s Style) UnderlineColor(c color.Color) Style { + s.set(underlineColorKey, c) + return s +} + // Width sets the width of the block before applying margins. The width, if // set, also determines where text will wrap. func (s Style) Width(i int) Style { diff --git a/style.go b/style.go index b4d4627e..40ff00cf 100644 --- a/style.go +++ b/style.go @@ -19,6 +19,10 @@ const ( boldKey propKey = 1 << iota italicKey underlineKey + doubleUnderlineKey + curlyUnderlineKey + dottedUnderlineKey + dashedUnderlineKey strikethroughKey reverseKey blinkKey @@ -30,6 +34,7 @@ const ( // Non-boolean props. foregroundKey backgroundKey + underlineColorKey widthKey heightKey alignHorizontalKey @@ -111,8 +116,9 @@ type Style struct { attrs int // props that have values - fgColor color.Color - bgColor color.Color + fgColor color.Color + bgColor color.Color + underlineColor color.Color width int height int @@ -231,16 +237,21 @@ func (s Style) Render(strs ...string) string { teSpace ansi.Style teWhitespace ansi.Style - bold = s.getAsBool(boldKey, false) - italic = s.getAsBool(italicKey, false) - underline = s.getAsBool(underlineKey, false) - strikethrough = s.getAsBool(strikethroughKey, false) - reverse = s.getAsBool(reverseKey, false) - blink = s.getAsBool(blinkKey, false) - faint = s.getAsBool(faintKey, false) - - fg = s.getAsColor(foregroundKey) - bg = s.getAsColor(backgroundKey) + bold = s.getAsBool(boldKey, false) + italic = s.getAsBool(italicKey, false) + underline = s.getAsBool(underlineKey, false) + doubleUnderline = s.getAsBool(doubleUnderlineKey, false) + curlyUnderline = s.getAsBool(curlyUnderlineKey, false) + dottedUnderline = s.getAsBool(dottedUnderlineKey, false) + dashedUnderline = s.getAsBool(dashedUnderlineKey, false) + strikethrough = s.getAsBool(strikethroughKey, false) + reverse = s.getAsBool(reverseKey, false) + blink = s.getAsBool(blinkKey, false) + faint = s.getAsBool(faintKey, false) + + fg = s.getAsColor(foregroundKey) + bg = s.getAsColor(backgroundKey) + underlineColor = s.getAsColor(underlineColorKey) width = s.getAsInt(widthKey) height = s.getAsInt(heightKey) @@ -257,15 +268,19 @@ func (s Style) Render(strs ...string) string { maxWidth = s.getAsInt(maxWidthKey) maxHeight = s.getAsInt(maxHeightKey) - underlineSpaces = s.getAsBool(underlineSpacesKey, false) || (underline && s.getAsBool(underlineSpacesKey, true)) - strikethroughSpaces = s.getAsBool(strikethroughSpacesKey, false) || (strikethrough && s.getAsBool(strikethroughSpacesKey, true)) + underlineSpaces = s.getAsBool(underlineSpacesKey, false) || (underline && s.getAsBool(underlineSpacesKey, true)) + doubleUnderlineSpaces = s.getAsBool(underlineSpacesKey, false) || (doubleUnderline && s.getAsBool(underlineSpacesKey, true)) + curlyUnderlineSpaces = s.getAsBool(underlineSpacesKey, false) || (curlyUnderline && s.getAsBool(underlineSpacesKey, true)) + dottedUnderlineSpaces = s.getAsBool(underlineSpacesKey, false) || (dottedUnderline && s.getAsBool(underlineSpacesKey, true)) + dashedUnderlineSpaces = s.getAsBool(underlineSpacesKey, false) || (dashedUnderline && s.getAsBool(underlineSpacesKey, true)) + strikethroughSpaces = s.getAsBool(strikethroughSpacesKey, false) || (strikethrough && s.getAsBool(strikethroughSpacesKey, true)) // Do we need to style whitespace (padding and space outside // paragraphs) separately? styleWhitespace = reverse // Do we need to style spaces separately? - useSpaceStyler = (underline && !underlineSpaces) || (strikethrough && !strikethroughSpaces) || underlineSpaces || strikethroughSpaces + useSpaceStyler = ((underline || doubleUnderline || curlyUnderline || dottedUnderline || dashedUnderline) && !underlineSpaces) || (strikethrough && !strikethroughSpaces) || underline || doubleUnderline || curlyUnderline || dottedUnderline || dashedUnderline || strikethroughSpaces transform = s.getAsTransform(transformKey) ) @@ -287,6 +302,19 @@ func (s Style) Render(strs ...string) string { if underline { te = te.Underline() } + if doubleUnderline { + te = te.DoubleUnderline() + } + if curlyUnderline { + te = te.CurlyUnderline() + } + if dottedUnderline { + te = te.DottedUnderline() + } + if dashedUnderline { + te = te.DashedUnderline() + } + if reverse { teWhitespace = teWhitespace.Reverse() te = te.Reverse() @@ -318,9 +346,16 @@ func (s Style) Render(strs ...string) string { } } - if underline { - te = te.Underline() + if underlineColor != noColor { + te = te.UnderlineColor(underlineColor) + if colorWhitespace { + teWhitespace = teWhitespace.UnderlineColor(underlineColor) + } + if useSpaceStyler { + teSpace = teSpace.UnderlineColor(underlineColor) + } } + if strikethrough { te = te.Strikethrough() } @@ -328,6 +363,18 @@ func (s Style) Render(strs ...string) string { if underlineSpaces { teSpace = teSpace.Underline() } + if doubleUnderlineSpaces { + teSpace = teSpace.DoubleUnderline() + } + if curlyUnderlineSpaces { + teSpace = teSpace.CurlyUnderline() + } + if dottedUnderlineSpaces { + teSpace = teSpace.DottedUnderline() + } + if dashedUnderlineSpaces { + teSpace = teSpace.DashedUnderline() + } if strikethroughSpaces { teSpace = teSpace.Strikethrough() } diff --git a/unset.go b/unset.go index 1086e722..5ead0009 100644 --- a/unset.go +++ b/unset.go @@ -23,6 +23,36 @@ func (s Style) UnsetUnderline() Style { return s } +// UnsetDoubleUnderline removes the double underline style rule, if set. +func (s Style) UnsetDoubleUnderline() Style { + s.unset(doubleUnderlineKey) + return s +} + +// UnsetCurlyUnderline removes the curly underline style rule, if set. +func (s Style) UnsetCurlyUnderline() Style { + s.unset(curlyUnderlineKey) + return s +} + +// UnsetDottedUnderline removes the dotted underline style rule, if set. +func (s Style) UnsetDottedUnderline() Style { + s.unset(dottedUnderlineKey) + return s +} + +// UnsetDashedUnderline removes the dashed underline style rule, if set. +func (s Style) UnsetDashedUnderline() Style { + s.unset(dashedUnderlineKey) + return s +} + +// UnsetUnderlineColor removes the underline color style rule, if set. +func (s Style) UnsetUnderlineColor() Style { + s.unset(underlineColorKey) + return s +} + // UnsetStrikethrough removes the strikethrough style rule, if set. func (s Style) UnsetStrikethrough() Style { s.unset(strikethroughKey)