Skip to content

Instantly share code, notes, and snippets.

@MihaZupan
Last active November 5, 2019 15:43
Show Gist options
  • Save MihaZupan/51e6e30542f45e73f70e1fc6636a49b7 to your computer and use it in GitHub Desktop.
Save MihaZupan/51e6e30542f45e73f70e1fc6636a49b7 to your computer and use it in GitHub Desktop.
public const int NewLineWeight = 4;
public static void TrimDocument(MarkdownDocument document, int numberOfCharactersToKeep)
{
TrimObject(document, ref numberOfCharactersToKeep);
}
private static void TrimObject(MarkdownObject obj, ref int charactersAvailable)
{
if (obj is ContainerBlock containerBlock)
{
int i;
for (i = 0; i < containerBlock.Count && charactersAvailable > 0; i++)
{
TrimObject(containerBlock[i], ref charactersAvailable);
}
for (int removeIndex = containerBlock.Count - 1; removeIndex >= i; removeIndex--)
{
containerBlock.RemoveAt(removeIndex);
}
}
else if (obj is ContainerInline containerInline)
{
var inline = containerInline.FirstChild;
while (inline != null && charactersAvailable > 0)
{
TrimObject(inline, ref charactersAvailable);
inline = inline.NextSibling;
}
if (charactersAvailable <= 0)
{
while (inline != null)
{
var next = inline.NextSibling;
inline.Remove();
inline = next;
}
}
}
else if (obj is LeafBlock leafBlock)
{
if (leafBlock.Inline != null)
{
TrimObject(leafBlock.Inline, ref charactersAvailable);
}
ref var lines = ref leafBlock.Lines;
var stringLines = lines.Lines;
if (stringLines != null)
{
int i;
for (i = 0; i < lines.Count && charactersAvailable > 0; i++)
{
TrimStringSlice(ref stringLines[i].Slice, ref charactersAvailable);
charactersAvailable -= NewLineWeight;
}
for (int removeIndex = lines.Count - 1; removeIndex >= i; removeIndex--)
{
lines.RemoveAt(removeIndex);
}
}
charactersAvailable -= NewLineWeight;
}
else if (obj is LeafInline leafInline)
{
if (leafInline is LiteralInline literal)
{
TrimStringSlice(ref literal.Content, ref charactersAvailable);
}
else if (leafInline is CodeInline code)
{
code.Content = TrimString(code.Content, ref charactersAvailable);
}
else if (leafInline is AutolinkInline autoLink)
{
autoLink.Url = TrimString(autoLink.Url, ref charactersAvailable);
}
else if (leafInline is LineBreakInline)
{
charactersAvailable -= NewLineWeight;
}
}
}
private static void TrimStringSlice(ref StringSlice slice, ref int charactersAvailable)
{
if (slice.IsEmpty)
{
return;
}
else if (charactersAvailable <= 0)
{
slice = new StringSlice(null);
}
else
{
var span = slice.Text.AsSpan(slice.Start, slice.Length);
var trimmed = TrimSpan(span, ref charactersAvailable);
slice.End = slice.Start + trimmed.Length - 1;
}
}
private static string TrimString(string text, ref int charactersAvailable)
{
if (string.IsNullOrEmpty(text))
{
return text;
}
else if (charactersAvailable <= 0)
{
return string.Empty;
}
else
{
var span = TrimSpan(text.AsSpan(), ref charactersAvailable);
return span.Length == text.Length ? text : span.ToString();
}
}
private static ReadOnlySpan<char> TrimSpan(ReadOnlySpan<char> span, ref int charactersAvailable)
{
int i = 0;
while (i < span.Length && charactersAvailable > 0)
{
int nextLine = span.Slice(i).IndexOf('\n');
int lineLength = nextLine == -1 ? span.Length - i : nextLine - i;
if (lineLength <= charactersAvailable)
{
i += lineLength;
charactersAvailable -= lineLength;
if (charactersAvailable > NewLineWeight && nextLine != -1)
{
i++;
charactersAvailable -= NewLineWeight;
}
else
{
if (nextLine != -1)
{
charactersAvailable = 0;
}
break;
};
}
else
{
i += charactersAvailable;
charactersAvailable = 0;
}
}
return span.Slice(0, i);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment