Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport Markdown from Julia 0.4. #23

Merged
merged 1 commit into from
May 8, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Lazy
Compat

9 changes: 7 additions & 2 deletions src/Common/Common.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# This file is a part of Julia. License is MIT: http://julialang.org/license

include("block.jl")
include("inline.jl")

@flavor common [list, indentcode, blockquote, hashheader, paragraph,
escapes, en_dash, inline_code, asterisk_bold, asterisk_italic, image, link]
@flavor common [list, indentcode, blockquote, hashheader, horizontalrule,
paragraph,

linebreak, escapes, inline_code,
asterisk_bold, asterisk_italic, image, link]
276 changes: 174 additions & 102 deletions src/Common/block.jl
Original file line number Diff line number Diff line change
@@ -1,166 +1,238 @@
# This file is a part of Julia. License is MIT: http://julialang.org/license

# ––––––––––
# Paragraphs
# ––––––––––

type Paragraph
content
content
end

Paragraph() = Paragraph({})

function paragraph(stream::IO, md::MD, config::Config)
buffer = IOBuffer()
p = Paragraph()
push!(md, p)
skipwhitespace(stream)
while !eof(stream)
char = read(stream, Char)
if char == '\n' || char == '\r'
if blankline(stream) || parse(stream, md, config, breaking = true)
break
else
write(buffer, ' ')
end
else
write(buffer, char)
Paragraph() = Paragraph([])

function paragraph(stream::IO, md::MD)
buffer = IOBuffer()
p = Paragraph()
push!(md, p)
skipwhitespace(stream)
while !eof(stream)
char = read(stream, Char)
if char == '\n' || char == '\r'
if blankline(stream) || parse(stream, md, breaking = true)
break
else
write(buffer, ' ')
end
else
write(buffer, char)
end
end
end
p.content = parseinline(seek(buffer, 0), config)
return true
p.content = parseinline(seek(buffer, 0), md)
return true
end

# –––––––
# Headers
# –––––––

type Header{level}
text
text
end

Header(s, level::Int) = Header{level}(s)
Header(s) = Header(s, 1)

@breaking true ->
function hashheader(stream::IO, md::MD, config::Config)
startswith(stream, "#") || return false
level = 1
while startswith(stream, "#")
level += 1
end
h = readline(stream) |> chomp
h = match(r"\s*(.*)(?<![#\s])", h).captures[1]
buffer = IOBuffer()
print(buffer, h)
if !isempty(h)
push!(md.content, Header(parseinline(seek(buffer, 0), config), level))
return true
else
return false
end
function hashheader(stream::IO, md::MD)
withstream(stream) do
eatindent(stream) || return false
level = 0
while startswith(stream, '#') level += 1 end
level < 1 || level > 6 && return false

c = ' '
# Allow empty headers, but require a space
!eof(stream) && (c = read(stream, Char); !(c in " \n")) &&
return false

if c != '\n' # Empty header
h = readline(stream) |> strip
h = match(r"(.*?)( +#+)?$", h).captures[1]
buffer = IOBuffer()
print(buffer, h)
push!(md.content, Header(parseinline(seek(buffer, 0), md), level))
else
push!(md.content, Header("", level))
end
return true
end
end

function setextheader(stream::IO, md::MD)
withstream(stream) do
eatindent(stream) || return false
header = readline(stream) |> strip
header == "" && return false

eatindent(stream) || return false
underline = readline(stream) |> strip
length(underline) < 3 && return false
u = underline[1]
u in "-=" || return false
all(c -> c == u, underline) || return false
level = (u == '=') ? 1 : 2

push!(md.content, Header(parseinline(header, md), level))
return true
end
end

# ––––
# Code
# ––––

type Code
language::UTF8String
code::UTF8String
language::UTF8String
code::UTF8String
end

Code(code) = Code("", code)

function indentcode(stream::IO, block::MD, config::Config)
withstream(stream) do
buffer = IOBuffer()
while startswith(stream, " ") || startswith(stream, "\t")
write(buffer, readline(stream))
function indentcode(stream::IO, block::MD)
withstream(stream) do
buffer = IOBuffer()
while !eof(stream)
if startswith(stream, " ") || startswith(stream, "\t")
write(buffer, readline(stream))
elseif blankline(stream)
write(buffer, '\n')
else
break
end
end
code = takebuf_string(buffer)
!isempty(code) && (push!(block, Code(rstrip(code))); return true)
return false
end
code = takebuf_string(buffer)
!isempty(code) && (push!(block, Code(chomp(code))); return true)
return false
end
end

