Jira writer: use jira-wiki-markup renderer

Pandoc's AST is translated into the Jira AST, which is then rendered by
the dedicated Jira printer.

The following improvements are included in this change:

- non-jira raw blocks are fully discarded instead of showing as blank
  lines;
- table cells can contain multiple blocks;
- unnecessary blank lines are removed from the output;
- markup chars within words are properly surrounded by braces;
- preserving soft linebreaks via `--wrap=preserve` is supported.

Note that backslashes are rendered as HTML entities, as there appears no
alternative to produce a plain backslash if it is followed by markup.
This may cause problems when used with confluence, where rendering seems
to fail in this case.

Closes: #5926
This commit is contained in:
Albert Krewinkel 2019-12-20 17:12:46 +01:00
parent b06124e43a
commit 2c13773be8
No known key found for this signature in database
GPG key ID: 388DC0B21F631124
3 changed files with 268 additions and 394 deletions

View file

@ -1,3 +1,4 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE NoImplicitPrelude #-}
{- | {- |
@ -16,296 +17,251 @@ JIRA:
-} -}
module Text.Pandoc.Writers.Jira ( writeJira ) where module Text.Pandoc.Writers.Jira ( writeJira ) where
import Prelude import Prelude
import Control.Monad.State.Strict import Control.Monad.Reader (ReaderT, ask, asks, runReaderT)
import Control.Monad.State.Strict (StateT, evalStateT, gets, modify)
import Data.Foldable (find) import Data.Foldable (find)
import Data.Text (Text, pack) import Data.Text (Text)
import Text.Pandoc.Class (PandocMonad, report) import Text.Jira.Parser (plainText)
import Text.Jira.Printer (prettyBlocks, prettyInlines)
import Text.Pandoc.Class (PandocMonad)
import Text.Pandoc.Definition import Text.Pandoc.Definition
import Text.Pandoc.Logging (LogMessage (BlockNotRendered, InlineNotRendered)) import Text.Pandoc.Options (WriterOptions (writerTemplate, writerWrapText),
import Text.Pandoc.Options (WriterOptions (writerTemplate)) WrapOption (..))
import Text.Pandoc.Shared (blocksToInlines, linesToPara) import Text.Pandoc.Shared (linesToPara)
import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Templates (renderTemplate)
import Text.Pandoc.Writers.Math (texMathToInlines) import Text.Pandoc.Writers.Math (texMathToInlines)
import Text.Pandoc.Writers.Shared (metaToContext, defField) import Text.Pandoc.Writers.Shared (defField, metaToContext)
import qualified Data.Text as T
import Text.DocLayout (literal, render) import Text.DocLayout (literal, render)
import qualified Data.Text as T
data WriterState = WriterState import qualified Text.Jira.Markup as Jira
{ stNotes :: [Text] -- Footnotes
, stListLevel :: Text -- String at beginning of list items, e.g. "**"
}
-- | Initial writer state
startState :: WriterState
startState = WriterState
{ stNotes = []
, stListLevel = ""
}
type JiraWriter = StateT WriterState
-- | Convert Pandoc to Jira. -- | Convert Pandoc to Jira.
writeJira :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeJira :: PandocMonad m => WriterOptions -> Pandoc -> m Text
writeJira opts document = writeJira opts = runDefaultConverter (writerWrapText opts) (pandocToJira opts)
evalStateT (pandocToJira opts document) startState
-- | State to keep track of footnotes.
newtype ConverterState = ConverterState { stNotes :: [Text] }
-- | Initial converter state.
startState :: ConverterState
startState = ConverterState { stNotes = [] }
-- | Converter monad
type JiraConverter m = ReaderT WrapOption (StateT ConverterState m)
-- | Run a converter using the default state
runDefaultConverter :: PandocMonad m
=> WrapOption
-> (a -> JiraConverter m Text)
-> a
-> m Text
runDefaultConverter wrap c x = evalStateT (runReaderT (c x) wrap) startState
-- | Return Jira representation of document. -- | Return Jira representation of document.
pandocToJira :: PandocMonad m pandocToJira :: PandocMonad m
=> WriterOptions -> Pandoc -> JiraWriter m Text => WriterOptions -> Pandoc -> JiraConverter m Text
pandocToJira opts (Pandoc meta blocks) = do pandocToJira opts (Pandoc meta blocks) = do
wrap <- ask
metadata <- metaToContext opts metadata <- metaToContext opts
(fmap literal . blockListToJira opts) (fmap literal . runDefaultConverter wrap blockListToJira)
(fmap literal . inlineListToJira opts) meta (fmap literal . runDefaultConverter wrap inlineListToJira) meta
body <- blockListToJira opts blocks body <- blockListToJira blocks
notes <- gets $ T.intercalate "\n" . reverse . stNotes notes <- gets $ T.intercalate "\n" . reverse . stNotes
let main = body <> if T.null notes let main = body <> if T.null notes then mempty else "\n\n" <> notes
then mempty
else T.pack "\n\n" <> notes
let context = defField "body" main metadata let context = defField "body" main metadata
return $ return $
case writerTemplate opts of case writerTemplate opts of
Nothing -> main Nothing -> main
Just tpl -> render Nothing $ renderTemplate tpl context Just tpl -> render Nothing $ renderTemplate tpl context
-- | Escape one character as needed for Jira. blockListToJira :: PandocMonad m => [Block] -> JiraConverter m Text
escapeCharForJira :: Char -> Text blockListToJira = fmap prettyBlocks . toJiraBlocks
escapeCharForJira c =
let specialChars = "_*-+~^|!{}[]" :: String
in case c of
'\x2013' -> " -- "
'\x2014' -> " --- "
'\x2026' -> "..."
_ | c `elem` specialChars -> T.cons '\\' (T.singleton c)
_ -> T.singleton c
-- | Escape string as needed for Jira. inlineListToJira :: PandocMonad m => [Inline] -> JiraConverter m Text
escapeStringForJira :: Text -> Text inlineListToJira = fmap prettyInlines . toJiraInlines
escapeStringForJira = T.concatMap escapeCharForJira
-- | Create an anchor macro from the given element attributes. toJiraBlocks :: PandocMonad m => [Block] -> JiraConverter m [Jira.Block]
anchor :: Attr -> Text toJiraBlocks blocks = do
anchor (ident,_,_) = let convert = \case
if ident == "" BlockQuote bs -> singleton . Jira.BlockQuote
then "" <$> toJiraBlocks bs -- FIXME!
else "{anchor:" <> ident <> "}" BulletList items -> singleton . Jira.List Jira.CircleBullets
<$> toJiraItems items
CodeBlock attr cs -> toJiraCode attr cs
DefinitionList items -> toJiraDefinitionList items
Div attr bs -> toJiraPanel attr bs
Header lvl attr xs -> toJiraHeader lvl attr xs
HorizontalRule -> return . singleton $ Jira.HorizontalRule
LineBlock xs -> toJiraBlocks [linesToPara xs]
OrderedList _ items -> singleton . Jira.List Jira.Enumeration
<$> toJiraItems items
Para xs -> singleton . Jira.Para <$> toJiraInlines xs
Plain xs -> singleton . Jira.Para <$> toJiraInlines xs
RawBlock fmt cs -> rawBlockToJira fmt cs
Null -> return mempty
Table _ _ _ hd body -> singleton <$> do
headerRow <- if null hd
then Just <$> toRow Jira.HeaderCell hd
else pure Nothing
bodyRows <- mapM (toRow Jira.BodyCell) body
let rows = case headerRow of
Just header -> header : bodyRows
Nothing -> bodyRows
return $ Jira.Table rows
jiraBlocks <- mapM convert blocks
return $ mconcat jiraBlocks
-- | Append a newline character unless we are in a list. toRow :: PandocMonad m
appendNewlineUnlessInList :: PandocMonad m => ([Jira.Block] -> Jira.Cell)
=> Text -> [TableCell]
-> JiraWriter m Text -> JiraConverter m Jira.Row
appendNewlineUnlessInList t = do toRow mkCell cells = Jira.Row <$>
listLevel <- gets stListLevel mapM (fmap mkCell . toJiraBlocks) cells
return (if T.null listLevel then t <> "\n" else t)
-- | Convert Pandoc block element to Jira. toJiraItems :: PandocMonad m => [[Block]] -> JiraConverter m [[Jira.Block]]
blockToJira :: PandocMonad m toJiraItems = mapM toJiraBlocks
=> WriterOptions -- ^ Options
-> Block -- ^ Block element
-> JiraWriter m Text
blockToJira _ Null = return "" toJiraCode :: PandocMonad m
=> Attr
-> Text
-> JiraConverter m [Jira.Block]
toJiraCode (ident, classes, _attribs) code = do
let lang = case find (\c -> T.toLower c `elem` knownLanguages) classes of
Nothing -> Jira.Language "java"
Just l -> Jira.Language l
let addAnchor b = if T.null ident
then b
else [Jira.Para (singleton (Jira.Anchor ident))] <> b
return . addAnchor . singleton $ Jira.Code lang mempty code
blockToJira opts (Div attr bs) = -- | Creates a Jira definition list
(anchor attr <>) <$> blockListToJira opts bs toJiraDefinitionList :: PandocMonad m
=> [([Inline], [[Block]])]
-> JiraConverter m [Jira.Block]
toJiraDefinitionList defItems = do
let convertDefItem (term, defs) = do
jiraTerm <- Jira.Para <$> styled Jira.Strong term
jiraDefs <- mconcat <$> mapM toJiraBlocks defs
return $ jiraTerm : jiraDefs
singleton . Jira.List Jira.CircleBullets <$> mapM convertDefItem defItems
blockToJira opts (Plain inlines) = -- | Creates a Jira panel
inlineListToJira opts inlines toJiraPanel :: PandocMonad m
=> Attr -> [Block]
-> JiraConverter m [Jira.Block]
toJiraPanel attr blocks = do
jiraBlocks <- toJiraBlocks blocks
return $ if attr == nullAttr
then jiraBlocks
else singleton (Jira.Panel [] jiraBlocks)
blockToJira opts (Para inlines) = do -- | Creates a Jira header
contents <- inlineListToJira opts inlines toJiraHeader :: PandocMonad m
appendNewlineUnlessInList contents => Int -> Attr -> [Inline]
-> JiraConverter m [Jira.Block]
toJiraHeader lvl (ident, _, _) inlines =
let anchor = Jira.Anchor ident
in singleton . Jira.Header lvl . (anchor :) <$> toJiraInlines inlines
blockToJira opts (LineBlock lns) = -- | Handles raw block. Jira is included verbatim, everything else is
blockToJira opts $ linesToPara lns -- discarded.
rawBlockToJira :: PandocMonad m
=> Format -> Text
-> JiraConverter m [Jira.Block]
rawBlockToJira fmt cs = do
rawInlines <- toJiraRaw fmt cs
return $
if null rawInlines
then mempty
else singleton (Jira.Para rawInlines)
blockToJira _ b@(RawBlock f str) = toJiraRaw :: PandocMonad m
if f == Format "jira" => Format -> Text -> JiraConverter m [Jira.Inline]
then return str toJiraRaw fmt cs = case fmt of
else "" <$ report (BlockNotRendered b) Format "jira" -> return . singleton $ Jira.Str cs
_ -> return mempty
blockToJira _ HorizontalRule = return "----\n"
blockToJira opts (Header level attr inlines) = do --
contents <- inlineListToJira opts inlines -- Inlines
let prefix = "h" <> pack (show level) <> ". " --
return $ prefix <> anchor attr <> contents <> "\n"
blockToJira _ (CodeBlock attr@(_,classes,_) str) = do toJiraInlines :: PandocMonad m => [Inline] -> JiraConverter m [Jira.Inline]
let lang = find (\c -> T.toLower c `elem` knownLanguages) classes toJiraInlines inlines = do
let start = case lang of let convert = \case
Nothing -> "{code}" Cite _ xs -> toJiraInlines xs
Just l -> "{code:" <> l <> "}" Code _ cs -> return . singleton $
let anchorMacro = anchor attr Jira.Monospaced (escapeSpecialChars cs)
appendNewlineUnlessInList . T.intercalate "\n" $ Emph xs -> styled Jira.Emphasis xs
(if anchorMacro == "" then id else (anchorMacro :)) Image _ _ (src, _) -> pure . singleton $ Jira.Image [] (Jira.URL src)
[start, str, "{code}"] LineBreak -> pure . singleton $ Jira.Linebreak
Link _ xs (tgt, _) -> singleton . flip Jira.Link (Jira.URL tgt)
<$> toJiraInlines xs
Math mtype cs -> mathToJira mtype cs
Note bs -> registerNotes bs
Quoted qt xs -> quotedToJira qt xs
RawInline fmt cs -> toJiraRaw fmt cs
SmallCaps xs -> styled Jira.Strong xs
SoftBreak -> do
preserveBreak <- asks (== WrapPreserve)
pure . singleton $ if preserveBreak
then Jira.Linebreak
else Jira.Space
Space -> pure . singleton $ Jira.Space
Span _attr xs -> toJiraInlines xs
Str s -> pure $ escapeSpecialChars s
Strikeout xs -> styled Jira.Strikeout xs
Strong xs -> styled Jira.Strong xs
Subscript xs -> styled Jira.Subscript xs
Superscript xs -> styled Jira.Superscript xs
jiraInlines <- mapM convert inlines
return $ mconcat jiraInlines
blockToJira opts (BlockQuote [p@(Para _)]) = do singleton :: a -> [a]
contents <- blockToJira opts p singleton = (:[])
return ("bq. " <> contents)
blockToJira opts (BlockQuote blocks) = do styled :: PandocMonad m
contents <- blockListToJira opts blocks => Jira.InlineStyle -> [Inline]
appendNewlineUnlessInList . T.unlines $ -> JiraConverter m [Jira.Inline]
[ "{quote}", contents, "{quote}"] styled s = fmap (singleton . Jira.Styled s) . toJiraInlines
blockToJira opts (Table _caption _aligns _widths headers rows) = do -- | Converts a plain text value to Jira inlines, ensuring that all
headerCells <- mapM blocksToCell headers -- special characters will be handled appropriately.
bodyRows <- mapM (mapM blocksToCell) rows escapeSpecialChars :: Text -> [Jira.Inline]
let tblHead = headerCellsToRow headerCells escapeSpecialChars t = case plainText t of
let tblBody = map cellsToRow bodyRows Right xs -> xs
return $ if all null headers Left _ -> singleton $ Jira.Str t
then T.unlines tblBody
else T.unlines (tblHead : tblBody)
where
blocksToCell :: PandocMonad m => [Block] -> JiraWriter m Text
blocksToCell = inlineListToJira opts . blocksToInlines
cellsToRow :: [Text] -> Text mathToJira :: PandocMonad m
cellsToRow cells = "|" <> T.intercalate "|" cells <> "|" => MathType
-> Text
-> JiraConverter m [Jira.Inline]
mathToJira mtype cs = do
mathInlines <- toJiraInlines =<< texMathToInlines mtype cs
return $ case mtype of
InlineMath -> mathInlines
DisplayMath -> Jira.Linebreak : mathInlines ++ [Jira.Linebreak]
headerCellsToRow :: [Text] -> Text quotedToJira :: PandocMonad m
headerCellsToRow cells = "||" <> T.intercalate "||" cells <> "||" => QuoteType
-> [Inline]
-> JiraConverter m [Jira.Inline]
quotedToJira qtype xs = do
let quoteChar = case qtype of
DoubleQuote -> "\""
SingleQuote -> "'"
let surroundWithQuotes = (Jira.Str quoteChar :) . (++ [Jira.Str quoteChar])
surroundWithQuotes <$> toJiraInlines xs
blockToJira opts (BulletList items) = registerNotes :: PandocMonad m => [Block] -> JiraConverter m [Jira.Inline]
listWithMarker opts items '*' registerNotes contents = do
blockToJira opts (OrderedList _listAttr items) =
listWithMarker opts items '#'
blockToJira opts (DefinitionList items) =
blockToJira opts (BulletList (map defToBulletItem items))
where
defToBulletItem :: ([Inline], [[Block]]) -> [Block]
defToBulletItem (inlns, defs) =
let term = Plain [Strong inlns]
blks = mconcat defs
in term : blks
-- Auxiliary functions for lists:
-- | Create a list using the given character as bullet item marker.
listWithMarker :: PandocMonad m
=> WriterOptions
-> [[Block]]
-> Char
-> JiraWriter m Text
listWithMarker opts items marker = do
modify $ \s -> s { stListLevel = stListLevel s `T.snoc` marker }
contents <- mapM (listItemToJira opts) items
modify $ \s -> s { stListLevel = T.init (stListLevel s) }
appendNewlineUnlessInList $ T.intercalate "\n" contents
-- | Convert bullet or ordered list item (list of blocks) to Jira.
listItemToJira :: PandocMonad m
=> WriterOptions
-> [Block]
-> JiraWriter m Text
listItemToJira opts items = do
contents <- blockListToJira opts items
marker <- gets stListLevel
return $ marker <> " " <> contents
-- | Convert list of Pandoc block elements to Jira.
blockListToJira :: PandocMonad m
=> WriterOptions -- ^ Options
-> [Block] -- ^ List of block elements
-> JiraWriter m Text
blockListToJira opts blocks =
T.intercalate "\n" <$> mapM (blockToJira opts) blocks
-- | Convert list of Pandoc inline elements to Jira.
inlineListToJira :: PandocMonad m
=> WriterOptions
-> [Inline]
-> JiraWriter m Text
inlineListToJira opts lst =
T.concat <$> mapM (inlineToJira opts) lst
-- | Convert Pandoc inline element to Jira.
inlineToJira :: PandocMonad m
=> WriterOptions
-> Inline
-> JiraWriter m Text
inlineToJira opts (Span attr lst) =
(anchor attr <>) <$> inlineListToJira opts lst
inlineToJira opts (Emph lst) = do
contents <- inlineListToJira opts lst
return $ "_" <> contents <> "_"
inlineToJira opts (Strong lst) = do
contents <- inlineListToJira opts lst
return $ "*" <> contents <> "*"
inlineToJira opts (Strikeout lst) = do
contents <- inlineListToJira opts lst
return $ "-" <> contents <> "-"
inlineToJira opts (Superscript lst) = do
contents <- inlineListToJira opts lst
return $ "{^" <> contents <> "^}"
inlineToJira opts (Subscript lst) = do
contents <- inlineListToJira opts lst
return $ "{~" <> contents <> "~}"
inlineToJira opts (SmallCaps lst) = inlineListToJira opts lst
inlineToJira opts (Quoted SingleQuote lst) = do
contents <- inlineListToJira opts lst
return $ "'" <> contents <> "'"
inlineToJira opts (Quoted DoubleQuote lst) = do
contents <- inlineListToJira opts lst
return $ "\"" <> contents <> "\""
inlineToJira opts (Cite _ lst) = inlineListToJira opts lst
inlineToJira _ (Code attr str) =
return (anchor attr <> "{{" <> str <> "}}")
inlineToJira _ (Str str) = return $ escapeStringForJira str
inlineToJira opts (Math InlineMath str) =
lift (texMathToInlines InlineMath str) >>= inlineListToJira opts
inlineToJira opts (Math DisplayMath str) = do
mathInlines <- lift (texMathToInlines DisplayMath str)
contents <- inlineListToJira opts mathInlines
return $ "\\\\" <> contents <> "\\\\"
inlineToJira _opts il@(RawInline f str) =
if f == Format "jira"
then return str
else "" <$ report (InlineNotRendered il)
inlineToJira _ LineBreak = return "\n"
inlineToJira _ SoftBreak = return " "
inlineToJira _ Space = return " "
inlineToJira opts (Link _attr txt (src, _title)) = do
linkText <- inlineListToJira opts txt
return $ T.concat
[ "["
, if null txt then "" else linkText <> "|"
, src
, "]"
]
inlineToJira _opts (Image attr _alt (src, _title)) =
return . T.concat $ [anchor attr, "!", src, "!"]
inlineToJira opts (Note contents) = do
curNotes <- gets stNotes curNotes <- gets stNotes
let newnum = length curNotes + 1 let newnum = length curNotes + 1
contents' <- blockListToJira opts contents contents' <- blockListToJira contents
let thisnote = "[" <> pack (show newnum) <> "] " <> contents' <> "\n" let thisnote = "\\[" <> T.pack (show newnum) <> "] " <> contents' <> "\n"
modify $ \s -> s { stNotes = thisnote : curNotes } modify $ \s -> s { stNotes = thisnote : curNotes }
return $ "[" <> pack (show newnum) <> "]" return . singleton . Jira.Str $
"[" <> T.pack (show newnum) <> "]"
-- | Language codes recognized by jira -- | Language codes recognized by jira
knownLanguages :: [Text] knownLanguages :: [Text]

