class Textbringer::Controller

Attributes

current_prefix_arg[RW]
key_sequence[R]
last_command[RW]
last_key[R]
last_keyboard_macro[R]
overriding_map[RW]
prefix_arg[RW]
recursive_edit_level[R]
this_command[RW]
this_command_keys[R]

Public Class Methods

current() click to toggle source
# File lib/textbringer/controller.rb, line 14
def self.current
  @@current
end
current=(controller) click to toggle source
# File lib/textbringer/controller.rb, line 18
def self.current=(controller)
  @@current = controller
end
new() click to toggle source
# File lib/textbringer/controller.rb, line 22
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
  @next_tick_queue = []
  @next_tick_queue_mutex = Mutex.new
  @next_tick_input, @next_tick_output = IO.pipe
end

Public Instance Methods

call_last_keyboard_macro(n) click to toggle source
# File lib/textbringer/controller.rb, line 231
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
close() click to toggle source
# File lib/textbringer/controller.rb, line 42
def close
  @next_tick_input.close
  @next_tick_output.close
end
command_loop(tag) click to toggle source
# File lib/textbringer/controller.rb, line 47
def command_loop(tag)
  catch(tag) do
    loop do
      begin
        echo_input
        c = read_event
        break if c.nil?
        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)
          @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)
              @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 = 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
    end
  end
end
echo_input() click to toggle source
# File lib/textbringer/controller.rb, line 172
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
    s = +""
    if @prefix_arg
      s << "C-u"
      if @prefix_arg != [4]
        s << "(#{@prefix_arg.inspect})"
      end
    end
    if !@key_sequence.empty?
      s << " " if !s.empty?
      s << Keymap.key_sequence_string(@key_sequence)
    end
    s << "-"
    Window.echo_area.show(s)
    Window.echo_area.redisplay
    Window.current.window.noutrefresh
    Window.update
  else
    @echo_immediately = false
  end
end
end_keyboard_macro() click to toggle source
# File lib/textbringer/controller.rb, line 208
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
execute_keyboard_macro(macro, n = 1) click to toggle source
# File lib/textbringer/controller.rb, line 220
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
executing_keyboard_macro?() click to toggle source
# File lib/textbringer/controller.rb, line 242
def executing_keyboard_macro?
  !@executing_keyboard_macro.nil?
end
key_binding(key_sequence) click to toggle source
# File lib/textbringer/controller.rb, line 246
def key_binding(key_sequence)
  @overriding_map&.lookup(key_sequence) ||
  Buffer.current&.keymap&.lookup(key_sequence) ||
    GLOBAL_MAP.lookup(key_sequence)
end
next_tick(&block) click to toggle source
# File lib/textbringer/controller.rb, line 108
def next_tick(&block)
  @next_tick_queue_mutex.synchronize do
    @next_tick_queue.push(block)
  end
  @next_tick_output.write("\n")
end
read_event() click to toggle source
# File lib/textbringer/controller.rb, line 115
def read_event
  if executing_keyboard_macro?
    return @executing_keyboard_macro.shift
  end
  event = read_event_nonblock
  if event
    return event
  end
  loop do
    if Window.echo_area.active?
      wait_files = [STDIN]
    else
      wait_files = [STDIN, @next_tick_input]
    end
    files, = IO.select(wait_files, nil, nil, 1)
    # KEY_RESIZE may be returned even if STDIN is not included in files.
    event = read_event_nonblock
    if event
      return event
    end
    if !Window.echo_area.active? && files&.include?(@next_tick_input)
      c = @next_tick_input.read_nonblock(1, exception: false)
      if !c.nil? && c != :wait_readable
        block = @next_tick_queue_mutex.synchronize {
          @next_tick_queue.shift
        }
        block.call
        Window.redisplay
      end
    end
  end
end
read_event_nonblock() click to toggle source
# File lib/textbringer/controller.rb, line 148
def read_event_nonblock
  read_event_with_keyboard_macro(:read_event_nonblock)
end
received_keyboard_quit?() click to toggle source
# File lib/textbringer/controller.rb, line 152
def received_keyboard_quit?
  while key = read_event_nonblock
    if GLOBAL_MAP.lookup([key]) == :keyboard_quit
      return true
    end
  end
  false
end
recording_keyboard_macro?() click to toggle source
# File lib/textbringer/controller.rb, line 238
def recording_keyboard_macro?
  !@recording_keyboard_macro.nil?
end
recursive_edit() click to toggle source
# File lib/textbringer/controller.rb, line 161
def recursive_edit
  @recursive_edit_level += 1
  begin
    if command_loop(RECURSIVE_EDIT_TAG)
      raise Quit
    end
  ensure
    @recursive_edit_level -= 1
  end
end
start_keyboard_macro() click to toggle source
# File lib/textbringer/controller.rb, line 200
def start_keyboard_macro
  if @recording_keyboard_macro
    @recording_keyboard_macro = nil
    raise EditorError, "Already recording keyboard macro"
  end
  @recording_keyboard_macro = []
end
wait_input(msecs) click to toggle source
# File lib/textbringer/controller.rb, line 100
def wait_input(msecs)
  # TODO: Check @next_tick_queue
  if executing_keyboard_macro?
    return @executing_keyboard_macro.first
  end
  Window.current.wait_input(msecs)
end

Private Instance Methods

call_read_event_method(read_event_method) click to toggle source
# File lib/textbringer/controller.rb, line 266
def call_read_event_method(read_event_method)
  Window.current.send(read_event_method)&.then { |event|
    if @key_sequence.empty?
      Buffer.current.filter_event(event)
    else
      event
    end
  }
end
read_event_with_keyboard_macro(read_event_method) click to toggle source
# File lib/textbringer/controller.rb, line 254
def read_event_with_keyboard_macro(read_event_method)
  if !executing_keyboard_macro?
    c = call_read_event_method(read_event_method)
    if c && @recording_keyboard_macro
      @recording_keyboard_macro.push(c)
    end
    c
  else
    @executing_keyboard_macro.shift
  end
end