Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: cc3ada478a35
Choose a base ref
...
head repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: fbdc73673e80
Choose a head ref
  • 2 commits
  • 3 files changed
  • 1 contributor

Commits on Dec 5, 2016

  1. Fixed #3640: Char#to_i was returning Int32 | Int8

    Ary Borenszweig committed Dec 5, 2016
    Copy the full SHA
    35c8d92 View commit details
  2. Copy the full SHA
    fbdc736 View commit details
Showing with 103 additions and 57 deletions.
  1. +12 −2 spec/std/markdown/markdown_spec.cr
  2. +5 −5 src/char.cr
  3. +86 −50 src/markdown/parser.cr
14 changes: 12 additions & 2 deletions spec/std/markdown/markdown_spec.cr
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ require "markdown"

private def assert_render(input, output, file = __FILE__, line = __LINE__)
it "renders #{input.inspect}", file, line do
Markdown.to_html(input).should eq(output)
Markdown.to_html(input).should eq(output), file, line
end
end

@@ -70,25 +70,32 @@ describe Markdown do
assert_render "Hello\n```\nWorld\n```", "<p>Hello</p>\n\n<pre><code>World</code></pre>"

assert_render "> Hello World\n", "<blockquote>Hello World</blockquote>"
assert_render "> This spawns\nmultiple\nlines\n\ntext", "<blockquote>This spawns\nmultiple\nlines</blockquote>\n\n<p>text</p>"

assert_render "* Hello", "<ul><li>Hello</li></ul>"
assert_render "* Hello\n* World", "<ul><li>Hello</li><li>World</li></ul>"
assert_render "* Hello\n* World\n * Crystal", "<ul><li>Hello</li><li>World</li><ul><li>Crystal</li></ul></ul>"
assert_render "* Level1\n * Level2\n * Level2\n* Level1", "<ul><li>Level1</li><ul><li>Level2</li><li>Level2</li></ul><li>Level1</li></ul>"
assert_render "* Level1\n * Level2\n * Level2", "<ul><li>Level1</li><ul><li>Level2</li><li>Level2</li></ul></ul>"
assert_render "* Hello\nWorld", "<ul><li>Hello</li></ul>\n\n<p>World</p>"
assert_render "* Hello\nWorld", "<ul><li>Hello\nWorld</li></ul>"
assert_render "Params:\n* Foo\n* Bar", "<p>Params:</p>\n\n<ul><li>Foo</li><li>Bar</li></ul>"

assert_render "+ Hello", "<ul><li>Hello</li></ul>"
assert_render "- Hello", "<ul><li>Hello</li></ul>"

assert_render "* Hello\n+ World\n- Crystal", "<ul><li>Hello</li></ul>\n\n<ul><li>World</li></ul>\n\n<ul><li>Crystal</li></ul>"

assert_render "* This spawns\nmultiple\nlines\n\ntext", "<ul><li>This spawns\nmultiple\nlines</li></ul>\n\n<p>text</p>"
assert_render "* Two\nlines\n* This spawns\nmultiple\nlines\n\ntext", "<ul><li>Two\nlines</li><li>This spawns\nmultiple\nlines</li></ul>\n\n<p>text</p>"

assert_render "1. Hello", "<ol><li>Hello</li></ol>"
assert_render "2. Hello", "<ol><li>Hello</li></ol>"
assert_render "01. Hello\n02. World", "<ol><li>Hello</li><li>World</li></ol>"
assert_render "Params:\n 1. Foo\n 2. Bar", "<p>Params:</p>\n\n<ol><li>Foo</li><li>Bar</li></ol>"

assert_render "1. This spawns\nmultiple\nlines\n\ntext", "<ol><li>This spawns\nmultiple\nlines</li></ol>\n\n<p>text</p>"
assert_render "1. Two\nlines\n1. This spawns\nmultiple\nlines\n\ntext", "<ol><li>Two\nlines</li><li>This spawns\nmultiple\nlines</li></ol>\n\n<p>text</p>"

assert_render "Hello [world](http://example.com)", %(<p>Hello <a href="http://example.com">world</a></p>)
assert_render "Hello [world](http://example.com)!", %(<p>Hello <a href="http://example.com">world</a>!</p>)
assert_render "Hello [world **2**](http://example.com)!", %(<p>Hello <a href="http://example.com">world <strong>2</strong></a>!</p>)
@@ -98,6 +105,9 @@ describe Markdown do

assert_render "[![foo](bar)](baz)", %(<p><a href="baz"><img src="bar" alt="foo"/></a></p>)

assert_render "This [spawns\nmultiple\nlines](http://example.com)\n\ntext",
%(<p>This <a href="http://example.com">spawns\nmultiple\nlines</a></p>\n\n<p>text</p>)

assert_render "***", "<hr/>"
assert_render "---", "<hr/>"
assert_render "___", "<hr/>"
10 changes: 5 additions & 5 deletions src/char.cr
Original file line number Diff line number Diff line change
@@ -563,7 +563,7 @@ struct Char
# 'f'.to_i(16) # => 15
# 'z'.to_i(16) # => ArgumentError
# ```
def to_i(base : Int = 10)
def to_i(base : Int = 10) : Int32
to_i?(base) || raise ArgumentError.new("Invalid integer: #{self}")
end

