summaryrefslogtreecommitdiff
path: root/lib/irb/color.rb
diff options
context:
space:
mode:
authorTakashi Kokubun <takashikkbn@gmail.com>2019-04-25 21:16:21 +0900
committerTakashi Kokubun <takashikkbn@gmail.com>2019-04-26 00:47:39 +0900
commit94af6cd383f9dc3ae1204a5fba8f56ee7826cbce (patch)
tree42ae2c951135341abf7b891917fa3b10c87c2707 /lib/irb/color.rb
parent790f6709ae418345829d12f053cf270f4d535f1c (diff)
Colorize IRB's code_around_binding
Closes: https://github.com/ruby/ruby/pull/2150
Diffstat (limited to 'lib/irb/color.rb')
-rw-r--r--lib/irb/color.rb83
1 files changed, 83 insertions, 0 deletions
diff --git a/lib/irb/color.rb b/lib/irb/color.rb
new file mode 100644
index 0000000000..150e9e5666
--- /dev/null
+++ b/lib/irb/color.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+require 'ripper'
+
+module IRB # :nodoc:
+ module Color
+ CLEAR = 0
+ BOLD = 1
+ UNDERLINE = 4
+ RED = 31
+ GREEN = 32
+ BLUE = 34
+ MAGENTA = 35
+ CYAN = 36
+
+ TOKEN_KEYWORDS = {
+ on_kw: ['nil', 'self', 'true', 'false'],
+ on_const: ['ENV'],
+ }
+
+ TOKEN_SEQ_EXPRS = {
+ on_CHAR: [[BLUE, BOLD], [Ripper::EXPR_END]],
+ on_const: [[BLUE, BOLD, UNDERLINE], [Ripper::EXPR_ARG, Ripper::EXPR_CMDARG]],
+ on_embexpr_beg: [[RED], [Ripper::EXPR_BEG, Ripper::EXPR_END]],
+ on_embexpr_end: [[RED], [Ripper::EXPR_END, Ripper::EXPR_ENDFN]],
+ on_ident: [[BLUE, BOLD], [Ripper::EXPR_ENDFN]],
+ on_int: [[BLUE, BOLD], [Ripper::EXPR_END]],
+ on_float: [[MAGENTA, BOLD], [Ripper::EXPR_END]],
+ on_kw: [[GREEN], [Ripper::EXPR_CLASS, Ripper::EXPR_BEG, Ripper::EXPR_END, Ripper::EXPR_FNAME]],
+ on_label: [[MAGENTA], [Ripper::EXPR_LABELED]],
+ on_qwords_beg: [[RED], [Ripper::EXPR_BEG]],
+ on_regexp_beg: [[RED], [Ripper::EXPR_BEG]],
+ on_regexp_end: [[RED], [Ripper::EXPR_BEG]],
+ on_symbeg: [[BLUE, BOLD], [Ripper::EXPR_FNAME]],
+ on_tstring_beg: [[RED], [Ripper::EXPR_BEG, Ripper::EXPR_END, Ripper::EXPR_ARG, Ripper::EXPR_CMDARG]],
+ on_tstring_content: [[RED], [Ripper::EXPR_BEG, Ripper::EXPR_ARG, Ripper::EXPR_CMDARG]],
+ on_tstring_end: [[RED], [Ripper::EXPR_END]],
+ }
+
+ class << self
+ def colorable?
+ $stdout.tty? && ENV.key?('TERM')
+ end
+
+ def clear
+ return '' unless colorable?
+ "\e[#{CLEAR}m"
+ end
+
+ def colorize(text, seq)
+ return text unless colorable?
+ "#{seq.map { |s| "\e[#{const_get(s)}m" }.join('')}#{text}#{clear}"
+ end
+
+ def colorize_code(code)
+ return code unless colorable?
+
+ colored = +''
+ Ripper.lex(code).each do |(_line, _col), token, str, expr|
+ if seq = dispatch_seq(token, expr, str)
+ colored << "#{seq.map { |s| "\e[#{s}m" }.join('')}#{str}#{clear}"
+ else
+ colored << str
+ end
+ end
+ colored
+ end
+
+ private
+
+ def dispatch_seq(token, expr, str)
+ if token == :on_comment
+ [BLUE, BOLD]
+ elsif TOKEN_KEYWORDS.fetch(token, []).include?(str)
+ [CYAN, BOLD]
+ elsif (seq, exprs = TOKEN_SEQ_EXPRS[token]; exprs&.any? { |e| (expr & e) != Ripper::EXPR_NONE })
+ seq
+ else
+ nil
+ end
+ end
+ end
+ end
+end