Skip to content

Commit 1852125

Browse files
committed
tests
1 parent 28a09ae commit 1852125

File tree

9 files changed

+314
-14
lines changed

9 files changed

+314
-14
lines changed

src/FSharp.Formatting.Literate/Literate.fs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,61 @@ type Literate private () =
513513
let docModel = Formatting.transformDocument filesWithFrontMatter doc output ctx
514514
docModel
515515

516+
/// Parse and transform a pynb document
517+
static member internal ParseAndTransformPynbFile
518+
(
519+
input,
520+
output,
521+
outputKind,
522+
prefix,
523+
fscOptions,
524+
lineNumbers,
525+
references,
526+
substitutions,
527+
generateAnchors,
528+
imageSaver,
529+
rootInputFolder,
530+
crefResolver,
531+
mdlinkResolver,
532+
parseOptions,
533+
onError,
534+
filesWithFrontMatter: FrontMatterFile array
535+
) =
536+
537+
let parseOptions =
538+
match outputKind with
539+
| OutputKind.Markdown
540+
| OutputKind.Fsx
541+
| OutputKind.Pynb -> parseOptions ||| MarkdownParseOptions.ParseCodeAsOther
542+
//||| MarkdownParseOptions.ParseNonCodeAsOther
543+
| _ -> parseOptions
544+
545+
let md = ParsePynb.pynbToMarkdown input
546+
let doc =
547+
Literate.ParseMarkdownString(
548+
md,
549+
?fscOptions = fscOptions,
550+
?references = references,
551+
parseOptions = parseOptions,
552+
?rootInputFolder = rootInputFolder,
553+
?onError = onError
554+
)
555+
556+
let ctx =
557+
makeFormattingContext
558+
outputKind
559+
prefix
560+
lineNumbers
561+
generateAnchors
562+
substitutions
563+
crefResolver
564+
mdlinkResolver
565+
None
566+
567+
let doc = downloadImagesForDoc imageSaver doc
568+
let docModel = Formatting.transformDocument filesWithFrontMatter doc output ctx
569+
docModel
570+
516571
/// Convert a markdown file into HTML or another output kind
517572
static member ConvertMarkdownFile
518573
(

src/FSharp.Formatting.Literate/ParsePynb.fs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ namespace FSharp.Formatting.Literate
22

33
open System.IO
44
open System.Text.Json
5+
open FSharp.Formatting.Templating
56

67
module internal ParsePynb =
78

@@ -21,17 +22,15 @@ module internal ParsePynb =
2122
match code.outputs with
2223
| None -> codeBlock
2324
| Some outputs ->
24-
let outputsString = outputs |> String.concat "\n\n"
25-
sprintf $"{codeBlock}\n\n{outputsString}\n\n"
25+
let outputsString = outputs |> String.concat "\n"
26+
sprintf $"{codeBlock}\n{outputsString}"
2627

2728
module Output =
2829
let (|TextHtml|_|) (x: JsonElement) =
2930
match x.TryGetProperty("text/html") with
3031
| true, html ->
31-
html.EnumerateArray()
32-
|> Seq.map (fun x -> x.GetString())
33-
|> String.concat ""
34-
|> Some
32+
let html = html.EnumerateArray() |> Seq.map (fun x -> x.GetString()) |> String.concat "\n"
33+
Some("""<p>""" + html + """</p>""")
3534
| _ -> None
3635

3736
let (|TextPlain|_|) (x: JsonElement) =
@@ -40,9 +39,9 @@ module internal ParsePynb =
4039
let text = text.EnumerateArray() |> Seq.map (fun x -> x.GetString()) |> String.concat ""
4140

4241
Some(
43-
"""<table class="pre"><tr><td><pre><code>"""
42+
"""<table class="pre"><tbody><tr><td><pre><code>"""
4443
+ text
45-
+ """</code></pre></td></tr></table>"""
44+
+ """</code></pre></td></tr></tbody></table>"""
4645
)
4746
| _ -> None
4847

@@ -64,9 +63,9 @@ module internal ParsePynb =
6463
|> String.concat ""
6564

6665
Some(
67-
"""<table class="pre"><tr><td><pre><code>"""
66+
"""<table class="pre"><tbody><tr><td><pre><code>"""
6867
+ text
69-
+ """</code></pre></td></tr></table>"""
68+
+ """</code></pre></td></tr></tbody></table>"""
7069
)
7170
else
7271
None
@@ -143,6 +142,20 @@ module internal ParsePynb =
143142

144143
json.RootElement.GetProperty("cells").EnumerateArray()
145144
|> Seq.map (parseCell >> (fun x -> x.ToMarkdown()))
146-
|> String.concat ""
145+
|> String.concat "\n\n"
146+
147+
let pynbToMarkdown ipynbFile =
148+
ipynbFile |> File.ReadAllText |> pynbStringToMarkdown
147149

148-
let pynbToMarkdown ipynbFile = ipynbFile |> File.ReadAllText |> pynbStringToMarkdown
150+
let parseFrontMatter ipynbFile =
151+
let json = JsonDocument.Parse(ipynbFile |> File.ReadAllText)
152+
153+
json.RootElement.GetProperty("cells").EnumerateArray()
154+
|> Seq.map parseCell
155+
|> Seq.choose (fun cell ->
156+
match cell with
157+
| Code _ -> None
158+
| Markdown source ->
159+
let lines = source.Split([| '\n'; '\r' |], System.StringSplitOptions.RemoveEmptyEntries)
160+
FrontMatterFile.ParseFromLines ipynbFile lines)
161+
|> Seq.tryHead

src/fsdocs-tool/BuildCommand.fs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,11 @@ type internal DocContent
7474
let inputFileName = Path.GetFileName(inputFileFullPath)
7575
let isFsx = inputFileFullPath.EndsWith(".fsx", true, CultureInfo.InvariantCulture)
7676
let isMd = inputFileFullPath.EndsWith(".md", true, CultureInfo.InvariantCulture)
77+
let isPynb = inputFileFullPath.EndsWith(".ipynb", true, CultureInfo.InvariantCulture)
7778
let ext = outputKind.Extension
7879

7980
let outputFileRelativeToRoot =
80-
if isFsx || isMd then
81+
if isFsx || isMd || isPynb then
8182
let basename = Path.GetFileNameWithoutExtension(inputFileFullPath)
8283

8384
Path.Combine(outputFolderRelativeToRoot, sprintf "%s.%s" basename ext)
@@ -189,6 +190,8 @@ type internal DocContent
189190

190191
let isMd = inputFileFullPath.EndsWith(".md", true, CultureInfo.InvariantCulture)
191192

193+
let isPynb = inputFileFullPath.EndsWith(".ipynb", true, CultureInfo.InvariantCulture)
194+
192195
// A _template.tex or _template.pynb is needed to generate those files
193196
match outputKind, template with
194197
| OutputKind.Pynb, None -> ()
@@ -344,7 +347,43 @@ type internal DocContent
344347
template,
345348
outputFileFullPath
346349
)))
350+
elif isPynb then
351+
printfn " preparing %s --> %s" inputFileFullPath outputFileRelativeToRoot
352+
let model =
353+
Literate.ParseAndTransformPynbFile(
354+
inputFileFullPath,
355+
output = outputFileRelativeToRoot,
356+
outputKind = outputKind,
357+
prefix = None,
358+
fscOptions = None,
359+
lineNumbers = lineNumbers,
360+
references = Some false,
361+
substitutions = substitutions,
362+
generateAnchors = Some true,
363+
imageSaver = imageSaverOpt,
364+
rootInputFolder = rootInputFolder,
365+
crefResolver = crefResolver,
366+
mdlinkResolver = mdlinkResolver,
367+
parseOptions = MarkdownParseOptions.AllowYamlFrontMatter,
368+
onError = Some onError,
369+
filesWithFrontMatter = filesWithFrontMatter
370+
)
371+
372+
yield
373+
((if mainRun then
374+
Some(inputFileFullPath, isOtherLang, model)
375+
else
376+
None),
377+
(fun p ->
378+
printfn " writing %s --> %s" inputFileFullPath outputFileRelativeToRoot
379+
ensureDirectory (Path.GetDirectoryName(outputFileFullPath))
347380

381+
SimpleTemplating.UseFileAsSimpleTemplate(
382+
p @ model.Substitutions,
383+
template,
384+
outputFileFullPath
385+
)))
386+
348387
else if mainRun then
349388
yield
350389
(None,
@@ -558,6 +597,8 @@ type internal DocContent
558597
ParseScript.ParseFrontMatter(fileName)
559598
elif ext = ".md" then
560599
File.ReadLines fileName |> FrontMatterFile.ParseFromLines fileName
600+
elif ext = ".ipynb" then
601+
ParsePynb.parseFrontMatter fileName
561602
else
562603
None)
563604
|> Seq.sortBy (fun { Index = idx; CategoryIndex = cIdx } -> cIdx, idx)

