forked from livecode/livecode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmarkdown_compiler.livecodescript
More file actions
537 lines (492 loc) · 21.9 KB
/
markdown_compiler.livecodescript
File metadata and controls
537 lines (492 loc) · 21.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
script "MarkdownCompiler"
/*
Compiles LiveCode flavoured markdown files to HTML
MD:
##... <headings>
* <unordered list element>
1. <ordered list element>
*<italic text>*
**<bold text>**
tab tab ... <indented text>
Images

Videos
!! [<alt id>](<video file>)
Links
[<link text>](<link url>)
!- <Note:> -!
` <code> `
|table header 1|table header 2|
--------------------------------
|table contents|table contents|
|table contents|table contents|
*/
global gImgRegex, gLinkRegex, gVidRegex
global gDocumentPrefix, gDocumentSuffix
global gHeading, gItalic, gList, gIndent, gImg
global gAnchorCount, gExtension
on libraryStack
markdownInit
end libraryStack
command markdownInit
put 1 into gAnchorCount
put "md" into gExtension
put "<html>" into gDocumentPrefix
put "</html>" into gDocumentSuffix
put "#" into gHeading
put "*" into gItalic -- bold is "**"
put "*" into gList
put " " into gIndent
put "!" into gImg
put "!\[(.*)\]\((.*)\)" into gImgRegex
put "!!\[.*\]\((.*)\)" into gVidRegex
put "\[([^\]]*)\]\(([^\)]*)\)" into gLinkRegex
end markdownInit
function getIndentMargin pIndents
return (quote & "margin-left:" & 2*pIndents & "em;" & quote)
end getIndentMargin
function processFileToHTML pFile, @xContents
local tFileName
local tFileUrl
local tFileText
local tBody
put pFile & "." & gExtension into tFileName
put "file:" & notesPath() & slash & tFileName into tFileUrl
put url tFileUrl into tFileText
if tFileText is not empty then
markdownToHTML tFileText, 1, 0, xContents, tBody, false, true
end if
return tBody
end processFileToHTML
function parseEnclosingMarkup pText
local tRegex
local tFound
put "(\/\*|\*\*|\*|`|!-)" into tRegex
local tStart, tEnd
if matchChunk(pText, tRegex, tStart, tEnd) is false then
return pText
else
local tParsed
local tInsides
if char tStart-1 of pText is "\" then
put char 1 to tStart-2 of pText & char tStart to tEnd of pText into tParsed
put char tEnd + 1 to -1 of pText into tInsides
put tParsed & parseEnclosingMarkup(tInsides) into tParsed
return tParsed
end if
local tWhich
put char 1 to tStart - 1 of pText into tParsed
put char tStart to tEnd of pText into tWhich
put char tEnd + 1 to -1 of pText into pText
if tWhich is "/*" then
put "[^\\](\*\/)" into tRegex
get matchChunk(pText, tRegex, tStart, tEnd)
else if tWhich is "*" then
put "[^\\](\*)" into tRegex
put matchChunk(pText, tRegex, tStart, tEnd) into tFound
repeat while tFound is true and char tStart + 1 of pText is "*"
put tInsides & char 1 to tStart + 1 of pText into tInsides
put char tStart + 2 to -1 of pText into pText
put matchChunk(pText, tRegex, tStart, tEnd) into tFound
end repeat
if tFound is false then
put tInsides into pText
end if
else if tWhich is "**" then
put "[^\\](\*\*)" into tRegex
put matchChunk(pText, tRegex, tStart, tEnd) into tFound
else if tWhich is "!-" then
put "[^\\](-!)" into tRegex
put matchChunk(pText, tRegex, tStart, tEnd) into tFound
else
put "[^\\](" & tWhich & ")" into tRegex
put matchChunk(pText, tRegex, tStart, tEnd) into tFound
end if
if tFound is false then
return tParsed & parseEnclosingMarkup(pText)
end if
put tInsides & char 1 to tStart - 1 of pText into tInsides
put char tEnd + 1 to -1 of pText into pText
if tWhich is "**" then
put tParsed & "<strong>" & parseEnclosingMarkup(tInsides) & "</strong>" into tParsed
else if tWhich is "*" then
put tParsed & "<em>" & parseEnclosingMarkup(tInsides) & "</em>" into tParsed
else if tWhich is "`" then
put tParsed & "<code>" & parseEnclosingMarkup(tInsides) & "</code>" into tParsed
else if tWhich is "!-" then
put tParsed & "<p class=" & quote & "warning" & quote & ">" & parseEnclosingMarkup(tInsides) & "</p>" into tParsed
else if tWhich is "/*" then
put tParsed & "<p class=" & quote & "note" & quote & ">" & parseEnclosingMarkup(tInsides) & "</p>" into tParsed
end if
put tParsed & parseEnclosingMarkup(pText) into tParsed
return tParsed
end if
end parseEnclosingMarkup
function countOccurrences pOccurrer, pText
local tOffset
put offset(pOccurrer, pText) into tOffset
if tOffset is 0 then
return 0
else
local tEscape
put char tOffset -1 of pText into tEscape
put char tOffset + 1 to -1 of pText into pText
if tEscape is "\" then
return countOccurrences(pOccurrer, pText)
else
return 1 + countOccurrences(pOccurrer, pText)
end if
end if
end countOccurrences
function countConsecutive pOccurrer, pText
local tCount, tLength
put the number of chars in pOccurrer into tLength
put 0 into tCount
repeat while pText begins with pOccurrer
add 1 to tCount
delete char 1 to tLength of pText
end repeat
return tCount
end countConsecutive
function getAnchor pTitle
local tLink
put replaceText(pTitle, quote, "_") into tLink
put word 1 of tLink & gAnchorCount into tLink
return "id=" & quote & tLink & quote
end getAnchor
command appendLink pIndent, pTitle, @rContents
local tLink
put replaceText(pTitle, quote, "_") into tLink
put word 1 of tLink & gAnchorCount into tLink
put rContents & "<a " into rContents
put rContents & "style = " & quote & "margin-left:" & pIndent-1 & "em;" & quote & " " into rContents
put rContents & "href=" & quote & "#" & tLink & quote & ">" & pTitle & "</a><br>" & CR into rContents
add 1 to gAnchorCount
end appendLink
command addLink @pText, pLinkText, pUrl
local tLinkStart
local tLinkEnd
get matchChunk(pText, "(" & gLinkRegex & ")", tLinkStart, tLinkEnd)
local tBegin
local tEnd
put char 0 to tLinkStart-1 of pText into tBegin
put char tLinkEnd + 1 to -1 of pText into tEnd
if pLinkText is empty then
put pUrl into pLinkText
end if
put tBegin & "<a href=" & quote & pUrl & quote & ">" & pLinkText &"</a>" & tEnd into pText
end addLink
command addVideo @pText, pUrl
local tVidStart
local tVidEnd
get matchChunk(pText, "(" & gVidRegex & ")", tVidStart, tVidEnd)
local tBegin
local tEnd
put char 0 to tVidStart-1 of pText into tBegin
put char tVidEnd + 1 to -1 of pText into tEnd
local tVidOpts
put " height=" & quote & "100%" & quote & " width=" & quote & "100%" & quote & " frameborder=" & quote & "0" & quote into tVidOpts
put tBegin & "<iframe src=" & quote & pUrl & quote & tVidOpts & "></iframe>" & tEnd into pText
end addVideo
function createTableLine pLine, pIsHeader
local tNewLine
local tOffset
put "<tr>" & CR into tNewLine
set the itemdelimiter to "|"
repeat with i = 1 to the number of items of pLine
if pIsHeader then
put tNewLine & "<th>" & item i of pLine & "</th>" & CR into tNewLine
else
put tNewLine & "<td>" & item i of pLine & "</td>" & CR into tNewLine
end if
end repeat
put tNewLine & "</tr>" into tNewLine
return tNewLine
end createTableLine
function markdownFileToHTML pFileName
local tText
put url ("file:" & pFileName) into tText
markdownInit
local tContents
local tBody
markdownToHTML "", tText, 0, 0, tContents, tBody, false, true
return (tContents & CR & tBody)
end markdownFileToHTML
command addImage @pText, pAlt, pSrc
local tImgStart
local tImgEnd
get matchChunk(pText, "(" & gImgRegex & ")", tImgStart, tImgEnd)
local tBegin
local tEnd
put char 0 to tImgStart - 1 of pText into tBegin
put char tImgEnd + 1 to -1 of pText into tEnd
put tBegin & "<img alt=" & quote & pAlt & quote & " src=" & quote & pSrc & quote & "></img>" & tEnd into pText
end addImage
function markdownToHTML @pText, pHLevel, pCLevel, @xContents, pTopLevel
replace "<" with "<" in pText
replace ">" with ">" in pText
local tParsedBody
local tParsedContents
local tNumLines
put the number of lines of pText into tNumLines
local tCurLine
local tCurHeading
local tHValue
local tListDepth
local tListPrefixStart
local tListPrefixEnd
local tQuoteDepth
local tIsOrdered
local tNumIndents
local tClosers
local tImageUrl
local tVidUrl
local tAltText
local tLinkRef
local tLinkText
local tStart
local tEnd
local tSectionLine
local tLine
repeat with x = 1 to tNumLines
if tClosers is empty then
put 0 into tListDepth
put 0 into tQuoteDepth
end if
put line x of pText into tCurLine
# tCurLine - line currently being parsed
# tParsedBody - text already parsed
# tClosers - corresponding closing tags of any open ones
# whenever the line starts and ends with | then we are definitely in a table
if tCurLine begins with "|" and tCurLine ends with "|" then
if tClosers is "</table>" then # if we are already in a table then continue it
put createTableLine(tCurLine, false) into tCurLine
else # otherwise start a new table
if tClosers is not empty then
put tParsedBody & tClosers & CR into tParsedBody
put empty into tClosers
end if
put tParsedBody & "<table>" & CR into tParsedBody
put "</table>" into tClosers
if matchText(line x+1 of pText, "[^-]") then # if the next line is not all -
put createTableLine(tCurLine, false) into tCurLine # then continue the table as usual
else
put createTableLine(tCurLine, true) into tCurLine # otherwise this line is a table header
add 1 to x #and the next line should be skipped
end if
end if
else
if tClosers is "</table>" then # if this line is not a table line then close the table tag
put tParsedBody & tClosers & CR into tParsedBody
put empty into tClosers
end if
end if
# remove any indents from the beginning of the line and record the number
put countConsecutive(gIndent, tCurLine) into tNumIndents
put char (the number of chars in gIndent * tNumIndents) + 1 to -1 of tCurLine into tCurLine
# check if the line starts with any list characters. If it starts with *, check if there are an odd number, otherwise the star is for bold / italic markup.
if (tCurLine begins with gList and countOccurrences (gList, tCurLine) mod 2 is 1) or (matchChunk(tCurLine, "^([0-9]+\.[[0-9]*\.*]*)", tListPrefixStart, tListPrefixEnd)) then
if tClosers begins with "</p>" then # if we were in a paragraph then close the tag
put tParsedBody & char 1 to 4 of tClosers & CR into tParsedBody
put char 5 to -1 of tClosers into tClosers
put 0 into tQuoteDepth
end if
if tCurLine begins with gList then # check if the list should be ordered or unordered
put char 2 to -1 of tCurLine into tCurLine
put false into tIsOrdered
else
put char tListPrefixEnd + 1 to -1 of tCurLine into tCurLine
put true into tIsOrdered
end if
if tListDepth is tNumIndents + 1 then # we are just continuing a list
if tIsOrdered and char 3 of tClosers is "u" then
put tParsedBody & "</ul>" & CR & "<ol>" & CR into tParsedBody
put "</ol>" & char 6 to -1 of tClosers into tClosers
else if tIsOrdered is false and char 3 of tClosers is "o" then
put tParsedBody & "</ol>" & CR & "<ul>" & CR into tParsedBody
put "</ul>" & char 6 to -1 of tClosers into tClosers
end if
put "<li>" & tCurLine into tCurLine
else
if tListDepth <= tNumIndents then # we are adding nesting to a list
repeat with i = 1 to tNumIndents + 1 - tListDepth # open the appropriate number of list tags
if tIsOrdered then
put tParsedBody & "<ol>" & CR into tParsedBody
put "</ol>" & tClosers into tClosers
else
put tParsedBody & "<ul>" & CR into tParsedBody
put "</ul>" & tClosers into tClosers
end if
end repeat
else # we are substracting nesting from a list
repeat with i = 1 to tListDepth - 1 - tNumIndents # close the appropriate number of list tags
put tParsedBody & char 1 to 5 of tClosers & CR into tParsedBody
put char 6 to -1 of tClosers into tClosers
end repeat
end if
put "<li>" & tCurLine into tCurLine # this line is a list item
put tNumIndents + 1 into tListDepth # update the list nesting count
end if
else # this line is either not part of a list, or it is a line break in a list
if tListDepth is not 0 then
if tListDepth is tNumIndents then # it is a line break in a list item
put "<br>" after line -1 of tParsedBody
else # not in a list, so close the appropriate number of tags
repeat with i = 1 to tListDepth
put tParsedBody & char 1 to 5 of tClosers & CR into tParsedBody
put char 6 to -1 of tClosers into tClosers
end repeat
put 0 into tListDepth
end if
end if
end if
if tCurLine begins with gHeading then # if this is a heading then
if tClosers is not empty then
put tParsedBody & tClosers & CR into tParsedBody # close all open tags
put empty into tClosers
end if
put 0 into tHValue
repeat while char tHValue + 1 of tCurLine is gHeading # count the hashes
add 1 to tHValue
end repeat
if matchText(char tHvalue + 1 to -1 of tCurLine, "(.*) \(contents\)", tCurHeading) then # if we require a table of contents for this section
local tHashes
put empty into tHashes
repeat with j = 0 to tHValue
put tHashes & "#" into tHashes
end repeat
local tLocalConts
local tLocalBody
put x + 1 into tSectionLine
repeat until ((line tSectionLine of pText begins with "#") and (line tSectionLine of pText begins with tHashes is false)) or tSectionLine > tNumLines # find out where the section ends
add 1 to tSectionLine
end repeat
local tSection
put line x + 1 to tSectionLine -1 of pText into tSection
put markdownToHTML(tSection, pHLevel, pCLevel - 1, tLocalConts, false) into tLocalBody # and recursively call markdownToHTML on that section, including its 'local' contents in the body
put tParsedBody & "<h" & pHLevel + tHValue & " " & getAnchor(tCurHeading) & ">" & tCurHeading & "</h" & pHLevel + tHValue & ">" into tParsedBody
put tParsedBody & CR & tLocalConts & CR & tLocalBody into tParsedBody
put empty into tCurLine
put tSectionLine - 1 into x
else # otherwise it's just a new section
put char tHValue + 1 to -1 of tCurLine into tCurHeading
put "<h" & pHLevel + tHValue & " " & getAnchor(tCurHeading) & ">" & tCurHeading & "</h" & pHLevel + tHValue & ">" into tCurLine
end if
appendLink pCLevel + tHValue,tCurHeading, tParsedContents # add it to the table of contents
else
if tCurLine begins with "!-" or tCurLine begins with "/*" or tCurLine begins with "`" then # for blocks of warnings (!-), notes (/*) or code (`), don't do any further processing to the block
local tClosing
switch
case tCurLine begins with "!-"
put "-!" into tClosing
break
case tCurLine begins with "/*"
put "*/" into tClosing
break
case tCurLine begins with "`"
put "`" into tClosing
break
end switch
if tClosers is not empty then # close any open tags
put tParsedBody & tClosers & CR into tParsedBody
put empty into tClosers
end if
# Ensure this is a multiline element
if not char 2 to -1 of tCurLine contains tClosing then
put x into tSectionLine
put tCurLine into tLine
if tNumIndents > 0 then # if there were indents before the block marker, put them inside the block
local tTabs
repeat with k = 1 to tNumIndents
put tTabs & gIndent into tTabs
end repeat
if tLine begins with "`" then
put char 1 of tLine & tTabs & char 2 to -1 of tLine into tLine
else
put char 1 to 2 of tLine & tTabs & char 3 to -1 of tLine into tLine
end if
put empty into tTabs
end if
repeat until tLine ends with tClosing or tSectionLine is tNumLines # find the end of the section
local tLinkUrl
repeat while matchText(tLine,gLinkRegex, tLinkText, tLinkUrl) # linkify any links
addLink tLine, tLinkText, tLinkUrl
end repeat
put tParsedBody & tLine & "<br>" & CR into tParsedBody # adding <br> tags to each line
add 1 to tSectionLine
put line tSectionLine of pText into tLine
end repeat
repeat while matchText(tLine,gLinkRegex, tLinkText, tLinkUrl) # check for links in the last line
addLink tLine, tLinkText, tLinkUrl
end repeat
put tParsedBody & tLine & CR into tParsedBody
put tSectionLine into x
put empty into tCurLine
end if
else if tCurLine is empty then # if the line is empty
if tClosers is not empty then
put tParsedBody & tClosers & CR into tParsedBody
put empty into tClosers
end if
else
if tListDepth is 0 and tNumIndents <> tQuoteDepth then # if the indent has changed
if tClosers is not empty then
put tParsedBody & tClosers & CR into tParsedBody
put empty into tClosers
end if
put 0 into tQuoteDepth
if tNumIndents is not 0 then # if there is still indent then open a paragraph appropriately
put tParsedBody & "<p style=" & getIndentMargin(tNumIndents) & ">" & CR into tParsedBody
put tNumIndents into tQuoteDepth
put "</p>" & tClosers into tClosers
end if
end if
if tClosers is empty then
put "<p>" & tCurLine into tCurLine
put "</p>" into tClosers
end if
if tClosers is "</p>" then
put tCurLine & "<br>" into tCurLine
end if
end if
end if
repeat while matchChunk(tCurLine, "(" & gVidRegex & ")", tStart, tEnd) # linkify any links
if char tStart -1 of tCurLine is "\" then # unless there is an escape character
put tParsedBody & char 1 to tStart - 2 of tCurLine & char tStart to tEnd of tCurLine into tParsedBody
put char tEnd + 1 to -1 of tCurLine into tCurLine
else
get matchText(tCurLine, gVidRegex, tVidUrl)
addVideo tCurLine, tVidUrl
end if
end repeat
repeat while matchChunk(tCurLine, "(" & gImgRegex & ")", tStart, tEnd) # imagify any images
if char tStart -1 of tCurLine is "\" then # unless there is an escape character
put tParsedBody & char 1 to tStart - 2 of tCurLine & char tStart to tEnd of tCurLine into tParsedBody
put char tEnd + 1 to -1 of tCurLine into tCurLine
else
get matchText(tCurLine,gImgRegex, tAltText, tImageUrl)
addImage tCurLine, tAltText, tImageUrl
end if
end repeat
repeat while matchChunk(tCurLine, "(" & gLinkRegex & ")", tStart, tEnd) # linkify any links
if char tStart -1 of tCurLine is "\" then # unless there is an escape character
put tParsedBody & char 1 to tStart - 2 of tCurLine & char tStart to tEnd of tCurLine into tParsedBody
put char tEnd + 1 to -1 of tCurLine into tCurLine
else
get matchText(tCurLine, gLinkRegex, tLinkText, tLinkUrl)
addLink tCurLine, tLinkText, tLinkUrl
end if
end repeat
if tCurLine is not empty then # if the current line has anything in it after that onslaught, then add it to the parsed body
put tParsedBody & tCurLine & CR into tParsedBody
end if
end repeat
# now close any open tags
put tParsedBody & tClosers into tParsedBody
# and deal with the 'inline' markdown elements (but not in recursive calls)
if pTopLevel then
put parseEnclosingMarkup(tParsedBody) into tParsedBody
end if
put xContents & tParsedContents into xContents
return tParsedBody
end markdownToHTML