git.iko.soy/typeable/pandoc
on master help sign in
Merged 'strict' branch from r324. This adds a '--strict'

option to pandoc, which forces it to stay as close as possible to official Markdown syntax. git-svn-id: https://pandoc.googlecode.com/svn/trunk@347 788f1e2b-df1e-0410-8736-df70ead52e1b

commit4ea1b2bdc0a86f135bae4ae95cfc3d45a9416604 authorfiddlosopher <fiddlosopher@788f1e2b-df1e-0410-8736-df70ead52e1b> dateSat 30 Dec 2006, 22:51 (20 years ago) parent7cd9db0
23 files changed+248−141
Makefile+7−2 README+13−22 man/man1/html2markdown.1+4 man/man1/markdown2pdf.1+4 man/man1/pandoc.1+3 src/Main.hs+25−13 src/Text/Pandoc/Readers/HTML.hs+17−7 src/Text/Pandoc/Readers/Markdown.hs+123−31 src/Text/Pandoc/Shared.hs+7−4 src/Text/Pandoc/Writers/HTML.hs+9−10 src/Text/Pandoc/Writers/LaTeX.hs−4 src/Text/Pandoc/Writers/Markdown.hs+6−8 src/Text/Pandoc/Writers/RST.hs−6 src/Text/Pandoc/Writers/RTF.hs−4 tests/testsuite.native+8−8 tests/testsuite.txt+3−3 tests/writer.html+1−1 tests/writer.latex+1−1 tests/writer.markdown+6−6 tests/writer.native+8−8 tests/writer.rst+1−1 tests/writer.rtf+1−1 tests/writer.smart.html+1−1
Makefile+7−2
@@ -296,8 +296,13 @@ $(win_pkg_name): $(THIS).exe $(win_docs)
296296.PHONY: test test-markdown
297297test: $(MAIN)
298298 @cd $(TESTDIR) && perl runtests.pl -s $(PWD)/$(MAIN)
299−test-markdown: $(MAIN)
300− @cd $(TESTDIR)/MarkdownTest_1.0.3 && perl MarkdownTest.pl -s $(PWD)/$(MAIN) -tidy
299+strict:=$(MAIN)-strict
300+cleanup_files+=$(strict)
301+$(strict): $(MAIN)
302+ echo "#!/bin/sh -e\n$(PWD)/$(MAIN) --strict \"\$$@\"" > $@; \
303+ chmod +x $(strict)
304+test-markdown: $(strict)
305+ @cd $(TESTDIR)/MarkdownTest_1.0.3 && perl MarkdownTest.pl -s $(PWD)/$(strict) -tidy
301306
302307# Stolen and slightly improved from a GPLed Makefile. Credits to John Meacham.
303308src_all:=$(shell find $(SRCDIR) -type f -name '*hs' | egrep -v '^\./(_darcs|lib|test)/')
README+13−22
@@ -213,6 +213,9 @@ preserved, rather than converted to spaces (the default).
213213
214214`--tabstop` allows the user to set the tab stop (which defaults to 4).
215215
216+`--strict` specifies that strict markdown syntax is to be used, without
217+pandoc's usual extensions and variants (described below).
218+
216219`-R` or `--parse-raw` causes the HTML and LaTeX readers to parse HTML
217220codes and LaTeX environments that it can't translate as raw HTML or
218221LaTeX. Raw HTML can be printed in markdown, reStructuredText, HTML,
@@ -293,7 +296,8 @@ Pandoc's markdown vs. standard markdown
293296
294297In parsing markdown, Pandoc departs from and extends [standard markdown]
295298in a few respects. (To run Pandoc on the official
296−markdown test suite, type `make test-markdown`.)
299+markdown test suite, type `make test-markdown`.) These differences can
300+be suppressed by specifying the `--strict` command-line option.
297301
298302[standard markdown]: http://daringfireball.net/projects/markdown/syntax
299303 "Markdown syntax description"
@@ -319,7 +323,10 @@ the blank space around "Third". Pandoc follows a simple rule:
319323if the text is followed by a blank line, it is treated as a
320324paragraph. Since "Second" is followed by a list, and not a blank
321325line, it isn't treated as a paragraph. The fact that the list
322−is followed by a blank line is irrelevant.
326+is followed by a blank line is irrelevant. (Note: Pandoc works
327+this way even when the `--strict` option is specified. This
328+behavior is consistent with the official markdown syntax
329+description, even though it is different from that of `Markdown.pl`.)
323330
324331Unlike standard markdown, Pandoc allows ordered list items to be
325332marked with single letters, instead of numbers. So, for example,
@@ -350,33 +357,17 @@ the example above:
350357 B) Fie
351358 C) Third
352359
353−Literal quotes in titles
354−------------------------
355−
356−Standard markdown allows unescaped literal quotes in titles, as
357−in
358−
359− [foo]: "bar "embedded" baz"
360−
361−Pandoc requires all quotes within titles to be escaped:
362−
363− [foo]: "bar \"embedded\" baz"
364−
365360Reference links
366361---------------
367362
368−Pandoc allows implicit reference links in either of two styles:
363+Pandoc allows implicit reference links with just a single set of
364+brackets. So, the following links are equivalent:
369365
370366 1. Here's my [link]
371367 2. Here's my [link][]
372368
373369 [link]: linky.com
374370
375−If there's no corresponding reference, the implicit reference link
376−will appear as regular bracketed text. Note: even `[link][]` will
377−appear as `[link]` if there's no reference for `link`. If you want
378−`[link][]`, use a backslash escape: `\[link]\[]`.
379−
380371Footnotes
381372---------
382373
@@ -439,7 +430,7 @@ into
439430 </tr>
440431 </table>
441432
442−whereas Markdown 1.0 will preserve it as is.
433+whereas `Markdown.pl` will preserve it as is.
443434
444435There is one exception to this rule: text between `<script>` and
445436`</script>` tags is not interpreted as markdown.
@@ -527,7 +518,7 @@ Note, however, that material between the begin and end tags will
527518be interpreted as raw LaTeX, not as markdown.
528519
529520Custom headers
530−--------------
521+==============
531522
532523When run with the "standalone" option (`-s`), `pandoc` creates a
533524standalone file, complete with an appropriate header. To see the
man/man1/html2markdown.1+4
@@ -32,6 +32,10 @@ Preserve tabs instead of converting them to spaces.
3232.B \-\-tab-stop=\fITABSTOP\fB
3333Specify tab stop (default is 4).
3434.TP
35+.B \-\-strict
36+Use strict markdown syntax, with no extensions or variants.
37+.TP
38+.TP
3539.B \-R, \-\-parse-raw
3640Parse untranslatable HTML codes as raw HTML.
3741.TP
man/man1/markdown2pdf.1+4
@@ -36,6 +36,10 @@ Preserve tabs instead of converting them to spaces.
3636.B \-\-tab-stop=\fITABSTOP\fB
3737Specify tab stop (default is 4).
3838.TP
39+.B \-\-strict
40+Use strict markdown syntax, with no extensions or variants.
41+.TP
42+.TP
3943.B \-N, \-\-number-sections
4044Number section headings in LaTeX output. (Default is not to number them.)
4145.TP
man/man1/pandoc.1+3
@@ -94,6 +94,9 @@ Preserve tabs instead of converting them to spaces.
9494.B \-\-tab-stop=\fITABSTOP\fB
9595Specify tab stop (default is 4).
9696.TP
97+.B \-\-strict
98+Use strict markdown syntax, with no extensions or variants.
99+.TP
97100.B \-R, \-\-parse-raw
98101Parse untranslatable HTML codes and LaTeX environments as raw HTML
99102or LaTeX, instead of ignoring them.
src/Main.hs+25−13
@@ -91,13 +91,12 @@ writeDoc options = prettyPandoc
9191
9292-- | Data structure for command line options.
9393data Opt = Opt
94− { optPreserveTabs :: Bool -- ^ If @False@, convert tabs to spaces
94+ { optPreserveTabs :: Bool -- ^ Convert tabs to spaces
9595 , optTabStop :: Int -- ^ Number of spaces per tab
96− , optStandalone :: Bool -- ^ If @True@, include header, footer
96+ , optStandalone :: Bool -- ^ Include header, footer
9797 , optReader :: String -- ^ Reader format
9898 , optWriter :: String -- ^ Writer format
99− , optParseRaw :: Bool -- ^ If @True@, parse unconvertable
100− -- HTML and TeX
99+ , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
101100 , optCSS :: String -- ^ CSS file to link to
102101 , optIncludeInHeader :: String -- ^ File to include in header
103102 , optIncludeBeforeBody :: String -- ^ File to include at top of body
@@ -105,11 +104,12 @@ data Opt = Opt
105104 , optCustomHeader :: String -- ^ Custom header to use, or "DEFAULT"
106105 , optTitlePrefix :: String -- ^ Optional prefix for HTML title
107106 , optOutputFile :: String -- ^ Name of output file
108− , optNumberSections :: Bool -- ^ If @True@, number sections in LaTeX
109− , optIncremental :: Bool -- ^ If @True@, incremental lists in S5
110− , optSmart :: Bool -- ^ If @True@, use smart typography
111− , optASCIIMathML :: Bool -- ^ If @True@, use ASCIIMathML in HTML
112− , optDebug :: Bool -- ^ If @True@, output debug messages
107+ , optNumberSections :: Bool -- ^ Number sections in LaTeX
108+ , optIncremental :: Bool -- ^ Use incremental lists in S5
109+ , optSmart :: Bool -- ^ Use smart typography
110+ , optASCIIMathML :: Bool -- ^ Use ASCIIMathML in HTML
111+ , optDebug :: Bool -- ^ Output debug messages
112+ , optStrict :: Bool -- ^ Use strict markdown syntax
113113 }
114114
115115-- | Defaults for command-line options.
@@ -133,6 +133,7 @@ defaultOpts = Opt
133133 , optSmart = False
134134 , optASCIIMathML = False
135135 , optDebug = False
136+ , optStrict = False
136137 }
137138
138139-- | A list of functions, each transforming the options data structure
@@ -175,6 +176,11 @@ options =
175176 "TABSTOP")
176177 "Tab stop (default 4)"
177178
179+ , Option "" ["strict"]
180+ (NoArg
181+ (\opt -> return opt { optStrict = True } ))
182+ "Use strict markdown syntax with no extensions"
183+
178184 , Option "R" ["parse-raw"]
179185 (NoArg
180186 (\opt -> return opt { optParseRaw = True }))
@@ -364,6 +370,7 @@ main = do
364370 , optSmart = smart
365371 , optASCIIMathML = asciiMathML
366372 , optDebug = debug
373+ , optStrict = strict
367374 } = opts
368375
369376 -- assign reader and writer based on options and filenames
@@ -399,7 +406,9 @@ main = do
399406 let filter = tabFilter . addBlank . removeCRs
400407 let startParserState = defaultParserState { stateParseRaw = parseRaw,
401408 stateTabStop = tabStop,
402− stateStandalone = standalone }
409+ stateStandalone = standalone &&
410+ (not strict),
411+ stateStrict = strict }
403412 let csslink = if (css == "")
404413 then ""
405414 else "<link rel=\"stylesheet\" href=\"" ++ css ++
@@ -409,16 +418,19 @@ main = do
409418 then defaultHeader
410419 else customHeader) ++
411420 csslink ++ asciiMathMLLink ++ includeHeader
412− let writerOptions = WriterOptions { writerStandalone = standalone,
421+ let writerOptions = WriterOptions { writerStandalone = standalone &&
422+ (not strict),
413423 writerHeader = header,
414424 writerTitlePrefix = titlePrefix,
415− writerSmart = smart,
425+ writerSmart = smart &&
426+ (not strict),
416427 writerTabStop = tabStop,
417428 writerS5 = (writerName=="s5"),
418429 writerIncremental = incremental,
419430 writerNumberSections = numberSections,
420431 writerIncludeBefore = includeBefore,
421− writerIncludeAfter = includeAfter }
432+ writerIncludeAfter = includeAfter,
433+ writerStrictMarkdown = strict }
422434
423435 (readSources sources) >>= (hPutStr output . encodeUTF8 .
424436 (writer writerOptions) .
src/Text/Pandoc/Readers/HTML.hs+17−7
@@ -32,7 +32,12 @@ module Text.Pandoc.Readers.HTML (
3232 rawHtmlInline,
3333 rawHtmlBlock,
3434 anyHtmlBlockTag,
35− anyHtmlInlineTag
35+ anyHtmlInlineTag,
36+ anyHtmlTag,
37+ anyHtmlEndTag,
38+ htmlEndTag,
39+ extractTagType,
40+ htmlBlockElement
3641 ) where
3742
3843import Text.Regex ( matchRegex, mkRegex )
@@ -78,17 +83,18 @@ inlinesTilEnd tag = try (do
7883 inlines <- manyTill inline (htmlEndTag tag)
7984 return inlines)
8085
81−-- extract type from a tag: e.g. br from <br>, < br >, </br>, etc.
86+-- | Extract type from a tag: e.g. 'br' from '<br>'
8287extractTagType tag =
8388 case (matchRegex (mkRegex "<[[:space:]]*/?([A-Za-z0-9]+)") tag) of
8489 Just [match] -> (map toLower match)
8590 Nothing -> ""
8691
92+-- | Parse any HTML tag (closing or opening) and return text of tag
8793anyHtmlTag = try (do
8894 char '<'
8995 spaces
9096 tag <- many1 alphaNum
91− attribs <- htmlAttributes
97+ attribs <- htmlAttributes
9298 spaces
9399 ender <- option "" (string "/")
94100 let ender' = if (null ender) then "" else " /"
@@ -150,9 +156,10 @@ htmlRegularAttribute = try (do
150156 (do
151157 a <- many (alphaNum <|> (oneOf "-._:"))
152158 return (a,"")) ]
153− return (name, content,
159+ return (name, content,
154160 (" " ++ name ++ "=" ++ quoteStr ++ content ++ quoteStr)))
155161
162+-- | Parse an end tag of type 'tag'
156163htmlEndTag tag = try (do
157164 char '<'
158165 spaces
@@ -174,20 +181,23 @@ anyHtmlInlineTag = try (do
174181 tag <- choice [ anyHtmlTag, anyHtmlEndTag ]
175182 if isInline tag then return tag else fail "not an inline tag")
176183
177−-- scripts must be treated differently, because they can contain <> etc.
184+-- | Parses material between script tags.
185+-- Scripts must be treated differently, because they can contain '<>' etc.
178186htmlScript = try (do
179187 open <- string "<script"
180188 rest <- manyTill anyChar (htmlEndTag "script")
181189 return (open ++ rest ++ "</script>"))
182190
191+htmlBlockElement = choice [ htmlScript, htmlComment, xmlDec, definition ]
192+
183193rawHtmlBlock = try (do
184194 notFollowedBy' (choice [htmlTag "/body", htmlTag "/html"])
185− body <- choice [htmlScript, anyHtmlBlockTag, htmlComment, xmlDec,
186− definition]
195+ body <- htmlBlockElement <|> anyHtmlBlockTag
187196 sp <- (many space)
188197 state <- getState
189198 if stateParseRaw state then return (RawHtml (body ++ sp)) else return Null)
190199
200+-- | Parses an HTML comment.
191201htmlComment = try (do
192202 string "<!--"
193203 comment <- manyTill anyChar (try (string "-->"))
src/Text/Pandoc/Readers/Markdown.hs+123−31
@@ -36,8 +36,11 @@ import Text.ParserCombinators.Pandoc
3636import Text.Pandoc.Definition
3737import Text.Pandoc.Readers.LaTeX ( rawLaTeXInline, rawLaTeXEnvironment )
3838import Text.Pandoc.Shared
39−import Text.Pandoc.Readers.HTML ( rawHtmlInline, rawHtmlBlock,
40− anyHtmlBlockTag, anyHtmlInlineTag )
39+import Text.Pandoc.Readers.HTML ( rawHtmlBlock,
40+ anyHtmlBlockTag, anyHtmlInlineTag,
41+ anyHtmlTag, anyHtmlEndTag,
42+ htmlEndTag, extractTagType,
43+ htmlBlockElement )
4144import Text.Pandoc.HtmlEntities ( decodeEntities )
4245import Text.Regex ( matchRegex, mkRegex )
4346import Text.ParserCombinators.Parsec
@@ -107,6 +110,16 @@ skipNonindentSpaces = do
107110 let tabStop = stateTabStop state
108111 choice (map (\n -> (try (count n (char ' ')))) (reverse [0..(tabStop - 1)]))
109112
113+-- | Fail if reader is in strict markdown syntax mode.
114+failIfStrict = do
115+ state <- getState
116+ if stateStrict state then fail "Strict markdown mode" else return ()
117+
118+-- | Fail unless we're at beginning of a line.
119+failUnlessBeginningOfLine = do
120+ pos <- getPosition
121+ if sourceColumn pos == 1 then return () else fail "not beginning of line"
122+
110123--
111124-- document structure
112125--
@@ -132,6 +145,7 @@ dateLine = try (do
132145 return (removeTrailingSpace date))
133146
134147titleBlock = try (do
148+ failIfStrict
135149 title <- option [] titleLine
136150 author <- option [] authorsLine
137151 date <- option "" dateLine
@@ -147,7 +161,14 @@ parseMarkdown = do
147161 updateState (\state -> state { stateParseRaw = True })
148162 -- need to parse raw HTML, since markdown allows it
149163 (title, author, date) <- option ([],[],"") titleBlock
150− blocks <- parseBlocks
164+ oldState <- getState
165+ oldInput <- getInput
166+ parseBlocks -- go through once just to get list of reference keys
167+ newState <- getState
168+ let keysUsed = stateKeysUsed newState
169+ setInput oldInput
170+ setState (oldState { stateKeysUsed = keysUsed })
171+ blocks <- parseBlocks -- go through again, for real
151172 let blocks' = filter (/= Null) blocks
152173 state <- getState
153174 let keys = reverse $ stateKeyBlocks state
@@ -165,7 +186,7 @@ parseBlocks = do
165186 return result
166187
167188block = choice [ codeBlock, note, referenceKey, header, hrule, list,
168− blockQuote, rawHtmlBlocks, rawLaTeXEnvironment, para,
189+ blockQuote, htmlBlock, rawLaTeXEnvironment', para,
169190 plain, blankBlock, nullBlock ] <?> "block"
170191
171192--
@@ -190,8 +211,7 @@ setextHeader = choice $
190211 map (\x -> setextH x) (enumFromTo 1 (length setextHChars))
191212
192213setextH n = try (do
193− txt <- many1 (do {notFollowedBy newline; inline})
194− endline
214+ txt <- many1Till inline newline
195215 many1 (char (setextHChars !! (n-1)))
196216 skipSpaces
197217 newline
@@ -256,6 +276,7 @@ rawLines = do
256276 return (concat lines)
257277
258278note = try (do
279+ failIfStrict
259280 ref <- noteMarker
260281 char ':'
261282 skipSpaces
@@ -280,6 +301,7 @@ note = try (do
280301--
281302
282303emacsBoxQuote = try (do
304+ failIfStrict
283305 string ",----"
284306 manyTill anyChar newline
285307 raw <- manyTill (try (do
@@ -336,8 +358,9 @@ bulletListStart = try (do
336358orderedListStart = try (do
337359 option ' ' newline -- if preceded by a Plain block in a list context
338360 skipNonindentSpaces
339− many1 digit <|> count 1 letter
340− oneOf orderedListDelimiters
361+ many1 digit <|> (do{failIfStrict; count 1 letter})
362+ delim <- oneOf orderedListDelimiters
363+ if delim /= '.' then failIfStrict else return ()
341364 oneOf spaceChars
342365 skipSpaces)
343366
@@ -410,10 +433,12 @@ bulletList = try (do
410433para = try (do
411434 result <- many1 inline
412435 newline
413− choice [ (do
414− followedBy' (oneOfStrings [">", ",----"])
415− return "" ),
416− blanklines ]
436+ st <- getState
437+ if stateStrict st
438+ then choice [followedBy' blockQuote, followedBy' header,
439+ (do{blanklines; return ()})]
440+ else choice [followedBy' emacsBoxQuote,
441+ (do{blanklines; return ()})]
417442 let result' = normalizeSpaces result
418443 return (Para result'))
419444
@@ -426,6 +451,36 @@ plain = do
426451-- raw html
427452--
428453
454+htmlElement = choice [strictHtmlBlock,
455+ htmlBlockElement] <?> "html element"
456+
457+htmlBlock = do
458+ st <- getState
459+ if stateStrict st
460+ then do
461+ failUnlessBeginningOfLine
462+ first <- htmlElement
463+ finalSpace <- many (oneOf spaceChars)
464+ finalNewlines <- many newline
465+ return (RawHtml (first ++ finalSpace ++ finalNewlines))
466+ else rawHtmlBlocks
467+
468+-- True if tag is self-closing
469+selfClosing tag = case (matchRegex (mkRegex "\\/[[:space:]]*>$") tag) of
470+ Just _ -> True
471+ Nothing -> False
472+
473+strictHtmlBlock = try (do
474+ tag <- anyHtmlBlockTag
475+ let tag' = extractTagType tag
476+ if selfClosing tag || tag' == "hr"
477+ then return tag
478+ else do
479+ contents <- many (do{notFollowedBy' (htmlEndTag tag');
480+ htmlElement <|> (count 1 anyChar)})
481+ end <- htmlEndTag tag'
482+ return $ tag ++ (concat contents) ++ end)
483+
429484rawHtmlBlocks = try (do
430485 htmlBlocks <- many1 rawHtmlBlock
431486 let combined = concatMap (\(RawHtml str) -> str) htmlBlocks
@@ -448,7 +503,18 @@ referenceKey = try (do
448503 option ' ' (char autoLinkEnd)
449504 tit <- option "" title
450505 blanklines
451− return (Key label (Src (removeTrailingSpace src) tit)))
506+ state <- getState
507+ let keysUsed = stateKeysUsed state
508+ updateState (\st -> st { stateKeysUsed = (label:keysUsed) })
509+ return $ Key label (Src (removeTrailingSpace src) tit))
510+
511+--
512+-- LaTeX
513+--
514+
515+rawLaTeXEnvironment' = do
516+ failIfStrict
517+ rawLaTeXEnvironment
452518
453519--
454520-- inline
@@ -457,10 +523,10 @@ referenceKey = try (do
457523text = choice [ math, strong, emph, code2, code1, str, linebreak, tabchar,
458524 whitespace, endline ] <?> "text"
459525
460−inline = choice [ rawLaTeXInline, escapedChar, special, hyphens, text,
526+inline = choice [ rawLaTeXInline', escapedChar, special, hyphens, text,
461527 ltSign, symbol ] <?> "inline"
462528
463−special = choice [ noteRef, inlineNote, link, referenceLink, rawHtmlInline,
529+special = choice [ noteRef, inlineNote, link, referenceLink, rawHtmlInline',
464530 autoLink, image ] <?> "link, inline html, note, or image"
465531
466532escapedChar = escaped anyChar
@@ -507,6 +573,7 @@ mathWord = many1 (choice [ (noneOf (" \t\n\\" ++ [mathEnd])),
507573 return c))])
508574
509575math = try (do
576+ failIfStrict
510577 char mathStart
511578 notFollowedBy space
512579 words <- sepBy1 mathWord (many1 space)
@@ -549,18 +616,17 @@ str = do
549616-- an endline character that can be treated as a space, not a structural break
550617endline = try (do
551618 newline
552− -- next line would allow block quotes without preceding blank line
553− -- Markdown.pl does allow this, but there's a chance of a wrapped
554− -- greater-than sign triggering a block quote by accident...
555− -- notFollowedBy' (choice [emailBlockQuoteStart, string ",----"])
556619 notFollowedBy blankline
557− -- parse potential list-starts differently if in a list:
558620 st <- getState
621+ if stateStrict st
622+ then do
623+ notFollowedBy' emailBlockQuoteStart
624+ notFollowedBy' header
625+ else return ()
626+ -- parse potential list-starts differently if in a list:
559627 if (stateParserContext st) == ListItemState
560− then do
561− notFollowedBy' orderedListStart
562− notFollowedBy' bulletListStart
563− else option () pzero
628+ then notFollowedBy' (orderedListStart <|> bulletListStart)
629+ else return ()
564630 return Space)
565631
566632--
@@ -571,8 +637,12 @@ endline = try (do
571637reference = do
572638 char labelStart
573639 notFollowedBy (char noteStart)
574− label <- manyTill inline (char labelEnd)
575− return (normalizeSpaces label)
640+ -- allow for embedded brackets:
641+ label <- manyTill ((do{res <- reference;
642+ return $ [Str "["] ++ res ++ [Str "]"]}) <|>
643+ count 1 inline)
644+ (char labelEnd)
645+ return (normalizeSpaces (concat label))
576646
577647-- source for a link, with optional title
578648source = try (do
@@ -590,8 +660,10 @@ titleWith startChar endChar = try (do
590660 skipEndline -- a title can be on the next line from the source
591661 skipSpaces
592662 char startChar
593− tit <- manyTill (choice [ try (do {char '\\'; char endChar}),
594− (noneOf (endChar:endLineChars)) ]) (char endChar)
663+ tit <- manyTill anyChar (try (do
664+ char endChar
665+ skipSpaces
666+ followedBy' (char ')' <|> newline)))
595667 let tit' = gsub "\"" "&quot;" tit
596668 return tit')
597669
@@ -608,19 +680,26 @@ explicitLink = try (do
608680
609681referenceLink = choice [referenceLinkDouble, referenceLinkSingle]
610682
611−-- a link like [this][/url/]
683+-- a link like [this][ref]
612684referenceLinkDouble = try (do
613685 label <- reference
614686 skipSpaces
615687 skipEndline
616688 skipSpaces
617689 ref <- reference
618− return (Link label (Ref ref)))
690+ let ref' = if null ref then label else ref
691+ state <- getState
692+ if ref' `elem` (stateKeysUsed state)
693+ then return () else fail "no corresponding key"
694+ return (Link label (Ref ref')))
619695
620696-- a link like [this]
621697referenceLinkSingle = try (do
622698 label <- reference
623− return (Link label (Ref [])))
699+ state <- getState
700+ if label `elem` (stateKeysUsed state)
701+ then return () else fail "no corresponding key"
702+ return (Link label (Ref label)))
624703
625704-- a link <like.this.com>
626705autoLink = try (do
@@ -645,6 +724,7 @@ noteMarker = try (do
645724 manyTill (noneOf " \t\n") (char labelEnd))
646725
647726noteRef = try (do
727+ failIfStrict
648728 ref <- noteMarker
649729 state <- getState
650730 let identifiers = (stateNoteIdentifiers state) ++ [ref]
@@ -652,6 +732,7 @@ noteRef = try (do
652732 return (NoteRef (show (length identifiers))))
653733
654734inlineNote = try (do
735+ failIfStrict
655736 char noteStart
656737 char labelStart
657738 contents <- manyTill inline (char labelEnd)
@@ -664,3 +745,14 @@ inlineNote = try (do
664745 (Note ref [Para contents]):noteBlocks})
665746 return (NoteRef ref))
666747
748+rawLaTeXInline' = do
749+ failIfStrict
750+ rawLaTeXInline
751+
752+rawHtmlInline' = do
753+ st <- getState
754+ result <- if stateStrict st
755+ then choice [htmlBlockElement, anyHtmlTag, anyHtmlEndTag]
756+ else choice [htmlBlockElement, anyHtmlInlineTag]
757+ return (HtmlInline result)
758+
src/Text/Pandoc/Shared.hs+7−4
@@ -114,6 +114,7 @@ data ParserState = ParserState
114114 stateTitle :: [Inline], -- ^ Title of document
115115 stateAuthors :: [String], -- ^ Authors of document
116116 stateDate :: String, -- ^ Date of document
117+ stateStrict :: Bool, -- ^ Use strict markdown syntax
117118 stateHeaderTable :: [HeaderType] -- ^ List of header types used,
118119 -- in what order (rst only)
119120 }
@@ -132,6 +133,7 @@ defaultParserState =
132133 stateTitle = [],
133134 stateAuthors = [],
134135 stateDate = [],
136+ stateStrict = False,
135137 stateHeaderTable = [] }
136138
137139-- | Consolidate @Str@s and @Space@s in an inline list into one big @Str@.
@@ -325,10 +327,11 @@ data WriterOptions = WriterOptions
325327 , writerHeader :: String -- ^ Header for the document
326328 , writerIncludeBefore :: String -- ^ String to include before the body
327329 , writerIncludeAfter :: String -- ^ String to include after the body
328− , writerSmart :: Bool -- ^ If @True@, use smart typography
329− , writerS5 :: Bool -- ^ @True@ if we're writing S5
330− , writerIncremental :: Bool -- ^ If @True@, inceremental S5 lists
331− , writerNumberSections :: Bool -- ^ If @True@, number sections in LaTeX
330+ , writerSmart :: Bool -- ^ Use smart typography
331+ , writerS5 :: Bool -- ^ We're writing S5
332+ , writerIncremental :: Bool -- ^ Incremental S5 lists
333+ , writerNumberSections :: Bool -- ^ Number sections in LaTeX
334+ , writerStrictMarkdown :: Bool -- ^ Use strict markdown syntax
332335 , writerTabStop :: Int -- ^ Tabstop for conversion between
333336 -- spaces and tabs
334337 } deriving Show
src/Text/Pandoc/Writers/HTML.hs+9−10
@@ -91,12 +91,15 @@ obfuscateLink options text src =
9191 then name ++ " at " ++ domain'
9292 else text' ++ " (" ++ name ++ " at " ++
9393 domain' ++ ")" in
94− "<script type=\"text/javascript\">\n<!--\nh='" ++
95− obfuscateString domain ++ "';a='" ++ at' ++ "';n='" ++
96− obfuscateString name ++ "';e=n+a+h;\n" ++
97− "document.write('<a h'+'ref'+'=\"ma'+'ilto'+':'+e+'\">'+" ++
98− linkText ++ "+'<\\/'+'a'+'>');\n// -->\n</script><noscript>" ++
99− obfuscateString altText ++ "</noscript>"
94+ if writerStrictMarkdown options
95+ then "<a href=\"" ++ obfuscateString src' ++ "\">" ++
96+ obfuscateString text' ++ "</a>"
97+ else "<script type=\"text/javascript\">\n<!--\nh='" ++
98+ obfuscateString domain ++ "';a='" ++ at' ++ "';n='" ++
99+ obfuscateString name ++ "';e=n+a+h;\n" ++
100+ "document.write('<a h'+'ref'+'=\"ma'+'ilto'+':'+e+'\">'+" ++
101+ linkText ++ "+'<\\/'+'a'+'>');\n// -->\n</script><noscript>" ++
102+ obfuscateString altText ++ "</noscript>"
100103 _ -> "<a href=\"" ++ src ++ "\">" ++ text' ++ "</a>" -- malformed email
101104
102105-- | Obfuscate character as entity.
@@ -264,8 +267,6 @@ inlineToHtml options (Link text (Src src tit)) =
264267 else "<a href=\"" ++ (codeStringToHtml src) ++ "\"" ++
265268 (if tit /= "" then " title=\"" ++ title ++ "\">" else ">") ++
266269 (inlineListToHtml options text) ++ "</a>"
267−inlineToHtml options (Link text (Ref [])) =
268− "[" ++ (inlineListToHtml options text) ++ "]"
269270inlineToHtml options (Link text (Ref ref)) =
270271 "[" ++ (inlineListToHtml options text) ++ "][" ++
271272 (inlineListToHtml options ref) ++ "]"
@@ -276,8 +277,6 @@ inlineToHtml options (Image alt (Src source tit)) =
276277 "<img src=\"" ++ source ++ "\"" ++
277278 (if tit /= "" then " title=\"" ++ title ++ "\"" else "") ++
278279 (if alternate /= "" then " alt=\"" ++ alternate ++ "\"" else "") ++ ">"
279−inlineToHtml options (Image alternate (Ref [])) =
280− "![" ++ (inlineListToHtml options alternate) ++ "]"
281280inlineToHtml options (Image alternate (Ref ref)) =
282281 "![" ++ (inlineListToHtml options alternate) ++ "][" ++
283282 (inlineListToHtml options ref) ++ "]"
src/Text/Pandoc/Writers/LaTeX.hs−4
@@ -180,15 +180,11 @@ inlineToLaTeX notes (LineBreak) = "\\\\\n"
180180inlineToLaTeX notes Space = " "
181181inlineToLaTeX notes (Link text (Src src tit)) =
182182 "\\href{" ++ src ++ "}{" ++ (inlineListToLaTeX notes (deVerb text)) ++ "}"
183−inlineToLaTeX notes (Link text (Ref [])) = "[" ++
184− (inlineListToLaTeX notes text) ++ "]"
185183inlineToLaTeX notes (Link text (Ref ref)) = "[" ++
186184 (inlineListToLaTeX notes text) ++ "][" ++ (inlineListToLaTeX notes ref) ++
187185 "]" -- this is what markdown does, for better or worse
188186inlineToLaTeX notes (Image alternate (Src source tit)) =
189187 "\\includegraphics{" ++ source ++ "}"
190−inlineToLaTeX notes (Image alternate (Ref [])) =
191− "![" ++ (inlineListToLaTeX notes alternate) ++ "]"
192188inlineToLaTeX notes (Image alternate (Ref ref)) =
193189 "![" ++ (inlineListToLaTeX notes alternate) ++ "][" ++
194190 (inlineListToLaTeX notes ref) ++ "]"
src/Text/Pandoc/Writers/Markdown.hs+6−8
@@ -168,11 +168,12 @@ inlineToMarkdown (Link txt (Src src tit)) =
168168 (if tit /= ""
169169 then text (" \"" ++ (escapeLinkTitle tit) ++ "\"")
170170 else empty) <> char ')'
171−inlineToMarkdown (Link txt (Ref [])) =
172− char '[' <> inlineListToMarkdown txt <> text "][]"
173171inlineToMarkdown (Link txt (Ref ref)) =
174− char '[' <> inlineListToMarkdown txt <> char ']' <> char '[' <>
175− inlineListToMarkdown ref <> char ']'
172+ let first = char '[' <> inlineListToMarkdown txt <> char ']'
173+ second = if (txt == ref)
174+ then empty
175+ else char '[' <> inlineListToMarkdown ref <> char ']' in
176+ first <> second
176177inlineToMarkdown (Image alternate (Src source tit)) =
177178 let alt = if (null alternate) || (alternate == [Str ""])
178179 then text "image"
@@ -181,10 +182,7 @@ inlineToMarkdown (Image alternate (Src source tit)) =
181182 (if tit /= ""
182183 then text (" \"" ++ (escapeLinkTitle tit) ++ "\"")
183184 else empty) <> char ')'
184−inlineToMarkdown (Image alternate (Ref [])) =
185− char '!' <> char '[' <> inlineListToMarkdown alternate <> char ']'
186185inlineToMarkdown (Image alternate (Ref ref)) =
187− char '!' <> char '[' <> inlineListToMarkdown alternate <> char ']' <>
188− char '[' <> inlineListToMarkdown ref <> char ']'
186+ char '!' <> inlineToMarkdown (Link alternate (Ref ref))
189187inlineToMarkdown (NoteRef ref) =
190188 text "[^" <> text (escapeString ref) <> char ']'
src/Text/Pandoc/Writers/RST.hs−6
@@ -202,9 +202,6 @@ inlineToRST (Link txt (Src src tit)) =
202202 else linktext' in
203203 let ref = text ".. _" <> text linktext'' <> text ": " <> text src in
204204 (link, ref' $$ ref)
205−inlineToRST (Link txt (Ref [])) =
206− let (linktext, refs) = inlineListToRST txt in
207− (char '[' <> linktext <> char ']', refs)
208205inlineToRST (Link txt (Ref ref)) =
209206 let (linktext, refs1) = inlineListToRST txt
210207 (reftext, refs2) = inlineListToRST ref in
@@ -216,9 +213,6 @@ inlineToRST (Image alternate (Src source tit)) =
216213 let link = char '|' <> alt <> char '|' in
217214 let ref = text ".. " <> link <> text " image:: " <> text source in
218215 (link, ref' $$ ref)
219−inlineToRST (Image alternate (Ref [])) =
220− let (alttext, refs) = inlineListToRST alternate in
221− (char '|' <> alttext <> char '|', refs)
222216-- The following case won't normally occur...
223217inlineToRST (Image alternate (Ref ref)) =
224218 let (alttext, refs1) = inlineListToRST alternate
src/Text/Pandoc/Writers/RTF.hs−4
@@ -220,15 +220,11 @@ inlineToRTF notes Space = " "
220220inlineToRTF notes (Link text (Src src tit)) =
221221 "{\\field{\\*\\fldinst{HYPERLINK \"" ++ (codeStringToRTF src) ++
222222 "\"}}{\\fldrslt{\\ul\n" ++ (inlineListToRTF notes text) ++ "\n}}}\n"
223−inlineToRTF notes (Link text (Ref [])) =
224− "[" ++ (inlineListToRTF notes text) ++ "]"
225223inlineToRTF notes (Link text (Ref ref)) =
226224 "[" ++ (inlineListToRTF notes text) ++ "][" ++
227225 (inlineListToRTF notes ref) ++ "]" -- this is what markdown does
228226inlineToRTF notes (Image alternate (Src source tit)) =
229227 "{\\cf1 [image: " ++ source ++ "]\\cf0}"
230−inlineToRTF notes (Image alternate (Ref [])) =
231− "![" ++ (inlineListToRTF notes alternate) ++ "]"
232228inlineToRTF notes (Image alternate (Ref ref)) = "![" ++
233229 (inlineListToRTF notes alternate) ++ "][" ++
234230 (inlineListToRTF notes ref) ++ "]"
tests/testsuite.native+8−8
@@ -277,18 +277,18 @@ Pandoc (Meta [Str "Pandoc",Space,Str "Test",Space,Str "Suite"] ["John MacFarlane
277277, Para [Str "Foo",Space,Link [Str "bar"] (Ref [Str "a"]),Str "."]
278278, Para [Str "Foo",Space,Link [Str "bar"] (Ref [Str "a"]),Str "."]
279279, Key [Str "a"] (Src "/url/" "")
280−, Para [Str "With",Space,Link [Str "embedded",Space,Link [Str "brackets"] (Ref [])] (Ref [Str "b"]),Str "."]
281−, Para [Link [Str "b"] (Ref []),Space,Str "by",Space,Str "itself",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "link."]
282−, Para [Str "Indented",Space,Link [Str "once"] (Ref []),Str "."]
283−, Para [Str "Indented",Space,Link [Str "twice"] (Ref []),Str "."]
284−, Para [Str "Indented",Space,Link [Str "thrice"] (Ref []),Str "."]
285−, Para [Str "This",Space,Str "should",Space,Link [Str "not"] (Ref []),Space,Str "be",Space,Str "a",Space,Str "link."]
280+, Para [Str "With",Space,Link [Str "embedded",Space,Str "[",Str "brackets",Str "]"] (Ref [Str "b"]),Str "."]
281+, Para [Link [Str "b"] (Ref [Str "b"]),Space,Str "by",Space,Str "itself",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "link."]
282+, Para [Str "Indented",Space,Link [Str "once"] (Ref [Str "once"]),Str "."]
283+, Para [Str "Indented",Space,Link [Str "twice"] (Ref [Str "twice"]),Str "."]
284+, Para [Str "Indented",Space,Link [Str "thrice"] (Ref [Str "thrice"]),Str "."]
285+, Para [Str "This",Space,Str "should",Space,Str "[",Str "not",Str "]",Str "[",Str "]",Space,Str "be",Space,Str "a",Space,Str "link."]
286286, Key [Str "once"] (Src "/url" "")
287287, Key [Str "twice"] (Src "/url" "")
288288, Key [Str "thrice"] (Src "/url" "")
289289, CodeBlock "[not]: /url"
290290, Key [Str "b"] (Src "/url/" "")
291−, Para [Str "Foo",Space,Link [Str "bar"] (Ref []),Str "."]
291+, Para [Str "Foo",Space,Link [Str "bar"] (Ref [Str "bar"]),Str "."]
292292, Para [Str "Foo",Space,Link [Str "biz"] (Src "/url/" "Title with &quot;quote&quot; inside"),Str "."]
293293, Key [Str "bar"] (Src "/url/" "Title with &quot;quotes&quot; inside")
294294, Header 2 [Str "With",Space,Str "ampersands"]
@@ -313,7 +313,7 @@ Pandoc (Meta [Str "Pandoc",Space,Str "Test",Space,Str "Suite"] ["John MacFarlane
313313, HorizontalRule
314314, Header 1 [Str "Images"]
315315, Para [Str "From",Space,Str "\"Voyage",Space,Str "dans",Space,Str "la",Space,Str "Lune\"",Space,Str "by",Space,Str "Georges",Space,Str "Melies",Space,Str "(1902):"]
316−, Para [Image [Str "lalune"] (Ref [])]
316+, Para [Image [Str "lalune"] (Ref [Str "lalune"])]
317317, Key [Str "lalune"] (Src "lalune.jpg" "Voyage dans la Lune")
318318, Para [Str "Here",Space,Str "is",Space,Str "a",Space,Str "movie",Space,Image [Str "movie"] (Src "movie.jpg" ""),Space,Str "icon."]
319319, HorizontalRule
tests/testsuite.txt+3−3
@@ -498,7 +498,7 @@ Just a [URL](/url/).
498498
499499[URL and title](/url/ "title preceded by a tab").
500500
501−[URL and title](/url/ "title with \"quotes\" in it")
501+[URL and title](/url/ "title with "quotes" in it")
502502
503503[URL and title](/url/ 'title with single quotes')
504504
@@ -541,9 +541,9 @@ This should [not][] be a link.
541541
542542Foo [bar][].
543543
544−Foo [biz](/url/ "Title with \"quote\" inside").
544+Foo [biz](/url/ "Title with "quote" inside").
545545
546− [bar]: /url/ "Title with \"quotes\" inside"
546+ [bar]: /url/ "Title with "quotes" inside"
547547
548548## With ampersands
549549
tests/writer.html+1−1
@@ -407,7 +407,7 @@ document.write('<a h'+'ref'+'="ma'+'ilto'+':'+e+'">'+'Email link'+'<\/'+'a'+'>')
407407<p>Indented <a href="/url">once</a>.</p>
408408<p>Indented <a href="/url">twice</a>.</p>
409409<p>Indented <a href="/url">thrice</a>.</p>
410−<p>This should [not] be a link.</p>
410+<p>This should [not][] be a link.</p>
411411<pre><code>[not]: /url
412412</code></pre>
413413<p>Foo <a href="/url/" title="Title with &quot;quotes&quot; inside">bar</a>.</p>
tests/writer.latex+1−1
@@ -514,7 +514,7 @@ Indented \href{/url}{twice}.
514514
515515Indented \href{/url}{thrice}.
516516
517−This should [not] be a link.
517+This should [not][] be a link.
518518
519519\begin{verbatim}
520520[not]: /url
tests/writer.markdown+6−6
@@ -529,15 +529,15 @@ Foo [bar][a].
529529
530530 [a]: /url/
531531
532−With [embedded [brackets][]][b].
532+With [embedded [brackets]][b].
533533
534−[b][] by itself should be a link.
534+[b] by itself should be a link.
535535
536−Indented [once][].
536+Indented [once].
537537
538−Indented [twice][].
538+Indented [twice].
539539
540−Indented [thrice][].
540+Indented [thrice].
541541
542542This should [not][] be a link.
543543
@@ -550,7 +550,7 @@ This should [not][] be a link.
550550
551551 [b]: /url/
552552
553−Foo [bar][].
553+Foo [bar].
554554
555555Foo [biz](/url/ "Title with &quot;quote&quot; inside").
556556
tests/writer.native+8−8
@@ -277,18 +277,18 @@ Pandoc (Meta [Str "Pandoc",Space,Str "Test",Space,Str "Suite"] ["John MacFarlane
277277, Para [Str "Foo",Space,Link [Str "bar"] (Ref [Str "a"]),Str "."]
278278, Para [Str "Foo",Space,Link [Str "bar"] (Ref [Str "a"]),Str "."]
279279, Key [Str "a"] (Src "/url/" "")
280−, Para [Str "With",Space,Link [Str "embedded",Space,Link [Str "brackets"] (Ref [])] (Ref [Str "b"]),Str "."]
281−, Para [Link [Str "b"] (Ref []),Space,Str "by",Space,Str "itself",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "link."]
282−, Para [Str "Indented",Space,Link [Str "once"] (Ref []),Str "."]
283−, Para [Str "Indented",Space,Link [Str "twice"] (Ref []),Str "."]
284−, Para [Str "Indented",Space,Link [Str "thrice"] (Ref []),Str "."]
285−, Para [Str "This",Space,Str "should",Space,Link [Str "not"] (Ref []),Space,Str "be",Space,Str "a",Space,Str "link."]
280+, Para [Str "With",Space,Link [Str "embedded",Space,Str "[",Str "brackets",Str "]"] (Ref [Str "b"]),Str "."]
281+, Para [Link [Str "b"] (Ref [Str "b"]),Space,Str "by",Space,Str "itself",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "link."]
282+, Para [Str "Indented",Space,Link [Str "once"] (Ref [Str "once"]),Str "."]
283+, Para [Str "Indented",Space,Link [Str "twice"] (Ref [Str "twice"]),Str "."]
284+, Para [Str "Indented",Space,Link [Str "thrice"] (Ref [Str "thrice"]),Str "."]
285+, Para [Str "This",Space,Str "should",Space,Str "[",Str "not",Str "]",Str "[",Str "]",Space,Str "be",Space,Str "a",Space,Str "link."]
286286, Key [Str "once"] (Src "/url" "")
287287, Key [Str "twice"] (Src "/url" "")
288288, Key [Str "thrice"] (Src "/url" "")
289289, CodeBlock "[not]: /url"
290290, Key [Str "b"] (Src "/url/" "")
291−, Para [Str "Foo",Space,Link [Str "bar"] (Ref []),Str "."]
291+, Para [Str "Foo",Space,Link [Str "bar"] (Ref [Str "bar"]),Str "."]
292292, Para [Str "Foo",Space,Link [Str "biz"] (Src "/url/" "Title with &quot;quote&quot; inside"),Str "."]
293293, Key [Str "bar"] (Src "/url/" "Title with &quot;quotes&quot; inside")
294294, Header 2 [Str "With",Space,Str "ampersands"]
@@ -313,7 +313,7 @@ Pandoc (Meta [Str "Pandoc",Space,Str "Test",Space,Str "Suite"] ["John MacFarlane
313313, HorizontalRule
314314, Header 1 [Str "Images"]
315315, Para [Str "From",Space,Str "\"Voyage",Space,Str "dans",Space,Str "la",Space,Str "Lune\"",Space,Str "by",Space,Str "Georges",Space,Str "Melies",Space,Str "(1902):"]
316−, Para [Image [Str "lalune"] (Ref [])]
316+, Para [Image [Str "lalune"] (Ref [Str "lalune"])]
317317, Key [Str "lalune"] (Src "lalune.jpg" "Voyage dans la Lune")
318318, Para [Str "Here",Space,Str "is",Space,Str "a",Space,Str "movie",Space,Image [Str "movie"] (Src "movie.jpg" ""),Space,Str "icon."]
319319, HorizontalRule
tests/writer.rst+1−1
@@ -638,7 +638,7 @@ Indented `twice`_.
638638
639639Indented `thrice`_.
640640
641−This should [not] be a link.
641+This should [not][] be a link.
642642
643643::
644644
tests/writer.rtf+1−1
@@ -312,7 +312,7 @@ twice
312312thrice
313313}}}
314314.\par}
315−{\pard \f0 \sa180 \li0 \fi0 This should [not] be a link.\par}
315+{\pard \f0 \sa180 \li0 \fi0 This should [not][] be a link.\par}
316316{\pard \f0 \sa180 \li0 \fi0 \f1 [not]: /url\par}
317317{\pard \f0 \sa180 \li0 \fi0 Foo {\field{\*\fldinst{HYPERLINK "/url/"}}{\fldrslt{\ul
318318bar
tests/writer.smart.html+1−1
@@ -407,7 +407,7 @@ document.write('<a h'+'ref'+'="ma'+'ilto'+':'+e+'">'+'Email link'+'<\/'+'a'+'>')
407407<p>Indented <a href="/url">once</a>.</p>
408408<p>Indented <a href="/url">twice</a>.</p>
409409<p>Indented <a href="/url">thrice</a>.</p>
410−<p>This should [not] be a link.</p>
410+<p>This should [not][] be a link.</p>
411411<pre><code>[not]: /url
412412</code></pre>
413413<p>Foo <a href="/url/" title="Title with &quot;quotes&quot; inside">bar</a>.</p>