Skip to content

Commit

Permalink
Do not double escape text when processing markdown.
Browse files Browse the repository at this point in the history
  • Loading branch information
fnando committed Feb 5, 2024
1 parent 3910939 commit b491668
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 62 deletions.
2 changes: 2 additions & 0 deletions lib/kitabu.rb
Expand Up @@ -54,6 +54,8 @@ module Kitabu
require "kitabu/front_matter"
require "kitabu/context"

I18n.load_path += Dir[File.join(__dir__, "kitabu/locales/*.yml")]

def self.config(root_dir = nil)
root_dir ||= Pathname.new(Dir.pwd)
path = root_dir.join("config/kitabu.yml")
Expand Down
8 changes: 3 additions & 5 deletions lib/kitabu/exporter/base.rb
Expand Up @@ -7,11 +7,9 @@ def self.default_i18n_load_path
end

def self.load_translations(root_dir:)
paths = default_i18n_load_path.dup
paths += Dir[root_dir.join("config/locales/**/*.{yml,rb}").to_s]
paths << File.expand_path(File.join(__dir__, "../../../templates/en.yml"))

I18n.load_path = paths
I18n.load_path += Dir[root_dir.join("config/locales/**/*.{yml,rb}").to_s]
I18n.load_path << File.expand_path(File.join(__dir__,
"../../../templates/en.yml"))

I18n.backend.reload!
I18n.backend.eager_load!
Expand Down
22 changes: 13 additions & 9 deletions lib/kitabu/extensions/string.rb
@@ -1,13 +1,17 @@
# frozen_string_literal: true

class String
def to_permalink
str = dup.unicode_normalize(:nfkd)
str = str.gsub(/[^\x00-\x7F]/, "").to_s
str.gsub!(/[^-\w]+/xim, "-")
str.gsub!(/-+/xm, "-")
str.gsub!(/^-?(.*?)-?$/, '\1')
str.downcase!
str
module Kitabu
module Extensions
refine String do
def to_permalink
str = dup.unicode_normalize(:nfkd)
str = str.gsub(/[^\x00-\x7F]/, "").to_s
str.gsub!(/[^-\w]+/xim, "-")
str.gsub!(/-+/xm, "-")
str.gsub!(/^-?(.*?)-?$/, '\1')
str.downcase!
str
end
end
end
end
12 changes: 12 additions & 0 deletions lib/kitabu/locales/en.yml
@@ -0,0 +1,12 @@
---
en:
contents: Contents
left_blank: This page intentionally left blank
chapter: Chapter

alerts:
warning: Warning!
note: Just so you know…
tip: Here’s a tip!
caution: Caution!
important: Important!
38 changes: 35 additions & 3 deletions lib/kitabu/markdown.rb
Expand Up @@ -2,13 +2,46 @@

module Kitabu
module Markdown
# N.B.: Redcarpet pass down escaped HTML. There's no need to escape the
# content and we can just interpolate it.
class Renderer < Redcarpet::Render::HTML
include Redcarpet::Render::SmartyPants
include Rouge::Plugins::Redcarpet
using Extensions

# Be more flexible than github and support any arbitrary name.
ALERT_MARK = /^\[!(?<type>[A-Z]+)\](?<title>.*?)?$/

