Skip to content
Kward Search API index

Class: Kward::PromptInterface::ComposerState

Inherits:
Object
  • Object
show all
Defined in:
lib/kward/prompt_interface/composer_state.rb

Overview

Mutable text, cursor, history, and overlay state for the composer.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeComposerState

Returns a new instance of ComposerState.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/kward/prompt_interface/composer_state.rb', line 34

def initialize
  @input = +""
  @cursor = 0
  @attachments = []
  @kill_buffer = ""
  @history = []
  @history_index = nil
  @history_draft = nil
  @prefill_input = nil
  @history_search_query = nil
  @history_search_draft = nil
  @history_search_index = 0
  @history_search_matches_query = nil
  @history_search_matches = nil
end

Instance Attribute Details

#attachmentsArray<Hash> (readonly)

Returns pending image/file attachments submitted with the next turn.

Returns:

  • (Array<Hash>)

    pending image/file attachments submitted with the next turn



30
31
32
# File 'lib/kward/prompt_interface/composer_state.rb', line 30

def attachments
  @attachments
end

#cursorInteger

Returns cursor offset into input.

Returns:

  • (Integer)

    cursor offset into input



14
15
16
# File 'lib/kward/prompt_interface/composer_state.rb', line 14

def cursor
  @cursor
end

#historyArray<String> (readonly)

Returns submitted input history.

Returns:

  • (Array<String>)

    submitted input history



32
33
34
# File 'lib/kward/prompt_interface/composer_state.rb', line 32

def history
  @history
end

#history_draftString?

Returns draft restored after leaving history navigation.

Returns:

  • (String, nil)

    draft restored after leaving history navigation



20
21
22
# File 'lib/kward/prompt_interface/composer_state.rb', line 20

def history_draft
  @history_draft
end

#history_indexInteger?

Returns active history index while navigating history.

Returns:

  • (Integer, nil)

    active history index while navigating history



18
19
20
# File 'lib/kward/prompt_interface/composer_state.rb', line 18

def history_index
  @history_index
end

#history_search_draftString?

Returns draft restored after canceling history search.

Returns:

  • (String, nil)

    draft restored after canceling history search



26
27
28
# File 'lib/kward/prompt_interface/composer_state.rb', line 26

def history_search_draft
  @history_search_draft
end

#history_search_indexInteger

Returns active selection index while searching history.

Returns:

  • (Integer)

    active selection index while searching history



28
29
30
# File 'lib/kward/prompt_interface/composer_state.rb', line 28

def history_search_index
  @history_search_index
end

#history_search_queryString?

Returns query typed while searching history.

Returns:

  • (String, nil)

    query typed while searching history



24
25
26
# File 'lib/kward/prompt_interface/composer_state.rb', line 24

def history_search_query
  @history_search_query
end

#inputString

Returns editable text currently shown in the composer.

Returns:

  • (String)

    editable text currently shown in the composer



12
13
14
# File 'lib/kward/prompt_interface/composer_state.rb', line 12

def input
  @input
end

#kill_bufferString

Returns most recently killed text available for yank.

Returns:

  • (String)

    most recently killed text available for yank



16
17
18
# File 'lib/kward/prompt_interface/composer_state.rb', line 16

def kill_buffer
  @kill_buffer
end

#prefill_inputString?

Returns text queued for the next composer prompt.

Returns:

  • (String, nil)

    text queued for the next composer prompt



22
23
24
# File 'lib/kward/prompt_interface/composer_state.rb', line 22

def prefill_input
  @prefill_input
end

Instance Method Details

#accept_history_searchObject



276
277
278
279
280
# File 'lib/kward/prompt_interface/composer_state.rb', line 276

def accept_history_search
  match = selected_history_search_match
  replace_input(match) if match
  reset_history_search
end

#add_attachment(attachment) ⇒ Object

Adds one attachment unless its source is already pending.



56
57
58
59
60
61
62
63
64
65
# File 'lib/kward/prompt_interface/composer_state.rb', line 56

def add_attachment(attachment)
  return false unless attachment.respond_to?(:key?)

  source = attachment[:source_text] || attachment["source_text"] || attachment[:original_path] || attachment["original_path"]
  return false if source.to_s.empty?
  return false if @attachments.any? { |item| (item[:source_text] || item["source_text"]).to_s == source.to_s }

  @attachments << attachment
  true
end

#add_history(value) ⇒ Object

Stores a submitted input unless it is blank or duplicates the previous entry.



192
193
194
195
196
197
198
199
200
# File 'lib/kward/prompt_interface/composer_state.rb', line 192

def add_history(value)
  stripped = value.to_s.strip
  return false if stripped.empty?
  return false if @history.last == value

  @history << value
  invalidate_history_search_matches
  true
end

#cancel_history_searchObject



282
283
284
285
# File 'lib/kward/prompt_interface/composer_state.rb', line 282

def cancel_history_search
  replace_input(@history_search_draft.to_s)
  reset_history_search
end

