summaryrefslogtreecommitdiff
path: root/lib/reline/config.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/reline/config.rb')
-rw-r--r--lib/reline/config.rb235
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