Skip to content

Commit 1b2279f

Browse files
authored
Merge branch 'main' into repo-assist/perf-compile-regex-2026-04-10-f88f43f8587c44e1
2 parents 4e2a8cc + 3380bbc commit 1b2279f

7 files changed

Lines changed: 131 additions & 15 deletions

File tree

.github/workflows/pull-requests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
run: dotnet fsi build.fsx
4646

4747
- name: Upload test results
48-
uses: actions/upload-artifact@v4
48+
uses: actions/upload-artifact@v7
4949
if: failure()
5050
with:
5151
name: test-results-${{ matrix.os }}

.github/workflows/push-main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
run: dotnet fsi build.fsx
4444

4545
- name: Upload test results
46-
uses: actions/upload-artifact@v4
46+
uses: actions/upload-artifact@v7
4747
if: failure()
4848
with:
4949
name: test-results-ubuntu

Directory.Packages.props

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="" PrivateAssets="all" />
1515
<PackageVersion Include="Microsoft.Build.Tasks.Core" Version="" PrivateAssets="all" />
1616
<PackageVersion Include="Ionide.ProjInfo" Version="0.74.2" />
17-
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
17+
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
1818
<PackageVersion Include="Suave" Version="2.6.2" />
19-
<PackageVersion Include="System.Memory" Version="4.5.5" />
19+
<PackageVersion Include="System.Memory" Version="4.6.3" />
2020
<PackageVersion Include="System.Text.Json" Version="10.0.5" />
2121
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
2222
<PackageVersion Include="NUnit" Version="4.5.1" />
2323
<PackageVersion Include="FsUnit" Version="7.1.1" />
24-
<PackageVersion Include="FSharp.Data" Version="8.1.6" />
24+
<PackageVersion Include="FSharp.Data" Version="8.1.7" />
2525
<PackageVersion Include="NUnit3TestAdapter" Version="6.2.0" />
2626
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.4.0" />
2727
<PackageVersion Include="Ionide.KeepAChangelog.Tasks" Version="0.3.3" />

RELEASE_NOTES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44

55
### Changed
66
* Compile `Regex` instances to module-level singletons (with `RegexOptions.Compiled`) in `PageContentList`, `HtmlFormatting`, `Formatting`, `Menu`, and `LlmsTxt`. Previously a new, uncompiled `Regex` was constructed on every call (once per page heading, once per HTML page, once per menu item, once per llms.txt entry), incurring repeated JIT overhead. The patterns are now compiled once at module load and reused across all calls.
7+
* Replace deprecated `System.Net.WebClient` with `System.Net.Http.HttpClient` in the image downloader used by `--saveimages`. Removes the `#nowarn "44"` suppression.
8+
* Bump `Newtonsoft.Json` transitive-dependency pin from 13.0.3 to 13.0.4.
9+
* Bump `System.Memory` transitive-dependency pin from 4.5.5 to 4.6.3.0
10+
711
### Fixed
12+
* Fix `Markdown.ToMd` silently dropping `EmbedParagraphs` nodes: the serialiser now delegates to the node's `Render()` method and formats the resulting paragraphs, consistent with the HTML and LaTeX back-ends.
813
* Fix `Markdown.ToMd` dropping link titles in `DirectLink` and `DirectImage` spans. Links with a title attribute (e.g. `[text](url "title")`) now round-trip correctly; without this fix the title was silently discarded on serialisation.
914
* Fix `Markdown.ToMd` serialising inline code spans that contain backtick characters. Previously, `InlineCode` was always wrapped in single backticks, producing syntactically incorrect Markdown when the code body contained backticks. Now the serialiser selects the shortest backtick fence that does not collide with the body content (e.g. a double-backtick fence for bodies containing single backticks, triple for double, etc.), matching the CommonMark spec.
1015

