konpeito 0.1.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 (180) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/CHANGELOG.md +75 -0
  4. data/CONTRIBUTING.md +123 -0
  5. data/LICENSE +21 -0
  6. data/README.md +257 -0
  7. data/Rakefile +11 -0
  8. data/bin/konpeito +6 -0
  9. data/konpeito.gemspec +43 -0
  10. data/lib/konpeito/ast/typed_ast.rb +620 -0
  11. data/lib/konpeito/ast/visitor.rb +78 -0
  12. data/lib/konpeito/cache/cache_manager.rb +230 -0
  13. data/lib/konpeito/cache/dependency_graph.rb +192 -0
  14. data/lib/konpeito/cache.rb +8 -0
  15. data/lib/konpeito/cli/base_command.rb +187 -0
  16. data/lib/konpeito/cli/build_command.rb +220 -0
  17. data/lib/konpeito/cli/check_command.rb +104 -0
  18. data/lib/konpeito/cli/config.rb +231 -0
  19. data/lib/konpeito/cli/deps_command.rb +128 -0
  20. data/lib/konpeito/cli/doctor_command.rb +340 -0
  21. data/lib/konpeito/cli/fmt_command.rb +199 -0
  22. data/lib/konpeito/cli/init_command.rb +312 -0
  23. data/lib/konpeito/cli/lsp_command.rb +40 -0
  24. data/lib/konpeito/cli/run_command.rb +150 -0
  25. data/lib/konpeito/cli/test_command.rb +248 -0
  26. data/lib/konpeito/cli/watch_command.rb +212 -0
  27. data/lib/konpeito/cli.rb +301 -0
  28. data/lib/konpeito/codegen/builtin_methods.rb +229 -0
  29. data/lib/konpeito/codegen/cruby_backend.rb +1090 -0
  30. data/lib/konpeito/codegen/debug_info.rb +352 -0
  31. data/lib/konpeito/codegen/inliner.rb +486 -0
  32. data/lib/konpeito/codegen/jvm_backend.rb +197 -0
  33. data/lib/konpeito/codegen/jvm_generator.rb +13412 -0
  34. data/lib/konpeito/codegen/llvm_generator.rb +13191 -0
  35. data/lib/konpeito/codegen/loop_optimizer.rb +363 -0
  36. data/lib/konpeito/codegen/monomorphizer.rb +359 -0
  37. data/lib/konpeito/codegen/profile_runtime.c +341 -0
  38. data/lib/konpeito/codegen/profiler.rb +99 -0
  39. data/lib/konpeito/compiler.rb +592 -0
  40. data/lib/konpeito/dependency_resolver.rb +296 -0
  41. data/lib/konpeito/diagnostics/collector.rb +127 -0
  42. data/lib/konpeito/diagnostics/diagnostic.rb +237 -0
  43. data/lib/konpeito/diagnostics/renderer.rb +144 -0
  44. data/lib/konpeito/formatter/formatter.rb +1214 -0
  45. data/lib/konpeito/hir/builder.rb +7167 -0
  46. data/lib/konpeito/hir/nodes.rb +2465 -0
  47. data/lib/konpeito/lsp/document_manager.rb +820 -0
  48. data/lib/konpeito/lsp/server.rb +183 -0
  49. data/lib/konpeito/lsp/transport.rb +38 -0
  50. data/lib/konpeito/parser/prism_adapter.rb +65 -0
  51. data/lib/konpeito/platform.rb +103 -0
  52. data/lib/konpeito/profile/report.rb +136 -0
  53. data/lib/konpeito/rbs_inline/preprocessor.rb +199 -0
  54. data/lib/konpeito/stdlib/compression/compression.rb +72 -0
  55. data/lib/konpeito/stdlib/compression/compression.rbs +60 -0
  56. data/lib/konpeito/stdlib/compression/compression_native.c +415 -0
  57. data/lib/konpeito/stdlib/compression/extconf.rb +19 -0
  58. data/lib/konpeito/stdlib/crypto/crypto.rb +85 -0
  59. data/lib/konpeito/stdlib/crypto/crypto.rbs +74 -0
  60. data/lib/konpeito/stdlib/crypto/crypto_native.c +312 -0
  61. data/lib/konpeito/stdlib/crypto/extconf.rb +40 -0
  62. data/lib/konpeito/stdlib/http/extconf.rb +19 -0
  63. data/lib/konpeito/stdlib/http/http.rb +125 -0
  64. data/lib/konpeito/stdlib/http/http.rbs +57 -0
  65. data/lib/konpeito/stdlib/http/http_native.c +440 -0
  66. data/lib/konpeito/stdlib/json/extconf.rb +17 -0
  67. data/lib/konpeito/stdlib/json/json.rb +44 -0
  68. data/lib/konpeito/stdlib/json/json.rbs +33 -0
  69. data/lib/konpeito/stdlib/json/json_native.c +286 -0
  70. data/lib/konpeito/stdlib/ui/extconf.rb +216 -0
  71. data/lib/konpeito/stdlib/ui/konpeito_ui_native.cpp +1625 -0
  72. data/lib/konpeito/stdlib/ui/konpeito_ui_native.h +162 -0
  73. data/lib/konpeito/stdlib/ui/ui.rb +318 -0
  74. data/lib/konpeito/stdlib/ui/ui.rbs +247 -0
  75. data/lib/konpeito/type_checker/annotation_parser.rb +67 -0
  76. data/lib/konpeito/type_checker/hm_inferrer.rb +2565 -0
  77. data/lib/konpeito/type_checker/inferrer.rb +565 -0
  78. data/lib/konpeito/type_checker/rbs_loader.rb +1621 -0
  79. data/lib/konpeito/type_checker/type_resolver.rb +276 -0
  80. data/lib/konpeito/type_checker/types.rb +1434 -0
  81. data/lib/konpeito/type_checker/unification.rb +323 -0
  82. data/lib/konpeito/ui/animation/animated_state.rb +80 -0
  83. data/lib/konpeito/ui/animation/easing.rb +59 -0
  84. data/lib/konpeito/ui/animation/value_tween.rb +66 -0
  85. data/lib/konpeito/ui/app.rb +379 -0
  86. data/lib/konpeito/ui/box.rb +38 -0
  87. data/lib/konpeito/ui/castella.rb +70 -0
  88. data/lib/konpeito/ui/castella_native.rb +76 -0
  89. data/lib/konpeito/ui/chart/area_chart.rb +305 -0
  90. data/lib/konpeito/ui/chart/bar_chart.rb +288 -0
  91. data/lib/konpeito/ui/chart/base_chart.rb +210 -0
  92. data/lib/konpeito/ui/chart/chart_helpers.rb +79 -0
  93. data/lib/konpeito/ui/chart/gauge_chart.rb +171 -0
  94. data/lib/konpeito/ui/chart/heatmap_chart.rb +222 -0
  95. data/lib/konpeito/ui/chart/line_chart.rb +289 -0
  96. data/lib/konpeito/ui/chart/pie_chart.rb +219 -0
  97. data/lib/konpeito/ui/chart/scales.rb +77 -0
  98. data/lib/konpeito/ui/chart/scatter_chart.rb +303 -0
  99. data/lib/konpeito/ui/chart/stacked_bar_chart.rb +276 -0
  100. data/lib/konpeito/ui/column.rb +271 -0
  101. data/lib/konpeito/ui/core.rb +2199 -0
  102. data/lib/konpeito/ui/dsl.rb +443 -0
  103. data/lib/konpeito/ui/frame.rb +171 -0
  104. data/lib/konpeito/ui/frame_native.rb +494 -0
  105. data/lib/konpeito/ui/markdown/ast.rb +124 -0
  106. data/lib/konpeito/ui/markdown/mermaid/layout.rb +387 -0
  107. data/lib/konpeito/ui/markdown/mermaid/models.rb +232 -0
  108. data/lib/konpeito/ui/markdown/mermaid/parser.rb +519 -0
  109. data/lib/konpeito/ui/markdown/mermaid/renderer.rb +336 -0
  110. data/lib/konpeito/ui/markdown/parser.rb +805 -0
  111. data/lib/konpeito/ui/markdown/renderer.rb +639 -0
  112. data/lib/konpeito/ui/markdown/theme.rb +165 -0
  113. data/lib/konpeito/ui/render_node.rb +260 -0
  114. data/lib/konpeito/ui/row.rb +207 -0
  115. data/lib/konpeito/ui/spacer.rb +18 -0
  116. data/lib/konpeito/ui/style.rb +799 -0
  117. data/lib/konpeito/ui/theme.rb +563 -0
  118. data/lib/konpeito/ui/themes/material.rb +35 -0
  119. data/lib/konpeito/ui/themes/tokyo_night.rb +6 -0
  120. data/lib/konpeito/ui/widgets/button.rb +103 -0
  121. data/lib/konpeito/ui/widgets/calendar.rb +1034 -0
  122. data/lib/konpeito/ui/widgets/checkbox.rb +119 -0
  123. data/lib/konpeito/ui/widgets/container.rb +91 -0
  124. data/lib/konpeito/ui/widgets/data_table.rb +667 -0
  125. data/lib/konpeito/ui/widgets/divider.rb +29 -0
  126. data/lib/konpeito/ui/widgets/image.rb +105 -0
  127. data/lib/konpeito/ui/widgets/input.rb +485 -0
  128. data/lib/konpeito/ui/widgets/markdown.rb +57 -0
  129. data/lib/konpeito/ui/widgets/modal.rb +163 -0
  130. data/lib/konpeito/ui/widgets/multiline_input.rb +968 -0
  131. data/lib/konpeito/ui/widgets/multiline_text.rb +180 -0
  132. data/lib/konpeito/ui/widgets/net_image.rb +100 -0
  133. data/lib/konpeito/ui/widgets/progress_bar.rb +70 -0
  134. data/lib/konpeito/ui/widgets/radio_buttons.rb +93 -0
  135. data/lib/konpeito/ui/widgets/slider.rb +133 -0
  136. data/lib/konpeito/ui/widgets/switch.rb +84 -0
  137. data/lib/konpeito/ui/widgets/tabs.rb +157 -0
  138. data/lib/konpeito/ui/widgets/text.rb +110 -0
  139. data/lib/konpeito/ui/widgets/tree.rb +426 -0
  140. data/lib/konpeito/version.rb +5 -0
  141. data/lib/konpeito.rb +109 -0
  142. data/test_native_array.rb +172 -0
  143. data/test_native_array_class.rb +197 -0
  144. data/test_native_class.rb +151 -0
  145. data/tools/konpeito-asm/build.sh +65 -0
  146. data/tools/konpeito-asm/lib/asm-9.7.1.jar +0 -0
  147. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KArray.class +0 -0
  148. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCompression.class +0 -0
  149. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class +0 -0
  150. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCrypto.class +0 -0
  151. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KFile.class +0 -0
  152. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHTTP.class +0 -0
  153. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class +0 -0
  154. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON$Parser.class +0 -0
  155. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON.class +0 -0
  156. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KMath.class +0 -0
  157. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactor.class +0 -0
  158. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactorPort.class +0 -0
  159. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class +0 -0
  160. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KThread.class +0 -0
  161. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KTime.class +0 -0
  162. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/RubyDispatch.class +0 -0
  163. data/tools/konpeito-asm/src/ClassIntrospector.java +312 -0
  164. data/tools/konpeito-asm/src/KonpeitoAssembler.java +659 -0
  165. data/tools/konpeito-asm/src/konpeito/runtime/KArray.java +390 -0
  166. data/tools/konpeito-asm/src/konpeito/runtime/KCompression.java +168 -0
  167. data/tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java +48 -0
  168. data/tools/konpeito-asm/src/konpeito/runtime/KCrypto.java +151 -0
  169. data/tools/konpeito-asm/src/konpeito/runtime/KFile.java +100 -0
  170. data/tools/konpeito-asm/src/konpeito/runtime/KHTTP.java +113 -0
  171. data/tools/konpeito-asm/src/konpeito/runtime/KHash.java +228 -0
  172. data/tools/konpeito-asm/src/konpeito/runtime/KJSON.java +405 -0
  173. data/tools/konpeito-asm/src/konpeito/runtime/KMath.java +54 -0
  174. data/tools/konpeito-asm/src/konpeito/runtime/KRactor.java +244 -0
  175. data/tools/konpeito-asm/src/konpeito/runtime/KRactorPort.java +53 -0
  176. data/tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java +49 -0
  177. data/tools/konpeito-asm/src/konpeito/runtime/KThread.java +49 -0
  178. data/tools/konpeito-asm/src/konpeito/runtime/KTime.java +53 -0
  179. data/tools/konpeito-asm/src/konpeito/runtime/RubyDispatch.java +416 -0
  180. metadata +267 -0