tests/FSharp.Literate.Tests/DocContentTests.fs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,33 +45,48 @@ let ``Can build doc content`` () =
4545
// Check simple2.md --> simple2.html substititions
4646
let html1 = File.ReadAllText(rootOutputFolderAsGiven </> "simple1.html")
4747
let html2 = File.ReadAllText(rootOutputFolderAsGiven </> "simple2.html")
48+
let html3 = File.ReadAllText(rootOutputFolderAsGiven </> "simple3.html")
4849
html1 |> shouldContainText """href="simple1.html">"""
4950
html1 |> shouldContainText """href="simple2.html">"""
51+
html1 |> shouldContainText """href="simple3.html">"""
5052
html2 |> shouldContainText """href="simple1.html">"""
5153
html2 |> shouldContainText """href="simple2.html">"""
54+
html2 |> shouldContainText """href="simple3.html">"""
55+
html3 |> shouldContainText """href="simple1.html">"""
56+
html3 |> shouldContainText """href="simple2.html">"""
57+
html3 |> shouldContainText """href="simple3.html">"""
5258

5359
// Check simple1.fsx --> simple1.ipynb substititions
5460
// Check simple2.md --> simple1.ipynb substititions
5561
let ipynb1 = File.ReadAllText(rootOutputFolderAsGiven </> "simple1.ipynb")
5662
let ipynb2 = File.ReadAllText(rootOutputFolderAsGiven </> "simple2.ipynb")
63+
let ipynb3 = File.ReadAllText(rootOutputFolderAsGiven </> "simple3.ipynb")
5764
ipynb1 |> shouldContainText "simple2.ipynb"
65+
ipynb1 |> shouldContainText "simple3.ipynb"
5866
ipynb2 |> shouldContainText "simple1.ipynb"
67+
ipynb3 |> shouldContainText "simple1.ipynb"
5968

