Class: Tuile::StyledString::Style
- Inherits:
-
Object
- Object
- Tuile::StyledString::Style
- Defined in:
- lib/tuile/styled_string.rb,
sig/tuile.rbs
Overview
A frozen value type describing the visual style of a Span. Colors are
stored as Color instances (or nil for the terminal default); inputs
to Style.new and #merge are coerced via Color.coerce, so the four
accepted color forms — nil, Symbol, Integer 0..255, RGB Array — work
transparently.
Constant Summary collapse
- DEFAULT =
The style with no color and no attributes — what the terminal shows without any SGR applied.
new
Instance Attribute Summary collapse
- #bg ⇒ Color? readonly
- #bold ⇒ Boolean readonly
- #fg ⇒ Color? readonly
- #italic ⇒ Boolean readonly
- #strikethrough ⇒ Boolean readonly
- #underline ⇒ Boolean readonly
Class Method Summary collapse
-
.new(fg: nil, bg: nil, bold: false, italic: false, underline: false, strikethrough: false) ⇒ Object
@param
fg— coerced via Color.coerce.
Instance Method Summary collapse
-
#color_codes(color, target:) ⇒ ::Array[Integer]
@param
color. - #default? ⇒ Boolean
-
#merge(**overrides) ⇒ Style
Returns a new Style with the given attributes overridden.
-
#sgr_to(other) ⇒ String
Minimal SGR escape that transitions a terminal already showing
selfintoother: only the attributes that differ are emitted.
Instance Attribute Details
#bg ⇒ Color? (readonly)
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/tuile/styled_string.rb', line 83 class Style < Data.define(:fg, :bg, :bold, :italic, :underline, :strikethrough) # @param fg [Color, Symbol, Integer, Array<Integer>, nil] coerced via {Color.coerce}. # @param bg [Color, Symbol, Integer, Array<Integer>, nil] coerced via {Color.coerce}. # @param bold [Boolean] # @param italic [Boolean] # @param underline [Boolean] # @param strikethrough [Boolean] # @return [Style] # @raise [ArgumentError] when a color is not one of the accepted forms. def self.new(fg: nil, bg: nil, bold: false, italic: false, underline: false, strikethrough: false) super(fg: Color.coerce(fg), bg: Color.coerce(bg), bold:, italic:, underline:, strikethrough:) end # The style with no color and no attributes — what the terminal shows # without any SGR applied. # @return [Style] DEFAULT = new # @return [Boolean] def default? = self == DEFAULT # Returns a new {Style} with the given attributes overridden. # @param overrides [Hash{Symbol => Object}] # @return [Style] def merge(**overrides) = self.class.new(**to_h.merge(overrides)) # Minimal SGR escape that transitions a terminal already showing `self` # into `other`: only the attributes that differ are emitted. Returns # `""` when the styles are identical (nothing to do), and {Ansi::RESET} # (`\e[0m`, one code) when `other` is the default style — shorter than # turning each attribute off individually. # # Shared by {StyledString#to_ansi} (diffing span-to-span from the default # style) and {Buffer}'s flush (diffing cell-to-cell against the style the # terminal currently holds), so both emit identical minimal sequences. # @param other [Style] the style to transition to. # @return [String] def sgr_to(other) return "" if self == other return Ansi::RESET if other.default? codes = [] codes << (other.bold ? 1 : 22) if bold != other.bold codes << (other.italic ? 3 : 23) if italic != other.italic codes << (other.underline ? 4 : 24) if underline != other.underline codes << (other.strikethrough ? 9 : 29) if strikethrough != other.strikethrough codes.concat(color_codes(other.fg, target: :fg)) if fg != other.fg codes.concat(color_codes(other.bg, target: :bg)) if bg != other.bg return "" if codes.empty? "\e[#{codes.join(";")}m" end private # @param color [Color, nil] # @param target [Symbol] either `:fg` or `:bg`. # @return [Array<Integer>] SGR codes; `[39]` / `[49]` for the "default" # reset when `color` is `nil`, otherwise delegated to {Color#sgr_codes}. def color_codes(color, target:) return [target == :fg ? 39 : 49] if color.nil? color.sgr_codes(target) end end |
#bold ⇒ Boolean (readonly)
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/tuile/styled_string.rb', line 83 class Style < Data.define(:fg, :bg, :bold, :italic, :underline, :strikethrough) # @param fg [Color, Symbol, Integer, Array<Integer>, nil] coerced via {Color.coerce}. # @param bg [Color, Symbol, Integer, Array<Integer>, nil] coerced via {Color.coerce}. # @param bold [Boolean] # @param italic [Boolean] # @param underline [Boolean] # @param strikethrough [Boolean] # @return [Style] # @raise [ArgumentError] when a color is not one of the accepted forms. def self.new(fg: nil, bg: nil, bold: false, italic: false, underline: false, strikethrough: false) super(fg: Color.coerce(fg), bg: Color.coerce(bg), bold:, italic:, underline:, strikethrough:) end # The style with no color and no attributes — what the terminal shows # without any SGR applied. # @return [Style] DEFAULT = new # @return [Boolean] def default? = self == DEFAULT # Returns a new {Style} with the given attributes overridden. # @param overrides [Hash{Symbol => Object}] # @return [Style] def merge(**overrides) = self.class.new(**to_h.merge(overrides)) # Minimal SGR escape that transitions a terminal already showing `self` # into `other`: only the attributes that differ are emitted. Returns # `""` when the styles are identical (nothing to do), and {Ansi::RESET} # (`\e[0m`, one code) when `other` is the default style — shorter than # turning each attribute off individually. # # Shared by {StyledString#to_ansi} (diffing span-to-span from the default # style) and {Buffer}'s flush (diffing cell-to-cell against the style the # terminal currently holds), so both emit identical minimal sequences. # @param other [Style] the style to transition to. # @return [String] def sgr_to(other) return "" if self == other return Ansi::RESET if other.default? codes = [] codes << (other.bold ? 1 : 22) if bold != other.bold codes << (other.italic ? 3 : 23) if italic != other.italic codes << (other.underline ? 4 : 24) if underline != other.underline codes << (other.strikethrough ? 9 : 29) if strikethrough != other.strikethrough codes.concat(color_codes(other.fg, target: :fg)) if fg != other.fg codes.concat(color_codes(other.bg, target: :bg)) if bg != other.bg return "" if codes.empty? "\e[#{codes.join(";")}m" end private # @param color [Color, nil] # @param target [Symbol] either `:fg` or `:bg`. # @return [Array<Integer>] SGR codes; `[39]` / `[49]` for the "default" # reset when `color` is `nil`, otherwise delegated to {Color#sgr_codes}. def color_codes(color, target:) return [target == :fg ? 39 : 49] if color.nil? color.sgr_codes(target) end end |
#fg ⇒ Color? (readonly)
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/tuile/styled_string.rb', line 83 class Style < Data.define(:fg, :bg, :bold, :italic, :underline, :strikethrough) # @param fg [Color, Symbol, Integer, Array<Integer>, nil] coerced via {Color.coerce}. # @param bg [Color, Symbol, Integer, Array<Integer>, nil] coerced via {Color.coerce}. # @param bold [Boolean] # @param italic [Boolean] # @param underline [Boolean] # @param strikethrough [Boolean] # @return [Style] # @raise [ArgumentError] when a color is not one of the accepted forms. def self.new(fg: nil, bg: nil, bold: false, italic: false, underline: false, strikethrough: false) super(fg: Color.coerce(fg), bg: Color.coerce(bg), bold:, italic:, underline:, strikethrough:) end # The style with no color and no attributes — what the terminal shows # without any SGR applied. # @return [Style] DEFAULT = new # @return [Boolean] def default? = self == DEFAULT # Returns a new {Style} with the given attributes overridden. # @param overrides [Hash{Symbol => Object}] # @return [Style] def merge(**overrides) = self.class.new(**to_h.merge(overrides)) # Minimal SGR escape that transitions a terminal already showing `self` # into `other`: only the attributes that differ are emitted. Returns # `""` when the styles are identical (nothing to do), and {Ansi::RESET} # (`\e[0m`, one code) when `other` is the default style — shorter than # turning each attribute off individually. # # Shared by {StyledString#to_ansi} (diffing span-to-span from the default # style) and {Buffer}'s flush (diffing cell-to-cell against the style the # terminal currently holds), so both emit identical minimal sequences. # @param other [Style] the style to transition to. # @return [String] def sgr_to(other) return "" if self == other return Ansi::RESET if other.default? codes = [] codes << (other.bold ? 1 : 22) if bold != other.bold codes << (other.italic ? 3 : 23) if italic != other.italic codes << (other.underline ? 4 : 24) if underline != other.underline codes << (other.strikethrough ? 9 : 29) if strikethrough != other.strikethrough codes.concat(color_codes(other.fg, target: :fg)) if fg != other.fg codes.concat(color_codes(other.bg, target: :bg)) if bg != other.bg return "" if codes.empty? "\e[#{codes.join(";")}m" end private # @param color [Color, nil] # @param target [Symbol] either `:fg` or `:bg`. # @return [Array<Integer>] SGR codes; `[39]` / `[49]` for the "default" # reset when `color` is `nil`, otherwise delegated to {Color#sgr_codes}. def color_codes(color, target:) return [target == :fg ? 39 : 49] if color.nil? color.sgr_codes(target) end end |
#italic ⇒ Boolean (readonly)
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/tuile/styled_string.rb', line 83 class Style < Data.define(:fg, :bg, :bold, :italic, :underline, :strikethrough) # @param fg [Color, Symbol, Integer, Array<Integer>, nil] coerced via {Color.coerce}. # @param bg [Color, Symbol, Integer, Array<Integer>, nil] coerced via {Color.coerce}. # @param bold [Boolean] # @param italic [Boolean] # @param underline [Boolean] # @param strikethrough [Boolean] # @return [Style] # @raise [ArgumentError] when a color is not one of the accepted forms. def self.new(fg: nil, bg: nil, bold: false, italic: false, underline: false, strikethrough: false) super(fg: Color.coerce(fg), bg: Color.coerce(bg), bold:, italic:, underline:, strikethrough:) end # The style with no color and no attributes — what the terminal shows # without any SGR applied. # @return [Style] DEFAULT = new # @return [Boolean] def default? = self == DEFAULT # Returns a new {Style} with the given attributes overridden. # @param overrides [Hash{Symbol => Object}] # @return [Style] def merge(**overrides) = self.class.new(**to_h.merge(overrides)) # Minimal SGR escape that transitions a terminal already showing `self` # into `other`: only the attributes that differ are emitted. Returns # `""` when the styles are identical (nothing to do), and {Ansi::RESET} # (`\e[0m`, one code) when `other` is the default style — shorter than # turning each attribute off individually. # # Shared by {StyledString#to_ansi} (diffing span-to-span from the default # style) and {Buffer}'s flush (diffing cell-to-cell against the style the # terminal currently holds), so both emit identical minimal sequences. # @param other [Style] the style to transition to. # @return [String] def sgr_to(other) return "" if self == other return Ansi::RESET if other.default? codes = [] codes << (other.bold ? 1 : 22) if bold != other.bold codes << (other.italic ? 3 : 23) if italic != other.italic codes << (other.underline ? 4 : 24) if underline != other.underline codes << (other.strikethrough ? 9 : 29) if strikethrough != other.strikethrough codes.concat(color_codes(other.fg, target: :fg)) if fg != other.fg codes.concat(color_codes(other.bg, target: :bg)) if bg != other.bg return "" if codes.empty? "\e[#{codes.join(";")}m" end private # @param color [Color, nil] # @param target [Symbol] either `:fg` or `:bg`. # @return [Array<Integer>] SGR codes; `[39]` / `[49]` for the "default" # reset when `color` is `nil`, otherwise delegated to {Color#sgr_codes}. def color_codes(color, target:) return [target == :fg ? 39 : 49] if color.nil? color.sgr_codes(target) end end |
#strikethrough ⇒ Boolean (readonly)
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/tuile/styled_string.rb', line 83 class Style < Data.define(:fg, :bg, :bold, :italic, :underline, :strikethrough) # @param fg [Color, Symbol, Integer, Array<Integer>, nil] coerced via {Color.coerce}. # @param bg [Color, Symbol, Integer, Array<Integer>, nil] coerced via {Color.coerce}. # @param bold [Boolean] # @param italic [Boolean] # @param underline [Boolean] # @param strikethrough [Boolean] # @return [Style] # @raise [ArgumentError] when a color is not one of the accepted forms. def self.new(fg: nil, bg: nil, bold: false, italic: false, underline: false, strikethrough: false) super(fg: Color.coerce(fg), bg: Color.coerce(bg), bold:, italic:, underline:, strikethrough:) end # The style with no color and no attributes — what the terminal shows # without any SGR applied. # @return [Style] DEFAULT = new # @return [Boolean] def default? = self == DEFAULT # Returns a new {Style} with the given attributes overridden. # @param overrides [Hash{Symbol => Object}] # @return [Style] def merge(**overrides) = self.class.new(**to_h.merge(overrides)) # Minimal SGR escape that transitions a terminal already showing `self` # into `other`: only the attributes that differ are emitted. Returns # `""` when the styles are identical (nothing to do), and {Ansi::RESET} # (`\e[0m`, one code) when `other` is the default style — shorter than # turning each attribute off individually. # # Shared by {StyledString#to_ansi} (diffing span-to-span from the default # style) and {Buffer}'s flush (diffing cell-to-cell against the style the # terminal currently holds), so both emit identical minimal sequences. # @param other [Style] the style to transition to. # @return [String] def sgr_to(other) return "" if self == other return Ansi::RESET if other.default? codes = [] codes << (other.bold ? 1 : 22) if bold != other.bold codes << (other.italic ? 3 : 23) if italic != other.italic codes << (other.underline ? 4 : 24) if underline != other.underline codes << (other.strikethrough ? 9 : 29) if strikethrough != other.strikethrough codes.concat(color_codes(other.fg, target: :fg)) if fg != other.fg codes.concat(color_codes(other.bg, target: :bg)) if bg != other.bg return "" if codes.empty? "\e[#{codes.join(";")}m" end private # @param color [Color, nil] # @param target [Symbol] either `:fg` or `:bg`. # @return [Array<Integer>] SGR codes; `[39]` / `[49]` for the "default" # reset when `color` is `nil`, otherwise delegated to {Color#sgr_codes}. def color_codes(color, target:) return [target == :fg ? 39 : 49] if color.nil? color.sgr_codes(target) end end |
#underline ⇒ Boolean (readonly)
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/tuile/styled_string.rb', line 83 class Style < Data.define(:fg, :bg, :bold, :italic, :underline, :strikethrough) # @param fg [Color, Symbol, Integer, Array<Integer>, nil] coerced via {Color.coerce}. # @param bg [Color, Symbol, Integer, Array<Integer>, nil] coerced via {Color.coerce}. # @param bold [Boolean] # @param italic [Boolean] # @param underline [Boolean] # @param strikethrough [Boolean] # @return [Style] # @raise [ArgumentError] when a color is not one of the accepted forms. def self.new(fg: nil, bg: nil, bold: false, italic: false, underline: false, strikethrough: false) super(fg: Color.coerce(fg), bg: Color.coerce(bg), bold:, italic:, underline:, strikethrough:) end # The style with no color and no attributes — what the terminal shows # without any SGR applied. # @return [Style] DEFAULT = new # @return [Boolean] def default? = self == DEFAULT # Returns a new {Style} with the given attributes overridden. # @param overrides [Hash{Symbol => Object}] # @return [Style] def merge(**overrides) = self.class.new(**to_h.merge(overrides)) # Minimal SGR escape that transitions a terminal already showing `self` # into `other`: only the attributes that differ are emitted. Returns # `""` when the styles are identical (nothing to do), and {Ansi::RESET} # (`\e[0m`, one code) when `other` is the default style — shorter than # turning each attribute off individually. # # Shared by {StyledString#to_ansi} (diffing span-to-span from the default # style) and {Buffer}'s flush (diffing cell-to-cell against the style the # terminal currently holds), so both emit identical minimal sequences. # @param other [Style] the style to transition to. # @return [String] def sgr_to(other) return "" if self == other return Ansi::RESET if other.default? codes = [] codes << (other.bold ? 1 : 22) if bold != other.bold codes << (other.italic ? 3 : 23) if italic != other.italic codes << (other.underline ? 4 : 24) if underline != other.underline codes << (other.strikethrough ? 9 : 29) if strikethrough != other.strikethrough codes.concat(color_codes(other.fg, target: :fg)) if fg != other.fg codes.concat(color_codes(other.bg, target: :bg)) if bg != other.bg return "" if codes.empty? "\e[#{codes.join(";")}m" end private # @param color [Color, nil] # @param target [Symbol] either `:fg` or `:bg`. # @return [Array<Integer>] SGR codes; `[39]` / `[49]` for the "default" # reset when `color` is `nil`, otherwise delegated to {Color#sgr_codes}. def color_codes(color, target:) return [target == :fg ? 39 : 49] if color.nil? color.sgr_codes(target) end end |
Class Method Details
.new(fg: nil, bg: nil, bold: false, italic: false, underline: false, strikethrough: false) ⇒ Object
@param fg — coerced via Color.coerce.
@param bg — coerced via Color.coerce.
@param bold
@param italic
@param underline
@param strikethrough
92 93 94 |
# File 'lib/tuile/styled_string.rb', line 92 def self.new(fg: nil, bg: nil, bold: false, italic: false, underline: false, strikethrough: false) super(fg: Color.coerce(fg), bg: Color.coerce(bg), bold:, italic:, underline:, strikethrough:) end |
Instance Method Details
#color_codes(color, target:) ⇒ ::Array[Integer]
@param color
@param target — either :fg or :bg.
@return — SGR codes; [39] / [49] for the "default"
reset when color is nil, otherwise delegated to Color#sgr_codes.
142 143 144 145 146 |
# File 'lib/tuile/styled_string.rb', line 142 def color_codes(color, target:) return [target == :fg ? 39 : 49] if color.nil? color.sgr_codes(target) end |
#default? ⇒ Boolean
102 |
# File 'lib/tuile/styled_string.rb', line 102 def default? = self == DEFAULT |
#merge(**overrides) ⇒ Style
Returns a new Tuile::StyledString::Style with the given attributes overridden.
@param overrides
107 |
# File 'lib/tuile/styled_string.rb', line 107 def merge(**overrides) = self.class.new(**to_h.merge(overrides)) |
#sgr_to(other) ⇒ String
Minimal SGR escape that transitions a terminal already showing self
into other: only the attributes that differ are emitted. Returns
"" when the styles are identical (nothing to do), and Ansi::RESET
(\e[0m, one code) when other is the default style — shorter than
turning each attribute off individually.
Shared by Tuile::StyledString#to_ansi (diffing span-to-span from the default style) and Buffer's flush (diffing cell-to-cell against the style the terminal currently holds), so both emit identical minimal sequences.
@param other — the style to transition to.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/tuile/styled_string.rb', line 120 def sgr_to(other) return "" if self == other return Ansi::RESET if other.default? codes = [] codes << (other.bold ? 1 : 22) if bold != other.bold codes << (other.italic ? 3 : 23) if italic != other.italic codes << (other.underline ? 4 : 24) if underline != other.underline codes << (other.strikethrough ? 9 : 29) if strikethrough != other.strikethrough codes.concat(color_codes(other.fg, target: :fg)) if fg != other.fg codes.concat(color_codes(other.bg, target: :bg)) if bg != other.bg return "" if codes.empty? "\e[#{codes.join(";")}m" end |