Class: Chamomile::TextArea

Inherits:
Object
  • Object
show all
Defined in:
lib/chamomile/components/text_area.rb,
lib/chamomile/components/text_area/key_map.rb

Overview

Multi-line text editor with cursor navigation, word wrapping, and line numbers.

Constant Summary collapse

DEFAULT_KEY_MAP =
KeyBinding.normalize({
  character_forward: [[:right, []], ["f", [:ctrl]]],
  character_backward: [[:left, []], ["b", [:ctrl]]],
  word_forward: [[:right, [:alt]], [:right, [:ctrl]], ["f", [:alt]]],
  word_backward: [[:left, [:alt]], [:left, [:ctrl]], ["b", [:alt]]],
  delete_word_backward: [[:backspace, [:alt]], ["w", [:ctrl]]],
  delete_word_forward: [[:delete, [:alt]], ["d", [:alt]]],
  delete_after_cursor: [["k", [:ctrl]]],
  delete_before_cursor: [["u", [:ctrl]]],
  delete_char_backward: [[:backspace, []], ["h", [:ctrl]]],
  delete_char_forward: [[:delete, []], ["d", [:ctrl]]],
  line_start: [[:home, []], ["a", [:ctrl]]],
  line_end: [[:end_key, []], ["e", [:ctrl]]],
  line_up: [[:up, []]],
  line_down: [[:down, []]],
  new_line: [[:enter, []]],
  input_begin: [[:home, [:ctrl]]],
  input_end: [[:end_key, [:ctrl]]],
  page_up: [[:page_up, []]],
  page_down: [[:page_down, []]],
})

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(width: 40, height: 6, key_map: DEFAULT_KEY_MAP, prompt: "", placeholder: "", char_limit: 0, show_line_numbers: false, end_of_buffer_char: "~") ⇒ TextArea

Returns a new instance of TextArea.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/chamomile/components/text_area.rb', line 11

def initialize(width: 40, height: 6, key_map: DEFAULT_KEY_MAP,
               prompt: "", placeholder: "", char_limit: 0,
               show_line_numbers: false, end_of_buffer_char: "~")
  @width = width
  @height = height
  @key_map = key_map
  @prompt = prompt
  @placeholder = placeholder
  @char_limit = char_limit
  @show_line_numbers = show_line_numbers
  @end_of_buffer_char = end_of_buffer_char
  @max_height = 0
  @max_width = 0
  @lines = [""]
  @row = 0
  @col = 0
  @offset = 0
  @focused = false
  @last_char_offset = 0
end

Instance Attribute Details

#char_limitObject

Returns the value of attribute char_limit.



7
8
9
# File 'lib/chamomile/components/text_area.rb', line 7

def char_limit
  @char_limit
end

#colObject (readonly)

Returns the value of attribute col.



6
7
8
# File 'lib/chamomile/components/text_area.rb', line 6

def col
  @col
end

#end_of_buffer_charObject

Returns the value of attribute end_of_buffer_char.



7
8
9
# File 'lib/chamomile/components/text_area.rb', line 7

def end_of_buffer_char
  @end_of_buffer_char
end

#heightObject

Returns the value of attribute height.



7
8
9
# File 'lib/chamomile/components/text_area.rb', line 7

def height
  @height
end

#key_mapObject

Returns the value of attribute key_map.



7
8
9
# File 'lib/chamomile/components/text_area.rb', line 7

def key_map
  @key_map
end

#max_heightObject

Returns the value of attribute max_height.



7
8
9
# File 'lib/chamomile/components/text_area.rb', line 7

def max_height
  @max_height
end

#max_widthObject

Returns the value of attribute max_width.



7
8
9
# File 'lib/chamomile/components/text_area.rb', line 7

def max_width
  @max_width
end

#placeholderObject

Returns the value of attribute placeholder.



7
8
9
# File 'lib/chamomile/components/text_area.rb', line 7

def placeholder
  @placeholder
end

#promptObject

Returns the value of attribute prompt.



7
8
9
# File 'lib/chamomile/components/text_area.rb', line 7

def prompt
  @prompt
end

#rowObject (readonly)

Returns the value of attribute row.



6
7
8
# File 'lib/chamomile/components/text_area.rb', line 6

def row
  @row
end

#show_line_numbersObject

Returns the value of attribute show_line_numbers.



7
8
9
# File 'lib/chamomile/components/text_area.rb', line 7

def show_line_numbers
  @show_line_numbers
end

#widthObject

Returns the value of attribute width.



7
8
9
# File 'lib/chamomile/components/text_area.rb', line 7

def width
  @width
end

Instance Method Details

#blurObject



37
38
39
40
# File 'lib/chamomile/components/text_area.rb', line 37

def blur
  @focused = false
  self
end

#cursor_downObject



83
84
85
86
87
88
89
# File 'lib/chamomile/components/text_area.rb', line 83

def cursor_down
  return if @row >= @lines.length - 1

  @row += 1
  @col = [@last_char_offset, @lines[@row].length].min
  clamp_offset
end

#cursor_endObject



96
97
98
99
# File 'lib/chamomile/components/text_area.rb', line 96

def cursor_end
  @col = @lines[@row].length
  @last_char_offset = @col
end

