require_relative '../dencli' class DenCli::Interactive attr_reader :cl, :prompt, :cur, :histfile def initialize cl, prompt, histfile: nil require 'readline' @cl, self.prompt = cl, prompt @cur = cl.subs cur.instance_variable_set :@name, '' @histfile = case histfile when nil then nil when Pathname then histfile else Pathname.new histfile.to_s end read_history if @histfile Readline.vi_editing_mode rescue NotImplementedError Readline.completion_append_character = " " Readline.completion_proc = method :complete prepare_sub cl.subs cl.cmd :exit, "exit", min: 2 do exit 0 end cl.subs.aliases['?'] = cl.subs.subs['help'] cl.subs.subs.delete 'cli' stty_save = %x`stty -g`.chomp trap "INT" do system "stty", stty_save Readline.refresh_line end end def history_file file = nil file = case file when Pathname then file when nil then @histfile else Pathname.new file.to_s end end def read_history file = nil file = history_file file return unless file and file.exist? file.each_line do |line| Readline::HISTORY.push line.chomp end end def write_history file = nil file = history_file file return unless file file.open 'w+' do |f| Readline::HISTORY.each do |line| f.puts line end end end def prompt= s @prompt = s.to_s end def complete s ws = words Readline.line_buffer @cur.complete *ws[0...-1], s end def sub *args, cur: nil args.inject( cur || @cur) do |r, a| return nil unless r.is_a? DenCli::Sub r[a] end end private :sub def words line = nil r = line.split " " r.push '' if ' ' == line[-1] r end private :words def prepare_sub c c.subs.values.each do |n| case n when DenCli::Sub n.cmd :exit, "<- #{n.parent.full_cmd.join ' '} - #{n.parent.desc[3..-1]}", min: 2 do @cur = n.parent end n.cmd '', "", min: 2, aliases: [nil] do @cur = n end n.subs.delete '' n.aliases['?'] = n.subs['help'] prepare_sub n when DenCli::CMD else raise "Unsupported sub-type: #{x}" end end end def read line = Readline.readline( "#{prompt}#{cur.full_cmd.join ' '}> ", true) return nil if line.nil? Readline::HISTORY.pop if /^\s*$/ =~ line if 0 < Readline::HISTORY.length-2 and Readline::HISTORY[Readline::HISTORY.length-2] == line Readline::HISTORY.pop end line.split " " end def step line = read return nil if line.nil? begin cur.call *line rescue ::UsageError STDERR.puts "! #$!" end true end def run while step end end end