Skip to content
Snippets Groups Projects
Verified Commit 260e43dd authored by Volker Schukai's avatar Volker Schukai :alien:
Browse files

fix: exclude files

parent fbefdfc5
No related branches found
No related tags found
No related merge requests found
Showing
with 1449 additions and 104 deletions
...@@ -4,26 +4,30 @@ import ( ...@@ -4,26 +4,30 @@ import (
"strings" "strings"
"unicode" "unicode"
"github.com/muesli/reflow/truncate" "github.com/charmbracelet/x/ansi"
"github.com/muesli/reflow/wordwrap"
"github.com/muesli/reflow/wrap"
"github.com/muesli/termenv" "github.com/muesli/termenv"
) )
const tabWidthDefault = 4 const tabWidthDefault = 4
// Property for a key. // Property for a key.
type propKey int type propKey int64
// Available properties. // Available properties.
const ( const (
boldKey propKey = iota // Boolean props come first.
boldKey propKey = 1 << iota
italicKey italicKey
underlineKey underlineKey
strikethroughKey strikethroughKey
reverseKey reverseKey
blinkKey blinkKey
faintKey faintKey
underlineSpacesKey
strikethroughSpacesKey
colorWhitespaceKey
// Non-boolean props.
foregroundKey foregroundKey
backgroundKey backgroundKey
widthKey widthKey
...@@ -37,8 +41,6 @@ const ( ...@@ -37,8 +41,6 @@ const (
paddingBottomKey paddingBottomKey
paddingLeftKey paddingLeftKey
colorWhitespaceKey
// Margins. // Margins.
marginTopKey marginTopKey
marginRightKey marginRightKey
...@@ -71,14 +73,27 @@ const ( ...@@ -71,14 +73,27 @@ const (
maxWidthKey maxWidthKey
maxHeightKey maxHeightKey
tabWidthKey tabWidthKey
underlineSpacesKey
strikethroughSpacesKey
transformKey transformKey
) )
// A set of properties. // props is a set of properties.
type rules map[propKey]interface{} type props int64
// set sets a property.
func (p props) set(k propKey) props {
return p | props(k)
}
// unset unsets a property.
func (p props) unset(k propKey) props {
return p &^ props(k)
}
// has checks if a property is set.
func (p props) has(k propKey) bool {
return p&props(k) != 0
}
// NewStyle returns a new, empty Style. While it's syntactic sugar for the // NewStyle returns a new, empty Style. While it's syntactic sugar for the
// Style{} primitive, it's recommended to use this function for creating styles // Style{} primitive, it's recommended to use this function for creating styles
...@@ -100,8 +115,48 @@ func (r *Renderer) NewStyle() Style { ...@@ -100,8 +115,48 @@ func (r *Renderer) NewStyle() Style {
// Style contains a set of rules that comprise a style as a whole. // Style contains a set of rules that comprise a style as a whole.
type Style struct { type Style struct {
r *Renderer r *Renderer
rules map[propKey]interface{} props props
value string value string
// we store bool props values here
attrs int
// props that have values
fgColor TerminalColor
bgColor TerminalColor
width int
height int
alignHorizontal Position
alignVertical Position
paddingTop int
paddingRight int
paddingBottom int
paddingLeft int
marginTop int
marginRight int
marginBottom int
marginLeft int
marginBgColor TerminalColor
borderStyle Border
borderTopFgColor TerminalColor
borderRightFgColor TerminalColor
borderBottomFgColor TerminalColor
borderLeftFgColor TerminalColor
borderTopBgColor TerminalColor
borderRightBgColor TerminalColor
borderBottomBgColor TerminalColor
borderLeftBgColor TerminalColor
maxWidth int
maxHeight int
tabWidth int
transform func(string) string
} }
// joinString joins a list of strings into a single string separated with a // joinString joins a list of strings into a single string separated with a
...@@ -133,15 +188,10 @@ func (s Style) String() string { ...@@ -133,15 +188,10 @@ func (s Style) String() string {
} }
// Copy returns a copy of this style, including any underlying string values. // Copy returns a copy of this style, including any underlying string values.
//
// Deprecated: to copy just use assignment (i.e. a := b). All methods also return a new style.
func (s Style) Copy() Style { func (s Style) Copy() Style {
o := NewStyle() return s
o.init()
for k, v := range s.rules {
o.rules[k] = v
}
o.r = s.r
o.value = s.value
return o
} }
// Inherit overlays the style in the argument onto this style by copying each explicitly // Inherit overlays the style in the argument onto this style by copying each explicitly
...@@ -150,9 +200,11 @@ func (s Style) Copy() Style { ...@@ -150,9 +200,11 @@ func (s Style) Copy() Style {
// //
// Margins, padding, and underlying string values are not inherited. // Margins, padding, and underlying string values are not inherited.
func (s Style) Inherit(i Style) Style { func (s Style) Inherit(i Style) Style {
s.init() for k := boldKey; k <= transformKey; k <<= 1 {
if !i.isSet(k) {
continue
}
for k, v := range i.rules {
switch k { //nolint:exhaustive switch k { //nolint:exhaustive
case marginTopKey, marginRightKey, marginBottomKey, marginLeftKey: case marginTopKey, marginRightKey, marginBottomKey, marginLeftKey:
// Margins are not inherited // Margins are not inherited
...@@ -163,14 +215,15 @@ func (s Style) Inherit(i Style) Style { ...@@ -163,14 +215,15 @@ func (s Style) Inherit(i Style) Style {
case backgroundKey: case backgroundKey:
// The margins also inherit the background color // The margins also inherit the background color
if !s.isSet(marginBackgroundKey) && !i.isSet(marginBackgroundKey) { if !s.isSet(marginBackgroundKey) && !i.isSet(marginBackgroundKey) {
s.rules[marginBackgroundKey] = v s.set(marginBackgroundKey, i.bgColor)
} }
} }
if _, exists := s.rules[k]; exists { if s.isSet(k) {
continue continue
} }
s.rules[k] = v
s.setFrom(k, i)
} }
return s return s
} }
...@@ -218,20 +271,24 @@ func (s Style) Render(strs ...string) string { ...@@ -218,20 +271,24 @@ func (s Style) Render(strs ...string) string {
maxWidth = s.getAsInt(maxWidthKey) maxWidth = s.getAsInt(maxWidthKey)
maxHeight = s.getAsInt(maxHeightKey) maxHeight = s.getAsInt(maxHeightKey)
underlineSpaces = underline && s.getAsBool(underlineSpacesKey, true) underlineSpaces = s.getAsBool(underlineSpacesKey, false) || (underline && s.getAsBool(underlineSpacesKey, true))
strikethroughSpaces = strikethrough && s.getAsBool(strikethroughSpacesKey, true) strikethroughSpaces = s.getAsBool(strikethroughSpacesKey, false) || (strikethrough && s.getAsBool(strikethroughSpacesKey, true))
// Do we need to style whitespace (padding and space outside // Do we need to style whitespace (padding and space outside
// paragraphs) separately? // paragraphs) separately?
styleWhitespace = reverse styleWhitespace = reverse
// Do we need to style spaces separately? // Do we need to style spaces separately?
useSpaceStyler = underlineSpaces || strikethroughSpaces useSpaceStyler = (underline && !underlineSpaces) || (strikethrough && !strikethroughSpaces) || underlineSpaces || strikethroughSpaces
transform = s.getAsTransform(transformKey) transform = s.getAsTransform(transformKey)
) )
if len(s.rules) == 0 { if transform != nil {
str = transform(str)
}
if s.props == 0 {
return s.maybeConvertTabs(str) return s.maybeConvertTabs(str)
} }
...@@ -306,8 +363,7 @@ func (s Style) Render(strs ...string) string { ...@@ -306,8 +363,7 @@ func (s Style) Render(strs ...string) string {
// Word wrap // Word wrap
if !inline && width > 0 { if !inline && width > 0 {
wrapAt := width - leftPadding - rightPadding wrapAt := width - leftPadding - rightPadding
str = wordwrap.String(str, wrapAt) str = ansi.Wrap(str, wrapAt, "")
str = wrap.String(str, wrapAt) // force-wrap long strings
} }
// Render core text // Render core text
...@@ -337,7 +393,7 @@ func (s Style) Render(strs ...string) string { ...@@ -337,7 +393,7 @@ func (s Style) Render(strs ...string) string {
} }
// Padding // Padding
if !inline { if !inline { //nolint:nestif
if leftPadding > 0 { if leftPadding > 0 {
var st *termenv.Style var st *termenv.Style
if colorWhitespace || styleWhitespace { if colorWhitespace || styleWhitespace {
...@@ -393,7 +449,7 @@ func (s Style) Render(strs ...string) string { ...@@ -393,7 +449,7 @@ func (s Style) Render(strs ...string) string {
lines := strings.Split(str, "\n") lines := strings.Split(str, "\n")
for i := range lines { for i := range lines {
lines[i] = truncate.String(lines[i], uint(maxWidth)) lines[i] = ansi.Truncate(lines[i], maxWidth, "")
} }
str = strings.Join(lines, "\n") str = strings.Join(lines, "\n")
...@@ -402,11 +458,10 @@ func (s Style) Render(strs ...string) string { ...@@ -402,11 +458,10 @@ func (s Style) Render(strs ...string) string {
// Truncate according to MaxHeight // Truncate according to MaxHeight
if maxHeight > 0 { if maxHeight > 0 {
lines := strings.Split(str, "\n") lines := strings.Split(str, "\n")
str = strings.Join(lines[:min(maxHeight, len(lines))], "\n") height := min(maxHeight, len(lines))
if len(lines) > 0 {
str = strings.Join(lines[:height], "\n")
} }
if transform != nil {
return transform(str)
} }
return str return str
...@@ -508,7 +563,7 @@ func pad(str string, n int, style *termenv.Style) string { ...@@ -508,7 +563,7 @@ func pad(str string, n int, style *termenv.Style) string {
return b.String() return b.String()
} }
func max(a, b int) int { func max(a, b int) int { //nolint:unparam
if a > b { if a > b {
return a return a
} }
......
package lipgloss package lipgloss
// unset unsets a property from a style.
func (s *Style) unset(key propKey) {
s.props = s.props.unset(key)
}
// UnsetBold removes the bold style rule, if set. // UnsetBold removes the bold style rule, if set.
func (s Style) UnsetBold() Style { func (s Style) UnsetBold() Style {
delete(s.rules, boldKey) s.unset(boldKey)
return s return s
} }
// UnsetItalic removes the italic style rule, if set. // UnsetItalic removes the italic style rule, if set.
func (s Style) UnsetItalic() Style { func (s Style) UnsetItalic() Style {
delete(s.rules, italicKey) s.unset(italicKey)
return s return s
} }
// UnsetUnderline removes the underline style rule, if set. // UnsetUnderline removes the underline style rule, if set.
func (s Style) UnsetUnderline() Style { func (s Style) UnsetUnderline() Style {
delete(s.rules, underlineKey) s.unset(underlineKey)
return s return s
} }
// UnsetStrikethrough removes the strikethrough style rule, if set. // UnsetStrikethrough removes the strikethrough style rule, if set.
func (s Style) UnsetStrikethrough() Style { func (s Style) UnsetStrikethrough() Style {
delete(s.rules, strikethroughKey) s.unset(strikethroughKey)
return s return s
} }
// UnsetReverse removes the reverse style rule, if set. // UnsetReverse removes the reverse style rule, if set.
func (s Style) UnsetReverse() Style { func (s Style) UnsetReverse() Style {
delete(s.rules, reverseKey) s.unset(reverseKey)
return s return s
} }
// UnsetBlink removes the blink style rule, if set. // UnsetBlink removes the blink style rule, if set.
func (s Style) UnsetBlink() Style { func (s Style) UnsetBlink() Style {
delete(s.rules, blinkKey) s.unset(blinkKey)
return s return s
} }
// UnsetFaint removes the faint style rule, if set. // UnsetFaint removes the faint style rule, if set.
func (s Style) UnsetFaint() Style { func (s Style) UnsetFaint() Style {
delete(s.rules, faintKey) s.unset(faintKey)
return s return s
} }
// UnsetForeground removes the foreground style rule, if set. // UnsetForeground removes the foreground style rule, if set.
func (s Style) UnsetForeground() Style { func (s Style) UnsetForeground() Style {
delete(s.rules, foregroundKey) s.unset(foregroundKey)
return s return s
} }
// UnsetBackground removes the background style rule, if set. // UnsetBackground removes the background style rule, if set.
func (s Style) UnsetBackground() Style { func (s Style) UnsetBackground() Style {
delete(s.rules, backgroundKey) s.unset(backgroundKey)
return s return s
} }
// UnsetWidth removes the width style rule, if set. // UnsetWidth removes the width style rule, if set.
func (s Style) UnsetWidth() Style { func (s Style) UnsetWidth() Style {
delete(s.rules, widthKey) s.unset(widthKey)
return s return s
} }
// UnsetHeight removes the height style rule, if set. // UnsetHeight removes the height style rule, if set.
func (s Style) UnsetHeight() Style { func (s Style) UnsetHeight() Style {
delete(s.rules, heightKey) s.unset(heightKey)
return s return s
} }
// UnsetAlign removes the horizontal and vertical text alignment style rule, if set. // UnsetAlign removes the horizontal and vertical text alignment style rule, if set.
func (s Style) UnsetAlign() Style { func (s Style) UnsetAlign() Style {
delete(s.rules, alignHorizontalKey) s.unset(alignHorizontalKey)
delete(s.rules, alignVerticalKey) s.unset(alignVerticalKey)
return s return s
} }
// UnsetAlignHorizontal removes the horizontal text alignment style rule, if set. // UnsetAlignHorizontal removes the horizontal text alignment style rule, if set.
func (s Style) UnsetAlignHorizontal() Style { func (s Style) UnsetAlignHorizontal() Style {
delete(s.rules, alignHorizontalKey) s.unset(alignHorizontalKey)
return s return s
} }
// UnsetAlignVertical removes the vertical text alignment style rule, if set. // UnsetAlignVertical removes the vertical text alignment style rule, if set.
func (s Style) UnsetAlignVertical() Style { func (s Style) UnsetAlignVertical() Style {
delete(s.rules, alignVerticalKey) s.unset(alignVerticalKey)
return s return s
} }
// UnsetPadding removes all padding style rules. // UnsetPadding removes all padding style rules.
func (s Style) UnsetPadding() Style { func (s Style) UnsetPadding() Style {
delete(s.rules, paddingLeftKey) s.unset(paddingLeftKey)
delete(s.rules, paddingRightKey) s.unset(paddingRightKey)
delete(s.rules, paddingTopKey) s.unset(paddingTopKey)
delete(s.rules, paddingBottomKey) s.unset(paddingBottomKey)
return s return s
} }
// UnsetPaddingLeft removes the left padding style rule, if set. // UnsetPaddingLeft removes the left padding style rule, if set.
func (s Style) UnsetPaddingLeft() Style { func (s Style) UnsetPaddingLeft() Style {
delete(s.rules, paddingLeftKey) s.unset(paddingLeftKey)
return s return s
} }
// UnsetPaddingRight removes the right padding style rule, if set. // UnsetPaddingRight removes the right padding style rule, if set.
func (s Style) UnsetPaddingRight() Style { func (s Style) UnsetPaddingRight() Style {
delete(s.rules, paddingRightKey) s.unset(paddingRightKey)
return s return s
} }
// UnsetPaddingTop removes the top padding style rule, if set. // UnsetPaddingTop removes the top padding style rule, if set.
func (s Style) UnsetPaddingTop() Style { func (s Style) UnsetPaddingTop() Style {
delete(s.rules, paddingTopKey) s.unset(paddingTopKey)
return s return s
} }
// UnsetPaddingBottom removes the bottom padding style rule, if set. // UnsetPaddingBottom removes the bottom padding style rule, if set.
func (s Style) UnsetPaddingBottom() Style { func (s Style) UnsetPaddingBottom() Style {
delete(s.rules, paddingBottomKey) s.unset(paddingBottomKey)
return s return s
} }
// UnsetColorWhitespace removes the rule for coloring padding, if set. // UnsetColorWhitespace removes the rule for coloring padding, if set.
func (s Style) UnsetColorWhitespace() Style { func (s Style) UnsetColorWhitespace() Style {
delete(s.rules, colorWhitespaceKey) s.unset(colorWhitespaceKey)
return s return s
} }
// UnsetMargins removes all margin style rules. // UnsetMargins removes all margin style rules.
func (s Style) UnsetMargins() Style { func (s Style) UnsetMargins() Style {
delete(s.rules, marginLeftKey) s.unset(marginLeftKey)
delete(s.rules, marginRightKey) s.unset(marginRightKey)
delete(s.rules, marginTopKey) s.unset(marginTopKey)
delete(s.rules, marginBottomKey) s.unset(marginBottomKey)
return s return s
} }
// UnsetMarginLeft removes the left margin style rule, if set. // UnsetMarginLeft removes the left margin style rule, if set.
func (s Style) UnsetMarginLeft() Style { func (s Style) UnsetMarginLeft() Style {
delete(s.rules, marginLeftKey) s.unset(marginLeftKey)
return s return s
} }
// UnsetMarginRight removes the right margin style rule, if set. // UnsetMarginRight removes the right margin style rule, if set.
func (s Style) UnsetMarginRight() Style { func (s Style) UnsetMarginRight() Style {
delete(s.rules, marginRightKey) s.unset(marginRightKey)
return s return s
} }
// UnsetMarginTop removes the top margin style rule, if set. // UnsetMarginTop removes the top margin style rule, if set.
func (s Style) UnsetMarginTop() Style { func (s Style) UnsetMarginTop() Style {
delete(s.rules, marginTopKey) s.unset(marginTopKey)
return s return s
} }
// UnsetMarginBottom removes the bottom margin style rule, if set. // UnsetMarginBottom removes the bottom margin style rule, if set.
func (s Style) UnsetMarginBottom() Style { func (s Style) UnsetMarginBottom() Style {
delete(s.rules, marginBottomKey) s.unset(marginBottomKey)
return s return s
} }
...@@ -161,153 +166,153 @@ func (s Style) UnsetMarginBottom() Style { ...@@ -161,153 +166,153 @@ func (s Style) UnsetMarginBottom() Style {
// margin's background color can be set from the background color of another // margin's background color can be set from the background color of another
// style during inheritance. // style during inheritance.
func (s Style) UnsetMarginBackground() Style { func (s Style) UnsetMarginBackground() Style {
delete(s.rules, marginBackgroundKey) s.unset(marginBackgroundKey)
return s return s
} }
// UnsetBorderStyle removes the border style rule, if set. // UnsetBorderStyle removes the border style rule, if set.
func (s Style) UnsetBorderStyle() Style { func (s Style) UnsetBorderStyle() Style {
delete(s.rules, borderStyleKey) s.unset(borderStyleKey)
return s return s
} }
// UnsetBorderTop removes the border top style rule, if set. // UnsetBorderTop removes the border top style rule, if set.
func (s Style) UnsetBorderTop() Style { func (s Style) UnsetBorderTop() Style {
delete(s.rules, borderTopKey) s.unset(borderTopKey)
return s return s
} }
// UnsetBorderRight removes the border right style rule, if set. // UnsetBorderRight removes the border right style rule, if set.
func (s Style) UnsetBorderRight() Style { func (s Style) UnsetBorderRight() Style {
delete(s.rules, borderRightKey) s.unset(borderRightKey)
return s return s
} }
// UnsetBorderBottom removes the border bottom style rule, if set. // UnsetBorderBottom removes the border bottom style rule, if set.
func (s Style) UnsetBorderBottom() Style { func (s Style) UnsetBorderBottom() Style {
delete(s.rules, borderBottomKey) s.unset(borderBottomKey)
return s return s
} }
// UnsetBorderLeft removes the border left style rule, if set. // UnsetBorderLeft removes the border left style rule, if set.
func (s Style) UnsetBorderLeft() Style { func (s Style) UnsetBorderLeft() Style {
delete(s.rules, borderLeftKey) s.unset(borderLeftKey)
return s return s
} }
// UnsetBorderForeground removes all border foreground color styles, if set. // UnsetBorderForeground removes all border foreground color styles, if set.
func (s Style) UnsetBorderForeground() Style { func (s Style) UnsetBorderForeground() Style {
delete(s.rules, borderTopForegroundKey) s.unset(borderTopForegroundKey)
delete(s.rules, borderRightForegroundKey) s.unset(borderRightForegroundKey)
delete(s.rules, borderBottomForegroundKey) s.unset(borderBottomForegroundKey)
delete(s.rules, borderLeftForegroundKey) s.unset(borderLeftForegroundKey)
return s return s
} }
// UnsetBorderTopForeground removes the top border foreground color rule, // UnsetBorderTopForeground removes the top border foreground color rule,
// if set. // if set.
func (s Style) UnsetBorderTopForeground() Style { func (s Style) UnsetBorderTopForeground() Style {
delete(s.rules, borderTopForegroundKey) s.unset(borderTopForegroundKey)
return s return s
} }
// UnsetBorderRightForeground removes the right border foreground color rule, // UnsetBorderRightForeground removes the right border foreground color rule,
// if set. // if set.
func (s Style) UnsetBorderRightForeground() Style { func (s Style) UnsetBorderRightForeground() Style {
delete(s.rules, borderRightForegroundKey) s.unset(borderRightForegroundKey)
return s return s
} }
// UnsetBorderBottomForeground removes the bottom border foreground color // UnsetBorderBottomForeground removes the bottom border foreground color
// rule, if set. // rule, if set.
func (s Style) UnsetBorderBottomForeground() Style { func (s Style) UnsetBorderBottomForeground() Style {
delete(s.rules, borderBottomForegroundKey) s.unset(borderBottomForegroundKey)
return s return s
} }
// UnsetBorderLeftForeground removes the left border foreground color rule, // UnsetBorderLeftForeground removes the left border foreground color rule,
// if set. // if set.
func (s Style) UnsetBorderLeftForeground() Style { func (s Style) UnsetBorderLeftForeground() Style {
delete(s.rules, borderLeftForegroundKey) s.unset(borderLeftForegroundKey)
return s return s
} }
// UnsetBorderBackground removes all border background color styles, if // UnsetBorderBackground removes all border background color styles, if
// set. // set.
func (s Style) UnsetBorderBackground() Style { func (s Style) UnsetBorderBackground() Style {
delete(s.rules, borderTopBackgroundKey) s.unset(borderTopBackgroundKey)
delete(s.rules, borderRightBackgroundKey) s.unset(borderRightBackgroundKey)
delete(s.rules, borderBottomBackgroundKey) s.unset(borderBottomBackgroundKey)
delete(s.rules, borderLeftBackgroundKey) s.unset(borderLeftBackgroundKey)
return s return s
} }
// UnsetBorderTopBackgroundColor removes the top border background color rule, // UnsetBorderTopBackgroundColor removes the top border background color rule,
// if set. // if set.
func (s Style) UnsetBorderTopBackgroundColor() Style { func (s Style) UnsetBorderTopBackgroundColor() Style {
delete(s.rules, borderTopBackgroundKey) s.unset(borderTopBackgroundKey)
return s return s
} }
// UnsetBorderRightBackground removes the right border background color // UnsetBorderRightBackground removes the right border background color
// rule, if set. // rule, if set.
func (s Style) UnsetBorderRightBackground() Style { func (s Style) UnsetBorderRightBackground() Style {
delete(s.rules, borderRightBackgroundKey) s.unset(borderRightBackgroundKey)
return s return s
} }
// UnsetBorderBottomBackground removes the bottom border background color // UnsetBorderBottomBackground removes the bottom border background color
// rule, if set. // rule, if set.
func (s Style) UnsetBorderBottomBackground() Style { func (s Style) UnsetBorderBottomBackground() Style {
delete(s.rules, borderBottomBackgroundKey) s.unset(borderBottomBackgroundKey)
return s return s
} }
// UnsetBorderLeftBackground removes the left border color rule, if set. // UnsetBorderLeftBackground removes the left border color rule, if set.
func (s Style) UnsetBorderLeftBackground() Style { func (s Style) UnsetBorderLeftBackground() Style {
delete(s.rules, borderLeftBackgroundKey) s.unset(borderLeftBackgroundKey)
return s return s
} }
// UnsetInline removes the inline style rule, if set. // UnsetInline removes the inline style rule, if set.
func (s Style) UnsetInline() Style { func (s Style) UnsetInline() Style {
delete(s.rules, inlineKey) s.unset(inlineKey)
return s return s
} }
// UnsetMaxWidth removes the max width style rule, if set. // UnsetMaxWidth removes the max width style rule, if set.
func (s Style) UnsetMaxWidth() Style { func (s Style) UnsetMaxWidth() Style {
delete(s.rules, maxWidthKey) s.unset(maxWidthKey)
return s return s
} }
// UnsetMaxHeight removes the max height style rule, if set. // UnsetMaxHeight removes the max height style rule, if set.
func (s Style) UnsetMaxHeight() Style { func (s Style) UnsetMaxHeight() Style {
delete(s.rules, maxHeightKey) s.unset(maxHeightKey)
return s return s
} }
// UnsetTabWidth removes the tab width style rule, if set. // UnsetTabWidth removes the tab width style rule, if set.
func (s Style) UnsetTabWidth() Style { func (s Style) UnsetTabWidth() Style {
delete(s.rules, tabWidthKey) s.unset(tabWidthKey)
return s return s
} }
// UnsetUnderlineSpaces removes the value set by UnderlineSpaces. // UnsetUnderlineSpaces removes the value set by UnderlineSpaces.
func (s Style) UnsetUnderlineSpaces() Style { func (s Style) UnsetUnderlineSpaces() Style {
delete(s.rules, underlineSpacesKey) s.unset(underlineSpacesKey)
return s return s
} }
// UnsetStrikethroughSpaces removes the value set by StrikethroughSpaces. // UnsetStrikethroughSpaces removes the value set by StrikethroughSpaces.
func (s Style) UnsetStrikethroughSpaces() Style { func (s Style) UnsetStrikethroughSpaces() Style {
delete(s.rules, strikethroughSpacesKey) s.unset(strikethroughSpacesKey)
return s return s
} }
// UnsetTransform removes the value set by Transform. // UnsetTransform removes the value set by Transform.
func (s Style) UnsetTransform() Style { func (s Style) UnsetTransform() Style {
delete(s.rules, transformKey) s.unset(transformKey)
return s return s
} }
......
...@@ -3,7 +3,7 @@ package lipgloss ...@@ -3,7 +3,7 @@ package lipgloss
import ( import (
"strings" "strings"
"github.com/muesli/reflow/ansi" "github.com/charmbracelet/x/ansi"
"github.com/muesli/termenv" "github.com/muesli/termenv"
) )
...@@ -45,12 +45,12 @@ func (w whitespace) render(width int) string { ...@@ -45,12 +45,12 @@ func (w whitespace) render(width int) string {
if j >= len(r) { if j >= len(r) {
j = 0 j = 0
} }
i += ansi.PrintableRuneWidth(string(r[j])) i += ansi.StringWidth(string(r[j]))
} }
// Fill any extra gaps white spaces. This might be necessary if any runes // Fill any extra gaps white spaces. This might be necessary if any runes
// are more than one cell wide, which could leave a one-rune gap. // are more than one cell wide, which could leave a one-rune gap.
short := width - ansi.PrintableRuneWidth(b.String()) short := width - ansi.StringWidth(b.String())
if short > 0 { if short > 0 {
b.WriteString(strings.Repeat(" ", short)) b.WriteString(strings.Repeat(" ", short))
} }
......
MIT License MIT License
Copyright (c) 2019 Christian Muehlhaeuser Copyright (c) 2023 Charmbracelet, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
......
package ansi
import "io"
// Execute is a function that "execute" the given escape sequence by writing it
// to the provided output writter.
//
// This is a syntactic sugar over [io.WriteString].
func Execute(w io.Writer, s string) (int, error) {
return io.WriteString(w, s)
}
package ansi
const (
// SP is the space character (Char: \x20).
SP = 0x20
// DEL is the delete character (Caret: ^?, Char: \x7f).
DEL = 0x7F
)
package ansi
import (
"image/color"
)
// SetForegroundColor returns a sequence that sets the default terminal
// foreground color.
//
// OSC 10 ; color ST
// OSC 10 ; color BEL
//
// Where color is the encoded color number.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
func SetForegroundColor(c color.Color) string {
return "\x1b]10;" + colorToHexString(c) + "\x07"
}
// RequestForegroundColor is a sequence that requests the current default
// terminal foreground color.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
const RequestForegroundColor = "\x1b]10;?\x07"
// SetBackgroundColor returns a sequence that sets the default terminal
// background color.
//
// OSC 11 ; color ST
// OSC 11 ; color BEL
//
// Where color is the encoded color number.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
func SetBackgroundColor(c color.Color) string {
return "\x1b]11;" + colorToHexString(c) + "\x07"
}
// RequestBackgroundColor is a sequence that requests the current default
// terminal background color.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
const RequestBackgroundColor = "\x1b]11;?\x07"
// SetCursorColor returns a sequence that sets the terminal cursor color.
//
// OSC 12 ; color ST
// OSC 12 ; color BEL
//
// Where color is the encoded color number.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
func SetCursorColor(c color.Color) string {
return "\x1b]12;" + colorToHexString(c) + "\x07"
}
// RequestCursorColor is a sequence that requests the current terminal cursor
// color.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
const RequestCursorColor = "\x1b]12;?\x07"
package ansi
// C0 control characters.
//
// These range from (0x00-0x1F) as defined in ISO 646 (ASCII).
// See: https://en.wikipedia.org/wiki/C0_and_C1_control_codes
const (
// NUL is the null character (Caret: ^@, Char: \0).
NUL = 0x00
// SOH is the start of heading character (Caret: ^A).
SOH = 0x01
// STX is the start of text character (Caret: ^B).
STX = 0x02
// ETX is the end of text character (Caret: ^C).
ETX = 0x03
// EOT is the end of transmission character (Caret: ^D).
EOT = 0x04
// ENQ is the enquiry character (Caret: ^E).
ENQ = 0x05
// ACK is the acknowledge character (Caret: ^F).
ACK = 0x06
// BEL is the bell character (Caret: ^G, Char: \a).
BEL = 0x07
// BS is the backspace character (Caret: ^H, Char: \b).
BS = 0x08
// HT is the horizontal tab character (Caret: ^I, Char: \t).
HT = 0x09
// LF is the line feed character (Caret: ^J, Char: \n).
LF = 0x0A
// VT is the vertical tab character (Caret: ^K, Char: \v).
VT = 0x0B
// FF is the form feed character (Caret: ^L, Char: \f).
FF = 0x0C
// CR is the carriage return character (Caret: ^M, Char: \r).
CR = 0x0D
// SO is the shift out character (Caret: ^N).
SO = 0x0E
// SI is the shift in character (Caret: ^O).
SI = 0x0F
// DLE is the data link escape character (Caret: ^P).
DLE = 0x10
// DC1 is the device control 1 character (Caret: ^Q).
DC1 = 0x11
// DC2 is the device control 2 character (Caret: ^R).
DC2 = 0x12
// DC3 is the device control 3 character (Caret: ^S).
DC3 = 0x13
// DC4 is the device control 4 character (Caret: ^T).
DC4 = 0x14
// NAK is the negative acknowledge character (Caret: ^U).
NAK = 0x15
// SYN is the synchronous idle character (Caret: ^V).
SYN = 0x16
// ETB is the end of transmission block character (Caret: ^W).
ETB = 0x17
// CAN is the cancel character (Caret: ^X).
CAN = 0x18
// EM is the end of medium character (Caret: ^Y).
EM = 0x19
// SUB is the substitute character (Caret: ^Z).
SUB = 0x1A
// ESC is the escape character (Caret: ^[, Char: \e).
ESC = 0x1B
// FS is the file separator character (Caret: ^\).
FS = 0x1C
// GS is the group separator character (Caret: ^]).
GS = 0x1D
// RS is the record separator character (Caret: ^^).
RS = 0x1E
// US is the unit separator character (Caret: ^_).
US = 0x1F
)
package ansi
// C1 control characters.
//
// These range from (0x80-0x9F) as defined in ISO 6429 (ECMA-48).
// See: https://en.wikipedia.org/wiki/C0_and_C1_control_codes
const (
// PAD is the padding character.
PAD = 0x80
// HOP is the high octet preset character.
HOP = 0x81
// BPH is the break permitted here character.
BPH = 0x82
// NBH is the no break here character.
NBH = 0x83
// IND is the index character.
IND = 0x84
// NEL is the next line character.
NEL = 0x85
// SSA is the start of selected area character.
SSA = 0x86
// ESA is the end of selected area character.
ESA = 0x87
// HTS is the horizontal tab set character.
HTS = 0x88
// HTJ is the horizontal tab with justification character.
HTJ = 0x89
// VTS is the vertical tab set character.
VTS = 0x8A
// PLD is the partial line forward character.
PLD = 0x8B
// PLU is the partial line backward character.
PLU = 0x8C
// RI is the reverse index character.
RI = 0x8D
// SS2 is the single shift 2 character.
SS2 = 0x8E
// SS3 is the single shift 3 character.
SS3 = 0x8F
// DCS is the device control string character.
DCS = 0x90
// PU1 is the private use 1 character.
PU1 = 0x91
// PU2 is the private use 2 character.
PU2 = 0x92
// STS is the set transmit state character.
STS = 0x93
// CCH is the cancel character.
CCH = 0x94
// MW is the message waiting character.
MW = 0x95
// SPA is the start of guarded area character.
SPA = 0x96
// EPA is the end of guarded area character.
EPA = 0x97
// SOS is the start of string character.
SOS = 0x98
// SGCI is the single graphic character introducer character.
SGCI = 0x99
// SCI is the single character introducer character.
SCI = 0x9A
// CSI is the control sequence introducer character.
CSI = 0x9B
// ST is the string terminator character.
ST = 0x9C
// OSC is the operating system command character.
OSC = 0x9D
// PM is the privacy message character.
PM = 0x9E
// APC is the application program command character.
APC = 0x9F
)
package ansi
import "encoding/base64"
// Clipboard names.
const (
SystemClipboard = 'c'
PrimaryClipboard = 'p'
)
// SetClipboard returns a sequence for manipulating the clipboard.
//
// OSC 52 ; Pc ; Pd ST
// OSC 52 ; Pc ; Pd BEL
//
// Where Pc is the clipboard name and Pd is the base64 encoded data.
// Empty data or invalid base64 data will reset the clipboard.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
func SetClipboard(c byte, d string) string {
if d != "" {
d = base64.StdEncoding.EncodeToString([]byte(d))
}
return "\x1b]52;" + string(c) + ";" + d + "\x07"
}
// SetSystemClipboard returns a sequence for setting the system clipboard.
//
// This is equivalent to SetClipboard(SystemClipboard, d).
func SetSystemClipboard(d string) string {
return SetClipboard(SystemClipboard, d)
}
// SetPrimaryClipboard returns a sequence for setting the primary clipboard.
//
// This is equivalent to SetClipboard(PrimaryClipboard, d).
func SetPrimaryClipboard(d string) string {
return SetClipboard(PrimaryClipboard, d)
}
// ResetClipboard returns a sequence for resetting the clipboard.
//
// This is equivalent to SetClipboard(c, "").
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
func ResetClipboard(c byte) string {
return SetClipboard(c, "")
}
// ResetSystemClipboard is a sequence for resetting the system clipboard.
//
// This is equivalent to ResetClipboard(SystemClipboard).
const ResetSystemClipboard = "\x1b]52;c;\x07"
// ResetPrimaryClipboard is a sequence for resetting the primary clipboard.
//
// This is equivalent to ResetClipboard(PrimaryClipboard).
const ResetPrimaryClipboard = "\x1b]52;p;\x07"
// RequestClipboard returns a sequence for requesting the clipboard.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
func RequestClipboard(c byte) string {
return "\x1b]52;" + string(c) + ";?\x07"
}
// RequestSystemClipboard is a sequence for requesting the system clipboard.
//
// This is equivalent to RequestClipboard(SystemClipboard).
const RequestSystemClipboard = "\x1b]52;c;?\x07"
// RequestPrimaryClipboard is a sequence for requesting the primary clipboard.
//
// This is equivalent to RequestClipboard(PrimaryClipboard).
const RequestPrimaryClipboard = "\x1b]52;p;?\x07"
package ansi
import (
"image/color"
)
// Technically speaking, the 16 basic ANSI colors are arbitrary and can be
// customized at the terminal level. Given that, we're returning what we feel
// are good defaults.
//
// This could also be a slice, but we use a map to make the mappings very
// explicit.
//
// See: https://www.ditig.com/publications/256-colors-cheat-sheet
var lowANSI = map[uint32]uint32{
0: 0x000000, // black
1: 0x800000, // red
2: 0x008000, // green
3: 0x808000, // yellow
4: 0x000080, // blue
5: 0x800080, // magenta
6: 0x008080, // cyan
7: 0xc0c0c0, // white
8: 0x808080, // bright black
9: 0xff0000, // bright red
10: 0x00ff00, // bright green
11: 0xffff00, // bright yellow
12: 0x0000ff, // bright blue
13: 0xff00ff, // bright magenta
14: 0x00ffff, // bright cyan
15: 0xffffff, // bright white
}
// Color is a color that can be used in a terminal. ANSI (including
// ANSI256) and 24-bit "true colors" fall under this category.
type Color interface {
color.Color
}
// BasicColor is an ANSI 3-bit or 4-bit color with a value from 0 to 15.
type BasicColor uint8
var _ Color = BasicColor(0)
const (
// Black is the ANSI black color.
Black BasicColor = iota
// Red is the ANSI red color.
Red
// Green is the ANSI green color.
Green
// Yellow is the ANSI yellow color.
Yellow
// Blue is the ANSI blue color.
Blue
// Magenta is the ANSI magenta color.
Magenta
// Cyan is the ANSI cyan color.
Cyan
// White is the ANSI white color.
White
// BrightBlack is the ANSI bright black color.
BrightBlack
// BrightRed is the ANSI bright red color.
BrightRed
// BrightGreen is the ANSI bright green color.
BrightGreen
// BrightYellow is the ANSI bright yellow color.
BrightYellow
// BrightBlue is the ANSI bright blue color.
BrightBlue
// BrightMagenta is the ANSI bright magenta color.
BrightMagenta
// BrightCyan is the ANSI bright cyan color.
BrightCyan
// BrightWhite is the ANSI bright white color.
BrightWhite
)
// RGBA returns the red, green, blue and alpha components of the color. It
// satisfies the color.Color interface.
func (c BasicColor) RGBA() (uint32, uint32, uint32, uint32) {
ansi := uint32(c)
if ansi > 15 {
return 0, 0, 0, 0xffff
}
r, g, b := ansiToRGB(ansi)
return toRGBA(r, g, b)
}
// ExtendedColor is an ANSI 256 (8-bit) color with a value from 0 to 255.
type ExtendedColor uint8
var _ Color = ExtendedColor(0)
// RGBA returns the red, green, blue and alpha components of the color. It
// satisfies the color.Color interface.
func (c ExtendedColor) RGBA() (uint32, uint32, uint32, uint32) {
r, g, b := ansiToRGB(uint32(c))
return toRGBA(r, g, b)
}
// TrueColor is a 24-bit color that can be used in the terminal.
// This can be used to represent RGB colors.
//
// For example, the color red can be represented as:
//
// TrueColor(0xff0000)
type TrueColor uint32
var _ Color = TrueColor(0)
// RGBA returns the red, green, blue and alpha components of the color. It
// satisfies the color.Color interface.
func (c TrueColor) RGBA() (uint32, uint32, uint32, uint32) {
r, g, b := hexToRGB(uint32(c))
return toRGBA(r, g, b)
}
// ansiToRGB converts an ANSI color to a 24-bit RGB color.
//
// r, g, b := ansiToRGB(57)
func ansiToRGB(ansi uint32) (uint32, uint32, uint32) {
// For out-of-range values return black.
if ansi > 255 {
return 0, 0, 0
}
// Low ANSI.
if ansi < 16 {
h, ok := lowANSI[ansi]
if !ok {
return 0, 0, 0
}
r, g, b := hexToRGB(h)
return r, g, b
}
// Grays.
if ansi > 231 {
s := (ansi-232)*10 + 8
return s, s, s
}
// ANSI256.
n := ansi - 16
b := n % 6
g := (n - b) / 6 % 6
r := (n - b - g*6) / 36 % 6
for _, v := range []*uint32{&r, &g, &b} {
if *v > 0 {
c := *v*40 + 55
*v = c
}
}
return r, g, b
}
// hexToRGB converts a number in hexadecimal format to red, green, and blue
// values.
//
// r, g, b := hexToRGB(0x0000FF)
func hexToRGB(hex uint32) (uint32, uint32, uint32) {
return hex >> 16, hex >> 8 & 0xff, hex & 0xff
}
// toRGBA converts an RGB 8-bit color values to 32-bit color values suitable
// for color.Color.
//
// color.Color requires 16-bit color values, so we duplicate the 8-bit values
// to fill the 16-bit values.
//
// This always returns 0xffff (opaque) for the alpha channel.
func toRGBA(r, g, b uint32) (uint32, uint32, uint32, uint32) {
r |= r << 8
g |= g << 8
b |= b << 8
return r, g, b, 0xffff
}
package ansi
import (
"bytes"
"strconv"
"github.com/charmbracelet/x/ansi/parser"
)
// CsiSequence represents a control sequence introducer (CSI) sequence.
//
// The sequence starts with a CSI sequence, CSI (0x9B) in a 8-bit environment
// or ESC [ (0x1B 0x5B) in a 7-bit environment, followed by any number of
// parameters in the range of 0x30-0x3F, then by any number of intermediate
// byte in the range of 0x20-0x2F, then finally with a single final byte in the
// range of 0x20-0x7E.
//
// CSI P..P I..I F
//
// See ECMA-48 § 5.4.
type CsiSequence struct {
// Params contains the raw parameters of the sequence.
// This is a slice of integers, where each integer is a 32-bit integer
// containing the parameter value in the lower 31 bits and a flag in the
// most significant bit indicating whether there are more sub-parameters.
Params []int
// Cmd contains the raw command of the sequence.
// The command is a 32-bit integer containing the CSI command byte in the
// lower 8 bits, the private marker in the next 8 bits, and the intermediate
// byte in the next 8 bits.
//
// CSI ? u
//
// Is represented as:
//
// 'u' | '?' << 8
Cmd int
}
var _ Sequence = CsiSequence{}
// Marker returns the marker byte of the CSI sequence.
// This is always gonna be one of the following '<' '=' '>' '?' and in the
// range of 0x3C-0x3F.
// Zero is returned if the sequence does not have a marker.
func (s CsiSequence) Marker() int {
return parser.Marker(s.Cmd)
}
// Intermediate returns the intermediate byte of the CSI sequence.
// An intermediate byte is in the range of 0x20-0x2F. This includes these
// characters from ' ', '!', '"', '#', '$', '%', '&', ”', '(', ')', '*', '+',
// ',', '-', '.', '/'.
// Zero is returned if the sequence does not have an intermediate byte.
func (s CsiSequence) Intermediate() int {
return parser.Intermediate(s.Cmd)
}
// Command returns the command byte of the CSI sequence.
func (s CsiSequence) Command() int {
return parser.Command(s.Cmd)
}
// Param returns the parameter at the given index.
// It returns -1 if the parameter does not exist.
func (s CsiSequence) Param(i int) int {
return parser.Param(s.Params, i)
}
// HasMore returns true if the parameter has more sub-parameters.
func (s CsiSequence) HasMore(i int) bool {
return parser.HasMore(s.Params, i)
}
// Subparams returns the sub-parameters of the given parameter.
// It returns nil if the parameter does not exist.
func (s CsiSequence) Subparams(i int) []int {
return parser.Subparams(s.Params, i)
}
// Len returns the number of parameters in the sequence.
// This will return the number of parameters in the sequence, excluding any
// sub-parameters.
func (s CsiSequence) Len() int {
return parser.Len(s.Params)
}
// Range iterates over the parameters of the sequence and calls the given
// function for each parameter.
// The function should return false to stop the iteration.
func (s CsiSequence) Range(fn func(i int, param int, hasMore bool) bool) {
parser.Range(s.Params, fn)
}
// Clone returns a copy of the CSI sequence.
func (s CsiSequence) Clone() Sequence {
return CsiSequence{
Params: append([]int(nil), s.Params...),
Cmd: s.Cmd,
}
}
// String returns a string representation of the sequence.
// The string will always be in the 7-bit format i.e (ESC [ P..P I..I F).
func (s CsiSequence) String() string {
return s.buffer().String()
}
// buffer returns a buffer containing the sequence.
func (s CsiSequence) buffer() *bytes.Buffer {
var b bytes.Buffer
b.WriteString("\x1b[")
if m := s.Marker(); m != 0 {
b.WriteByte(byte(m))
}
s.Range(func(i, param int, hasMore bool) bool {
if param >= 0 {
b.WriteString(strconv.Itoa(param))
}
if i < len(s.Params)-1 {
if hasMore {
b.WriteByte(':')
} else {
b.WriteByte(';')
}
}
return true
})
if i := s.Intermediate(); i != 0 {
b.WriteByte(byte(i))
}
b.WriteByte(byte(s.Command()))
return &b
}
// Bytes returns the byte representation of the sequence.
// The bytes will always be in the 7-bit format i.e (ESC [ P..P I..I F).
func (s CsiSequence) Bytes() []byte {
return s.buffer().Bytes()
}
package ansi
// RequestXTVersion is a control sequence that requests the terminal's XTVERSION. It responds with a DSR sequence identifying the version.
//
// CSI > Ps q
// DCS > | text ST
//
// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-PC-Style-Function-Keys
const RequestXTVersion = "\x1b[>0q"
// RequestPrimaryDeviceAttributes is a control sequence that requests the
// terminal's primary device attributes (DA1).
//
// CSI c
//
// See https://vt100.net/docs/vt510-rm/DA1.html
const RequestPrimaryDeviceAttributes = "\x1b[c"
package ansi
import "strconv"
// SaveCursor (DECSC) is an escape sequence that saves the current cursor
// position.
//
// ESC 7
//
// See: https://vt100.net/docs/vt510-rm/DECSC.html
const SaveCursor = "\x1b7"
// RestoreCursor (DECRC) is an escape sequence that restores the cursor
// position.
//
// ESC 8
//
// See: https://vt100.net/docs/vt510-rm/DECRC.html
const RestoreCursor = "\x1b8"
// RequestCursorPosition (CPR) is an escape sequence that requests the current
// cursor position.
//
// CSI 6 n
//
// The terminal will report the cursor position as a CSI sequence in the
// following format:
//
// CSI Pl ; Pc R
//
// Where Pl is the line number and Pc is the column number.
// See: https://vt100.net/docs/vt510-rm/CPR.html
const RequestCursorPosition = "\x1b[6n"
// RequestExtendedCursorPosition (DECXCPR) is a sequence for requesting the
// cursor position report including the current page number.
//
// CSI ? 6 n
//
// The terminal will report the cursor position as a CSI sequence in the
// following format:
//
// CSI ? Pl ; Pc ; Pp R
//
// Where Pl is the line number, Pc is the column number, and Pp is the page
// number.
// See: https://vt100.net/docs/vt510-rm/DECXCPR.html
const RequestExtendedCursorPosition = "\x1b[?6n"
// CursorUp (CUU) returns a sequence for moving the cursor up n cells.
//
// CSI n A
//
// See: https://vt100.net/docs/vt510-rm/CUU.html
func CursorUp(n int) string {
var s string
if n > 1 {
s = strconv.Itoa(n)
}
return "\x1b[" + s + "A"
}
// CursorUp1 is a sequence for moving the cursor up one cell.
//
// This is equivalent to CursorUp(1).
const CursorUp1 = "\x1b[A"
// CursorDown (CUD) returns a sequence for moving the cursor down n cells.
//
// CSI n B
//
// See: https://vt100.net/docs/vt510-rm/CUD.html
func CursorDown(n int) string {
var s string
if n > 1 {
s = strconv.Itoa(n)
}
return "\x1b[" + s + "B"
}
// CursorDown1 is a sequence for moving the cursor down one cell.
//
// This is equivalent to CursorDown(1).
const CursorDown1 = "\x1b[B"
// CursorRight (CUF) returns a sequence for moving the cursor right n cells.
//
// CSI n C
//
// See: https://vt100.net/docs/vt510-rm/CUF.html
func CursorRight(n int) string {
var s string
if n > 1 {
s = strconv.Itoa(n)
}
return "\x1b[" + s + "C"
}
// CursorRight1 is a sequence for moving the cursor right one cell.
//
// This is equivalent to CursorRight(1).
const CursorRight1 = "\x1b[C"
// CursorLeft (CUB) returns a sequence for moving the cursor left n cells.
//
// CSI n D
//
// See: https://vt100.net/docs/vt510-rm/CUB.html
func CursorLeft(n int) string {
var s string
if n > 1 {
s = strconv.Itoa(n)
}
return "\x1b[" + s + "D"
}
// CursorLeft1 is a sequence for moving the cursor left one cell.
//
// This is equivalent to CursorLeft(1).
const CursorLeft1 = "\x1b[D"
// CursorNextLine (CNL) returns a sequence for moving the cursor to the
// beginning of the next line n times.
//
// CSI n E
//
// See: https://vt100.net/docs/vt510-rm/CNL.html
func CursorNextLine(n int) string {
var s string
if n > 1 {
s = strconv.Itoa(n)
}
return "\x1b[" + s + "E"
}
// CursorPreviousLine (CPL) returns a sequence for moving the cursor to the
// beginning of the previous line n times.
//
// CSI n F
//
// See: https://vt100.net/docs/vt510-rm/CPL.html
func CursorPreviousLine(n int) string {
var s string
if n > 1 {
s = strconv.Itoa(n)
}
return "\x1b[" + s + "F"
}
// MoveCursor (CUP) returns a sequence for moving the cursor to the given row
// and column.
//
// CSI n ; m H
//
// See: https://vt100.net/docs/vt510-rm/CUP.html
func MoveCursor(row, col int) string {
if row < 0 {
row = 0
}
if col < 0 {
col = 0
}
return "\x1b[" + strconv.Itoa(row) + ";" + strconv.Itoa(col) + "H"
}
// MoveCursorOrigin is a sequence for moving the cursor to the upper left
// corner of the screen. This is equivalent to MoveCursor(1, 1).
const MoveCursorOrigin = "\x1b[1;1H"
// SaveCursorPosition (SCP or SCOSC) is a sequence for saving the cursor
// position.
//
// CSI s
//
// This acts like Save, except the page number where the cursor is located is
// not saved.
//
// See: https://vt100.net/docs/vt510-rm/SCOSC.html
const SaveCursorPosition = "\x1b[s"
// RestoreCursorPosition (RCP or SCORC) is a sequence for restoring the cursor
// position.
//
// CSI u
//
// This acts like Restore, except the cursor stays on the same page where the
// cursor was saved.
//
// See: https://vt100.net/docs/vt510-rm/SCORC.html
const RestoreCursorPosition = "\x1b[u"
package ansi
import (
"bytes"
"strconv"
"github.com/charmbracelet/x/ansi/parser"
)
// DcsSequence represents a Device Control String (DCS) escape sequence.
//
// The DCS sequence is used to send device control strings to the terminal. The
// sequence starts with the C1 control code character DCS (0x9B) or ESC P in
// 7-bit environments, followed by parameter bytes, intermediate bytes, a
// command byte, followed by data bytes, and ends with the C1 control code
// character ST (0x9C) or ESC \ in 7-bit environments.
//
// This follows the parameter string format.
// See ECMA-48 § 5.4.1
type DcsSequence struct {
// Params contains the raw parameters of the sequence.
// This is a slice of integers, where each integer is a 32-bit integer
// containing the parameter value in the lower 31 bits and a flag in the
// most significant bit indicating whether there are more sub-parameters.
Params []int
// Data contains the string raw data of the sequence.
// This is the data between the final byte and the escape sequence terminator.
Data []byte
// Cmd contains the raw command of the sequence.
// The command is a 32-bit integer containing the DCS command byte in the
// lower 8 bits, the private marker in the next 8 bits, and the intermediate
// byte in the next 8 bits.
//
// DCS > 0 ; 1 $ r <data> ST
//
// Is represented as:
//
// 'r' | '>' << 8 | '$' << 16
Cmd int
}
var _ Sequence = DcsSequence{}
// Marker returns the marker byte of the DCS sequence.
// This is always gonna be one of the following '<' '=' '>' '?' and in the
// range of 0x3C-0x3F.
// Zero is returned if the sequence does not have a marker.
func (s DcsSequence) Marker() int {
return parser.Marker(s.Cmd)
}
// Intermediate returns the intermediate byte of the DCS sequence.
// An intermediate byte is in the range of 0x20-0x2F. This includes these
// characters from ' ', '!', '"', '#', '$', '%', '&', ”', '(', ')', '*', '+',
// ',', '-', '.', '/'.
// Zero is returned if the sequence does not have an intermediate byte.
func (s DcsSequence) Intermediate() int {
return parser.Intermediate(s.Cmd)
}
// Command returns the command byte of the CSI sequence.
func (s DcsSequence) Command() int {
return parser.Command(s.Cmd)
}
// Param returns the parameter at the given index.
// It returns -1 if the parameter does not exist.
func (s DcsSequence) Param(i int) int {
return parser.Param(s.Params, i)
}
// HasMore returns true if the parameter has more sub-parameters.
func (s DcsSequence) HasMore(i int) bool {
return parser.HasMore(s.Params, i)
}
// Subparams returns the sub-parameters of the given parameter.
// It returns nil if the parameter does not exist.
func (s DcsSequence) Subparams(i int) []int {
return parser.Subparams(s.Params, i)
}
// Len returns the number of parameters in the sequence.
// This will return the number of parameters in the sequence, excluding any
// sub-parameters.
func (s DcsSequence) Len() int {
return parser.Len(s.Params)
}
// Range iterates over the parameters of the sequence and calls the given
// function for each parameter.
// The function should return false to stop the iteration.
func (s DcsSequence) Range(fn func(i int, param int, hasMore bool) bool) {
parser.Range(s.Params, fn)
}
// Clone returns a copy of the DCS sequence.
func (s DcsSequence) Clone() Sequence {
return DcsSequence{
Params: append([]int(nil), s.Params...),
Data: append([]byte(nil), s.Data...),
Cmd: s.Cmd,
}
}
// String returns a string representation of the sequence.
// The string will always be in the 7-bit format i.e (ESC P p..p i..i f <data> ESC \).
func (s DcsSequence) String() string {
return s.buffer().String()
}
// buffer returns a buffer containing the sequence.
func (s DcsSequence) buffer() *bytes.Buffer {
var b bytes.Buffer
b.WriteString("\x1bP")
if m := s.Marker(); m != 0 {
b.WriteByte(byte(m))
}
s.Range(func(i, param int, hasMore bool) bool {
if param >= -1 {
b.WriteString(strconv.Itoa(param))
}
if i < len(s.Params)-1 {
if hasMore {
b.WriteByte(':')
} else {
b.WriteByte(';')
}
}
return true
})
if i := s.Intermediate(); i != 0 {
b.WriteByte(byte(i))
}
b.WriteByte(byte(s.Command()))
b.Write(s.Data)
b.WriteByte(ESC)
b.WriteByte('\\')
return &b
}
// Bytes returns the byte representation of the sequence.
// The bytes will always be in the 7-bit format i.e (ESC P p..p i..i F <data> ESC \).
func (s DcsSequence) Bytes() []byte {
return s.buffer().Bytes()
}
// Package ansi defines common ANSI escape sequences based on the ECMA-48
// specs.
//
// All sequences use 7-bit C1 control codes, which are supported by most
// terminal emulators. OSC sequences are terminated by a BEL for wider
// compatibility with terminals.
package ansi
package ansi
import "strings"
// SetHyperlink returns a sequence for starting a hyperlink.
//
// OSC 8 ; Params ; Uri ST
// OSC 8 ; Params ; Uri BEL
//
// To reset the hyperlink, omit the URI.
//
// See: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
func SetHyperlink(uri string, params ...string) string {
var p string
if len(params) > 0 {
p = strings.Join(params, ":")
}
return "\x1b]8;" + p + ";" + uri + "\x07"
}
// ResetHyperlink returns a sequence for resetting the hyperlink.
//
// This is equivalent to SetHyperlink("", params...).
//
// See: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
func ResetHyperlink(params ...string) string {
return SetHyperlink("", params...)
}
package ansi
import "strconv"
// Kitty keyboard protocol progressive enhancement flags.
// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
const (
KittyDisambiguateEscapeCodes = 1 << iota
KittyReportEventTypes
KittyReportAlternateKeys
KittyReportAllKeys
KittyReportAssociatedKeys
KittyAllFlags = KittyDisambiguateEscapeCodes | KittyReportEventTypes |
KittyReportAlternateKeys | KittyReportAllKeys | KittyReportAssociatedKeys
)
// RequestKittyKeyboard is a sequence to request the terminal Kitty keyboard
// protocol enabled flags.
//
// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/
const RequestKittyKeyboard = "\x1b[?u"
// PushKittyKeyboard returns a sequence to push the given flags to the terminal
// Kitty Keyboard stack.
//
// CSI > flags u
//
// See https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
func PushKittyKeyboard(flags int) string {
var f string
if flags > 0 {
f = strconv.Itoa(flags)
}
return "\x1b[>" + f + "u"
}
// DisableKittyKeyboard is a sequence to push zero into the terminal Kitty
// Keyboard stack to disable the protocol.
//
// This is equivalent to PushKittyKeyboard(0).
const DisableKittyKeyboard = "\x1b[>0u"
// PopKittyKeyboard returns a sequence to pop n number of flags from the
// terminal Kitty Keyboard stack.
//
// CSI < flags u
//
// See https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
func PopKittyKeyboard(n int) string {
var num string
if n > 0 {
num = strconv.Itoa(n)
}
return "\x1b[<" + num + "u"
}
package ansi
// This file define uses multiple sequences to set (SM), reset (RM), and request
// (DECRQM) different ANSI and DEC modes.
//
// See: https://vt100.net/docs/vt510-rm/SM.html
// See: https://vt100.net/docs/vt510-rm/RM.html
// See: https://vt100.net/docs/vt510-rm/DECRQM.html
//
// The terminal then responds to the request with a Report Mode function
// (DECRPM) in the format:
//
// ANSI format:
//
// CSI Pa ; Ps ; $ y
//
// DEC format:
//
// CSI ? Pa ; Ps $ y
//
// Where Pa is the mode number, and Ps is the mode value.
// See: https://vt100.net/docs/vt510-rm/DECRPM.html
// Application Cursor Keys (DECCKM) is a mode that determines whether the
// cursor keys send ANSI cursor sequences or application sequences.
//
// See: https://vt100.net/docs/vt510-rm/DECCKM.html
const (
EnableCursorKeys = "\x1b[?1h"
DisableCursorKeys = "\x1b[?1l"
RequestCursorKeys = "\x1b[?1$p"
)
// Text Cursor Enable Mode (DECTCEM) is a mode that shows/hides the cursor.
//
// See: https://vt100.net/docs/vt510-rm/DECTCEM.html
const (
ShowCursor = "\x1b[?25h"
HideCursor = "\x1b[?25l"
RequestCursorVisibility = "\x1b[?25$p"
)
// VT Mouse Tracking is a mode that determines whether the mouse reports on
// button press and release.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
EnableMouse = "\x1b[?1000h"
DisableMouse = "\x1b[?1000l"
RequestMouse = "\x1b[?1000$p"
)
// VT Hilite Mouse Tracking is a mode that determines whether the mouse reports on
// button presses, releases, and highlighted cells.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
EnableMouseHilite = "\x1b[?1001h"
DisableMouseHilite = "\x1b[?1001l"
RequestMouseHilite = "\x1b[?1001$p"
)
// Cell Motion Mouse Tracking is a mode that determines whether the mouse
// reports on button press, release, and motion events.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
EnableMouseCellMotion = "\x1b[?1002h"
DisableMouseCellMotion = "\x1b[?1002l"
RequestMouseCellMotion = "\x1b[?1002$p"
)
// All Mouse Tracking is a mode that determines whether the mouse reports on
// button press, release, motion, and highlight events.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
EnableMouseAllMotion = "\x1b[?1003h"
DisableMouseAllMotion = "\x1b[?1003l"
RequestMouseAllMotion = "\x1b[?1003$p"
)
// SGR Mouse Extension is a mode that determines whether the mouse reports events
// formatted with SGR parameters.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
EnableMouseSgrExt = "\x1b[?1006h"
DisableMouseSgrExt = "\x1b[?1006l"
RequestMouseSgrExt = "\x1b[?1006$p"
)
// Alternate Screen Buffer is a mode that determines whether the alternate screen
// buffer is active.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
const (
EnableAltScreenBuffer = "\x1b[?1049h"
DisableAltScreenBuffer = "\x1b[?1049l"
RequestAltScreenBuffer = "\x1b[?1049$p"
)
// Bracketed Paste Mode is a mode that determines whether pasted text is
// bracketed with escape sequences.
//
// See: https://cirw.in/blog/bracketed-paste
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Bracketed-Paste-Mode
const (
EnableBracketedPaste = "\x1b[?2004h"
DisableBracketedPaste = "\x1b[?2004l"
RequestBracketedPaste = "\x1b[?2004$p"
)
// Synchronized Output Mode is a mode that determines whether output is
// synchronized with the terminal.
//
// See: https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036
const (
EnableSyncdOutput = "\x1b[?2026h"
DisableSyncdOutput = "\x1b[?2026l"
RequestSyncdOutput = "\x1b[?2026$p"
)
// Win32Input is a mode that determines whether input is processed by the
// Win32 console and Conpty.
//
// See: https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md
const (
EnableWin32Input = "\x1b[?9001h"
DisableWin32Input = "\x1b[?9001l"
RequestWin32Input = "\x1b[?9001$p"
)
package ansi
import (
"bytes"
"strings"
)
// OscSequence represents an OSC sequence.
//
// The sequence starts with a OSC sequence, OSC (0x9D) in a 8-bit environment
// or ESC ] (0x1B 0x5D) in a 7-bit environment, followed by positive integer identifier,
// then by arbitrary data terminated by a ST (0x9C) in a 8-bit environment,
// ESC \ (0x1B 0x5C) in a 7-bit environment, or BEL (0x07) for backwards compatibility.
//
// OSC Ps ; Pt ST
// OSC Ps ; Pt BEL
//
// See ECMA-48 § 5.7.
type OscSequence struct {
// Data contains the raw data of the sequence including the identifier
// command.
Data []byte
// Cmd contains the raw command of the sequence.
Cmd int
}
var _ Sequence = OscSequence{}
// Command returns the command of the OSC sequence.
func (s OscSequence) Command() int {
return s.Cmd
}
// Params returns the parameters of the OSC sequence split by ';'.
// The first element is the identifier command.
func (s OscSequence) Params() []string {
return strings.Split(string(s.Data), ";")
}
// Clone returns a copy of the OSC sequence.
func (s OscSequence) Clone() Sequence {
return OscSequence{
Data: append([]byte(nil), s.Data...),
Cmd: s.Cmd,
}
}
// String returns the string representation of the OSC sequence.
// To be more compatible with different terminal, this will always return a
// 7-bit formatted sequence, terminated by BEL.
func (s OscSequence) String() string {
return s.buffer().String()
}
// Bytes returns the byte representation of the OSC sequence.
// To be more compatible with different terminal, this will always return a
// 7-bit formatted sequence, terminated by BEL.
func (s OscSequence) Bytes() []byte {
return s.buffer().Bytes()
}
func (s OscSequence) buffer() *bytes.Buffer {
var b bytes.Buffer
b.WriteString("\x1b]")
b.Write(s.Data)
b.WriteByte(BEL)
return &b
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment