Class: SwarmCLI::V3::RawInputReader

Inherits:
Object
  • Object
show all
Defined in:
lib/swarm_cli/v3/raw_input_reader.rb

Overview

Note:

This class directly controls terminal state via stty. Always call #stop or #restore_terminal before exiting.

Raw terminal input reader with history support.

Manages raw terminal mode and reads characters in a background Thread, producing events on a Thread::Queue. The Async scheduler intercepts Thread#value in #next_event, yielding the fiber so the agent task can run concurrently.

Events: [:line, text], [:interrupt], [:eof], [:exit]

Instance Method Summary collapse

Constructor Details

#initialize(display) ⇒ RawInputReader

Returns a new instance of RawInputReader.

Parameters:

  • display (Display)

    the display coordinator for echoing input



18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/swarm_cli/v3/raw_input_reader.rb', line 18

def initialize(display)
  @display = display
  @queue = Thread::Queue.new
  @history = []
  @history_index = nil
  @saved_buffer = nil
  @original_stty = nil
  @thread = nil
  @running = false
  @at_exit_registered = false
  @last_ctrl_c = nil
  @last_escape = nil
end

Instance Method Details

#load_history_file(path) ⇒ void

This method returns an undefined value.

Load history from a file (one entry per line).

Parameters:

  • path (String)

    path to the history file



82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/swarm_cli/v3/raw_input_reader.rb', line 82

def load_history_file(path)
  return unless File.exist?(path)

  File.open(path, "r:UTF-8") do |f|
    f.each_line do |line|
      line = line.chomp
      @history << line unless line.empty?
    end
  end
rescue Errno::ENOENT, Errno::EACCES
  nil
end

#next_eventArray

Returns the next input event, yielding the Async fiber while waiting.

Async’s scheduler intercepts Thread#value and suspends the fiber, letting the agent task run while we wait for user input.

Returns:

  • (Array)

    event tuple, e.g. [:line, “hello”]



74
75
76
# File 'lib/swarm_cli/v3/raw_input_reader.rb', line 74

def next_event
  Thread.new { @queue.pop }.value
end

#restore_terminalvoid

This method returns an undefined value.

Restore terminal to its original state (idempotent).



61
62
63
64
65
66
# File 'lib/swarm_cli/v3/raw_input_reader.rb', line 61

def restore_terminal
  return unless @original_stty && !@original_stty.empty?

  system("stty #{@original_stty} 2>/dev/null")
  @original_stty = nil
end

#save_history_file(path, max: 1000) ⇒ void

This method returns an undefined value.

Save history to a file, keeping the last max entries.

Parameters:

  • path (String)

    path to the history file

  • max (Integer) (defaults to: 1000)

    maximum entries to keep



100
101
102
103
104
105
106
# File 'lib/swarm_cli/v3/raw_input_reader.rb', line 100

def save_history_file(path, max: 1000)
  FileUtils.mkdir_p(File.dirname(path))
  lines = @history.last(max)
  File.open(path, "w", 0o600) { |f| lines.each { |l| f.puts(l) } }
rescue Errno::EACCES, Errno::ENOENT
  nil
end

#startvoid

This method returns an undefined value.

Enter raw terminal mode and start the input thread.



35
36
37
38
39
40
41
42
43
44
45
# File 'lib/swarm_cli/v3/raw_input_reader.rb', line 35

def start
  @original_stty = %x(stty -g 2>/dev/null).chomp
  system("stty -icanon -echo -isig min 1 2>/dev/null")
  @running = true
  @thread = Thread.new { read_loop }

  return if @at_exit_registered

  at_exit { restore_terminal }
  @at_exit_registered = true
end

#stopvoid

This method returns an undefined value.

Stop the input thread and restore terminal state.



50
51
52
53
54
55
56
# File 'lib/swarm_cli/v3/raw_input_reader.rb', line 50

def stop
  @running = false
  @thread&.kill
  @thread&.join(0.1)
  @thread = nil
  restore_terminal
end