Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## [Unreleased]

### Changed
* Refactor `Markdown.ToMd` list-block formatting: extract a shared `formatListBlock` helper to eliminate the near-identical code paths for ordered and unordered lists in `MarkdownUtils.fs`.

### Changed
* 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.
* Replace deprecated `System.Net.WebClient` with `System.Net.Http.HttpClient` in the image downloader used by `--saveimages`. Removes the `#nowarn "44"` suppression.
Expand Down
76 changes: 28 additions & 48 deletions src/FSharp.Formatting.Markdown/MarkdownUtils.fs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,32 @@ module internal MarkdownUtils =

/// Format a MarkdownParagraph
let rec formatParagraph (ctx: FormattingContext) paragraph =
// Shared helper for both ordered and unordered list blocks.
// getPrefix receives the 0-based item index and returns the leading string (e.g. "* " or "1. ").
let formatListBlock paragraphsl (getPrefix: int -> string) =
let isTight =
paragraphsl
|> List.forall (function
| [ Span _ ] -> true
| _ -> false)

[ for (n, paragraphs) in List.indexed paragraphsl do
for (i, paragraph) in List.indexed paragraphs do
let lines: string list = formatParagraph ctx paragraph
let lines = if lines.IsEmpty then [ "" ] else lines

for (j, line) in List.indexed lines do
if i = 0 && j = 0 then
yield getPrefix n + line
else
yield " " + line

if not isTight then
yield ""

if isTight then
yield "" ]

[ match paragraph with
| LatexBlock(env, lines, _) ->
// Single-line equation blocks are rendered with the compact $$...$$ notation
Expand Down Expand Up @@ -197,54 +223,8 @@ module internal MarkdownUtils =
yield f

yield ""
| ListBlock(Unordered, paragraphsl, _) ->
// A tight list has exactly one Span per item (no blank lines between items).
let isTight =
paragraphsl
|> List.forall (function
| [ Span _ ] -> true
| _ -> false)

for paragraphs in paragraphsl do
for (i, paragraph) in List.indexed paragraphs do
let lines = formatParagraph ctx paragraph
let lines = if lines.IsEmpty then [ "" ] else lines

for (j, line) in List.indexed lines do
if i = 0 && j = 0 then
yield "* " + line
else
yield " " + line

if not isTight then
yield ""

if isTight then
yield ""
| ListBlock(Ordered, paragraphsl, _) ->
// A tight list has exactly one Span per item (no blank lines between items).
let isTight =
paragraphsl
|> List.forall (function
| [ Span _ ] -> true
| _ -> false)

for (n, paragraphs) in List.indexed paragraphsl do
for (i, paragraph) in List.indexed paragraphs do
let lines = formatParagraph ctx paragraph
let lines = if lines.IsEmpty then [ "" ] else lines

for (j, line) in List.indexed lines do
if i = 0 && j = 0 then
yield $"%i{n + 1}. " + line
else
yield " " + line

if not isTight then
yield ""

if isTight then
yield ""
| ListBlock(Unordered, paragraphsl, _) -> yield! formatListBlock paragraphsl (fun _ -> "* ")
| ListBlock(Ordered, paragraphsl, _) -> yield! formatListBlock paragraphsl (fun n -> $"{n + 1}. ")
| TableBlock(headers, alignments, rows, _) ->

match headers with
Expand Down
Loading