6069
// Check fsx exists
6170
// Check simple1.fsx --> simple1.fsx substititions
6271
// Check simple2.md --> simple1.fsx substititions
6372
let fsx1 = File.ReadAllText(rootOutputFolderAsGiven </> "simple1.fsx")
6473
let fsx2 = File.ReadAllText(rootOutputFolderAsGiven </> "simple2.fsx")
74+
let fsx3 = File.ReadAllText(rootOutputFolderAsGiven </> "simple3.fsx")
6575
fsx1 |> shouldContainText "simple2.fsx"
76+
fsx1 |> shouldContainText "simple3.fsx"
6677
fsx2 |> shouldContainText "simple1.fsx"
78+
fsx3 |> shouldContainText "simple1.fsx"
6779

6880
// Check md contents
6981
// Check simple1.fsx --> simple1.md substititions
7082
// Check simple2.md --> simple1.md substititions
7183
let md1 = File.ReadAllText(rootOutputFolderAsGiven </> "simple1.md")
7284
let md2 = File.ReadAllText(rootOutputFolderAsGiven </> "simple2.md")
85+
let md3 = File.ReadAllText(rootOutputFolderAsGiven </> "simple3.md")
7386
md1 |> shouldContainText "simple2.md"
87+
md1 |> shouldContainText "simple3.md"
7488
md2 |> shouldContainText "simple1.md"
89+
md3 |> shouldContainText "simple1.md"
7590

7691

7792
// Check in-folder1.fsx --> in-folder1.html substititions
@@ -134,34 +149,48 @@ let ``Can build doc content using relative input path`` () =
134149
// Check simple2.md --> simple2.html substititions
135150
let html1 = File.ReadAllText(rootOutputFolderAsGiven </> "simple1.html")
136151
let html2 = File.ReadAllText(rootOutputFolderAsGiven </> "simple2.html")
152+
let html3 = File.ReadAllText(rootOutputFolderAsGiven </> "simple3.html")
137153
html1 |> shouldContainText """href="simple1.html">"""
138154
html1 |> shouldContainText """href="simple2.html">"""
155+
html1 |> shouldContainText """href="simple3.html">"""
139156
html2 |> shouldContainText """href="simple1.html">"""
140157
html2 |> shouldContainText """href="simple2.html">"""
158+
html2 |> shouldContainText """href="simple3.html">"""
159+
html3 |> shouldContainText """href="simple1.html">"""
160+
html3 |> shouldContainText """href="simple2.html">"""
161+
html3 |> shouldContainText """href="simple3.html">"""
141162