View file

@ -1,43 +1,38 @@
Simple table with caption: Simple table with caption:
||Right||Left||Center||Default|| | 12 | 12 | 12 | 12 |
|12|12|12|12| | 123 | 123 | 123 | 123 |
|123|123|123|123| | 1 | 1 | 1 | 1 |
|1|1|1|1|
Simple table without caption: Simple table without caption:
||Right||Left||Center||Default|| | 12 | 12 | 12 | 12 |
|12|12|12|12| | 123 | 123 | 123 | 123 |
|123|123|123|123| | 1 | 1 | 1 | 1 |
|1|1|1|1|
Simple table indented two spaces: Simple table indented two spaces:
||Right||Left||Center||Default|| | 12 | 12 | 12 | 12 |
|12|12|12|12| | 123 | 123 | 123 | 123 |
|123|123|123|123| | 1 | 1 | 1 | 1 |
|1|1|1|1|
Multiline table with caption: Multiline table with caption:
||Centered Header||Left Aligned||Right Aligned||Default aligned|| | First | row | 12.0 | Example of a row that spans multiple lines. |
|First|row|12.0|Example of a row that spans multiple lines.| | Second | row | 5.0 | Heres another one. Note the blank line between rows. |
|Second|row|5.0|Heres another one. Note the blank line between rows.|
Multiline table without caption: Multiline table without caption:
||Centered Header||Left Aligned||Right Aligned||Default aligned|| | First | row | 12.0 | Example of a row that spans multiple lines. |
|First|row|12.0|Example of a row that spans multiple lines.| | Second | row | 5.0 | Heres another one. Note the blank line between rows. |
|Second|row|5.0|Heres another one. Note the blank line between rows.|
Table without column headers: Table without column headers:
|12|12|12|12| | 12 | 12 | 12 | 12 |
|123|123|123|123| | 123 | 123 | 123 | 123 |
|1|1|1|1| | 1 | 1 | 1 | 1 |
Multiline table without column headers: Multiline table without column headers:
|First|row|12.0|Example of a row that spans multiple lines.| | First | row | 12.0 | Example of a row that spans multiple lines. |
|Second|row|5.0|Heres another one. Note the blank line between rows.| | Second | row | 5.0 | Heres another one. Note the blank line between rows. |

