module SyntaxTree::CLI

def colorize_line(line)

Take a line of Ruby source and colorize the output.
def colorize_line(line)
  require "irb"
  IRB::Color.colorize_code(line, **colorize_options)
end

def colorize_options

some reflection to make sure we always pass valid options.
Since we support multiple versions of IRB, we're going to need to do
These are the options we're going to pass into IRB::Color.colorize_code.
def colorize_options
  options = { complete: false }
  parameters = IRB::Color.method(:colorize_code).parameters
  if parameters.any? { |(_type, name)| name == :ignore_error }
    options[:ignore_error] = true
  end
  options
end

def highlight_error(error, source)

Highlights a snippet from a source and parse error.
def highlight_error(error, source)
  lines = source.lines
  maximum = [error.lineno + 3, lines.length].min
  digits = Math.log10(maximum).ceil
  ([error.lineno - 3, 0].max...maximum).each do |line_index|
    line_number = line_index + 1
    if line_number == error.lineno
      part1 = Color.red(">")
      part2 = Color.gray("%#{digits}d |" % line_number)
      warn("#{part1} #{part2} #{colorize_line(lines[line_index])}")
      part3 = Color.gray("  %#{digits}s |" % " ")
      warn("#{part3} #{" " * error.column}#{Color.red("^")}")
    else
      prefix = Color.gray("  %#{digits}d |" % line_number)
      warn("#{prefix} #{colorize_line(lines[line_index])}")
    end
  end
end

def process_queue(queue, action)

or not any errors were encountered.
Processes each item in the queue with the given action. Returns whether
def process_queue(queue, action)
  workers =
    [Etc.nprocessors, queue.size].min.times.map do
      Thread.new do
        # Propagate errors in the worker threads up to the parent thread.
        Thread.current.abort_on_exception = true
        # Track whether or not there are any errors from any of the files
        # that we take action on so that we can properly clean up and
        # exit.
        errored = false
        # While there is still work left to do, shift off the queue and
        # process the item.
        until queue.empty?
          item = queue.shift
          errored |=
            begin
              action.run(item)
              false
            rescue Parser::ParseError => error
              warn("Error: #{error.message}")
              highlight_error(error, item.source)
              true
            rescue Check::UnformattedError,
                   Debug::NonIdempotentFormatError
              true
            rescue StandardError => error
              warn(error.message)
              warn(error.backtrace)
              true
            end
        end
        # At the end, we're going to return whether or not this worker
        # ever encountered an error.
        errored
      end
    end
  workers.map(&:value).inject(:|)
end

def run(argv)

passed to the invocation.
Run the CLI over the given array of strings that make up the arguments
def run(argv)
  name, *arguments = argv
  config_file = ConfigFile.new
  arguments.unshift(*config_file.arguments)
  options = Options.new
  options.parse(arguments)
  action =
    case name
    when "a", "ast"
      AST.new(options)
    when "c", "check"
      Check.new(options)
    when "ctags"
      CTags.new(options)
    when "debug"
      Debug.new(options)
    when "doc"
      Doc.new(options)
    when "e", "expr"
      Expr.new(options)
    when "f", "format"
      Format.new(options)
    when "help"
      puts HELP
      return 0
    when "j", "json"
      Json.new(options)
    when "lsp"
      LanguageServer.new(print_width: options.print_width).run
      return 0
    when "m", "match"
      Match.new(options)
    when "s", "search"
      Search.new(arguments.shift)
    when "version"
      puts SyntaxTree::VERSION
      return 0
    when "w", "write"
      Write.new(options)
    else
      warn(HELP)
      return 1
    end
  # We're going to build up a queue of items to process.
  queue = Queue.new
  # If there are any arguments or scripts, then we'll add those to the
  # queue. Otherwise we'll read the content off STDIN.
  if arguments.any? || options.scripts.any?
    arguments.each do |pattern|
      Dir
        .glob(pattern)
        .each do |filepath|
          # Skip past invalid filepaths by default.
          next unless File.readable?(filepath)
          # Skip past any ignored filepaths.
          next if options.ignore_files.any? { File.fnmatch(_1, filepath) }
          # Otherwise, a new file item for the given filepath to the list.
          queue << FileItem.new(filepath)
        end
    end
    options.scripts.each { |script| queue << ScriptItem.new(script) }
  else
    queue << STDINItem.new
  end
  # At the end, we're going to return whether or not this worker ever
  # encountered an error.
  if process_queue(queue, action)
    action.failure
    1
  else
    action.success
    0
  end
end