on master help sign in
Implement markdown interpolation
56ecbbcEvan Czaplicki
compiler/Generate/JavaScript.hs+6−3
@@ -186,9 +186,12 @@ expression (L span expr) =
186 ctor = (prop "ctor", string (makeSafe name))
186 ctor = (prop "ctor", string (makeSafe name))
187 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
187 fields = zipWith (\n e -> (prop ("_" ++ show n), e)) [0..]
188
188
189− Markdown doc _ -> return $ obj "Text.text" <| string (pad ++ md ++ pad)
189+ Markdown uid doc es ->
190+ do es' <- mapM expression es
191+ return $ obj "Text.markdown" `call` (string md : string uid : es')
192+ where
190− where pad = "<div style=\"height:0;width:0;\">&nbsp;</div>"
193+ pad = "<div style=\"height:0;width:0;\">&nbsp;</div>"
191− md = Pan.writeHtmlString Pan.def doc
194+ md = pad ++ Pan.writeHtmlString Pan.def doc ++ pad
192
195
193definition :: Def () () -> State Int [Statement ()]
196definition :: Def () () -> State Int [Statement ()]
194definition def =
197definition def =
compiler/Generate/Noscript.hs+1−1
@@ -49,7 +49,7 @@ instance Extract (Expr t v) where
49 Case e cases -> concatMap (f . snd) cases
49 Case e cases -> concatMap (f . snd) cases
50 Data _ es -> concatMap f es
50 Data _ es -> concatMap f es
51 MultiIf es -> concatMap (f . snd) es
51 MultiIf es -> concatMap (f . snd) es
52− Markdown doc _ -> [ Pan.writeHtmlString Pan.def doc ]
52+ Markdown _ doc _ -> [ Pan.writeHtmlString Pan.def doc ]
53 _ -> []
53 _ -> []
54
54
55extractLink src txt =
55extractLink src txt =
compiler/Parse/Expression.hs+8−6
@@ -55,20 +55,22 @@ listTerm = markdown' <|> braces (try range <|> ExplicitList <$> commaSep expr)
55 Range lo <$> expr
55 Range lo <$> expr
56
56
57 markdown' = do
57 markdown' = do
58+ pos <- getPosition
59+ let uid = show (sourceLine pos) ++ ":" ++ show (sourceColumn pos)
58− (rawText, exprs) <- markdown interpolation
60+ (rawText, exprs) <- markdown (interpolation uid)
59 let md = Pan.readMarkdown Pan.def (filter (/='\r') rawText)
61 let md = Pan.readMarkdown Pan.def (filter (/='\r') rawText)
60− return (Markdown md exprs)
62+ return (Markdown uid md exprs)
61
63
64+ span uid index =
62− span xs = "<span id=\"md" ++ show (length xs) ++
65+ "<span id=\"md-" ++ uid ++ "-" ++ show index ++ "\"></span>"
63− "\" style=\"font-family:monospace;\">{{ ... }}</span>"
64
66
65− interpolation md exprs = do
67+ interpolation uid md exprs = do
66 try (string "{{")
68 try (string "{{")
67 whitespace
69 whitespace
68 e <- expr
70 e <- expr
69 whitespace
71 whitespace
70 string "}}"
72 string "}}"
71− return (md ++ span exprs, exprs ++ [e])
73+ return (md ++ span uid (length exprs), exprs ++ [e])
72
74
73parensTerm :: IParser (LExpr t v)
75parensTerm :: IParser (LExpr t v)
74parensTerm = try (parens opFn) <|> parens (tupleFn <|> parened)
76parensTerm = try (parens opFn) <|> parens (tupleFn <|> parened)
compiler/SourceSyntax/Expression.hs+2−2
@@ -30,7 +30,7 @@ data Expr t v
30 | Insert (LExpr t v) String (LExpr t v)
30 | Insert (LExpr t v) String (LExpr t v)
31 | Modify (LExpr t v) [(String, LExpr t v)]
31 | Modify (LExpr t v) [(String, LExpr t v)]
32 | Record [(String, LExpr t v)]
32 | Record [(String, LExpr t v)]
33− | Markdown Pandoc.Pandoc [LExpr t v]
33+ | Markdown String Pandoc.Pandoc [LExpr t v]
34 deriving (Eq, Show, Data, Typeable)
34 deriving (Eq, Show, Data, Typeable)
35
35
36data Def tipe var
36data Def tipe var
@@ -96,7 +96,7 @@ instance Pretty (Expr t v) where
96 where
96 where
97 field (x,e) = variable x <+> P.text "=" <+> pretty e
97 field (x,e) = variable x <+> P.text "=" <+> pretty e
98
98
99− Markdown _ _ -> P.text "[markdown| ... |]"
99+ Markdown _ _ _ -> P.text "[markdown| ... |]"
100
100
101instance Pretty (Def t v) where
101instance Pretty (Def t v) where
102 pretty def =
102 pretty def =
compiler/Transform/Canonicalize.hs+1−1
@@ -173,7 +173,7 @@ rename env lexpr@(L s expr) =
173 branch (pattern,e) = (,) `liftM` format (renamePattern env pattern)
173 branch (pattern,e) = (,) `liftM` format (renamePattern env pattern)
174 `ap` rename (extend env pattern) e
174 `ap` rename (extend env pattern) e
175
175
176− Markdown md es -> Markdown md `liftM` mapM rnm es
176+ Markdown uid md es -> Markdown uid md `liftM` mapM rnm es
177
177
178
178
179renamePattern :: Env -> Pattern -> Either String Pattern
179renamePattern :: Env -> Pattern -> Either String Pattern
compiler/Transform/SortDefinitions.hs+1−1
@@ -97,7 +97,7 @@ reorder lexpr@(L s expr) =
97 Record fields ->
97 Record fields ->
98 Record `liftM` mapM reorderField fields
98 Record `liftM` mapM reorderField fields
99
99
100− Markdown md es -> Markdown md <$> mapM reorder es
100+ Markdown uid md es -> Markdown uid md <$> mapM reorder es
101
101
102 -- Actually do some reordering
102 -- Actually do some reordering
103 Let defs body ->
103 Let defs body ->
compiler/Transform/Substitute.hs+1−1
@@ -40,4 +40,4 @@ subst old new expr =
40 Modify r fs -> Modify (f r) (map (second f) fs)
40 Modify r fs -> Modify (f r) (map (second f) fs)
41 Record fs -> Record (map (second f) fs)
41 Record fs -> Record (map (second f) fs)
42 Literal _ -> expr
42 Literal _ -> expr
43− Markdown md es -> Markdown md (map f es)
43+ Markdown uid md es -> Markdown uid md (map f es)
compiler/Type/Constrain/Expression.hs+1−1
@@ -134,7 +134,7 @@ constrain env (L span expr) tipe =
134 recordType = record fields' (TermN EmptyRecord1)
134 recordType = record fields' (TermN EmptyRecord1)
135 return . ex vars . and $ tipe === recordType : cs
135 return . ex vars . and $ tipe === recordType : cs
136
136
137− Markdown _ es ->
137+ Markdown _ _ es ->
138 do vars <- forM es $ \_ -> liftIO (var Flexible)
138 do vars <- forM es $ \_ -> liftIO (var Flexible)
139 let tvars = map VarN vars
139 let tvars = map VarN vars
140 cs <- zipWithM (constrain env) es tvars
140 cs <- zipWithM (constrain env) es tvars
libraries/Graphics/Element.elm+2−2
@@ -76,7 +76,7 @@ width nw e =
76 let p = e.props
76 let p = e.props
77 props = case e.element of
77 props = case e.element of
78 Image _ w h _ -> {p| height <- round (toFloat h / toFloat w * toFloat nw) }
78 Image _ w h _ -> {p| height <- round (toFloat h / toFloat w * toFloat nw) }
79− RawHtml html -> {p| height <- snd (Native.Utils.htmlHeight nw html)}
79+ RawHtml -> {p| height <- snd (Native.Utils.htmlHeight nw e.element) }
80 _ -> p
80 _ -> p
81 in { element=e.element, props={ props | width <- nw } }
81 in { element=e.element, props={ props | width <- nw } }
82
82
@@ -128,7 +128,7 @@ data ElementPrim
128 | Container Position Element
128 | Container Position Element
129 | Flow Direction [Element]
129 | Flow Direction [Element]
130 | Spacer
130 | Spacer
131− | RawHtml JSString
131+ | RawHtml
132 | Custom -- for custom Elements implemented in JS, see collage for example
132 | Custom -- for custom Elements implemented in JS, see collage for example
133
133
134data ImageStyle = Plain | Fitted | Cropped (Int,Int) | Tiled
134data ImageStyle = Plain | Fitted | Cropped (Int,Int) | Tiled
libraries/Native/Text.js+30−8
@@ -91,17 +91,38 @@ Elm.Native.Text.make = function(elm) {
91 return Utils.txt("<a href='" + toText(href) + "'>" + text + "</a>");
91 return Utils.txt("<a href='" + toText(href) + "'>" + text + "</a>");
92 }
92 }
93
93
94− function position(pos) {
94+ function position(align) {
95 return function(text) {
95 return function(text) {
96+ var raw = {
96− var e = {ctor:'RawHtml',
97+ ctor:'RawHtml',
97− _0: '<div style="padding:0;margin:0;text-align:' +
98+ html: '<span style="text-align:' + align + ';">' + text + '</span>',
98− pos + '">' + text + '</div>'
99+ guid: '0',
100+ args: [],
99− };
101+ };
100− var p = A2(Utils.htmlHeight, 0, text);
102+ var pos = A2(Utils.htmlHeight, 0, raw);
101− return A3(Element.newElement, p._0, p._1, e);
103+ return A3(Element.newElement, pos._0, pos._1, raw);
102 }
104 }
103 }
105 }
104
106
107+ function markdown(text, guid) {
108+ var raw = {
109+ ctor:'RawHtml',
110+ html: text,
111+ guid: guid,
112+ args: Array.prototype.slice.call(arguments, 2).map(function(arg) {
113+ if (arg.props && arg.element) {
114+ arg.isElement = true;
115+ return arg;
116+ } else if (!arg.isText) {
117+ return Utils.txt('<code>' + show(arg) + '</code>');
118+ }
119+ return arg;
120+ }),
121+ };
122+ var pos = A2(Utils.htmlHeight, 0, raw);
123+ return A3(Element.newElement, pos._0, pos._1, raw);
124+ }
125+
105 function asText(v) {
126 function asText(v) {
106 return position('left')(monospace(toText(show(v))));
127 return position('left')(monospace(toText(show(v))));
107 }
128 }
@@ -130,7 +151,8 @@ Elm.Native.Text.make = function(elm) {
130 righted : position('right'),
151 righted : position('right'),
131 text : position('left'),
152 text : position('left'),
132 plainText : plainText,
153 plainText : plainText,
154+ markdown : markdown,
133
155
134− asText : asText
156+ asText : asText,
135 };
157 };
136};
158};
libraries/Native/Utils.js+19−1
@@ -122,7 +122,9 @@ Elm.Native.Utils.make = function(elm) {
122 return m === b ? 0 : m;
122 return m === b ? 0 : m;
123 }
123 }
124
124
125− function htmlHeight(width, html) {
125+ function htmlHeight(width, rawHtml) {
126+ // create dummy node
127+ var html = rawHtml.html;
126 var t = document.createElement('div');
128 var t = document.createElement('div');
127 t.innerHTML = html;
129 t.innerHTML = html;
128 if (width > 0) { t.style.width = width + "px"; }
130 if (width > 0) { t.style.width = width + "px"; }
@@ -131,6 +133,22 @@ Elm.Native.Utils.make = function(elm) {
131 t.style.cssFloat = "left";
133 t.style.cssFloat = "left";
132
134
133 document.body.appendChild(t);
135 document.body.appendChild(t);
136+
137+ // insert interpolated values
138+ var args = rawHtml.args;
139+ var guid = rawHtml.guid;
140+ for (var i = args.length; i--; ) {
141+ var arg = args[i];
142+ var span = document.getElementById('md-' + guid + '-' + i);
143+ if (arg.isElement) {
144+ span.style.width = arg.props.width + 'px';
145+ span.style.height = arg.props.height + 'px';
146+ } else {
147+ span.innerHTML = arg;
148+ }
149+ }
150+
151+ // get dimensions
134 var style = window.getComputedStyle(t, null);
152 var style = window.getComputedStyle(t, null);
135 var w = Math.ceil(style.getPropertyValue("width").slice(0,-2) - 0);
153 var w = Math.ceil(style.getPropertyValue("width").slice(0,-2) - 0);
136 var h = Math.ceil(style.getPropertyValue("height").slice(0,-2) - 0);
154 var h = Math.ceil(style.getPropertyValue("height").slice(0,-2) - 0);
runtime/Render/Element.js+51−6
@@ -154,10 +154,31 @@ function container(pos,elem) {
154 return div;
154 return div;
155}
155}
156
156
157−function rawHtml(html) {
157+function rawHtml(elem) {
158+ var html = elem.html;
159+ var args = elem.args;
160+ var guid = elem.guid;
161+
158− var e = newElement('div');
162+ var div = newElement('div');
159− e.innerHTML = html;
163+ div.innerHTML = html;
164+ div.style.visibility = "hidden";
165+ document.body.appendChild(div);
166+
167+ for (var i = args.length; i--; ) {
168+ var arg = args[i];
169+ var span = document.getElementById('md-' + guid + '-' + i);
170+ if (arg.isText) {
171+ span.innerHTML = arg;
172+ } else {
173+ span.style.display = 'block';
174+ span.style.width = arg.props.width + 'px';
175+ span.style.height = arg.props.height + 'px';
176+ span.appendChild(render(arg));
177+ }
178+ }
179+ document.body.removeChild(div);
180+ div.style.visibility = 'visible';
160− return e;
181+ return div;
161}
182}
162
183
163function render(elem) { return setProps(elem.props, makeElement(elem)); }
184function render(elem) { return setProps(elem.props, makeElement(elem)); }
@@ -168,7 +189,7 @@ function makeElement(e) {
168 case 'Flow': return flow(elem._0, elem._1);
189 case 'Flow': return flow(elem._0, elem._1);
169 case 'Container': return container(elem._0, elem._1);
190 case 'Container': return container(elem._0, elem._1);
170 case 'Spacer': return newElement('div');
191 case 'Spacer': return newElement('div');
171− case 'RawHtml': return rawHtml(elem._0);
192+ case 'RawHtml': return rawHtml(elem);
172 case 'Custom': return elem.render(elem.model);
193 case 'Custom': return elem.render(elem.model);
173 }
194 }
174}
195}
@@ -184,7 +205,31 @@ function update(node, curr, next) {
184 switch(nextE.ctor) {
205 switch(nextE.ctor) {
185 case "Spacer": break;
206 case "Spacer": break;
186 case "RawHtml":
207 case "RawHtml":
187− if (nextE._0 !== currE._0) node.innerHTML = nextE._0;
208+ if (nextE.guid !== currE.guid) {
209+ node.parentNode.replaceChild(render(next),node);
210+ return true;
211+ }
212+ var nargs = nextE.args;
213+ var cargs = currE.args;
214+ for (var i = nargs.length; i--; ) {
215+ var narg = nargs[i];
216+ var carg = cargs[i]
217+ if (narg == carg) continue;
218+ var span = document.getElementById('md-' + currE.guid + '-' + i);
219+ if (narg.isElement) {
220+ if (carg.isElement) {
221+ update(span, carg, narg);
222+ } else {
223+ span.style.display = 'block';
224+ var e = render(narg);
225+ span.innerHTML = '';
226+ span.appendChild(e);
227+ }
228+ } else {
229+ span.style.display = 'inline';
230+ span.innerHTML = narg;
231+ }
232+ }
188 break;
233 break;
189 case "Image":
234 case "Image":
190 if (nextE._0.ctor === 'Plain') {
235 if (nextE._0.ctor === 'Plain') {