@@ -0,0 +1,248 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Konpeito
4
+ module Commands
5
+ # Test command - run tests with Minitest
6
+ class TestCommand < BaseCommand
7
+ def self.command_name
8
+ "test"
9
+ end
10
+
11
+ def self.description
12
+ "Run tests (Minitest integration)"
13
+ end
14
+
15
+ def run
16
+ parse_options!
17
+
18
+ test_files = find_test_files
19
+
20
+ if test_files.empty?
21
+ puts "No test files found matching pattern: #{options[:pattern]}"
22
+ return
23
+ end
24
+
25
+ target = options[:target] || config.target
26
+
27
+ if target == :jvm
28
+ run_jvm_tests(test_files)
29
+ else
30
+ if options[:compile_first]
31
+ compile_source_files
32
+ end
33
+ run_tests(test_files)
34
+ end
35
+ end
36
+
37
+ protected
38
+
39
+ def default_options
40
+ {
41
+ verbose: false,
42
+ color: $stderr.tty?,
43
+ pattern: config.test_pattern,
44
+ compile_first: false,
45
+ name_pattern: nil,
46
+ target: nil,
47
+ classpath: nil
48
+ }
49
+ end
50
+
51
+ def setup_option_parser(opts)
52
+ opts.on("-p", "--pattern PATTERN", "Test file pattern (default: test/**/*_test.rb)") do |pattern|
53
+ options[:pattern] = pattern
54
+ end
55
+
56
+ opts.on("-n", "--name PATTERN", "Run tests matching name pattern") do |pattern|
57
+ options[:name_pattern] = pattern
58
+ end
59
+
60
+ opts.on("--compile", "Compile source files before running tests") do
61
+ options[:compile_first] = true
62
+ end
63
+
64
+ opts.on("--target TARGET", %i[native jvm], "Target platform (native, jvm)") do |target|
65
+ options[:target] = target
66
+ end
67
+
68
+ opts.on("--classpath PATH", "Add external JARs/directories to classpath (JVM only)") do |path|
69
+ options[:classpath] = path
70
+ end
71
+
72
+ super
73
+ end
74
+
75
+ def banner
76
+ <<~BANNER.chomp
77
+ Usage: konpeito test [options] [test_files...]
78
+
79
+ Examples:
80
+ konpeito test Run all tests
81
+ konpeito test test/main_test.rb Run specific test file
82
+ konpeito test -n test_hello Run matching test name
83
+ konpeito test --target jvm Run JVM tests
84
+ BANNER
85
+ end
86
+
87
+ private
88
+
89
+ def find_test_files
90
+ if args.empty?
91
+ Dir.glob(options[:pattern])
92
+ else
93
+ args.select { |f| File.exist?(f) }
94
+ end
95
+ end
96
+
97
+ def compile_source_files
98
+ puts_verbose "Compiling source files..."
99
+
100
+ src_files = Dir.glob("src/**/*.rb")
101
+ src_files.each do |source_file|
102
+ puts_verbose " Compiling #{source_file}..."
103
+ begin
104
+ output_file = default_output_name(source_file)
105
+ compiler = Compiler.new(
106
+ source_file: source_file,
107
+ output_file: output_file,
108
+ format: :cruby_ext,
109
+ verbose: options[:verbose],
110
+ rbs_paths: config.rbs_paths,
111
+ require_paths: config.require_paths
112
+ )
113
+ compiler.compile
114
+ rescue Konpeito::Error => e
115
+ $stderr.puts "Warning: Failed to compile #{source_file}: #{e.message}"
116
+ end
117
+ end
118
+ end
119
+
120
+ def run_jvm_tests(test_files)
121
+ require "tmpdir"
122
+ require "fileutils"
123
+
124
+ passed = 0
125
+ failed = 0
126
+ errors = 0
127
+
128
+ # Build classpath
129
+ classpath_parts = []
130
+ cp = options[:classpath] || config.jvm_classpath
131
+ classpath_parts << cp unless cp.empty?
132
+ lib_jars = Dir.glob("lib/*.jar")
133
+ classpath_parts << lib_jars.join(Platform.classpath_separator) unless lib_jars.empty?
134
+ classpath = classpath_parts.reject(&:empty?).join(Platform.classpath_separator)
135
+
136
+ puts "Running #{test_files.size} JVM test file(s)..."
137
+ puts ""
138
+
139
+ test_files.each do |test_file|
140
+ puts_verbose "Compiling #{test_file}..."
141
+
142
+ Dir.mktmpdir("konpeito-test") do |tmpdir|
143
+ jar_path = File.join(tmpdir, "test.jar")
144
+
145
+ begin
146
+ # Compile to JAR
147
+ build_args = ["--target", "jvm", "-o", jar_path]
148
+ build_args += ["--classpath", classpath] unless classpath.empty?
149
+ config.rbs_paths.each { |p| build_args << "--rbs" << p }
150
+ build_args << test_file
151
+
152
+ Commands::BuildCommand.new(build_args, config: config).run
153
+
154
+ # Run the JAR
155
+ java_cmd = find_java
156
+ run_cmd = if classpath.empty?
157
+ [java_cmd, "-jar", jar_path]
158
+ else
159
+ main_class = File.basename(test_file, ".rb").split("_").map(&:capitalize).join
160
+ cp = "#{jar_path}#{Platform.classpath_separator}#{classpath}"
161
+ [java_cmd, "-cp", cp, main_class]
162
+ end
163
+
164
+ output = `#{run_cmd.map { |s| s.include?(" ") ? "\"#{s}\"" : s }.join(" ")} 2>&1`
165
+
166
+ # Parse PASS:/FAIL: from output
167
+ output.each_line do |line|
168
+ line = line.strip
169
+ if line.start_with?("PASS:")
170
+ passed += 1
171
+ puts " \e[32m#{line}\e[0m" if options[:color]
172
+ puts " #{line}" unless options[:color]
173
+ elsif line.start_with?("FAIL:")
174
+ failed += 1
175
+ puts " \e[31m#{line}\e[0m" if options[:color]
176
+ puts " #{line}" unless options[:color]
177
+ end
178
+ end
179
+ rescue => e
180
+ errors += 1
181
+ $stderr.puts " ERROR: #{test_file}: #{e.message}"
182
+ end
183
+ end
184
+ end
185
+
186
+ # Summary
187
+ puts ""
188
+ total = passed + failed
189
+ summary = "#{total} assertions, #{passed} passed, #{failed} failed"
190
+ summary += ", #{errors} errors" if errors > 0
191
+
192
+ if failed > 0 || errors > 0
193
+ puts options[:color] ? "\e[31m#{summary}\e[0m" : summary
194
+ exit 1
195
+ else
196
+ puts options[:color] ? "\e[32m#{summary}\e[0m" : summary
197
+ end
198
+ end
199
+
200
+ def find_java
201
+ java_home = config.jvm_java_home
202
+ java_home = ENV["JAVA_HOME"] || Platform.default_java_home if java_home.empty?
203
+ java_path = File.join(java_home, "bin", "java")
204
+ return java_path if File.exist?(java_path)
205
+
206
+ java_in_path = Platform.find_executable("java")
207
+ return java_in_path if java_in_path
208
+
209
+ error("Java not found. Install Java 21+: #{Platform.java_install_hint}")
210
+ end
211
+
212
+ def run_tests(test_files)
213
+ puts_verbose "Running #{test_files.size} test file(s)..."
214
+
215
+ # Build minitest command
216
+ cmd = ["ruby"]
217
+
218
+ # Add load paths
219
+ cmd << "-I" << "lib"
220
+ cmd << "-I" << "test"
221
+ cmd << "-I" << "src" if Dir.exist?("src")
222
+
223
+ # Require minitest
224
+ cmd << "-rminitest/autorun"
225
+
226
+ # Add name filter if specified
227
+ if options[:name_pattern]
228
+ cmd << "-e" << "Minitest.run_with_autorun([])"
229
+ cmd << "--" << "--name" << "/#{options[:name_pattern]}/"
230
+ end
231
+
232
+ # Add test files
233
+ test_files.each { |f| cmd << "-r" << "./#{f}" }
234
+
235
+ # Add final execution trigger if no name pattern
236
+ unless options[:name_pattern]
237
+ cmd << "-e" << ""
238
+ end
239
+
240
+ puts_verbose "Running: #{cmd.join(' ')}"
241
+
242
+ # Execute tests
243
+ success = system(*cmd)
244
+ exit(success ? 0 : 1)
245
+ end
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,212 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Konpeito
4
+ module Commands
5
+ # Watch command - watch for file changes and recompile
6
+ class WatchCommand < BaseCommand
7
+ def self.command_name
8
+ "watch"
9
+ end
10
+
11
+ def self.description
12
+ "Watch for file changes and recompile automatically"
13
+ end
14
+
15
+ def run
16
+ parse_options!
17
+
18
+ unless listen_available?
19
+ error("The 'listen' gem is required for watch mode. Install it with 'gem install listen'.")
20
+ end
21
+
22
+ require "listen"
23
+
24
+ @source_file = args.first
25
+ if @source_file && !File.exist?(@source_file)
26
+ error("File not found: #{@source_file}")
27
+ end
28
+
29
+ start_watching
30
+ end
31
+
32
+ protected
33
+
34
+ def default_options
35
+ {
36
+ verbose: false,
37
+ color: $stderr.tty?,
38
+ output: config.build_output,
39
+ format: config.build_format,
40
+ paths: config.watch_paths,
41
+ extensions: config.watch_extensions,
42
+ clear: true
43
+ }
44
+ end
45
+
46
+ def setup_option_parser(opts)
47
+ opts.on("-o", "--output FILE", "Output file name") do |file|
48
+ options[:output] = file
49
+ end
50
+
51
+ opts.on("-w", "--watch PATH", "Additional paths to watch (can be used multiple times)") do |path|
52
+ options[:paths] << path
53
+ end
54
+
55
+ opts.on("--no-clear", "Do not clear screen before each rebuild") do
56
+ options[:clear] = false
57
+ end
58
+
59
+ super
60
+ end
61
+
62
+ def banner
63
+ <<~BANNER.chomp
64
+ Usage: konpeito watch [options] [source.rb]
65
+
66
+ Examples:
67
+ konpeito watch src/main.rb Watch and recompile
68
+ konpeito watch -w lib src/main.rb Watch additional paths
69
+ BANNER
70
+ end
71
+
72
+ private
73
+
74
+ def listen_available?
75
+ require "listen"
76
+ true
77
+ rescue LoadError
78
+ false
79
+ end
80
+
81
+ def start_watching
82
+ watch_paths = options[:paths].select { |p| Dir.exist?(p) }
83
+
84
+ if watch_paths.empty?
85
+ watch_paths = ["."]
86
+ end
87
+
88
+ timestamp = Time.now.strftime("%H:%M:%S")
89
+ emit("Watching", "#{watch_paths.join(', ')} [#{options[:extensions].join(', ')}]")
90
+ $stderr.puts ""
91
+
92
+ # Initial build
93
+ if @source_file
94
+ rebuild
95
+ end
96
+
97
+ # Start watching
98
+ extensions_pattern = /\.(#{options[:extensions].join('|')})$/
99
+
100
+ listener = Listen.to(*watch_paths) do |modified, added, removed|
101
+ changed_files = (modified + added + removed).select { |f| f.match?(extensions_pattern) }
102
+
103
+ unless changed_files.empty?
104
+ $stderr.puts ""
105
+ changed_files.each do |f|
106
+ emit("Changed", File.basename(f))
107
+ end
108
+ rebuild
109
+ end
110
+ end
111
+
112
+ listener.start
113
+
114
+ # Keep running
115
+ begin
116
+ sleep
117
+ rescue Interrupt
118
+ $stderr.puts ""
119
+ emit("Stopped", "watch mode")
120
+ listener.stop
121
+ end
122
+ end
123
+
124
+ def rebuild
125
+ clear_screen if options[:clear]
126
+
127
+ if @source_file
128
+ compile_single(@source_file)
129
+ else
130
+ compile_all
131
+ end
132
+ end
133
+
134
+ def compile_single(source_file)
135
+ output_file = options[:output] || default_output_name(source_file, format: options[:format])
136
+
137
+ emit("Compiling", "#{source_file} (native)")
138
+
139
+ compiler = Compiler.new(
140
+ source_file: source_file,
141
+ output_file: output_file,
142
+ format: options[:format],
143
+ verbose: options[:verbose],
144
+ rbs_paths: config.rbs_paths,
145
+ require_paths: config.require_paths,
146
+ incremental: true
147
+ )
148
+
149
+ compiler.compile
150
+ display_diagnostics(compiler.diagnostics)
151
+
152
+ if compiler.diagnostics.any?(&:error?)
153
+ error_count = compiler.diagnostics.count(&:error?)
154
+ emit_error("Failed", "with #{error_count} error(s)")
155
+ else
156
+ stats = compiler.compile_stats
157
+ if stats
158
+ duration_str = "%.2fs" % stats.duration_s
159
+ emit("Finished", "in #{duration_str} -> #{output_file}")
160
+ end
161
+ end
162
+ rescue Konpeito::Error => e
163
+ $stderr.puts "Error: #{e.message}"
164
+ emit_error("Failed", "build error")
165
+ end
166
+
167
+ def compile_all
168
+ src_files = Dir.glob("src/**/*.rb")
169
+
170
+ if src_files.empty?
171
+ puts "No source files found in src/"
172
+ return
173
+ end
174
+
175
+ success_count = 0
176
+ error_count = 0
177
+
178
+ src_files.each do |source_file|
179
+ begin
180
+ output_file = default_output_name(source_file, format: options[:format])
181
+ compiler = Compiler.new(
182
+ source_file: source_file,
183
+ output_file: output_file,
184
+ format: options[:format],
185
+ verbose: options[:verbose],
186
+ rbs_paths: config.rbs_paths,
187
+ require_paths: config.require_paths,
188
+ incremental: true
189
+ )
190
+ compiler.compile
191
+
192
+ if compiler.diagnostics.any?(&:error?)
193
+ display_diagnostics(compiler.diagnostics)
194
+ error_count += 1
195
+ else
196
+ success_count += 1
197
+ end
198
+ rescue Konpeito::Error => e
199
+ $stderr.puts "Error compiling #{source_file}: #{e.message}"
200
+ error_count += 1
201
+ end
202
+ end
203
+
204
+ puts "Build complete: #{success_count} succeeded, #{error_count} failed"
205
+ end
206
+
207
+ def clear_screen
208
+ print "\e[2J\e[H"
209
+ end
210
+ end
211
+ end
212
+ end