View file

@ -1,59 +1,43 @@
This is a set of tests for pandoc. Most of them are adapted from John Grubers markdown test suite. This is a set of tests for pandoc. Most of them are adapted from John Grubers markdown test suite.
---- ----
h1. {anchor:headers}Headers h1. {anchor:headers}Headers
h2. {anchor:level-2-with-an-embedded-link}Level 2 with an [embedded link|/url] h2. {anchor:level-2-with-an-embedded-link}Level 2 with an [embedded link|/url]
h3. {anchor:level-3-with-emphasis}Level 3 with _emphasis_ h3. {anchor:level-3-with-emphasis}Level 3 with _emphasis_
h4. {anchor:level-4}Level 4 h4. {anchor:level-4}Level 4
h5. {anchor:level-5}Level 5 h5. {anchor:level-5}Level 5
h1. {anchor:level-1}Level 1 h1. {anchor:level-1}Level 1
h2. {anchor:level-2-with-emphasis}Level 2 with _emphasis_ h2. {anchor:level-2-with-emphasis}Level 2 with _emphasis_
h3. {anchor:level-3}Level 3 h3. {anchor:level-3}Level 3
with no blank line with no blank line
h2. {anchor:level-2}Level 2 h2. {anchor:level-2}Level 2
with no blank line with no blank line
---- ----
h1. {anchor:paragraphs}Paragraphs h1. {anchor:paragraphs}Paragraphs
Heres a regular paragraph. Heres a regular paragraph.
In Markdown 1.0.0 and earlier. Version 8. This line turns into a list item. Because a hard\-wrapped line in the middle of a paragraph looked like a list item. In Markdown 1.0.0 and earlier. Version 8. This line turns into a list item. Because a hard-wrapped line in the middle of a paragraph looked like a list item.
Heres one with a bullet. \* criminey. Heres one with a bullet. * criminey.
There should be a hard line break There should be a hard line break
here. here.
---- ----
h1. {anchor:block-quotes}Block Quotes h1. {anchor:block-quotes}Block Quotes
E-mail style:
E\-mail style:
bq. This is a block quote. It is pretty short. bq. This is a block quote. It is pretty short.
{quote} {quote}
Code in a block quote: Code in a block quote:
{code} {code:java}
sub status { sub status {
print "working"; print "working";
} }
{code} {code}
A list: A list:
# item one # item one
@ -62,23 +46,17 @@ A list:
Nested block quotes: Nested block quotes:
bq. nested bq. nested
bq. nested bq. nested
{quote} {quote}
This should not be a block quote: 2 > 1. This should not be a block quote: 2 > 1.
And a following paragraph. And a following paragraph.
---- ----
h1. {anchor:code-blocks}Code Blocks h1. {anchor:code-blocks}Code Blocks
Code: Code:
{code} {code:java}
---- (should be four hyphens) ---- (should be four hyphens)
sub status { sub status {
@ -87,21 +65,16 @@ sub status {
this code block is indented by one tab this code block is indented by one tab
{code} {code}
And: And:
{code} {code:java}
this code block is indented by two tabs this code block is indented by two tabs
These should not be escaped: \$ \\ \> \[ \{ These should not be escaped: \$ \\ \> \[ \{
{code} {code}
---- ----
h1. {anchor:lists}Lists h1. {anchor:lists}Lists
h2. {anchor:unordered}Unordered h2. {anchor:unordered}Unordered
Asterisks tight: Asterisks tight:
* asterisk 1 * asterisk 1
@ -139,7 +112,6 @@ Minuses loose:
* Minus 3 * Minus 3
h2. {anchor:ordered}Ordered h2. {anchor:ordered}Ordered
Tight: Tight:
# First # First
@ -172,7 +144,6 @@ Item 1. graf two. The quick brown fox jumped over the lazy dogs back.
# Item 3. # Item 3.
h2. {anchor:nested}Nested h2. {anchor:nested}Nested
* Tab * Tab
** Tab ** Tab
*** Tab *** Tab
@ -196,14 +167,12 @@ Same thing but with paragraphs:
# Third # Third
h2. {anchor:tabs-and-spaces}Tabs and spaces h2. {anchor:tabs-and-spaces}Tabs and spaces
* this is a list item indented with tabs * this is a list item indented with tabs
* this is a list item indented with spaces * this is a list item indented with spaces
** this is an example list item indented with tabs ** this is an example list item indented with tabs
** this is an example list item indented with spaces ** this is an example list item indented with spaces
h2. {anchor:fancy-list-markers}Fancy list markers h2. {anchor:fancy-list-markers}Fancy list markers
# begins with 2 # begins with 2
# and now 3 # and now 3
with a continuation with a continuation
@ -232,9 +201,7 @@ M.A. 2007
B. Williams B. Williams
---- ----
h1. {anchor:definition-lists}Definition Lists h1. {anchor:definition-lists}Definition Lists
Tight using spaces: Tight using spaces:
* *apple* * *apple*
@ -269,7 +236,7 @@ red fruit
contains seeds, crisp, pleasant to taste contains seeds, crisp, pleasant to taste
* *_orange_* * *_orange_*
orange fruit orange fruit
{code} {code:java}
{ orange code block } { orange code block }
{code} {code}
bq. orange block quote bq. orange block quote
@ -303,86 +270,62 @@ orange fruit
*# sublist *# sublist
h1. {anchor:html-blocks}HTML Blocks h1. {anchor:html-blocks}HTML Blocks
Simple block on one line: Simple block on one line:
foo foo
And nested without indentation: And nested without indentation:
foo foo
bar bar
Interpreted markdown in a table: Interpreted markdown in a table:
This is _emphasized_ This is _emphasized_
And this is *strong* And this is *strong*
Heres a simple block: Heres a simple block:
foo foo
This should be a code block, though: This should be a code block, though:
{code} {code:java}
<div> <div>
foo foo
</div> </div>
{code} {code}
As should this: As should this:
{code} {code:java}
<div>foo</div> <div>foo</div>
{code} {code}
Now, nested: Now, nested:
foo foo
This should just be an HTML comment:
This should just be an HTML comment:
Multiline: Multiline:
Code block: Code block:
{code} {code:java}
<!-- Comment --> <!-- Comment -->
{code} {code}
Just plain comment, with trailing spaces on the line: Just plain comment, with trailing spaces on the line:
Code: Code:
{code} {code:java}
<hr /> <hr />
{code} {code}
Hrs: Hrs:
---- ----
h1. {anchor:inline-markup}Inline Markup h1. {anchor:inline-markup}Inline Markup
This is _emphasized_, and so _is this_. This is _emphasized_, and so _is this_.
This is *strong*, and so *is this*. This is *strong*, and so *is this*.
@ -397,20 +340,18 @@ So is *_this_* word.
So is *_this_* word. So is *_this_* word.
This is code: {{>}}, {{$}}, {{\}}, {{\$}}, {{<html>}}. This is code: {{>}}, {{$}}, {{&bsol;}}, {{&bsol;$}}, {{<html>}}.
-This is _strikeout_.- -This is _strikeout_.-
Superscripts: a{^bc^}d a{^_hello_^} a{^hello there^}. Superscripts: a{^}bc{^}d a{^}_hello_{^} a{^}hello there{^}.
Subscripts: H{~2~}O, H{~23~}O, H{~many of them~}O. Subscripts: H{~}2{~}O, H{~}23{~}O, H{~}many of them{~}O.
These should not be superscripts or subscripts, because of the unescaped spaces: a\^b c\^d, a\~b c\~d. These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.
---- ----
h1. {anchor:smart-quotes-ellipses-dashes}Smart quotes, ellipses, dashes h1. {anchor:smart-quotes-ellipses-dashes}Smart quotes, ellipses, dashes
"Hello," said the spider. "'Shelob' is my name." "Hello," said the spider. "'Shelob' is my name."
'A', 'B', and 'C' are letters. 'A', 'B', and 'C' are letters.
@ -421,39 +362,36 @@ h1. {anchor:smart-quotes-ellipses-dashes}Smart quotes, ellipses, dashes
Here is some quoted '{{code}}' and a "[quoted link|http://example.com/?foo=1&bar=2]". Here is some quoted '{{code}}' and a "[quoted link|http://example.com/?foo=1&bar=2]".
Some dashes: one --- two --- three --- four --- five. Some dashes: one—two — three—four — five.
Dashes between numbers: 5 -- 7, 255 -- 66, 1987 -- 1999. Dashes between numbers: 57, 25566, 19871999.
Ellipses...and...and.... Ellipses…and…and….
---- ----
h1. {anchor:latex}LaTeX h1. {anchor:latex}LaTeX
* *
* 2\+2=4 * 2+2=4
* _x__y_ * _x_{_}y{_}
* _α__ω_ * _α_{_}ω{_}
* 223 * 223
* _p_\-Tree * _p_\-Tree
* Heres some display math: \\$$\frac\{d\}\{dx\}f(x)=\lim\_\{h\to 0\}\frac\{f(x\+h)\-f(x)\}\{h\}$$\\ * Heres some display math:
* Heres one that has a line break in it: _α_\+_ω_×_x_{^2^}. $$\frac{d\}\{dx}f\(x)=\lim\_\{h\to 0\}&bsol;frac{f(x+h)-f\(x)\}\{h}$$
* Heres one that has a line break in it: _α_+{_}ω{_}×{_}x{_}^2^.
These shouldnt be math: These shouldnt be math:
* To get the famous equation, write {{$e = mc^2$}}. * To get the famous equation, write {{$e = mc^2$}}.
* $22,000 is a _lot_ of money. So is $34,000. (It worked if "lot" is emphasized.) * $22,000 is a _lot_ of money. So is $34,000. \(It worked if "lot" is emphasized.)
* Shoes ($20) and socks ($5). * Shoes \($20) and socks \($5).
* Escaped {{$}}: $73 _this should be emphasized_ 23$. * Escaped {{$}}: $73 _this should be emphasized_ 23$.
Heres a LaTeX table: Heres a LaTeX table:
---- ----
h1. {anchor:special-characters}Special Characters h1. {anchor:special-characters}Special Characters
Here is some unicode: Here is some unicode:
* I hat: Î * I hat: Î
@ -472,7 +410,7 @@ This & that.
6 > 5. 6 > 5.
Backslash: \ Backslash: &bsol;
Backtick: ` Backtick: `
@ -488,11 +426,11 @@ Left bracket: \[
Right bracket: \] Right bracket: \]
Left paren: ( Left paren: \(
Right paren: ) Right paren: )
Greater\-than: > Greater-than: >
Hash: # Hash: #
@ -505,11 +443,8 @@ Plus: \+
Minus: \- Minus: \-
---- ----
h1. {anchor:links}Links h1. {anchor:links}Links
h2. {anchor:explicit}Explicit h2. {anchor:explicit}Explicit
Just a [URL|/url/]. Just a [URL|/url/].
[URL and title|/url/]. [URL and title|/url/].
@ -522,14 +457,13 @@ Just a [URL|/url/].
[URL and title|/url/] [URL and title|/url/]
[with\_underscore|/url/with_underscore] [with_underscore|/url/with_underscore]
[Email link|mailto:nobody@nowhere.net] [Email link|mailto:nobody@nowhere.net]
[Empty|]. [Empty|].
h2. {anchor:reference}Reference h2. {anchor:reference}Reference
Foo [bar|/url/]. Foo [bar|/url/].
With [embedded \[brackets\]|/url/]. With [embedded \[brackets\]|/url/].
@ -544,16 +478,14 @@ Indented [thrice|/url].
This should \[not\]\[\] be a link. This should \[not\]\[\] be a link.
{code} {code:java}
[not]: /url [not]: /url
{code} {code}
Foo [bar|/url/]. Foo [bar|/url/].
Foo [biz|/url/]. Foo [biz|/url/].
h2. {anchor:with-ampersands}With ampersands h2. {anchor:with-ampersands}With ampersands
Heres a [link with an ampersand in the URL|http://example.com/?foo=1&bar=2]. Heres a [link with an ampersand in the URL|http://example.com/?foo=1&bar=2].
Heres a link with an amersand in the link text: [AT&T|http://att.com/]. Heres a link with an amersand in the link text: [AT&T|http://att.com/].
@ -563,64 +495,55 @@ Heres an [inline link|/script?foo=1&bar=2].
Heres an [inline link in pointy braces|/script?foo=1&bar=2]. Heres an [inline link in pointy braces|/script?foo=1&bar=2].
h2. {anchor:autolinks}Autolinks h2. {anchor:autolinks}Autolinks
With an ampersand: [http://example.com/?foo=1&bar=2|http://example.com/?foo=1&bar=2] With an ampersand: [http://example.com/?foo=1&bar=2|http://example.com/?foo=1&bar=2]
* In a list? * In a list?
* [http://example.com/|http://example.com/] * [http://example.com/|http://example.com/]
* It should. * It should.
An e\-mail address: [nobody@nowhere.net|mailto:nobody@nowhere.net] An e-mail address: [nobody@nowhere.net|mailto:nobody@nowhere.net]
bq. Blockquoted: [http://example.com/|http://example.com/] bq. Blockquoted: [http://example.com/|http://example.com/]
Auto-links should not occur here: {{<http://example.com/>}}
Auto\-links should not occur here: {{<http://example.com/>}} {code:java}
{code}
or here: <http://example.com/> or here: <http://example.com/>
{code} {code}
---- ----
h1. {anchor:images}Images h1. {anchor:images}Images
From "Voyage dans la Lune" by Georges Melies \(1902):
From "Voyage dans la Lune" by Georges Melies (1902):
!lalune.jpg! !lalune.jpg!
Here is a movie !movie.jpg! icon. Here is a movie !movie.jpg! icon.
---- ----
h1. {anchor:footnotes}Footnotes h1. {anchor:footnotes}Footnotes
Here is a footnote reference,[1] and another.[2] This should _not_ be a footnote reference, because it contains a space.\[\^my note\] Here is an inline note.[3] Here is a footnote reference,[1] and another.[2] This should _not_ be a footnote reference, because it contains a space.\[\^my note\] Here is an inline note.[3]
bq. Notes can go in quotes.[4] bq. Notes can go in quotes.[4]
# And in list items.[5] # And in list items.[5]
This paragraph should not be part of the note, as it is not indented. This paragraph should not be part of the note, as it is not indented.
[1] Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. \[1] Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.
[2] Heres the long note. This one contains multiple blocks. \[2] Heres the long note. This one contains multiple blocks.
Subsequent blocks are indented to show that they belong to the footnote (as with list items). Subsequent blocks are indented to show that they belong to the footnote \(as with list items).
{code} {code:java}
{ <code> } { <code> }
{code} {code}
If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.
[3] This is _easier_ to type. Inline notes may contain [links|http://google.com] and {{]}} verbatim characters, as well as \[bracketed text\]. \[3] This is _easier_ to type. Inline notes may contain [links|http://google.com] and {{\]}} verbatim characters, as well as \[bracketed text].
[4] In quote. \[4] In quote.
[5] In list. \[5] In list.