# ––––––
# Quotes
# ––––––

type BlockQuote
content
content
end

BlockQuote() = BlockQuote({})
BlockQuote() = BlockQuote([])

# TODO: Laziness
@breaking true ->
function blockquote(stream::IO, block::MD, config::Config)
withstream(stream) do
buffer = IOBuffer()
while startswith(stream, ">")
startswith(stream, " ")
write(buffer, readline(stream))
end
md = takebuf_string(buffer)
if !isempty(md)
push!(block, BlockQuote(parse(md, flavor = config).content))
return true
else
return false
function blockquote(stream::IO, block::MD)
withstream(stream) do
buffer = IOBuffer()
empty = true
while eatindent(stream) && startswith(stream, '>')
startswith(stream, " ")
write(buffer, readline(stream))
empty = false
end
empty && return false

md = takebuf_string(buffer)
push!(block, BlockQuote(parse(md, flavor = config(block)).content))
return true
end
end
end

# –––––
# Lists
# –––––

type List
items::Vector{Any}
ordered::Bool
items::Vector{Any}
ordered::Bool

List(x::AbstractVector) = new(x)
List(x::AbstractVector, b::Bool) = new(x, b)
List(x::AbstractVector) = new(x, false)
List(b::Bool) = new(Any[], b)
end

List(xs...) = List([xs...])
List(xs...) = List(vcat(xs...))

const bullets = ["* ", "• ", "+ ", "- "]
const bullets = "*•+-"
const num_or_bullets = r"^(\*|•|\+|-|\d+(\.|\))) "

# Todo: ordered lists, inline formatting
function list(stream::IO, block::MD, config::Config)
withstream(stream) do
skipwhitespace(stream)
startswith(stream, bullets) || return false
the_list = List()
buffer = IOBuffer()
fresh_line = false
while !eof(stream)
if fresh_line
skipwhitespace(stream)
if startswith(stream, bullets)
push!(the_list.items, parseinline(takebuf_string(buffer), config))
buffer = IOBuffer()
else
write(buffer, ' ')
function list(stream::IO, block::MD)
withstream(stream) do
eatindent(stream) || return false
b = startswith(stream, num_or_bullets)
(b == nothing || b == "") && return false
ordered = !(b[1] in bullets)
if ordered
b = b[end - 1] == '.' ? r"^\d+\. " : r"^\d+\) "
# TODO start value
end
the_list = List(ordered)

buffer = IOBuffer()
fresh_line = false
else
c = read(stream, Char)
if c == '\n'
eof(stream) && break
next = peek(stream)
if next == '\n'
break
else
fresh_line = true
end
else
write(buffer, c)
while !eof(stream)
if fresh_line
sp = startswith(stream, r"^ {0,3}")
if !(startswith(stream, b) in [false, ""])
push!(the_list.items, parseinline(takebuf_string(buffer), block))
buffer = IOBuffer()
else
# TODO write a newline here, and deal with nested
write(buffer, ' ', sp)
end
fresh_line = false
else
c = read(stream, Char)
if c == '\n'
eof(stream) && break
next = peek(stream)
if next == '\n'
break
else
fresh_line = true
end
else
write(buffer, c)
end
end
end
end
push!(the_list.items, parseinline(takebuf_string(buffer), block))
push!(block, the_list)
return true
end
push!(the_list.items, parseinline(takebuf_string(buffer), config))
push!(block, the_list)
return true
end
end

# ––––––––––––––
# HorizontalRule
# ––––––––––––––

type HorizontalRule
end

function horizontalrule(stream::IO, block::MD)
withstream(stream) do
n, rule = 0, ' '
while !eof(stream)
char = read(stream, Char)
char == '\n' && break
isspace(char) && continue
if n==0 || char==rule
rule = char
n += 1
else
return false
end
end
is_hr = (n ≥ 3 && rule in "*-")
is_hr && push!(block, HorizontalRule())
return is_hr
end
end
Loading