lib/textbringer/controller.rb in textbringer-0.1.8 vs lib/textbringer/controller.rb in textbringer-0.1.9

- old
+ new

@@ -3,13 +3,15 @@ module Textbringer TOP_LEVEL_TAG = Object.new RECURSIVE_EDIT_TAG = Object.new class Controller + attr_reader :this_command_keys attr_accessor :this_command, :last_command, :overriding_map attr_accessor :prefix_arg, :current_prefix_arg attr_reader :key_sequence, :last_key, :recursive_edit_level + attr_reader :last_keyboard_macro @@current = nil def self.current @@current @@ -18,19 +20,24 @@ def self.current=(controller) @@current = controller end def initialize + @top_self = eval("self", TOPLEVEL_BINDING) @key_sequence = [] @last_key = nil @recursive_edit_level = 0 + @this_command_keys = nil @this_command = nil @last_command = nil @overriding_map = nil @prefix_arg = nil @current_prefix_arg = nil @echo_immediately = false + @recording_keyboard_macro = nil + @last_keyboard_macro = nil + @executing_keyboard_macro = nil end def command_loop(tag) catch(tag) do loop do @@ -41,53 +48,64 @@ Window.echo_area.clear_message @last_key = c @key_sequence << @last_key cmd = key_binding(@key_sequence) if cmd.is_a?(Symbol) || cmd.respond_to?(:call) - @key_sequence.clear + @this_command_keys = @key_sequence + @key_sequence = [] @this_command = cmd @current_prefix_arg = @prefix_arg @prefix_arg = nil begin run_hooks(:pre_command_hook, remove_on_error: true) if cmd.is_a?(Symbol) - send(cmd) + @top_self.send(cmd) else cmd.call end ensure run_hooks(:post_command_hook, remove_on_error: true) @last_command = @this_command @this_command = nil end else if cmd.nil? - keys = @key_sequence.map { |ch| key_name(ch) }.join(" ") + keys = Keymap.key_sequence_string(@key_sequence) @key_sequence.clear @prefix_arg = nil message("#{keys} is undefined") end end + Window.redisplay rescue Exception => e show_exception(e) @prefix_arg = nil + @recording_keyboard_macro = nil + Window.redisplay + if Window.echo_area.active? + wait_input(2000) + Window.echo_area.clear_message + Window.redisplay + end end - Window.redisplay end end end def wait_input(msecs) + if executing_keyboard_macro? + return @executing_keyboard_macro.first + end Window.current.wait_input(msecs) end def read_char - Window.current.read_char + read_char_with_keyboard_macro(:read_char) end def read_char_nonblock - Window.current.read_char_nonblock + read_char_with_keyboard_macro(:read_char_nonblock) end def received_keyboard_quit? while key = read_char_nonblock if GLOBAL_MAP.lookup([key]) == :keyboard_quit @@ -106,26 +124,12 @@ ensure @recursive_edit_level -= 1 end end - def key_name(key) - case key - when Symbol - "<#{key}>" - when "\e" - "ESC" - when "\C-m" - "RET" - when /\A[\0-\b\v-\x1f\x7f]\z/ - "C-" + (key.ord ^ 0x40).chr.downcase - else - key.to_s - end - end - def echo_input + return if executing_keyboard_macro? if @prefix_arg || !@key_sequence.empty? if !@echo_immediately return if wait_input(1000) end @echo_immediately = true @@ -136,11 +140,11 @@ s << "(#{@prefix_arg.inspect})" end end if !@key_sequence.empty? s << " " if !s.empty? - s << @key_sequence.map { |ch| key_name(ch) }.join(" ") + s << Keymap.key_sequence_string(@key_sequence) end s << "-" Window.echo_area.show(s) Window.echo_area.redisplay Window.current.window.noutrefresh @@ -148,14 +152,76 @@ else @echo_immediately = false end end - private + def start_keyboard_macro + if @recording_keyboard_macro + @recording_keyboard_macro = nil + raise EditorError, "Already recording keyboard macro" + end + @recording_keyboard_macro = [] + end + def end_keyboard_macro + if @recording_keyboard_macro.nil? + raise EditorError, "Not recording keyboard macro" + end + if @recording_keyboard_macro.empty? + raise EditorError, "Empty keyboard macro" + end + @recording_keyboard_macro.pop(@this_command_keys.size) + @last_keyboard_macro = @recording_keyboard_macro + @recording_keyboard_macro = nil + end + + def execute_keyboard_macro(macro, n = 1) + n.times do + @executing_keyboard_macro = macro.dup + begin + recursive_edit + ensure + @executing_keyboard_macro = nil + end + end + end + + def call_last_keyboard_macro(n) + if @last_keyboard_macro.nil? + raise EditorError, "Keyboard macro not defined" + end + execute_keyboard_macro(@last_keyboard_macro, n) + end + + def recording_keyboard_macro? + !@recording_keyboard_macro.nil? + end + + def executing_keyboard_macro? + !@executing_keyboard_macro.nil? + end + def key_binding(key_sequence) @overriding_map&.lookup(key_sequence) || Buffer.current&.keymap&.lookup(key_sequence) || GLOBAL_MAP.lookup(key_sequence) + end + + private + + def read_char_with_keyboard_macro(read_char_method) + if !executing_keyboard_macro? + c = call_read_char_method(read_char_method) + if @recording_keyboard_macro + @recording_keyboard_macro.push(c) + end + c + else + @executing_keyboard_macro.shift + end + end + + def call_read_char_method(read_char_method) + Window.current.send(read_char_method) end end end