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.
- checksums.yaml +7 -0
- data/.dockerignore +23 -0
- data/Dockerfile +54 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +12 -0
- data/lib/rubish/arithmetic.rb +140 -0
- data/lib/rubish/ast.rb +168 -0
- data/lib/rubish/builtins/arithmetic.rb +129 -0
- data/lib/rubish/builtins/bind_readline.rb +834 -0
- data/lib/rubish/builtins/directory_stack.rb +182 -0
- data/lib/rubish/builtins/echo_printf.rb +510 -0
- data/lib/rubish/builtins/hash_directories.rb +260 -0
- data/lib/rubish/builtins/read.rb +299 -0
- data/lib/rubish/builtins/trap.rb +324 -0
- data/lib/rubish/codegen.rb +1273 -0
- data/lib/rubish/completion.rb +840 -0
- data/lib/rubish/completions/bash_helpers.rb +530 -0
- data/lib/rubish/completions/git.rb +431 -0
- data/lib/rubish/completions/help_parser.rb +453 -0
- data/lib/rubish/completions/ssh.rb +114 -0
- data/lib/rubish/config.rb +267 -0
- data/lib/rubish/data/builtin_help.rb +716 -0
- data/lib/rubish/data/completion_data.rb +53 -0
- data/lib/rubish/data/readline_config.rb +47 -0
- data/lib/rubish/data/shell_options.rb +251 -0
- data/lib/rubish/data_define.rb +65 -0
- data/lib/rubish/execution_context.rb +1124 -0
- data/lib/rubish/expansion.rb +988 -0
- data/lib/rubish/history.rb +663 -0
- data/lib/rubish/lazy_loader.rb +127 -0
- data/lib/rubish/lexer.rb +1194 -0
- data/lib/rubish/parser.rb +1167 -0
- data/lib/rubish/prompt.rb +766 -0
- data/lib/rubish/repl.rb +2267 -0
- data/lib/rubish/runtime/builtins.rb +7222 -0
- data/lib/rubish/runtime/command.rb +1153 -0
- data/lib/rubish/runtime/job.rb +153 -0
- data/lib/rubish/runtime.rb +1169 -0
- data/lib/rubish/shell_state.rb +241 -0
- data/lib/rubish/startup_profiler.rb +67 -0
- data/lib/rubish/version.rb +5 -0
- data/lib/rubish.rb +60 -0
- data/sig/rubish.rbs +4 -0
- metadata +85 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubish
|
|
4
|
+
# Background lazy loader for slow shell initializations
|
|
5
|
+
#
|
|
6
|
+
# Usage in config:
|
|
7
|
+
# lazy_load {
|
|
8
|
+
# `rbenv init - --no-rehash bash`
|
|
9
|
+
# }
|
|
10
|
+
#
|
|
11
|
+
# The block runs in a background thread immediately.
|
|
12
|
+
# Its return value (a string of shell code) is eval'd in the main thread
|
|
13
|
+
# before the next prompt.
|
|
14
|
+
#
|
|
15
|
+
# Benefits:
|
|
16
|
+
# - Shell starts instantly
|
|
17
|
+
# - Slow inits run in parallel during startup
|
|
18
|
+
# - By the time you need the command, it's usually ready
|
|
19
|
+
#
|
|
20
|
+
module LazyLoader
|
|
21
|
+
@pending_tasks = []
|
|
22
|
+
@completed_results = Queue.new
|
|
23
|
+
@mutex = Mutex.new
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
# Register a lazy load task to run in background
|
|
27
|
+
def register(name, executor, &block)
|
|
28
|
+
task = {
|
|
29
|
+
name: name,
|
|
30
|
+
thread: nil,
|
|
31
|
+
executor: executor,
|
|
32
|
+
started_at: Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
task[:thread] = Thread.new do
|
|
36
|
+
begin
|
|
37
|
+
# Run the block and capture its return value (shell code to eval)
|
|
38
|
+
result = block.call
|
|
39
|
+
# If result is a string, it's shell code to eval
|
|
40
|
+
if result.is_a?(String) && !result.strip.empty?
|
|
41
|
+
@completed_results << {name: name, code: result, error: nil, started_at: task[:started_at]}
|
|
42
|
+
else
|
|
43
|
+
@completed_results << {name: name, code: nil, error: nil, started_at: task[:started_at]}
|
|
44
|
+
end
|
|
45
|
+
rescue => e
|
|
46
|
+
@completed_results << {name: name, code: nil, error: e, started_at: task[:started_at]}
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
@mutex.synchronize { @pending_tasks << task }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Check for completed background tasks and apply their results
|
|
54
|
+
# Called before each prompt
|
|
55
|
+
def apply_completed(executor)
|
|
56
|
+
applied = []
|
|
57
|
+
|
|
58
|
+
while !@completed_results.empty?
|
|
59
|
+
begin
|
|
60
|
+
item = @completed_results.pop(true) # non-blocking
|
|
61
|
+
rescue ThreadError
|
|
62
|
+
break
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
if item[:error]
|
|
66
|
+
$stderr.puts "lazy_load: #{item[:error].message}"
|
|
67
|
+
elsif item[:code]
|
|
68
|
+
begin
|
|
69
|
+
# Execute the shell code in main thread
|
|
70
|
+
executor.call(item[:code])
|
|
71
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - item[:started_at]
|
|
72
|
+
applied << {name: item[:name], elapsed: elapsed}
|
|
73
|
+
rescue => e
|
|
74
|
+
$stderr.puts "lazy_load: #{e.message}"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Report if profiling is enabled
|
|
80
|
+
if defined?(StartupProfiler) && StartupProfiler.enabled && applied.any?
|
|
81
|
+
applied.each do |info|
|
|
82
|
+
puts " [lazy] #{info[:name]}: loaded in background (#{(info[:elapsed] * 1000).round(1)}ms)"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
applied.map { |a| a[:name] }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Wait for all pending tasks to complete (useful for scripts)
|
|
90
|
+
def wait_all(executor, timeout: 30)
|
|
91
|
+
deadline = Time.now + timeout
|
|
92
|
+
|
|
93
|
+
@mutex.synchronize { @pending_tasks.dup }.each do |task|
|
|
94
|
+
remaining = deadline - Time.now
|
|
95
|
+
break if remaining <= 0
|
|
96
|
+
task[:thread]&.join(remaining)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
apply_completed(executor)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Check if any tasks are still running
|
|
103
|
+
def pending?
|
|
104
|
+
@mutex.synchronize { @pending_tasks.any? { |t| t[:thread]&.alive? } }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Get status of all tasks
|
|
108
|
+
def status
|
|
109
|
+
@mutex.synchronize do
|
|
110
|
+
@pending_tasks.map do |task|
|
|
111
|
+
{
|
|
112
|
+
name: task[:name],
|
|
113
|
+
running: task[:thread]&.alive?,
|
|
114
|
+
elapsed: Process.clock_gettime(Process::CLOCK_MONOTONIC) - task[:started_at]
|
|
115
|
+
}
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Clear all tasks (for testing)
|
|
121
|
+
def clear!
|
|
122
|
+
@mutex.synchronize { @pending_tasks.clear }
|
|
123
|
+
@completed_results.clear rescue nil
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|