HEADING_ID = /^(?<text>.*?)(?: {#(?<id>.*?)})?$/

def escape(text)
Nokogiri::HTML.fragment(text).text
end

def heading_counter
@heading_counter ||= Hash.new {|h, k| h[k] = 0 }
end

def header(text, level)
matches = text.strip.match(HEADING_ID)
title = matches[:text].strip
html = Nokogiri::HTML.fragment("<h#{level}>#{title}</h#{level}>")
heading = html.first_element_child
title = heading.text

id = matches[:id]
id ||= title.to_permalink

heading_counter[id] += 1
id = "#{id}-#{heading_counter[id]}" if heading_counter[id] > 1

heading.add_child %[<a class="anchor" href="##{id}" aria-hidden="true" tabindex="-1"></a>] # rubocop:disable Style/LineLength
heading.set_attribute :tabindex, "-1"
heading.set_attribute(:id, id)

heading.to_s
end

# Support alert boxes just like github.
# https://github.com/orgs/community/discussions/16925
def block_quote(quote)
Expand All @@ -26,7 +59,7 @@ def block_quote(quote)
title = matches[:title].strip

if title == ""
title = I18n.t(type, scope: :notes, default: type.titleize)
title = I18n.t(type, scope: :alerts, default: type.titleize)
end

html = Nokogiri::HTML.fragment <<~HTML.strip_heredoc
Expand All @@ -44,10 +77,9 @@ def block_quote(quote)
def table_cell(content, alignment, header)
tag = header ? "th" : "td"

html = Nokogiri::HTML.fragment("<#{tag}></#{tag}>")
html = Nokogiri::HTML.fragment("<#{tag}>#{content}</#{tag}>")
node = html.children.first
node.append_class("align-#{alignment}") if alignment
node.content = content

html.to_s
end
Expand Down
24 changes: 0 additions & 24 deletions lib/kitabu/toc/html.rb
Expand Up @@ -8,7 +8,6 @@ class HTML
Result = Struct.new(:toc, :html, :hierarchy, keyword_init: true)

def self.generate(html)
html = normalize(html)
file = Tempfile.new("kitabu")
file.write(html.to_s)
file.close
Expand All @@ -26,29 +25,6 @@ def self.generate(html)
ensure
file.unlink
end

def self.normalize(html)
counter = {}

html.search("h1, h2, h3, h4, h5, h6").each do |tag|
title = tag.inner_text.strip
permalink = title.to_permalink

counter[permalink] ||= 0
counter[permalink] += 1

if counter[permalink] > 1
permalink = "#{permalink}-#{counter[permalink]}"
end

tag.set_attribute("id", permalink)
tag["tabindex"] = "-1"

tag.prepend_child %[<a class="anchor" href="##{permalink}" aria-hidden="true" tabindex="-1"></a>] # rubocop:disable Style/LineLength
end

html
end
end
end
end
3 changes: 3 additions & 0 deletions spec/kitabu/extensions/string_spec.rb
Expand Up @@ -5,9 +5,12 @@
require "spec_helper"

describe String do
using Kitabu::Extensions

describe "#to_permalink" do
it "normalizes strings" do
{
"Simple > Complex" => "simple-complex",
"This IS a Tripped out title!!.!1 (well/ not really)" => "this-is-a-tripped-out-title-1-well-not-really",
"////// meph1sto r0x ! \\\\\\" => "meph1sto-r0x",
"āčēģīķļņū" => "acegiklnu",
Expand Down
26 changes: 26 additions & 0 deletions spec/kitabu/markdown_spec.rb
Expand Up @@ -157,4 +157,30 @@ class User
expect(html).not_to have_tag("figure")
expect(html).not_to have_tag("figcaption")
end

it "sets automatic ids when defining headers" do
html = Kitabu::Markdown.render <<~TEXT
# My header
## My header
### My header {#my-custom-id}
TEXT

html = Nokogiri::HTML.fragment(html)

expect(html).to have_tag("h1#my-header", "My header")
expect(html).to have_tag("h2#my-header-2", "My header")
expect(html).to have_tag("h3#my-custom-id", "My header")
end

it "sets custom ids when defining headers" do
html = Kitabu::Markdown.render <<~TEXT
# My header {#header}
TEXT

html = Nokogiri::HTML.fragment(html)

expect(html).to have_tag("h1#header", "My header")
end
end
36 changes: 17 additions & 19 deletions spec/kitabu/toc/html_spec.rb
Expand Up @@ -8,28 +8,26 @@ def regexp(text)
end

def input
(+<<-HTML).force_encoding("utf-8")
<h2>Item 1</h2>
<h3>Item 1.1</h3>
<h4>Item 1.1.1</h4>
<h5>Item 1.1.1.1</h5>
<h6>Item 1.1.1.1.1</h6>
html = Kitabu::Markdown.render <<~MARKDOWN
## Item 1
### Item 1.1
#### Item 1.1.1
##### Item 1.1.1.1
###### Item 1.1.1.1.1
## Item 2
### Item 2.1
#### Item 2.1.1
#### Item 2.1.2
## Item 3
## Internacionalização
## Title
## Title
MARKDOWN

<h2>Item 2</h2>
<h3>Item 2.1</h3>
<h4>Item 2.1.1</h4>
<h4>Item 2.1.2</h4>
<h2>Item 3</h2>
<h2>Internacionalização</h2>
<!-- Duplicated titles -->
<h2>Title</h2>
<h2>Title</h2>
HTML
html.to_s
end

let(:result) { described_class.generate(Nokogiri::HTML(input)) }
let(:result) { described_class.generate(input) }
let(:toc) { Nokogiri::HTML(result.toc) }
let(:html) { result.html }

Expand Down
2 changes: 2 additions & 0 deletions spec/spec_helper.rb
Expand Up @@ -61,7 +61,9 @@ def bundle_install
config.before { FileUtils.mkdir_p(TMPDIR) }

config.before do
I18n.load_path = Dir["#{__dir__}/../lib/kitabu/locales/*.yml"]
Kitabu::Markdown.hooks.clear
Kitabu::Markdown.setup_default_hooks
Kitabu::Markdown.processor.renderer.heading_counter.clear
end
end
2 changes: 1 addition & 1 deletion spec/support/mybook/config/locales/en.yml
Expand Up @@ -4,7 +4,7 @@ en:
left_blank: This page intentionally left blank
chapter: Chapter

notes:
alerts:
warning: Warning!
note: Just so you know…
tip: Here’s a tip!
Expand Down
2 changes: 1 addition & 1 deletion templates/en.yml
Expand Up @@ -4,7 +4,7 @@ en:
left_blank: This page intentionally left blank
chapter: Chapter

notes:
alerts:
warning: Warning!
note: Just so you know…
tip: Here’s a tip!
Expand Down

0 comments on commit b491668

Please sign in to comment.