Implement markdown interpolation

This commit is contained in:
Evan Czaplicki 2013-10-25 17:36:30 +02:00
parent cb0183ba46
commit 56ecbbc70a
12 changed files with 123 additions and 33 deletions

View file

@ -186,9 +186,12 @@ expression (L span expr) =
ctor = (prop "ctor", string (makeSafe name))
fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
Markdown doc _ -> return $ obj "Text.text" <| string (pad ++ md ++ pad)
where pad = "<div style=\"height:0;width:0;\">&nbsp;</div>"
md = Pan.writeHtmlString Pan.def doc
Markdown uid doc es ->
do es' <- mapM expression es
return $ obj "Text.markdown" `call` (string md : string uid : es')
where
pad = "<div style=\"height:0;width:0;\">&nbsp;</div>"
md = pad ++ Pan.writeHtmlString Pan.def doc ++ pad
definition :: Def () () -> State Int [Statement ()]
definition def =

View file

@ -49,7 +49,7 @@ instance Extract (Expr t v) where
Case e cases -> concatMap (f . snd) cases
Data _ es -> concatMap f es
MultiIf es -> concatMap (f . snd) es
Markdown doc _ -> [ Pan.writeHtmlString Pan.def doc ]
Markdown _ doc _ -> [ Pan.writeHtmlString Pan.def doc ]
_ -> []
extractLink src txt =

View file

@ -55,20 +55,22 @@ listTerm = markdown' <|> braces (try range <|> ExplicitList <$> commaSep expr)
Range lo <$> expr
markdown' = do
(rawText, exprs) <- markdown interpolation
pos <- getPosition
let uid = show (sourceLine pos) ++ ":" ++ show (sourceColumn pos)
(rawText, exprs) <- markdown (interpolation uid)
let md = Pan.readMarkdown Pan.def (filter (/='\r') rawText)
return (Markdown md exprs)
return (Markdown uid md exprs)
span xs = "<span id=\"md" ++ show (length xs) ++
"\" style=\"font-family:monospace;\">{{ ... }}</span>"
span uid index =
"<span id=\"md-" ++ uid ++ "-" ++ show index ++ "\"></span>"
interpolation md exprs = do
interpolation uid md exprs = do
try (string "{{")
whitespace
e <- expr
whitespace
string "}}"
return (md ++ span exprs, exprs ++ [e])
return (md ++ span uid (length exprs), exprs ++ [e])
parensTerm :: IParser (LExpr t v)
parensTerm = try (parens opFn) <|> parens (tupleFn <|> parened)

View file

@ -30,7 +30,7 @@ data Expr t v
| Insert (LExpr t v) String (LExpr t v)
| Modify (LExpr t v) [(String, LExpr t v)]
| Record [(String, LExpr t v)]
| Markdown Pandoc.Pandoc [LExpr t v]
| Markdown String Pandoc.Pandoc [LExpr t v]
deriving (Eq, Show, Data, Typeable)
data Def tipe var
@ -96,7 +96,7 @@ instance Pretty (Expr t v) where
where
field (x,e) = variable x <+> P.text "=" <+> pretty e
Markdown _ _ -> P.text "[markdown| ... |]"
Markdown _ _ _ -> P.text "[markdown| ... |]"
instance Pretty (Def t v) where
pretty def =

View file

@ -173,7 +173,7 @@ rename env lexpr@(L s expr) =
branch (pattern,e) = (,) `liftM` format (renamePattern env pattern)
`ap` rename (extend env pattern) e
Markdown md es -> Markdown md `liftM` mapM rnm es
Markdown uid md es -> Markdown uid md `liftM` mapM rnm es
renamePattern :: Env -> Pattern -> Either String Pattern

View file

@ -97,7 +97,7 @@ reorder lexpr@(L s expr) =
Record fields ->
Record `liftM` mapM reorderField fields
Markdown md es -> Markdown md <$> mapM reorder es
Markdown uid md es -> Markdown uid md <$> mapM reorder es
-- Actually do some reordering
Let defs body ->

View file

@ -40,4 +40,4 @@ subst old new expr =
Modify r fs -> Modify (f r) (map (second f) fs)
Record fs -> Record (map (second f) fs)
Literal _ -> expr
Markdown md es -> Markdown md (map f es)
Markdown uid md es -> Markdown uid md (map f es)

View file

@ -134,7 +134,7 @@ constrain env (L span expr) tipe =
recordType = record fields' (TermN EmptyRecord1)
return . ex vars . and $ tipe === recordType : cs
Markdown _ es ->
Markdown _ _ es ->
do vars <- forM es $ \_ -> liftIO (var Flexible)
let tvars = map VarN vars
cs <- zipWithM (constrain env) es tvars

View file

@ -76,7 +76,7 @@ width nw e =
let p = e.props
props = case e.element of
Image _ w h _ -> {p| height <- round (toFloat h / toFloat w * toFloat nw) }
RawHtml html -> {p| height <- snd (Native.Utils.htmlHeight nw html)}
RawHtml -> {p| height <- snd (Native.Utils.htmlHeight nw e.element) }
_ -> p
in { element=e.element, props={ props | width <- nw } }
@ -128,7 +128,7 @@ data ElementPrim
| Container Position Element
| Flow Direction [Element]
| Spacer
| RawHtml JSString
| RawHtml
| Custom -- for custom Elements implemented in JS, see collage for example
data ImageStyle = Plain | Fitted | Cropped (Int,Int) | Tiled

View file

@ -91,17 +91,38 @@ Elm.Native.Text.make = function(elm) {
return Utils.txt("<a href='" + toText(href) + "'>" + text + "</a>");
}
function position(pos) {
function position(align) {
return function(text) {
var e = {ctor:'RawHtml',
_0: '<div style="padding:0;margin:0;text-align:' +
pos + '">' + text + '</div>'
};
var p = A2(Utils.htmlHeight, 0, text);
return A3(Element.newElement, p._0, p._1, e);
var raw = {
ctor:'RawHtml',
html: '<span style="text-align:' + align + ';">' + text + '</span>',
guid: '0',
args: [],
};
var pos = A2(Utils.htmlHeight, 0, raw);
return A3(Element.newElement, pos._0, pos._1, raw);
}
}
function markdown(text, guid) {
var raw = {
ctor:'RawHtml',
html: text,
guid: guid,
args: Array.prototype.slice.call(arguments, 2).map(function(arg) {
if (arg.props && arg.element) {
arg.isElement = true;
return arg;
} else if (!arg.isText) {
return Utils.txt('<code>' + show(arg) + '</code>');
}
return arg;
}),
};
var pos = A2(Utils.htmlHeight, 0, raw);
return A3(Element.newElement, pos._0, pos._1, raw);
}
function asText(v) {
return position('left')(monospace(toText(show(v))));
}
@ -130,7 +151,8 @@ Elm.Native.Text.make = function(elm) {
righted : position('right'),
text : position('left'),
plainText : plainText,
markdown : markdown,
asText : asText
asText : asText,
};
};

View file

@ -122,7 +122,9 @@ Elm.Native.Utils.make = function(elm) {
return m === b ? 0 : m;
}
function htmlHeight(width, html) {
function htmlHeight(width, rawHtml) {
// create dummy node
var html = rawHtml.html;
var t = document.createElement('div');
t.innerHTML = html;
if (width > 0) { t.style.width = width + "px"; }
@ -131,6 +133,22 @@ Elm.Native.Utils.make = function(elm) {
t.style.cssFloat = "left";
document.body.appendChild(t);
// insert interpolated values
var args = rawHtml.args;
var guid = rawHtml.guid;
for (var i = args.length; i--; ) {
var arg = args[i];
var span = document.getElementById('md-' + guid + '-' + i);
if (arg.isElement) {
span.style.width = arg.props.width + 'px';
span.style.height = arg.props.height + 'px';
} else {
span.innerHTML = arg;
}
}
// get dimensions
var style = window.getComputedStyle(t, null);
var w = Math.ceil(style.getPropertyValue("width").slice(0,-2) - 0);
var h = Math.ceil(style.getPropertyValue("height").slice(0,-2) - 0);

View file

@ -154,10 +154,31 @@ function container(pos,elem) {
return div;
}
function rawHtml(html) {
var e = newElement('div');
e.innerHTML = html;
return e;
function rawHtml(elem) {
var html = elem.html;
var args = elem.args;
var guid = elem.guid;
var div = newElement('div');
div.innerHTML = html;
div.style.visibility = "hidden";
document.body.appendChild(div);
for (var i = args.length; i--; ) {
var arg = args[i];
var span = document.getElementById('md-' + guid + '-' + i);
if (arg.isText) {
span.innerHTML = arg;
} else {
span.style.display = 'block';
span.style.width = arg.props.width + 'px';
span.style.height = arg.props.height + 'px';
span.appendChild(render(arg));
}
}
document.body.removeChild(div);
div.style.visibility = 'visible';
return div;
}
function render(elem) { return setProps(elem.props, makeElement(elem)); }
@ -168,7 +189,7 @@ function makeElement(e) {
case 'Flow': return flow(elem._0, elem._1);
case 'Container': return container(elem._0, elem._1);
case 'Spacer': return newElement('div');
case 'RawHtml': return rawHtml(elem._0);
case 'RawHtml': return rawHtml(elem);
case 'Custom': return elem.render(elem.model);
}
}
@ -184,7 +205,31 @@ function update(node, curr, next) {
switch(nextE.ctor) {
case "Spacer": break;
case "RawHtml":
if (nextE._0 !== currE._0) node.innerHTML = nextE._0;
if (nextE.guid !== currE.guid) {
node.parentNode.replaceChild(render(next),node);
return true;
}
var nargs = nextE.args;
var cargs = currE.args;
for (var i = nargs.length; i--; ) {
var narg = nargs[i];
var carg = cargs[i]
if (narg == carg) continue;
var span = document.getElementById('md-' + currE.guid + '-' + i);
if (narg.isElement) {
if (carg.isElement) {
update(span, carg, narg);
} else {
span.style.display = 'block';
var e = render(narg);
span.innerHTML = '';
span.appendChild(e);
}
} else {
span.style.display = 'inline';
span.innerHTML = narg;
}
}
break;
case "Image":
if (nextE._0.ctor === 'Plain') {