• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

type-ruby / t-ruby / 20892862486

11 Jan 2026 09:22AM UTC coverage: 92.014% (-0.4%) from 92.41%
20892862486

Pull #37

github

web-flow
Merge 0818416d2 into c6a520f1b
Pull Request #37: feat: add direct .trb file execution without intermediate files

21 of 61 new or added lines in 3 files covered. (34.43%)

8227 of 8941 relevant lines covered (92.01%)

1794.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.9
/lib/t_ruby/cli.rb
1
# frozen_string_literal: true
2

3
module TRuby
1✔
4
  class CLI
1✔
5
    HELP_TEXT = <<~HELP.freeze
1✔
6
      t-ruby compiler (trc) v#{VERSION}
7

8
      Usage:
9
        trc <file.trb>           Compile a .trb file to .rb
10
        trc <file.rb>            Copy .rb file to build/ and generate .rbs
11
        trc --init               Initialize a new t-ruby project
12
        trc --config, -c <path>  Use custom config file
13
        trc --watch, -w          Watch input files and recompile on change
14
        trc --decl <file.trb>    Generate .d.trb declaration file
15
        trc --lsp                Start LSP server (for IDE integration)
16
        trc run <file.trb>       Run a .trb file directly (delegates to t-ruby)
17
        trc update               Update t-ruby to the latest version
18
        trc --version, -v        Show version (and check for updates)
19
        trc --help, -h           Show this help
20

21
      Examples:
22
        trc hello.trb            Compile hello.trb to build/hello.rb
23
        trc utils.rb             Copy utils.rb to build/ and generate utils.rbs
24
        trc --init               Create trbconfig.yml and src/, build/ directories
25
        trc -c custom.yml file.trb  Compile with custom config file
26
        trc -w                   Watch all .trb and .rb files in current directory
27
        trc -w src/              Watch all .trb and .rb files in src/ directory
28
        trc --watch hello.trb    Watch specific file for changes
29
        trc --decl hello.trb     Generate hello.d.trb declaration file
30
        trc --lsp                Start language server for VS Code
31
        trc run hello.trb        Run hello.trb directly without compilation
32
    HELP
33

34
    def self.run(args)
1✔
35
      new(args).run
11✔
36
    end
37

38
    def initialize(args)
1✔
39
      @args = args
59✔
40
    end
41

42
    def run
1✔
43
      if @args.empty? || @args.include?("--help") || @args.include?("-h")
48✔
44
        puts HELP_TEXT
5✔
45
        return
5✔
46
      end
47

48
      if @args.include?("--version") || @args.include?("-v")
43✔
49
        puts "trc #{VERSION}"
6✔
50
        check_for_updates
6✔
51
        return
6✔
52
      end
53

54
      if @args.include?("update")
37✔
55
        update_gem
2✔
56
        return
2✔
57
      end
58

59
      if @args.include?("--init")
35✔
60
        init_project
12✔
61
        return
12✔
62
      end
63

64
      if @args.include?("--lsp")
23✔
65
        start_lsp_server
1✔
66
        return
1✔
67
      end
68

69
      if @args.first == "run"
22✔
NEW
70
        run_direct
×
NEW
71
        return
×
72
      end
73

74
      if @args.include?("--watch") || @args.include?("-w")
22✔
75
        start_watch_mode
3✔
76
        return
3✔
77
      end
78

79
      if @args.include?("--decl")
19✔
80
        input_file = @args[@args.index("--decl") + 1]
2✔
81
        generate_declaration(input_file)
2✔
82
        return
1✔
83
      end
84

85
      # Extract config path if --config or -c flag is present
86
      config_path = extract_config_path
17✔
87

88
      # Get input file (first non-flag argument)
89
      input_file = find_input_file
17✔
90
      compile(input_file, config_path: config_path)
17✔
91
    end
92

93
    private
1✔
94

95
    def check_for_updates
1✔
96
      result = VersionChecker.check
6✔
97
      return unless result
6✔
98

99
      puts ""
1✔
100
      puts "New version available: #{result[:latest]} (current: #{result[:current]})"
1✔
101
      puts "Run 'trc update' to update"
1✔
102
    end
103

104
    def update_gem
1✔
105
      puts "Updating t-ruby..."
2✔
106
      if VersionChecker.update
2✔
107
        puts "Successfully updated t-ruby!"
1✔
108
      else
109
        puts "Update failed. Try: gem install t-ruby"
1✔
110
      end
111
    end
112

113
    def init_project
1✔
114
      config_file = "trbconfig.yml"
12✔
115
      src_dir = "src"
12✔
116
      build_dir = "build"
12✔
117

118
      created = []
12✔
119
      skipped = []
12✔
120

121
      # Create trbconfig.yml with new schema
122
      if File.exist?(config_file)
12✔
123
        skipped << config_file
4✔
124
      else
125
        File.write(config_file, <<~YAML)
8✔
126
          # T-Ruby configuration file
127
          # See: https://type-ruby.github.io/docs/getting-started/project-configuration
128

129
          source:
130
            include:
