# frozen_string_literal: true module Asciidoctor class SyntaxHighlighter::PygmentsAdapter < SyntaxHighlighter::Base register_for 'pygments' def initialize *args super @requires_stylesheet = @style = nil end def highlight? library_available? end def highlight node, source, lang, opts lexer = (::Pygments::Lexer.find_by_alias lang) || (::Pygments::Lexer.find_by_mimetype 'text/plain') @requires_stylesheet = true unless (noclasses = opts[:css_mode] != :class) highlight_opts = { classprefix: TOKEN_CLASS_PREFIX, cssclass: WRAPPER_CLASS, nobackground: true, noclasses: noclasses, startinline: lexer.name == 'PHP' && !(node.option? 'mixed'), stripnl: false, style: (@style ||= (style = opts[:style]) && (style_available? style) || DEFAULT_STYLE), } if (highlight_lines = opts[:highlight_lines]) highlight_opts[:hl_lines] = highlight_lines.join ' ' end if (linenos = opts[:number_lines]) && (highlight_opts[:linenostart] = opts[:start_line_number]) && (highlight_opts[:linenos] = linenos) == :table if (highlighted = lexer.highlight source, options: highlight_opts) highlighted = highlighted.sub StyledLinenoColumnStartTagsRx, LinenoColumnStartTagsCs if noclasses highlighted = highlighted.sub WrapperTagRx, PreTagCs opts[:callouts] ? [highlighted, (idx = highlighted.index CodeCellStartTagCs) ? idx + CodeCellStartTagCs.length : nil] : highlighted else node.sub_specialchars source # handles nil response from ::Pygments::Lexer#highlight end elsif (highlighted = lexer.highlight source, options: highlight_opts) highlighted = highlighted.gsub StyledLinenoSpanTagRx, LinenoSpanTagCs if linenos && noclasses highlighted.sub WrapperTagRx, '\1' else node.sub_specialchars source # handles nil response from ::Pygments::Lexer#highlight end end def format node, lang, opts if opts[:css_mode] != :class && (@style = (style = opts[:style]) && (style_available? style) || DEFAULT_STYLE) && (pre_style_attr_val = base_style @style) opts[:transform] = proc {|pre| pre['style'] = pre_style_attr_val } end super end def docinfo? location @requires_stylesheet && location == :head end def docinfo location, doc, opts if opts[:linkcss] %() else %() end end def write_stylesheet? doc @requires_stylesheet end def write_stylesheet doc, to_dir ::File.write (::File.join to_dir, (stylesheet_basename @style)), (read_stylesheet @style), mode: FILE_WRITE_MODE end module Loader private def library_available? (@@library_status ||= load_library) == :loaded ? true : nil end def load_library (defined? ::Pygments::Lexer) ? :loaded : (Helpers.require_library 'pygments', 'pygments.rb', :warn).nil? ? :unavailable : :loaded end end module Styles include Loader def read_stylesheet style library_available? ? @@stylesheet_cache[style || DEFAULT_STYLE] || '/* Failed to load Pygments CSS. */' : '/* Pygments CSS disabled because Pygments is not available. */' end def stylesheet_basename style %(pygments-#{style || DEFAULT_STYLE}.css) end private def base_style style library_available? ? @@base_style_cache[style || DEFAULT_STYLE] : nil end def style_available? style (((@@available_styles ||= ::Pygments.styles.to_set).include? style) rescue nil) && style end @@base_style_cache = ::Hash.new do |cache, key| if BaseStyleRx =~ @@stylesheet_cache[key] @@base_style_cache = cache.merge key => (style = $1.strip) style end end @@stylesheet_cache = ::Hash.new do |cache, key| if (stylesheet = ::Pygments.css BASE_SELECTOR, classprefix: TOKEN_CLASS_PREFIX, style: key) @@stylesheet_cache = cache.merge key => stylesheet stylesheet end end DEFAULT_STYLE = 'default' BASE_SELECTOR = 'pre.pygments' TOKEN_CLASS_PREFIX = 'tok-' BaseStyleRx = /^#{BASE_SELECTOR.gsub '.', '\\.'} +\{([^}]+?)\}/ private_constant :BASE_SELECTOR, :TOKEN_CLASS_PREFIX, :BaseStyleRx end extend Styles # exports static methods include Loader, Styles # adds methods to instance CodeCellStartTagCs = '
' LinenoSpanTagCs = '\1' PreTagCs = '\1' StyledLinenoColumnStartTagsRx = / / StyledLinenoSpanTagRx = %r(( *\d+ )) WRAPPER_CLASS = 'lineno' # doesn't appear in output; Pygments appends "table" to this value to make nested table class # NOTEhas style attribute when pygments-css=style # NOTEhas trailing newline when pygments-linenums-mode=table # NOTE initial preserves leading blank lines WrapperTagRx = %r(\n*)m private_constant :CodeCellStartTagCs, :LinenoColumnStartTagsCs, :LinenoSpanTagCs, :PreTagCs, :StyledLinenoColumnStartTagsRx, :StyledLinenoSpanTagRx, :WrapperTagRx, :WRAPPER_CLASS end end]*?>(.*)