Class: Rich::Syntax
- Inherits:
-
Object
- Object
- Rich::Syntax
- Defined in:
- lib/rich/syntax.rb
Overview
Syntax highlighting for source code. Provides token-based syntax highlighting for multiple programming languages.
Constant Summary collapse
- DEFAULT_THEME =
Default theme for syntax highlighting
{ # Keywords keyword: Style.new(color: Color.parse("magenta"), bold: true), keyword_constant: Style.new(color: Color.parse("cyan"), bold: true), keyword_declaration: Style.new(color: Color.parse("magenta"), bold: true), keyword_namespace: Style.new(color: Color.parse("magenta"), bold: true), keyword_type: Style.new(color: Color.parse("cyan")), # Names name: Style.new(color: Color.parse("white")), name_builtin: Style.new(color: Color.parse("cyan")), name_class: Style.new(color: Color.parse("green"), bold: true), name_constant: Style.new(color: Color.parse("cyan")), name_decorator: Style.new(color: Color.parse("bright_magenta")), name_exception: Style.new(color: Color.parse("green"), bold: true), name_function: Style.new(color: Color.parse("green")), name_variable: Style.new(color: Color.parse("white")), name_tag: Style.new(color: Color.parse("bright_magenta")), name_attribute: Style.new(color: Color.parse("yellow")), # Literals string: Style.new(color: Color.parse("yellow")), string_doc: Style.new(color: Color.parse("yellow"), italic: true), string_escape: Style.new(color: Color.parse("bright_magenta")), string_interpol: Style.new(color: Color.parse("bright_magenta")), string_regex: Style.new(color: Color.parse("bright_yellow")), string_symbol: Style.new(color: Color.parse("bright_green")), number: Style.new(color: Color.parse("cyan")), number_float: Style.new(color: Color.parse("cyan")), number_hex: Style.new(color: Color.parse("cyan")), # Operators and Punctuation operator: Style.new(color: Color.parse("bright_magenta")), punctuation: Style.new(color: Color.parse("white")), # Comments comment: Style.new(color: Color.parse("bright_black"), italic: true), comment_doc: Style.new(color: Color.parse("bright_black"), italic: true), comment_preproc: Style.new(color: Color.parse("bright_magenta")), # Generic generic_deleted: Style.new(color: Color.parse("red")), generic_inserted: Style.new(color: Color.parse("green")), generic_heading: Style.new(color: Color.parse("bright_blue"), bold: true), generic_subheading: Style.new(color: Color.parse("bright_blue")), generic_error: Style.new(color: Color.parse("bright_red")), # Other text: Style.new, error: Style.new(color: Color.parse("bright_red"), bold: true) }.freeze
- MONOKAI_THEME =
Monokai theme
{ keyword: Style.new(color: Color.parse("#f92672"), bold: true), keyword_constant: Style.new(color: Color.parse("#ae81ff")), keyword_type: Style.new(color: Color.parse("#66d9ef"), italic: true), name: Style.new(color: Color.parse("#f8f8f2")), name_builtin: Style.new(color: Color.parse("#66d9ef")), name_class: Style.new(color: Color.parse("#a6e22e")), name_function: Style.new(color: Color.parse("#a6e22e")), name_decorator: Style.new(color: Color.parse("#a6e22e")), string: Style.new(color: Color.parse("#e6db74")), string_doc: Style.new(color: Color.parse("#e6db74")), number: Style.new(color: Color.parse("#ae81ff")), operator: Style.new(color: Color.parse("#f92672")), comment: Style.new(color: Color.parse("#75715e"), italic: true), punctuation: Style.new(color: Color.parse("#f8f8f2")), text: Style.new(color: Color.parse("#f8f8f2")), error: Style.new(color: Color.parse("#f92672"), bold: true) }.freeze
- DRACULA_THEME =
Dracula theme
{ keyword: Style.new(color: Color.parse("#ff79c6"), bold: true), keyword_constant: Style.new(color: Color.parse("#bd93f9")), keyword_type: Style.new(color: Color.parse("#8be9fd"), italic: true), name: Style.new(color: Color.parse("#f8f8f2")), name_builtin: Style.new(color: Color.parse("#8be9fd")), name_class: Style.new(color: Color.parse("#50fa7b")), name_function: Style.new(color: Color.parse("#50fa7b")), name_decorator: Style.new(color: Color.parse("#50fa7b")), string: Style.new(color: Color.parse("#f1fa8c")), string_doc: Style.new(color: Color.parse("#6272a4")), number: Style.new(color: Color.parse("#bd93f9")), operator: Style.new(color: Color.parse("#ff79c6")), comment: Style.new(color: Color.parse("#6272a4"), italic: true), punctuation: Style.new(color: Color.parse("#f8f8f2")), text: Style.new(color: Color.parse("#f8f8f2")), error: Style.new(color: Color.parse("#ff5555"), bold: true) }.freeze
- THEMES =
{ default: DEFAULT_THEME, monokai: MONOKAI_THEME, dracula: DRACULA_THEME }.freeze
Instance Attribute Summary collapse
-
#background_style ⇒ Style?
readonly
Background style.
-
#code ⇒ String
readonly
Source code.
-
#highlight_lines ⇒ Array<Integer>?
readonly
Lines to highlight.
-
#language ⇒ String
readonly
Language name.
-
#line_numbers ⇒ Boolean
readonly
Show line numbers.
-
#start_line ⇒ Integer?
readonly
Starting line number.
-
#tab_size ⇒ Integer
readonly
Tab size.
-
#theme ⇒ Hash
readonly
Theme styles.
-
#word_wrap ⇒ Boolean
readonly
Word wrap.
Class Method Summary collapse
-
.detect_language(path) ⇒ String
Detect language from file extension.
-
.from_file(path, **kwargs) ⇒ Syntax
Create syntax from file.
-
.supported_languages ⇒ Array<String>
List supported languages.
Instance Method Summary collapse
-
#highlight_line(line) ⇒ Array<Segment>
Highlight a single line.
-
#initialize(code, language: "text", theme: :default, line_numbers: false, start_line: 1, highlight_lines: nil, word_wrap: false, background_style: nil, tab_size: 4) ⇒ Syntax
constructor
A new instance of Syntax.
-
#render(color_system: ColorSystem::TRUECOLOR) ⇒ String
Render to string with ANSI codes.
-
#to_panel(title: nil, max_width: 80) ⇒ String
Render inside a panel.
-
#to_segments ⇒ Array<Segment>
Highlight the code and return segments.
Constructor Details
#initialize(code, language: "text", theme: :default, line_numbers: false, start_line: 1, highlight_lines: nil, word_wrap: false, background_style: nil, tab_size: 4) ⇒ Syntax
Returns a new instance of Syntax.
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/rich/syntax.rb', line 139 def initialize( code, language: "text", theme: :default, line_numbers: false, start_line: 1, highlight_lines: nil, word_wrap: false, background_style: nil, tab_size: 4 ) @code = code.to_s @language = language.to_s.downcase @theme = theme.is_a?(Hash) ? theme : (THEMES[theme] || DEFAULT_THEME) @line_numbers = line_numbers @start_line = start_line @highlight_lines = highlight_lines @word_wrap = word_wrap @background_style = background_style @tab_size = tab_size end |
Instance Attribute Details
#background_style ⇒ Style? (readonly)
Returns Background style.
134 135 136 |
# File 'lib/rich/syntax.rb', line 134 def background_style @background_style end |
#code ⇒ String (readonly)
Returns Source code.
113 114 115 |
# File 'lib/rich/syntax.rb', line 113 def code @code end |
#highlight_lines ⇒ Array<Integer>? (readonly)
Returns Lines to highlight.
128 129 130 |
# File 'lib/rich/syntax.rb', line 128 def highlight_lines @highlight_lines end |
#language ⇒ String (readonly)
Returns Language name.
116 117 118 |
# File 'lib/rich/syntax.rb', line 116 def language @language end |
#line_numbers ⇒ Boolean (readonly)
Returns Show line numbers.
122 123 124 |
# File 'lib/rich/syntax.rb', line 122 def line_numbers @line_numbers end |
#start_line ⇒ Integer? (readonly)
Returns Starting line number.
125 126 127 |
# File 'lib/rich/syntax.rb', line 125 def start_line @start_line end |
#tab_size ⇒ Integer (readonly)
Returns Tab size.
137 138 139 |
# File 'lib/rich/syntax.rb', line 137 def tab_size @tab_size end |
#theme ⇒ Hash (readonly)
Returns Theme styles.
119 120 121 |
# File 'lib/rich/syntax.rb', line 119 def theme @theme end |
#word_wrap ⇒ Boolean (readonly)
Returns Word wrap.
131 132 133 |
# File 'lib/rich/syntax.rb', line 131 def word_wrap @word_wrap end |
Class Method Details
.detect_language(path) ⇒ String
Detect language from file extension
241 242 243 244 |
# File 'lib/rich/syntax.rb', line 241 def detect_language(path) ext = File.extname(path).downcase.delete(".") EXTENSION_MAP[ext] || "text" end |
.from_file(path, **kwargs) ⇒ Syntax
Create syntax from file
232 233 234 235 236 |
# File 'lib/rich/syntax.rb', line 232 def from_file(path, **kwargs) code = File.read(path) language = kwargs.delete(:language) || detect_language(path) new(code, language: language, **kwargs) end |
.supported_languages ⇒ Array<String>
List supported languages
248 249 250 |
# File 'lib/rich/syntax.rb', line 248 def supported_languages LEXERS.keys.sort end |
Instance Method Details
#highlight_line(line) ⇒ Array<Segment>
Highlight a single line
201 202 203 204 |
# File 'lib/rich/syntax.rb', line 201 def highlight_line(line) lexer = get_lexer(@language) lexer.tokenize(line, @theme) end |
#render(color_system: ColorSystem::TRUECOLOR) ⇒ String
Render to string with ANSI codes
209 210 211 |
# File 'lib/rich/syntax.rb', line 209 def render(color_system: ColorSystem::TRUECOLOR) Segment.render(to_segments, color_system: color_system) end |
#to_panel(title: nil, max_width: 80) ⇒ String
Render inside a panel
216 217 218 219 220 221 222 223 224 225 |
# File 'lib/rich/syntax.rb', line 216 def to_panel(title: nil, max_width: 80) title ||= @language.capitalize panel = Panel.new( render, title: title, border_style: "dim", padding: 0 ) panel.render(max_width: max_width) end |
#to_segments ⇒ Array<Segment>
Highlight the code and return segments
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/rich/syntax.rb', line 163 def to_segments segments = [] lines = @code.gsub("\t", " " * @tab_size).split("\n", -1) # Calculate line number width line_num_width = (@start_line + lines.length - 1).to_s.length lines.each_with_index do |line, index| line_num = @start_line + index is_highlighted = @highlight_lines&.include?(line_num) # Line number if @line_numbers num_style = is_highlighted ? Style.new(color: Color.parse("yellow"), bold: true) : Style.new(color: Color.parse("bright_black")) segments << Segment.new(line_num.to_s.rjust(line_num_width), style: num_style) segments << Segment.new(" │ ", style: Style.new(color: Color.parse("bright_black"))) end # Highlighted line background if is_highlighted bg_style = Style.new(bgcolor: Color.parse("color(237)")) segments.concat(highlight_line(line).map do |seg| combined_style = seg.style ? seg.style + bg_style : bg_style Segment.new(seg.text, style: combined_style) end) else segments.concat(highlight_line(line)) end segments << Segment.new("\n") if index < lines.length - 1 end segments end |