#cursor_startObject



91
92
93
94
# File 'lib/chamomile/components/text_area.rb', line 91

def cursor_start
  @col = 0
  @last_char_offset = @col
end

#cursor_upObject



75
76
77
78
79
80
81
# File 'lib/chamomile/components/text_area.rb', line 75

def cursor_up
  return if @row.zero?

  @row -= 1
  @col = [@last_char_offset, @lines[@row].length].min
  clamp_offset
end

#display_heightObject



69
70
71
72
73
# File 'lib/chamomile/components/text_area.rb', line 69

def display_height
  return @height unless @max_height.positive?

  [@lines.length, @height].max.clamp(@height, @max_height)
end

#focusObject



32
33
34
35
# File 'lib/chamomile/components/text_area.rb', line 32

def focus
  @focused = true
  self
end

#focused?Boolean

Returns:

  • (Boolean)


42
43
44
# File 'lib/chamomile/components/text_area.rb', line 42

def focused?
  @focused
end

#handle(msg) ⇒ Object Also known as: update



171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/chamomile/components/text_area.rb', line 171

def handle(msg)
  return unless @focused

  case msg
  when Chamomile::KeyEvent
    handle_key(msg)
  when Chamomile::PasteEvent
    handle_paste(msg)
  end

  nil
end

#insert_rune(c) ⇒ Object



152
153
154
155
156
157
158
159
160
161
# File 'lib/chamomile/components/text_area.rb', line 152

def insert_rune(c)
  return if c == "\n"

  return if @char_limit.positive? && length >= @char_limit

  line = @lines[@row]
  @lines[@row] = line[0, @col].to_s + c + line[@col..].to_s
  @col += 1
  @last_char_offset = @col
end

#insert_string(s) ⇒ Object



142
143
144
145
146
147
148
149
150
# File 'lib/chamomile/components/text_area.rb', line 142

def insert_string(s)
  s.each_char do |c|
    if c == "\n"
      split_line
    else
      insert_rune(c)
    end
  end
end

#lengthObject



65
66
67
# File 'lib/chamomile/components/text_area.rb', line 65

def length
  value.length
end

#line_countObject



61
62
63
# File 'lib/chamomile/components/text_area.rb', line 61

def line_count
  @lines.length
end

#move_to_beginObject



101
102
103
104
105
106
# File 'lib/chamomile/components/text_area.rb', line 101

def move_to_begin
  @row = 0
  @col = 0
  @last_char_offset = 0
  clamp_offset
end

#move_to_endObject



108
109
110
111
112
113
# File 'lib/chamomile/components/text_area.rb', line 108

def move_to_end
  @row = @lines.length - 1
  @col = @lines[@row].length
  @last_char_offset = @col
  clamp_offset
end

#page_downObject



122
123
124
125
126
127
# File 'lib/chamomile/components/text_area.rb', line 122

def page_down
  rows = display_height
  @row = [@row + rows, @lines.length - 1].min
  @col = [@last_char_offset, @lines[@row].length].min
  clamp_offset
end

#page_upObject



115
116
117
118
119
120
# File 'lib/chamomile/components/text_area.rb', line 115

def page_up
  rows = display_height
  @row = [@row - rows, 0].max
  @col = [@last_char_offset, @lines[@row].length].min
  clamp_offset
end

#resetObject



163
164
165
166
167
168
169
# File 'lib/chamomile/components/text_area.rb', line 163

def reset
  @lines = [""]
  @row = 0
  @col = 0
  @offset = 0
  @last_char_offset = 0
end

#valueObject



46
47
48
# File 'lib/chamomile/components/text_area.rb', line 46

def value
  @lines.join("\n")
end

#value=(s) ⇒ Object



50
51
52
53
54
55
56
57
58
59
# File 'lib/chamomile/components/text_area.rb', line 50

def value=(s)
  s = s.to_s
  s = s[0, @char_limit] if @char_limit.positive? && s.length > @char_limit
  @lines = s.split("\n", -1)
  @lines = [""] if @lines.empty?
  @row = @row.clamp(0, @lines.length - 1)
  @col = @col.clamp(0, @lines[@row].length)
  @last_char_offset = @col
  clamp_offset
end

#viewObject



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/chamomile/components/text_area.rb', line 186

def view
  return "#{@prompt}#{@placeholder}" if @lines == [""] && !@placeholder.empty? && !@focused

  view_h = display_height
  visible_lines = @lines[@offset, view_h] || []
  rendered = visible_lines.each_with_index.map do |line, i|
    actual_row = @offset + i
    render_line(line, actual_row)
  end

  # Pad with end-of-buffer chars
  while rendered.length < view_h
    prefix = @show_line_numbers ? "#{" " * gutter_width} " : ""
    rendered << "#{@prompt}#{prefix}#{@end_of_buffer_char}"
  end

  rendered.join("\n")
end

#wordObject



129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/chamomile/components/text_area.rb', line 129

def word
  line = @lines[@row]
  return "" if line.empty? || @col >= line.length || line[@col] =~ /\s/

  left = @col
  left -= 1 while left.positive? && line[left - 1] =~ /\S/

  right = @col
  right += 1 while right < line.length && line[right] =~ /\S/

  line[left...right]
end