#clear_attachmentsObject

Removes all pending attachments without changing text input.



51
52
53
# File 'lib/kward/prompt_interface/composer_state.rb', line 51

def clear_attachments
  @attachments.clear
end

#delete_at_cursorObject

Deletes one grapheme at the cursor without moving it.



94
95
96
97
98
99
100
# File 'lib/kward/prompt_interface/composer_state.rb', line 94

def delete_at_cursor
  return false unless @cursor < @input.length

  following = TerminalText.next_grapheme_boundary(@input, @cursor)
  @input = @input[0...@cursor] + @input[following..]
  true
end

#delete_before_cursorObject

Deletes one grapheme before the cursor.



84
85
86
87
88
89
90
91
# File 'lib/kward/prompt_interface/composer_state.rb', line 84

def delete_before_cursor
  return false if @cursor.zero?

  previous = TerminalText.previous_grapheme_boundary(@input, @cursor)
  @input = @input[0...previous] + @input[@cursor..]
  @cursor = previous
  true
end

#delete_word_after_cursorObject

Kills the word after the cursor into kill_buffer.



138
139
140
# File 'lib/kward/prompt_interface/composer_state.rb', line 138

def delete_word_after_cursor
  kill_range(@cursor, TextBoundary.next_word_boundary(@input, @cursor))
end

#delete_word_before_cursorObject

Kills the word before the cursor into kill_buffer.



133
134
135
# File 'lib/kward/prompt_interface/composer_state.rb', line 133

def delete_word_before_cursor
  kill_range(TextBoundary.previous_word_boundary(@input, @cursor), @cursor)
end

#history_search_active?Boolean

Returns:

  • (Boolean)


237
238
239
# File 'lib/kward/prompt_interface/composer_state.rb', line 237

def history_search_active?
  !@history_search_query.nil?
end

#history_search_matchesObject



247
248
249
250
251
252
253
254
255
256
# File 'lib/kward/prompt_interface/composer_state.rb', line 247

def history_search_matches
  query = @history_search_query.to_s.downcase
  return @history_search_matches if @history_search_matches_query == query && @history_search_matches

  pattern = TextMatcher.subsequence_pattern(query)
  @history_search_matches_query = query
  @history_search_matches = @history.reverse.select do |value|
    TextMatcher.subsequence?(value.downcase, query, pattern)
  end
end

#insert_string(string) ⇒ Object

Inserts text at the cursor and advances by the inserted length.



76
77
78
79
80
81
# File 'lib/kward/prompt_interface/composer_state.rb', line 76

def insert_string(string)
  return if string.empty?

  @input = @input[0...@cursor] + string + @input[@cursor..]
  @cursor += string.length
end

#invalidate_history_search_matchesObject



293
294
295
296
# File 'lib/kward/prompt_interface/composer_state.rb', line 293

def invalidate_history_search_matches
  @history_search_matches_query = nil
  @history_search_matches = nil
end

#kill_line_after_cursorObject

Kills all text after the cursor into kill_buffer.



148
149
150
# File 'lib/kward/prompt_interface/composer_state.rb', line 148

def kill_line_after_cursor
  kill_range(@cursor, @input.length)
end

#kill_line_before_cursorObject

Kills all text before the cursor into kill_buffer.



143
144
145
# File 'lib/kward/prompt_interface/composer_state.rb', line 143

def kill_line_before_cursor
  kill_range(0, @cursor)
end

#kill_range(start_index, end_index) ⇒ Object

Removes a range, stores it in kill_buffer, and moves the cursor to the start.



153
154
155
156
157
158
159
160
# File 'lib/kward/prompt_interface/composer_state.rb', line 153

def kill_range(start_index, end_index)
  return false if start_index == end_index

  @kill_buffer = @input[start_index...end_index].to_s
  @input = @input[0...start_index].to_s + @input[end_index..].to_s
  @cursor = start_index
  true
end

#lines_and_cursorObject

Returns [lines, row, column] for cursor-aware multi-line layout.



174
175
176
177
178
179
180
181
# File 'lib/kward/prompt_interface/composer_state.rb', line 174

def lines_and_cursor
  lines = @input.split("\n", -1)
  before_cursor = @input[0...@cursor]
  row = before_cursor.count("\n")
  line_start = before_cursor.rindex("\n")
  column = line_start ? before_cursor.length - line_start - 1 : before_cursor.length
  [lines.empty? ? [""] : lines, row, column]
end

#load_history(values) ⇒ Object

Replaces the in-memory history list with persisted entries.



184
185
186
187
188
189
# File 'lib/kward/prompt_interface/composer_state.rb', line 184

def load_history(values)
  @history = Array(values).map(&:to_s).reject { |value| value.strip.empty? }
  reset_history_navigation
  reset_history_search
  invalidate_history_search_matches
end

#move_cursor_leftObject

Moves the cursor one grapheme left when possible.



103
104
105
# File 'lib/kward/prompt_interface/composer_state.rb', line 103

