ruby-shell 3.5.0 → 3.6.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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/bin/rsh +146 -87
  3. metadata +5 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bac3cf4ea368be8c2991ee650e1977c7fafd7b1548645c1e3daaa94fd29bae2d
4
- data.tar.gz: 31b8ae6ffcf460c7a236c72c8188254243879ab8b83ac690fcf8e61fc0aa46ad
3
+ metadata.gz: c1a7a1c2414582ba14f71073684dc8046d52ba9bc45bf9dcc8e333ba4a99cdfe
4
+ data.tar.gz: 7a41cf6ed38b56e4e706e42712a59adfd0ac35fb4b60329a37bfa30d40b77abc
5
5
  SHA512:
6
- metadata.gz: bb8eb4cef6df5403bc751cf29d440a4289877fd9f7684f153e82196a3f24e425f88fabbe2ccae63c0ef108cae205feb241e84b52f138ad07975d03bd38834a7b
7
- data.tar.gz: 96eb9e448ffd6c20b469e1b0d0f11e7f8dc274e809c4cc543dea3aa7ff5b5a5121a1aae6de7522cb3af1b3ff59e8e655df7af68ed54322f96844c5df910b4904
6
+ metadata.gz: d6c26b7a0654f7f1dc37a55f42b6f5721ab7493838344e9bedbcb65fd023025b89b2448eae56a7dc5e907aa71ede64644b4636779d89f2ef991c1dfedf22728b
7
+ data.tar.gz: 03cd96dfeadcc4f6b3ee39db49ada250e1c4f301616269bb99f5940f672548c2d16f35631044fc137f483db57833c0111684443ac2aa39a1f7a4c4b1131934ba
data/bin/rsh CHANGED
@@ -8,7 +8,7 @@
8
8
  # Web_site: http://isene.com/
9
9
  # Github: https://github.com/isene/rsh
10
10
  # License: Public domain
11
- @version = "3.5.0" # Polish release: Nick/history export, default nicks, expanded completions, startup tips, validation templates
11
+ @version = "3.6.0" # Multi-line prompt support: Complete readline refactor with rcurses-inspired ANSI handling
12
12
 
13
13
  # MODULES, CLASSES AND EXTENSIONS
14
14
  class String # Add coloring to strings (with escaping for Readline)
@@ -185,67 +185,44 @@ end
185
185
  # HELP TEXT
186
186
  @info = <<~INFO
187
187
 
