Skip to content

Commit b5d29f9

Browse files
committed
Support syntax highlighting customization
For example, put the following code in ~/.irbrc: ```ruby Reline::Face.config(:syntax_highlighting) do |conf| conf.define :pseudo_variable, foreground: "#4dacfd", style: :bold conf.define :global_variable, foreground: "#4dacfd", style: :bold conf.define :constant, foreground: "#ffa657", style: :bold conf.define :comment, foreground: "#abaeb1", style: :bold conf.define :string_edge, foreground: "#c1daec", style: :bold conf.define :string_body, foreground: "#c1daec" conf.define :symbol, foreground: "#c1daec" conf.define :number, foreground: "#4dacfd", style: :bold conf.define :float, foreground: "#4dacfd", style: :bold conf.define :keyword, foreground: "#f16636" conf.define :label, foreground: "#4dacfd" conf.define :method_name, foreground: "#a887e6", style: :bold conf.define :message_name, foreground: "#a887e6" conf.define :error, foreground: "#f16636", style: :negative end ```
1 parent bf32202 commit b5d29f9

2 files changed

Lines changed: 133 additions & 92 deletions

File tree

lib/irb/color.rb

Lines changed: 132 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -18,104 +18,115 @@ module Color
1818
CYAN = 36
1919
WHITE = 37
2020

