Add: julia-0.6.2
Former-commit-id: ccc667cf67d569f3fb3df39aa57c2134755a7551
This commit is contained in:
11
julia-0.6.2/share/julia/base/markdown/Common/Common.jl
Normal file
11
julia-0.6.2/share/julia/base/markdown/Common/Common.jl
Normal file
@@ -0,0 +1,11 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
include("block.jl")
|
||||
include("inline.jl")
|
||||
|
||||
@flavor common [list, indentcode, blockquote, admonition, footnote, hashheader, horizontalrule,
|
||||
paragraph,
|
||||
|
||||
linebreak, escapes, inline_code,
|
||||
asterisk_bold, asterisk_italic, image, footnote_link, link, autolink]
|
||||
|
||||
356
julia-0.6.2/share/julia/base/markdown/Common/block.jl
Normal file
356
julia-0.6.2/share/julia/base/markdown/Common/block.jl
Normal file
@@ -0,0 +1,356 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# ––––––––––
|
||||
# Paragraphs
|
||||
# ––––––––––
|
||||
|
||||
mutable struct Paragraph
|
||||
content
|
||||
end
|
||||
|
||||
Paragraph() = Paragraph([])
|
||||
|
||||
function paragraph(stream::IO, md::MD)
|
||||
buffer = IOBuffer()
|
||||
p = Paragraph()
|
||||
push!(md, p)
|
||||
skipwhitespace(stream)
|
||||
prev_char = '\n'
|
||||
while !eof(stream)
|
||||
char = read(stream, Char)
|
||||
if char == '\n' || char == '\r'
|
||||
char == '\r' && !eof(stream) && Char(peek(stream)) == '\n' && read(stream, Char)
|
||||
if prev_char == '\\'
|
||||
write(buffer, '\n')
|
||||
elseif blankline(stream) || parse(stream, md, breaking = true)
|
||||
break
|
||||
else
|
||||
write(buffer, ' ')
|
||||
end
|
||||
else
|
||||
write(buffer, char)
|
||||
end
|
||||
prev_char = char
|
||||
end
|
||||
p.content = parseinline(seek(buffer, 0), md)
|
||||
return true
|
||||
end
|
||||
|
||||
# –––––––
|
||||
# Headers
|
||||
# –––––––
|
||||
|
||||
mutable struct Header{level}
|
||||
text
|
||||
end
|
||||
|
||||
Header(s, level::Int) = Header{level}(s)
|
||||
Header(s) = Header(s, 1)
|
||||
|
||||
@breaking true ->
|
||||
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 = strip(readline(stream))
|
||||
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 = strip(readline(stream))
|
||||
isempty(header) && return false
|
||||
|
||||
eatindent(stream) || return false
|
||||
underline = strip(readline(stream))
|
||||
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
|
||||
# ––––
|
||||
|
||||
mutable struct Code
|
||||
language::String
|
||||
code::String
|
||||
end
|
||||
|
||||
Code(code) = Code("", code)
|
||||
|
||||
function indentcode(stream::IO, block::MD)
|
||||
withstream(stream) do
|
||||
buffer = IOBuffer()
|
||||
while !eof(stream)
|
||||
if startswith(stream, " ") || startswith(stream, "\t")
|
||||
write(buffer, readline(stream, chomp=false))
|
||||
elseif blankline(stream)
|
||||
write(buffer, '\n')
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
code = String(take!(buffer))
|
||||
!isempty(code) && (push!(block, Code(rstrip(code))); return true)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
# --------
|
||||
# Footnote
|
||||
# --------
|
||||
|
||||
mutable struct Footnote
|
||||
id::String
|
||||
text
|
||||
end
|
||||
|
||||
function footnote(stream::IO, block::MD)
|
||||
withstream(stream) do
|
||||
regex = r"^\[\^(\w+)\]:"
|
||||
str = startswith(stream, regex)
|
||||
if isempty(str)
|
||||
return false
|
||||
else
|
||||
ref = match(regex, str).captures[1]
|
||||
buffer = IOBuffer()
|
||||
write(buffer, readline(stream, chomp=false))
|
||||
while !eof(stream)
|
||||
if startswith(stream, " ")
|
||||
write(buffer, readline(stream, chomp=false))
|
||||
elseif blankline(stream)
|
||||
write(buffer, '\n')
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
content = parse(seekstart(buffer)).content
|
||||
push!(block, Footnote(ref, content))
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# ––––––
|
||||
# Quotes
|
||||
# ––––––
|
||||
|
||||
mutable struct BlockQuote
|
||||
content
|
||||
end
|
||||
|
||||
BlockQuote() = BlockQuote([])
|
||||
|
||||
# TODO: Laziness
|
||||
@breaking true ->
|
||||
function blockquote(stream::IO, block::MD)
|
||||
withstream(stream) do
|
||||
buffer = IOBuffer()
|
||||
empty = true
|
||||
while eatindent(stream) && startswith(stream, '>')
|
||||
startswith(stream, " ")
|
||||
write(buffer, readline(stream, chomp=false))
|
||||
empty = false
|
||||
end
|
||||
empty && return false
|
||||
|
||||
md = String(take!(buffer))
|
||||
push!(block, BlockQuote(parse(md, flavor = config(block)).content))
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
# -----------
|
||||
# Admonitions
|
||||
# -----------
|
||||
|
||||
mutable struct Admonition
|
||||
category::String
|
||||
title::String
|
||||
content::Vector
|
||||
end
|
||||
|
||||
@breaking true ->
|
||||
function admonition(stream::IO, block::MD)
|
||||
withstream(stream) do
|
||||
# Admonition syntax:
|
||||
#
|
||||
# !!! category "optional explicit title within double quotes"
|
||||
# Any number of other indented markdown elements.
|
||||
#
|
||||
# This is the second paragraph.
|
||||
#
|
||||
startswith(stream, "!!! ") || return false
|
||||
# Extract the category of admonition and its title:
|
||||
category, title =
|
||||
let untitled = r"^([a-z]+)$", # !!! <CATEGORY_NAME>
|
||||
titled = r"^([a-z]+) \"(.*)\"$", # !!! <CATEGORY_NAME> "<TITLE>"
|
||||
line = strip(readline(stream))
|
||||
if ismatch(untitled, line)
|
||||
m = match(untitled, line)
|
||||
# When no title is provided we use CATEGORY_NAME, capitalising it.
|
||||
m.captures[1], ucfirst(m.captures[1])
|
||||
elseif ismatch(titled, line)
|
||||
m = match(titled, line)
|
||||
# To have a blank TITLE provide an explicit empty string as TITLE.
|
||||
m.captures[1], m.captures[2]
|
||||
else
|
||||
# Admonition header is invalid so we give up parsing here and move
|
||||
# on to the next parser.
|
||||
return false
|
||||
end
|
||||
end
|
||||
# Consume the following indented (4 spaces) block.
|
||||
buffer = IOBuffer()
|
||||
while !eof(stream)
|
||||
if startswith(stream, " ")
|
||||
write(buffer, readline(stream, chomp=false))
|
||||
elseif blankline(stream)
|
||||
write(buffer, '\n')
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
# Parse the nested block as markdown and create a new Admonition block.
|
||||
nested = parse(String(take!(buffer)), flavor = config(block))
|
||||
push!(block, Admonition(category, title, nested.content))
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
# –––––
|
||||
# Lists
|
||||
# –––––
|
||||
|
||||
mutable struct List
|
||||
items::Vector{Any}
|
||||
ordered::Int # `-1` is unordered, `>= 0` is ordered.
|
||||
|
||||
List(x::AbstractVector, b::Integer) = new(x, b)
|
||||
List(x::AbstractVector) = new(x, -1)
|
||||
List(b::Integer) = new(Any[], b)
|
||||
end
|
||||
|
||||
List(xs...) = List(vcat(xs...))
|
||||
|
||||
isordered(list::List) = list.ordered >= 0
|
||||
|
||||
const BULLETS = r"^ {0,3}(\*|\+|-)( |$)"
|
||||
const NUM_OR_BULLETS = r"^ {0,3}(\*|\+|-|\d+(\.|\)))( |$)"
|
||||
|
||||
@breaking true ->
|
||||
function list(stream::IO, block::MD)
|
||||
withstream(stream) do
|
||||
bullet = startswith(stream, NUM_OR_BULLETS; eat = false)
|
||||
indent = isempty(bullet) ? (return false) : length(bullet)
|
||||
# Calculate the starting number and regex to use for bullet matching.
|
||||
initial, regex =
|
||||
if ismatch(BULLETS, bullet)
|
||||
# An unordered list. Use `-1` to flag the list as unordered.
|
||||
-1, BULLETS
|
||||
elseif ismatch(r"^ {0,3}\d+(\.|\))( |$)", bullet)
|
||||
# An ordered list. Either with `1. ` or `1) ` style numbering.
|
||||
r = contains(bullet, ".") ? r"^ {0,3}(\d+)\.( |$)" : r"^ {0,3}(\d+)\)( |$)"
|
||||
Base.parse(Int, match(r, bullet).captures[1]), r
|
||||
else
|
||||
# Failed to match any bullets. This branch shouldn't actually be needed
|
||||
# since the `NUM_OR_BULLETS` regex should cover this, but we include it
|
||||
# simply for thoroughness.
|
||||
return false
|
||||
end
|
||||
|
||||
# Initialise the empty list object: either ordered or unordered.
|
||||
list = List(initial)
|
||||
|
||||
buffer = IOBuffer() # For capturing nested text for recursive parsing.
|
||||
newline = false # For checking if we have two consecutive newlines: end of list.
|
||||
count = 0 # Count of list items. Used to check if we need to push remaining
|
||||
# content in `buffer` after leaving the `while` loop.
|
||||
while !eof(stream)
|
||||
if startswith(stream, "\n")
|
||||
if newline
|
||||
# Double newline ends the current list.
|
||||
pushitem!(list, buffer)
|
||||
break
|
||||
else
|
||||
newline = true
|
||||
println(buffer)
|
||||
end
|
||||
else
|
||||
newline = false
|
||||
if startswith(stream, " "^indent)
|
||||
# Indented text that is part of the current list item.
|
||||
print(buffer, readline(stream, chomp=false))
|
||||
else
|
||||
matched = startswith(stream, regex)
|
||||
if isempty(matched)
|
||||
# Unindented text meaning we have left the current list.
|
||||
pushitem!(list, buffer)
|
||||
break
|
||||
else
|
||||
# Start of a new list item.
|
||||
count += 1
|
||||
count > 1 && pushitem!(list, buffer)
|
||||
print(buffer, readline(stream, chomp=false))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
count == length(list.items) || pushitem!(list, buffer)
|
||||
push!(block, list)
|
||||
return true
|
||||
end
|
||||
end
|
||||
pushitem!(list, buffer) = push!(list.items, parse(String(take!(buffer))).content)
|
||||
|
||||
# ––––––––––––––
|
||||
# HorizontalRule
|
||||
# ––––––––––––––
|
||||
|
||||
mutable struct 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
|
||||
175
julia-0.6.2/share/julia/base/markdown/Common/inline.jl
Normal file
175
julia-0.6.2/share/julia/base/markdown/Common/inline.jl
Normal file
@@ -0,0 +1,175 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# ––––––––
|
||||
# Emphasis
|
||||
# ––––––––
|
||||
|
||||
mutable struct Italic
|
||||
text
|
||||
end
|
||||
|
||||
@trigger '*' ->
|
||||
function asterisk_italic(stream::IO, md::MD)
|
||||
result = parse_inline_wrapper(stream, "*")
|
||||
return result === nothing ? nothing : Italic(parseinline(result, md))
|
||||
end
|
||||
|
||||
mutable struct Bold
|
||||
text
|
||||
end
|
||||
|
||||
@trigger '*' ->
|
||||
function asterisk_bold(stream::IO, md::MD)
|
||||
result = parse_inline_wrapper(stream, "**")
|
||||
return result === nothing ? nothing : Bold(parseinline(result, md))
|
||||
end
|
||||
|
||||
# ––––
|
||||
# Code
|
||||
# ––––
|
||||
|
||||
@trigger '`' ->
|
||||
function inline_code(stream::IO, md::MD)
|
||||
withstream(stream) do
|
||||
ticks = startswith(stream, r"^(`+)")
|
||||
result = readuntil(stream, ticks)
|
||||
if result === nothing
|
||||
nothing
|
||||
else
|
||||
result = strip(result)
|
||||
# An odd number of backticks wrapping the text will produce a `Code` node, while
|
||||
# an even number will result in a `LaTeX` node. This allows for arbitary
|
||||
# backtick combinations to be embedded inside the resulting node, i.e.
|
||||
#
|
||||
# `a`, ``a``, `` `a` ``, ``` ``a`` ```, ``` `a` ```, etc.
|
||||
# ^ ^ ^ ^ ^
|
||||
# C L L C C with C=Code and L=LaTeX.
|
||||
#
|
||||
isodd(length(ticks)) ? Code(result) : LaTeX(result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# ––––––––––––––
|
||||
# Images & Links
|
||||
# ––––––––––––––
|
||||
|
||||
mutable struct Image
|
||||
url::String
|
||||
alt::String
|
||||
end
|
||||
|
||||
@trigger '!' ->
|
||||
function image(stream::IO, md::MD)
|
||||
withstream(stream) do
|
||||
startswith(stream, "![") || return
|
||||
alt = readuntil(stream, ']', match = '[')
|
||||
alt ≡ nothing && return
|
||||
skipwhitespace(stream)
|
||||
startswith(stream, '(') || return
|
||||
url = readuntil(stream, ')', match = '(')
|
||||
url ≡ nothing && return
|
||||
return Image(url, alt)
|
||||
end
|
||||
end
|
||||
|
||||
mutable struct Link
|
||||
text
|
||||
url::String
|
||||
end
|
||||
|
||||
@trigger '[' ->
|
||||
function link(stream::IO, md::MD)
|
||||
withstream(stream) do
|
||||
startswith(stream, '[') || return
|
||||
text = readuntil(stream, ']', match = '[')
|
||||
text ≡ nothing && return
|
||||
skipwhitespace(stream)
|
||||
startswith(stream, '(') || return
|
||||
url = readuntil(stream, ')', match = '(')
|
||||
url ≡ nothing && return
|
||||
return Link(parseinline(text, md), url)
|
||||
end
|
||||
end
|
||||
|
||||
@trigger '[' ->
|
||||
function footnote_link(stream::IO, md::MD)
|
||||
withstream(stream) do
|
||||
regex = r"^\[\^(\w+)\]"
|
||||
str = startswith(stream, regex)
|
||||
if isempty(str)
|
||||
return
|
||||
else
|
||||
ref = match(regex, str).captures[1]
|
||||
return Footnote(ref, nothing)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@trigger '<' ->
|
||||
function autolink(stream::IO, md::MD)
|
||||
withstream(stream) do
|
||||
startswith(stream, '<') || return
|
||||
url = readuntil(stream, '>')
|
||||
url ≡ nothing && return
|
||||
_is_link(url) && return Link(url, url)
|
||||
_is_mailto(url) && return Link(url, url)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
# This list is taken from the commonmark spec
|
||||
# http://spec.commonmark.org/0.19/#absolute-uri
|
||||
const _allowable_schemes = Set(split("coap doi javascript aaa aaas about acap cap cid crid data dav dict dns file ftp geo go gopher h323 http https iax icap im imap info ipp iris iris.beep iris.xpc iris.xpcs iris.lwz ldap mailto mid msrp msrps mtqp mupdate news nfs ni nih nntp opaquelocktoken pop pres rtsp service session shttp sieve sip sips sms snmp,soap.beep soap.beeps tag tel telnet tftp thismessage tn3270 tip tv urn vemmi ws wss xcon xcon-userid xmlrpc.beep xmlrpc.beeps xmpp z39.50r z39.50s
|
||||
adiumxtra afp afs aim apt,attachment aw beshare bitcoin bolo callto chrome,chrome-extension com-eventbrite-attendee content cvs,dlna-playsingle dlna-playcontainer dtn dvb ed2k facetime feed finger fish gg git gizmoproject gtalk hcp icon ipn irc irc6 ircs itms jar jms keyparc lastfm ldaps magnet maps market,message mms ms-help msnim mumble mvn notes oid palm paparazzi platform proxy psyc query res resource rmi rsync rtmp secondlife sftp sgn skype smb soldat spotify ssh steam svn teamspeak
|
||||
things udp unreal ut2004 ventrilo view-source webcal wtai wyciwyg xfire xri ymsgr"))
|
||||
|
||||
function _is_link(s::AbstractString)
|
||||
'<' in s && return false
|
||||
|
||||
m = match(r"^(.*)://(\S+?)(:\S*)?$", s)
|
||||
m ≡ nothing && return false
|
||||
scheme = lowercase(m.captures[1])
|
||||
return scheme in _allowable_schemes
|
||||
end
|
||||
|
||||
# non-normative regex from the HTML5 spec
|
||||
const _email_regex = r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
|
||||
|
||||
function _is_mailto(s::AbstractString)
|
||||
length(s) < 6 && return false
|
||||
# slicing strings is a bit risky, but this equality check is safe
|
||||
lowercase(s[1:6]) == "mailto:" || return false
|
||||
return ismatch(_email_regex, s[6:end])
|
||||
end
|
||||
|
||||
# –––––––––––
|
||||
# Punctuation
|
||||
# –––––––––––
|
||||
|
||||
mutable struct LineBreak end
|
||||
|
||||
@trigger '\\' ->
|
||||
function linebreak(stream::IO, md::MD)
|
||||
if startswith(stream, "\\\n")
|
||||
return LineBreak()
|
||||
end
|
||||
end
|
||||
|
||||
@trigger '-' ->
|
||||
function en_dash(stream::IO, md::MD)
|
||||
if startswith(stream, "--")
|
||||
return "–"
|
||||
end
|
||||
end
|
||||
|
||||
const escape_chars = "\\`*_#+-.!{}[]()\$"
|
||||
|
||||
@trigger '\\' ->
|
||||
function escapes(stream::IO, md::MD)
|
||||
withstream(stream) do
|
||||
if startswith(stream, "\\") && !eof(stream) && (c = read(stream, Char)) in escape_chars
|
||||
return string(c)
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user