Class: Muxr::InputHandler

Inherits:
Object
  • Object
show all
Defined in:
lib/muxr/input_handler.rb

Overview

Translates raw keystrokes into either commands (when the Ctrl-a prefix is active) or passthrough bytes to the focused pane. The handler is a small state machine: :idle → :prefix → :idle, with a separate :command branch for the “:”-driven mini-command line.

Constant Summary collapse

PREFIX =

Ctrl-a

"\x01".freeze
PREFIX_BINDINGS =
{
  "c"    => :new_pane,
  "n"    => :focus_next,
  "p"    => :focus_prev,
  "a"    => :focus_last,
  "k"    => :close_focused,
  "\t"   => :cycle_layout,
  "\r"   => :promote_master,
  "\n"   => :promote_master,
  "~"    => :toggle_drawer,
  "C"    => :toggle_claude_drawer,
  "P"    => :toggle_private_focused,
  "d"    => :detach,
  "?"    => :show_help,
  "q"    => :quit_immediate,
  "["    => :enter_scrollback,
  "]"    => :paste_from_buffer
}.freeze
SCROLLBACK_BINDINGS =
{
  "j"    => :line_forward,
  "k"    => :line_back,
  "\x04" => :half_forward, # Ctrl-d
  "\x15" => :half_back,    # Ctrl-u
  "d"    => :half_forward,
  "u"    => :half_back,
  "\x06" => :full_forward, # Ctrl-f
  "\x02" => :full_back,    # Ctrl-b
  "f"    => :full_forward,
  "b"    => :full_back,
  " "    => :full_forward,
  "g"    => :top,
  "G"    => :bottom
}.freeze
SCROLLBACK_EXITS =

q, Esc, Ctrl-c

["q", "\e", "\x03"].freeze
SELECTION_BINDINGS =
{
  "h"    => :left,
  "l"    => :right,
  "j"    => :down,
  "k"    => :up,
  "0"    => :line_start,
  "$"    => :line_end,
  "^"    => :line_first_nonblank,
  "g"    => :top,
  "G"    => :bottom,
  "H"    => :screen_top,
  "M"    => :screen_middle,
  "L"    => :screen_bottom,
  "w"    => :word_forward,
  "W"    => :word_forward_big,
  "e"    => :word_end,
  "E"    => :word_end_big,
  # `b` is vim word-back here; the tmux-style page-back alias lives on Ctrl-b.
  "b"    => :word_backward,
  "B"    => :word_backward_big,
  "\x04" => :half_down, # Ctrl-d
  "\x15" => :half_up,   # Ctrl-u
  "d"    => :half_down,
  "u"    => :half_up,
  "\x06" => :full_down, # Ctrl-f
  "\x02" => :full_up,   # Ctrl-b
  "f"    => :full_down,
  " "    => :full_down
}.freeze
SELECTION_YANK =
["\r", "\n", "y"].freeze
SELECTION_CANCEL =

q, Esc, Ctrl-c

["q", "\e", "\x03"].freeze
DIGIT_RE =
/\A[1-9]\z/.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app) ⇒ InputHandler

Returns a new instance of InputHandler.



83
84
85
86
87
# File 'lib/muxr/input_handler.rb', line 83

def initialize(app)
  @app = app
  @state = :idle
  @command_buffer = +""
end

Instance Attribute Details

#command_bufferObject (readonly)

Returns the value of attribute command_buffer.



81
82
83
# File 'lib/muxr/input_handler.rb', line 81

def command_buffer
  @command_buffer
end

#stateObject (readonly)

Returns the value of attribute state.



81
82
83
# File 'lib/muxr/input_handler.rb', line 81

def state
  @state
end

Instance Method Details

#cancelObject



148
149
150
151
# File 'lib/muxr/input_handler.rb', line 148

def cancel
  @state = :idle
  @command_buffer = +""
end

#enter_confirm_quitObject



132
133
134
# File 'lib/muxr/input_handler.rb', line 132

def enter_confirm_quit
  @state = :confirm_quit
end

#enter_help_modeObject



128
129
130
# File 'lib/muxr/input_handler.rb', line 128

def enter_help_mode
  @state = :help
end

#enter_idle_modeObject



144
145
146
# File 'lib/muxr/input_handler.rb', line 144

def enter_idle_mode
  @state = :idle
end

#enter_scrollback_modeObject



136
137
138
# File 'lib/muxr/input_handler.rb', line 136

def enter_scrollback_mode
  @state = :scrollback
end

#enter_selection_modeObject



140
141
142
# File 'lib/muxr/input_handler.rb', line 140

def enter_selection_mode
  @state = :selection
end

#feed(data) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/muxr/input_handler.rb', line 89

def feed(data)
  remaining = data
  until remaining.empty?
    if @state == :idle
      # Fast path for pass-through: forward everything up to the next
      # Ctrl-a as a single chunk so a large paste doesn't turn into one
      # PTY write per byte. PREFIX is single-byte ASCII (\x01) and never
      # appears mid-UTF-8, so byte/char index match.
      idx = remaining.index(PREFIX)
      if idx.nil?
        @app.send_to_focused(remaining)
        return
      end
      @app.send_to_focused(remaining[0...idx]) if idx > 0
      @state = :prefix
      remaining = remaining[(idx + 1)..] || ""
      next
    end

    ch = remaining[0]
    remaining = remaining[1..] || ""
    case @state
    when :help
      @app.dismiss_help
      @state = :idle
    when :confirm_quit
      handle_confirm_quit(ch)
    when :prefix
      handle_prefix(ch)
    when :command
      handle_command_input(ch)
    when :scrollback
      handle_scrollback_input(ch)
    when :selection
      handle_selection_input(ch)
    end
  end
end