Files
ZelWiki/ZelWiki.Engine/WikifierLite.cs
2025-02-24 21:09:37 +08:00

194 lines
7.6 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Text.RegularExpressions;
using ZelWiki.Engine.Function;
using ZelWiki.Models;
namespace ZelWiki.Engine
{
/// <summary>
///
/// </summary>
public class WikifierLite
{
public static string Process(string? unprocessedText)
{
unprocessedText = unprocessedText?.Trim() ?? string.Empty;
if (string.IsNullOrWhiteSpace(unprocessedText))
{
return string.Empty;
}
for (var i = 0; i < 100; i++)
{
var processedMatches = false;
var matchStore = new Dictionary<string, string>();
var content = new WikiString(unprocessedText);
TransformEmoji(content, matchStore);
TransformLinks(content, matchStore);
TransformMarkup(content, matchStore);
foreach (var match in matchStore)
{
processedMatches = true;
content.Replace(match.Key, match.Value);
}
if (!processedMatches)
{
break;
}
unprocessedText = content.ToString();
}
return unprocessedText;
}
private static string StoreMatch(Dictionary<string, string> matchStore, string value)
{
var guid = Guid.NewGuid().ToString();
matchStore.Add(guid, value);
return guid;
}
private static void TransformEmoji(WikiString pageContent, Dictionary<string, string> matchStore)
{
var rgx = new Regex(@"(%%.+?%%)", RegexOptions.IgnoreCase);
var matches = WikiUtility.OrderMatchesByLengthDescending(rgx.Matches(pageContent.ToString()));
foreach (var match in matches)
{
string key = match.Value.Trim().ToLower().Trim('%');
int scale = 100;
var parts = key.Split(',');
if (parts.Length > 1)
{
key = parts[0]; //Image key;
scale = int.Parse(parts[1]); //Image scale.
}
key = $"%%{key}%%";
var emoji = GlobalConfiguration.Emojis.FirstOrDefault(o => o.Shortcut == key);
if (GlobalConfiguration.Emojis.Exists(o => o.Shortcut == key))
{
if (scale != 100 && scale > 0 && scale <= 500)
{
var emojiImage =
$"<img src=\"/file/Emoji/{key.Trim('%')}?Scale={scale}\" alt=\"{emoji?.Name}\" />";
pageContent.Replace(match.Value, StoreMatch(matchStore, emojiImage));
}
else
{
var emojiImage = $"<img src=\"/file/Emoji/{key.Trim('%')}\" alt=\"{emoji?.Name}\" />";
pageContent.Replace(match.Value, StoreMatch(matchStore, emojiImage));
}
}
else
{
pageContent.Replace(match.Value, StoreMatch(matchStore, string.Empty));
}
}
}
/// <summary>
/// 转换单行和多行的基本标记,如粗体、斜体、下划线等
/// </summary>
/// <param name="pageContent"></param>
/// <param name="matchStore"></param>
private static void TransformMarkup(WikiString pageContent, Dictionary<string, string> matchStore)
{
var symbols = WikiUtility.GetApplicableSymbols(pageContent.Value);
foreach (var symbol in symbols)
{
var sequence = new string(symbol, 2);
var escapedSequence = Regex.Escape(sequence);
var rgx = new Regex(@$"{escapedSequence}(.*?){escapedSequence}", RegexOptions.IgnoreCase);
var orderedMatches = WikiUtility.OrderMatchesByLengthDescending(rgx.Matches(pageContent.ToString()));
foreach (var match in orderedMatches)
{
string body = match.Value.Substring(sequence.Length, match.Value.Length - sequence.Length * 2);
var markup = symbol switch
{
'~' => $"<strike>{body}</strike>",
'*' => $"<strong>{body}</strong>",
'_' => $"<u>{body}</u>",
'/' => $"<i>{body}</i>",
'!' => $"<mark>{body}</mark>",
_ => body,
};
pageContent.Replace(match.Value, StoreMatch(matchStore, markup));
}
}
}
/// <summary>
/// 转换链接这些链接可以是内部Wiki链接或外部链接
/// </summary>
/// <param name="pageContent"></param>
/// <param name="matchStore"></param>
private static void TransformLinks(WikiString pageContent, Dictionary<string, string> matchStore)
{
//解析外部链接 [[http://test.net]].
var rgx = new Regex(@"(\[\[http\:\/\/.+?\]\])", RegexOptions.IgnoreCase);
var matches = WikiUtility.OrderMatchesByLengthDescending(rgx.Matches(pageContent.ToString()));
foreach (var match in matches)
{
var keyword = match.Value.Substring(2, match.Value.Length - 4).Trim();
var args = FunctionParser.ParseRawArgumentsAddParenthesis(keyword);
if (args.Count > 1)
{
pageContent.Replace(match.Value, StoreMatch(matchStore, $"<a href=\"{args[0]}\">{args[1]}</a>"));
}
else
{
pageContent.Replace(match.Value, StoreMatch(matchStore, $"<a href=\"{args[0]}\">{args[0]}</a>"));
}
}
//解析外部链接. [[https://test.net]].
rgx = new Regex(@"(\[\[https\:\/\/.+?\]\])", RegexOptions.IgnoreCase);
matches = WikiUtility.OrderMatchesByLengthDescending(rgx.Matches(pageContent.ToString()));
foreach (var match in matches)
{
var keyword = match.Value.Substring(2, match.Value.Length - 4).Trim();
var args = FunctionParser.ParseRawArgumentsAddParenthesis(keyword);
if (args.Count == 1)
{
pageContent.Replace(match.Value, StoreMatch(matchStore, $"<a href=\"{args[0]}\">{args[1]}</a>"));
}
else if (args.Count > 1)
{
pageContent.Replace(match.Value, StoreMatch(matchStore, $"<a href=\"{args[0]}\">{args[0]}</a>"));
}
}
//解析内部链接 [[AboutUs|About Us]].
rgx = new Regex(@"(\[\[.+?\]\])", RegexOptions.IgnoreCase);
matches = WikiUtility.OrderMatchesByLengthDescending(rgx.Matches(pageContent.ToString()));
foreach (var match in matches)
{
var keyword = match.Value.Substring(2, match.Value.Length - 4);
var args = FunctionParser.ParseRawArgumentsAddParenthesis(keyword);
if (args.Count == 1)
{
pageContent.Replace(match.Value,
StoreMatch(matchStore, $"<a href=\"{GlobalConfiguration.BasePath}/{args[0]}\">{args[0]}</a>"));
}
else if (args.Count > 1)
{
pageContent.Replace(match.Value,
StoreMatch(matchStore, $"<a href=\"{GlobalConfiguration.BasePath}/{args[0]}\">{args[1]}</a>"));
}
}
}
}
}