rubish-gem 0.0.1

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 (45) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +23 -0
  3. data/Dockerfile +54 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +39 -0
  6. data/Rakefile +12 -0
  7. data/lib/rubish/arithmetic.rb +140 -0
  8. data/lib/rubish/ast.rb +168 -0
  9. data/lib/rubish/builtins/arithmetic.rb +129 -0
  10. data/lib/rubish/builtins/bind_readline.rb +834 -0
  11. data/lib/rubish/builtins/directory_stack.rb +182 -0
  12. data/lib/rubish/builtins/echo_printf.rb +510 -0
  13. data/lib/rubish/builtins/hash_directories.rb +260 -0
  14. data/lib/rubish/builtins/read.rb +299 -0
  15. data/lib/rubish/builtins/trap.rb +324 -0
  16. data/lib/rubish/codegen.rb +1273 -0
  17. data/lib/rubish/completion.rb +840 -0
  18. data/lib/rubish/completions/bash_helpers.rb +530 -0
  19. data/lib/rubish/completions/git.rb +431 -0
  20. data/lib/rubish/completions/help_parser.rb +453 -0
  21. data/lib/rubish/completions/ssh.rb +114 -0
  22. data/lib/rubish/config.rb +267 -0
  23. data/lib/rubish/data/builtin_help.rb +716 -0
  24. data/lib/rubish/data/completion_data.rb +53 -0
  25. data/lib/rubish/data/readline_config.rb +47 -0
  26. data/lib/rubish/data/shell_options.rb +251 -0
  27. data/lib/rubish/data_define.rb +65 -0
  28. data/lib/rubish/execution_context.rb +1124 -0
  29. data/lib/rubish/expansion.rb +988 -0
  30. data/lib/rubish/history.rb +663 -0
  31. data/lib/rubish/lazy_loader.rb +127 -0
  32. data/lib/rubish/lexer.rb +1194 -0
  33. data/lib/rubish/parser.rb +1167 -0
  34. data/lib/rubish/prompt.rb +766 -0
  35. data/lib/rubish/repl.rb +2267 -0
  36. data/lib/rubish/runtime/builtins.rb +7222 -0
  37. data/lib/rubish/runtime/command.rb +1153 -0
  38. data/lib/rubish/runtime/job.rb +153 -0
  39. data/lib/rubish/runtime.rb +1169 -0
  40. data/lib/rubish/shell_state.rb +241 -0
  41. data/lib/rubish/startup_profiler.rb +67 -0
  42. data/lib/rubish/version.rb +5 -0
  43. data/lib/rubish.rb +60 -0
  44. data/sig/rubish.rbs +4 -0
  45. metadata +85 -0
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubish
4
+ module Builtins
5
+ # Git commands for completion
6
+ GIT_COMMANDS = %w[
7
+ add am annotate archive bisect blame branch bundle cat-file
8
+ check-attr check-ignore check-mailmap checkout checkout-index
9
+ cherry cherry-pick citool clean clone column commit config count-objects
10
+ credential daemon describe diff diff-files diff-index diff-tree
11
+ difftool fast-export fast-import fetch fetch-pack filter-branch
12
+ fmt-merge-msg for-each-ref format-patch fsck gc get-tar-commit-id
13
+ grep gui hash-object help imap-send index-pack init init-db instaweb
14
+ interpret-trailers log ls-files ls-remote ls-tree mailinfo mailsplit
15
+ maintenance merge merge-base merge-file merge-index merge-octopus
16
+ merge-one-file merge-ours merge-recursive merge-resolve merge-subtree
17
+ merge-tree mergetool mktag mktree mv name-rev notes pack-objects
18
+ pack-redundant pack-refs patch-id prune prune-packed pull push
19
+ quiltimport range-diff read-tree rebase reflog remote repack replace
20
+ request-pull rerere reset restore rev-list rev-parse revert rm
21
+ send-email send-pack shortlog show show-branch show-index show-ref
22
+ sparse-checkout stash status stripspace submodule switch symbolic-ref
23
+ tag unpack-file unpack-objects update-index update-ref update-server-info
24
+ upload-archive upload-pack var verify-commit verify-pack verify-tag
25
+ whatchanged worktree write-tree
26
+ ].freeze
27
+
28
+ # Common git options that apply to most commands
29
+ GIT_COMMON_OPTIONS = %w[
30
+ --version --help -C --git-dir --work-tree --bare --no-replace-objects
31
+ --literal-pathspecs --glob-pathspecs --noglob-pathspecs --icase-pathspecs
32
+ --no-optional-locks -c --exec-path --html-path --man-path --info-path
33
+ --paginate --no-pager --config-env
34
+ ].freeze
35
+
36
+ # Action flags mapping for complete/compgen builtins
37
+ # Maps single-letter flags to their action symbols
38
+ COMPLETION_ACTION_FLAGS = {
39
+ 'a' => :alias,
40
+ 'b' => :builtin,
41
+ 'c' => :command,
42
+ 'd' => :directory,
43
+ 'e' => :export,
44
+ 'f' => :file,
45
+ 'g' => :group,
46
+ 'j' => :job,
47
+ 'k' => :keyword,
48
+ 's' => :service,
49
+ 'u' => :user,
50
+ 'v' => :variable
51
+ }.freeze
52
+ end
53
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubish
4
+ module Builtins
5
+ # Readline function names for -l option
6
+ READLINE_FUNCTIONS = %w[
7
+ abort accept-line backward-char backward-delete-char backward-kill-line
8
+ backward-kill-word backward-word beginning-of-history beginning-of-line
9
+ call-last-kbd-macro capitalize-word character-search character-search-backward
10
+ clear-screen complete delete-char delete-horizontal-space digit-argument
11
+ do-lowercase-version downcase-word dump-functions dump-macros dump-variables
12
+ emacs-editing-mode end-of-history end-of-line exchange-point-and-mark
13
+ forward-backward-delete-char forward-char forward-search-history forward-word
14
+ history-search-backward history-search-forward insert-comment insert-completions
15
+ kill-line kill-region kill-whole-line kill-word menu-complete menu-complete-backward
16
+ next-history non-incremental-forward-search-history non-incremental-reverse-search-history
17
+ overwrite-mode possible-completions previous-history quoted-insert
18
+ re-read-init-file redraw-current-line reverse-search-history revert-line
19
+ self-insert set-mark shell-backward-kill-word shell-backward-word shell-expand-line
20
+ shell-forward-word shell-kill-word start-kbd-macro tilde-expand transpose-chars
21
+ transpose-words undo universal-argument unix-filename-rubout unix-line-discard
22
+ unix-word-rubout upcase-word vi-append-eol vi-append-mode vi-arg-digit
23
+ vi-bWord vi-back-to-indent vi-bword vi-change-case vi-change-char vi-change-to
24
+ vi-char-search vi-column vi-complete vi-delete vi-delete-to vi-eWord vi-editing-mode
25
+ vi-end-word vi-eof-maybe vi-eword vi-fWord vi-fetch-history vi-first-print
26
+ vi-fword vi-goto-mark vi-insert-beg vi-insertion-mode vi-match vi-movement-mode
27
+ vi-next-word vi-overstrike vi-overstrike-delete vi-prev-word vi-put vi-redo
28
+ vi-replace vi-rubout vi-search vi-search-again vi-set-mark vi-subst vi-tilde-expand
29
+ vi-undo vi-yank-arg vi-yank-to yank yank-last-arg yank-nth-arg yank-pop
30
+ ].freeze
31
+
32
+ # Readline variable names for -v/-V options
33
+ READLINE_VARIABLES_LIST = %w[
34
+ bell-style bind-tty-special-chars blink-matching-paren colored-completion-prefix
35
+ colored-stats comment-begin completion-display-width completion-ignore-case
36
+ completion-map-case completion-prefix-display-length completion-query-items
37
+ convert-meta disable-completion echo-control-characters editing-mode
38
+ emacs-mode-string enable-bracketed-paste enable-keypad expand-tilde
39
+ history-preserve-point history-size horizontal-scroll-mode input-meta
40
+ isearch-terminators keymap keyseq-timeout mark-directories mark-modified-lines
41
+ mark-symlinked-directories match-hidden-files menu-complete-display-prefix
42
+ output-meta page-completions print-completions-horizontally revert-all-at-newline
43
+ show-all-if-ambiguous show-all-if-unmodified show-mode-in-prompt skip-completed-text
44
+ vi-cmd-mode-string vi-ins-mode-string visible-stats
45
+ ].freeze
46
+ end
47
+ end
@@ -0,0 +1,251 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubish
4
+ module Builtins
5
+ # Valid shell options with their default values and descriptions
6
+ SHELL_OPTIONS = {
7
+ 'array_expand_once' => [false, 'only expand array subscripts once (bash 5.2+)'],
8
+ 'assoc_expand_once' => [false, 'only expand associative array subscripts once (deprecated, use array_expand_once)'],
9
+ 'autocd' => [false, 'cd to a directory when typed as command'],
10
+ 'bash_source_fullpath' => [false, 'store full pathnames in BASH_SOURCE array'],
11
+ 'cdable_vars' => [false, 'cd argument can be a variable containing a directory name'],
12
+ 'cdspell' => [false, 'correct minor spelling errors in cd'],
13
+ 'checkhash' => [false, 'check hash table before executing'],
14
+ 'checkjobs' => [false, 'check for running jobs before exit'],
15
+ 'checkwinsize' => [false, 'check window size after each command and update LINES and COLUMNS'],
16
+ 'cmdhist' => [true, 'save multi-line commands as single history entry'],
17
+ 'compat10' => [false, 'compatibility mode for rubish 1.0'],
18
+ 'compat31' => [false, 'compatibility mode for bash 3.1'],
19
+ 'compat32' => [false, 'compatibility mode for bash 3.2'],
20
+ 'compat40' => [false, 'compatibility mode for bash 4.0'],
21
+ 'compat41' => [false, 'compatibility mode for bash 4.1'],
22
+ 'compat42' => [false, 'compatibility mode for bash 4.2'],
23
+ 'compat43' => [false, 'compatibility mode for bash 4.3'],
24
+ 'compat44' => [false, 'compatibility mode for bash 4.4'],
25
+ 'compat50' => [false, 'compatibility mode for bash 5.0'],
26
+ 'compat51' => [false, 'compatibility mode for bash 5.1'],
27
+ 'compat52' => [false, 'compatibility mode for bash 5.2'],
28
+ 'compat53' => [false, 'compatibility mode for bash 5.3'],
29
+ 'compat54' => [false, 'compatibility mode for bash 5.4'],
30
+ 'compat55' => [false, 'compatibility mode for bash 5.5'],
31
+ 'complete_fullquote' => [true, 'quote all metacharacters in filename completion'],
32
+ 'direxpand' => [false, 'expand directory names during word completion'],
33
+ 'dirspell' => [false, 'correct minor spelling errors in directory names during completion'],
34
+ 'dotglob' => [false, 'include dotfiles in pathname expansion'],
35
+ 'execfail' => [false, 'do not exit non-interactive shell if exec fails'],
36
+ 'expand_aliases' => [true, 'expand aliases'],
37
+ 'extdebug' => [false, 'enable extended debugging mode'],
38
+ 'extglob' => [false, 'enable extended pattern matching'],
39
+ 'extquote' => [true, 'enable $\'...\' and $"..." quoting within ${...}'],
40
+ 'failglob' => [false, 'patterns which fail to match produce an error'],
41
+ 'force_fignore' => [true, 'apply FIGNORE to word completion'],
42
+ 'globasciiranges' => [true, 'use ASCII ordering for range expressions in bracket patterns'],
43
+ 'globskipdots' => [false, 'skip . and .. in pathname expansion'],
44
+ 'globstar' => [false, 'enable ** for recursive globbing'],
45
+ 'gnu_errfmt' => [false, 'print error messages in GNU format'],
46
+ 'histappend' => [false, 'append to history file'],
47
+ 'histreedit' => [false, 'allow re-editing of failed history substitution'],
48
+ 'histverify' => [false, 'verify history substitution before executing'],
49
+ 'hostcomplete' => [true, 'attempt hostname completion'],
50
+ 'huponexit' => [false, 'send SIGHUP to all jobs when an interactive login shell exits'],
51
+ 'inherit_errexit' => [false, 'command substitution inherits the errexit option'],
52
+ 'interactive_comments' => [true, 'allow comments in interactive shell'],
53
+ 'lastpipe' => [false, 'run last command of pipeline in current shell'],
54
+ 'lithist' => [false, 'preserve newlines in multi-line history'],
55
+ 'localvar_inherit' => [false, 'local variables inherit value from previous scope'],
56
+ 'localvar_unset' => [false, 'calling unset on local variable removes it from scope'],
57
+ 'localvar_warning' => [false, 'warn when local variable shadows variable from outer scope'],
58
+ 'login_shell' => [false, 'shell is a login shell (read-only)'],
59
+ 'mailwarn' => [false, 'warn if mail file has been accessed since last check'],
60
+ 'no_empty_cmd_completion' => [false, 'do not complete on empty command line'],
61
+ 'nocaseglob' => [false, 'case-insensitive pathname expansion'],
62
+ 'nocasematch' => [false, 'case-insensitive pattern matching'],
63
+ 'noexpand_translation' => [false, 'do not expand $"..." strings for translation'],
64
+ 'nullglob' => [false, 'patterns matching nothing expand to null'],
65
+ 'patsub_replacement' => [true, 'enable & replacement in pattern substitution'],
66
+ 'progcomp' => [true, 'enable programmable completion'],
67
+ 'progcomp_alias' => [true, 'allow programmable completion for aliases'],
68
+ 'promptvars' => [true, 'expand variables in prompt strings'],
69
+ 'restricted_shell' => [false, 'shell is restricted (read-only)'],
70
+ 'shift_verbose' => [false, 'print error if shift count exceeds positional parameters'],
71
+ 'sourcepath' => [true, 'use PATH to find sourced files'],
72
+ 'syslog_history' => [false, 'send history to syslog'],
73
+ 'varredir_close' => [false, 'close file descriptors opened by {varname} redirection'],
74
+ 'xpg_echo' => [false, 'echo expands backslash-escape sequences']
75
+ }.freeze
76
+
77
+ # Compatibility level options (like bash's compat31, compat32, etc.)
78
+ COMPAT_OPTIONS = %w[compat10 compat31 compat32 compat40 compat41 compat42 compat43 compat44 compat50 compat51 compat52 compat53 compat54 compat55].freeze
79
+
80
+ # Mapping from zsh option names to bash shopt equivalents
81
+ # These options exist in both shells (possibly with different names)
82
+ ZSH_TO_BASH_OPTIONS = {
83
+ 'autocd' => 'autocd',
84
+ 'auto_cd' => 'autocd',
85
+ 'cdablevars' => 'cdable_vars',
86
+ 'cdable_vars' => 'cdable_vars',
87
+ 'globdots' => 'dotglob',
88
+ 'glob_dots' => 'dotglob',
89
+ 'extendedglob' => 'extglob',
90
+ 'extended_glob' => 'extglob',
91
+ 'nullglob' => 'nullglob',
92
+ 'null_glob' => 'nullglob',
93
+ 'globstar' => 'globstar',
94
+ 'glob_star' => 'globstar',
95
+ 'nocaseglob' => 'nocaseglob',
96
+ 'nocase_glob' => 'nocaseglob',
97
+ 'histappend' => 'histappend',
98
+ 'hist_append' => 'histappend',
99
+ 'appendhistory' => 'histappend',
100
+ 'append_history' => 'histappend',
101
+ 'interactivecomments' => 'interactive_comments',
102
+ 'interactive_comments' => 'interactive_comments',
103
+ 'checkjobs' => 'checkjobs',
104
+ 'check_jobs' => 'checkjobs',
105
+ 'pipefail' => 'pipefail',
106
+ 'pipe_fail' => 'pipefail'
107
+ }.freeze
108
+
109
+ # Zsh-specific options (not in bash)
110
+ # Format: option_name => [default_value, description]
111
+ ZSH_OPTIONS = {
112
+ # Changing Directories
113
+ 'auto_pushd' => [false, 'make cd push the old directory onto the directory stack'],
114
+ 'chase_dots' => [false, 'resolve .. in cd to physical directory'],
115
+ 'chase_links' => [false, 'resolve symbolic links in cd to physical directory'],
116
+ 'pushd_ignore_dups' => [false, 'do not push duplicate directories'],
117
+ 'pushd_minus' => [false, 'exchange +/- meanings in pushd'],
118
+ 'pushd_silent' => [false, 'do not print directory stack after pushd/popd'],
119
+ 'pushd_to_home' => [false, 'pushd with no args goes to home'],
120
+
121
+ # Completion
122
+ 'always_to_end' => [false, 'move cursor to end after completion'],
123
+ 'auto_list' => [true, 'automatically list choices on ambiguous completion'],
124
+ 'auto_menu' => [true, 'show completion menu on successive tab'],
125
+ 'auto_param_slash' => [true, 'add trailing slash for completed directories'],
126
+ 'auto_remove_slash' => [true, 'remove trailing slash when next char is word delimiter'],
127
+ 'complete_in_word' => [false, 'complete from both ends of cursor'],
128
+ 'glob_complete' => [false, 'generate matches from glob pattern'],
129
+ 'list_ambiguous' => [true, 'list completions when ambiguous'],
130
+ 'list_packed' => [false, 'variable width completion list'],
131
+ 'list_rows_first' => [false, 'list completions in rows instead of columns'],
132
+ 'list_types' => [true, 'show file type indicator in completion list'],
133
+ 'menu_complete' => [false, 'insert first match immediately on ambiguous completion'],
134
+ 'rec_exact' => [false, 'recognize exact matches even if ambiguous'],
135
+
136
+ # Expansion and Globbing
137
+ 'bad_pattern' => [true, 'print error on bad glob pattern'],
138
+ 'bare_glob_qual' => [true, 'allow glob qualifiers without parentheses'],
139
+ 'brace_ccl' => [false, 'expand {a-z} to a b c ... z'],
140
+ 'case_glob' => [true, 'case-sensitive globbing'],
141
+ 'case_match' => [true, 'case-sensitive pattern matching'],
142
+ 'equals' => [true, 'expand =cmd to path of cmd'],
143
+ 'extended_glob' => [false, 'enable extended glob operators'],
144
+ 'glob' => [true, 'enable globbing'],
145
+ 'glob_subst' => [false, 'treat characters from parameter expansion as glob'],
146
+ 'mark_dirs' => [false, 'append / to directories from globbing'],
147
+ 'multibyte' => [true, 'respect multibyte characters'],
148
+ 'nomatch' => [true, 'print error if glob has no matches'],
149
+ 'numeric_glob_sort' => [false, 'sort numerically when globbing'],
150
+ 'rc_expand_param' => [false, 'array expansion like rc shell'],
151
+ 'rematch_pcre' => [false, 'use PCRE for =~ operator'],
152
+ 'sh_glob' => [false, 'disable special glob characters'],
153
+ 'unset' => [false, 'treat unset variables as empty instead of error'],
154
+ 'warn_create_global' => [false, 'warn when creating global in function'],
155
+
156
+ # History
157
+ 'bang_hist' => [true, 'enable ! history expansion'],
158
+ 'extended_history' => [false, 'save timestamp in history'],
159
+ 'hist_expire_dups_first' => [false, 'expire duplicate entries first'],
160
+ 'hist_find_no_dups' => [false, 'skip duplicates when searching history'],
161
+ 'hist_ignore_all_dups' => [false, 'remove older duplicate entries'],
162
+ 'hist_ignore_dups' => [false, 'do not record consecutive duplicates'],
163
+ 'hist_ignore_space' => [false, 'do not record entries starting with space'],
164
+ 'hist_no_functions' => [false, 'do not record function definitions'],
165
+ 'hist_no_store' => [false, 'do not record history command'],
166
+ 'hist_reduce_blanks' => [false, 'remove superfluous blanks'],
167
+ 'hist_save_no_dups' => [false, 'do not save duplicates to history file'],
168
+ 'hist_verify' => [false, 'show history expansion before executing'],
169
+ 'inc_append_history' => [false, 'append incrementally to history file'],
170
+ 'share_history' => [false, 'share history between sessions'],
171
+
172
+ # Input/Output
173
+ 'aliases' => [true, 'enable aliases'],
174
+ 'clobber' => [true, 'allow > to overwrite existing files'],
175
+ 'correct' => [false, 'try to correct spelling of commands'],
176
+ 'correct_all' => [false, 'try to correct spelling of all arguments'],
177
+ 'flow_control' => [true, 'enable ^S/^Q flow control'],
178
+ 'ignore_eof' => [false, 'do not exit on end-of-file'],
179
+ 'hash_cmds' => [true, 'hash command locations'],
180
+ 'hash_dirs' => [true, 'hash directories containing commands'],
181
+ 'mail_warning' => [false, 'warn if mail file has been accessed'],
182
+ 'path_dirs' => [false, 'search path for / commands'],
183
+ 'print_exit_value' => [false, 'print non-zero exit values'],
184
+ 'rc_quotes' => [false, "allow '' to escape ' in single quotes"],
185
+ 'rm_star_silent' => [false, 'do not warn on rm *'],
186
+ 'rm_star_wait' => [false, 'wait before executing rm *'],
187
+ 'short_loops' => [true, 'allow short loop forms'],
188
+
189
+ # Job Control
190
+ 'auto_continue' => [false, 'automatically send SIGCONT to disowned jobs'],
191
+ 'auto_resume' => [false, 'treat single word as resume of existing job'],
192
+ 'bg_nice' => [true, 'run background jobs at lower priority'],
193
+ 'check_running_jobs' => [false, 'check for running jobs on exit'],
194
+ 'hup' => [true, 'send SIGHUP to jobs when shell exits'],
195
+ 'long_list_jobs' => [false, 'list jobs in long format'],
196
+ 'monitor' => [true, 'enable job control'],
197
+ 'notify' => [false, 'report job status immediately'],
198
+
199
+ # Prompting
200
+ 'prompt_bang' => [false, 'enable ! substitution in prompts'],
201
+ 'prompt_cr' => [true, 'print CR before prompt'],
202
+ 'prompt_percent' => [true, 'enable % substitution in prompts'],
203
+ 'prompt_sp' => [true, 'preserve partial line'],
204
+ 'prompt_subst' => [false, 'enable parameter expansion in prompts'],
205
+ 'transient_rprompt' => [false, 'remove right prompt after command'],
206
+
207
+ # Scripts and Functions
208
+ 'c_bases' => [false, 'output hex/octal in C format'],
209
+ 'err_exit' => [false, 'exit on non-zero status (like set -e)'],
210
+ 'err_return' => [false, 'return from function on error'],
211
+ 'exec' => [true, 'execute commands'],
212
+ 'function_argzero' => [true, 'set $0 to function name'],
213
+ 'local_loops' => [false, 'break/continue affect only local loops'],
214
+ 'local_options' => [false, 'restore options on function return'],
215
+ 'local_traps' => [false, 'restore traps on function return'],
216
+ 'multios' => [true, 'enable multiple redirections'],
217
+ 'octal_zeroes' => [false, 'interpret leading 0 as octal'],
218
+ 'source_trace' => [false, 'print source file names'],
219
+ 'typeset_silent' => [false, 'do not print variable values in typeset'],
220
+ 'verbose' => [false, 'print shell input lines'],
221
+ 'xtrace' => [false, 'print commands before execution'],
222
+
223
+ # Shell Emulation
224
+ 'bsd_echo' => [false, 'make echo BSD compatible'],
225
+ 'csh_junkie_loops' => [false, 'allow csh-style loop syntax'],
226
+ 'csh_nullcmd' => [false, 'do not use NULLCMD/READNULLCMD'],
227
+ 'ksh_arrays' => [false, 'array index starts at 0'],
228
+ 'ksh_autoload' => [false, 'ksh-style autoloading'],
229
+ 'ksh_option_print' => [false, 'print options ksh-style'],
230
+ 'ksh_typeset' => [false, 'ksh-style typeset behavior'],
231
+ 'posix_aliases' => [false, 'POSIX alias expansion'],
232
+ 'posix_builtins' => [false, 'POSIX builtin behavior'],
233
+ 'posix_identifiers' => [false, 'POSIX identifier rules'],
234
+ 'posix_strings' => [false, 'POSIX string behavior'],
235
+ 'posix_traps' => [false, 'POSIX trap behavior'],
236
+ 'sh_file_expansion' => [false, 'sh-style filename expansion'],
237
+ 'sh_nullcmd' => [false, 'do not use NULLCMD for empty redirections'],
238
+ 'sh_word_split' => [false, 'split unquoted parameter expansions'],
239
+ 'traps_async' => [false, 'run traps asynchronously'],
240
+
241
+ # Zle (Zsh Line Editor)
242
+ 'beep' => [true, 'beep on errors'],
243
+ 'combining_chars' => [false, 'handle combining characters'],
244
+ 'emacs' => [false, 'use emacs keybindings'],
245
+ 'overstrike' => [false, 'start in overstrike mode'],
246
+ 'single_line_zle' => [false, 'use single line editing'],
247
+ 'vi' => [false, 'use vi keybindings'],
248
+ 'zle' => [true, 'use zsh line editor']
249
+ }.freeze
250
+ end
251
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Polyfill Data.define for Ruby < 3.2
4
+ data_has_define = Data.respond_to?(:define)
5
+
6
+ unless data_has_define
7
+ class Data
8
+ def self.define(*keys, &block)
9
+ # Use a module for initialize so that custom initialize can call super
10
+ init_module = Module.new do
11
+ define_method(:initialize) do |*args, **kwargs|
12
+ # Ruby 2.6 has quirky keyword argument handling where a trailing Hash
13
+ # may be interpreted as kwargs instead of a positional argument
14
+ has_expected_kwargs = keys.any? { |k| kwargs.key?(k) }
15
+
16
+ if has_expected_kwargs
17
+ keys.each do |key|
18
+ instance_variable_set("@#{key}", kwargs.fetch(key) { raise ArgumentError, "missing keyword: #{key}" })
19
+ end
20
+ elsif args.empty? && kwargs.empty?
21
+ # Called from super with implicit kwargs (Ruby 2.x behavior)
22
+ # Do nothing - instance variables already set by caller
23
+ elsif args.size == 1 && args.first.is_a?(Hash)
24
+ # Ruby 2.6 may pass keyword args as a positional hash
25
+ hash = args.first
26
+ keys.each do |key|
27
+ instance_variable_set("@#{key}", hash.fetch(key) { raise ArgumentError, "missing keyword: #{key}" })
28
+ end
29
+ elsif kwargs.any? && args.size + 1 == keys.size
30
+ # Ruby 2.6: trailing Hash was converted to kwargs, treat it as last positional arg
31
+ all_args = args + [kwargs.to_h]
32
+ keys.zip(all_args).each { |key, val| instance_variable_set("@#{key}", val) }
33
+ elsif args.size == keys.size
34
+ keys.zip(args).each { |key, val| instance_variable_set("@#{key}", val) }
35
+ else
36
+ raise ArgumentError, "wrong number of arguments (given #{args.size}, expected #{keys.size})"
37
+ end
38
+ freeze
39
+ end
40
+ end
41
+
42
+ Class.new do
43
+ include init_module
44
+
45
+ keys.each do |key|
46
+ define_method(key) { instance_variable_get("@#{key}") }
47
+ end
48
+
49
+ define_method(:deconstruct) { keys.map { |k| send(k) } }
50
+ define_method(:deconstruct_keys) { |_| keys.to_h { |k| [k, send(k)] } }
51
+ define_method(:to_h) { keys.to_h { |k| [k, send(k)] } }
52
+ define_method(:members) { keys }
53
+
54
+ define_method(:==) do |other|
55
+ other.class == self.class && keys.all? { |k| send(k) == other.send(k) }
56
+ end
57
+ alias eql? ==
58
+
59
+ define_method(:hash) { [self.class, *keys.map { |k| send(k) }].hash }
60
+
61
+ class_exec(&block) if block
62
+ end
63
+ end
64
+ end
65
+ end