Skip to content

Commit 5e3d985

Browse files
github-actions[bot]GitHub CopilotCopilotdsyme
authored
[Repo Assist] Make MarkdownRange non-optional in MarkdownSpan and MarkdownParagraph (fixes #742) (#1067)
* Make MarkdownRange non-optional in MarkdownSpan and MarkdownParagraph Replace all `range: MarkdownRange option` fields in the MarkdownSpan and MarkdownParagraph discriminated unions with `range: MarkdownRange`, using `MarkdownRange.zero` as the fallback where no range was available. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci: trigger CI checks * Fix docs/evaluation.fsx: use MarkdownRange.zero instead of None after breaking change The PR changed MarkdownRange from option to non-optional in all DU cases. docs/evaluation.fsx was missed and still used None for range arguments in Literal, Paragraph, and ListBlock constructors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci: trigger checks --------- Co-authored-by: GitHub Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Don Syme <dsyme@users.noreply.github.com>
1 parent f70f7f6 commit 5e3d985

13 files changed

Lines changed: 388 additions & 493 deletions

File tree

RELEASE_NOTES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@
22

33
## [Unreleased]
44

5+
### Refactored
6+
* Split `MarkdownParser.fs` (1500 lines) into `MarkdownInlineParser.fs` (inline formatting) and `MarkdownParser.fs` (block-level parsing) for better maintainability. [#1022](https://github.com/fsprojects/FSharp.Formatting/issues/1022)
7+
58
### Added
69
* Add `dotnet fsdocs convert` command to convert a single `.md`, `.fsx`, or `.ipynb` file to HTML (or another output format) without building a full documentation site. [#811](https://github.com/fsprojects/FSharp.Formatting/issues/811)
710
* `fsdocs convert` now accepts the input file as a positional argument (e.g. `fsdocs convert notebook.ipynb -o notebook.html`). [#1019](https://github.com/fsprojects/FSharp.Formatting/pull/1019)
811
* `fsdocs convert` infers the output format from the output file extension when `--outputformat` is not specified (e.g. `-o out.md` implies `--outputformat markdown`). [#1019](https://github.com/fsprojects/FSharp.Formatting/pull/1019)
912
* `fsdocs convert` now accepts `-o` as a shorthand for `--output`. [#1019](https://github.com/fsprojects/FSharp.Formatting/pull/1019)
1013

1114
### Changed
15+
* Changed `range` fields in `MarkdownSpan` and `MarkdownParagraph` DU cases from `MarkdownRange option` to `MarkdownRange`, using `MarkdownRange.zero` as the default/placeholder value instead of `None`.
1216
* When no template is provided (e.g. `fsdocs convert` without `--template`), `fsdocs-tip` tooltip divs are no longer included in the output. Tooltips require JavaScript/CSS from a template to function, so omitting them produces cleaner raw output. [#1019](https://github.com/fsprojects/FSharp.Formatting/pull/1019)
17+
1318
## 22.0.0-alpha.1 - 2026-03-03
1419

1520
### Added

docs/evaluation.fsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,9 +200,10 @@ fsiEvaluator.RegisterTransformation(fun (o, ty, _executionCount) ->
200200
&& ty.GetGenericTypeDefinition() = typedefof<list<_>> then
201201
let items =
202202
// Get items as objects and create a paragraph for each item
203-
[ for it in Seq.cast<obj> (unbox o) -> [ Paragraph([ Literal(it.ToString(), None) ], None) ] ]
203+
[ for it in Seq.cast<obj> (unbox o) ->
204+
[ Paragraph([ Literal(it.ToString(), MarkdownRange.zero) ], MarkdownRange.zero) ] ]
204205
// Return option value (success) with ordered list
205-
Some [ ListBlock(MarkdownListKind.Ordered, items, None) ]
206+
Some [ ListBlock(MarkdownListKind.Ordered, items, MarkdownRange.zero) ]
206207
else
207208
None)
208209
(**

src/FSharp.Formatting.Literate/Formatting.fs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,15 @@ module internal Formatting =
8080
/// entire source code of the specified document (with possible fsx formatting)
8181
let getSourceDocument (doc: LiterateDocument) =
8282
match doc.Source with
83-
| LiterateSource.Markdown text -> doc.With(paragraphs = [ CodeBlock(text, None, None, "", "", None) ])
83+
| LiterateSource.Markdown text ->
84+
doc.With(paragraphs = [ CodeBlock(text, None, None, "", "", MarkdownRange.zero) ])
8485
| LiterateSource.Script snippets ->
8586
let mutable count = 0
8687

8788
let paragraphs =
8889
[ for Snippet(name, lines) in snippets do
8990
if snippets.Length > 1 then
90-
yield Heading(3, [ Literal(name, None) ], None)
91+
yield Heading(3, [ Literal(name, MarkdownRange.zero) ], MarkdownRange.zero)
9192

9293
let id =
9394
count <- count + 1
@@ -100,7 +101,7 @@ module internal Formatting =
100101
Visibility = LiterateCodeVisibility.VisibleCode }
101102

102103
let popts = { Condition = None }
103-
yield EmbedParagraphs(LiterateCode(lines, opts, popts), None) ]
104+
yield EmbedParagraphs(LiterateCode(lines, opts, popts), MarkdownRange.zero) ]
104105

105106
doc.With(paragraphs = paragraphs)
106107

src/FSharp.Formatting.Literate/Literate.fs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,11 @@ type Literate private () =
286286
let doc = Transformations.replaceLiterateParagraphs ctx doc
287287

288288
let doc =
289-
MarkdownDocument(doc.Paragraphs @ [ InlineHtmlBlock(doc.FormattedTips, None, None) ], doc.DefinedLinks)
289+
MarkdownDocument(
290+
doc.Paragraphs
291+
@ [ InlineHtmlBlock(doc.FormattedTips, None, MarkdownRange.zero) ],
292+
doc.DefinedLinks
293+
)
290294

291295
let sb = System.Text.StringBuilder()
292296
use wr = new StringWriter(sb)
@@ -334,7 +338,10 @@ type Literate private () =
334338

335339
let doc = Transformations.replaceLiterateParagraphs ctx doc
336340

337-
let paragraphs = doc.Paragraphs @ [ InlineHtmlBlock(doc.FormattedTips, None, None) ], doc.DefinedLinks
341+
let paragraphs =
342+
doc.Paragraphs
343+
@ [ InlineHtmlBlock(doc.FormattedTips, None, MarkdownRange.zero) ],
344+
doc.DefinedLinks
338345

339346
let doc = MarkdownDocument(paragraphs)
340347

src/FSharp.Formatting.Literate/ParseScript.fs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -183,103 +183,103 @@ type internal ParseScript(parseOptions, ctx: CompilerContext) =
183183
| BlockCommand((Command "include" ref) as cmds) :: blocks, _ ->
184184
let popts = getParaOptions cmds
185185

186-
let p = EmbedParagraphs(CodeReference(ref, popts), None)
186+
let p = EmbedParagraphs(CodeReference(ref, popts), MarkdownRange.zero)
187187

188188
transformBlocks false None count noEval (p :: acc) defs blocks
189189

190190
// Include console output (stdout) of previous block
191191
| BlockCommand(Command "include-output" EmptyString as cmds) :: blocks, Some prevCodeId ->
192192
let popts = getParaOptions cmds
193193

194-
let p1 = EmbedParagraphs(OutputReference(prevCodeId, popts), None)
194+
let p1 = EmbedParagraphs(OutputReference(prevCodeId, popts), MarkdownRange.zero)
195195

196196
transformBlocks false (Some prevCodeId) count noEval (p1 :: acc) defs blocks
197197

198198
// Include console output (stdout) of a named block
199199
| BlockCommand(Command "include-output" ref as cmds) :: blocks, _ ->
200200
let popts = getParaOptions cmds
201201

202-
let p = EmbedParagraphs(OutputReference(ref, popts), None)
202+
let p = EmbedParagraphs(OutputReference(ref, popts), MarkdownRange.zero)
203203

204204
transformBlocks false prevCodeId count noEval (p :: acc) defs blocks
205205

206206
// Include FSI output (stdout) of previous block
207207
| BlockCommand(Command "include-fsi-output" EmptyString as cmds) :: blocks, Some prevCodeId ->
208208
let popts = getParaOptions cmds
209209

210-
let p1 = EmbedParagraphs(FsiOutputReference(prevCodeId, popts), None)
210+
let p1 = EmbedParagraphs(FsiOutputReference(prevCodeId, popts), MarkdownRange.zero)
211211

212212
transformBlocks false (Some prevCodeId) count noEval (p1 :: acc) defs blocks
213213

214214
// Include FSI output (stdout) of a named block
215215
| BlockCommand(Command "include-fsi-output" ref as cmds) :: blocks, _ ->
216216
let popts = getParaOptions cmds
217217

218-
let p = EmbedParagraphs(FsiOutputReference(ref, popts), None)
218+
let p = EmbedParagraphs(FsiOutputReference(ref, popts), MarkdownRange.zero)
219219

220220
transformBlocks false prevCodeId count noEval (p :: acc) defs blocks
221221

222222
// Include the merge of the console and FSI output (stdout) of previous block
223223
| BlockCommand(Command "include-fsi-merged-output" EmptyString as cmds) :: blocks, Some prevCodeId ->
224224
let popts = getParaOptions cmds
225225

226-
let p1 = EmbedParagraphs(FsiMergedOutputReference(prevCodeId, popts), None)
226+
let p1 = EmbedParagraphs(FsiMergedOutputReference(prevCodeId, popts), MarkdownRange.zero)
227227

228228
transformBlocks false (Some prevCodeId) count noEval (p1 :: acc) defs blocks
229229

230230
// Include the merge of the console and FSI output (stdout) of a named block
231231
| BlockCommand(Command "include-fsi-merged-output" ref as cmds) :: blocks, _ ->
232232
let popts = getParaOptions cmds
233233

234-
let p = EmbedParagraphs(FsiMergedOutputReference(ref, popts), None)
234+
let p = EmbedParagraphs(FsiMergedOutputReference(ref, popts), MarkdownRange.zero)
235235

236236
transformBlocks false prevCodeId count noEval (p :: acc) defs blocks
237237

238238
// Include formatted 'it' of previous block
239239
| BlockCommand((Command "include-it" EmptyString) as cmds) :: blocks, Some prevCodeId ->
240240
let popts = getParaOptions cmds
241241

242-
let p1 = EmbedParagraphs(ItValueReference(prevCodeId, popts), None)
242+
let p1 = EmbedParagraphs(ItValueReference(prevCodeId, popts), MarkdownRange.zero)
243243

244244
transformBlocks false (Some prevCodeId) count noEval (p1 :: acc) defs blocks
245245

246246
// Include formatted 'it' of a named block
247247
| BlockCommand(Command "include-it" ref as cmds) :: blocks, _ ->
248248
let popts = getParaOptions cmds
249249

250-
let p = EmbedParagraphs(ItValueReference(ref, popts), None)
250+
let p = EmbedParagraphs(ItValueReference(ref, popts), MarkdownRange.zero)
251251

252252
transformBlocks false None count noEval (p :: acc) defs blocks
253253

254254
// Include unformatted 'it' of previous block
255255
| BlockCommand((Command "include-it-raw" EmptyString) as cmds) :: blocks, Some prevCodeId ->
256256
let popts = getParaOptions cmds
257257

258-
let p1 = EmbedParagraphs(ItRawReference(prevCodeId, popts), None)
258+
let p1 = EmbedParagraphs(ItRawReference(prevCodeId, popts), MarkdownRange.zero)
259259

260260
transformBlocks false (Some prevCodeId) count noEval (p1 :: acc) defs blocks
261261

262262
// Include unformatted 'it' of a named block
263263
| BlockCommand(Command "include-it-raw" ref as cmds) :: blocks, _ ->
264264
let popts = getParaOptions cmds
265265

266-
let p = EmbedParagraphs(ItRawReference(ref, popts), None)
266+
let p = EmbedParagraphs(ItRawReference(ref, popts), MarkdownRange.zero)
267267

268268
transformBlocks false None count noEval (p :: acc) defs blocks
269269

270270
// Include formatted named value
271271
| BlockCommand(Command "include-value" ref as cmds) :: blocks, _ ->
272272
let popts = getParaOptions cmds
273273

274-
let p = EmbedParagraphs(ValueReference(ref, popts), None)
274+
let p = EmbedParagraphs(ValueReference(ref, popts), MarkdownRange.zero)
275275

276276
transformBlocks false None count noEval (p :: acc) defs blocks
277277

278278
// Include code without evaluation
279279
| BlockCommand(Command "raw" _ as cmds) :: BlockSnippet(snip) :: blocks, _ ->
280280
let popts = getParaOptions cmds
281281

282-
let p = EmbedParagraphs(RawBlock(snip, popts), None)
282+
let p = EmbedParagraphs(RawBlock(snip, popts), MarkdownRange.zero)
283283

284284
transformBlocks false None count noEval (p :: acc) defs blocks
285285

@@ -304,7 +304,7 @@ type internal ParseScript(parseOptions, ctx: CompilerContext) =
304304

305305
let popts = getParaOptions cmds
306306

307-
let code = EmbedParagraphs(LiterateCode(snip, opts, popts), None)
307+
let code = EmbedParagraphs(LiterateCode(snip, opts, popts), MarkdownRange.zero)
308308

309309
transformBlocks false (Some outputName) count noEval (code :: acc) defs blocks
310310

@@ -329,7 +329,7 @@ type internal ParseScript(parseOptions, ctx: CompilerContext) =
329329

330330
let popts = { Condition = None }
331331

332-
let p = EmbedParagraphs(LiterateCode(snip, opts, popts), None)
332+
let p = EmbedParagraphs(LiterateCode(snip, opts, popts), MarkdownRange.zero)
333333

334334
transformBlocks false (Some id) count noEval (p :: acc) defs blocks
335335

src/FSharp.Formatting.Literate/Transformations.fs

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -264,25 +264,35 @@ module internal Transformations =
264264

265265
yield
266266
[ Span(
267-
[ Literal(sprintf "[%d] " i, None)
268-
DirectLink([ Literal(name.Trim(), None) ], link, Some title, None)
269-
Literal(" - " + auth, None) ],
270-
None
267+
[ Literal(sprintf "[%d] " i, MarkdownRange.zero)
268+
DirectLink(
269+
[ Literal(name.Trim(), MarkdownRange.zero) ],
270+
link,
271+
Some title,
272+
MarkdownRange.zero
273+
)
274+
Literal(" - " + auth, MarkdownRange.zero) ],
275+
MarkdownRange.zero
271276
) ]
272277
else
273278
yield
274279
[ Span(
275-
[ Literal(sprintf "[%d] " i, None)
276-
DirectLink([ Literal(title, None) ], link, Some title, None) ],
277-
None
280+
[ Literal(sprintf "[%d] " i, MarkdownRange.zero)
281+
DirectLink(
282+
[ Literal(title, MarkdownRange.zero) ],
283+
link,
284+
Some title,
285+
MarkdownRange.zero
286+
) ],
287+
MarkdownRange.zero
278288
) ] ]
279289

280290
// Return the document together with dictionary for looking up indices
281291
let id = DateTime.Now.ToString("yyMMddhh")
282292

283-
[ Paragraph([ AnchorLink(id, None) ], None)
284-
Heading(3, [ Literal("References", None) ], None)
285-
ListBlock(MarkdownListKind.Unordered, refList, None) ],
293+
[ Paragraph([ AnchorLink(id, MarkdownRange.zero) ], MarkdownRange.zero)
294+
Heading(3, [ Literal("References", MarkdownRange.zero) ], MarkdownRange.zero)
295+
ListBlock(MarkdownListKind.Unordered, refList, MarkdownRange.zero) ],
286296
refLookup
287297

288298
/// Turn all indirect links into a references
@@ -417,8 +427,8 @@ module internal Transformations =
417427
{ opts with
418428
ExecutionCount = Some executionCount }
419429

420-
[ EmbedParagraphs(LiterateCode(lines, opts, popts), None) ]
421-
| _ -> [ EmbedParagraphs(special, None) ]
430+
[ EmbedParagraphs(LiterateCode(lines, opts, popts), MarkdownRange.zero) ]
431+
| _ -> [ EmbedParagraphs(special, MarkdownRange.zero) ]
422432

423433
// Traverse all other structrues recursively
424434
| MarkdownPatterns.ParagraphNested(pn, nested) ->
@@ -517,7 +527,7 @@ module internal Transformations =
517527
| LiterateCode(_, { Visibility = LiterateCodeVisibility.NamedCode _ }, _) -> None
518528
| _ ->
519529
match special with
520-
| RawBlock(lines, _) -> Some(InlineHtmlBlock(unparse lines, None, None))
530+
| RawBlock(lines, _) -> Some(InlineHtmlBlock(unparse lines, None, MarkdownRange.zero))
521531
| LiterateCode(lines, _, _) -> Some(formatted.[Choice1Of2 lines])
522532
| CodeReference(ref, _) ->
523533
match formatted.TryGetValue(Choice2Of2 ref) with
@@ -536,7 +546,7 @@ module internal Transformations =
536546
let msg = "Warning: Output, it-value and value references require --eval"
537547

538548
printfn "%s" msg
539-
Some(InlineHtmlBlock(msg, None, None))
549+
Some(InlineHtmlBlock(msg, None, MarkdownRange.zero))
540550
| LanguageTaggedCode(lang, code, _) ->
541551
let inlined =
542552
match ctx.OutputKind with
@@ -546,7 +556,7 @@ module internal Transformations =
546556
| OutputKind.Fsx -> code
547557
| OutputKind.Markdown -> code
548558

549-
Some(InlineHtmlBlock(inlined, None, None))
559+
Some(InlineHtmlBlock(inlined, None, MarkdownRange.zero))
550560
// Traverse all other structures recursively
551561
| MarkdownPatterns.ParagraphNested(pn, nested) ->
552562
let nested = List.map (List.choose (replaceLiterateParagraph ctx formatted)) nested
@@ -602,7 +612,7 @@ module internal Transformations =
602612
[ for (key, (_, executionCount)), fmtd in Seq.zip codes formatted.Snippets ->
603613
let block =
604614
match ctx.OutputKind with
605-
| OutputKind.Html -> InlineHtmlBlock(fmtd.Content, executionCount, None)
615+
| OutputKind.Html -> InlineHtmlBlock(fmtd.Content, executionCount, MarkdownRange.zero)
606616
| OutputKind.Fsx
607617
| OutputKind.Markdown
608618
| OutputKind.Latex
@@ -613,7 +623,7 @@ module internal Transformations =
613623
fence = Some "```",
614624
language = "fsharp",
615625
ignoredLine = "",
616-
range = None
626+
range = MarkdownRange.zero
617627
)
618628

619629
key, block ]

src/FSharp.Formatting.Markdown/Markdown.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ type Markdown internal () =
7070
{ Newline = newline
7171
IsFirst = true
7272
Links = links
73-
CurrentRange = Some(MarkdownRange.zero)
73+
CurrentRange = MarkdownRange.zero
7474
ParseOptions = parseOptions }
7575

7676
let paragraphs =

src/FSharp.Formatting.Markdown/MarkdownBlockParser.fs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -742,8 +742,8 @@ let (|LinkDefinition|_|) s =
742742

743743
let updateCurrentRange lines =
744744
match lines with
745-
| [] -> None
746-
| (_, l) :: _ -> Some(l)
745+
| [] -> MarkdownRange.zero
746+
| (_, l) :: _ -> l
747747

748748
/// Parse a list of lines into a sequence of markdown paragraphs
749749
let rec parseParagraphs (ctx: ParsingContext) (lines: (string * MarkdownRange) list) =
@@ -755,7 +755,7 @@ let rec parseParagraphs (ctx: ParsingContext) (lines: (string * MarkdownRange) l
755755
let frontMatter, (Lines.TrimBlankStart(_, moreLines)) =
756756
if ctx.IsFirst && ctx.AllowYamlFrontMatter then
757757
match lines with
758-
| YamlFrontmatter(yaml, loc, rest) -> Some(YamlFrontmatter(yaml, Some loc)), rest
758+
| YamlFrontmatter(yaml, loc, rest) -> Some(YamlFrontmatter(yaml, loc)), rest
759759
| _ -> None, lines
760760
else
761761
None, lines

0 commit comments

Comments
 (0)