in src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs [141:255]
private static void ProcessCodeBlock(
StringLineGroup lines,
string language,
EnhancedCodeBlock codeBlock,
ParserContext context)
{
string argsString;
if (codeBlock.Arguments == null)
argsString = "";
else if (codeBlock.Info?.IndexOf('{') == -1)
argsString = codeBlock.Arguments ?? "";
else
{
// if the code block starts with {code-block} and is followed by a language, we need to skip the language
var parts = codeBlock.Arguments.Split();
argsString = parts.Length > 1 && CodeBlock.Languages.Contains(parts[0])
? string.Join(" ", parts[1..])
: codeBlock.Arguments;
}
var codeBlockArgs = CodeBlockArguments.Default;
if (!CodeBlockArguments.TryParse(argsString, out var codeArgs))
codeBlock.EmitError($"Unable to parse code block arguments: {argsString}. Valid arguments are {CodeBlockArguments.KnownKeysString}.");
else
codeBlockArgs = codeArgs;
var callOutIndex = 0;
var originatingLine = 0;
for (var index = 0; index < lines.Lines.Length; index++)
{
originatingLine++;
var line = lines.Lines[index];
if (index == 0 && language == "console")
{
codeBlock.ApiCallHeader = line.ToString();
var s = new StringSlice("");
lines.Lines[index] = new StringLine(ref s);
continue;
}
var span = line.Slice.AsSpan();
if (codeBlockArgs.UseSubstitutions)
{
if (span.ReplaceSubstitutions(context.YamlFrontMatter?.Properties, context.Build.Collector, out var frontMatterReplacement))
{
var s = new StringSlice(frontMatterReplacement);
lines.Lines[index] = new StringLine(ref s);
span = lines.Lines[index].Slice.AsSpan();
}
if (span.ReplaceSubstitutions(context.Substitutions, context.Build.Collector, out var globalReplacement))
{
var s = new StringSlice(globalReplacement);
lines.Lines[index] = new StringLine(ref s);
span = lines.Lines[index].Slice.AsSpan();
}
}
if (codeBlock.OpeningFencedCharCount > 3)
continue;
if (codeBlockArgs.UseCallouts)
{
List<CallOut> callOuts = [];
var hasClassicCallout = span.IndexOf("<") > 0 && span.LastIndexOf(">") == span.Length - 1;
if (hasClassicCallout)
{
var matchClassicCallout = CallOutParser.CallOutNumber().EnumerateMatches(span);
callOuts.AddRange(
EnumerateAnnotations(matchClassicCallout, ref span, ref callOutIndex, originatingLine, false)
);
}
// only support magic callouts for smaller line lengths
if (callOuts.Count == 0 && span.Length < 200)
{
var matchInline = CallOutParser.MathInlineAnnotation().EnumerateMatches(span);
callOuts.AddRange(
EnumerateAnnotations(matchInline, ref span, ref callOutIndex, originatingLine, true)
);
}
codeBlock.CallOuts.AddRange(callOuts);
}
}
//update string slices to ignore call outs
if (codeBlock.CallOuts.Count > 0)
{
var callouts = codeBlock.CallOuts.Aggregate(new Dictionary<int, CallOut>(), (acc, curr) =>
{
if (acc.TryAdd(curr.Line, curr))
return acc;
if (acc[curr.Line].SliceStart > curr.SliceStart)
acc[curr.Line] = curr;
return acc;
});
foreach (var callout in callouts.Values)
{
var line = lines.Lines[callout.Line - 1];
var newSpan = line.Slice.AsSpan()[..callout.SliceStart];
var s = new StringSlice(newSpan.ToString());
lines.Lines[callout.Line - 1] = new StringLine(ref s);
}
}
var inlineAnnotations = codeBlock.CallOuts.Count(c => c.InlineCodeAnnotation);
var classicAnnotations = codeBlock.CallOuts.Count - inlineAnnotations;
if (inlineAnnotations > 0 && classicAnnotations > 0)
codeBlock.EmitError("Both inline and classic callouts are not supported");
if (inlineAnnotations > 0)
codeBlock.InlineAnnotations = true;
}