Skip to content

Commit 2da64a7

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 ee2af9f commit 2da64a7

2 files changed

Lines changed: 132 additions & 91 deletions

File tree

lib/irb/color.rb

Lines changed: 131 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -18,103 +18,114 @@ 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_LOOP: [GREEN],
73-
KEYWORD_FOR: [GREEN],
74-
KEYWORD_BEGIN: [GREEN],
75-
KEYWORD_RESCUE: [GREEN],
76-
KEYWORD_ENSURE: [GREEN],
77-
KEYWORD_ALIAS: [GREEN],
78-
KEYWORD_UNDEF: [GREEN],
79-
KEYWORD_BEGIN_UPCASE: [GREEN],
80-
KEYWORD_END_UPCASE: [GREEN],
81-
KEYWORD_YIELD: [GREEN],
82-
KEYWORD_REDO: [GREEN],
83-
KEYWORD_RETRY: [GREEN],
84-
KEYWORD_NEXT: [GREEN],
85-
KEYWORD_BREAK: [GREEN],
86-
KEYWORD_SUPER: [GREEN],
87-
KEYWORD_RETURN: [GREEN],
88-
KEYWORD_DEFINED: [GREEN],
89-
KEYWORD_NOT: [GREEN],
90-
KEYWORD_AND: [GREEN],
91-
KEYWORD_OR: [GREEN],
92-
LABEL: [MAGENTA],
93-
LABEL_END: [RED, BOLD],
94-
NUMBERED_REFERENCE: [GREEN, BOLD],
95-
PERCENT_UPPER_W: [RED, BOLD],
96-
PERCENT_LOWER_W: [RED, BOLD],
97-
PERCENT_LOWER_X: [RED, BOLD],
98-
REGEXP_BEGIN: [RED, BOLD],
99-
REGEXP_END: [RED, BOLD],
100-
STRING_BEGIN: [RED, BOLD],
101-
STRING_CONTENT: [RED],
102-
STRING_END: [RED, BOLD],
103-
__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_LOOP: :keyword,
72+
KEYWORD_FOR: :keyword,
73+
KEYWORD_BEGIN: :keyword,
74+
KEYWORD_RESCUE: :keyword,
75+
KEYWORD_ENSURE: :keyword,
76+
KEYWORD_ALIAS: :keyword,
77+
KEYWORD_UNDEF: :keyword,
78+
KEYWORD_BEGIN_UPCASE: :keyword,
79+
KEYWORD_END_UPCASE: :keyword,
80+
KEYWORD_YIELD: :keyword,
81+
KEYWORD_REDO: :keyword,
82+
KEYWORD_RETRY: :keyword,
83+
KEYWORD_NEXT: :keyword,
84+
KEYWORD_BREAK: :keyword,
85+
KEYWORD_SUPER: :keyword,
86+
KEYWORD_RETURN: :keyword,
87+
KEYWORD_DEFINED: :keyword,
88+
KEYWORD_NOT: :keyword,
89+
KEYWORD_AND: :keyword,
90+
KEYWORD_OR: :keyword,
91+
LABEL: :label,
92+
LABEL_END: :string_edge,
93+
NUMBERED_REFERENCE: :global_variable,
94+
PERCENT_UPPER_W: :string_edge,
95+
PERCENT_LOWER_W: :string_edge,
96+
PERCENT_LOWER_X: :string_edge,
97+
REGEXP_BEGIN: :string_edge,
98+
REGEXP_END: :string_edge,
99+
STRING_BEGIN: :string_edge,
100+
STRING_CONTENT: :string_body,
101+
STRING_END: :string_edge,
102+
__END__: :keyword,
104103
# tokens from syntax tree traversal
105-
method_name: [CYAN, BOLD],
106-
message_name: [CYAN],
107-
symbol: [YELLOW],
104+
method_name: :method_name,
105+
message_name: :message_name,
106+
symbol: :symbol,
108107
# special colorization
109-
error: [RED, REVERSE],
110-
}.transform_values do |styles|
111-
styles.map { |style| "\e[#{style}m" }.join
112-
end
108+
error: :error,
109+
}
110+
111+
TOKEN_SEQS = {}
113112
CLEAR_SEQ = "\e[#{CLEAR}m"
114113
OPERATORS = %i(!= !~ =~ == === <=> > >= < <= & | ^ >> << - + % / * ** -@ +@ ~ ! [] []=)
115-
private_constant :TOKEN_SEQS, :CLEAR_SEQ, :OPERATORS
114+
private_constant :TOKEN_FACES, :TOKEN_SEQS, :CLEAR_SEQ, :OPERATORS
116115

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

@@ -326,6 +337,15 @@ def dispatch_alias_method_name(node)
326337
:embdoc_term, # `=begin`
327338
]
328339

340+
# Normalize SGR sequences for existing test cases
341+
def normalize_sgr(seq)
342+
s = seq.sub(/\A\e\[0m/, "")
343+
return s if s.match?(/\e\[(38|48|58);/) # Do not normalize extended colors
344+
s.gsub(/\e\[([0-9;]+)m/) {
345+
$1.split(/;/).map { |i| "\e[#{i}m" }.join
346+
}
347+
end
348+
329349
# Filter out syntax errors that are likely to be caused by incomplete code, to avoid showing misleading error highlights to users.
330350
def filter_incomplete_code_errors(errors, tokens)
331351
last_non_comment_space_token, = tokens.reverse_each.find do |t,|
@@ -348,3 +368,23 @@ def without_circular_ref(obj, seen:, &block)
348368
end
349369
end
350370
end
371+
372+
# Following pry's colors where possible
373+
Reline::Face.config(:syntax_highlighting) do |conf|
374+
conf.define :pseudo_variable, foreground: :cyan, style: :bold
375+
conf.define :global_variable, foreground: :green, style: :bold
376+
conf.define :constant, foreground: :blue, style: [:bold, :underlined]
377+
conf.define :comment, foreground: :blue, style: :bold
378+
conf.define :string_edge, foreground: :red, style: :bold
379+
conf.define :string_body, foreground: :red
380+
conf.define :symbol, foreground: :yellow
381+
conf.define :number, foreground: :blue, style: :bold
382+
conf.define :float, foreground: :magenta, style: :bold
383+
conf.define :keyword, foreground: :green
384+
conf.define :label, foreground: :magenta
385+
conf.define :method_name, foreground: :cyan, style: :bold
386+
conf.define :message_name, foreground: :cyan
387+
conf.define :error, foreground: :red, style: :negative
388+
end
389+
390+
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)