diff --git a/lib/reline/config.rb b/lib/reline/config.rb index 87726393a6..bbaed84dc1 100644 --- a/lib/reline/config.rb +++ b/lib/reline/config.rb @@ -1,3 +1,5 @@ +require 'rubygems' + class Reline::Config attr_reader :test_mode @@ -45,6 +47,16 @@ class InvalidInputrc < RuntimeError attr_accessor v end + RUBY_OPERATOR_BY_INPUTRC_OPERATOR = { + '=': :==, + '==': :==, + '!=': :!=, + '<=': :<=, + '>=': :>=, + '<': :<, + '>': :> + } + attr_accessor :autocompletion def initialize @@ -227,18 +239,36 @@ def read_lines(lines, file = nil) end def handle_directive(directive, file, no) - directive, args = directive.split(' ') + directive, args = directive.split(/\s+/, 2) + args, _comment = args.split(/\s+#\s*/, 2) if args case directive when 'if' - condition = false - case args - when 'mode' - when 'term' - when 'version' - else # application name - condition = true if args == 'Ruby' - condition = true if args == 'Reline' - end + lhs, operator, rhs = args.partition(/\s*(==|=|!=|<=|>=|<|>)\s*/) + stripped_operator = $1 + ruby_operator = (RUBY_OPERATOR_BY_INPUTRC_OPERATOR[stripped_operator.to_sym] if stripped_operator) + + condition = + if lhs == 'term' && operator == '=' + rhs == ENV['TERM'] || rhs == ENV['TERM'].sub(/-.*\z/, '') + elsif lhs == 'mode' && operator == '=' + editing_mode = %i[vi_insert vi_command].include?(@editing_mode_label) ? :vi : @editing_mode_label + rhs == editing_mode.to_s + elsif lhs == 'version' && %w[= == != >= <= < >].include?(stripped_operator) + # Comment syntax here is different from comment syntax above: `#` does + # not need to be preceded by whitespace + rhs_version_str, _comment = rhs.split(/\s*#\s*/) + rhs_version = Gem::Version.new(rhs_version_str) + reline_compatible_readline_version = Gem::Version.new(Reline::COMPATIBLE_READLINE_VERSION) + reline_compatible_readline_version.public_send(ruby_operator, rhs_version) + elsif %w[Ruby Reline].include?(args) # application name + true + elsif %w[= == !=].include?(stripped_operator) # variable + parse_variable(lhs, rhs).all? do |var_name, rhs_value| + actual_value = instance_variable_get(:"@#{var_name}") + actual_value.public_send(ruby_operator, rhs_value) + end + end + @if_stack << [file, no, @skip_section] @skip_section = !condition when 'else' @@ -256,81 +286,103 @@ def handle_directive(directive, file, no) end end - def bind_variable(name, value) + def parse_variable(name, value) case name when 'history-size' begin - @history_size = Integer(value) + { history_size: Integer(value) } rescue ArgumentError - @history_size = 500 + { history_size: 500 } end when 'bell-style' - @bell_style = - case value - when 'none', 'off' - :none - when 'audible', 'on' - :audible - when 'visible' - :visible - else - :audible - end + { + bell_style: + case value + when 'none', 'off' + :none + when 'audible', 'on' + :audible + when 'visible' + :visible + else + :audible + end + } when 'comment-begin' - @comment_begin = value.dup + { comment_begin: value.dup } when 'completion-query-items' - @completion_query_items = value.to_i + { completion_query_items: value.to_i } when 'isearch-terminators' - @isearch_terminators = retrieve_string(value) + { isearch_terminators: retrieve_string(value) } when 'editing-mode' case value when 'emacs' - @editing_mode_label = :emacs - @keymap_label = :emacs - @keymap_prefix = [] + { + editing_mode_label: :emacs, + keymap_label: :emacs, + keymap_prefix: [] + } when 'vi' - @editing_mode_label = :vi_insert - @keymap_label = :vi_insert - @keymap_prefix = [] + { + editing_mode_label: :vi_insert, + keymap_label: :vi_insert, + keymap_prefix: [] + } end when 'keymap' case value when 'emacs', 'emacs-standard' - @keymap_label = :emacs - @keymap_prefix = [] + { + keymap_label: :emacs, + keymap_prefix: [] + } when 'emacs-ctlx' - @keymap_label = :emacs - @keymap_prefix = [?\C-x.ord] + { + keymap_label: :emacs, + keymap_prefix: [?\C-x.ord] + } when 'emacs-meta' - @keymap_label = :emacs - @keymap_prefix = [?\e.ord] + { + keymap_label: :emacs, + keymap_prefix: [?\e.ord] + } when 'vi', 'vi-move', 'vi-command' - @keymap_label = :vi_command - @keymap_prefix = [] + { + keymap_label: :vi_command, + keymap_prefix: [] + } when 'vi-insert' - @keymap_label = :vi_insert - @keymap_prefix = [] + { + keymap_label: :vi_insert, + keymap_prefix: [] + } end when 'keyseq-timeout' - @keyseq_timeout = value.to_i + { keyseq_timeout: value.to_i } when 'show-mode-in-prompt' case value when 'off' - @show_mode_in_prompt = false + { show_mode_in_prompt: false } when 'on' - @show_mode_in_prompt = true + { show_mode_in_prompt: true } else - @show_mode_in_prompt = false + { show_mode_in_prompt: false } end when 'vi-cmd-mode-string' - @vi_cmd_mode_string = retrieve_string(value) + { vi_cmd_mode_string: retrieve_string(value) } when 'vi-ins-mode-string' - @vi_ins_mode_string = retrieve_string(value) + { vi_ins_mode_string: retrieve_string(value) } when 'emacs-mode-string' - @emacs_mode_string = retrieve_string(value) + { emacs_mode_string: retrieve_string(value) } when *VARIABLE_NAMES then - variable_name = :"@#{name.tr(?-, ?_)}" - instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on') + variable_name = :"#{name.tr(?-, ?_)}" + { variable_name => value.nil? || value == '1' || value == 'on' } + end + end + + def bind_variable(name, value) + parse_variable(name, value).each do |parsed_name, parsed_value| + instance_variable_set(:"@#{parsed_name}", parsed_value) end end diff --git a/lib/reline/version.rb b/lib/reline/version.rb index a2538f6fa3..a7e0e7f565 100644 --- a/lib/reline/version.rb +++ b/lib/reline/version.rb @@ -1,3 +1,4 @@ module Reline VERSION = '0.3.8' + COMPATIBLE_READLINE_VERSION = '8.2' end diff --git a/test/reline/test_config.rb b/test/reline/test_config.rb index b16aed720a..b359eb26f2 100644 --- a/test/reline/test_config.rb +++ b/test/reline/test_config.rb @@ -160,9 +160,94 @@ def test_include assert_equal :audible, @config.instance_variable_get(:@bell_style) end - def test_if + def test_if_mode @config.read_lines(<<~LINES.lines) - $if Ruby + set editing-mode vi + $if mode=vi # comment + set bell-style audible + $else + set bell-style visible + $endif + LINES + + assert_equal :audible, @config.instance_variable_get(:@bell_style) + end + + def test_if_term + original_term = ENV['TERM'] + ENV['TERM'] = 'fake' + + @config.read_lines(<<~LINES.lines) + $if term=fake # comment + set bell-style audible + $else + set bell-style visible + $endif + LINES + + assert_equal :audible, @config.instance_variable_get(:@bell_style) + ensure + ENV['TERM'] = original_term + end + + def test_if_term_prefix + original_term = ENV['TERM'] + ENV['TERM'] = 'fake-color' + + @config.read_lines(<<~LINES.lines) + $if term=fake # comment + set bell-style audible + $else + set bell-style visible + $endif + LINES + + assert_equal :audible, @config.instance_variable_get(:@bell_style) + ensure + ENV['TERM'] = original_term + end + + def test_if_version + @config.read_lines(<<~LINES.lines) + $if version >= 0.0#comment without whitespace # comment with whitespace + set bell-style audible + $else + set bell-style visible + $endif + LINES + + assert_equal :audible, @config.instance_variable_get(:@bell_style) + end + + def test_if_application + @config.read_lines(<<~LINES.lines) + $if Ruby # comment + set bell-style audible + $else + set bell-style visible + $endif + LINES + + assert_equal :audible, @config.instance_variable_get(:@bell_style) + end + + def test_if_variable_boolean + @config.read_lines(<<~LINES.lines) + set show-mode-in-prompt on + $if show-mode-in-prompt = on # comment + set bell-style audible + $else + set bell-style visible + $endif + LINES + + assert_equal :audible, @config.instance_variable_get(:@bell_style) + end + + def test_if_variable_string + @config.read_lines(<<~LINES.lines) + set editing-mode vi + $if editing-mode == vi # comment set bell-style audible $else set bell-style visible