Skip to content

Commit c1437fd

Browse files
github-actions[bot]Repo AssistCopilot
authored
[Repo Assist] Split MarkdownTableParser.fs out of MarkdownBlockParser.fs (#1075)
* Split MarkdownTableParser.fs out of MarkdownBlockParser.fs Extract pipe-table and emacs-table parsing active patterns into a new MarkdownTableParser.fs (196 lines). MarkdownBlockParser.fs shrinks from 958 → 760 lines. MarkdownBlockParser now opens FSharp.Formatting.Markdown.TableParser to access PipeTableBlock and EmacsTableBlock. Part of #1022 (split large files). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci: trigger checks --------- Co-authored-by: Repo Assist <github-actions@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>
1 parent 13c8d55 commit c1437fd

4 files changed

Lines changed: 214 additions & 199 deletions

File tree

RELEASE_NOTES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Refactored
66
* 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+
* Split pipe-table and Emacs-table parsing out of `MarkdownBlockParser.fs` into a new `MarkdownTableParser.fs` (196 lines), reducing `MarkdownBlockParser.fs` from 958 to 760 lines. [#1022](https://github.com/fsprojects/FSharp.Formatting/issues/1022)
78

89
### Added
910
* 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)

src/FSharp.Formatting.Markdown/FSharp.Formatting.Markdown.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
</Compile>
1313
<Compile Include="MarkdownModel.fs" />
1414
<Compile Include="MarkdownInlineParser.fs" />
15+
<Compile Include="MarkdownTableParser.fs" />
1516
<Compile Include="MarkdownBlockParser.fs" />
1617
<Compile Include="MarkdownUtils.fs" />
1718
<Compile Include="HtmlFormatting.fs" />

src/FSharp.Formatting.Markdown/MarkdownBlockParser.fs

Lines changed: 1 addition & 199 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ open FSharp.Patterns
1313
open FSharp.Collections
1414
open FSharp.Formatting.Common
1515
open FSharp.Formatting.Markdown.InlineParser
16+
open FSharp.Formatting.Markdown.TableParser
1617

1718
// --------------------------------------------------------------------------------------
1819
// Parsing of Markdown - second part handles paragraph-level formatting (headings, etc.)
@@ -394,205 +395,6 @@ let rec (|ListItems|_|) prevSimple lines =
394395
| _ -> Some([ indent, info ], takenLines, rest)
395396
| _ -> None
396397

397-
398-
// Code for parsing pipe tables
399-
400-
// Splits table row into deliminated parts escaping inline code and latex
401-
let rec pipeTableFindSplits (delim: char array) (line: char list) =
402-
let cLstToStr (x: char list) =
403-
x |> Array.ofList |> System.String.Concat
404-
405-
let rec ptfs delim line =
406-
match line with
407-
| DelimitedLatexDisplayMath [ '$'; '$' ] (_body, rest) -> ptfs delim rest
408-
| DelimitedLatexInlineMath [ '$' ] (_body, rest) -> ptfs delim rest
409-
| List.DelimitedWith [ '`'; ' ' ] [ ' '; '`' ] (_body, rest, _s, _e) -> ptfs delim rest
410-
| List.DelimitedNTimes '`' (_body, rest, _s, _e) -> ptfs delim rest
411-
| x :: rest when Array.exists ((=) x) delim -> Some rest
412-
| '\\' :: _ :: rest
413-
| _ :: rest -> ptfs delim rest
414-
| [] -> None
415-
416-
let rest = ptfs delim line
417-
418-
match rest with
419-
| None -> [ cLstToStr line ]
420-
| Some _x when List.isEmpty line -> [ "" ]
421-
| Some x ->
422-
let chunkSize = List.length line - List.length x - 1
423-
424-
cLstToStr (Seq.take chunkSize line |> Seq.toList) :: pipeTableFindSplits delim x
425-
426-
427-
428-
429-
430-
/// Recognizes alignment specified in the passed separator line.
431-
let (|TableCellSeparator|_|) =
432-
function
433-
| StringPosition.StartsAndEndsWith (":", ":") (StringPosition.EqualsRepeated("-", MarkdownRange.zero)) ->
434-
Some(AlignCenter)
435-
| StringPosition.StartsWith ":" (StringPosition.EqualsRepeated("-", MarkdownRange.zero)) -> Some(AlignLeft)
436-
| StringPosition.StartsAndEndsWith ("", ":") (StringPosition.EqualsRepeated("-", MarkdownRange.zero)) ->
437-
Some(AlignRight)
438-
| StringPosition.EqualsRepeated("-", MarkdownRange.zero) -> Some(AlignDefault)
439-
| _ -> None
440-
441-
/// Recognizes row of pipe table.
442-
/// The function takes number of expected columns and array of delimiters.
443-
/// Returns list of strings between delimiters.
444-
let (|PipeTableRow|_|) (size: int option) delimiters (line: string, n: MarkdownRange) =
445-
let parts =
446-
pipeTableFindSplits delimiters (line.ToCharArray() |> Array.toList)
447-
|> List.toArray
448-
|> Array.map (fun s -> (s.Trim(), n))
449-
450-
let n = parts.Length
451-
452-
let m = size |> Option.defaultValue 1
453-
454-
let x =
455-
if String.IsNullOrEmpty(fst parts.[0]) && n > m then
456-
1
457-
else
458-
0
459-
460-
let y =
461-
if String.IsNullOrEmpty(fst parts.[n - 1]) && n - x > m then
462-
n - 2
463-
else
464-
n - 1
465-
466-
if n = 1 || (size.IsSome && y - x + 1 <> m) then
467-
None
468-
else
469-
Some(parts.[x..y] |> Array.toList)
470-
471-
472-
/// Recognizes separator row of pipe table.
473-
/// Returns list of alignments.
474-
let (|PipeSeparatorRow|_|) size =
475-
function
476-
| PipeTableRow size [| '|'; '+' |] parts ->
477-
let alignments = parts |> List.choose (|TableCellSeparator|_|)
478-
479-
if parts.Length <> alignments.Length then
480-
None
481-
else
482-
(Some alignments)
483-
| _ -> None
484-
485-
/// Recognizes pipe table
486-
let (|PipeTableBlock|_|) input =
487-
let rec getTableRows size acc takenLinesAcc lines =
488-
match lines with
489-
| (PipeTableRow size [| '|' |] columns) as takenLine :: rest ->
490-
getTableRows size (List.map (fun l -> [ l ]) columns :: acc) (takenLine :: takenLinesAcc) rest
491-
| rest -> (List.rev acc, List.rev takenLinesAcc, rest)
492-
493-
match input with
494-
| (PipeSeparatorRow None alignments) as takenLine :: rest ->
495-
let rows, takenLines, others = getTableRows (Some alignments.Length) [] [] rest
496-
497-
Some((None, alignments, rows), takenLine :: takenLines, others)
498-
| ((PipeTableRow None [| '|' |] headers) as takenLine) :: rest ->
499-
match rest with
500-
| ((PipeSeparatorRow (Some headers.Length) alignments) as takenLine2) :: rest ->
501-
let rows, takenLines, others = getTableRows (Some headers.Length) [] [] rest
502-
503-
let header_paragraphs = headers |> List.map (fun l -> [ l ])
504-
Some((Some(header_paragraphs), alignments, rows), takenLine :: takenLine2 :: takenLines, others)
505-
| _ -> None
506-
| _ -> None
507-
508-
// Code for parsing emacs tables
509-
510-
/// Recognizes one line of emacs table. It can be line with content or separator line.
511-
/// The function takes positions of grid columns (if known) and expected grid separator.
512-
/// Passed function is used to check whether all parts within grid are valid.
513-
/// Retuns tuple (position of grid columns, text between grid columns).
514-
let (|EmacsTableLine|_|)
515-
(grid: int array option)
516-
(c: char)
517-
(check: string * MarkdownRange -> bool)
518-
(line: string, _n: MarkdownRange)
519-
=
520-
let p =
521-
grid
522-
|> Option.defaultValue (Array.FindAll([| 0 .. line.Length - 1 |], (fun i -> line.[i] = c)))
523-
524-
let n = p.Length - 1
525-
526-
if n < 2 || line.Length <= p.[n] || Array.exists (fun i -> line.[i] <> c) p then
527-
None
528-
else
529-
let parts =
530-
[ 1..n ]
531-
|> List.map (fun i ->
532-
let rng =
533-
{ StartLine = n
534-
StartColumn = 0
535-
EndLine = n
536-
EndColumn = p.[i] - p.[i - 1] - 1 }
537-
538-
line.Substring(p.[i - 1] + 1, p.[i] - p.[i - 1] - 1), rng)
539-
540-
if List.forall check parts then Some(p, parts) else None
541-
542-
/// Recognizes emacs table
543-
let (|EmacsTableBlock|_|) (lines) =
544-
let isCellSep s =
545-
match s with
546-
| StringPosition.EqualsRepeated ("-", MarkdownRange.zero) _ -> true
547-
| _ -> false
548-
549-
let isAlignedCellSep = (|TableCellSeparator|_|) >> Option.isSome
550-
551-
let isHeadCellSep s =
552-
match s with
553-
| StringPosition.EqualsRepeated ("=", MarkdownRange.zero) _ -> true
554-
| _ -> false
555-
556-
let isText (_s: string, _n: MarkdownRange) = true
557-
558-
match lines with
559-
| ((EmacsTableLine None '+' isAlignedCellSep (grid, parts)) as takenLine) :: rest ->
560-
let alignments = List.choose (|TableCellSeparator|_|) parts
561-
// iterate over rows and go from state to state
562-
// headers - the content of head row (initially none)
563-
// prevRow - content of the processed rows
564-
// cur - list of paragraphs in the current row (list of empty lists after each separator line)
565-
// flag indicates whether current row is empty (similar to List.forall (List.isEmpty) cur)
566-
let emptyCur = List.replicate<(string * MarkdownRange) list> (grid.Length - 1) []
567-
568-
let rec loop
569-
flag
570-
takenLines2
571-
headers
572-
(prevRows: (string * MarkdownRange) list list list)
573-
(cur: (string * MarkdownRange) list list)
574-
lines
575-
=
576-
match lines with
577-
| ((EmacsTableLine (Some grid) '|' isText (_, parts)) as takenLine2) :: others ->
578-
loop
579-
false
580-
(takenLine2 :: takenLines2)
581-
headers
582-
prevRows
583-
(List.zip parts cur |> List.map (fun ((h, n), t) -> (h.TrimEnd(), n) :: t))
584-
others
585-
| ((EmacsTableLine (Some grid) '+' isCellSep _) as takenLine2) :: others ->
586-
loop true (takenLine2 :: takenLines2) headers (List.map (List.rev) cur :: prevRows) emptyCur others
587-
| ((EmacsTableLine (Some grid) '+' isHeadCellSep _) as takenLine2) :: others when Option.isNone headers ->
588-
loop true (takenLine2 :: takenLines2) (Some(List.map (List.rev) cur)) prevRows emptyCur others
589-
| others when flag ->
590-
Some((headers, alignments, List.rev prevRows), takenLine :: List.rev takenLines2, others)
591-
| _ -> None
592-
593-
loop true [] None [] emptyCur rest
594-
| _ -> None
595-
596398
/// Recognizes a start of a blockquote
597399
let private blockquoteRegex =
598400
Regex(

0 commit comments

Comments
 (0)