mirror of
https://github.com/danog/blackfriday.git
synced 2024-11-30 04:29:13 +01:00
output validates, command-line tool has useful options
This commit is contained in:
parent
157bb44c05
commit
f9b03f67fb
23
README.md
23
README.md
@ -53,18 +53,14 @@ All features of upskirt are supported, including:
|
||||
|
||||
* Good performance. I have not done rigorous benchmarking, but
|
||||
informal testing suggests it is around 3.5x slower than upskirt.
|
||||
This is an ugly, direct translation from the C code, so
|
||||
the difference is unlikely to be related to differences in
|
||||
coding style. There is a lot of bounds checking that is
|
||||
duplicated (by user code for the application and again by code
|
||||
the compiler generates) and there is some additional memory
|
||||
management overhead, since I allocate and garbage collect
|
||||
buffers instead of explicitly managing them as upskirt does.
|
||||
|
||||
* Minimal dependencies. blackfriday only depends on standard
|
||||
library packages in Go. The source code is pretty
|
||||
self-contained, so it is easy to add to any project.
|
||||
|
||||
* Output successfully validates using the W3C validation tool for
|
||||
HTML 4.01 and XHTML 1.0 Transitional.
|
||||
|
||||
|
||||
Extensions
|
||||
----------
|
||||
@ -83,15 +79,12 @@ LaTeX Output
|
||||
------------
|
||||
|
||||
A rudimentary LaTeX rendering backend is also included. To see an
|
||||
example of its usage, comment out this link in `main.go`:
|
||||
example of its usage, see `main.go`:
|
||||
|
||||
renderer := blackfriday.HtmlRenderer(html_flags)
|
||||
|
||||
and uncomment this line:
|
||||
|
||||
renderer := blackfriday.LatexRenderer(0)
|
||||
|
||||
It renders some basic documents, but is only experimental at this point.
|
||||
It renders some basic documents, but is only experimental at this
|
||||
point. In particular, it does not do any inline escaping, so input
|
||||
that happens to look like LaTeX code will be passed through without
|
||||
modification.
|
||||
|
||||
|
||||
Todo
|
||||
|
125
example/main.go
125
example/main.go
@ -13,6 +13,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"github.com/russross/blackfriday"
|
||||
@ -20,22 +21,55 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
// parse command-line options
|
||||
var page, xhtml, latex, smartypants bool
|
||||
var css string
|
||||
var repeat int
|
||||
flag.BoolVar(&page, "page", false,
|
||||
"Generate a standalone HTML page (implies -latex=false)")
|
||||
flag.BoolVar(&xhtml, "xhtml", true,
|
||||
"Use XHTML-style tags in HTML output")
|
||||
flag.BoolVar(&latex, "latex", false,
|
||||
"Generate LaTeX output instead of HTML")
|
||||
flag.BoolVar(&smartypants, "smartypants", false,
|
||||
"Apply smartypants-style substitutions")
|
||||
flag.StringVar(&css, "css", "",
|
||||
"Link to a CSS stylesheet (implies -page)")
|
||||
flag.IntVar(&repeat, "repeat", 1,
|
||||
"Process the input multiple times (for benchmarking)")
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage:\n"+
|
||||
" %s [options] [inputfile [outputfile]]\n\n"+
|
||||
"Options:\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
// enforce implied options
|
||||
if css != "" {
|
||||
page = true
|
||||
}
|
||||
if page {
|
||||
latex = false
|
||||
}
|
||||
|
||||
// read the input
|
||||
var input []byte
|
||||
var err os.Error
|
||||
switch len(os.Args) {
|
||||
case 1:
|
||||
args := flag.Args()
|
||||
switch len(args) {
|
||||
case 0:
|
||||
if input, err = ioutil.ReadAll(os.Stdin); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error reading from Stdin:", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
case 2, 3:
|
||||
if input, err = ioutil.ReadFile(os.Args[1]); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error reading from", os.Args[1], ":", err)
|
||||
case 1, 2:
|
||||
if input, err = ioutil.ReadFile(args[0]); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error reading from", args[0], ":", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[inputfile [outputfile]]")
|
||||
flag.Usage()
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
@ -48,33 +82,70 @@ func main() {
|
||||
extensions |= blackfriday.EXTENSION_STRIKETHROUGH
|
||||
extensions |= blackfriday.EXTENSION_SPACE_HEADERS
|
||||
|
||||
html_flags := 0
|
||||
html_flags |= blackfriday.HTML_USE_XHTML
|
||||
// note: uncomment the following line to enable smartypants
|
||||
// it is commented out by default so that markdown
|
||||
// compatibility tests pass
|
||||
//html_flags |= blackfriday.HTML_USE_SMARTYPANTS
|
||||
html_flags |= blackfriday.HTML_SMARTYPANTS_FRACTIONS
|
||||
html_flags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES
|
||||
var renderer *blackfriday.Renderer
|
||||
if latex {
|
||||
// render the data into LaTeX
|
||||
renderer = blackfriday.LatexRenderer(0)
|
||||
} else {
|
||||
// render the data into HTML
|
||||
html_flags := 0
|
||||
if xhtml {
|
||||
html_flags |= blackfriday.HTML_USE_XHTML
|
||||
}
|
||||
if smartypants {
|
||||
html_flags |= blackfriday.HTML_USE_SMARTYPANTS
|
||||
html_flags |= blackfriday.HTML_SMARTYPANTS_FRACTIONS
|
||||
html_flags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES
|
||||
}
|
||||
renderer = blackfriday.HtmlRenderer(html_flags)
|
||||
}
|
||||
|
||||
// render the data into HTML (comment this out to deselect HTML)
|
||||
renderer := blackfriday.HtmlRenderer(html_flags)
|
||||
|
||||
// render the data into LaTeX (uncomment to select LaTeX)
|
||||
//renderer := blackfriday.LatexRenderer(0)
|
||||
|
||||
output := blackfriday.Markdown(input, renderer, extensions)
|
||||
// parse and render
|
||||
var output []byte
|
||||
for i := 0; i < repeat; i++ {
|
||||
output = blackfriday.Markdown(input, renderer, extensions)
|
||||
}
|
||||
|
||||
// output the result
|
||||
if len(os.Args) == 3 {
|
||||
if err = ioutil.WriteFile(os.Args[2], output, 0644); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error writing to", os.Args[2], ":", err)
|
||||
var out *os.File
|
||||
if len(args) == 2 {
|
||||
if out, err = os.Create(args[1]); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error creating %s: %v", args[1], err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
defer out.Close()
|
||||
} else {
|
||||
if _, err = os.Stdout.Write(output); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error writing to Stdout:", err)
|
||||
os.Exit(-1)
|
||||
out = os.Stdout
|
||||
}
|
||||
|
||||
if page {
|
||||
ending := ""
|
||||
if xhtml {
|
||||
fmt.Fprint(out, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" ")
|
||||
fmt.Fprintln(out, "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">")
|
||||
fmt.Fprintln(out, "<html xmlns=\"http://www.w3.org/1999/xhtml\">")
|
||||
ending = " /"
|
||||
} else {
|
||||
fmt.Fprint(out, "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" ")
|
||||
fmt.Fprintln(out, "\"http://www.w3.org/TR/html4/strict.dtd\">")
|
||||
fmt.Fprintln(out, "<html>")
|
||||
}
|
||||
fmt.Fprintln(out, "<head>")
|
||||
fmt.Fprintln(out, " <title></title>")
|
||||
fmt.Fprintf(out, " <meta name=\"GENERATOR\" content=\"Blackfriday markdown processor\"%s>\n", ending)
|
||||
fmt.Fprintf(out, " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"%s>\n", ending)
|
||||
if css != "" {
|
||||
fmt.Fprintf(out, " <link rel=\"stylesheet\" type=\"text/css\" href=\"%s\" />\n", css)
|
||||
}
|
||||
fmt.Fprintln(out, "</head>")
|
||||
fmt.Fprintln(out, "<body>")
|
||||
}
|
||||
if _, err = out.Write(output); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error writing output:", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
if page {
|
||||
fmt.Fprintln(out, "</body>")
|
||||
fmt.Fprintln(out, "</html>")
|
||||
}
|
||||
}
|
||||
|
10
html.go
10
html.go
@ -419,7 +419,7 @@ func htmlAutoLink(out *bytes.Buffer, link []byte, kind int, opaque interface{})
|
||||
if kind == LINK_TYPE_EMAIL {
|
||||
out.WriteString("mailto:")
|
||||
}
|
||||
out.Write(link)
|
||||
attrEscape(out, link)
|
||||
out.WriteString("\">")
|
||||
|
||||
/*
|
||||
@ -504,17 +504,13 @@ func htmlLink(out *bytes.Buffer, link []byte, title []byte, content []byte, opaq
|
||||
}
|
||||
|
||||
out.WriteString("<a href=\"")
|
||||
if len(link) > 0 {
|
||||
out.Write(link)
|
||||
}
|
||||
attrEscape(out, link)
|
||||
if len(title) > 0 {
|
||||
out.WriteString("\" title=\"")
|
||||
attrEscape(out, title)
|
||||
}
|
||||
out.WriteString("\">")
|
||||
if len(content) > 0 {
|
||||
out.Write(content)
|
||||
}
|
||||
out.Write(content)
|
||||
out.WriteString("</a>")
|
||||
return 1
|
||||
}
|
||||
|
13
inline.go
13
inline.go
@ -124,7 +124,7 @@ func inlineCodeSpan(out *bytes.Buffer, rndr *render, data []byte, offset int) in
|
||||
}
|
||||
|
||||
if i < nb && end >= len(data) {
|
||||
out.WriteByte('`')
|
||||
out.WriteByte('`')
|
||||
return 0 // no matching delimiter
|
||||
}
|
||||
|
||||
@ -185,6 +185,11 @@ func inlineLineBreak(out *bytes.Buffer, rndr *render, data []byte, offset int) i
|
||||
|
||||
// '[': parse a link or an image
|
||||
func inlineLink(out *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||
// no links allowed inside other links
|
||||
if rndr.insideLink {
|
||||
return 0
|
||||
}
|
||||
|
||||
isImg := offset > 0 && data[offset-1] == '!'
|
||||
|
||||
data = data[offset:]
|
||||
@ -410,7 +415,11 @@ func inlineLink(out *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||
if isImg {
|
||||
content.Write(data[1:txt_e])
|
||||
} else {
|
||||
// links cannot contain other links, so turn off link parsing temporarily
|
||||
insideLink := rndr.insideLink
|
||||
rndr.insideLink = true
|
||||
parseInline(&content, rndr, data[1:txt_e])
|
||||
rndr.insideLink = insideLink
|
||||
}
|
||||
}
|
||||
|
||||
@ -539,7 +548,7 @@ func inlineEntity(out *bytes.Buffer, rndr *render, data []byte, offset int) int
|
||||
|
||||
func inlineAutoLink(out *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||
// quick check to rule out most false hits on ':'
|
||||
if len(data) < offset+3 || data[offset+1] != '/' || data[offset+2] != '/' {
|
||||
if rndr.insideLink || len(data) < offset+3 || data[offset+1] != '/' || data[offset+2] != '/' {
|
||||
return 0
|
||||
}
|
||||
|
||||
|
@ -141,6 +141,7 @@ type render struct {
|
||||
flags uint32
|
||||
nesting int
|
||||
maxNesting int
|
||||
insideLink bool
|
||||
}
|
||||
|
||||
|
||||
@ -165,6 +166,7 @@ func Markdown(input []byte, renderer *Renderer, extensions uint32) []byte {
|
||||
rndr.flags = extensions
|
||||
rndr.refs = make(map[string]*reference)
|
||||
rndr.maxNesting = 16
|
||||
rndr.insideLink = false
|
||||
|
||||
// register inline parsers
|
||||
if rndr.mk.Emphasis != nil || rndr.mk.DoubleEmphasis != nil || rndr.mk.TripleEmphasis != nil {
|
||||
@ -464,7 +466,7 @@ func expandTabs(out *bytes.Buffer, line []byte) {
|
||||
// the slow case: we need to count runes to figure out how
|
||||
// many spaces to insert for each tab
|
||||
column := 0
|
||||
i = 0
|
||||
i = 0
|
||||
for i < len(line) {
|
||||
start := i
|
||||
for i < len(line) && line[i] != '\t' {
|
||||
|
@ -8,10 +8,10 @@
|
||||
|
||||
<p>6 > 5.</p>
|
||||
|
||||
<p>Here's a <a href="http://example.com/?foo=1&bar=2">link</a> with an ampersand in the URL.</p>
|
||||
<p>Here's a <a href="http://example.com/?foo=1&bar=2">link</a> with an ampersand in the URL.</p>
|
||||
|
||||
<p>Here's a link with an amersand in the link text: <a href="http://att.com/" title="AT&T">AT&T</a>.</p>
|
||||
|
||||
<p>Here's an inline <a href="/script?foo=1&bar=2">link</a>.</p>
|
||||
<p>Here's an inline <a href="/script?foo=1&bar=2">link</a>.</p>
|
||||
|
||||
<p>Here's an inline <a href="/script?foo=1&bar=2">link</a>.</p>
|
||||
<p>Here's an inline <a href="/script?foo=1&bar=2">link</a>.</p>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<p>Link: <a href="http://example.com/">http://example.com/</a>.</p>
|
||||
|
||||
<p>With an ampersand: <a href="http://example.com/?foo=1&bar=2">http://example.com/?foo=1&bar=2</a></p>
|
||||
<p>With an ampersand: <a href="http://example.com/?foo=1&bar=2">http://example.com/?foo=1&bar=2</a></p>
|
||||
|
||||
<ul>
|
||||
<li>In a list?</li>
|
||||
|
Loading…
Reference in New Issue
Block a user