131
              - #{src_dir}
132
            exclude: []
133
            extensions:
134
              - ".trb"
135
              - ".rb"
136

137
          output:
138
            ruby_dir: #{build_dir}
139
            # rbs_dir: sig  # Optional: separate directory for .rbs files
140
            # clean_before_build: false
141

142
          compiler:
143
            strictness: standard  # strict | standard | permissive
144
            generate_rbs: true
145
            target_ruby: "#{RubyVersion.current.major}.#{RubyVersion.current.minor}"
146
            # experimental: []
147
            # checks:
148
            #   no_implicit_any: false
149
            #   no_unused_vars: false
150
            #   strict_nil: false
151

152
          watch:
153
            # paths: []  # Additional paths to watch
154
            debounce: 100
155
            # clear_screen: false
156
            # on_success: "bundle exec rspec"
157
        YAML
158
        created << config_file
8✔
159
      end
160

161
      # Create src/ directory
162
      if Dir.exist?(src_dir)
12✔
163
        skipped << "#{src_dir}/"
5✔
164
      else
165
        Dir.mkdir(src_dir)
7✔
166
        created << "#{src_dir}/"
7✔
167
      end
168

169
      # Create build/ directory
170
      if Dir.exist?(build_dir)
12✔
171
        skipped << "#{build_dir}/"
4✔
172
      else
173
        Dir.mkdir(build_dir)
8✔
174
        created << "#{build_dir}/"
8✔
175
      end
176

177
      # Output results
178
      if created.any?
12✔
179
        puts "Created: #{created.join(", ")}"
9✔
180
      end
181
      if skipped.any?
12✔
182
        puts "Skipped (already exists): #{skipped.join(", ")}"
6✔
183
      end
184
      if created.empty? && skipped.any?
12✔
185
        puts "Project already initialized."
3✔
186
      else
187
        puts "t-ruby project initialized successfully!"
9✔
188
      end
189
    end
190

191
    def start_lsp_server
1✔
192
      server = LSPServer.new
1✔
193
      server.run
1✔
194
    end
195

196
    def run_direct
1✔
NEW
197
      remaining_args = @args[1..] || []
×
198

199
      # Find t-ruby executable path
NEW
200
      t_ruby_bin = File.expand_path("../../bin/t-ruby", __dir__)
×
201

202
      # Execute t-ruby (replaces current process)
NEW
203
      exec(t_ruby_bin, *remaining_args)
×
204
    end
205

206
    def start_watch_mode
1✔
207
      # Get paths to watch (everything after --watch or -w flag)
208
      watch_index = @args.index("--watch") || @args.index("-w")
3✔
209
      paths = @args[(watch_index + 1)..]
3✔
210

211
      # Default to current directory if no paths specified
212
      paths = ["."] if paths.empty?
3✔
213

214
      config = Config.new
3✔
215
      watcher = Watcher.new(paths: paths, config: config)
3✔
216
      watcher.watch
3✔
217
    end
218

219
    def generate_declaration(input_file)
1✔
220
      config = Config.new
2✔
221
      generator = DeclarationGenerator.new
2✔
222

223
      output_path = generator.generate_file(input_file, config.out_dir)
2✔
224
      puts "Generated: #{input_file} -> #{output_path}"
1✔
225
    rescue ArgumentError => e
226
      puts "Error: #{e.message}"
1✔
227
      exit 1
1✔
228
    end
229

230
    def compile(input_file, config_path: nil)
1✔
231
      config = Config.new(config_path)
17✔
232
      compiler = Compiler.new(config)
17✔
233

234
      result = compiler.compile_with_diagnostics(input_file)
17✔
235

236
      if result[:success]
17✔
237
        puts "Compiled: #{input_file} -> #{result[:output_path]}"
8✔
238
      else
239
        formatter = DiagnosticFormatter.new(use_colors: $stdout.tty?)
9✔
240
        result[:diagnostics].each do |diagnostic|
9✔
241
          puts formatter.format(diagnostic)
9✔
242
        end
243
        puts
9✔
244
        puts formatter.send(:format_summary, result[:diagnostics])
9✔
245
        exit 1
9✔
246
      end
247
    end
248

249
    # Extract config path from --config or -c flag
250
    def extract_config_path
1✔
251
      config_index = @args.index("--config") || @args.index("-c")
20✔
252
      return nil unless config_index
20✔
253

254
      @args[config_index + 1]
6✔
255
    end
256

257
    # Find the input file (first non-flag argument)
258
    def find_input_file
1✔
259
      skip_next = false
21✔
260
      @args.each do |arg|
21✔
261
        if skip_next
37✔
262
          skip_next = false
8✔
263
          next
8✔
264
        end
265

266
        # Skip known flags with arguments
267
        if %w[--config -c --decl].include?(arg)
29✔
268
          skip_next = true
8✔
269
          next
8✔
270
        end
271

272
        # Skip flags without arguments
273
        next if arg.start_with?("-")
21✔
274

275
        return arg
20✔
276
      end
277
      nil
278
    end
279
  end
280
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc