irb-autosuggestions 0.2.1 → 0.2.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9d8279c02d62499ce7d58d2b8c4c47c7f8289fa4e601b140106219ea4d0d472
4
- data.tar.gz: f35c372e76961a2b91fc65bdf1fe94e6ec6daa7fec810461b49eb8dd11c34379
3
+ metadata.gz: df8665e8ded51b93cb8c175133f17a82bdf369ffe8dfc191740d65958821ab9c
4
+ data.tar.gz: 8c37639ad88f22439a243b5f59e4794feb1edd744dcfaf0caa65c59e662eefb7
5
5
  SHA512:
6
- metadata.gz: 838a7272d857247def598a2989cd8c43eb35b8272183ce3623c25c53faa8e3bb2c375ed3b0006ec9942c2a34537e8fdc644961ab61477dfd86118fbc9f4ccf33
7
- data.tar.gz: 85643f9c47045b2accbceb5a0f84b4eb13ae988b39550a541a21e320fec2f81d75e56128a73a59a9ab5061f0ccce1a2c8e984fcebb8b5a083b2927fcd0c6171f
6
+ metadata.gz: eefd9feddd378c75bc6052223b9873460dcc4cf52dc07762eab5c7541bf9bd34d9efb37ed9ceedb74d4dc5acac7ee734cf547a52dd659021e3df00139d97da0c
7
+ data.tar.gz: 1ff38d6e5db43910299127bd891780b6e9a82c781af7d9febd7627eda9acfecae578fcbe1dc655fa25ffb006a602d1051c59fe42b4eeb666689e8ca8eea890da
data/CHANGELOG.md ADDED
@@ -0,0 +1,71 @@
1
+ # Changelog
2
+
3
+ ## [0.2.2] - 2026-07-08
4
+
5
+ ### Added
6
+
7
+ - Alternative key bindings to accept ghost suggestion: Tab, Ctrl+F, Ctrl+E (configurable via
8
+ `IRB.conf[:AUTOSUGGESTION_ACCEPT_KEYS]`) ([#15], [#157])
9
+ - Configurable ghost color/style via `IRB.conf[:AUTOSUGGESTION_GHOST_COLOR]` (ANSI string) and
10
+ `IRB.conf[:AUTOSUGGESTION_GHOST_STYLE]` (declarative hash) ([#16], [#163])
11
+ - GitHub issue templates (bug report, feature request) and pull request template
12
+
13
+ ### Fixed
14
+
15
+ - Ghost text artifacts and cursor misalignment after accepting multiline (10+ lines) suggestions ([#19], [#256])
16
+ - Auto-execute guard — verified Enter always fires exactly once regardless of ghost visibility ([#18], [#159])
17
+
18
+ ## [0.2.1] - 2026-07-05
19
+
20
+ ### Fixed
21
+
22
+ - Ruby 4.0 compatibility: arrow (`ed_next_char`) not erasing after suggestion accept ([#13])
23
+
24
+ ## [0.2.0] - 2026-06-30
25
+
26
+ ### Added
27
+
28
+ - Syntax-colored ghost text via `IRB::Color.colorize_code` (dimmed)
29
+ - Prefix-filtered history navigation (up/down arrows match typed prefix)
30
+ - Ruby 2.7 support
31
+
32
+ ### Changed
33
+
34
+ - Ghost text now uses dim syntax colors when `IRB.conf[:USE_COLORIZE]` is enabled
35
+
36
+ ## [0.1.1] - 2026-06-25
37
+
38
+ ### Added
39
+
40
+ - `IRB.conf[:USE_AUTOSUGGESTIONS]` toggle
41
+ - Environment variable `IRB_AUTOSUGGESTIONS` override
42
+ - RBS type signatures
43
+
44
+ ### Fixed
45
+
46
+ - Gem metadata (licenses, homepage)
47
+
48
+ ## [0.1.0] - 2026-06-20
49
+
50
+ ### Added
51
+
52
+ - Initial release: fish-like autosuggestions from `Reline::HISTORY`
53
+ - Right arrow key accepts ghost suggestion
54
+ - Gray ghost text (`\e[90m`)
55
+ - Configuration via `IRB.conf[:USE_AUTOSUGGESTIONS]`
56
+
57
+ [#15]: https://github.com/unurgunite/irb-autosuggestions/pull/15
58
+
59
+ [#16]: https://github.com/unurgunite/irb-autosuggestions/pull/16
60
+
61
+ [#18]: https://github.com/unurgunite/irb-autosuggestions/pull/18
62
+
63
+ [#19]: https://github.com/unurgunite/irb-autosuggestions/pull/19
64
+
65
+ [#157]: https://github.com/unurgunite/irb-autosuggestions/issues/157
66
+
67
+ [#163]: https://github.com/unurgunite/irb-autosuggestions/issues/163
68
+
69
+ [#159]: https://github.com/unurgunite/irb-autosuggestions/issues/159
70
+
71
+ [#256]: https://github.com/unurgunite/irb-autosuggestions/issues/256
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- irb-autosuggestions (0.2.1)
4
+ irb-autosuggestions (0.2.2)
5
5
  reline
6
6
 
7
7
  GEM
@@ -9,11 +9,11 @@ GEM
9
9
  specs:
10
10
  ast (2.4.3)
11
11
  diff-lcs (1.6.2)
12
- docscribe (1.3.3)
12
+ docscribe (1.5.1)
13
13
  parser (>= 3.3)
14
14
  prism (~> 1.8)
15
15
  io-console (0.8.2)
16
- json (2.19.5)
16
+ json (2.20.0)
17
17
  language_server-protocol (3.17.0.5)
18
18
  lint_roller (1.1.0)
19
19
  logger (1.7.0)
@@ -43,7 +43,7 @@ GEM
43
43
  diff-lcs (>= 1.2.0, < 2.0)
44
44
  rspec-support (~> 3.13.0)
45
45
  rspec-support (3.13.7)
46
- rubocop (1.86.2)
46
+ rubocop (1.88.1)
47
47
  json (~> 2.3)
48
48
  language_server-protocol (~> 3.17.0.2)
49
49
  lint_roller (~> 1.1.0)
@@ -60,10 +60,11 @@ GEM
60
60
  rubocop-rake (0.7.1)
61
61
  lint_roller (~> 1.1)
62
62
  rubocop (>= 1.72.1)
63
- rubocop-rspec (3.9.0)
63
+ rubocop-rspec (3.10.2)
64
64
  lint_roller (~> 1.1)
65
- rubocop (~> 1.81)
66
- rubocop-sorted_methods_by_call (1.2.2)
65
+ regexp_parser (>= 2.0)
66
+ rubocop (~> 1.86, >= 1.86.2)
67
+ rubocop-sorted_methods_by_call (1.2.3)
67
68
  lint_roller
68
69
  rubocop (>= 1.72.0)
69
70
  ruby-progressbar (1.13.0)
@@ -91,4 +92,4 @@ DEPENDENCIES
91
92
  yard (>= 0.9.38)
92
93
 
93
94
  BUNDLED WITH
94
- 4.0.11
95
+ 2.4.22
data/Gemfile_2_7.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- irb-autosuggestions (0.2.1)
4
+ irb-autosuggestions (0.2.2)
5
5
  reline
6
6
 
7
7
  GEM
@@ -9,11 +9,11 @@ GEM
9
9
  specs:
10
10
  ast (2.4.3)
11
11
  diff-lcs (1.6.2)
12
- docscribe (1.3.3)
12
+ docscribe (1.5.1)
13
13
  parser (>= 3.3)
14
14
  prism (~> 1.8)
15
15
  io-console (0.8.2)
16
- json (2.19.5)
16
+ json (2.20.0)
17
17
  language_server-protocol (3.17.0.5)
18
18
  lint_roller (1.1.0)
19
19
  parallel (1.28.0)
@@ -40,7 +40,7 @@ GEM
40
40
  diff-lcs (>= 1.2.0, < 2.0)
41
41
  rspec-support (~> 3.13.0)
42
42
  rspec-support (3.13.7)
43
- rubocop (1.86.2)
43
+ rubocop (1.88.1)
44
44
  json (~> 2.3)
45
45
  language_server-protocol (~> 3.17.0.2)
46
46
  lint_roller (~> 1.1.0)
@@ -57,10 +57,11 @@ GEM
57
57
  rubocop-rake (0.7.1)
58
58
  lint_roller (~> 1.1)
59
59
  rubocop (>= 1.72.1)
60
- rubocop-rspec (3.9.0)
60
+ rubocop-rspec (3.10.2)
61
61
  lint_roller (~> 1.1)
62
- rubocop (~> 1.81)
63
- rubocop-sorted_methods_by_call (1.2.2)
62
+ regexp_parser (>= 2.0)
63
+ rubocop (~> 1.86, >= 1.86.2)
64
+ rubocop-sorted_methods_by_call (1.2.3)
64
65
  lint_roller
65
66
  rubocop (>= 1.72.0)
66
67
  ruby-progressbar (1.13.0)
data/README.md CHANGED
@@ -18,7 +18,9 @@ No need to explain. Fish-like autosuggestions for IRB — ghost text from histor
18
18
  * [Usage](#usage)
19
19
  * [Prefix-filtered history navigation](#prefix-filtered-history-navigation)
20
20
  * [Configuration](#configuration)
21
+ * [Ghost color](#ghost-color)
21
22
  * [Colors](#colors)
23
+ * [Accept keys](#accept-keys)
22
24
  * [How it works](#how-it-works)
23
25
  * [Development](#development)
24
26
  * [License](#license)
@@ -48,7 +50,8 @@ irb(main):002* el.succ <- "cc" in gray
48
50
  irb(main):003> end <- "d" in gray
49
51
  ```
50
52
 
51
- Press **right arrow** (`->`) to accept the full multiline suggestion.
53
+ Press **right arrow** (`->`), **Tab**, **Ctrl+F**, or **Ctrl+E** to accept the full multiline suggestion. Tab falls
54
+ back to normal completion when no suggestion is visible. Accept keys are configurable (see below).
52
55
 
53
56
  ### Prefix-filtered history navigation
54
57
 
@@ -96,6 +99,29 @@ Or via environment variable:
96
99
  export IRB_AUTOSUGGESTIONS=0
97
100
  ```
98
101
 
102
+ ### Ghost color
103
+
104
+ The plain gray fallback color can be customized. Syntax-colored ghost text (when `IRB::Color` is available) always
105
+ takes precedence over these settings.
106
+
107
+ Set a raw ANSI escape code:
108
+
109
+ ```ruby
110
+ IRB.conf[:AUTOSUGGESTION_GHOST_COLOR] = "\e[32m" # green
111
+ ```
112
+
113
+ Or use the declarative style hash:
114
+
115
+ ```ruby
116
+ IRB.conf[:AUTOSUGGESTION_GHOST_STYLE] = { fg: :bright_black, italic: true }
117
+ ```
118
+
119
+ Supported colors: `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`, `default`,
120
+ `bright_black`, `bright_red`, `bright_green`, `bright_yellow`, `bright_blue`, `bright_magenta`,
121
+ `bright_cyan`, `bright_white`.
122
+
123
+ Supported attributes: `bold`, `dim`, `italic`, `underline`, `blink`, `reverse`.
124
+
99
125
  ### Colors
100
126
 
101
127
  Syntax-colored ghost text is **enabled by default** when `IRB::Color` is available and `IRB.conf[:USE_COLORIZE]` is
@@ -118,6 +144,19 @@ irb --nocolorize
118
144
  > schemes. If you notice visual artifacts (e.g., wrong colors, underlines, or unusual brightness), try disabling the
119
145
  > feature or switch to the gray fallback or create new issue.
120
146
 
147
+ ### Accept keys
148
+
149
+ Configure which keys accept the ghost suggestion:
150
+
151
+ ```ruby
152
+ IRB.conf[:AUTOSUGGESTION_ACCEPT_KEYS] = %i[ed_next_char tab ed_end_of_line]
153
+ ```
154
+
155
+ Defaults: `[:ed_next_char, :ed_end_of_line, :tab]` (right arrow/Ctrl+F, Ctrl+E, Tab).
156
+
157
+ Available symbols: `:ed_next_char` (right arrow/Ctrl+F), `:ed_end_of_line` (Ctrl+E), `:tab`, or
158
+ any Reline method symbol. When no suggestion is visible, the key falls through to its normal behavior.
159
+
121
160
  ### How it works
122
161
 
123
162
  - Each keystroke queries `Reline::HISTORY` for the most recent entry whose prefix matches the current buffer.
@@ -127,7 +166,7 @@ irb --nocolorize
127
166
  - Extra ghost lines (for multiline history entries) are drawn below the prompt.
128
167
  - Ghost artifacts are cleared each frame using cursor save/restore (`\e[s`/`\e[u`) and per-line clearing (`\e[2K`),
129
168
  avoiding `\e[J` which interfered with Reline's cursor tracking.
130
- - Right arrow triggers `ed_next_char`, which replaces the buffer with the ghost text.
169
+ - Configurable accept keys (right arrow, Tab, Ctrl+F, Ctrl+E by default) replace the buffer with the ghost text.
131
170
  - Up/down arrows use prefix-filtered navigation when the buffer is non-empty, falling back to unfiltered browsing
132
171
  when nothing was typed.
133
172
 
@@ -14,8 +14,22 @@ module Irb
14
14
  ENV_KEY = 'IRB_AUTOSUGGESTIONS'
15
15
  CONFIG_NAV_KEY = :USE_PREFIX_HISTORY_NAVIGATION
16
16
  ENV_NAV_KEY = 'IRB_PREFIX_HISTORY_NAVIGATION'
17
-
18
- # Intercepts key input to accept autosuggestions on right arrow
17
+ ACCEPT_KEYS_CONFIG = :AUTOSUGGESTION_ACCEPT_KEYS
18
+ DEFAULT_ACCEPT_KEYS = %i[ed_next_char ed_end_of_line tab].freeze
19
+ GHOST_COLOR_CONFIG = :AUTOSUGGESTION_GHOST_COLOR
20
+ GHOST_STYLE_CONFIG = :AUTOSUGGESTION_GHOST_STYLE
21
+ DEFAULT_GHOST_COLOR = "\e[90m"
22
+ FG_MAP = {
23
+ black: 30, red: 31, green: 32, yellow: 33, blue: 34, magenta: 35, cyan: 36, white: 37,
24
+ default: 39,
25
+ bright_black: 90, bright_red: 91, bright_green: 92, bright_yellow: 93,
26
+ bright_blue: 94, bright_magenta: 95, bright_cyan: 96, bright_white: 97
27
+ }.freeze
28
+ ATTR_MAP = {
29
+ bold: 1, dim: 2, italic: 3, underline: 4, blink: 5, reverse: 7
30
+ }.freeze
31
+
32
+ # Intercepts key input to accept autosuggestions on configured keys
19
33
  # and clears the prefix navigation anchor on non-history keys.
20
34
  #
21
35
  # @param [Object] key A Reline key event.
@@ -23,7 +37,7 @@ module Irb
23
37
  def input_key(key)
24
38
  clear_prefix_anchor if navigation_enabled? && !history_navigation_key?(key)
25
39
 
26
- if enabled? && right_arrow?(key)
40
+ if enabled? && accept_key?(key)
27
41
  buffer = whole_buffer
28
42
  suggestion = find_suggestion(buffer)
29
43
 
@@ -41,13 +55,17 @@ module Irb
41
55
  # Works on both Reline 0.3.x (rerender is the main rendering entry) and
42
56
  # Reline 0.4+ (rerender calls render internally).
43
57
  #
58
+ # Ghost is cleared BEFORE super to avoid cursor-position-dependent
59
+ # escapes targeting wrong lines after accept_suggestion changes buffer.
60
+ #
44
61
  # @return [Object] The result of +super+.
45
62
  def rerender
63
+ clear_previous_ghost if enabled?
64
+
46
65
  result = super
47
- if enabled?
48
- clear_previous_ghost
49
- render_ghost_suggestion
50
- end
66
+
67
+ render_ghost_suggestion if enabled?
68
+
51
69
  result
52
70
  end
53
71
 
@@ -212,14 +230,34 @@ module Irb
212
230
  end
213
231
  end
214
232
 
215
- # Checks if a key event is a right arrow press.
233
+ # Checks if a key event matches any configured accept key.
234
+ # Defaults to right arrow (:ed_next_char).
235
+ # Tab is matched specially — method_symbol == :ed_insert + char == "\t".
216
236
  #
217
237
  # @private
218
238
  # @param [Object] key A Reline key event.
219
239
  # @return [Boolean]
220
- def right_arrow?(key)
221
- key.respond_to?(:method_symbol) &&
222
- key.method_symbol == :ed_next_char
240
+ def accept_key?(key)
241
+ accept_keys = IRB.conf.fetch(ACCEPT_KEYS_CONFIG, DEFAULT_ACCEPT_KEYS)
242
+ accept_keys.any? { |k| key_match?(key, k) }
243
+ end
244
+
245
+ # Checks if a key matches a given config symbol.
246
+ #
247
+ # @private
248
+ # @param [Object] key A Reline key event.
249
+ # @param [Symbol] config_symbol One of :ed_next_char, :ed_end_of_line, :tab, etc.
250
+ # @return [Boolean]
251
+ def key_match?(key, config_symbol)
252
+ return false unless key.respond_to?(:method_symbol)
253
+
254
+ if config_symbol == :tab
255
+ key.method_symbol == :ed_insert &&
256
+ key.respond_to?(:char) &&
257
+ key.char == "\t"
258
+ else
259
+ key.method_symbol == config_symbol
260
+ end
223
261
  end
224
262
 
225
263
  # Finds the index of the previous (older) history entry starting with +buffer+.
@@ -354,7 +392,6 @@ module Irb
354
392
  #
355
393
  # When colorization is enabled, the full suggestion is colorized via
356
394
  # IRB::Color and the ghost portion is extracted from the colored output.
357
- # Otherwise, each line is wrapped in GRAY/RESET.
358
395
  #
359
396
  # @private
360
397
  # @param [String] ghost The ghost text (suffix of the suggestion).
@@ -365,10 +402,35 @@ module Irb
365
402
  if suggestion && use_colorize?
366
403
  colorize_ghost_lines(ghost, suggestion)
367
404
  else
368
- ghost.split("\n").map { |line| "#{GRAY}#{line}#{RESET}" }
405
+ ghost.split("\n").map { |line| "#{ghost_color}#{line}#{RESET}" }
369
406
  end
370
407
  rescue StandardError
371
- ghost.split("\n").map { |line| "#{GRAY}#{line}#{RESET}" }
408
+ ghost.split("\n").map { |line| "#{ghost_color}#{line}#{RESET}" }
409
+ end
410
+
411
+ # Returns the effective ANSI color code for ghost text.
412
+ # Checks GHOST_STYLE_CONFIG first, then GHOST_COLOR_CONFIG, then default.
413
+ #
414
+ # @private
415
+ # @return [String]
416
+ def ghost_color
417
+ style = IRB.conf[GHOST_STYLE_CONFIG]
418
+ return resolve_ghost_style(style) if style.is_a?(Hash)
419
+
420
+ IRB.conf.fetch(GHOST_COLOR_CONFIG, DEFAULT_GHOST_COLOR)
421
+ end
422
+
423
+ # Converts a style hash like +{ fg: :bright_black, italic: true }+
424
+ # to an ANSI escape sequence.
425
+ #
426
+ # @private
427
+ # @param [Hash] hash Style hash with +:fg+ color and optional attribute keys.
428
+ # @return [String]
429
+ def resolve_ghost_style(hash)
430
+ codes = []
431
+ codes << FG_MAP[hash[:fg]] if hash[:fg]
432
+ ATTR_MAP.each { |attr, code| codes << code if hash[attr] }
433
+ "\e[#{codes.join(';')}m"
372
434
  end
373
435
 
374
436
  # Checks whether syntax coloring is available and enabled.
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Irb
4
4
  module Autosuggestions
5
- VERSION = '0.2.1'
5
+ VERSION = '0.2.2'
6
6
  end
7
7
  end
data/readme.png CHANGED
Binary file
@@ -10,24 +10,39 @@ module Irb
10
10
  CONFIG_KEY: Symbol
11
11
  ENV_KEY: String
12
12
  RESET_COLOR: String
13
+ ACCEPT_KEYS_CONFIG: Symbol
14
+ DEFAULT_ACCEPT_KEYS: Array[Symbol]
15
+ GHOST_COLOR_CONFIG: Symbol
16
+ GHOST_STYLE_CONFIG: Symbol
17
+ DEFAULT_GHOST_COLOR: String
18
+ FG_MAP: Hash[Symbol, Integer]
19
+ ATTR_MAP: Hash[Symbol, Integer]
13
20
 
14
21
  @buffer_of_lines: Array[String]
15
22
  @byte_pointer: Integer
16
23
  @line_index: Integer
17
24
 
18
25
  def input_key: (untyped key) -> untyped
26
+ def rerender: () -> untyped
19
27
  def render: (*untyped) -> untyped
20
28
  def enabled?: () -> bool
29
+ def navigation_enabled?: () -> bool
21
30
  def use_colorize?: () -> bool
22
- def right_arrow?: (untyped key) -> bool
31
+ def accept_key?: (untyped key) -> bool
32
+ def key_match?: (untyped key, Symbol config_symbol) -> bool
33
+ def ghost_color: () -> String
34
+ def resolve_ghost_style: (Hash[Symbol, untyped] hash) -> String
35
+ def render_ghost_suggestion: () -> void
23
36
  def render_ghost: (String ghost, ?String suggestion) -> void
24
37
  def ghost_display_lines: (String ghost, ?String suggestion) -> Array[String]
25
38
  def colorize_ghost_lines: (String ghost, String suggestion) -> Array[String]
39
+ def dim_line: (String line) -> String
26
40
  def extract_ansi_colored_suffix: (String colored_text, Integer visible_byte_offset) -> String
27
41
  def write_extra_ghost_lines: (Array[String] lines) -> void
28
- def restore_cursor_after: (Array[String] lines) -> void
29
42
  def find_suggestion: (String buffer) -> (String | nil)
30
43
  def accept_suggestion: (String suggestion) -> void
44
+ def find_prev_match: (String buffer, Integer? from_pointer) -> Integer?
45
+ def find_next_match: (String buffer, Integer? from_pointer) -> Integer?
31
46
  end
32
47
  end
33
48
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: irb-autosuggestions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - unurgunite
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-30 00:00:00.000000000 Z
11
+ date: 2026-07-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: reline
@@ -145,6 +145,7 @@ files:
145
145
  - ".rspec"
146
146
  - ".rubocop.yml"
147
147
  - ".ruby-version"
148
+ - CHANGELOG.md
148
149
  - CODE_OF_CONDUCT.md
149
150
  - Gemfile
150
151
  - Gemfile.lock