142163
// Check simple1.fsx --> simple1.ipynb substititions
143164
// Check simple2.md --> simple1.ipynb substititions
144165
let ipynb1 = File.ReadAllText(rootOutputFolderAsGiven </> "simple1.ipynb")
145166
let ipynb2 = File.ReadAllText(rootOutputFolderAsGiven </> "simple2.ipynb")
167+
let ipynb3 = File.ReadAllText(rootOutputFolderAsGiven </> "simple3.ipynb")
146168
ipynb1 |> shouldContainText "simple2.ipynb"
169+
ipynb1 |> shouldContainText "simple3.ipynb"
147170
ipynb2 |> shouldContainText "simple1.ipynb"
171+
ipynb3 |> shouldContainText "simple1.ipynb"
148172

149173
// Check fsx exists
150174
// Check simple1.fsx --> simple1.fsx substititions
151175
// Check simple2.md --> simple1.fsx substititions
152176
let fsx1 = File.ReadAllText(rootOutputFolderAsGiven </> "simple1.fsx")
153177
let fsx2 = File.ReadAllText(rootOutputFolderAsGiven </> "simple2.fsx")
178+
let fsx3 = File.ReadAllText(rootOutputFolderAsGiven </> "simple3.fsx")
154179
fsx1 |> shouldContainText "simple2.fsx"
180+
fsx1 |> shouldContainText "simple3.fsx"
155181
fsx2 |> shouldContainText "simple1.fsx"
182+
fsx3 |> shouldContainText "simple1.fsx"
156183

157184
// Check md contents
158185
// Check simple1.fsx --> simple1.md substititions
159186
// Check simple2.md --> simple1.md substititions
160187
let md1 = File.ReadAllText(rootOutputFolderAsGiven </> "simple1.md")
161188
let md2 = File.ReadAllText(rootOutputFolderAsGiven </> "simple2.md")
189+
let md3 = File.ReadAllText(rootOutputFolderAsGiven </> "simple3.md")
162190
md1 |> shouldContainText "simple2.md"
191+
md1 |> shouldContainText "simple3.md"
163192
md2 |> shouldContainText "simple1.md"
164-
193+
md3 |> shouldContainText "simple1.md"
165194

166195
// Check in-folder1.fsx --> in-folder1.html substititions
167196
let f1html1 = File.ReadAllText(rootOutputFolderAsGiven </> "folder1" </> "in-folder1.html")
@@ -225,3 +254,38 @@ let ``Parses frontmatter correctly `` () =
225254
twoTowersHtml |> shouldContainText "<a href=\"fellowship.html\">Previous</a>"
226255
twoTowersHtml |> shouldContainText "<a href=\"return.html\">Next</a>"
227256
returnHtml |> shouldContainText "<a href=\"two-tower.html\">Previous</a>"
257+
258+
259+
(* Cannot get this test to evaluate the notebook
260+
[<Test>]
261+
let ``ipynb notebook evaluates`` () =
262+
let rootOutputFolderAsGiven = __SOURCE_DIRECTORY__ </> "ipynb-eval-output"
263+
let rootInputFolderAsGiven = __SOURCE_DIRECTORY__ </> "ipynb-eval"
264+
265+
if Directory.Exists(rootOutputFolderAsGiven) then
266+
Directory.Delete(rootOutputFolderAsGiven, true)
267+
268+
let content =
269+
DocContent(
270+
rootOutputFolderAsGiven,
271+
Map.empty,
272+
lineNumbers = None,
273+
evaluate = true,
274+
substitutions = [],
275+
saveImages = None,
276+
watch = false,
277+
root = "https://github.com",
278+
crefResolver = (fun _ -> None),
279+
onError = failwith
280+
)
281+
282+
let docModels = content.Convert(rootInputFolderAsGiven, None, [])
283+
let globals = []
284+
285+
for (_thing, action) in docModels do
286+
action globals
287+
288+
let ipynbOut = rootOutputFolderAsGiven </> "eval.html" |> File.ReadAllText
289+
290+
ipynbOut |> shouldContainText "10007"
291+
*)

tests/FSharp.Literate.Tests/files/simple1.fsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ substitute-in-markdown: {{fsdocs-source-basename}}
1313
1414
Another [hyperlink](simple1.fsx)
1515
Another [hyperlink](simple2.md)
16+
And another [hyperlink](simple3.ipynb)
1617
1718
*)
1819
let hello = "Code sample"

tests/FSharp.Literate.Tests/files/simple2.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ With some [hyperlink](http://tomasp.net)
77

88
Another [hyperlink](simple1.fsx)
99
Yet another [hyperlink](simple2.md)
10+
And another [hyperlink](simple3.ipynb)

0 commit comments

Comments
 (0)