# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'redcarpet' require 'pygments' require 'slugize' require 'nokogiri' module Jekyll module Converters # Jekyll Markdown Converter wrapper for Redcarpet using the PebbleMarkdown # HTML render class. class Markdown::PebbleMarkdownParser def initialize(config) @site_config = config end def convert(content) content = '' if content.nil? pbl_md = PebbleMarkdown.new(@site_config) Redcarpet::Markdown.new(pbl_md, fenced_code_blocks: true, autolink: true, tables: true, no_intra_emphasis: true, strikethrough: true, highlight: true).render(content) end # Redcarpet HTML render class to handle the extra functionality. class PebbleMarkdown < Redcarpet::Render::HTML def initialize(config) @site_config = config super() end def preprocess(document) process_links_with_double_backticks(document) process_double_backticks(document) document end # Add ID and anchors to all headers. def header(text, header_level) if text.include?('<') id = Nokogiri::HTML(text).text.slugize else id = text.slugize end str = "" str += text str += "" str end def paragraph(text) if (match = /^\^(CP|LC)\^/.match(text)) "

#{text[(match[1].length + 2)..-1].strip}

" else "

#{text}

" end end # Use Pygments to generate the syntax highlighting markup. def block_code(code, language) classes = ['highlight'] if /^nc\|/.match(language) classes << 'no-copy' language = language[3..-1] end if language == 'text' "
#{code}
" else set_classes(Pygments.highlight(code, lexer: language), classes) end end def link(url, title, content) if content == 'EMBED' embed(url) else classes = [] if /^DOCS:/.match(title) title = title[5..-1] classes << 'link--docs' end # URL begins with a single slash (but not double slash) url = baseurl + url if %r{^/[^/]}.match(url) data_str = '' if (match = regex_button.match(content)) classes << 'btn' classes << 'btn--markdown' classes.concat(match[3].split(',').map { |cls| 'btn--' + cls }) content = match[1] end if (match = regex_link_data.match(title)) match[3].split(',').each do |item| item = item.split(':') data_str += ' data-' + item[0] + '="' + item[1] + '"' end title = match[1] end "#{content}" end end # Better image handling. # * Add size specificiations (taken from RDiscount) # * Prepend the site baselink to images that beings with / # TODO: Handle the cases where image link begins with // # TODO: Maybe add additional style choices (centered, inline, etc) def image(link, title, alt_text) if (size_match = /^(.*)\ =([0-9]+)x?([0-9]*)$/.match(link)) link = size_match[1] width = size_match[2] height = size_match[3] end classes = [] if (match = regex_button.match(alt_text)) classes.concat(match[3].split(',')) alt_text = match[1] end link = asset_path + link if %r{^/[^/]}.match(link) img_str = "
' \ '" \ '
' end def youtube_playlist(id) '
' \ '" \ '
' end def vimeo(id) '
' \ '" \ '
' end def slideshare(id) '
' \ "'\ '
' end def gist(id) '
' \ "" \ '
' end def baseurl @site_config['baseurl'] || '' end def asset_path @site_config['asset_path'] || '' end def link_sdk(url, title, content) end def regex_youtube_video %r{youtube\.com/(watch\?v=|v/|embed/)([a-z0-9A-Z\-_]*)} end def regex_youtube_playlist %r{^(https?://)?([w]{3}\.)?youtube\.com/playlist\?list=([a-z0-9A-Z\-]*)} end def regex_vimeo_video %r{vimeo.com/video/([0-9]+)} end def regex_slideshare %r{slideshare.net/slideshow/embed_code/key/([a-z0-9A-Z]*)} end def regex_gist %r{^(https?://)?gist.github\.com/(.*)} end def regex_button /^(.*)\ (>|>)\{?([a-z,0-9\-]*)\}?$/ end def regex_link_data /^(.*)\ (>|>)\{([a-z\-_,:0-9A-Z]+)\}$/ end def shortcode_to_platform(shortcode) platforms = { 'CP' => 'cloudpebble', 'LC' => 'local' } platforms[shortcode] end end end end end