21-
# Following pry's colors where possible
22-
TOKEN_SEQS = {
23-
KEYWORD_NIL: [CYAN, BOLD],
24-
KEYWORD_SELF: [CYAN, BOLD],
25-
KEYWORD_TRUE: [CYAN, BOLD],
26-
KEYWORD_FALSE: [CYAN, BOLD],
27-
KEYWORD___FILE__: [CYAN, BOLD],
28-
KEYWORD___LINE__: [CYAN, BOLD],
29-
KEYWORD___ENCODING__: [CYAN, BOLD],
30-
CHARACTER_LITERAL: [BLUE, BOLD],
31-
BACK_REFERENCE: [GREEN, BOLD],
32-
BACKTICK: [RED, BOLD],
33-
COMMENT: [BLUE, BOLD],
34-
EMBDOC_BEGIN: [BLUE, BOLD],
35-
EMBDOC_LINE: [BLUE, BOLD],
36-
EMBDOC_END: [BLUE, BOLD],
37-
CONSTANT: [BLUE, BOLD, UNDERLINE],
38-
EMBEXPR_BEGIN: [RED],
39-
EMBEXPR_END: [RED],
40-
EMBVAR: [RED],
41-
FLOAT: [MAGENTA, BOLD],
42-
GLOBAL_VARIABLE: [GREEN, BOLD],
43-
HEREDOC_START: [RED],
44-
HEREDOC_END: [RED],
45-
FLOAT_IMAGINARY: [BLUE, BOLD],
46-
INTEGER_IMAGINARY: [BLUE, BOLD],
47-
FLOAT_RATIONAL_IMAGINARY: [BLUE, BOLD],
48-
INTEGER_RATIONAL_IMAGINARY: [BLUE, BOLD],
49-
INTEGER: [BLUE, BOLD],
50-
INTEGER_RATIONAL: [BLUE, BOLD],
51-
FLOAT_RATIONAL: [BLUE, BOLD],
52-
KEYWORD_END: [GREEN],
53-
KEYWORD_CLASS: [GREEN],
54-
KEYWORD_MODULE: [GREEN],
55-
KEYWORD_IF: [GREEN],
56-
KEYWORD_IF_MODIFIER: [GREEN],
57-
KEYWORD_UNLESS_MODIFIER: [GREEN],
58-
KEYWORD_WHILE_MODIFIER: [GREEN],
59-
KEYWORD_UNTIL_MODIFIER: [GREEN],
60-
KEYWORD_RESCUE_MODIFIER: [GREEN],
61-
KEYWORD_THEN: [GREEN],
62-
KEYWORD_UNLESS: [GREEN],
63-
KEYWORD_ELSE: [GREEN],
64-
KEYWORD_ELSIF: [GREEN],
65-
KEYWORD_WHILE: [GREEN],
66-
KEYWORD_UNTIL: [GREEN],
67-
KEYWORD_CASE: [GREEN],
68-
KEYWORD_WHEN: [GREEN],
69-
KEYWORD_IN: [GREEN],
70-
KEYWORD_DEF: [GREEN],
71-
KEYWORD_DO: [GREEN],
72-
KEYWORD_DO_BLOCK: [GREEN],
73-
KEYWORD_DO_LOOP: [GREEN],
74-
KEYWORD_FOR: [GREEN],
75-
KEYWORD_BEGIN: [GREEN],
76-
KEYWORD_RESCUE: [GREEN],
77-
KEYWORD_ENSURE: [GREEN],
78-
KEYWORD_ALIAS: [GREEN],
79-
KEYWORD_UNDEF: [GREEN],
80-
KEYWORD_BEGIN_UPCASE: [GREEN],
81-
KEYWORD_END_UPCASE: [GREEN],
82-
KEYWORD_YIELD: [GREEN],
83-
KEYWORD_REDO: [GREEN],
84-
KEYWORD_RETRY: [GREEN],
85-
KEYWORD_NEXT: [GREEN],
86-
KEYWORD_BREAK: [GREEN],
87-
KEYWORD_SUPER: [GREEN],
88-
KEYWORD_RETURN: [GREEN],
89-
KEYWORD_DEFINED: [GREEN],
90-
KEYWORD_NOT: [GREEN],
91-
KEYWORD_AND: [GREEN],
92-
KEYWORD_OR: [GREEN],
93-
LABEL: [MAGENTA],
94-
LABEL_END: [RED, BOLD],
95-
NUMBERED_REFERENCE: [GREEN, BOLD],
96-
PERCENT_UPPER_W: [RED, BOLD],
97-
PERCENT_LOWER_W: [RED, BOLD],
98-
PERCENT_LOWER_X: [RED, BOLD],
99-
REGEXP_BEGIN: [RED, BOLD],
100-
REGEXP_END: [RED, BOLD],
101-
STRING_BEGIN: [RED, BOLD],
102-
STRING_CONTENT: [RED],
103-
STRING_END: [RED, BOLD],
104-
__END__: [GREEN],
21+
TOKEN_FACES = {
22+
KEYWORD_NIL: :pseudo_variable,
23+
KEYWORD_SELF: :pseudo_variable,
24+
KEYWORD_TRUE: :pseudo_variable,
25+
KEYWORD_FALSE: :pseudo_variable,
26+
KEYWORD___FILE__: :pseudo_variable,
27+
KEYWORD___LINE__: :pseudo_variable,
28+
KEYWORD___ENCODING__: :pseudo_variable,
29+
CHARACTER_LITERAL: :number,
30+
BACK_REFERENCE: :global_variable,
31+
BACKTICK: :string_edge,
32+
COMMENT: :comment,
33+
EMBDOC_BEGIN: :comment,
34+
EMBDOC_LINE: :comment,
35+
EMBDOC_END: :comment,
36+
CONSTANT: :constant,
37+
EMBEXPR_BEGIN: :string_body,
38+
EMBEXPR_END: :string_body,
39+
EMBVAR: :string_body,
40+
FLOAT: :float,
41+
GLOBAL_VARIABLE: :global_variable,
42+
HEREDOC_START: :string_body,
43+
HEREDOC_END: :string_body,
44+
FLOAT_IMAGINARY: :number,
45+
INTEGER_IMAGINARY: :number,
46+
FLOAT_RATIONAL_IMAGINARY: :number,
47+
INTEGER_RATIONAL_IMAGINARY: :number,
48+
INTEGER: :number,
49+
INTEGER_RATIONAL: :number,
50+
FLOAT_RATIONAL: :number,
51+
KEYWORD_END: :keyword,
52+
KEYWORD_CLASS: :keyword,
53+
KEYWORD_MODULE: :keyword,
54+
KEYWORD_IF: :keyword,
55+
KEYWORD_IF_MODIFIER: :keyword,
56+
KEYWORD_UNLESS_MODIFIER: :keyword,
57+
KEYWORD_WHILE_MODIFIER: :keyword,
58+
KEYWORD_UNTIL_MODIFIER: :keyword,
59+
KEYWORD_RESCUE_MODIFIER: :keyword,
60+
KEYWORD_THEN: :keyword,
61+
KEYWORD_UNLESS: :keyword,
62+
KEYWORD_ELSE: :keyword,
63+
KEYWORD_ELSIF: :keyword,
64+
KEYWORD_WHILE: :keyword,
65+
KEYWORD_UNTIL: :keyword,
66+
KEYWORD_CASE: :keyword,
67+
KEYWORD_WHEN: :keyword,
68+
KEYWORD_IN: :keyword,
69+
KEYWORD_DEF: :keyword,
70+
KEYWORD_DO: :keyword,
71+
KEYWORD_DO_BLOCK: :keyword,
72+
KEYWORD_DO_LOOP: :keyword,
73+
KEYWORD_FOR: :keyword,
74+
KEYWORD_BEGIN: :keyword,
75+
KEYWORD_RESCUE: :keyword,
76+
KEYWORD_ENSURE: :keyword,
77+
KEYWORD_ALIAS: :keyword,
78+
KEYWORD_UNDEF: :keyword,
79+
KEYWORD_BEGIN_UPCASE: :keyword,
80+
KEYWORD_END_UPCASE: :keyword,
81+
KEYWORD_YIELD: :keyword,
82+
KEYWORD_REDO: :keyword,
83+
KEYWORD_RETRY: :keyword,
84+
KEYWORD_NEXT: :keyword,
85+
KEYWORD_BREAK: :keyword,
86+
KEYWORD_SUPER: :keyword,
87+
KEYWORD_RETURN: :keyword,
88+
KEYWORD_DEFINED: :keyword,
89+
KEYWORD_NOT: :keyword,
90+
KEYWORD_AND: :keyword,
91+
KEYWORD_OR: :keyword,
92+
LABEL: :label,
93+
LABEL_END: :string_edge,
94+
NUMBERED_REFERENCE: :global_variable,
95+
PERCENT_UPPER_W: :string_edge,
96+
PERCENT_LOWER_W: :string_edge,
97+
PERCENT_LOWER_X: :string_edge,
98+
REGEXP_BEGIN: :string_edge,
99+
REGEXP_END: :string_edge,
100+
STRING_BEGIN: :string_edge,
101+
STRING_CONTENT: :string_body,
102+
STRING_END: :string_edge,
103+
__END__: :keyword,
105104
# tokens from syntax tree traversal
106-
method_name: [CYAN, BOLD],
107-
message_name: [CYAN],
108-
symbol: [YELLOW],
105+
method_name: :method_name,
106+
message_name: :message_name,
107+
symbol: :symbol,
109108
# special colorization
110-
error: [RED, REVERSE],
111-
}.transform_values do |styles|
112-
styles.map { |style| "\e[#{style}m" }.join
113-
end
109+
error: :error,
110+
}
111+
112+
TOKEN_SEQS = {}
114113
CLEAR_SEQ = "\e[#{CLEAR}m"
115114
OPERATORS = %i(!= !~ =~ == === <=> > >= < <= & | ^ >> << - + % / * ** -@ +@ ~ ! [] []=)
116-
private_constant :TOKEN_SEQS, :CLEAR_SEQ, :OPERATORS
115+
private_constant :TOKEN_FACES, :TOKEN_SEQS, :CLEAR_SEQ, :OPERATORS
117116

