Implement markdown interpolation
This commit is contained in:
parent
cb0183ba46
commit
56ecbbc70a
12 changed files with 123 additions and 33 deletions
|
@ -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;\"> </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;\"> </div>"
|
||||
md = pad ++ Pan.writeHtmlString Pan.def doc ++ pad
|
||||
|
||||
definition :: Def () () -> State Int [Statement ()]
|
||||
definition def =
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ->
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 raw = {
|
||||
ctor:'RawHtml',
|
||||
html: '<span style="text-align:' + align + ';">' + text + '</span>',
|
||||
guid: '0',
|
||||
args: [],
|
||||
};
|
||||
var p = A2(Utils.htmlHeight, 0, text);
|
||||
return A3(Element.newElement, p._0, p._1, e);
|
||||
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,
|
||||
};
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -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') {
|
||||
|
|
Loading…
Reference in a new issue