pretty_irb 0.1.3 → 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/configuration.rb +40 -0
- data/lib/pretty_irb/formatter.rb +143 -31
- data/lib/pretty_irb/history_manager.rb +14 -2
- data/lib/pretty_irb/irb_commands.rb +80 -39
- data/lib/pretty_irb/railtie.rb +35 -0
- data/lib/pretty_irb/shell.rb +355 -74
- data/lib/pretty_irb/snippet_manager.rb +1 -0
- data/lib/pretty_irb/ui.rb +88 -0
- data/lib/pretty_irb/version.rb +1 -1
- data/lib/pretty_irb.rb +16 -0
- data/pretty_irb.gemspec +3 -1
- metadata +4 -1
data/lib/pretty_irb/shell.rb
CHANGED
|
@@ -9,66 +9,268 @@ module PrettyIRB
|
|
|
9
9
|
╰─────────────────────────────────────────────────────────────╯
|
|
10
10
|
BANNER
|
|
11
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
|
+
|
|
12
85
|
class << self
|
|
13
|
-
attr_accessor :history_manager, :snippet_manager
|
|
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
|
|
14
121
|
end
|
|
15
122
|
|
|
16
123
|
def initialize(config = {})
|
|
17
|
-
@config = config
|
|
124
|
+
@config = config || {}
|
|
18
125
|
end
|
|
19
126
|
|
|
20
127
|
def run
|
|
21
128
|
STDOUT.sync = true
|
|
22
|
-
|
|
129
|
+
resolved_config = resolve_configuration
|
|
130
|
+
prev_config = PrettyIRB.configuration
|
|
131
|
+
PrettyIRB.instance_variable_set(:@configuration, resolved_config)
|
|
132
|
+
self.class.active = true
|
|
23
133
|
|
|
24
|
-
|
|
25
|
-
Shell.snippet_manager = SnippetManager.new
|
|
134
|
+
puts render_banner if resolved_config.banner
|
|
26
135
|
|
|
27
|
-
|
|
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
|
|
28
140
|
install_output_hook
|
|
29
141
|
install_eval_hook
|
|
142
|
+
install_exception_hook
|
|
30
143
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
34
156
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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!
|
|
38
160
|
tune_input_mode
|
|
39
|
-
|
|
40
|
-
# Step 4: prompt — IRB.conf[:PROMPT] now exists.
|
|
41
161
|
configure_prompt
|
|
162
|
+
install_irb_rc_hook
|
|
163
|
+
end
|
|
42
164
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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] || {})
|
|
51
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!
|
|
52
217
|
|
|
53
|
-
# Step 6: start the REPL directly to avoid a second IRB.setup call.
|
|
54
218
|
irb = IRB::Irb.new
|
|
55
219
|
IRB.conf[:MAIN_CONTEXT] = irb.context
|
|
56
220
|
irb.run(IRB.conf)
|
|
57
221
|
end
|
|
58
222
|
|
|
59
|
-
|
|
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
|
|
60
268
|
|
|
61
269
|
# ── Terminal / input detection ────────────────────────────────────────────
|
|
62
270
|
# On Windows, Reline uses a Win32 API console handler. In "legacy
|
|
63
271
|
# console" mode (old conhost.exe — PowerShell or cmd.exe without
|
|
64
272
|
# Windows Terminal) Alt+key sequences are read in a way that blocks the
|
|
65
273
|
# input loop, freezing the entire REPL.
|
|
66
|
-
#
|
|
67
|
-
# We detect this and switch to single-line (non-Reline) input, which
|
|
68
|
-
# avoids the freeze while keeping colour, our custom prompt, and all
|
|
69
|
-
# pretty_irb commands. Full features (auto-indent, │ continuation, tab
|
|
70
|
-
# completion) work when the user opens the same shell inside Windows
|
|
71
|
-
# Terminal.
|
|
72
274
|
|
|
73
275
|
def tune_input_mode
|
|
74
276
|
return unless Gem.win_platform?
|
|
@@ -82,73 +284,152 @@ module PrettyIRB
|
|
|
82
284
|
if legacy
|
|
83
285
|
IRB.conf[:USE_MULTILINE] = false # avoid Reline Windows hang
|
|
84
286
|
IRB.conf[:USE_AUTOCOMPLETE] = false
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
" full auto-indent and tab-complete support."
|
|
89
|
-
).light_black
|
|
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)
|
|
90
290
|
end
|
|
91
291
|
rescue
|
|
92
292
|
nil
|
|
93
293
|
end
|
|
94
294
|
|
|
95
|
-
# ── Prompt
|
|
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
|
|
96
335
|
|
|
97
336
|
def configure_prompt
|
|
337
|
+
prefix = build_prompt_prefix
|
|
338
|
+
caret_styles = rails_production? ? %i[red bold] : %i[cyan bold]
|
|
339
|
+
IRB.conf[:PROMPT] ||= {}
|
|
98
340
|
IRB.conf[:PROMPT][:PRETTY_IRB] = {
|
|
99
|
-
PROMPT_I: "
|
|
100
|
-
PROMPT_N: "
|
|
101
|
-
PROMPT_S: "
|
|
102
|
-
PROMPT_C: "
|
|
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)}",
|
|
103
345
|
RETURN: "=> %s\n"
|
|
104
346
|
}
|
|
105
347
|
IRB.conf[:PROMPT_MODE] = :PRETTY_IRB
|
|
106
|
-
IRB.conf[:SAVE_HISTORY]
|
|
348
|
+
IRB.conf[:SAVE_HISTORY] ||= 1000
|
|
107
349
|
end
|
|
108
350
|
|
|
109
|
-
|
|
351
|
+
def build_prompt_prefix
|
|
352
|
+
base = style_text("[%03n]", :light_black)
|
|
353
|
+
badge = rails_env_badge
|
|
354
|
+
badge ? "#{base} #{badge}" : base
|
|
355
|
+
end
|
|
110
356
|
|
|
111
|
-
def
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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)
|
|
115
391
|
end
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
392
|
+
IRB.conf[:MAIN_CONTEXT] ||= ctx
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def style_text(text, *styles)
|
|
397
|
+
PrettyIRB::UI.paint(text, *styles)
|
|
119
398
|
end
|
|
120
399
|
|
|
121
|
-
# ──
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
|
125
409
|
|
|
126
410
|
def install_eval_hook
|
|
127
|
-
IRB::Context.
|
|
128
|
-
def evaluate(code, line_no, *args, **kwargs, &block)
|
|
129
|
-
raw = code.is_a?(String) ? code
|
|
130
|
-
: code.respond_to?(:code) ? code.code.to_s
|
|
131
|
-
: nil
|
|
411
|
+
return if IRB::Context.ancestors.include?(EvalHook)
|
|
132
412
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
end
|
|
138
|
-
end
|
|
413
|
+
IRB::Context.prepend(EvalHook)
|
|
414
|
+
rescue => e
|
|
415
|
+
warn style_text(" pretty_irb: eval hook skipped (#{e.message})", :light_black)
|
|
416
|
+
end
|
|
139
417
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
super(code, line_no)
|
|
144
|
-
end
|
|
418
|
+
def install_exception_hook
|
|
419
|
+
return unless IRB::Irb.instance_methods.include?(:handle_exception)
|
|
420
|
+
return if IRB::Irb.ancestors.include?(ExceptionHook)
|
|
145
421
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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)
|
|
150
431
|
rescue => e
|
|
151
|
-
warn " pretty_irb:
|
|
432
|
+
warn style_text(" pretty_irb: rails init hook skipped (#{e.message})", :light_black)
|
|
152
433
|
end
|
|
153
434
|
end
|
|
154
435
|
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require "colorize"
|
|
2
|
+
|
|
3
|
+
module PrettyIRB
|
|
4
|
+
# Shared ANSI-aware terminal layout and styling utilities.
|
|
5
|
+
module UI
|
|
6
|
+
ANSI_REGEX = /\e\[[0-9;]*m/.freeze
|
|
7
|
+
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def colorize?
|
|
11
|
+
return true unless defined?(PrettyIRB) && PrettyIRB.respond_to?(:configuration)
|
|
12
|
+
|
|
13
|
+
PrettyIRB.configuration.colorize
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def strip_ansi(text)
|
|
17
|
+
text.to_s.gsub(ANSI_REGEX, "")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def visible_length(text)
|
|
21
|
+
strip_ansi(text).length
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def pad_right(text, width)
|
|
25
|
+
padding = [width - visible_length(text), 0].max
|
|
26
|
+
"#{text}#{' ' * padding}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def paint(text, *styles)
|
|
30
|
+
str = text.to_s
|
|
31
|
+
return str unless colorize?
|
|
32
|
+
|
|
33
|
+
styles.reduce(str) { |acc, style| acc.public_send(style) }
|
|
34
|
+
rescue
|
|
35
|
+
text.to_s
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def rule(width = 62, char: "─")
|
|
39
|
+
paint(char * width, :light_black)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def badge(label, color: :cyan, bold: false)
|
|
43
|
+
styles = bold ? [color, :bold] : [color]
|
|
44
|
+
paint("[#{label}]", *styles)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def table_row(left, right, left_width: 34, indent: 4)
|
|
48
|
+
prefix = " " * indent
|
|
49
|
+
"#{prefix}#{pad_right(left, left_width)}#{right}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Renders a rounded box with ANSI-safe width calculation and optional title.
|
|
53
|
+
def box(lines, title: nil, min_width: 60, padding_x: 2)
|
|
54
|
+
content_lines = Array(lines).map { |line| line == :divider ? :divider : line.to_s }
|
|
55
|
+
text_lines = content_lines.reject { |line| line == :divider }
|
|
56
|
+
max_line_w = text_lines.map { |line| visible_length(line) }.max || 0
|
|
57
|
+
title_w = title ? visible_length(title) + 4 : 0
|
|
58
|
+
inner_width = [min_width, max_line_w + (padding_x * 2), title_w].max
|
|
59
|
+
|
|
60
|
+
top_border = build_top_border(inner_width, title)
|
|
61
|
+
bottom_border = paint("╰#{'─' * inner_width}╯", :light_black)
|
|
62
|
+
pad_str = " " * padding_x
|
|
63
|
+
v_bar = paint("│", :light_black)
|
|
64
|
+
|
|
65
|
+
body = content_lines.map do |line|
|
|
66
|
+
if line == :divider
|
|
67
|
+
paint("├#{'─' * inner_width}┤", :light_black)
|
|
68
|
+
else
|
|
69
|
+
padded = pad_right(line, inner_width - (padding_x * 2))
|
|
70
|
+
"#{v_bar}#{pad_str}#{padded}#{pad_str}#{v_bar}"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
([top_border] + body + [bottom_border]).join("\n")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def build_top_border(inner_width, title)
|
|
78
|
+
if title
|
|
79
|
+
label = " #{title} "
|
|
80
|
+
remaining = [inner_width - 1 - visible_length(label), 1].max
|
|
81
|
+
"#{paint('╭─', :light_black)}#{label}#{paint("#{'─' * remaining}╮", :light_black)}"
|
|
82
|
+
else
|
|
83
|
+
paint("╭#{'─' * inner_width}╮", :light_black)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
private_class_method :build_top_border
|
|
87
|
+
end
|
|
88
|
+
end
|
data/lib/pretty_irb/version.rb
CHANGED
data/lib/pretty_irb.rb
CHANGED
|
@@ -5,6 +5,8 @@ require "did_you_mean"
|
|
|
5
5
|
require "reline"
|
|
6
6
|
|
|
7
7
|
require_relative "pretty_irb/version"
|
|
8
|
+
require_relative "pretty_irb/configuration"
|
|
9
|
+
require_relative "pretty_irb/ui"
|
|
8
10
|
require_relative "pretty_irb/formatter"
|
|
9
11
|
require_relative "pretty_irb/auto_corrector"
|
|
10
12
|
require_relative "pretty_irb/ai_helper"
|
|
@@ -15,11 +17,25 @@ require_relative "pretty_irb/benchmarker"
|
|
|
15
17
|
require_relative "pretty_irb/cheat_sheet"
|
|
16
18
|
require_relative "pretty_irb/irb_commands"
|
|
17
19
|
require_relative "pretty_irb/shell"
|
|
20
|
+
require_relative "pretty_irb/railtie" if defined?(Rails::Railtie)
|
|
18
21
|
|
|
19
22
|
module PrettyIRB
|
|
20
23
|
class Error < StandardError; end
|
|
21
24
|
|
|
22
25
|
class << self
|
|
26
|
+
def configuration
|
|
27
|
+
@configuration ||= Configuration.new
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def configure
|
|
31
|
+
yield(configuration)
|
|
32
|
+
configuration
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def reset_configuration!
|
|
36
|
+
@configuration = Configuration.new
|
|
37
|
+
end
|
|
38
|
+
|
|
23
39
|
def start(config = {})
|
|
24
40
|
Shell.new(config).run
|
|
25
41
|
end
|
data/pretty_irb.gemspec
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
require_relative "lib/pretty_irb/version"
|
|
2
|
+
|
|
1
3
|
Gem::Specification.new do |spec|
|
|
2
4
|
spec.name = "pretty_irb"
|
|
3
|
-
spec.version =
|
|
5
|
+
spec.version = PrettyIRB::VERSION
|
|
4
6
|
spec.authors = ["Jayesh"]
|
|
5
7
|
spec.email = ["jayesh@example.com"]
|
|
6
8
|
spec.summary = "A pretty IRB replacement with auto-correct and beautiful fonts"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pretty_irb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jayesh
|
|
@@ -185,11 +185,14 @@ files:
|
|
|
185
185
|
- lib/pretty_irb/auto_corrector.rb
|
|
186
186
|
- lib/pretty_irb/benchmarker.rb
|
|
187
187
|
- lib/pretty_irb/cheat_sheet.rb
|
|
188
|
+
- lib/pretty_irb/configuration.rb
|
|
188
189
|
- lib/pretty_irb/formatter.rb
|
|
189
190
|
- lib/pretty_irb/history_manager.rb
|
|
190
191
|
- lib/pretty_irb/irb_commands.rb
|
|
192
|
+
- lib/pretty_irb/railtie.rb
|
|
191
193
|
- lib/pretty_irb/shell.rb
|
|
192
194
|
- lib/pretty_irb/snippet_manager.rb
|
|
195
|
+
- lib/pretty_irb/ui.rb
|
|
193
196
|
- lib/pretty_irb/variable_inspector.rb
|
|
194
197
|
- lib/pretty_irb/version.rb
|
|
195
198
|
- pretty_irb.gemspec
|