@@ -579,7 +579,7 @@ struct Char
# 'f'.to_i(16) # => 15
# 'z'.to_i(16) # => ArgumentError
# ```
def to_i?(base : Int = 10)
def to_i?(base : Int = 10) : Int32?
raise ArgumentError.new "invalid base #{base}, expected 2 to 36" unless 2 <= base <= 36

if base == 10
@@ -590,18 +590,18 @@ struct Char
if 0 <= ord < 256
digit = String::CHAR_TO_DIGIT.to_unsafe[ord]
return if digit == -1 || digit >= base
digit
digit.to_i32
end
end
end

# Same as `to_i`
def to_i32(base : Int = 10)
def to_i32(base : Int = 10) : Int32
to_i(base)
end

# Same as `to_i?`
def to_i32?(base : Int = 10)
def to_i32?(base : Int = 10) : Int32?
to_i?(base)
end

136 changes: 86 additions & 50 deletions src/markdown/parser.cr
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
class Markdown::Parser
record PrefixHeader, count : Int32
record UnorderedList, char : Char

@lines : Array(String)

def initialize(text : String, @renderer : Renderer)
@@ -15,57 +18,82 @@ class Markdown::Parser
def process_paragraph
line = @lines[@line]

if empty? line
case item = classify(line)
when :empty
@line += 1
return
when :header1
render_header 1, line, 2
when :header2
render_header 2, line, 2
when PrefixHeader
render_prefix_header(item.count, line)
when :code
render_code
when :horizontal_rule
render_horizontal_rule
when UnorderedList
render_unordered_list(item.char)
when :fenced_code
render_fenced_code
when :ordered_list
render_ordered_list
when :quote
render_quote
else
render_paragraph
end
end

def classify(line)
if empty? line
return :empty
end

if next_line_is_all?('=')
return render_header 1, line, 2
return :header1
end

if next_line_is_all?('-')
return render_header 2, line, 2
return :header2
end

pounds = count_pounds line
if pounds
return render_prefix_header pounds, line
if pounds = count_pounds line
return PrefixHeader.new(pounds)
end

if line.starts_with? " "
return render_code
return :code
end

if horizontal_rule? line
return render_horizontal_rule
return :horizontal_rule
end

if starts_with_bullet_list_marker?(line, '*')
return render_unordered_list('*')
return UnorderedList.new('*')
end

if starts_with_bullet_list_marker?(line, '+')
return render_unordered_list('+')
return UnorderedList.new('+')
end

if starts_with_bullet_list_marker?(line, '-')
return render_unordered_list('-')
return UnorderedList.new('-')
end

if starts_with_backticks? line
return render_fenced_code
return :fenced_code
end

if starts_with_digits_dot? line
return render_ordered_list
return :ordered_list
end

if line.starts_with? ">"
return render_quote
return :quote
end

render_paragraph
nil
end

def render_prefix_header(level, line)
@@ -91,27 +119,9 @@ class Markdown::Parser
def render_paragraph
@renderer.begin_paragraph

while true
process_line @lines[@line]
@line += 1

if @line == @lines.size
break
end

line = @lines[@line]

if empty? line
@line += 1
break
end

if (starts_with_bullet_list_marker?(line) || starts_with_backticks?(line) || starts_with_digits_dot?(line))
break
end

newline
end
join_next_lines continue_on: nil
process_line @lines[@line]
@line += 1

@renderer.end_paragraph

@@ -185,20 +195,11 @@ class Markdown::Parser
def render_quote
@renderer.begin_quote

while true
line = @lines[@line]

break unless line.starts_with? ">"

@renderer.text line.byte_slice(Math.min(line.bytesize, 2))
@line += 1

if @line == @lines.size
break
end
join_next_lines continue_on: :quote
line = @lines[@line]

newline
end
@renderer.text line.byte_slice(Math.min(line.bytesize, 2))
@line += 1

@renderer.end_quote

@@ -209,6 +210,7 @@ class Markdown::Parser
@renderer.begin_unordered_list

while true
join_next_lines continue_on: nil, stop_on: UnorderedList.new(prefix)
line = @lines[@line]

if empty? line
@@ -251,6 +253,7 @@ class Markdown::Parser
@renderer.begin_ordered_list

while true
join_next_lines continue_on: nil, stop_on: :ordered_list
line = @lines[@line]

if empty? line
@@ -604,4 +607,37 @@ class Markdown::Parser
def newline
@renderer.text "\n"
end

# Join this line with next lines if they form a paragraph,
# until next lines don't start another entity like a list,
# header, etc.
def join_next_lines(continue_on = :none, stop_on = :none)
start = @line
line = @line
line += 1
while line < @lines.size
item = classify(@lines[line])

case item
when continue_on
# continue
when stop_on
line -= 1
break
when nil
# paragraph: continue
else
line -= 1
break
end

line += 1
end
line -= 1 if line == @lines.size

if line > start
@lines[line] = (start..line).join("\n") { |i| @lines[i] }
@line = line
end
end
end