(Data.SyntaxId);
internal string Render()
{
var result = new StringBuilder();
Render(result);
return UtilDoc.StringNull(result.ToString());
}
internal void Render(StringBuilder result)
{
RenderBegin(result);
RenderContent(result);
RenderEnd(result);
}
internal virtual void RenderBegin(StringBuilder result)
{
}
internal virtual void RenderContent(StringBuilder result)
{
foreach (HtmlBase item in List)
{
item.Render(result);
}
}
internal virtual void RenderEnd(StringBuilder result)
{
}
}
///
/// Html tree root.
///
internal class HtmlDoc : HtmlBase
{
public HtmlDoc(Component owner)
: base(owner)
{
}
}
internal class HtmlPage : HtmlBase
{
///
/// Constructor parse html.
///
public HtmlPage(HtmlBase owner, SyntaxBase syntax)
: base(owner, syntax)
{
}
internal override void RenderBegin(StringBuilder result)
{
if (Data.Registry.IsDebug)
{
result.Append("(page)");
}
}
internal override void RenderEnd(StringBuilder result)
{
if (Data.Registry.IsDebug)
{
result.Append("(/page)");
}
}
}
internal class HtmlComment : HtmlBase
{
///
/// Constructor parse html.
///
public HtmlComment(HtmlBase owner, SyntaxComment syntax)
: base(owner, syntax)
{
}
internal override void RenderContent(StringBuilder result)
{
result.Append(Syntax.Text);
}
}
internal class HtmlTitle : HtmlBase
{
///
/// Constructor parse html.
///
public HtmlTitle(HtmlBase owner, SyntaxTitle syntax)
: base(owner, syntax)
{
}
public new SyntaxTitle Syntax => (SyntaxTitle)base.Syntax;
internal override void RenderBegin(StringBuilder result)
{
// For example
result.Append("");
if (Syntax.TitleId != null)
{
var anchor = string.Format("", Syntax.TitleId);
result.Append(anchor);
}
}
internal override void RenderEnd(StringBuilder result)
{
// For example
result.Append("
");
}
}
internal class HtmlParagraph : HtmlBase
{
///
/// Constructor parse html.
///
public HtmlParagraph(HtmlBase owner, SyntaxParagraph syntax)
: base(owner, syntax)
{
}
internal override void RenderBegin(StringBuilder result)
{
if (!Data.Registry.IsDebug && List.Count == 0)
{
// Do not render empty paragraph
return;
}
result.Append("
");
if (Data.Registry.IsDebug)
{
result.Append("(p)");
}
}
internal override void RenderEnd(StringBuilder result)
{
if (!Data.Registry.IsDebug && List.Count == 0)
{
// Do not render empty paragraph
return;
}
if (Data.Registry.IsDebug)
{
result.Append("(/p)");
}
result.Append("
");
}
}
internal class HtmlBulletList : HtmlBase
{
///
/// Constructor parse html.
///
public HtmlBulletList(HtmlBase owner)
: base(owner, null)
{
}
internal override void RenderBegin(StringBuilder result)
{
result.Append("");
}
internal override void RenderEnd(StringBuilder result)
{
result.Append("
");
}
}
internal class HtmlBulletItem : HtmlBase
{
///
/// Constructor parse html.
///
public HtmlBulletItem(HtmlBulletList owner, SyntaxBullet syntax)
: base(owner, syntax)
{
}
internal override void RenderBegin(StringBuilder result)
{
result.Append("");
}
internal override void RenderEnd(StringBuilder result)
{
result.Append("");
}
}
internal class HtmlFont : HtmlBase
{
///
/// Constructor parse html.
///
public HtmlFont(HtmlBase owner, SyntaxFont syntax)
: base(owner, syntax)
{
}
public new SyntaxFont Syntax => (SyntaxFont)base.Syntax;
internal override void RenderBegin(StringBuilder result)
{
switch (Syntax.FontEnum)
{
case MdFontEnum.Bold:
result.Append("");
break;
case MdFontEnum.Italic:
result.Append("");
break;
default:
throw new Exception("Enum unknown!");
}
}
internal override void RenderEnd(StringBuilder result)
{
switch (Syntax.FontEnum)
{
case MdFontEnum.Bold:
result.Append("");
break;
case MdFontEnum.Italic:
result.Append("");
break;
default:
throw new Exception("Enum unknown!");
}
}
}
internal class HtmlLink : HtmlBase
{
///
/// Constructor parse html.
///
public HtmlLink(HtmlBase owner, SyntaxLink syntax)
: base(owner, syntax)
{
}
public new SyntaxLink Syntax => (SyntaxLink)base.Syntax;
internal override void RenderContent(StringBuilder result)
{
result.Append($"{ Syntax.LinkText }");
}
}
internal class HtmlImage : HtmlBase
{
///
/// Constructor parse html.
///
public HtmlImage(HtmlBase owner, SyntaxImage syntax)
: base(owner, syntax)
{
}
public new SyntaxImage Syntax => (SyntaxImage)base.Syntax;
internal override void RenderContent(StringBuilder result)
{
if (Syntax.LinkText == null)
{
result.Append($"{ Syntax.LinkText }");
}
else
{
var fileNameExtension = UtilDoc.StringNull(Path.GetExtension(Syntax.Link));
// Render html image tag only if src file name has an extension.
// For example the image file name "/" would cause the session to navigate.
if (fileNameExtension != null)
{
result.Append($"
");
}
}
}
}
internal class HtmlCode : HtmlBase
{
///
/// Constructor parse html.
///
public HtmlCode(HtmlBase owner, SyntaxCode syntax)
: base(owner, syntax)
{
}
public new SyntaxCode Syntax => (SyntaxCode)base.Syntax;
internal override void RenderContent(StringBuilder result)
{
result.Append(string.Format("", "language-" + Syntax.CodeLanguage));
var codeText = Syntax.CodeText;
if (Data.Registry.IsDebug == false)
{
if (codeText.StartsWith("\r"))
{
codeText = codeText.Substring(1);
}
if (codeText.StartsWith("\n"))
{
codeText = codeText.Substring(1);
}
if (codeText.EndsWith("\n"))
{
codeText = codeText.Substring(0, codeText.Length - 1);
}
if (codeText.EndsWith("\r"))
{
codeText = codeText.Substring(0, codeText.Length - 1);
}
}
// Escape html special chars.
codeText = System.Security.SecurityElement.Escape(codeText);
result.Append(codeText);
result.Append("
");
}
}
internal class HtmlCustomNote : HtmlBase
{
///
/// Constructor parse html.
///
public HtmlCustomNote(HtmlBase owner, SyntaxBase syntax)
: base(owner, syntax)
{
}
internal override void RenderBegin(StringBuilder result)
{
result.Append("");
}
internal override void RenderEnd(StringBuilder result)
{
result.Append("
");
}
}
internal class HtmlCustomYoutube : HtmlBase
{
///
/// Constructor parse html.
///
public HtmlCustomYoutube(HtmlBase owner, SyntaxBase syntax)
: base(owner, syntax)
{
}
internal override void RenderContent(StringBuilder result)
{
string link = ((SyntaxCustomYoutube)Syntax).Link;
string html = $"";
result.Append(html);
}
}
internal class HtmlCustomImage : HtmlBase
{
///
/// Constructor parse html.
///
public HtmlCustomImage(HtmlBase owner, SyntaxBase syntax)
: base(owner, syntax)
{
}
internal override void RenderContent(StringBuilder result)
{
var syntax = ((SyntaxCustomImage)Syntax);
bool isFirst = true; // First image of a series.
bool isLast = true; // Last image of a series.
var previous = syntax.Previous();
if (previous is SyntaxParagraph previousParagraph)
{
if (previousParagraph.List.Count == 1 && previousParagraph.List.First() is SyntaxNewLine)
{
if (previous.Previous() is SyntaxCustomImage)
{
isFirst = false;
}
}
}
var next = syntax.Next();
if (next is SyntaxParagraph nextParagraph)
{
if (nextParagraph.List.Count == 1 && nextParagraph.List.First() is SyntaxNewLine)
{
if (next.Next() is SyntaxCustomImage)
{
isLast = false;
}
}
}
string alt = null;
if (syntax.Title != null)
{
alt = syntax.Title;
}
else
{
if (syntax.Description != null)
{
alt = syntax.Description;
}
}
string html = $"
";
if (syntax.Title != null)
{
html += $"{ syntax.Title }
";
}
if (syntax.Description != null)
{
html += $"{ syntax.Description }
";
}
if (syntax.Href != null)
{
html = $"{ html }";
}
html = $"{ html }
"; // TODO new class Render
if (isFirst)
{
html = "" + html;
}
if (isLast)
{
html = html + "
";
}
result.Append(html);
}
}
internal class HtmlContent : HtmlBase
{
///
/// Constructor parse html.
///
public HtmlContent(HtmlBase owner, SyntaxBase syntax)
: base(owner, syntax)
{
}
internal override void RenderContent(StringBuilder result)
{
result.Append(Syntax.Text);
}
}
internal static class UtilParse
{
///
/// Returns current text parse index.
///
public static int ParseIndex(MdPage owner)
{
var result = 0;
MdTokenBase token = (MdTokenBase)owner.Data.Last(isOrDefault: true)?.Component();
if (token != null)
{
result = token.IndexEnd + 1;
}
return result;
}
///
/// Create syntax component of type createType and add it to owner.
///
/// Tree to add new syntax component.
/// Syntax component to reference (copy).
/// Returns new syntax component.
internal static SyntaxBase Create(SyntaxRegistry registry, SyntaxBase owner, SyntaxBase syntax, Type createType)
{
return registry.TypeList[createType].Create(owner, syntax);
}
///
/// Create new syntax component of type syntax and add it to owner.
///
internal static SyntaxBase Create(SyntaxRegistry registry, SyntaxBase owner, SyntaxBase syntax)
{
return Create(registry, owner, syntax, syntax.GetType());
}
///
/// Returns token of currently parsed syntax.
///
internal static MdTokenBase ParseOneToken(SyntaxBase syntax)
{
var result = syntax.TokenBegin;
var last = syntax.Data.Last(isOrDefault: true);
if (last != null)
{
var syntaxLast = (SyntaxBase)syntax.Data.Registry.IdList[last.Id].Component();
result = syntaxLast.TokenEnd;
result = result.Next(syntax.TokenEnd);
}
return result;
}
///
/// Returns true, if line starts with token T. Allows leading spaces.
///
internal static bool ParseOneIsNewLine(MdTokenBase token, out T tokenEnd) where T : MdTokenBase
{
var result = false;
tokenEnd = null;
bool isStart;
Component component = token;
// Leading start or NewLine or Paragraph
var previous = token.Previous();
isStart = previous == null || previous is MdNewLine || previous is MdParagraph;
// Leading Space
if (component is MdSpace)
{
component = component.Next(null);
}
// Token
if (component is T)
{
tokenEnd = (T)component;
}
if (isStart && tokenEnd != null)
{
result = true;
}
return result;
}
///
/// Returns true, if tokenBegin is valid Link. For example https://workplacex.org
///
/// Detected Link.
internal static bool ParseOneIsLink(MdTokenBase tokenBegin, MdTokenBase tokenEnd, out MdTokenBase linkEnd, out string link)
{
bool result = false;
linkEnd = tokenBegin;
MdTokenBase next = tokenBegin;
link = null;
do
{
if (next is MdLink)
{
if (next != tokenBegin)
{
break;
}
}
if (!(next is MdContent || next is MdLink || next is MdSymbol))
{
break;
}
linkEnd = next;
link += next.Text;
result = true;
} while (Component.Next(ref next, tokenEnd));
return result;
}
///
/// Returns true, if tokenBegin contains valid LinkText. For example link description.
///
/// Detected LinkText.
internal static bool ParseOneIsLinkText(MdTokenBase tokenBegin, MdTokenBase tokenEnd, out MdTokenBase linkTextEnd, out string linkText)
{
var result = false;
linkTextEnd = tokenBegin;
MdTokenBase next = tokenBegin;
linkText = null;
do
{
if (!(next is MdContent || next is MdSpace || next is MdLink))
{
break;
}
linkTextEnd = next;
linkText += next.Text;
result = true;
} while (Component.Next(ref next, tokenEnd));
return result;
}
public class ParseOneIsCustomItem
{
public MdTokenBase TokenBegin;
public MdTokenBase TokenEnd;
public string Text;
}
private static bool ParseOneIsCustom(MdTokenBase token, out MdTokenBase tokenEnd, out Dictionary paramList)
{
var result = false;
tokenEnd = token;
paramList = null;
var next = token;
while (next is MdContent)
{
var paramName = next.Text;
next = next.Next();
if (next is MdSymbol symbol && symbol.SymbolEnum == MdSymbolEnum.Equal)
{
next = next.Next();
if (next is MdQuotation quotation && quotation.QuotationEnum == MdQuotationEnum.Double)
{
next = next.Next();
var paramTokenBegin = next;
var paramTokenEnd = next;
StringBuilder value = new StringBuilder();
while (next != null && !(next is MdQuotation quotationEnd && quotationEnd.QuotationEnum == MdQuotationEnum.Double))
{
value.Append(next.Text);
paramTokenEnd = next;
next = next.Next();
}
if (next is MdQuotation)
{
if (paramList == null)
{
paramList = new Dictionary();
}
paramList[paramName] = new ParseOneIsCustomItem { Text = UtilDoc.StringNull(value.ToString() ), TokenBegin = paramTokenBegin, TokenEnd = paramTokenEnd };
tokenEnd = next;
result = true;
}
}
next = next?.Next();
if (next is MdSpace)
{
next = next.Next();
}
}
}
return result;
}
internal static bool ParseOneIsCustom(MdTokenBase token, string commandName, out MdTokenBase tokenEnd, out Dictionary paramList)
{
var result = false;
tokenEnd = null;
paramList = null;
if (ParseOneIsNewLine(token, out var bracket))
{
if (bracket.TextBracket == "(")
{
if (bracket.Next() is MdContent content)
{
if (content.Text == commandName)
{
MdTokenBase next = content.Next();
bool isParamValid = true;
if (next is MdSpace)
{
next = next.Next();
isParamValid = ParseOneIsCustom(next, out var paramEnd, out paramList);
next = paramEnd.Next();
}
if (isParamValid && next is MdBracket bracketEnd)
{
if (bracketEnd.TextBracket == ")")
{
tokenEnd = bracketEnd;
if (paramList == null)
{
paramList = new Dictionary();
}
result = true;
}
}
}
}
}
}
return result;
}
}
internal static class UtilDoc
{
public static void Debug()
{
var textMd = "(Note A=\"";
// Doc
var appDoc = new AppDoc();
appDoc.Data.Registry.IsDebug = true;
new MdPage(appDoc.MdDoc, textMd);
string exceptionText = null;
try
{
appDoc.Parse();
}
catch (Exception exception)
{
exceptionText = exception.Message;
}
// Serialize, deserialize
appDoc.Serialize(out string json);
Component.Deserialize(json);
// Write file Debug.txt
TextDebugWriteToFile(appDoc, exceptionText);
}
private static string TextDebug(string text)
{
return text?.Replace("\r", "\\r").Replace("\n", "\\n");
}
private static void TextDebug(Component component, int level, StringBuilder result)
{
for (int i = 0; i < level; i++)
{
result.Append(" ");
}
string syntaxIdText = null;
if (component is SyntaxBase syntaxId && syntaxId.Data.SyntaxId != null)
{
syntaxIdText = "->" + string.Format("{0:000}", syntaxId.Data.SyntaxId);
}
result.Append("-(" + component.GetType().Name + " " + string.Format("{0:000}", component.Data.Id) + syntaxIdText + ")");
// Token
if (component is MdTokenBase token)
{
result.Append(" Text=\"" + TextDebug(token.Text) + "\";");
}
// Syntax
if (component is SyntaxBase syntax)
{
if (syntax is SyntaxDoc doc)
{
if (doc.OwnerFind().SyntaxDocOne.Data == doc.Data)
{
result.Append(" ParseOne (SyntaxToken)");
}
if (doc.OwnerFind().SyntaxDocTwo.Data == doc.Data)
{
result.Append(" ParseTwo (Block)");
}
if (doc.OwnerFind().SyntaxDocThree.Data == doc.Data)
{
result.Append(" ParseThree (Fold)");
}
if (doc.OwnerFind().SyntaxDocFour.Data == doc.Data)
{
result.Append(" ParseFour (Owner Insert)");
}
if (doc.OwnerFind().SyntaxDocFive.Data == doc.Data)
{
result.Append(" ParseFive (Owner Merge)");
}
}
else
{
result.Append(" Text=\"" + TextDebug(syntax.Text) + "\";");
if (syntax.IsCreateNew)
{
result.Append(" IsCreateNew;");
}
}
}
// Html
if (component is HtmlBase syntaxHtml)
{
result.Append(" Text=\"" + TextDebug(syntaxHtml.Syntax?.Text) + "\";");
}
result.AppendLine();
foreach (var item in component.List)
{
TextDebug(item, level + 1, result);
}
}
public static string TextDebug(Component component)
{
StringBuilder result = new StringBuilder();
TextDebug(component, 0, result);
return StringNull(result.ToString());
}
public static void TextDebugWriteToFile(AppDoc appDoc, string exceptionText = null)
{
string textMd = ((MdPage)appDoc.MdDoc.List.First()).Text;
string result = TextDebug(appDoc);
string textHtml = appDoc.HtmlDoc.Render();
if (exceptionText != null)
{
result += "\r\n\r\n" + exceptionText;
}
result += "\r\n\r\n" + "Md:\r\n";
result += textMd;
result += "\r\n\r\n" + "Html:\r\n";
result += textHtml;
string textMdEscape = textMd.Replace("\r", "\\r").Replace("\n", "\\n");
textMdEscape = textMdEscape.Replace("\"", "\\\"");
string textHtmlEscape = textHtml?.Replace("\"", @"\""");
textHtmlEscape = textHtmlEscape?.Replace("\r", "\\r").Replace("\n", "\\n");
string textCSharp = "list.Add(new Item { TextMd = \"" + textMdEscape + "\", TextHtml = \"" + textHtmlEscape + "\" });";
result += "\r\n\r\n" + "CSharp:\r\n";
result += textCSharp;
File.WriteAllText(@"C:\Temp\Debug.txt", result);
// File.WriteAllText(@"C:\Temp\Debug.html", textHtml);
}
public static void Assert(bool isAssert, string exceptionText)
{
if (!isAssert)
{
throw new Exception(exceptionText);
}
}
public static void Assert(bool isAssert)
{
Assert(isAssert, "Assert!");
}
public static bool IsSubclassOf(Type type, Type typeBase)
{
return type == typeBase || type.IsSubclassOf(typeBase);
}
public static string StringNull(string text)
{
return text == "" ? null : text;
}
}
}