Forge
csharp4405de34
1#nullable enable
2
3namespace CascadeIDE.Services.Intercom;
4
5/// <summary>Разбиение markdown на prose и fenced code (ADR 0129 §5).</summary>
6internal static class MarkdownProseSegments
7{
8 public static IEnumerable<string> EnumerateProse(string markdown)
9 {
10 if (string.IsNullOrEmpty(markdown))
11 yield break;
12
13 var i = 0;
14 var inFence = false;
15 while (i < markdown.Length)
16 {
17 if (IsFenceOpenAt(markdown, i))
18 {
19 var close = FindFenceClose(markdown, i + 3);
20 if (close < 0)
21 yield break;
22
23 i = close + 3;
24 inFence = !inFence;
25 continue;
26 }
27
28 if (inFence)
29 {
30 i++;
31 continue;
32 }
33
34 var nextFence = FindNextFenceOpen(markdown, i);
35 var end = nextFence < 0 ? markdown.Length : nextFence;
36 if (end > i)
37 yield return markdown[i..end];
38
39 i = end;
40 }
41 }
42
43 private static bool IsFenceOpenAt(string text, int index)
44 {
45 if (index + 2 >= text.Length)
46 return false;
47 if (text[index] != '`' || text[index + 1] != '`' || text[index + 2] != '`')
48 return false;
49
50 if (index > 0 && text[index - 1] == '`')
51 return false;
52
53 return true;
54 }
55
56 private static int FindNextFenceOpen(string text, int start)
57 {
58 for (var i = start; i + 2 < text.Length; i++)
59 {
60 if (IsFenceOpenAt(text, i))
61 return i;
62 }
63
64 return -1;
65 }
66
67 private static int FindFenceClose(string text, int afterOpen)
68 {
69 for (var i = afterOpen; i + 2 < text.Length; i++)
70 {
71 if (text[i] != '`' || text[i + 1] != '`' || text[i + 2] != '`')
72 continue;
73
74 if (i > afterOpen && text[i - 1] == '`')
75 continue;
76
77 return i;
78 }
79
80 return -1;
81 }
82}
83
View only · write via MCP/CIDE