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