pretty_irb 0.1.2 → 0.1.4
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 +9 -0
- data/lib/pretty_irb/ai_helper.rb +3 -3
- data/lib/pretty_irb/auto_corrector.rb +87 -76
- data/lib/pretty_irb/benchmarker.rb +48 -45
- data/lib/pretty_irb/configuration.rb +40 -0
- data/lib/pretty_irb/formatter.rb +201 -66
- data/lib/pretty_irb/history_manager.rb +21 -2
- data/lib/pretty_irb/irb_commands.rb +180 -0
- data/lib/pretty_irb/railtie.rb +35 -0
- data/lib/pretty_irb/shell.rb +435 -387
- data/lib/pretty_irb/snippet_manager.rb +9 -0
- data/lib/pretty_irb/ui.rb +88 -0
- data/lib/pretty_irb/version.rb +1 -1
- data/lib/pretty_irb.rb +17 -0
- data/pretty_irb.gemspec +4 -1
- metadata +19 -1
data/lib/pretty_irb/shell.rb
CHANGED
|
@@ -1,387 +1,435 @@
|
|
|
1
|
-
require "irb"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
1
|
+
require "irb"
|
|
2
|
+
|
|
3
|
+
module PrettyIRB
|
|
4
|
+
class Shell
|
|
5
|
+
BANNER = <<~BANNER
|
|
6
|
+
╭─────────────────────────────────────────────────────────────╮
|
|
7
|
+
│ pretty_irb v#{PrettyIRB::VERSION} · Enhanced Interactive Ruby Shell │
|
|
8
|
+
│ type commands to list extras · exit / Ctrl+D to quit │
|
|
9
|
+
╰─────────────────────────────────────────────────────────────╯
|
|
10
|
+
BANNER
|
|
11
|
+
|
|
12
|
+
module OutputHook
|
|
13
|
+
def output_value(omit = false)
|
|
14
|
+
return super unless PrettyIRB::Shell.active?
|
|
15
|
+
return if omit
|
|
16
|
+
|
|
17
|
+
puts PrettyIRB::Formatter.format_result(@context.last_value)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module ExceptionHook
|
|
22
|
+
def handle_exception(exc)
|
|
23
|
+
return super unless PrettyIRB::Shell.active?
|
|
24
|
+
|
|
25
|
+
first_line = exc.message.to_s.lines.first&.strip
|
|
26
|
+
PrettyIRB::Shell.history_manager&.record_error("#{exc.class}: #{first_line}")
|
|
27
|
+
super
|
|
28
|
+
PrettyIRB::Shell.print_exception_hint(exc)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
module EvalHook
|
|
33
|
+
def evaluate(code, line_no, *args, **kwargs, &block)
|
|
34
|
+
raw = nil
|
|
35
|
+
if PrettyIRB::Shell.active?
|
|
36
|
+
raw = PrettyIRB::Shell.extract_raw_code(code)
|
|
37
|
+
if raw && PrettyIRB.configuration.auto_correct
|
|
38
|
+
corrected = PrettyIRB::AutoCorrector.auto_correct_code(raw)
|
|
39
|
+
if corrected.strip != raw.strip
|
|
40
|
+
label = PrettyIRB::UI.paint(" ↳ auto-corrected: ", :light_black)
|
|
41
|
+
fixed = PrettyIRB::UI.paint(corrected.strip, :yellow)
|
|
42
|
+
warn "#{label}#{fixed}"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
result = begin
|
|
48
|
+
if kwargs.empty?
|
|
49
|
+
super(code, line_no, *args, &block)
|
|
50
|
+
else
|
|
51
|
+
super(code, line_no, *args, **kwargs, &block)
|
|
52
|
+
end
|
|
53
|
+
rescue ArgumentError
|
|
54
|
+
super(code, line_no)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
if PrettyIRB::Shell.active? && raw && !raw.strip.empty?
|
|
58
|
+
preview = begin
|
|
59
|
+
str = result.inspect
|
|
60
|
+
str.length > 80 ? "#{str[0, 77]}..." : str
|
|
61
|
+
rescue
|
|
62
|
+
nil
|
|
63
|
+
end
|
|
64
|
+
PrettyIRB::Shell.history_manager&.add(raw.strip, preview)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
result
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
module RailsIRBInitHook
|
|
72
|
+
def initialize(*args, **kwargs, &block)
|
|
73
|
+
if (shell = PrettyIRB::Shell.consume_pending_rails_shell)
|
|
74
|
+
shell.apply_irb_configuration!
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
if kwargs.empty?
|
|
78
|
+
super(*args, &block)
|
|
79
|
+
else
|
|
80
|
+
super(*args, **kwargs, &block)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class << self
|
|
86
|
+
attr_accessor :history_manager, :snippet_manager, :active, :pending_rails_shell
|
|
87
|
+
|
|
88
|
+
def active?
|
|
89
|
+
!!@active
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def consume_pending_rails_shell
|
|
93
|
+
shell = @pending_rails_shell
|
|
94
|
+
@pending_rails_shell = nil
|
|
95
|
+
shell
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def extract_raw_code(code)
|
|
99
|
+
if code.is_a?(String)
|
|
100
|
+
code
|
|
101
|
+
elsif code.respond_to?(:code)
|
|
102
|
+
code.code.to_s
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def print_exception_hint(exc)
|
|
107
|
+
return unless PrettyIRB.configuration.auto_correct
|
|
108
|
+
|
|
109
|
+
suggestion = PrettyIRB::AutoCorrector.suggest_corrections(exc).to_s.strip
|
|
110
|
+
return if suggestion.empty?
|
|
111
|
+
if exc.message.to_s.include?("Did you mean?") &&
|
|
112
|
+
!suggestion.include?("Receiver is nil") &&
|
|
113
|
+
!suggestion.include?("Did you mean `.")
|
|
114
|
+
return
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
puts " #{PrettyIRB::UI.paint(suggestion, :yellow)}"
|
|
118
|
+
rescue
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def initialize(config = {})
|
|
124
|
+
@config = config || {}
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def run
|
|
128
|
+
STDOUT.sync = true
|
|
129
|
+
resolved_config = resolve_configuration
|
|
130
|
+
prev_config = PrettyIRB.configuration
|
|
131
|
+
PrettyIRB.instance_variable_set(:@configuration, resolved_config)
|
|
132
|
+
self.class.active = true
|
|
133
|
+
|
|
134
|
+
puts render_banner if resolved_config.banner
|
|
135
|
+
|
|
136
|
+
self.class.history_manager = HistoryManager.new
|
|
137
|
+
self.class.snippet_manager = SnippetManager.new
|
|
138
|
+
|
|
139
|
+
# Step 1: patch IRB classes idempotently before anything else
|
|
140
|
+
install_output_hook
|
|
141
|
+
install_eval_hook
|
|
142
|
+
install_exception_hook
|
|
143
|
+
|
|
144
|
+
@using_rails_irb_console = rails_irb_console_available?
|
|
145
|
+
if @using_rails_irb_console
|
|
146
|
+
run_with_rails_irb_console
|
|
147
|
+
else
|
|
148
|
+
run_standalone_or_legacy_rails
|
|
149
|
+
end
|
|
150
|
+
ensure
|
|
151
|
+
@using_rails_irb_console = false
|
|
152
|
+
self.class.consume_pending_rails_shell
|
|
153
|
+
self.class.active = false
|
|
154
|
+
PrettyIRB.instance_variable_set(:@configuration, prev_config)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Applies PrettyIRB's Reline tuning, prompt, and IRB_RC hook onto the
|
|
158
|
+
# current IRB.conf after IRB.setup has initialized it.
|
|
159
|
+
def apply_irb_configuration!
|
|
160
|
+
tune_input_mode
|
|
161
|
+
configure_prompt
|
|
162
|
+
install_irb_rc_hook
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private
|
|
166
|
+
|
|
167
|
+
def resolve_configuration
|
|
168
|
+
PrettyIRB.configuration.merge(@config)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def rails_app
|
|
172
|
+
return @config[:app] if @config.is_a?(Hash) && @config[:app]
|
|
173
|
+
return Rails.application if defined?(Rails) && Rails.respond_to?(:application)
|
|
174
|
+
|
|
175
|
+
nil
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def rails_irb_console_available?
|
|
179
|
+
return false unless rails_app
|
|
180
|
+
return true if defined?(Rails::Console::IRBConsole)
|
|
181
|
+
|
|
182
|
+
require "rails/commands/console/irb_console"
|
|
183
|
+
!!defined?(Rails::Console::IRBConsole)
|
|
184
|
+
rescue LoadError
|
|
185
|
+
false
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Rails 7.2+ / 8.x delegates IRB initialization, helper registration
|
|
189
|
+
# (`reload!`, `app`, `helper`, `controller`), and backtrace cleaning to
|
|
190
|
+
# Rails::Console::IRBConsole. Hook into IRB::Irb#initialize so PrettyIRB
|
|
191
|
+
# layers on top right after IRB.setup(nil) completes.
|
|
192
|
+
def run_with_rails_irb_console
|
|
193
|
+
install_rails_irb_hook
|
|
194
|
+
self.class.pending_rails_shell = self
|
|
195
|
+
build_rails_irb_console(rails_app).start
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def build_rails_irb_console(app)
|
|
199
|
+
init_arity = Rails::Console::IRBConsole.instance_method(:initialize).arity
|
|
200
|
+
if init_arity == 1
|
|
201
|
+
Rails::Console::IRBConsole.new(app)
|
|
202
|
+
else
|
|
203
|
+
Rails::Console::IRBConsole.new(app, @config[:rails_options] || {})
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Standalone Ruby or Rails 7.0 / 7.1 (before Rails::Console::IRBConsole).
|
|
208
|
+
def run_standalone_or_legacy_rails
|
|
209
|
+
app = rails_app
|
|
210
|
+
load_legacy_rails_console_helpers if app
|
|
211
|
+
|
|
212
|
+
# IRB.setup populates IRB.conf (and resets IRB_RC to nil).
|
|
213
|
+
IRB.setup(nil)
|
|
214
|
+
|
|
215
|
+
configure_legacy_rails_irb(app) if app
|
|
216
|
+
apply_irb_configuration!
|
|
217
|
+
|
|
218
|
+
irb = IRB::Irb.new
|
|
219
|
+
IRB.conf[:MAIN_CONTEXT] = irb.context
|
|
220
|
+
irb.run(IRB.conf)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def load_legacy_rails_console_helpers
|
|
224
|
+
require "rails/console/app"
|
|
225
|
+
require "rails/console/helpers"
|
|
226
|
+
rescue LoadError
|
|
227
|
+
nil
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def configure_legacy_rails_irb(app)
|
|
231
|
+
IRB.conf[:IRB_NAME] = rails_app_name(app)
|
|
232
|
+
if !rails_local_env? && !ENV.key?("IRB_USE_AUTOCOMPLETE")
|
|
233
|
+
IRB.conf[:USE_AUTOCOMPLETE] = false
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) && Rails.backtrace_cleaner
|
|
237
|
+
prev_filter = IRB.conf[:BACKTRACE_FILTER]
|
|
238
|
+
IRB.conf[:BACKTRACE_FILTER] = lambda do |backtrace|
|
|
239
|
+
backtrace = prev_filter.call(backtrace) if prev_filter.respond_to?(:call)
|
|
240
|
+
Rails.backtrace_cleaner.filter(backtrace)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
if defined?(IRB::ExtendCommandBundle) && defined?(Rails::ConsoleMethods)
|
|
245
|
+
IRB::ExtendCommandBundle.include(Rails::ConsoleMethods)
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def rails_app_name(app)
|
|
250
|
+
if app.respond_to?(:name) && app.name
|
|
251
|
+
app.name.to_s
|
|
252
|
+
elsif app.class.respond_to?(:module_parent_name) && app.class.module_parent_name
|
|
253
|
+
app.class.module_parent_name.downcase
|
|
254
|
+
else
|
|
255
|
+
"rails"
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def rails_local_env?
|
|
260
|
+
return true unless defined?(Rails) && Rails.respond_to?(:env) && Rails.env
|
|
261
|
+
|
|
262
|
+
if Rails.env.respond_to?(:local?)
|
|
263
|
+
Rails.env.local?
|
|
264
|
+
else
|
|
265
|
+
Rails.env.development? || Rails.env.test?
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# ── Terminal / input detection ────────────────────────────────────────────
|
|
270
|
+
# On Windows, Reline uses a Win32 API console handler. In "legacy
|
|
271
|
+
# console" mode (old conhost.exe — PowerShell or cmd.exe without
|
|
272
|
+
# Windows Terminal) Alt+key sequences are read in a way that blocks the
|
|
273
|
+
# input loop, freezing the entire REPL.
|
|
274
|
+
|
|
275
|
+
def tune_input_mode
|
|
276
|
+
return unless Gem.win_platform?
|
|
277
|
+
|
|
278
|
+
legacy = begin
|
|
279
|
+
Reline::IOGate.instance_variable_get(:@legacy_console)
|
|
280
|
+
rescue
|
|
281
|
+
false
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
if legacy
|
|
285
|
+
IRB.conf[:USE_MULTILINE] = false # avoid Reline Windows hang
|
|
286
|
+
IRB.conf[:USE_AUTOCOMPLETE] = false
|
|
287
|
+
msg = " ℹ Legacy Windows console detected — running in single-line mode.\n" \
|
|
288
|
+
" Open Windows Terminal for full multiline & autocomplete support."
|
|
289
|
+
puts style_text(msg, :light_black)
|
|
290
|
+
end
|
|
291
|
+
rescue
|
|
292
|
+
nil
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# ── Banner & Prompt ───────────────────────────────────────────────────────
|
|
296
|
+
|
|
297
|
+
def render_banner
|
|
298
|
+
title = "#{style_text('pretty_irb', :white, :bold)} #{style_text("v#{PrettyIRB::VERSION}", :cyan)}"
|
|
299
|
+
lines = [
|
|
300
|
+
"#{style_text('Enhanced Interactive Ruby Shell', :white)} #{style_text("· Ruby #{RUBY_VERSION}", :light_black)}"
|
|
301
|
+
]
|
|
302
|
+
|
|
303
|
+
if (rails_line = build_rails_banner_line)
|
|
304
|
+
lines << rails_line
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
lines << :divider
|
|
308
|
+
lines << [
|
|
309
|
+
"#{style_text('commands', :cyan)} #{style_text('extras', :light_black)}",
|
|
310
|
+
"#{style_text('vars', :cyan)} #{style_text('scope', :light_black)}",
|
|
311
|
+
"#{style_text('bench', :cyan)} #{style_text('profile', :light_black)}",
|
|
312
|
+
"#{style_text('help', :cyan)} #{style_text('IRB', :light_black)}",
|
|
313
|
+
"#{style_text('Ctrl+D', :white)} #{style_text('exit', :light_black)}"
|
|
314
|
+
].join(style_text(" · ", :light_black))
|
|
315
|
+
|
|
316
|
+
PrettyIRB::UI.box(lines, title: title, min_width: 66)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def build_rails_banner_line
|
|
320
|
+
app = rails_app
|
|
321
|
+
return nil unless app
|
|
322
|
+
|
|
323
|
+
rails_ver = defined?(Rails) && Rails.respond_to?(:version) ? "Rails #{Rails.version}" : "Rails"
|
|
324
|
+
parts = [style_text("#{rails_app_name(app)} · #{rails_ver}", :light_black)]
|
|
325
|
+
if (badge = rails_env_badge)
|
|
326
|
+
parts << badge
|
|
327
|
+
end
|
|
328
|
+
parts << PrettyIRB::UI.badge("SANDBOX", color: :yellow, bold: true) if rails_sandbox?(app)
|
|
329
|
+
parts.join(" ")
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def rails_sandbox?(app = rails_app)
|
|
333
|
+
app && app.respond_to?(:sandbox?) && app.sandbox?
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def configure_prompt
|
|
337
|
+
prefix = build_prompt_prefix
|
|
338
|
+
caret_styles = rails_production? ? %i[red bold] : %i[cyan bold]
|
|
339
|
+
IRB.conf[:PROMPT] ||= {}
|
|
340
|
+
IRB.conf[:PROMPT][:PRETTY_IRB] = {
|
|
341
|
+
PROMPT_I: "#{prefix} #{style_text('❯ ', *caret_styles)}",
|
|
342
|
+
PROMPT_N: "#{prefix} #{style_text('│ ', :light_black)}",
|
|
343
|
+
PROMPT_S: "#{prefix} #{style_text('┆ ', :light_black)}",
|
|
344
|
+
PROMPT_C: "#{prefix} #{style_text('· ', :light_black)}",
|
|
345
|
+
RETURN: "=> %s\n"
|
|
346
|
+
}
|
|
347
|
+
IRB.conf[:PROMPT_MODE] = :PRETTY_IRB
|
|
348
|
+
IRB.conf[:SAVE_HISTORY] ||= 1000
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def build_prompt_prefix
|
|
352
|
+
base = style_text("[%03n]", :light_black)
|
|
353
|
+
badge = rails_env_badge
|
|
354
|
+
badge ? "#{base} #{badge}" : base
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def rails_production?
|
|
358
|
+
defined?(Rails) && Rails.respond_to?(:env) && Rails.env.to_s == "production"
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def rails_env_badge
|
|
362
|
+
return nil unless PrettyIRB.configuration.rails_env_prompt
|
|
363
|
+
return nil unless defined?(Rails) && Rails.respond_to?(:env) && Rails.env
|
|
364
|
+
|
|
365
|
+
env = Rails.env.to_s
|
|
366
|
+
return nil if env.empty?
|
|
367
|
+
|
|
368
|
+
styled_env = case env
|
|
369
|
+
when "development" then style_text("dev", :cyan)
|
|
370
|
+
when "test" then style_text("test", :light_blue)
|
|
371
|
+
when "production" then style_text("PROD", :red, :bold)
|
|
372
|
+
else style_text(env, :yellow)
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
if rails_sandbox?
|
|
376
|
+
"(#{styled_env}#{style_text(':', :light_black)}#{style_text('sandbox', :yellow)})"
|
|
377
|
+
else
|
|
378
|
+
"(#{styled_env})"
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def install_irb_rc_hook
|
|
383
|
+
prev_rc = IRB.conf[:IRB_RC]
|
|
384
|
+
IRB.conf[:IRB_RC] = proc do |ctx|
|
|
385
|
+
prev_rc.call(ctx) if prev_rc.respond_to?(:call)
|
|
386
|
+
ctx.prompt_mode = :PRETTY_IRB
|
|
387
|
+
ctx.auto_indent_mode = true
|
|
388
|
+
ctx.workspace.main.extend(PrettyIRB::IRBCommands)
|
|
389
|
+
if !@using_rails_irb_console && defined?(Rails::ConsoleMethods)
|
|
390
|
+
ctx.workspace.main.extend(Rails::ConsoleMethods)
|
|
391
|
+
end
|
|
392
|
+
IRB.conf[:MAIN_CONTEXT] ||= ctx
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def style_text(text, *styles)
|
|
397
|
+
PrettyIRB::UI.paint(text, *styles)
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
# ── Hooks ─────────────────────────────────────────────────────────────────
|
|
401
|
+
|
|
402
|
+
def install_output_hook
|
|
403
|
+
return if IRB::Irb.ancestors.include?(OutputHook)
|
|
404
|
+
|
|
405
|
+
IRB::Irb.prepend(OutputHook)
|
|
406
|
+
rescue => e
|
|
407
|
+
warn style_text(" pretty_irb: output hook skipped (#{e.message})", :light_black)
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def install_eval_hook
|
|
411
|
+
return if IRB::Context.ancestors.include?(EvalHook)
|
|
412
|
+
|
|
413
|
+
IRB::Context.prepend(EvalHook)
|
|
414
|
+
rescue => e
|
|
415
|
+
warn style_text(" pretty_irb: eval hook skipped (#{e.message})", :light_black)
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def install_exception_hook
|
|
419
|
+
return unless IRB::Irb.instance_methods.include?(:handle_exception)
|
|
420
|
+
return if IRB::Irb.ancestors.include?(ExceptionHook)
|
|
421
|
+
|
|
422
|
+
IRB::Irb.prepend(ExceptionHook)
|
|
423
|
+
rescue => e
|
|
424
|
+
warn style_text(" pretty_irb: exception hook skipped (#{e.message})", :light_black)
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def install_rails_irb_hook
|
|
428
|
+
return if IRB::Irb.ancestors.include?(RailsIRBInitHook)
|
|
429
|
+
|
|
430
|
+
IRB::Irb.prepend(RailsIRBInitHook)
|
|
431
|
+
rescue => e
|
|
432
|
+
warn style_text(" pretty_irb: rails init hook skipped (#{e.message})", :light_black)
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
end
|