src/FSharp.Formatting.Markdown/MarkdownUtils.fs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,19 @@ module internal MarkdownUtils =
160160
let rec formatParagraph (ctx: FormattingContext) paragraph =
161161
[ match paragraph with
162162
| LatexBlock(env, lines, _) ->
163-
yield sprintf "\\begin{%s}" env
163+
// Single-line equation blocks are rendered with the compact $$...$$ notation
164+
// (which is also valid markdown and what most authors write). Multi-line or
165+
// non-standard environments keep the \begin{env}...\end{env} form.
166+
if env = "equation" && lines.Length = 1 then
167+
yield sprintf "$$%s$$" lines.[0]
168+
else
169+
yield sprintf "\\begin{%s}" env
164170

165-
for line in lines do
166-
yield line
171+
for line in lines do
172+
yield line
173+
174+
yield sprintf "\\end{%s}" env
167175

168-
yield sprintf "\\end{%s}" env
169176
yield ""
170177

171178
| Heading(n, spans, _) ->
@@ -280,9 +287,7 @@ module internal MarkdownUtils =
280287
yield "> " + line
281288

282289
yield ""
283-
| _ ->
284-
printfn "// can't yet format %0A to markdown" paragraph
285-
yield "" ]
290+
| EmbedParagraphs(cmd, _) -> yield! cmd.Render() |> Seq.collect (formatParagraph ctx) ]
286291

287292
/// Strips <c>#if SYMBOL</c> / <c>#endif // SYMBOL</c> conditional compilation lines from an .fsx code block
288293
/// so that format-specific sections are removed from non-target output formats.

src/fsdocs-tool/BuildCommand.fs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ open System.Diagnostics
88
open System.IO
99
open System.Globalization
1010
open System.Net
11+
open System.Net.Http
1112
open System.Reflection
1213
open System.Text
1314

@@ -29,7 +30,6 @@ open Suave.Filters
2930
open Suave.Logging
3031
open FSharp.Formatting.Markdown
3132

32-
#nowarn "44" // Obsolete WebClient
3333

3434
/// Convert markdown, script and other content into a static site
3535
type internal DocContent
@@ -48,7 +48,7 @@ type internal DocContent
4848

4949
let createImageSaver (rootOutputFolderAsGiven) =
5050
// Download images so that they can be embedded
51-
let wc = new WebClient()
51+
let http = new HttpClient()
5252
let mutable counter = 0
5353

5454
fun (url: string) ->
@@ -65,7 +65,8 @@ type internal DocContent
6565

6666
ensureDirectory (sprintf "%s/savedimages" rootOutputFolderAsGiven)
6767
printfn "downloading %s --> %s" url fn
68-
wc.DownloadFile(url, fn)
68+
let bytes = http.GetByteArrayAsync(url).GetAwaiter().GetResult()
69+
File.WriteAllBytes(fn, bytes)
6970
url2
7071
else
7172
url

