kward 0.72.0 → 0.73.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +30 -0
- data/CHANGELOG.md +53 -0
- data/Gemfile.lock +2 -2
- data/doc/configuration.md +1 -1
- data/doc/editor.md +23 -2
- data/doc/git.md +1 -0
- data/doc/rpc.md +2 -2
- data/doc/shell.md +56 -10
- data/doc/usage.md +27 -1
- data/lib/kward/ansi.rb +62 -23
- data/lib/kward/cli/plugins.rb +1 -1
- data/lib/kward/cli/rendering.rb +4 -1
- data/lib/kward/cli/runtime_helpers.rb +141 -7
- data/lib/kward/cli/settings.rb +0 -1
- data/lib/kward/cli/slash_commands.rb +213 -0
- data/lib/kward/cli/tabs.rb +34 -4
- data/lib/kward/cli/tool_summaries.rb +6 -0
- data/lib/kward/cli.rb +4 -12
- data/lib/kward/clipboard.rb +2 -3
- data/lib/kward/compactor.rb +7 -19
- data/lib/kward/config_files.rb +26 -4
- data/lib/kward/ekwsh.rb +239 -42
- data/lib/kward/image_attachments.rb +3 -1
- data/lib/kward/interactive_pty_runner.rb +151 -0
- data/lib/kward/local_command_runner.rb +155 -0
- data/lib/kward/local_pty_command_runner.rb +171 -0
- data/lib/kward/model/context_usage.rb +2 -2
- data/lib/kward/model/payloads.rb +2 -5
- data/lib/kward/prompt_history.rb +5 -3
- data/lib/kward/prompt_interface/editor/auto_indent.rb +5 -4
- data/lib/kward/prompt_interface/editor/controller.rb +262 -62
- data/lib/kward/prompt_interface/editor/modes/emacs.rb +21 -21
- data/lib/kward/prompt_interface/editor/modes/modern.rb +38 -37
- data/lib/kward/prompt_interface/editor/modes/vibe.rb +23 -173
- data/lib/kward/prompt_interface/editor/modes/vibe_insert_readline.rb +166 -0
- data/lib/kward/prompt_interface/editor/renderer.rb +6 -5
- data/lib/kward/prompt_interface/editor/state.rb +28 -6
- data/lib/kward/prompt_interface/editor/syntax_highlighter.rb +5 -3
- data/lib/kward/prompt_interface/git_prompt.rb +12 -23
- data/lib/kward/prompt_interface/interactive/controller.rb +1 -1
- data/lib/kward/prompt_interface/key_handler.rb +93 -51
- data/lib/kward/prompt_interface/question_prompt.rb +1 -6
- data/lib/kward/prompt_interface/screen.rb +3 -3
- data/lib/kward/prompt_interface/selection_prompt.rb +3 -6
- data/lib/kward/prompt_interface/slash_overlay.rb +2 -0
- data/lib/kward/prompt_interface.rb +87 -221
- data/lib/kward/prompts/commands.rb +4 -0
- data/lib/kward/rpc/memory_methods.rb +83 -0
- data/lib/kward/rpc/server.rb +130 -83
- data/lib/kward/rpc/session_manager.rb +10 -74
- data/lib/kward/rpc/tool_metadata.rb +11 -0
- data/lib/kward/rpc/transcript_normalizer.rb +4 -39
- data/lib/kward/scratchpad_runner.rb +56 -0
- data/lib/kward/session_diff.rb +20 -3
- data/lib/kward/session_naming.rb +11 -0
- data/lib/kward/terminal_keys.rb +84 -0
- data/lib/kward/terminal_sequences.rb +42 -0
- data/lib/kward/tools/context_for_task.rb +2 -0
- data/lib/kward/version.rb +1 -1
- data/lib/kward/workers/git_guard.rb +25 -0
- data/lib/kward/workers/job.rb +99 -0
- data/lib/kward/workers/queue_runner.rb +166 -0
- data/lib/kward/workers/queue_store.rb +112 -0
- data/lib/kward/workers.rb +3 -0
- data/lib/kward/workspace.rb +15 -63
- data/templates/default/fulldoc/html/css/kward.css +33 -0
- data/templates/default/fulldoc/html/images/kward_screen_1.png +0 -0
- data/templates/default/fulldoc/html/setup.rb +1 -0
- data/templates/default/layout/html/layout.erb +19 -32
- metadata +15 -1
|
@@ -16,16 +16,19 @@ module Kward
|
|
|
16
16
|
class PromptInterface
|
|
17
17
|
# Mutable state for the built-in composer file editor.
|
|
18
18
|
class EditorState
|
|
19
|
-
attr_reader :path, :original_content, :original_digest, :original_mtime, :original_size
|
|
19
|
+
attr_reader :path, :display_path, :language, :original_content, :original_digest, :original_mtime, :original_size
|
|
20
20
|
attr_reader :buffer, :undo_stack, :redo_stack, :kill_buffer, :kill_ring, :last_yank_range, :last_yank_index
|
|
21
21
|
attr_accessor :viewport_row, :viewport_column, :status, :overwrite_confirmed, :quit_confirmed, :search_active, :search_query, :search_direction, :new_file, :editor_mode, :emacs_pending, :readonly, :diff_view
|
|
22
22
|
|
|
23
|
-
def initialize(path:, content:, new_file: false, editor_mode: "modern", readonly: false, diff_view: false)
|
|
24
|
-
@path = path.to_s
|
|
23
|
+
def initialize(path:, content:, new_file: false, editor_mode: "modern", readonly: false, diff_view: false, virtual: false, display_path: nil, language: nil)
|
|
24
|
+
@path = virtual ? nil : path.to_s
|
|
25
|
+
@display_path = display_path.to_s.empty? ? path.to_s : display_path.to_s
|
|
26
|
+
@language = language&.to_sym
|
|
25
27
|
@new_file = new_file
|
|
26
28
|
@readonly = readonly
|
|
27
29
|
@diff_view = diff_view
|
|
28
|
-
@
|
|
30
|
+
@virtual = virtual == true
|
|
31
|
+
@file_marker = EditorFileMarker.new(path: @path || @display_path, content: content, new_file: new_file || virtual?)
|
|
29
32
|
@original_content = @file_marker.content
|
|
30
33
|
@original_digest = @file_marker.digest
|
|
31
34
|
@original_mtime = @file_marker.mtime
|
|
@@ -61,9 +64,12 @@ module Kward
|
|
|
61
64
|
|
|
62
65
|
def initialize_copy(other)
|
|
63
66
|
super
|
|
64
|
-
@path = other.path
|
|
67
|
+
@path = other.path&.dup
|
|
68
|
+
@display_path = other.display_path.dup
|
|
69
|
+
@language = other.language
|
|
70
|
+
@virtual = other.virtual?
|
|
65
71
|
@original_content = other.original_content.dup
|
|
66
|
-
@file_marker = EditorFileMarker.new(path: @path, content: @original_content, new_file: other.new_file)
|
|
72
|
+
@file_marker = EditorFileMarker.new(path: @path || @display_path, content: @original_content, new_file: other.new_file || @virtual)
|
|
67
73
|
@original_digest = other.original_digest.dup
|
|
68
74
|
@original_mtime = other.original_mtime
|
|
69
75
|
@original_size = other.original_size
|
|
@@ -306,6 +312,18 @@ module Kward
|
|
|
306
312
|
@readonly == true
|
|
307
313
|
end
|
|
308
314
|
|
|
315
|
+
def virtual?
|
|
316
|
+
@virtual == true
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def bind_path(path)
|
|
320
|
+
@path = path.to_s
|
|
321
|
+
@display_path = @path
|
|
322
|
+
@virtual = false
|
|
323
|
+
@new_file = !File.exist?(@path)
|
|
324
|
+
@file_marker = EditorFileMarker.new(path: @path, content: @original_content, new_file: true)
|
|
325
|
+
end
|
|
326
|
+
|
|
309
327
|
def diff_view?
|
|
310
328
|
@diff_view == true
|
|
311
329
|
end
|
|
@@ -938,6 +956,8 @@ module Kward
|
|
|
938
956
|
|
|
939
957
|
def refresh_after_save(content)
|
|
940
958
|
@new_file = false
|
|
959
|
+
@virtual = false
|
|
960
|
+
@display_path = @path.to_s
|
|
941
961
|
@file_marker.refresh(content)
|
|
942
962
|
@original_content = @file_marker.content
|
|
943
963
|
@original_digest = @file_marker.digest
|
|
@@ -949,6 +969,8 @@ module Kward
|
|
|
949
969
|
end
|
|
950
970
|
|
|
951
971
|
def file_changed_on_disk?
|
|
972
|
+
return false if virtual?
|
|
973
|
+
|
|
952
974
|
@file_marker.changed_on_disk?(new_file: new_file)
|
|
953
975
|
end
|
|
954
976
|
|
|
@@ -134,11 +134,13 @@ module Kward
|
|
|
134
134
|
|
|
135
135
|
def editor_syntax_language
|
|
136
136
|
return nil unless @editor_state
|
|
137
|
+
return @editor_state.language if @editor_state.language
|
|
137
138
|
|
|
139
|
+
path = @editor_state.path || @editor_state.display_path
|
|
138
140
|
@editor_syntax_language_path ||= nil
|
|
139
|
-
if @editor_syntax_language_path !=
|
|
140
|
-
@editor_syntax_language_path =
|
|
141
|
-
@editor_syntax_language = editor_detect_syntax_language(
|
|
141
|
+
if @editor_syntax_language_path != path
|
|
142
|
+
@editor_syntax_language_path = path
|
|
143
|
+
@editor_syntax_language = editor_detect_syntax_language(path)
|
|
142
144
|
end
|
|
143
145
|
@editor_syntax_language
|
|
144
146
|
end
|
|
@@ -45,6 +45,14 @@ module Kward
|
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
def open_modal_diff_viewer(path, content)
|
|
49
|
+
@mutex.synchronize do
|
|
50
|
+
open_diff_viewer(path.to_s, content.to_s)
|
|
51
|
+
render_prompt_locked
|
|
52
|
+
end
|
|
53
|
+
read_editor_until_closed
|
|
54
|
+
end
|
|
55
|
+
|
|
48
56
|
private
|
|
49
57
|
|
|
50
58
|
def handle_git_key(key)
|
|
@@ -116,12 +124,9 @@ module Kward
|
|
|
116
124
|
end
|
|
117
125
|
|
|
118
126
|
def handle_git_bracketed_paste_key(key)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
insert_string(normalize_paste(paste[:content])) if git_composing?
|
|
123
|
-
queue_pending_keys(paste[:remaining]) if paste[:remaining] && !paste[:remaining].empty?
|
|
124
|
-
true
|
|
127
|
+
handle_bracketed_paste(key) do |content|
|
|
128
|
+
insert_string(content) if git_composing?
|
|
129
|
+
end
|
|
125
130
|
end
|
|
126
131
|
|
|
127
132
|
def handle_git_named_key(key_name)
|
|
@@ -149,18 +154,6 @@ module Kward
|
|
|
149
154
|
end
|
|
150
155
|
end
|
|
151
156
|
|
|
152
|
-
def handle_git_escape_sequence
|
|
153
|
-
pending_sequence = read_pending_escape_sequence
|
|
154
|
-
return SELECT_CANCEL if pending_sequence.empty?
|
|
155
|
-
|
|
156
|
-
full_sequence = "\e#{pending_sequence}"
|
|
157
|
-
sequence = next_key_token(full_sequence)
|
|
158
|
-
queue_pending_keys(full_sequence[sequence.length..]) if full_sequence.length > sequence.length
|
|
159
|
-
return SELECT_CANCEL if sequence == "\e"
|
|
160
|
-
|
|
161
|
-
handle_git_named_key(key_name_for(sequence))
|
|
162
|
-
end
|
|
163
|
-
|
|
164
157
|
def git_state_for(status_lines, selected_index: 0)
|
|
165
158
|
lines = Array(status_lines).map(&:to_s)
|
|
166
159
|
selected_index = [[selected_index.to_i, 0].max, [lines.length - 1, 0].max].min
|
|
@@ -204,11 +197,7 @@ module Kward
|
|
|
204
197
|
def open_git_diff_viewer(diff)
|
|
205
198
|
return unless diff.respond_to?(:[])
|
|
206
199
|
|
|
207
|
-
|
|
208
|
-
open_diff_viewer(diff[:path].to_s, diff[:content].to_s)
|
|
209
|
-
render_prompt_locked
|
|
210
|
-
end
|
|
211
|
-
read_editor_until_closed
|
|
200
|
+
open_modal_diff_viewer(diff[:path], diff[:content])
|
|
212
201
|
end
|
|
213
202
|
|
|
214
203
|
def read_editor_until_closed
|
|
@@ -50,7 +50,7 @@ module Kward
|
|
|
50
50
|
# @param row [Integer] zero-based row
|
|
51
51
|
# @param col [Integer] zero-based column
|
|
52
52
|
# @param char [String] single character to display
|
|
53
|
-
# @param
|
|
53
|
+
# @param colors [Array<Symbol, String>] ANSI style names or raw SGR codes
|
|
54
54
|
# @return [void]
|
|
55
55
|
def put(row, col, char, *colors)
|
|
56
56
|
row = row.to_i
|
|
@@ -14,7 +14,7 @@ module Kward
|
|
|
14
14
|
|
|
15
15
|
@reader.read_keypress(echo: false, raw: true, nonblock: nonblock)
|
|
16
16
|
rescue TTY::Reader::InputInterrupt
|
|
17
|
-
|
|
17
|
+
TerminalKeys::CTRL_C
|
|
18
18
|
rescue IO::WaitReadable, Errno::EAGAIN, Errno::EWOULDBLOCK
|
|
19
19
|
nil
|
|
20
20
|
end
|
|
@@ -116,11 +116,11 @@ module Kward
|
|
|
116
116
|
handle_tab_completion_key
|
|
117
117
|
when "\b", "\x7F"
|
|
118
118
|
delete_before_cursor
|
|
119
|
-
when
|
|
119
|
+
when TerminalKeys::CTRL_D
|
|
120
120
|
delete_at_cursor_or_exit
|
|
121
|
-
when
|
|
121
|
+
when TerminalKeys::CTRL_C
|
|
122
122
|
cancel_input_or_interrupt
|
|
123
|
-
when
|
|
123
|
+
when TerminalKeys::CTRL_R
|
|
124
124
|
start_history_search
|
|
125
125
|
when "\e"
|
|
126
126
|
handle_escape_sequence
|
|
@@ -152,7 +152,7 @@ module Kward
|
|
|
152
152
|
accept_history_search
|
|
153
153
|
when "\b", "\x7F"
|
|
154
154
|
update_history_search_query(composer_input[0...-1].to_s)
|
|
155
|
-
when
|
|
155
|
+
when TerminalKeys::CTRL_C, "\e"
|
|
156
156
|
cancel_history_search
|
|
157
157
|
else
|
|
158
158
|
append_history_search_key(key)
|
|
@@ -193,7 +193,7 @@ module Kward
|
|
|
193
193
|
def cancel_input_or_interrupt
|
|
194
194
|
return CANCEL_INPUT if @busy
|
|
195
195
|
|
|
196
|
-
|
|
196
|
+
true
|
|
197
197
|
end
|
|
198
198
|
|
|
199
199
|
def handle_tab_completion_key
|
|
@@ -253,21 +253,41 @@ module Kward
|
|
|
253
253
|
end
|
|
254
254
|
|
|
255
255
|
def handle_bracketed_paste_key(key)
|
|
256
|
+
handle_bracketed_paste(key) { |content| insert_paste(content) }
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def handle_mouse_reporting_key(key)
|
|
260
|
+
event = parse_sgr_mouse_event(key)
|
|
261
|
+
return false unless event
|
|
262
|
+
|
|
263
|
+
queue_pending_keys(event[:remaining]) unless event[:remaining].empty?
|
|
264
|
+
true
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def handle_bracketed_paste(key)
|
|
256
268
|
paste = read_bracketed_paste(key)
|
|
257
269
|
return false unless paste
|
|
258
270
|
|
|
259
|
-
|
|
271
|
+
yield normalize_paste(paste[:content])
|
|
260
272
|
queue_pending_keys(paste[:remaining]) if paste[:remaining] && !paste[:remaining].empty?
|
|
261
273
|
true
|
|
262
274
|
end
|
|
263
275
|
|
|
264
|
-
def
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return false unless match
|
|
276
|
+
def parse_sgr_mouse_event(key)
|
|
277
|
+
match = key.to_s.match(/\A(?:\e)?\[<(\d+);(\d+);(\d+)([Mm])/)
|
|
278
|
+
return nil unless match
|
|
268
279
|
|
|
269
|
-
|
|
270
|
-
|
|
280
|
+
code = match[1].to_i
|
|
281
|
+
{
|
|
282
|
+
code: code,
|
|
283
|
+
button: code & 3,
|
|
284
|
+
column: match[2].to_i,
|
|
285
|
+
row: match[3].to_i,
|
|
286
|
+
action: match[4],
|
|
287
|
+
release: match[4] == "m",
|
|
288
|
+
drag: (code & 32).positive?,
|
|
289
|
+
remaining: key.to_s[match[0].length..].to_s
|
|
290
|
+
}
|
|
271
291
|
end
|
|
272
292
|
|
|
273
293
|
def read_bracketed_paste(key)
|
|
@@ -329,7 +349,7 @@ module Kward
|
|
|
329
349
|
end
|
|
330
350
|
|
|
331
351
|
def parse_csi_u_key(key)
|
|
332
|
-
match = key.to_s.match(
|
|
352
|
+
match = key.to_s.match(TerminalKeys::CSI_U_PATTERN)
|
|
333
353
|
return nil unless match
|
|
334
354
|
|
|
335
355
|
fields = match[2].to_s.split(";", -1)[1..] || []
|
|
@@ -345,12 +365,34 @@ module Kward
|
|
|
345
365
|
}
|
|
346
366
|
end
|
|
347
367
|
|
|
368
|
+
def csi_u_key_event(sequence)
|
|
369
|
+
code = sequence[:code]
|
|
370
|
+
case code
|
|
371
|
+
when 9
|
|
372
|
+
{ type: :tab, modifier: sequence[:modifier] }
|
|
373
|
+
when 13
|
|
374
|
+
{ type: :enter, modifier: sequence[:modifier] }
|
|
375
|
+
when 27
|
|
376
|
+
{ type: :escape, modifier: sequence[:modifier] }
|
|
377
|
+
when 8, 127
|
|
378
|
+
{ type: :backspace, modifier: sequence[:modifier] }
|
|
379
|
+
when 4
|
|
380
|
+
{ type: :delete, modifier: sequence[:modifier] }
|
|
381
|
+
else
|
|
382
|
+
text = csi_u_printable_text(sequence)
|
|
383
|
+
return { type: :printable, text: text, modifier: sequence[:modifier] } if text
|
|
384
|
+
return { type: :text_field, modifier: sequence[:modifier] } if csi_u_text_field?(sequence)
|
|
385
|
+
|
|
386
|
+
{ type: :modified, code: code, modifier: sequence[:modifier] }
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
348
390
|
def insert_csi_u_text(sequence)
|
|
349
|
-
|
|
350
|
-
return true if
|
|
351
|
-
return false unless
|
|
391
|
+
event = csi_u_key_event(sequence)
|
|
392
|
+
return true if event[:type] == :text_field
|
|
393
|
+
return false unless event[:type] == :printable
|
|
352
394
|
|
|
353
|
-
insert_string(text)
|
|
395
|
+
insert_string(event[:text])
|
|
354
396
|
end
|
|
355
397
|
|
|
356
398
|
def csi_u_text_field?(sequence)
|
|
@@ -444,17 +486,17 @@ module Kward
|
|
|
444
486
|
def cursor_key_name(key)
|
|
445
487
|
text = key.to_s
|
|
446
488
|
case text
|
|
447
|
-
when
|
|
489
|
+
when TerminalKeys::UP_PATTERN, *TerminalKeys::UP
|
|
448
490
|
:up
|
|
449
|
-
when
|
|
491
|
+
when TerminalKeys::DOWN_PATTERN, *TerminalKeys::DOWN
|
|
450
492
|
:down
|
|
451
|
-
when
|
|
493
|
+
when TerminalKeys::RIGHT_PATTERN, *TerminalKeys::RIGHT
|
|
452
494
|
:right
|
|
453
|
-
when
|
|
495
|
+
when TerminalKeys::LEFT_PATTERN, *TerminalKeys::LEFT
|
|
454
496
|
:left
|
|
455
|
-
when
|
|
497
|
+
when *TerminalKeys::PAGE_UP
|
|
456
498
|
:pageup
|
|
457
|
-
when
|
|
499
|
+
when *TerminalKeys::PAGE_DOWN
|
|
458
500
|
:pagedown
|
|
459
501
|
end
|
|
460
502
|
end
|
|
@@ -506,8 +548,8 @@ module Kward
|
|
|
506
548
|
|
|
507
549
|
def next_key_token(keys)
|
|
508
550
|
text = keys.to_s
|
|
509
|
-
text.match(
|
|
510
|
-
text.match(
|
|
551
|
+
text.match(TerminalKeys::CSI_KEY_PATTERN)&.[](0) ||
|
|
552
|
+
text.match(TerminalKeys::SS3_KEY_PATTERN)&.[](0) ||
|
|
511
553
|
shift_enter_sequence_for(text) ||
|
|
512
554
|
(text.start_with?("\e") && text.length > 1 && alt_key_sequence?(text[1]) ? text[0, 2] : text[0, 1])
|
|
513
555
|
end
|
|
@@ -536,9 +578,9 @@ module Kward
|
|
|
536
578
|
sequence
|
|
537
579
|
end
|
|
538
580
|
|
|
539
|
-
CTRL_TAB_SEQUENCES =
|
|
540
|
-
CTRL_SHIFT_TAB_SEQUENCES =
|
|
541
|
-
SHIFT_TAB_SEQUENCES =
|
|
581
|
+
CTRL_TAB_SEQUENCES = TerminalKeys::CTRL_TAB
|
|
582
|
+
CTRL_SHIFT_TAB_SEQUENCES = TerminalKeys::CTRL_SHIFT_TAB
|
|
583
|
+
SHIFT_TAB_SEQUENCES = TerminalKeys::SHIFT_TAB
|
|
542
584
|
|
|
543
585
|
def handle_completion_provider_key(key)
|
|
544
586
|
return false unless key == "\t" && @completion_provider
|
|
@@ -617,9 +659,9 @@ module Kward
|
|
|
617
659
|
|
|
618
660
|
def handle_ctrl_tab_key_binding(key)
|
|
619
661
|
case key
|
|
620
|
-
when
|
|
662
|
+
when TerminalKeys::CTRL_T, TerminalKeys::CTRL_T_CSI_U
|
|
621
663
|
{ tab_action: :new }
|
|
622
|
-
when
|
|
664
|
+
when TerminalKeys::CTRL_W_CSI_U
|
|
623
665
|
{ tab_action: :close }
|
|
624
666
|
else
|
|
625
667
|
ctrl_number_tab_action(key)
|
|
@@ -627,7 +669,7 @@ module Kward
|
|
|
627
669
|
end
|
|
628
670
|
|
|
629
671
|
def ctrl_number_tab_action(key)
|
|
630
|
-
match = key.to_s.match(
|
|
672
|
+
match = key.to_s.match(TerminalKeys::CTRL_NUMBER_TAB_PATTERN)
|
|
631
673
|
return false unless match
|
|
632
674
|
|
|
633
675
|
{ tab_action: :select, index: match[1].to_i - 49 }
|
|
@@ -637,9 +679,9 @@ module Kward
|
|
|
637
679
|
case key
|
|
638
680
|
when "\et", "\eT"
|
|
639
681
|
{ tab_action: :new }
|
|
640
|
-
when
|
|
682
|
+
when *TerminalKeys::ALT_RIGHT
|
|
641
683
|
{ tab_action: :next }
|
|
642
|
-
when
|
|
684
|
+
when *TerminalKeys::ALT_LEFT
|
|
643
685
|
{ tab_action: :previous }
|
|
644
686
|
else
|
|
645
687
|
alt_number_tab_action(key)
|
|
@@ -655,35 +697,35 @@ module Kward
|
|
|
655
697
|
|
|
656
698
|
def handle_composer_key_binding(key)
|
|
657
699
|
case key
|
|
658
|
-
when
|
|
700
|
+
when TerminalKeys::CTRL_A
|
|
659
701
|
move_to_start_of_line
|
|
660
|
-
when
|
|
702
|
+
when TerminalKeys::CTRL_B
|
|
661
703
|
move_cursor_left
|
|
662
|
-
when
|
|
704
|
+
when TerminalKeys::CTRL_D
|
|
663
705
|
delete_at_cursor_or_exit
|
|
664
|
-
when
|
|
706
|
+
when TerminalKeys::CTRL_E
|
|
665
707
|
move_to_end_of_line
|
|
666
|
-
when
|
|
708
|
+
when TerminalKeys::CTRL_F
|
|
667
709
|
move_cursor_right
|
|
668
|
-
when
|
|
710
|
+
when TerminalKeys::CTRL_K
|
|
669
711
|
kill_line_after_cursor
|
|
670
|
-
when
|
|
712
|
+
when TerminalKeys::CTRL_L
|
|
671
713
|
redraw_screen_locked
|
|
672
|
-
when
|
|
714
|
+
when TerminalKeys::CTRL_U
|
|
673
715
|
kill_line_before_cursor
|
|
674
|
-
when
|
|
716
|
+
when TerminalKeys::CTRL_W
|
|
675
717
|
delete_word_before_cursor
|
|
676
|
-
when
|
|
718
|
+
when TerminalKeys::CTRL_Y
|
|
677
719
|
yank_kill_buffer
|
|
678
|
-
when
|
|
720
|
+
when *TerminalKeys::LEFT
|
|
679
721
|
move_cursor_left
|
|
680
|
-
when
|
|
722
|
+
when *TerminalKeys::RIGHT
|
|
681
723
|
move_cursor_right
|
|
682
|
-
when
|
|
724
|
+
when *TerminalKeys::HOME
|
|
683
725
|
move_to_start_of_line
|
|
684
|
-
when
|
|
726
|
+
when *TerminalKeys::END_KEY
|
|
685
727
|
move_to_end_of_line
|
|
686
|
-
when
|
|
728
|
+
when *TerminalKeys::DELETE
|
|
687
729
|
delete_at_cursor
|
|
688
730
|
when "\eb", "\eB"
|
|
689
731
|
move_to_previous_word
|
|
@@ -699,9 +741,9 @@ module Kward
|
|
|
699
741
|
end
|
|
700
742
|
|
|
701
743
|
def parse_modified_ansi_key(key)
|
|
702
|
-
if (match = key.to_s.match(
|
|
744
|
+
if (match = key.to_s.match(TerminalKeys::MODIFIED_CURSOR_PATTERN))
|
|
703
745
|
{ type: :cursor, modifier: match[2].to_i, final: match[3] }
|
|
704
|
-
elsif (match = key.to_s.match(
|
|
746
|
+
elsif (match = key.to_s.match(TerminalKeys::MODIFIED_DELETE_PATTERN))
|
|
705
747
|
{ type: :delete, modifier: match[1].to_i }
|
|
706
748
|
end
|
|
707
749
|
end
|
|
@@ -195,12 +195,7 @@ module Kward
|
|
|
195
195
|
end
|
|
196
196
|
|
|
197
197
|
def handle_question_bracketed_paste_key(key)
|
|
198
|
-
|
|
199
|
-
return false unless paste
|
|
200
|
-
|
|
201
|
-
question_insert_string(normalize_paste(paste[:content]))
|
|
202
|
-
queue_pending_keys(paste[:remaining]) if paste[:remaining] && !paste[:remaining].empty?
|
|
203
|
-
true
|
|
198
|
+
handle_bracketed_paste(key) { |content| question_insert_string(content) }
|
|
204
199
|
end
|
|
205
200
|
|
|
206
201
|
def current_question_answer
|
|
@@ -95,7 +95,7 @@ module Kward
|
|
|
95
95
|
old_top = [height - old_reserved_rows + 1, 1].max
|
|
96
96
|
@reserved_rows = new_reserved_rows
|
|
97
97
|
new_top = composer_top_row(height)
|
|
98
|
-
@output_io.print(
|
|
98
|
+
@output_io.print(TerminalSequences.scroll_region(1, transcript_bottom_row(height)))
|
|
99
99
|
clear_screen_rows_locked(old_top, new_top - 1) if new_top > old_top
|
|
100
100
|
@last_composer_rows = []
|
|
101
101
|
redraw_transcript_locked(width: width, height: height) if redraw_transcript && new_reserved_rows < old_reserved_rows
|
|
@@ -119,7 +119,7 @@ module Kward
|
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
def restore_scroll_region_locked
|
|
122
|
-
@output_io.print(
|
|
122
|
+
@output_io.print(TerminalSequences.restore_scroll_region)
|
|
123
123
|
@reserved_rows = 0
|
|
124
124
|
end
|
|
125
125
|
|
|
@@ -188,7 +188,7 @@ module Kward
|
|
|
188
188
|
end
|
|
189
189
|
|
|
190
190
|
def move_to_screen(row, col)
|
|
191
|
-
@output_io.print(
|
|
191
|
+
@output_io.print(TerminalSequences.move_to(row, col))
|
|
192
192
|
end
|
|
193
193
|
|
|
194
194
|
def screen_size
|
|
@@ -147,12 +147,9 @@ module Kward
|
|
|
147
147
|
end
|
|
148
148
|
|
|
149
149
|
def handle_select_bracketed_paste_key(key)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
select_insert_string(normalize_paste(paste[:content])) if select_editing_active?
|
|
154
|
-
queue_pending_keys(paste[:remaining]) if paste[:remaining] && !paste[:remaining].empty?
|
|
155
|
-
true
|
|
150
|
+
handle_bracketed_paste(key) do |content|
|
|
151
|
+
select_insert_string(content) if select_editing_active?
|
|
152
|
+
end
|
|
156
153
|
end
|
|
157
154
|
|
|
158
155
|
def select_current_choice
|