Class: Fatty::InputField

Inherits:
Object
  • Object
show all
Includes:
Actionable
Defined in:
lib/fatty/input_field.rb

Overview

The =InputField= class is a thin controller around an InputBuffer that adds:

- A prompt (text shown before the editable buffer)
- Optional command history integration (previous/next)
- A small set of "editor actions" intended to be bound to keys

=InputField= intentionally does *not* perform terminal I/O or rendering.
Higher-level UI components (e.g., Terminal/Screen/widgets) are responsible
for:

- Decoding keys and dispatching actions
- Translating buffer/cursor state into screen coordinates
- Drawing the prompt + buffer text and placing the cursor

Word-motion and word-deletion semantics are delegated to InputBuffer so
the definition of "word" can be configured in one place (via
=InputBuffer='s word_char_re).

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Actionable

included

Constructor Details

#initialize(prompt: ">", buffer: nil, completion_proc: nil, history: nil, history_kind: :command, history_ctx: nil) ⇒ InputField

Returns a new instance of InputField.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/fatty/input_field.rb', line 29

def initialize(
  prompt: ">",
  buffer: nil,
  completion_proc: nil,
  history: nil,
  history_kind: :command,
  history_ctx: nil
)
  @prompt = Prompt.ensure(prompt)
  @history = history
  @history_kind = history_kind
  @history_ctx = history_ctx
  @completion_proc = completion_proc
  @completion_state = nil

  @buffer =
    if buffer
      buffer
    else
      cfg = Fatty::Config.config
      word_char_re = cfg.dig(:word_char_re) || Fatty::InputBuffer::DEFAULT_WORD_CHAR_RE
      Fatty::InputBuffer.new(word_char_re: word_char_re)
    end
end

Instance Attribute Details

#bufferObject (readonly)

Returns the value of attribute buffer.



27
28
29
# File 'lib/fatty/input_field.rb', line 27

def buffer
  @buffer
end

#historyObject (readonly)

Returns the value of attribute history.



27
28
29
# File 'lib/fatty/input_field.rb', line 27

def history
  @history
end

#promptObject

Returns the value of attribute prompt.



27
28
29
# File 'lib/fatty/input_field.rb', line 27

def prompt
  @prompt
end

Instance Method Details

#act_on(action, *args, env: nil, **kwargs) ⇒ Object



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/fatty/input_field.rb', line 211

def act_on(action, *args, env: nil, **kwargs)
  return unless action

  reset_history_cursor_for(action)

  if Fatty::Actions.registered?(action)
    if env
      Fatty::Actions.call(action, env, *args, **kwargs)
    else
      defn = Fatty::Actions.lookup(action)
      target =
        case defn[:on]
        when :buffer then buffer
        else
          raise Fatty::ActionError, "Cannot dispatch #{action} without env for target #{defn[:on].inspect}"
        end
      target.public_send(defn[:method], *args, **kwargs)
    end
  else
    raise Fatty::ActionError, "Unknown action: #{action}"
  end
end

#autosuggestionObject



116
117
118
119
120
121
122
123
124
# File 'lib/fatty/input_field.rb', line 116

def autosuggestion
  active = active_completion_autosuggestion
  return active if active

  text = buffer.text.to_s
  return if text.empty?

  autosuggestion_candidates.first
end

#complete!(direction: 1) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/fatty/input_field.rb', line 126

def complete!(direction: 1)
  candidates = tab_completion_candidates
  common_prefix = common_completion_prefix(candidates)

  if candidates.length == 1
    accept_completion_line(candidates.first)
  elsif direction.positive? &&
        common_prefix &&
        common_prefix.length > buffer.text.length
    accept_completion_line(common_prefix)
  else
    cycle_completion!(direction: direction)
  end
end

#cursor_xObject

Visual cursor X position in the window



239
240
241
242
# File 'lib/fatty/input_field.rb', line 239

def cursor_x
  before_cursor = buffer.text.to_s[0...buffer.cursor].to_s
  prompt_width + Fatty::Ansi.visible_length(before_cursor)
end

#cycle_completion!(direction: 1) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/fatty/input_field.rb', line 141

def cycle_completion!(direction: 1)
  text = buffer.text.to_s
  candidates = tab_completion_candidates
  result = nil

  if candidates.empty?
    reset_completion_state!
  elsif same_completion_state?(base: text, candidates: candidates)
    @completion_state[:index] =
      (@completion_state[:index] + direction) % @completion_state[:candidates].length
    result = @completion_state[:candidates][@completion_state[:index]]
  else
    @completion_state = {
      base: text,
      candidates: candidates,
      index: 0,
    }
    result = @completion_state[:candidates].first
  end

  sync_virtual_suffix!
  result
end

#empty?Boolean

:category: Queries

Returns:

  • (Boolean)


63
64
65
# File 'lib/fatty/input_field.rb', line 63

def empty?
  buffer.text == ""
end

#path_completion_candidatesObject



174
175
176
177
178
179
# File 'lib/fatty/input_field.rb', line 174

def path_completion_candidates
  prefix = path_completion_prefix
  return [] if prefix.nil? || prefix.empty?

  rendered_path_candidates(prefix)
end

#path_completion_candidates_for(prefix) ⇒ Object



181
182
183
184
185
# File 'lib/fatty/input_field.rb', line 181

def path_completion_candidates_for(prefix)
  return [] if prefix.nil? || prefix.empty?

  rendered_path_candidates(prefix)
end


165
166
167
168
169
170
171
172
# File 'lib/fatty/input_field.rb', line 165

def popup_completion_candidates
  path_prefix = popup_path_completion_prefix
  if path_prefix
    path = rendered_path_candidates(path_prefix)
    return path if path.any?
  end
  autosuggestion_candidates(base: buffer.text.to_s[0...buffer.cursor])
end

Returns:

  • (Boolean)


199
200
201
# File 'lib/fatty/input_field.rb', line 199

def popup_completion_path?
  !!popup_path_completion_prefix
end


195
196
197
# File 'lib/fatty/input_field.rb', line 195

def popup_completion_query
  path_completion_prefix || buffer.completion_prefix
end


187
188
189
190
191
192
193
# File 'lib/fatty/input_field.rb', line 187

def popup_completion_range
  if popup_path_completion_prefix
    path_completion_range
  else
    0...buffer.text.length
  end
end

#prompt_textObject



234
235
236
# File 'lib/fatty/input_field.rb', line 234

def prompt_text
  prompt.text
end

#reset_completion_state!Object



203
204
205
# File 'lib/fatty/input_field.rb', line 203

def reset_completion_state!
  @completion_state = nil
end

#stateObject



67
68
69
70
71
72
73
74
75
76
# File 'lib/fatty/input_field.rb', line 67

def state
  [
    prompt_text.to_s.dup.freeze,
    buffer.text.to_s.dup.freeze,
    buffer.cursor,
    buffer.virtual_suffix.to_s.dup.freeze,
    buffer.region_active?,
    buffer.region_range,
  ]
end

#sync_virtual_suffix!Object



207
208
209
# File 'lib/fatty/input_field.rb', line 207

def sync_virtual_suffix!
  buffer.virtual_suffix = autosuggestion_suffix
end

#to_sObject Also known as: inspect

:category: Inspect



56
57
58
# File 'lib/fatty/input_field.rb', line 56

def to_s
  "<InputField:#{object_id}> Prompt => #{prompt} Buffer => #{buffer}"
end