118117
class << self
118+
def init
119+
face_conf = Reline::Face[:syntax_highlighting]
120+
face_seqs = {}
121+
face_conf.definition.each do |key, val|
122+
face_seqs[key] = normalize_sgr(val[:escape_sequence])
123+
end
124+
TOKEN_SEQS.clear
125+
TOKEN_FACES.each do |key, val|
126+
TOKEN_SEQS[key] = face_seqs[val]
127+
end
128+
end
129+
119130
def colorable?
120131
supported = $stdout.tty? && (/mswin|mingw/.match?(RUBY_PLATFORM) || (ENV.key?('TERM') && ENV['TERM'] != 'dumb'))
121132

@@ -332,6 +343,15 @@ def dispatch_alias_method_name(node)
332343
:embdoc_term, # `=begin`
333344
]
334345

346+
# Normalize SGR sequences for existing test cases
347+
def normalize_sgr(seq)
348+
s = seq.sub(/\A\e\[0m/, "")
349+
return s if s.match?(/\e\[(38|48|58);/) # Do not normalize extended colors
350+
s.gsub(/\e\[([0-9;]+)m/) {
351+
$1.split(/;/).map { |i| "\e[#{i}m" }.join
352+
}
353+
end
354+
335355
# Filter out syntax errors that are likely to be caused by incomplete code, to avoid showing misleading error highlights to users.
336356
def filter_incomplete_code_errors(errors, tokens)
337357
last_non_comment_space_token, = tokens.reverse_each.find do |t,|
@@ -354,3 +374,23 @@ def without_circular_ref(obj, seen:, &block)
354374
end
355375
end
356376
end
377+
378+
# Following pry's colors where possible
379+
Reline::Face.config(:syntax_highlighting) do |conf|
380+
conf.define :pseudo_variable, foreground: :cyan, style: :bold
381+
conf.define :global_variable, foreground: :green, style: :bold
382+
conf.define :constant, foreground: :blue, style: [:bold, :underlined]
383+
conf.define :comment, foreground: :blue, style: :bold
384+
conf.define :string_edge, foreground: :red, style: :bold
385+
conf.define :string_body, foreground: :red
386+
conf.define :symbol, foreground: :yellow
387+
conf.define :number, foreground: :blue, style: :bold
388+
conf.define :float, foreground: :magenta, style: :bold
389+
conf.define :keyword, foreground: :green
390+
conf.define :label, foreground: :magenta
391+
conf.define :method_name, foreground: :cyan, style: :bold
392+
conf.define :message_name, foreground: :cyan
393+
conf.define :error, foreground: :red, style: :negative
394+
end
395+
396+
IRB::Color.init

lib/irb/init.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def IRB.setup(ap_path, argv: ::ARGV)
5454
IRB.run_config
5555
IRB.validate_config
5656
IRB.load_modules
57+
IRB::Color.init
5758

5859
unless @CONF[:PROMPT][@CONF[:PROMPT_MODE]]
5960
fail UndefinedPromptMode, @CONF[:PROMPT_MODE]

0 commit comments

Comments
 (0)