irb-autosuggestions 0.2.0 → 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 +4 -4
- data/CHANGELOG.md +71 -0
- data/Gemfile.lock +10 -8
- data/Gemfile_2_7.lock +9 -8
- data/README.md +43 -3
- data/lib/irb/autosuggestions/line_editor_patch.rb +124 -52
- data/lib/irb/autosuggestions/version.rb +1 -1
- data/readme.png +0 -0
- data/sig/lib/irb/autosuggestions/line_editor_patch.rbs +17 -2
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: df8665e8ded51b93cb8c175133f17a82bdf369ffe8dfc191740d65958821ab9c
|
|
4
|
+
data.tar.gz: 8c37639ad88f22439a243b5f59e4794feb1edd744dcfaf0caa65c59e662eefb7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
63
|
+
rubocop-rspec (3.10.2)
|
|
64
64
|
lint_roller (~> 1.1)
|
|
65
|
-
|
|
66
|
-
|
|
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)
|
|
@@ -75,6 +76,7 @@ GEM
|
|
|
75
76
|
PLATFORMS
|
|
76
77
|
arm64-darwin-24
|
|
77
78
|
arm64-darwin-25
|
|
79
|
+
ruby
|
|
78
80
|
x86_64-linux
|
|
79
81
|
|
|
80
82
|
DEPENDENCIES
|
|
@@ -90,4 +92,4 @@ DEPENDENCIES
|
|
|
90
92
|
yard (>= 0.9.38)
|
|
91
93
|
|
|
92
94
|
BUNDLED WITH
|
|
93
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
60
|
+
rubocop-rspec (3.10.2)
|
|
61
61
|
lint_roller (~> 1.1)
|
|
62
|
-
|
|
63
|
-
|
|
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)
|
|
@@ -70,8 +71,8 @@ GEM
|
|
|
70
71
|
yard (0.9.44)
|
|
71
72
|
|
|
72
73
|
PLATFORMS
|
|
73
|
-
x86_64-linux
|
|
74
74
|
arm64-darwin-25
|
|
75
|
+
x86_64-linux
|
|
75
76
|
|
|
76
77
|
DEPENDENCIES
|
|
77
78
|
docscribe
|
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
|
|
@@ -113,10 +139,24 @@ Or from command line:
|
|
|
113
139
|
irb --nocolorize
|
|
114
140
|
```
|
|
115
141
|
|
|
116
|
-
> [!NOTE]
|
|
142
|
+
> [!NOTE]
|
|
143
|
+
> Colorized ghost rendering may behave differently across terminal emulators, Ruby versions, and IRB color
|
|
117
144
|
> schemes. If you notice visual artifacts (e.g., wrong colors, underlines, or unusual brightness), try disabling the
|
|
118
145
|
> feature or switch to the gray fallback or create new issue.
|
|
119
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
|
+
|
|
120
160
|
### How it works
|
|
121
161
|
|
|
122
162
|
- Each keystroke queries `Reline::HISTORY` for the most recent entry whose prefix matches the current buffer.
|
|
@@ -126,7 +166,7 @@ irb --nocolorize
|
|
|
126
166
|
- Extra ghost lines (for multiline history entries) are drawn below the prompt.
|
|
127
167
|
- Ghost artifacts are cleared each frame using cursor save/restore (`\e[s`/`\e[u`) and per-line clearing (`\e[2K`),
|
|
128
168
|
avoiding `\e[J` which interfered with Reline's cursor tracking.
|
|
129
|
-
-
|
|
169
|
+
- Configurable accept keys (right arrow, Tab, Ctrl+F, Ctrl+E by default) replace the buffer with the ghost text.
|
|
130
170
|
- Up/down arrows use prefix-filtered navigation when the buffer is non-empty, falling back to unfiltered browsing
|
|
131
171
|
when nothing was typed.
|
|
132
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
|
-
|
|
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? &&
|
|
40
|
+
if enabled? && accept_key?(key)
|
|
27
41
|
buffer = whole_buffer
|
|
28
42
|
suggestion = find_suggestion(buffer)
|
|
29
43
|
|
|
@@ -36,23 +50,27 @@ module Irb
|
|
|
36
50
|
super
|
|
37
51
|
end
|
|
38
52
|
|
|
39
|
-
private
|
|
40
|
-
|
|
41
53
|
# Injects ghost text into terminal output after Reline finishes rendering.
|
|
42
|
-
#
|
|
43
|
-
#
|
|
54
|
+
# Must be public because Reline::Core calls +rerender+ externally.
|
|
55
|
+
# Works on both Reline 0.3.x (rerender is the main rendering entry) and
|
|
56
|
+
# Reline 0.4+ (rerender calls render internally).
|
|
57
|
+
#
|
|
58
|
+
# Ghost is cleared BEFORE super to avoid cursor-position-dependent
|
|
59
|
+
# escapes targeting wrong lines after accept_suggestion changes buffer.
|
|
44
60
|
#
|
|
45
|
-
# @private
|
|
46
61
|
# @return [Object] The result of +super+.
|
|
47
|
-
def
|
|
62
|
+
def rerender
|
|
63
|
+
clear_previous_ghost if enabled?
|
|
64
|
+
|
|
48
65
|
result = super
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
end
|
|
66
|
+
|
|
67
|
+
render_ghost_suggestion if enabled?
|
|
68
|
+
|
|
53
69
|
result
|
|
54
70
|
end
|
|
55
71
|
|
|
72
|
+
private
|
|
73
|
+
|
|
56
74
|
# Prefix-filtered up-arrow history navigation.
|
|
57
75
|
#
|
|
58
76
|
# @private
|
|
@@ -109,8 +127,8 @@ module Irb
|
|
|
109
127
|
end
|
|
110
128
|
|
|
111
129
|
# Clears ghost from the previous frame: inline text from buffer end
|
|
112
|
-
# to end of line, and each extra line below.
|
|
113
|
-
# so
|
|
130
|
+
# to end of line, and each extra line below. Restores cursor column
|
|
131
|
+
# afterwards so Reline's cursor tracking is not disturbed.
|
|
114
132
|
#
|
|
115
133
|
# @private
|
|
116
134
|
# @return [void]
|
|
@@ -118,10 +136,11 @@ module Irb
|
|
|
118
136
|
clear_inline_ghost
|
|
119
137
|
return unless @ghost_line_count&.positive?
|
|
120
138
|
|
|
139
|
+
origin_column = cursor_column_at_byte_pointer
|
|
121
140
|
output = Reline.core.instance_variable_get(:@output)
|
|
122
|
-
output.write("\e[s")
|
|
123
141
|
@ghost_line_count.times { output.write("\e[1B\e[2K") }
|
|
124
|
-
output.write("\e[
|
|
142
|
+
output.write("\e[#{@ghost_line_count}A")
|
|
143
|
+
output.write("\e[0G\e[#{origin_column}C")
|
|
125
144
|
end
|
|
126
145
|
|
|
127
146
|
# Clears inline ghost text from the buffer end to end of line.
|
|
@@ -132,12 +151,10 @@ module Irb
|
|
|
132
151
|
return unless @has_inline_ghost
|
|
133
152
|
|
|
134
153
|
output = Reline.core.instance_variable_get(:@output)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
output.write("\e[
|
|
139
|
-
output.write("\e[0G\e[#{buf_end}C\e[K")
|
|
140
|
-
output.write("\e[u")
|
|
154
|
+
buf_end = move_to_buffer_end
|
|
155
|
+
origin_column = cursor_column_at_byte_pointer
|
|
156
|
+
output.write("#{buf_end}\e[K")
|
|
157
|
+
output.write("\e[0G\e[#{origin_column}C")
|
|
141
158
|
@has_inline_ghost = false
|
|
142
159
|
end
|
|
143
160
|
|
|
@@ -184,6 +201,21 @@ module Irb
|
|
|
184
201
|
remove_instance_variable(:@prefix_buffer)
|
|
185
202
|
end
|
|
186
203
|
|
|
204
|
+
# Whether prefix-filtered history navigation is enabled.
|
|
205
|
+
# Falls back to the value of +enabled?+ when not explicitly set.
|
|
206
|
+
#
|
|
207
|
+
# @private
|
|
208
|
+
# @return [Boolean]
|
|
209
|
+
def navigation_enabled?
|
|
210
|
+
case ENV.fetch(ENV_NAV_KEY, nil)
|
|
211
|
+
when '0' then false
|
|
212
|
+
when '1' then true
|
|
213
|
+
else
|
|
214
|
+
val = IRB.conf[CONFIG_NAV_KEY]
|
|
215
|
+
val.nil? ? enabled? : val
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
187
219
|
# Checks whether autosuggestions are enabled via IRB.conf or env var.
|
|
188
220
|
#
|
|
189
221
|
# @private
|
|
@@ -198,29 +230,34 @@ module Irb
|
|
|
198
230
|
end
|
|
199
231
|
end
|
|
200
232
|
|
|
201
|
-
#
|
|
202
|
-
#
|
|
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".
|
|
203
236
|
#
|
|
204
237
|
# @private
|
|
238
|
+
# @param [Object] key A Reline key event.
|
|
205
239
|
# @return [Boolean]
|
|
206
|
-
def
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
when '1' then true
|
|
210
|
-
else
|
|
211
|
-
val = IRB.conf[CONFIG_NAV_KEY]
|
|
212
|
-
val.nil? ? enabled? : val
|
|
213
|
-
end
|
|
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) }
|
|
214
243
|
end
|
|
215
244
|
|
|
216
|
-
# Checks if a key
|
|
245
|
+
# Checks if a key matches a given config symbol.
|
|
217
246
|
#
|
|
218
247
|
# @private
|
|
219
248
|
# @param [Object] key A Reline key event.
|
|
249
|
+
# @param [Symbol] config_symbol One of :ed_next_char, :ed_end_of_line, :tab, etc.
|
|
220
250
|
# @return [Boolean]
|
|
221
|
-
def
|
|
222
|
-
key.respond_to?(:method_symbol)
|
|
223
|
-
|
|
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
|
|
224
261
|
end
|
|
225
262
|
|
|
226
263
|
# Finds the index of the previous (older) history entry starting with +buffer+.
|
|
@@ -325,6 +362,8 @@ module Irb
|
|
|
325
362
|
end
|
|
326
363
|
|
|
327
364
|
# Writes the ghost text (inline + extra lines) to terminal output.
|
|
365
|
+
# Saves and restores cursor column so Reline's cursor tracking
|
|
366
|
+
# (used for left/right arrow positioning) is not disturbed.
|
|
328
367
|
#
|
|
329
368
|
# If +suggestion+ is provided and colorization is enabled, the ghost
|
|
330
369
|
# is rendered with syntax highlighting via IRB::Color.
|
|
@@ -333,16 +372,19 @@ module Irb
|
|
|
333
372
|
# @param [String] ghost The ghost text (suffix of the suggestion).
|
|
334
373
|
# @param [String, nil] suggestion The full matching history entry.
|
|
335
374
|
# @return [void]
|
|
336
|
-
def render_ghost(ghost, suggestion = nil)
|
|
375
|
+
def render_ghost(ghost, suggestion = nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
|
337
376
|
output = Reline.core.instance_variable_get(:@output)
|
|
338
377
|
display_lines = ghost_display_lines(ghost, suggestion)
|
|
339
378
|
@ghost_line_count = display_lines.size - 1
|
|
340
379
|
@has_inline_ghost = true
|
|
341
380
|
|
|
381
|
+
output.write(move_to_buffer_end)
|
|
342
382
|
first_line = display_lines.first
|
|
343
383
|
output.write(first_line) if first_line && !first_line.empty?
|
|
344
384
|
write_extra_ghost_lines(display_lines.drop(1))
|
|
345
|
-
|
|
385
|
+
origin_column = cursor_column_at_byte_pointer
|
|
386
|
+
output.write("\e[#{@ghost_line_count}A") if @ghost_line_count.positive?
|
|
387
|
+
output.write("\e[0G\e[#{origin_column}C")
|
|
346
388
|
output.flush
|
|
347
389
|
end
|
|
348
390
|
|
|
@@ -350,7 +392,6 @@ module Irb
|
|
|
350
392
|
#
|
|
351
393
|
# When colorization is enabled, the full suggestion is colorized via
|
|
352
394
|
# IRB::Color and the ghost portion is extracted from the colored output.
|
|
353
|
-
# Otherwise, each line is wrapped in GRAY/RESET.
|
|
354
395
|
#
|
|
355
396
|
# @private
|
|
356
397
|
# @param [String] ghost The ghost text (suffix of the suggestion).
|
|
@@ -361,10 +402,35 @@ module Irb
|
|
|
361
402
|
if suggestion && use_colorize?
|
|
362
403
|
colorize_ghost_lines(ghost, suggestion)
|
|
363
404
|
else
|
|
364
|
-
ghost.split("\n").map { |line| "#{
|
|
405
|
+
ghost.split("\n").map { |line| "#{ghost_color}#{line}#{RESET}" }
|
|
365
406
|
end
|
|
366
407
|
rescue StandardError
|
|
367
|
-
ghost.split("\n").map { |line| "#{
|
|
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"
|
|
368
434
|
end
|
|
369
435
|
|
|
370
436
|
# Checks whether syntax coloring is available and enabled.
|
|
@@ -456,20 +522,26 @@ module Irb
|
|
|
456
522
|
end
|
|
457
523
|
end
|
|
458
524
|
|
|
459
|
-
#
|
|
525
|
+
# Returns the terminal column where the cursor should sit (after the
|
|
526
|
+
# prompt and currently typed text at +@byte_pointer+).
|
|
460
527
|
#
|
|
461
528
|
# @private
|
|
462
|
-
# @
|
|
463
|
-
|
|
464
|
-
def restore_cursor_after(lines)
|
|
465
|
-
extra_count = lines.size - 1
|
|
529
|
+
# @return [Integer]
|
|
530
|
+
def cursor_column_at_byte_pointer
|
|
466
531
|
prompt_width = @prompt ? Reline::Unicode.calculate_width(@prompt) : 0
|
|
467
|
-
|
|
468
|
-
|
|
532
|
+
current_line = @buffer_of_lines[@line_index] || ''
|
|
533
|
+
prompt_width + Reline::Unicode.calculate_width(current_line[0...@byte_pointer])
|
|
534
|
+
end
|
|
469
535
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
536
|
+
# Moves cursor to the end of the buffer line (for writing inline ghost).
|
|
537
|
+
#
|
|
538
|
+
# @private
|
|
539
|
+
# @return [String]
|
|
540
|
+
def move_to_buffer_end
|
|
541
|
+
prompt_width = @prompt ? Reline::Unicode.calculate_width(@prompt) : 0
|
|
542
|
+
current_line = @buffer_of_lines[@line_index] || ''
|
|
543
|
+
buf_end = prompt_width + Reline::Unicode.calculate_width(current_line)
|
|
544
|
+
"\e[0G\e[#{buf_end}C"
|
|
473
545
|
end
|
|
474
546
|
end
|
|
475
547
|
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
|
|
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.
|
|
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-
|
|
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
|