188
- Hello #{@user}, welcome to rsh v3.3 - the Ruby SHell.
189
-
190
- rsh does not attempt to compete with the grand old shells like bash and zsh.
191
- It serves the specific needs and wants of its author. If you like it, then feel free
192
- to ask for more or different features here: https://github.com/isene/rsh.
193
-
194
- Core Features:
195
- * Aliases (called nicks in rsh) - both for commands and general nicks
196
- * Syntax highlighting for nicks, bookmarks, commands, switches and valid dirs/files
197
- * Tab completions for nicks, system commands, command switches and dirs/files
198
- * Smart context-aware tab completion for git, apt, docker, systemctl, cargo, npm, gem, bundle
199
- * History with editing, search and repeat (use `!` or UP arrow)
200
- * Auto-suggestions from history (press RIGHT arrow to accept)
201
- * Ruby functions callable as shell commands (persistent across sessions)
202
- * AI integration: Use @ for text responses and @@ for command suggestions
203
-
204
- NEW in v3.0:
205
- * Command analytics - :stats shows usage patterns and performance metrics
206
- * Enhanced bookmarks with tags - :bm "name path #tag1,tag2" then just type name to jump
207
- * Session management - :save_session and :load_session preserve your entire shell state
208
- * Smart typo detection - "Did you mean...?" suggestions for misspelled commands
209
- * Switch caching - Faster TAB completion for command options
210
- * Option value completion - TAB complete values like --format=json
211
- * Syntax validation - Pre-execution warnings for dangerous or malformed commands
212
- * Unified command syntax - :nick, :gnick, :bm all work the same way (list/create/delete)
213
-
214
- NEW in v3.4:
215
- * Completion learning - Shell learns which TAB completions you use and ranks them higher
216
- * Context-aware - Separate learning for each command (git, ls, docker, etc.)
217
- * :completion_stats - View learned patterns with visual bar charts
218
- * Persistent - Learning data saves to .rshrc across sessions
219
-
220
- v3.3 Features:
221
- * Quote-less syntax - No more quotes! Use :nick la = ls -la
222
- * Parametrized nicks - :nick gp = git push origin {{branch}}, then: gp branch=main
223
- * Ctrl-G multi-line edit - Press Ctrl-G to edit command in $EDITOR
224
- * Custom validation - :validate rm -rf / = block prevents dangerous commands
225
- * Shell script support - for/while/if loops work with full bash syntax
226
-
227
- v3.2 Features:
228
- * Plugin system - Extensible architecture for custom commands, completions, and hooks
229
- * Auto-correct typos - :config auto_correct on (with confirmation prompt)
230
- * Command timing alerts - :config slow_command_threshold 5 warns on slow commands
231
- * Inline calculator - :calc 2 + 2, :calc "Math::PI", full Ruby Math library
232
- * Enhanced history - !!, !-2, !5:7 for repeat last, nth-to-last, and chaining
233
- * Stats visualization - :stats --graph for colorful ASCII bar charts
234
-
235
- v3.1 Features:
236
- * Multiple named sessions - :save_session "project" and :load_session "project"
237
- * Stats export - :stats --csv or :stats --json for data analysis
238
- * Session auto-save - Set @session_autosave = 300 in .rshrc for 5-min auto-save
239
- * Bookmark import/export - :bm --export file.json and :bm --import file.json
240
- * Bookmark statistics - :bm --stats shows usage patterns and tag distribution
241
- * Color themes - :theme solarized|dracula|gruvbox|nord|monokai
242
- * Config management - :config shows/sets history_dedup, session_autosave, etc.
243
- * Environment management - :env for listing/setting/exporting environment variables
244
-
245
- Config file (.rshrc) updates on exit (Ctrl-d) or not (Ctrl-e).
246
- All colors are themeable in .rshrc (see github link for possibilities).
247
-
248
- Use `:help` for complete command reference.
188
+ Hello #{@user}, welcome to rsh v3.6 - the Ruby SHell.
189
+
190
+ rsh is a feature-rich shell written in pure Ruby, designed for productivity and customization.
191
+
192
+ KEY FEATURES:
193
+ • Nicks (aliases) with {{parameters}} - :nick gp = git push origin {{branch}}
194
+ Completion learning - Shell learns and adapts to your patterns
195
+ Plugin system - Extend with custom commands and hooks
196
+ Command recording/replay - Automate workflows
197
+ Sessions & bookmarks - Save and restore your workspace
198
+ Multi-line prompts - Full support for prompts with newlines
199
+ Stats & analytics - :stats shows usage patterns and performance
200
+ Validation rules - :validate rm -rf / = block for safety
201
+ 6 color themes - :theme solarized|dracula|gruvbox|nord|monokai|default
202
+ Auto-correct - Smart typo detection with suggestions
203
+ • Shell scripts - Full bash support (for, while, if loops)
204
+ AI integration - @ for questions, @@ for command suggestions
205
+ Ruby functions - :defun for custom shell commands
206
+ 18 tool completions - git, docker, kubectl, terraform, aws, etc.
207
+
208
+ QUICK START:
209
+ :help - Command reference
210
+ :info --features - Feature list
211
+ :nick - Create alias
212
+ :bm - Bookmark directory
213
+ • :stats - View analytics
214
+ :theme - Change colors
215
+ :plugins - Extend functionality
216
+
217
+ CUSTOMIZATION:
218
+ Config file: ~/.rshrc (edit with :config or manually)
219
+ • Colors: All themeable (see :help)
220
+ Exit: Ctrl-D (save) or Ctrl-E (don't save)
221
+
222
+ MORE INFO:
223
+ GitHub: https://github.com/isene/rsh
224
+ Docs: Type :help for quick reference
225
+ Version: Type :version for current/latest
249
226
 
250
227
  INFO
251
228
 
@@ -356,19 +333,73 @@ def getstr # A custom Readline-like function
356
333
  @pos = @ai_suggestion.length
357
334
  @ai_suggestion = nil
358
335
  end
336
+
337
+ # Print prompt ONCE at start (optimization + multi-line support)
359
338
  @row0, p = @c.pos
339
+
340
+ # Clear any previous prompt
341
+ @c.row(@row0)
342
+ @c.clear_screen_down
343
+
344
+ print @display_prompt
345
+
346
+ # Calculate positions and save prompt parts
347
+ if @display_prompt.include?("\n")
348
+ # Multi-line prompt
349
+ newline_count = @display_prompt.count("\n")
350
+ @row0 += newline_count
351
+
352
+ # Extract last line and reapply color codes (rcurses pattern)
353
+ # Detect ANY color codes at start of full prompt
354
+ color_codes = ""
355
+ if @display_prompt =~ /^(\001\e\[[0-9;]+m\002)/
356
+ color_codes = $1
357
+ end
358
+
359
+ # Split and get last line
360
+ lines = @display_prompt.split("\n")
361
+ @prompt_last_line = lines.last
362
+
363
+ # If original had colors and last line doesn't, reapply them
364
+ if !color_codes.empty? && !@prompt_last_line.start_with?("\001")
365
+ @prompt_last_line = color_codes + @prompt_last_line + "\001\e[0m\002"
366
+ end
367
+
368
+ # Calculate visible length (strip ANSI for counting)
369
+ @pos0 = @prompt_last_line.gsub(/\001?\e\[[0-9;]*m\002?/, '').length
370
+ @cmd_row = @row0
371
+ @is_multiline = true
372
+ else
373
+ # Single-line prompt
374
+ row, @pos0 = @c.pos
375
+ @prompt_last_line = @display_prompt
376
+ @cmd_row = @row0
377
+ @is_multiline = false
378
+ end
379
+
360
380
  while chr != "ENTER" # Keep going with readline until user presses ENTER
361
381
  @ci = nil
362
382
  lift = false
363
383
  right = false
364
- # The actual printing og the command line
365
- @c.row(@row0)
366
- @c.clear_line
367
- print @display_prompt
368
- @c.clear_screen_down
369
- row, @pos0 = @c.pos
370
- #@history[0] = "" if @history[0].nil?
384
+
385
+ # Update command line display (not the prompt!)
386
+ @c.row(@cmd_row)
387
+
388
+ # Reprint the prompt line (preserves colors)
389
+ if @is_multiline
390
+ @c.col(1)
391
+ print @prompt_last_line
392
+ @c.clear_line_after
393
+ else
394
+ @c.col(1)
395
+ print @prompt_last_line
396
+ @c.clear_line_after
397
+ end
398
+
399
+ # Print command text with syntax highlighting
371
400
  print cmd_check(@history[0])
401
+
402
+ # Print history suggestion if available
372
403
  @ci = @history[1..].find_index {|e| e =~ /^#{Regexp.escape(@history[0].to_s)}./}
373
404
  unless @ci == nil
374
405
  @ci += 1
@@ -378,15 +409,17 @@ def getstr # A custom Readline-like function
378
409
  print @ciprompt.c(@c_stamp)
379
410
  right = true
380
411
  end
381
- c_col = @pos0 + @pos
382
- c_row = @row0 + c_col/(@maxcol)
383
- c_col == 0 ? @c.row(c_row + 1) : @c.row(c_row)
384
- if c_col.modulo(@maxcol) == 0
385
- @c.col(c_col)
386
- @c.row(@c.rowget - 1)
412
+
413
+ # Position cursor (different logic for single vs multi-line)
414
+ if @is_multiline
415
+ # Multi-line: @pos0 is string length (0-indexed), need +1 for terminal
416
+ cursor_col = @pos0 + @pos + 1
387
417
  else
388
- @c.col(c_col.modulo(@maxcol))
418
+ # Single-line: @pos0 already from @c.pos (1-indexed)
419
+ cursor_col = @pos0 + @pos
389
420
  end
421
+ @c.row(@cmd_row)
422
+ @c.col(cursor_col)
390
423
  chr = getchr
391
424
  puts "DEBUG: Got char: '#{chr}' (length: #{chr.length})" if ENV['RSH_DEBUG']
392
425
  case chr
@@ -416,8 +449,21 @@ def getstr # A custom Readline-like function
416
449
  exit
417
450
  when 'C-L' # Clear screen and set position to top of the screen
418
451
  @c.row(1)
419
- @row0 = 1
420
452
  @c.clear_screen_down
453
+ # Reprint prompt after clear
454
+ @row0 = 1
455
+ print @display_prompt
456
+ # Recalculate positions
457
+ if @display_prompt.include?("\n")
458
+ newline_count = @display_prompt.count("\n")
459
+ @row0 += newline_count
460
+ last_line = @display_prompt.split("\n").last
461
+ @pos0 = last_line.gsub(/\001?\e\[[0-9;]*m\002?/, '').length
462
+ @cmd_row = @row0
463
+ else
464
+ row, @pos0 = @c.pos
465
+ @cmd_row = @row0
466
+ end
421
467
  when 'UP' # Go up in history
422
468
  if @stk == 0 and @history[0].length > 0
423
469
  @tabsearch = @history[0]
@@ -436,8 +482,6 @@ def getstr # A custom Readline-like function
436
482
  end
437
483
  lift = false
438
484
  end
439
- @c.row(@row0)
440
- @c.clear_screen_down
441
485
  when 'DOWN' # Go down in history
442
486
  if lift
443
487
  @history.unshift("")
@@ -457,8 +501,6 @@ def getstr # A custom Readline-like function
457
501
  @pos = @history[0].length
458
502
  end
459
503
  lift = false
460
- @c.row(@row0)
461
- @c.clear_screen_down
462
504
  when 'RIGHT' # Move right on the readline
463
505
  if right
464
506
  if lift
@@ -633,10 +675,17 @@ def tab(type)
633
675
  completed = @tabstr + "/"
634
676
  @history[0] = @pretab + completed + @postab
635
677
  @pos = @pretab.length + completed.length
636
- @c_col = @pos0 + @pos
678
+ # Consistent cursor calc
679
+ if @is_multiline
680
+ @c_col = @pos0 + @pos + 1
681
+ else
682
+ @c_col = @pos0 + @pos
683
+ end
637
684
  @c.clear_line
638
685
  line_display = cmd_check(@history[0]).to_s
639
- print @display_prompt + line_display
686
+ # Use last line only for multi-line prompts
687
+ prompt_display = @is_multiline ? @display_prompt.split("\n").last : @display_prompt
688
+ print prompt_display + line_display
640
689
  @c.col(@c_col)
641
690
  return
642
691
  end
@@ -851,6 +900,9 @@ def tab(type)
851
900
  @newhist0 = @pretab + tabchoice + @postab # Remember now the new value to be given to @history[0]
852
901
  line1 = cmd_check(@pretab).to_s # Syntax highlight before @tabstr
853
902
  line2 = cmd_check(@postab).to_s # Syntax highlight after @tabstr
903
+
904
+ # For multi-line prompts, only show last line during tab completion
905
+ prompt_for_tab = @display_prompt.include?("\n") ? @display_prompt.split("\n").last : @display_prompt
854
906
  # Color and underline the current tabchoice on the commandline with file type color:
855
907
  display_choice = tabchoice.dup
856
908
  clean_choice = tabchoice.gsub(/['"]/, '').chomp('/')
@@ -861,9 +913,14 @@ def tab(type)
861
913
  # Escape regex special characters in @tabstr for pattern matching
862
914
  escaped_tabstr = Regexp.escape(@tabstr)
863
915
  tabline = display_choice.sub(/(.*)#{escaped_tabstr}(.*)/, '\1'.c(choice_color) + @tabstr.u.c(choice_color) + '\2'.c(choice_color))
864
- print @display_prompt + line1 + tabline + line2 # Print the commandline
916
+ print prompt_for_tab + line1 + tabline + line2 # Print the commandline (use last line only for multi-line)
865
917
  @pos = @pretab.length.to_i + tabchoice.length.to_i # Set the position on that commandline
866
- @c_col = @pos0 + @pos # The cursor position must include the prompt as well
918
+ # Consistent cursor positioning
919
+ if @is_multiline
920
+ @c_col = @pos0 + @pos + 1 # Multi-line: string length needs +1
921
+ else
922
+ @c_col = @pos0 + @pos # Single-line: already 1-indexed
923
+ end
867
924
  @c.col(@c_col) # Set the cursor position
868
925
  nextline # Then start showing the completion items
869
926
  tabline = @tabarray[i] # Get the next matching tabline
@@ -3541,9 +3598,11 @@ loop do
3541
3598
  @cmd = hi if hi
3542
3599
  end
3543
3600
  # Move cursor to end of line and print the full command before clearing
3544
- @c.row(@row0)
3601
+ @c.row(@cmd_row || @row0) # Use cmd_row if set (multi-line)
3545
3602
  @c.clear_line
3546
- print @display_prompt + cmd_check(@cmd)
3603
+ # For multi-line, only print last line; for single-line, print full prompt
3604
+ prompt_to_print = (@display_prompt.include?("\n") && @prompt_last_line) ? @prompt_last_line : @display_prompt
3605
+ print prompt_to_print + cmd_check(@cmd)
3547
3606
  print "\n"; @c.clear_screen_down
3548
3607
  if @cmd == "r" # Integration with rtfm (https://github.com/isene/RTFM)
3549
3608
  t = Time.now
metadata CHANGED
@@ -1,20 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-shell
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.5.0
4
+ version: 3.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-10-30 00:00:00.000000000 Z
11
+ date: 2025-10-31 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'A shell written in Ruby with extensive tab completions, aliases/nicks,
14
14
  history, syntax highlighting, theming, auto-cd, auto-opening files and more. UPDATE
15
- v3.5.0: Nick/history export, 9 new tool completions (kubectl, terraform, aws, brew,
16
- etc.), validation templates, startup tips, improved :info, aggressive suggest_command
17
- optimization for huge PATHs (18K+ executables), timestamp shows corrected commands!'
15
+ v3.6.0: MULTI-LINE PROMPT SUPPORT - Complete readline refactor with rcurses-inspired
16
+ ANSI handling. Define prompts with newlines! Plus nick/history export, 9 new completions,
17
+ validation templates, startup tips, and critical performance fixes for huge PATHs!'
18
18
  email: g@isene.com
19
19
  executables:
20
20
  - rsh