dencli/lib/dencli/interactive.rb

139 lines
2.7 KiB
Ruby

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
begin
Readline.vi_editing_mode
rescue NotImplementedError
end
Readline.completion_append_character = " "
Readline.completion_proc = method :complete
prepare_sub cl.subs
cl.cmd :exit, "exit", min: cl.has?(:ex) ? cl.has?(:exi) ? 4 : 3 : 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.description[3..-1]}",
min: n.has?(:ex) ? n.has?( :exi) ? 4 : 3 : 2,
&lambda {|| @cur = n.parent }
n.aliases.delete nil
n.subs.delete nil
n.cmd '', "", min: 2, aliases: [nil] do
@cur = n
end
n.aliases['?'] = n[:help] if n.has? :help and not n.has? '?'
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 ::DenCli::UsageError
$stderr.puts "! #$!"
end
true
end
def run
while step
end
end
end