diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/reline.rb | 14 | ||||
-rw-r--r-- | lib/reline/face.rb | 157 | ||||
-rw-r--r-- | lib/reline/line_editor.rb | 23 |
3 files changed, 173 insertions, 21 deletions
diff --git a/lib/reline.rb b/lib/reline.rb index 3d1716c81b..fb19982081 100644 --- a/lib/reline.rb +++ b/lib/reline.rb @@ -7,6 +7,7 @@ require 'reline/key_stroke' require 'reline/line_editor' require 'reline/history' require 'reline/terminfo' +require 'reline/face' require 'rbconfig' module Reline @@ -36,10 +37,8 @@ module Reline DialogRenderInfo = Struct.new( :pos, :contents, - :bg_color, - :pointer_bg_color, - :fg_color, - :pointer_fg_color, + :face, + :bg_color, # For the time being, this line should stay here for the compatibility with IRB. :width, :height, :scrollbar, @@ -260,10 +259,7 @@ module Reline contents: result, scrollbar: true, height: [15, preferred_dialog_height].min, - bg_color: 46, - pointer_bg_color: 45, - fg_color: 37, - pointer_fg_color: 37 + face: :completion_dialog ) } Reline::DEFAULT_DIALOG_CONTEXT = Array.new @@ -606,4 +602,6 @@ else io end +Reline::Face.load_initial_configs + Reline::HISTORY = Reline::History.new(Reline.core.config) diff --git a/lib/reline/face.rb b/lib/reline/face.rb new file mode 100644 index 0000000000..b78f3b1ca5 --- /dev/null +++ b/lib/reline/face.rb @@ -0,0 +1,157 @@ +# frozen_string_literal: true + +class Reline::Face + SGR_PARAMETERS = { + foreground: { + black: 30, + red: 31, + green: 32, + yellow: 33, + blue: 34, + magenta: 35, + cyan: 36, + white: 37, + bright_black: 90, + gray: 90, + bright_red: 91, + bright_green: 92, + bright_yellow: 93, + bright_blue: 94, + bright_magenta: 95, + bright_cyan: 96, + bright_white: 97 + }, + background: { + black: 40, + red: 41, + green: 42, + yellow: 43, + blue: 44, + magenta: 45, + cyan: 46, + white: 47, + bright_black: 100, + gray: 100, + bright_red: 101, + bright_green: 102, + bright_yellow: 103, + bright_blue: 104, + bright_magenta: 105, + bright_cyan: 106, + bright_white: 107, + }, + style: { + reset: 0, + bold: 1, + faint: 2, + italicized: 3, + underlined: 4, + slowly_blinking: 5, + blinking: 5, + rapidly_blinking: 6, + negative: 7, + concealed: 8, + crossed_out: 9 + } + }.freeze + + class Config + ESSENTIAL_DEFINE_NAMES = %i(default enhanced scrollbar).freeze + RESET_SGR = "\e[0m".freeze + + def initialize(name, &block) + @definition = {} + block.call(self) + ESSENTIAL_DEFINE_NAMES.each do |name| + @definition[name] ||= { style: :reset, escape_sequence: RESET_SGR } + end + end + + attr_reader :definition + + def define(name, **values) + values[:escape_sequence] = format_to_sgr(values.to_a).freeze + @definition[name] = values + end + + def [](name) + @definition.dig(name, :escape_sequence) or raise ArgumentError, "unknown face: #{name}" + end + + private + + def sgr_rgb(key, value) + return nil unless rgb_expression?(value) + case key + when :foreground + "38;2;" + when :background + "48;2;" + end + value[1, 6].scan(/../).map(&:hex).join(";") + end + + def format_to_sgr(ordered_values) + sgr = "\e[" + ordered_values.map do |key_value| + key, value = key_value + case key + when :foreground, :background + case value + when Symbol + SGR_PARAMETERS[key][value] + when String + sgr_rgb(key, value) + end + when :style + [ value ].flatten.map do |style_name| + SGR_PARAMETERS[:style][style_name] + end.then do |sgr_parameters| + sgr_parameters.include?(nil) ? nil : sgr_parameters + end + end.then do |rendition_expression| + unless rendition_expression + raise ArgumentError, "invalid SGR parameter: #{value.inspect}" + end + rendition_expression + end + end.join(';') + "m" + sgr == RESET_SGR ? RESET_SGR : RESET_SGR + sgr + end + + def rgb_expression?(color) + color.respond_to?(:match?) and color.match?(/\A#[0-9a-fA-F]{6}\z/) + end + end + + private_constant :SGR_PARAMETERS, :Config + + def self.[](name) + @configs[name] + end + + def self.config(name, &block) + @configs ||= {} + @configs[name] = Config.new(name, &block) + end + + def self.configs + @configs.transform_values(&:definition) + end + + def self.load_initial_configs + config(:default) do |conf| + conf.define :default, style: :reset + conf.define :enhanced, style: :reset + conf.define :scrollbar, style: :reset + end + config(:completion_dialog) do |conf| + conf.define :default, foreground: :white, background: :cyan + conf.define :enhanced, foreground: :white, background: :magenta + conf.define :scrollbar, foreground: :white, background: :cyan + end + end + + def self.reset_to_initial_configs + @configs = {} + load_initial_configs + end +end diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb index 0d990c2c0a..d71b903701 100644 --- a/lib/reline/line_editor.rb +++ b/lib/reline/line_editor.rb @@ -831,27 +831,24 @@ class Reline::LineEditor dialog.column = 0 dialog.width = @screen_size.last end + face = Reline::Face[dialog_render_info.face || :default] + scrollbar_sgr = face[:scrollbar] + default_sgr = face[:default] + enhanced_sgr = face[:enhanced] dialog.contents = contents.map.with_index do |item, i| - if i == pointer - fg_color = dialog_render_info.pointer_fg_color - bg_color = dialog_render_info.pointer_bg_color - else - fg_color = dialog_render_info.fg_color - bg_color = dialog_render_info.bg_color - end + line_sgr = i == pointer ? enhanced_sgr : default_sgr str_width = dialog.width - (scrollbar_pos.nil? ? 0 : @block_elem_width) str = padding_space_with_escape_sequences(Reline::Unicode.take_range(item, 0, str_width), str_width) - colored_content = "\e[#{bg_color}m\e[#{fg_color}m#{str}" + colored_content = "#{line_sgr}#{str}" if scrollbar_pos - color_seq = "\e[37m" if scrollbar_pos <= (i * 2) and (i * 2 + 1) < (scrollbar_pos + bar_height) - colored_content + color_seq + @full_block + colored_content + scrollbar_sgr + @full_block elsif scrollbar_pos <= (i * 2) and (i * 2) < (scrollbar_pos + bar_height) - colored_content + color_seq + @upper_half_block + colored_content + scrollbar_sgr + @upper_half_block elsif scrollbar_pos <= (i * 2 + 1) and (i * 2) < (scrollbar_pos + bar_height) - colored_content + color_seq + @lower_half_block + colored_content + scrollbar_sgr + @lower_half_block else - colored_content + color_seq + ' ' * @block_elem_width + colored_content + scrollbar_sgr + ' ' * @block_elem_width end else colored_content |