Skip to content

Instantly share code, notes, and snippets.

@joemasilotti
Last active June 19, 2024 03:59
Show Gist options
  • Save joemasilotti/f0ff19c464a97a2f213c97ba97a3de43 to your computer and use it in GitHub Desktop.
Save joemasilotti/f0ff19c464a97a2f213c97ba97a3de43 to your computer and use it in GitHub Desktop.
Rouge syntax highlighting with line focusing

This gist shows how I'm using Rouge to highlight individual lines of code. On top of the existing syntax highlighting.

I'm using Sitepress which uses markdown-rails under the hood. But this should be applicable to any application rendering markdown with Redcarpet - sub out ApplicationMarkdown as your renderer.

Append @[] when setting the language in a fenced code block to highlight lines of code. Dashes will cover the range, inclusive. Commas will highlight each line.

Examples:

```swift@[4-6]
class Person {
    private let name: String
    
    init(name: String) {
        self.name = name
    }
}
```

image

```ruby@[2,5]
class Person
  attr_reader :name

  def initialize(name)
    @name = name
  end
end
```

image

<%# locals: (content:, language:) %>
<div class="full-width">
<div class="max-w-3xl mx-auto px-1">
<pre><code><%= content %></code></pre>
</div>
</div>
class ApplicationMarkdown < MarkdownRails::Renderer::Rails
def block_code(code, language)
content = raw CodeFormatter.new(code, language).formatted_code
render partial: "content/code", locals: {content: content, language:}
end
def renderer
renderer = self.class.new(render_options)
Redcarpet::Markdown.new(renderer, extensions)
end
private
def render_options
{
filter_html: true,
no_images: true,
no_styles: true,
safe_links_only: true,
with_toc_data: true
}
end
def extensions
{
disable_indented_code_blocks: true,
fenced_code_blocks: true
}
end
end
/* Syntax highlighting */
pre code { background: #1c1917; color: #cccccc }
pre code .c { color: #999999 }
pre code .err { color: #f2777a }
/* ... */
/* Code highlighting */
pre:has(.hll) span:not(.hll) {
opacity: .65;
}
pre span.hll span {
opacity: 1 !important;
}
pre:has(.hll):hover span:not(.hll) {
opacity: 1;
}
require "rouge"
class CodeFormatter
def initialize(code, language)
@code, @language = code, language
end
def formatted_code
language, highlight = @language.split("@")
highlight = extract_line_numbers(highlight.to_s)
formatter = Rouge::Formatters::HTML.new
formatter = Rouge::Formatters::HTMLLineHighlighter.new(formatter, highlight_lines: highlight)
lexer = Rouge::Lexer.find(language)
formatter.format(lexer.lex(@code))
end
private
# Converts "[1,2,5]" to [1,2,5] and "[3-6]" to [3-6].
def extract_line_numbers(string)
numbers = []
parts = string.tr("[]", "").split(",")
parts.each do |part|
if part.include?("-")
range_start, range_end = part.split("-").map(&:to_i)
numbers += (range_start..range_end).to_a
else
numbers << part.to_i
end
end
numbers
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment