Class: Rubino::UI::Composer::InputLine

Inherits:
Object
  • Object
show all
Defined in:
lib/rubino/ui/composer/input_line.rb

Overview

The editable input line: the text buffer and the cursor, plus the pure editing operations over them. NO terminal I/O, NO rendering, NO locking —the composer owns the render mutex and calls these under it, then redraws. Extracted from BottomComposer so the cursor/codepoint math lives in one small, directly-unit-testable place instead of the god class.

All indices are CODEPOINT offsets into text (0..length). Mutators clamp to the buffer and never raise on out-of-range input.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(text: +"",, cursor: 0) ⇒ InputLine

Returns a new instance of InputLine.



17
18
19
20
# File 'lib/rubino/ui/composer/input_line.rb', line 17

def initialize(text: +"", cursor: 0)
  @text   = text.to_s.dup
  @cursor = cursor.clamp(0, @text.length)
end

Instance Attribute Details

#cursorObject (readonly)

Returns the value of attribute cursor.



15
16
17
# File 'lib/rubino/ui/composer/input_line.rb', line 15

def cursor
  @cursor
end

#textObject (readonly)

Returns the value of attribute text.



15
16
17
# File 'lib/rubino/ui/composer/input_line.rb', line 15

def text
  @text
end

Instance Method Details

#clearObject

Clear the whole line (Ctrl+U on this single-line composer — see the BottomComposer note on why this kills the whole line, not just to BOL).



74
75
76
77
78
# File 'lib/rubino/ui/composer/input_line.rb', line 74

def clear
  @text = +""
  @cursor = 0
  self
end

#delete_backObject

Remove the single char BEFORE the cursor (Backspace). No-op at col 0.



35
36
37
38
39
40
41
42
43
# File 'lib/rubino/ui/composer/input_line.rb', line 35

def delete_back
  return self unless @cursor.positive?

  chars = @text.chars
  chars.delete_at(@cursor - 1)
  @text = chars.join
  @cursor -= 1
  self
end

#delete_forwardObject

Remove the char AT the cursor (Delete / Ctrl+D). No-op at end.



46
47
48
49
50
51
52
53
# File 'lib/rubino/ui/composer/input_line.rb', line 46

def delete_forward
  chars = @text.chars
  if @cursor < chars.length
    chars.delete_at(@cursor)
    @text = chars.join
  end
  self
end

#delete_span(start, len) ⇒ Object

Remove len chars starting at codepoint start and park the cursor at start — used to delete a whole “[Pasted text #N …]” placeholder token in one keystroke (the span is computed by the caller from the store).



58
59
60
61
62
63
64
# File 'lib/rubino/ui/composer/input_line.rb', line 58

def delete_span(start, len)
  chars = @text.chars
  chars.slice!(start, len)
  @text = chars.join
  @cursor = start
  self
end

#empty?Boolean

Returns:

  • (Boolean)


23
# File 'lib/rubino/ui/composer/input_line.rb', line 23

def empty? = @text.empty?

#insert(str) ⇒ Object

Insert str at the cursor and advance past it.



26
27
28
29
30
31
32
# File 'lib/rubino/ui/composer/input_line.rb', line 26

def insert(str)
  chars = @text.chars
  chars.insert(@cursor, *str.to_s.chars)
  @text = chars.join
  @cursor += str.to_s.chars.length
  self
end

#kill_to_endObject

Delete from the cursor to the end of the line (Ctrl+K).



67
68
69
70
# File 'lib/rubino/ui/composer/input_line.rb', line 67

def kill_to_end
  @text = @text.chars.first(@cursor).join
  self
end

#lengthObject



22
# File 'lib/rubino/ui/composer/input_line.rb', line 22

def length = @text.length

#move_by(delta) ⇒ Object

Move the cursor by delta codepoints, clamped.



89
90
91
92
# File 'lib/rubino/ui/composer/input_line.rb', line 89

def move_by(delta)
  @cursor = (@cursor + delta).clamp(0, @text.length)
  self
end

#move_to(index) ⇒ Object

Move the cursor to an absolute codepoint index, clamped.



95
96
97
98
# File 'lib/rubino/ui/composer/input_line.rb', line 95

def move_to(index)
  @cursor = index.to_i.clamp(0, @text.length)
  self
end

#replace(str) ⇒ Object

Replace the whole line and park the cursor at its end (history recall, draft restore).



82
83
84
85
86
# File 'lib/rubino/ui/composer/input_line.rb', line 82

def replace(str)
  @text = str.to_s.dup
  @cursor = @text.length
  self
end

#takeObject

Return the current text and reset to an empty line (submit).



123
124
125
126
127
# File 'lib/rubino/ui/composer/input_line.rb', line 123

def take
  taken = @text
  clear
  taken
end

#word_leftObject

Word-jump LEFT: skip whitespace immediately left, then the word, landing at the start of the previous word.



102
103
104
105
106
107
108
109
# File 'lib/rubino/ui/composer/input_line.rb', line 102

def word_left
  chars = @text.chars
  i = @cursor
  i -= 1 while i.positive? && chars[i - 1] =~ /\s/
  i -= 1 while i.positive? && chars[i - 1] !~ /\s/
  @cursor = i
  self
end

#word_rightObject

Word-jump RIGHT: skip the current word then trailing whitespace, landing at the start of the next word.



113
114
115
116
117
118
119
120
# File 'lib/rubino/ui/composer/input_line.rb', line 113

def word_right
  chars = @text.chars
  i = @cursor
  i += 1 while i < chars.length && chars[i] !~ /\s/
  i += 1 while i < chars.length && chars[i] =~ /\s/
  @cursor = i
  self
end