def move_cursor_left
  @cursor = TerminalText.previous_grapheme_boundary(@input, @cursor) if @cursor.positive?
end

#move_cursor_rightObject

Moves the cursor one grapheme right when possible.



108
109
110
# File 'lib/kward/prompt_interface/composer_state.rb', line 108

def move_cursor_right
  @cursor = TerminalText.next_grapheme_boundary(@input, @cursor) if @cursor < @input.length
end

#move_to_end_of_lineObject

Moves the cursor to the end of the input buffer.



118
119
120
# File 'lib/kward/prompt_interface/composer_state.rb', line 118

def move_to_end_of_line
  @cursor = @input.length
end

#move_to_next_wordObject

Moves the cursor to the next word boundary.



128
129
130
# File 'lib/kward/prompt_interface/composer_state.rb', line 128

def move_to_next_word
  @cursor = TextBoundary.next_word_boundary(@input, @cursor)
end

#move_to_previous_wordObject

Moves the cursor to the previous word boundary.



123
124
125
# File 'lib/kward/prompt_interface/composer_state.rb', line 123

def move_to_previous_word
  @cursor = TextBoundary.previous_word_boundary(@input, @cursor)
end

#move_to_start_of_lineObject

Moves the cursor to the beginning of the input buffer.



113
114
115
# File 'lib/kward/prompt_interface/composer_state.rb', line 113

def move_to_start_of_line
  @cursor = 0
end

#recall_next_historyObject

Replaces input with the next history entry or restores the saved draft.



212
213
214
215
216
217
218
219
220
221
222
# File 'lib/kward/prompt_interface/composer_state.rb', line 212

def recall_next_history
  return if @history_index.nil?

  if @history_index < @history.length - 1
    @history_index += 1
    replace_input(@history[@history_index])
  else
    replace_input(@history_draft || "")
    reset_history_navigation
  end
end

#recall_previous_historyObject

Replaces input with the previous history entry, preserving the draft first.



203
204
205
206
207
208
209
# File 'lib/kward/prompt_interface/composer_state.rb', line 203

def recall_previous_history
  return if @history.empty?

  @history_draft = @input if @history_index.nil?
  @history_index = @history_index.nil? ? @history.length - 1 : [@history_index - 1, 0].max
  replace_input(@history[@history_index])
end

#remove_last_attachmentObject

Removes the most recently added attachment.



68
69
70
71
72
73
# File 'lib/kward/prompt_interface/composer_state.rb', line 68

def remove_last_attachment
  return false if @attachments.empty?

  @attachments.pop
  true
end

#replace_input(value) ⇒ Object

Replaces the full input buffer and places the cursor at the end.



168
169
170
171
# File 'lib/kward/prompt_interface/composer_state.rb', line 168

def replace_input(value)
  @input = value.to_s
  @cursor = @input.length
end

#reset_history_navigationObject

Leaves history navigation and clears the saved draft/index state.



225
226
227
228
# File 'lib/kward/prompt_interface/composer_state.rb', line 225

def reset_history_navigation
  @history_index = nil
  @history_draft = nil
end

#reset_history_searchObject



287
288
289
290
291
# File 'lib/kward/prompt_interface/composer_state.rb', line 287

def reset_history_search
  @history_search_query = nil
  @history_search_draft = nil
  @history_search_index = 0
end

#select_next_history_search_matchObject



269
270
271
272
273
274
# File 'lib/kward/prompt_interface/composer_state.rb', line 269

def select_next_history_search_match
  matches = history_search_matches
  return if matches.empty?

  @history_search_index = [@history_search_index + 1, matches.length - 1].min
end

#select_previous_history_search_matchObject



265
266
267
# File 'lib/kward/prompt_interface/composer_state.rb', line 265

def select_previous_history_search_match
  @history_search_index = [@history_search_index - 1, 0].max
end

#selected_history_search_matchObject



258
259
260
261
262
263
# File 'lib/kward/prompt_interface/composer_state.rb', line 258

def selected_history_search_match
  matches = history_search_matches
  return nil if matches.empty?

  matches[[@history_search_index, matches.length - 1].min]
end

#start_history_searchObject



230
231
232
233
234
235
# File 'lib/kward/prompt_interface/composer_state.rb', line 230

def start_history_search
  @history_search_draft = @input if @history_search_query.nil?
  @history_search_query = @input.to_s
  @history_search_index = 0
  replace_input(@history_search_query)
end

#update_history_search_query(value) ⇒ Object



241
242
243
244
245
# File 'lib/kward/prompt_interface/composer_state.rb', line 241

def update_history_search_query(value)
  @history_search_query = value.to_s
  @history_search_index = 0
  replace_input(@history_search_query)
end

#yank_kill_bufferObject

Inserts the last killed text at the cursor.



163
164
165
# File 'lib/kward/prompt_interface/composer_state.rb', line 163

def yank_kill_buffer
  insert_string(@kill_buffer.to_s) unless @kill_buffer.to_s.empty?
end