模块 IRB::Color

常量

ALL

一个全为 1 的常量,用于匹配 dispatch_seq 中的任何 Ripper 状态

BLACK
BLUE
BOLD
CLEAR
CYAN
ERROR_TOKENS
GREEN
MAGENTA
RED
REVERSE
TOKEN_KEYWORDS
TOKEN_SEQ_EXPRS

尽可能遵循 pry 的颜色,但有时需要折衷,例如将反引号和正则表达式设为红色(字符串的颜色,因为它们共享标记)。

UNDERLINE
WHITE
YELLOW

公共类方法

clear(colorable: colorable?) 点击切换源代码
# File lib/irb/color.rb, line 114
def clear(colorable: colorable?)
  return '' unless colorable
  "\e[#{CLEAR}m"
end
colorable?() 点击切换源代码
# File lib/irb/color.rb, line 81
def colorable?
  supported = $stdout.tty? && (/mswin|mingw/ =~ RUBY_PLATFORM || (ENV.key?('TERM') && ENV['TERM'] != 'dumb'))

  # because ruby/debug also uses irb's color module selectively,
  # irb won't be activated in that case.
  if IRB.respond_to?(:conf)
    supported && IRB.conf.fetch(:USE_COLORIZE, true)
  else
    supported
  end
end
colorize(text, seq, colorable: colorable?) 点击切换源代码
# File lib/irb/color.rb, line 119
def colorize(text, seq, colorable: colorable?)
  return text unless colorable
  seq = seq.map { |s| "\e[#{const_get(s)}m" }.join('')
  "#{seq}#{text}#{clear(colorable: colorable)}"
end
colorize_code(code, complete: true, ignore_error: false, colorable: colorable?, local_variables: []) 点击切换源代码

如果 `complete` 为 false(代码不完整),则不会发出编译错误警告。此选项是必要的,以避免在编译错误发生时发出警告,因为输入不是错误的,只是不完整。

# File lib/irb/color.rb, line 128
def colorize_code(code, complete: true, ignore_error: false, colorable: colorable?, local_variables: [])
  return code unless colorable

  symbol_state = SymbolState.new
  colored = +''
  lvars_code = RubyLex.generate_local_variables_assign_code(local_variables)
  code_with_lvars = lvars_code ? "#{lvars_code}\n#{code}" : code

  scan(code_with_lvars, allow_last_error: !complete) do |token, str, expr|
    # handle uncolorable code
    if token.nil?
      colored << Reline::Unicode.escape_for_print(str)
      next
    end

    # IRB::ColorPrinter skips colorizing fragments with any invalid token
    if ignore_error && ERROR_TOKENS.include?(token)
      return Reline::Unicode.escape_for_print(code)
    end

    in_symbol = symbol_state.scan_token(token)
    str.each_line do |line|
      line = Reline::Unicode.escape_for_print(line)
      if seq = dispatch_seq(token, expr, line, in_symbol: in_symbol)
        colored << seq.map { |s| "\e[#{s}m" }.join('')
        colored << line.sub(/\Z/, clear(colorable: colorable))
      else
        colored << line
      end
    end
  end

  if lvars_code
    raise "#{lvars_code.dump} should have no \\n" if lvars_code.include?("\n")
    colored.sub!(/\A.+\n/, '') # delete_prefix lvars_code with colors
  end
  colored
end
inspect_colorable?(obj, seen: {}.compare_by_identity) 点击切换源代码
# File lib/irb/color.rb, line 93
def inspect_colorable?(obj, seen: {}.compare_by_identity)
  case obj
  when String, Symbol, Regexp, Integer, Float, FalseClass, TrueClass, NilClass
    true
  when Hash
    without_circular_ref(obj, seen: seen) do
      obj.all? { |k, v| inspect_colorable?(k, seen: seen) && inspect_colorable?(v, seen: seen) }
    end
  when Array
    without_circular_ref(obj, seen: seen) do
      obj.all? { |o| inspect_colorable?(o, seen: seen) }
    end
  when Range
    inspect_colorable?(obj.begin, seen: seen) && inspect_colorable?(obj.end, seen: seen)
  when Module
    !obj.name.nil?
  else
    false
  end
end

私有类方法

dispatch_seq(token, expr, str, in_symbol:) 点击切换源代码
# File lib/irb/color.rb, line 213
def dispatch_seq(token, expr, str, in_symbol:)
  if ERROR_TOKENS.include?(token)
    TOKEN_SEQ_EXPRS[token][0]
  elsif in_symbol
    [YELLOW]
  elsif TOKEN_KEYWORDS.fetch(token, []).include?(str)
    [CYAN, BOLD]
  elsif (seq, exprs = TOKEN_SEQ_EXPRS[token]; (expr & (exprs || 0)) != 0)
    seq
  else
    nil
  end
end
scan(code, allow_last_error:) { |nil, byteslice, nil| ... } 点击切换源代码
# File lib/irb/color.rb, line 177
def scan(code, allow_last_error:)
  verbose, $VERBOSE = $VERBOSE, nil
  RubyLex.compile_with_errors_suppressed(code) do |inner_code, line_no|
    lexer = Ripper::Lexer.new(inner_code, '(ripper)', line_no)
    byte_pos = 0
    line_positions = [0]
    inner_code.lines.each do |line|
      line_positions << line_positions.last + line.bytesize
    end

    on_scan = proc do |elem|
      start_pos = line_positions[elem.pos[0] - 1] + elem.pos[1]

      # yield uncolorable code
      if byte_pos < start_pos
        yield(nil, inner_code.byteslice(byte_pos...start_pos), nil)
      end

      if byte_pos <= start_pos
        str = elem.tok
        yield(elem.event, str, elem.state)
        byte_pos = start_pos + str.bytesize
      end
    end

    lexer.scan.each do |elem|
      next if allow_last_error and /meets end of file|unexpected end-of-input/ =~ elem.message
      on_scan.call(elem)
    end
    # yield uncolorable DATA section
    yield(nil, inner_code.byteslice(byte_pos...inner_code.bytesize), nil) if byte_pos < inner_code.bytesize
  end
ensure
  $VERBOSE = verbose
end
without_circular_ref(obj, seen:, &block) 点击切换源代码
# File lib/irb/color.rb, line 169
def without_circular_ref(obj, seen:, &block)
  return false if seen.key?(obj)
  seen[obj] = true
  block.call
ensure
  seen.delete(obj)
end