tests/FSharp.Markdown.Tests/Markdown.fs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,3 +1417,108 @@ let ``ToMd round-trip: indirect image with unresolved reference`` () =
14171417
let result = Markdown.ToMd(doc)
14181418
// When key is not resolved, should preserve the indirect form
14191419
result |> should contain "![alt text][unknown-ref]"
1420+
1421+
// --------------------------------------------------------------------------------------
1422+
// ToMd additional coverage: headings, nested structures, LaTeX display math, inline code
1423+
// --------------------------------------------------------------------------------------
1424+
1425+
[<Test>]
1426+
let ``ToMd preserves heading level 4`` () =
1427+
"#### Heading Four" |> toMd |> shouldEqual "#### Heading Four"
1428+
1429+
[<Test>]
1430+
let ``ToMd preserves heading level 5`` () =
1431+
"##### Heading Five" |> toMd |> shouldEqual "##### Heading Five"
1432+
1433+
[<Test>]
1434+
let ``ToMd preserves heading level 6`` () =
1435+
"###### Heading Six" |> toMd |> shouldEqual "###### Heading Six"
1436+
1437+
[<Test>]
1438+
let ``ToMd preserves emphasis inside a heading`` () =
1439+
let result = "## Hello *world*" |> toMd
1440+
result |> should contain "## Hello *world*"
1441+
1442+
[<Test>]
1443+
let ``ToMd preserves strong text inside a heading`` () =
1444+
let result = "## Hello **world**" |> toMd
1445+
result |> should contain "## Hello **world**"
1446+
1447+
[<Test>]
1448+
let ``ToMd preserves LaTeX display math`` () =
1449+
let md = "$$E = mc^2$$"
1450+
let result = toMd md
1451+
result |> should contain "$$E = mc^2$$"
1452+
1453+
[<Test>]
1454+
let ``ToMd preserves inline code containing special chars`` () =
1455+
let md = "Use `a + b = c` inline."
1456+
let result = toMd md
1457+
result |> should contain "`a + b = c`"
1458+
1459+
[<Test>]
1460+
let ``ToMd preserves nested unordered list`` () =
1461+
// Outer list item containing an inner list
1462+
let md = "* outer\n\n * inner"
1463+
let result = toMd md
1464+
result |> should contain "outer"
1465+
result |> should contain "inner"
1466+
1467+
[<Test>]
1468+
let ``ToMd preserves a nested blockquote`` () =
1469+
// A blockquote that itself contains a blockquote
1470+
let md = "> > inner quote"
1471+
let result = toMd md
1472+
result |> should contain "> "
1473+
result |> should contain "inner quote"
1474+
// The inner quote marker should appear in the output (two levels of '>')
1475+
result |> should contain "> >"
1476+
1477+
[<Test>]
1478+
let ``ToMd preserves emphasis inside a blockquote`` () =
1479+
let md = "> *italic text*"
1480+
let result = toMd md
1481+
result |> should contain "> "
1482+
result |> should contain "*italic text*"
1483+
1484+
[<Test>]
1485+
let ``ToMd preserves inline code inside a blockquote`` () =
1486+
let md = "> use `printf` here"
1487+
let result = toMd md
1488+
result |> should contain "> "
1489+
result |> should contain "`printf`"
1490+
1491+
[<Test>]
1492+
let ``ToMd preserves a code block without language`` () =
1493+
let md = "```\nsome code\n```"
1494+
let result = toMd md
1495+
result |> should contain "some code"
1496+
result |> should contain "```"
1497+
1498+
[<Test>]
1499+
let ``ToMd preserves horizontal rule (dash variant)`` () =
1500+
let md = "---"
1501+
let result = toMd md
1502+
result |> should contain "---"
1503+
1504+
[<Test>]
1505+
let ``ToMd preserves a link with a title`` () =
1506+
// Title attribute is allowed in Markdown links
1507+
let md = "[FSharp](https://fsharp.org \"F# home\")"
1508+
let result = toMd md
1509+
result |> should contain "[FSharp]("
1510+
result |> should contain "https://fsharp.org"
1511+
1512+
[<Test>]
1513+
let ``ToMd serialises EmbedParagraphs by delegating to Render()`` () =
1514+
// EmbedParagraphs was previously falling through to the catch-all '| _' branch,
1515+
// emitting a debug printfn and yielding an empty string. It should instead
1516+
// delegate to the Render() method and format the resulting paragraphs.
1517+
let inner =
1518+
{ new MarkdownEmbedParagraphs with
1519+
member _.Render() =
1520+
[ Paragraph([ Literal("embedded text", MarkdownRange.zero) ], MarkdownRange.zero) ] }
1521+
1522+
let doc = MarkdownDocument([ EmbedParagraphs(inner, MarkdownRange.zero) ], dict [])
1523+
let result = Markdown.ToMd(doc)
1524+
result |> should contain "embedded text"

0 commit comments

Comments
 (0)