mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-04 14:27:21 +00:00
Added formulas from LaTeX code blocks and buttons to copy and save formulas (#1154)
This commit is contained in:
1 parent
f4a86c2fb8
commit
acc7d09fa2
18 files changed
+943
-314
No files matched your search
@@ -327,7 +327,7 @@ public sealed class IContentExtensionsTests
|
||||
private static IReadOnlyList<MessageFile> FilesOf(IContent content)
|
||||
{
|
||||
content.TryGetMarkdownText(out var markdown);
|
||||
return PlainFileExport.ExtractFiles(markdown, ',');
|
||||
return PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(markdown), ',');
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Tests.Chat;
|
||||
|
||||
/// <summary>
|
||||
/// Checks which code blocks of LaTeX the chat shows as a formula.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public sealed class LatexFormulaTests
|
||||
{
|
||||
[TestCase(@"\frac{a}{b}")]
|
||||
[TestCase(@"\iint_D f(x,y)\,\mathrm{d}x\,\mathrm{d}y")]
|
||||
[TestCase(@"\text{if } x > 0", Description = "Text inside a formula is part of it.")]
|
||||
[TestCase("a^2\n+ b^2\n= c^2", Description = "A formula may take several lines.")]
|
||||
[TestCase(@"\begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix}")]
|
||||
[TestCase("\\begin{align}\n a &= b \\\\\n c &= d\n\\end{align}")]
|
||||
[TestCase(@"f(x) = \begin{cases} 1 & x > 0 \\ 0 & \text{otherwise} \end{cases}")]
|
||||
[TestCase(@"x_{max} = \max_i x_i", Description = "A word inside braces is part of the formula.")]
|
||||
[TestCase(@"\lim_{n \to \infty} \left\{ a_n \right\}")]
|
||||
public void AFormulaIsAFormula(string latex)
|
||||
{
|
||||
Assert.That(LatexFormula.IsFormulaOnly(latex), Is.True);
|
||||
}
|
||||
|
||||
[TestCase("\\documentclass{article}\n\\begin{document}\nHello\n\\end{document}", TestName = "A document")]
|
||||
[TestCase(@"\section{Results} The energy grows.", TestName = "A section")]
|
||||
[TestCase(@"\begin{itemize} \item One \end{itemize}", TestName = "A list")]
|
||||
[TestCase(@"\begin{tabular}{ll} a & b \end{tabular}", TestName = "A table")]
|
||||
[TestCase(@"\includegraphics{plot.png}", TestName = "A figure")]
|
||||
[TestCase("The value $x$ grows.", TestName = "Text with a formula in it")]
|
||||
[TestCase(@"The value \(x\) grows.", TestName = "Text with a formula in parentheses in it")]
|
||||
[TestCase("a^2 + b^2 = c^2\n\nE = mc^2", TestName = "Two formulas apart")]
|
||||
[TestCase("The content.", TestName = "A sentence")]
|
||||
[TestCase("Let x be a number.", TestName = "A sentence with short words")]
|
||||
[TestCase(@"\begin{align", TestName = "An environment which never opens")]
|
||||
public void MoreThanAFormulaIsNoFormula(string latex)
|
||||
{
|
||||
Assert.That(LatexFormula.IsFormulaOnly(latex), Is.False, "MathJax would typeset it all as one formula.");
|
||||
}
|
||||
|
||||
[TestCase("latex", " E = mc^2 ", "E = mc^2")]
|
||||
[TestCase("tex", "E = mc^2", "E = mc^2")]
|
||||
[TestCase("LaTeX", "E = mc^2", "E = mc^2", Description = "The language is read in any case.")]
|
||||
[TestCase("latex", "$$\nE = mc^2\n$$", "E = mc^2", Description = "The markers inside the block are no part of the formula.")]
|
||||
[TestCase("latex", @"\[ E = mc^2 \]", "E = mc^2")]
|
||||
[TestCase("latex", @"\(E = mc^2\)", "E = mc^2")]
|
||||
[TestCase("latex", "$E = mc^2$", "E = mc^2")]
|
||||
[TestCase("math", @"\section{x}", @"\section{x}", Description = "A block of math holds a formula only, whatever it looks like.")]
|
||||
public void TheFormulaOfACodeBlockIsFound(string fenceLanguage, string code, string expected)
|
||||
{
|
||||
var found = LatexFormula.TryFind(fenceLanguage, code, out var formula);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(found, Is.True);
|
||||
Assert.That(found ? code[formula] : null, Is.EqualTo(expected));
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase("latex", "", TestName = "An empty block")]
|
||||
[TestCase("math", "$$ $$", TestName = "Nothing but markers")]
|
||||
[TestCase("latex", @"\section{Results}", TestName = "A section of a document")]
|
||||
[TestCase("python", "E = mc^2", TestName = "Code in another language")]
|
||||
[TestCase("", "E = mc^2", TestName = "A block without a language")]
|
||||
public void ACodeBlockWithoutAFormulaHasNone(string fenceLanguage, string code)
|
||||
{
|
||||
Assert.That(LatexFormula.TryFind(fenceLanguage, code, out _), Is.False);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Tests.Chat;
|
||||
|
||||
/// <summary>
|
||||
/// Checks how the chat cuts a message into the parts it renders: Markdown, and the formulas between them.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public sealed class MarkdownRenderPlannerTests
|
||||
{
|
||||
[Test]
|
||||
public void ATextWithoutFormulasStaysOnePart()
|
||||
{
|
||||
Assert.That(Parts("Hello, **world**."), Is.EqualTo(new[] { "MARKDOWN: Hello, **world**." }));
|
||||
}
|
||||
|
||||
[TestCase("$$", "$$")]
|
||||
[TestCase(@"\[", @"\]")]
|
||||
public void AFormulaOnLinesOfItsOwnBecomesAPartOfItsOwn(string openingMarker, string closingMarker)
|
||||
{
|
||||
var parts = Parts($"The energy follows from\n{openingMarker}\nE = mc^2\n{closingMarker}\nfor every body at rest.");
|
||||
|
||||
Assert.That(parts, Is.EqualTo(new[] { "MARKDOWN: The energy follows from\n", "MATH_BLOCK: E = mc^2", "MARKDOWN: for every body at rest." }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AFormulaInAListIsFoundAsWell()
|
||||
{
|
||||
var parts = Parts("1. The energy:\n $$\n E = mc^2\n $$\n2. The mass");
|
||||
|
||||
Assert.That(parts, Is.EqualTo(new[] { "MARKDOWN: 1. The energy:\n", "MATH_BLOCK: E = mc^2", "MARKDOWN: 2. The mass" }));
|
||||
}
|
||||
|
||||
[TestCase("```")]
|
||||
[TestCase("~~~")]
|
||||
public void AFormulaInACodeBlockIsCode(string fence)
|
||||
{
|
||||
var text = $"Write it like this:\n{fence}\n$$\nE = mc^2\n$$\n{fence}";
|
||||
|
||||
Assert.That(Parts(text), Is.EqualTo(new[] { $"MARKDOWN: {text}" }));
|
||||
}
|
||||
|
||||
[TestCase("```latex")]
|
||||
[TestCase("```tex")]
|
||||
[TestCase("```math")]
|
||||
[TestCase("```LaTeX")]
|
||||
[TestCase("``` latex")]
|
||||
[TestCase("~~~latex")]
|
||||
public void ACodeBlockOfLatexWithNothingButAFormulaBecomesAFormula(string openingFence)
|
||||
{
|
||||
var parts = Parts($"The integral:\n{openingFence}\n\\iint_D f(x,y)\\,\\mathrm{{d}}x\\,\\mathrm{{d}}y\n{openingFence[..3]}\nover the area D.");
|
||||
|
||||
Assert.That(parts, Is.EqualTo(new[] { "MARKDOWN: The integral:\n", @"MATH_BLOCK: \iint_D f(x,y)\,\mathrm{d}x\,\mathrm{d}y", "MARKDOWN: over the area D." }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TheMarkersAroundAFormulaInACodeBlockOfLatexAreNoPartOfIt()
|
||||
{
|
||||
Assert.That(Parts("```latex\n$$\nE = mc^2\n$$\n```"), Is.EqualTo(new[] { "MATH_BLOCK: E = mc^2" }));
|
||||
}
|
||||
|
||||
[TestCase("```latex\n\\documentclass{article}\n\\begin{document}\nHello\n\\end{document}\n```", TestName = "A document")]
|
||||
[TestCase("```latex\nThe value $x$ grows.\n```", TestName = "Text with a formula in it")]
|
||||
[TestCase("```latex\n```", TestName = "An empty block")]
|
||||
[TestCase("```python\nE = mc^2\n```", TestName = "Code in another language")]
|
||||
[TestCase("```latex\nE = mc^2", TestName = "A block the model never closed")]
|
||||
public void ACodeBlockOfLatexWithMoreThanAFormulaStaysCode(string text)
|
||||
{
|
||||
Assert.That(Parts(text), Is.EqualTo(new[] { $"MARKDOWN: {text}" }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EveryFormulaKnowsWhereItsBlockStarts()
|
||||
{
|
||||
var text = "First:\n$$\na = b\n$$\nSecond:\n\\[\nc = d\n\\]\nThird:\n```latex\ne = f\n```\nFourth:\n1. Item\n $$\n g = h\n $$";
|
||||
|
||||
var formulas = MarkdownRenderPlanner.Build(text).Segments.Where(segment => segment.Type is MarkdownRenderSegmentType.MATH_BLOCK).Select(segment => segment.FileStart);
|
||||
|
||||
Assert.That(formulas, Is.EqualTo(new[] { text.IndexOf("$$", StringComparison.Ordinal), text.IndexOf(@"\[", StringComparison.Ordinal), text.IndexOf("```latex", StringComparison.Ordinal), text.IndexOf(" $$", StringComparison.Ordinal) }), "A formula starts with its markers, an indented one with the line of its marker.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MarkdownStartsWhereItsBlockStarts()
|
||||
{
|
||||
var plan = MarkdownRenderPlanner.Build("Before\n$$\na = b\n$$\nAfter");
|
||||
|
||||
Assert.That(plan.Segments.Where(segment => segment.Type is MarkdownRenderSegmentType.MARKDOWN).Select(segment => segment.FileStart - segment.Start), Is.All.Zero);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AFormulaTheModelNeverClosedLeavesEverythingAsMarkdown()
|
||||
{
|
||||
var text = "The first formula:\n$$\na^2 + b^2 = c^2\n$$\nThe second one broke off:\n$$\nE = mc^2";
|
||||
|
||||
Assert.That(Parts(text), Is.EqualTo(new[] { $"MARKDOWN: {text}" }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AnAnswerOfRawHtmlBecomesACodeBlock()
|
||||
{
|
||||
var parts = Parts("<html><body><p>Hi</p></body></html>");
|
||||
|
||||
Assert.That(parts, Is.EqualTo(new[] { $"MARKDOWN: ```html{Environment.NewLine}<html><body><p>Hi</p></body></html>{Environment.NewLine}```" }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TheThinkingOfAModelIsNotShown()
|
||||
{
|
||||
Assert.That(Parts("<think>Let me see.</think>\n\nThe answer."), Is.EqualTo(new[] { "MARKDOWN: The answer." }));
|
||||
}
|
||||
|
||||
[TestCase("")]
|
||||
[TestCase(" \n ")]
|
||||
[TestCase("<think>Still thinking", Description = "A model which is still thinking shows nothing yet.")]
|
||||
public void NothingToShowHasNoParts(string text)
|
||||
{
|
||||
Assert.That(MarkdownRenderPlanner.Build(text), Is.SameAs(MarkdownRenderPlan.EMPTY));
|
||||
}
|
||||
|
||||
private static IEnumerable<string> Parts(string text)
|
||||
{
|
||||
var plan = MarkdownRenderPlanner.Build(text);
|
||||
return plan.Segments.Select(segment => $"{segment.Type}: {segment.GetContent(plan.Source)}");
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Tools;
|
||||
|
||||
@@ -33,20 +34,50 @@ public sealed class ExportableMarkdownTests
|
||||
"<p>100</p>",
|
||||
"```");
|
||||
|
||||
var files = PlainFileExport.ExtractFiles(answer, ',');
|
||||
var files = PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(answer), ',');
|
||||
var foundFiles = Render(answer).Descendants<FencedCodeBlock>().Select(block => ExportableMarkdown.FileAt(files, block.Span.Start));
|
||||
|
||||
Assert.That(foundFiles, Is.EqualTo(new[] { files[1], files[2] }), "The table comes first among the files, but it is no code block.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AFormulaFindsNoFile()
|
||||
public void AFormulaIsNoPartOfTheMarkdown()
|
||||
{
|
||||
var answer = Lines("1. The energy follows from", string.Empty, " $$", " E = mc^2", " $$");
|
||||
|
||||
var formula = Render(answer).Descendants<MathBlock>().Single();
|
||||
var plan = MarkdownRenderPlanner.Build(answer);
|
||||
var formulasOfTheMarkdown = plan.Segments
|
||||
.Where(segment => segment.Type is MarkdownRenderSegmentType.MARKDOWN)
|
||||
.SelectMany(segment => Render(segment.GetContent(plan.Source)).Descendants<MathBlock>());
|
||||
|
||||
Assert.That(ExportableMarkdown.FileAt(PlainFileExport.ExtractFiles(answer, ','), formula.Span.Start), Is.Null, "Markdig reads a formula as a fenced block, but nobody saves a formula as a text file.");
|
||||
Assert.That(formulasOfTheMarkdown, Is.Empty, "The chat renders a formula and its actions itself, so the Markdown around it holds no formula to give a code block's actions to.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EveryFormulaFindsItsFile()
|
||||
{
|
||||
var answer = Lines(
|
||||
"$$",
|
||||
"a = b",
|
||||
"$$",
|
||||
@"\[",
|
||||
"c = d",
|
||||
@"\]",
|
||||
"```latex",
|
||||
"e = f",
|
||||
"```",
|
||||
"1. Item",
|
||||
" $$",
|
||||
" g = h",
|
||||
" $$");
|
||||
|
||||
var plan = MarkdownRenderPlanner.Build(answer);
|
||||
var files = PlainFileExport.ExtractFiles(plan, ',');
|
||||
var foundFiles = plan.Segments
|
||||
.Where(segment => segment.Type is MarkdownRenderSegmentType.MATH_BLOCK)
|
||||
.Select(segment => ExportableMarkdown.FileAt(files, segment.FileStart)?.Format);
|
||||
|
||||
Assert.That(foundFiles, Is.EqualTo(Enumerable.Repeat<FileExportFormat?>(FileExportFormat.LATEX, 4)), "The chat looks a formula's file up where its block starts, so that the formula gets the button to save it.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -56,7 +87,7 @@ public sealed class ExportableMarkdownTests
|
||||
|
||||
var block = Render(answer).Descendants<FencedCodeBlock>().Single();
|
||||
|
||||
Assert.That(ExportableMarkdown.FileAt(PlainFileExport.ExtractFiles(answer, ','), block.Span.Start), Is.Null, "The file would end wherever the answer broke off.");
|
||||
Assert.That(ExportableMarkdown.FileAt(PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(answer), ','), block.Span.Start), Is.Null, "The file would end wherever the answer broke off.");
|
||||
}
|
||||
|
||||
private static MarkdownDocument Render(string answer) => Markdig.Markdown.Parse(answer, Markdown.CHAT_MARKDOWN_PIPELINE);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Tools;
|
||||
|
||||
using Markdig.Syntax;
|
||||
@@ -23,7 +24,7 @@ public sealed class PlainFileExportTests
|
||||
[Test]
|
||||
public void AnAnswerMadeOfOneWebPageOffersThatPage()
|
||||
{
|
||||
var files = PlainFileExport.ExtractFiles(Lines("```html", PAGE, "```"), ',');
|
||||
var files = PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(Lines("```html", PAGE, "```")), ',');
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
@@ -39,7 +40,7 @@ public sealed class PlainFileExportTests
|
||||
{
|
||||
var answer = Lines("Here is your page:", string.Empty, "```html", PAGE, "```", string.Empty, "Save it and open it in your browser.");
|
||||
|
||||
var files = PlainFileExport.ExtractFiles(answer, ',');
|
||||
var files = PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(answer), ',');
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
@@ -54,7 +55,7 @@ public sealed class PlainFileExportTests
|
||||
[TestCase("html title=\"index.html\"", FileExportFormat.HTML, Description = "Whatever follows the language is an argument, not part of it.")]
|
||||
public void ACodeBlockIsOfferedInTheFormatItsLanguageNames(string infoString, FileExportFormat expectedFormat)
|
||||
{
|
||||
var files = PlainFileExport.ExtractFiles(Lines($"```{infoString}", "The content.", "```"), ',');
|
||||
var files = PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(Lines($"```{infoString}", "The content.", "```")), ',');
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
@@ -67,7 +68,7 @@ public sealed class PlainFileExportTests
|
||||
[Test]
|
||||
public void ATildeFenceIsOfferedAsWell()
|
||||
{
|
||||
var files = PlainFileExport.ExtractFiles(Lines("~~~latex", @"\section{Results}", "~~~"), ',');
|
||||
var files = PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(Lines("~~~latex", @"\section{Results}", "~~~")), ',');
|
||||
|
||||
Assert.That(files.Select(file => file.Format), Is.EqualTo(new[] { FileExportFormat.LATEX }));
|
||||
}
|
||||
@@ -79,7 +80,7 @@ public sealed class PlainFileExportTests
|
||||
[TestCase("```foo", "foo (.txt)", Description = "A language AI Studio does not know still names the block.")]
|
||||
public void ACodeBlockInAnyOtherLanguageIsOfferedAsCode(string openingFence, string expectedName)
|
||||
{
|
||||
var files = PlainFileExport.ExtractFiles(Lines(openingFence, "print(42)", openingFence[..3]), ',');
|
||||
var files = PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(Lines(openingFence, "print(42)", openingFence[..3])), ',');
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
@@ -96,7 +97,7 @@ public sealed class PlainFileExportTests
|
||||
[TestCase("Just a few words the model put into a block.", "Text (.txt)", Description = "A block whose language is not clear stays text.")]
|
||||
public void ACodeBlockWithoutALanguageIsOfferedInTheLanguageOfItsCode(string code, string expectedName)
|
||||
{
|
||||
var files = PlainFileExport.ExtractFiles(Lines("```", code, "```"), ',');
|
||||
var files = PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(Lines("```", code, "```")), ',');
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
@@ -110,7 +111,7 @@ public sealed class PlainFileExportTests
|
||||
[Test]
|
||||
public void ACodeBlockMarkedAsTextStaysText()
|
||||
{
|
||||
var files = PlainFileExport.ExtractFiles(Lines("```text", "using System;", string.Empty, "Console.WriteLine(\"Hello\");", "```"), ',');
|
||||
var files = PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(Lines("```text", "using System;", string.Empty, "Console.WriteLine(\"Hello\");", "```")), ',');
|
||||
|
||||
Assert.That(files.Select(file => file.ToName()), Is.EqualTo(new[] { "Text (.txt)" }), "Whoever marks a block as text gets a text file, whatever its code looks like.");
|
||||
}
|
||||
@@ -118,19 +119,41 @@ public sealed class PlainFileExportTests
|
||||
[Test]
|
||||
public void AWebPageWithoutALanguageIsOfferedAsAWebPage()
|
||||
{
|
||||
var files = PlainFileExport.ExtractFiles(Lines("```", "<div class=\"card\">", " <p>Hello</p>", "</div>", "```"), ',');
|
||||
var files = PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(Lines("```", "<div class=\"card\">", " <p>Hello</p>", "</div>", "```")), ',');
|
||||
|
||||
Assert.That(files.Select(file => file.Format), Is.EqualTo(new[] { FileExportFormat.HTML }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AFormulaIsNoCodeBlock()
|
||||
[TestCase("$$", "$$")]
|
||||
[TestCase(@"\[", @"\]")]
|
||||
[TestCase("```latex", "```", Description = "A code block of LaTeX which holds nothing but a formula is a formula, too.")]
|
||||
public void AFormulaIsOfferedAsLatex(string openingMarker, string closingMarker)
|
||||
{
|
||||
var answer = Lines("The energy follows from", string.Empty, "$$", "E = mc^2", "$$", string.Empty, "for every body at rest.");
|
||||
var answer = Lines("The energy follows from", string.Empty, openingMarker, "E = mc^2", closingMarker, string.Empty, "for every body at rest.");
|
||||
|
||||
var files = PlainFileExport.ExtractFiles(answer, ',');
|
||||
var files = PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(answer), ',');
|
||||
|
||||
Assert.That(files, Is.Empty, "Markdig reads a formula as a fenced block, but nobody saves a formula as a text file.");
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(files, Has.Count.EqualTo(1));
|
||||
Assert.That(files[0].ToName(), Is.EqualTo("LaTeX (.tex)"));
|
||||
Assert.That(files[0].Content, Is.EqualTo(Lines(@"\[", "E = mc^2", @"\]")), "Between these markers, the formula can be pasted into a document as it is.");
|
||||
Assert.That(files[0].Position, Is.EqualTo(answer.IndexOf(openingMarker, StringComparison.Ordinal)), "A formula finds its file where its markers start.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AFormulaIsCountedAmongTheCodeBlocks()
|
||||
{
|
||||
var answer = Lines("# Energy", string.Empty, "$$", "E = mc^2", "$$", string.Empty, "```python", "print(1)", "```");
|
||||
|
||||
var files = PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(answer), ',');
|
||||
|
||||
Assert.That(files.Select(file => (file.Ordinal, file.Caption, file.Format)), Is.EqualTo(new[]
|
||||
{
|
||||
(1, "Energy", FileExportFormat.LATEX),
|
||||
(2, "Energy", FileExportFormat.SOURCE_CODE),
|
||||
}));
|
||||
}
|
||||
|
||||
[TestCase("```html", "<html><body><p>The answer broke off here", TestName = "Half a web page")]
|
||||
@@ -139,7 +162,7 @@ public sealed class PlainFileExportTests
|
||||
[TestCase("```", "The answer broke off here", TestName = "Half a text")]
|
||||
public void ACodeBlockTheModelNeverClosedIsNotOffered(string openingFence, string content)
|
||||
{
|
||||
var files = PlainFileExport.ExtractFiles(Lines("The answer starts normally.", string.Empty, openingFence, content), ',');
|
||||
var files = PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(Lines("The answer starts normally.", string.Empty, openingFence, content)), ',');
|
||||
|
||||
Assert.That(files, Is.Empty, "The file would end wherever the answer broke off.");
|
||||
}
|
||||
@@ -170,7 +193,7 @@ public sealed class PlainFileExportTests
|
||||
"print('Third block')",
|
||||
"```");
|
||||
|
||||
var files = PlainFileExport.ExtractFiles(answer, ',');
|
||||
var files = PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(answer), ',');
|
||||
|
||||
Assert.That(files.Select(file => (file.Ordinal, file.Caption, file.Format)), Is.EqualTo(new[]
|
||||
{
|
||||
@@ -197,7 +220,7 @@ public sealed class PlainFileExportTests
|
||||
"print(100)",
|
||||
"```");
|
||||
|
||||
var files = PlainFileExport.ExtractFiles(answer, ',');
|
||||
var files = PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(answer), ',');
|
||||
|
||||
Assert.That(files.Select(file => file.Position), Is.EqualTo(new[] { answer.IndexOf("| Quarter", StringComparison.Ordinal), answer.IndexOf("```python", StringComparison.Ordinal) }));
|
||||
}
|
||||
@@ -213,7 +236,7 @@ public sealed class PlainFileExportTests
|
||||
" ```",
|
||||
"2. Run your script.");
|
||||
|
||||
var files = PlainFileExport.ExtractFiles(answer, ',');
|
||||
var files = PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(answer), ',');
|
||||
|
||||
Assert.That(files.Select(file => file.Position), Is.EqualTo(new[] { answer.IndexOf("```bash", StringComparison.Ordinal) }));
|
||||
}
|
||||
@@ -244,16 +267,16 @@ public sealed class PlainFileExportTests
|
||||
"fn main() {}",
|
||||
"```");
|
||||
|
||||
var files = PlainFileExport.ExtractFiles(answer, ',');
|
||||
var files = PlainFileExport.ExtractFiles(MarkdownRenderPlanner.Build(answer), ',');
|
||||
var partStart = answer.LastIndexOf("$$", StringComparison.Ordinal) + "$$".Length + Environment.NewLine.Length;
|
||||
var part = Markdig.Markdown.Parse(answer[partStart..], Markdown.CHAT_MARKDOWN_PIPELINE);
|
||||
var blockOfThePart = part.Descendants<FencedCodeBlock>().Single();
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(files, Has.Count.EqualTo(2));
|
||||
Assert.That(partStart + blockOfThePart.Span.Start, Is.EqualTo(files[1].Position));
|
||||
Assert.That(files[1].ToName(), Is.EqualTo("Rust (.rs)"));
|
||||
Assert.That(files, Has.Count.EqualTo(3), "The formula between the two code blocks is a file, too.");
|
||||
Assert.That(partStart + blockOfThePart.Span.Start, Is.EqualTo(files[2].Position));
|
||||
Assert.That(files[2].ToName(), Is.EqualTo("Rust (.rs)"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user