diff options
author | aycabta <[email protected]> | 2019-04-23 18:09:46 +0900 |
---|---|---|
committer | Nobuyoshi Nakada <[email protected]> | 2019-04-23 20:08:02 +0900 |
commit | f2cd4f4cd0a1e352fcc48a216127beaeda2b2399 (patch) | |
tree | 8e02f39a8df8fa5708e446aeb8d42d45d8a28abe /lib/reline/config.rb | |
parent | 87cf45a512a7803f266e4782c49e0a99c06a4039 (diff) |
IRB is improved with Reline and RDoc, take 2
Diffstat (limited to 'lib/reline/config.rb')
-rw-r--r-- | lib/reline/config.rb | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/lib/reline/config.rb b/lib/reline/config.rb new file mode 100644 index 0000000000..0800dfd30f --- /dev/null +++ b/lib/reline/config.rb @@ -0,0 +1,235 @@ +require 'pathname' + +class Reline::Config + DEFAULT_PATH = Pathname.new(Dir.home).join('.inputrc') + + def initialize + @skip_section = nil + @if_stack = [] + @editing_mode_label = :emacs + @keymap_label = :emacs + @key_actors = {} + @key_actors[:emacs] = Reline::KeyActor::Emacs.new + @key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new + @key_actors[:vi_command] = Reline::KeyActor::ViCommand.new + end + + def reset + if editing_mode_is?(:vi_command) + @editing_mode_label = :vi_insert + end + end + + def editing_mode + @key_actors[@editing_mode_label] + end + + def editing_mode=(val) + @editing_mode_label = val + end + + def editing_mode_is?(*val) + (val.respond_to?(:any?) ? val : [val]).any?(@editing_mode_label) + end + + def keymap + @key_actors[@keymap_label] + end + + def read(file = DEFAULT_PATH) + begin + if file.respond_to?(:readlines) + lines = file.readlines + else + File.open(file, 'rt') do |f| + lines = f.readlines + end + end + rescue Errno::ENOENT + $stderr.puts "no such file #{file}" + return nil + end + + read_lines(lines) + self + end + + def read_lines(lines) + lines.each do |line| + line = line.chomp.gsub(/^\s*/, '') + if line[0, 1] == '$' + handle_directive(line[1..-1]) + next + end + + next if @skip_section + + if line.match(/^set +([^ ]+) +([^ ]+)/i) + var, value = $1.downcase, $2.downcase + bind_variable(var, value) + next + end + + if line =~ /\s*(.*)\s*:\s*(.*)\s*$/ + key, func_name = $1, $2 + bind_key(key, func_name) + end + end + end + + def handle_directive(directive) + directive, args = directive.split(' ') + case directive + when 'if' + condition = false + case args # TODO: variables + when 'mode' + when 'term' + when 'version' + else # application name + condition = true if args == 'Ruby' + end + unless @skip_section.nil? + @if_stack << @skip_section + end + @skip_section = !condition + when 'else' + @skip_section = !@skip_section + when 'endif' + @skip_section = nil + unless @if_stack.empty? + @skip_section = @if_stack.pop + end + when 'include' + read(args) + end + end + + def bind_variable(name, value) + case name + when %w{ + bind-tty-special-chars + blink-matching-paren + byte-oriented + completion-ignore-case + convert-meta + disable-completion + enable-keypad + expand-tilde + history-preserve-point + horizontal-scroll-mode + input-meta + mark-directories + mark-modified-lines + mark-symlinked-directories + match-hidden-files + meta-flag + output-meta + page-completions + prefer-visible-bell + print-completions-horizontally + show-all-if-ambiguous + show-all-if-unmodified + visible-stats + } then + variable_name = :"@#{name.tr(?-, ?_)}" + instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on') + when 'bell-style' + @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 + when 'completion-query-items' + @completion_query_items = value.to_i + when 'isearch-terminators' + @isearch_terminators = instance_eval(value) + when 'editing-mode' + case value + when 'emacs' + @editing_mode_label = :emacs + @keymap_label = :emacs + when 'vi' + @editing_mode_label = :vi_insert + @keymap_label = :vi_insert + end + when 'keymap' + case value + when 'emacs', 'emacs-standard', 'emacs-meta', 'emacs-ctlx' + @keymap_label = :emacs + when 'vi', 'vi-move', 'vi-command' + @keymap_label = :vi_command + when 'vi-insert' + @keymap_label = :vi_insert + end + end + end + + def bind_key(key, func_name) + if key =~ /"(.*)"/ + keyseq = parse_keyseq($1).force_encoding('ASCII-8BIT') + else + keyseq = nil + end + if func_name =~ /"(.*)"/ + func = parse_keyseq($1).force_encoding('ASCII-8BIT') + else + func = func_name.to_sym # It must be macro. + end + [keyseq, func] + end + + def key_notation_to_char(notation) + case notation + when /\\C-([A-Za-z_])/ + (1 + $1.downcase.ord - ?a.ord).chr('ASCII-8BIT') + when /\\M-([0-9A-Za-z_])/ + modified_key = $1 + code = + case $1 + when /[0-9]/ + ?\M-0.bytes.first + (modified_key.ord - ?0.ord) + when /[A-Z]/ + ?\M-A.bytes.first + (modified_key.ord - ?A.ord) + when /[a-z]/ + ?\M-a.bytes.first + (modified_key.ord - ?a.ord) + end + code.chr('ASCII-8BIT') + when /\\C-M-[A-Za-z_]/, /\\M-C-[A-Za-z_]/ + # 129 M-^A + when /\\(\d{1,3})/ then $1.to_i(8).chr # octal + when /\\x(\h{1,2})/ then $1.to_i(16).chr # hexadecimal + when "\\e" then ?\e + when "\\\\" then ?\\ + when "\\\"" then ?" + when "\\'" then ?' + when "\\a" then ?\a + when "\\b" then ?\b + when "\\d" then ?\d + when "\\f" then ?\f + when "\\n" then ?\n + when "\\r" then ?\r + when "\\t" then ?\t + when "\\v" then ?\v + else notation + end + end + + def parse_keyseq(str) + # TODO: Control- and Meta- + ret = String.new(encoding: 'ASCII-8BIT') + while str =~ /(\\C-[A-Za-z_]|\\M-[0-9A-Za-z_]|\\C-M-[A-Za-z_]|\\M-C-[A-Za-z_]|\\e|\\\\|\\"|\\'|\\a|\\b|\\d|\\f|\\n|\\r|\\t|\\v|\\\d{1,3}|\\x\h{1,2}|.)/ + ret << key_notation_to_char($&) + str = $' + end + ret + end +end |