Merge MVP features #1

Merged
dominic merged 16 commits from prototype into master 2026-04-24 18:48:16 +00:00
3 changed files with 113 additions and 1 deletions
Showing only changes of commit 9a3f91e41c - Show all commits

View File

@ -0,0 +1,28 @@
using System.Xml.Linq;
using XNeedle.Parser.Elements;
namespace XNeedle.Transformation.Bangs
{
public class AfterBang : BangOperation
{
public override string Name => "after";
public override Context Execute(Context ctx, IEnumerable<string> arguments)
{
if (ctx.Node == null)
{
throw new ArgumentNullException("!after needs a reachable node");
}
if (arguments.Count() < 1)
{
throw new ArgumentException("!after needs at least the tag name as argument.");
}
var tagname = arguments.ElementAt(0);
var element = new XElement(tagname, "");
ctx.Node.AddAfterSelf(element);
return new Context{ Node = element, SLoc = ctx.SLoc.AsSynthetic(), XPathSelector = tagname};
}
}
}

View File

@ -0,0 +1,28 @@
using System.Xml.Linq;
using XNeedle.Parser.Elements;
namespace XNeedle.Transformation.Bangs
{
public class BeforeBang : BangOperation
{
public override string Name => "before";
public override Context Execute(Context ctx, IEnumerable<string> arguments)
{
if (ctx.Node == null)
{
throw new ArgumentNullException("!before needs a reachable node");
}
if (arguments.Count() < 1)
{
throw new ArgumentException("!before needs at least the tag name as argument.");
}
var tagname = arguments.ElementAt(0);
var element = new XElement(tagname, "");
ctx.Node.AddBeforeSelf(element);
return new Context{ Node = element, SLoc = ctx.SLoc.AsSynthetic(), XPathSelector = tagname};
}
}
}

View File

@ -113,7 +113,7 @@ namespace XNeedle.Transformation
{
if (ctx.Node == null)
{
throw new ArgumentNullException("!seap needs a reachable node");
throw new ArgumentNullException("!swap needs a reachable node");
}
var a_node = ctx.Node.XPathSelectElement(a_select);
@ -131,5 +131,61 @@ namespace XNeedle.Transformation
return "";
}
public static string remove(Context ctx, string select)
{
if (ctx.Node == null)
{
throw new ArgumentNullException("!remove needs a reachable node");
}
// Use ToList to make the lazy/generator enumeration concrete before applying operations
var nodes = ctx.Node.XPathSelectElements(select).ToList();
foreach (var node in nodes)
{
node.Remove();
}
return "";
}
public static string rename(Context ctx, string newName)
{
if (ctx.Node is null) return "";
var replacement = new XElement(newName, ctx.Node.Attributes(), ctx.Node.Nodes());
ctx.Node.ReplaceWith(replacement);
ctx.Node = replacement; // update context to point to new element
return "";
}
public static string rename(Context ctx, string select, string newName)
{
if (ctx.Node is null) return "";
int atIdx = select.IndexOf('@');
if (atIdx >= 0)
{
// "Node@Attr" — rename an attribute
var elementSelect = select[..atIdx].TrimEnd();
var attrName = select[(atIdx + 1)..].TrimStart();
var target = string.IsNullOrEmpty(elementSelect)
? ctx.Node
: ctx.Node.XPathSelectElement(elementSelect);
var attr = target?.Attribute(attrName);
if (attr is null) return "";
attr.Remove();
target!.SetAttributeValue(newName, attr.Value);
}
else
{
// plain XPath select — rename element
var target = ctx.Node.XPathSelectElement(select);
if (target is null) return "";
var replacement = new XElement(newName, target.Attributes(), target.Nodes());
target.ReplaceWith(replacement);
}
return "";
}
}
}