sup

A curses threads-with-tags style email client

sup.git

git clone https://supmua.dev/git/sup/

lib/sup/mode.rb (3328B) - raw

      1 require 'open3'
      2 module Redwood
      3 
      4 class Mode
      5   attr_accessor :buffer
      6   @@keymaps = {}
      7 
      8   def self.register_keymap keymap=nil, &b
      9     keymap = Keymap.new(&b) if keymap.nil?
     10     @@keymaps[self] = keymap
     11   end
     12 
     13   def self.keymap
     14     @@keymaps[self] || register_keymap
     15   end
     16 
     17   def self.keymaps
     18     @@keymaps
     19   end
     20 
     21   def initialize
     22     @buffer = nil
     23   end
     24 
     25   def self.make_name s; s.gsub(/.*::/, "").camel_to_hyphy; end
     26   def name; Mode.make_name self.class.name; end
     27 
     28   def self.load_all_modes dir
     29     Dir[File.join(dir, "*.rb")].each do |f|
     30       $stderr.puts "## loading mode #{f}"
     31       require f
     32     end
     33   end
     34 
     35   def killable?; true; end
     36   def unsaved?; false end
     37   def draw; end
     38   def focus; end
     39   def blur; end
     40   def spawned; end
     41   def cancel_search!; end
     42   def in_search?; false end
     43   def status; ""; end
     44   def resize rows, cols; end
     45   def cleanup
     46     @buffer = nil
     47   end
     48 
     49   def resolve_input c
     50     self.class.ancestors.each do |klass| # try all keymaps in order of ancestry
     51       next unless @@keymaps.member?(klass)
     52       action = BufferManager.resolve_input_with_keymap c, @@keymaps[klass]
     53       return action if action
     54     end
     55     nil
     56   end
     57 
     58   def handle_input c
     59     action = resolve_input(c) or return false
     60     send action
     61     true
     62   end
     63 
     64   def help_text
     65     used_keys = {}
     66     self.class.ancestors.map do |klass|
     67       km = @@keymaps[klass] or next
     68       title = "Keybindings from #{Mode.make_name klass.name}"
     69       s = <<EOS
     70 #{title}
     71 #{'-' * title.display_length}
     72 
     73 #{km.help_text used_keys}
     74 EOS
     75       begin
     76         used_keys.merge! km.keysyms.to_boolean_h
     77       rescue ArgumentError
     78         raise km.keysyms.inspect
     79       end
     80       s
     81     end.compact.join "\n"
     82   end
     83 
     84 ### helper functions
     85 
     86   def save_to_file fn, talk=true
     87     FileUtils.mkdir_p File.dirname(fn)
     88     if File.exist? fn
     89       unless BufferManager.ask_yes_or_no "File \"#{fn}\" exists. Overwrite?"
     90         info "Not overwriting #{fn}"
     91         return
     92       end
     93     end
     94     begin
     95       File.open(fn, "w") { |f| yield f }
     96       BufferManager.flash "Successfully wrote #{fn}." if talk
     97       true
     98     rescue SystemCallError, IOError => e
     99       m = "Error writing file: #{e.message}"
    100       info m
    101       BufferManager.flash m
    102       false
    103     end
    104   end
    105 
    106   def pipe_to_process command
    107     begin
    108       Open3.popen3(command) do |input, output, error|
    109         err, data, * = IO.select [error], [input], nil
    110 
    111         unless err.empty?
    112           message = err.first.read
    113           if message =~ /^\s*$/
    114             warn "error running #{command} (but no error message)"
    115             BufferManager.flash "Error running #{command}!"
    116           else
    117             warn "error running #{command}: #{message}"
    118             BufferManager.flash "Error: #{message}"
    119           end
    120           return nil, false
    121         end
    122 
    123         data = data.first
    124         data.sync = false # buffer input
    125 
    126         yield data
    127         data.close # output will block unless input is closed
    128 
    129         ## BUG?: shows errors or output but not both....
    130         data, * = IO.select [output, error], nil, nil
    131         data = data.first
    132 
    133         if data.eof
    134           BufferManager.flash "'#{command}' done!"
    135           return nil, true
    136         else
    137           return data.read, true
    138         end
    139       end
    140     rescue Errno::ENOENT
    141       # If the command is invalid
    142       return nil, false
    143     end
    144   end
    145 end
    146 
    147 end