From: Jean-Philippe Bruyère Date: Fri, 4 Jul 2025 11:45:57 +0000 (+0200) Subject: wip X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=e68b1d64527b7acc6b89156c36060eb0c8b2fe4f;p=jp%2Fcrowedit.git wip --- diff --git a/CrowEditBase/src/Compiler/SyntaxNodes/SingleTokenSyntax.cs b/CrowEditBase/src/Compiler/SyntaxNodes/SingleTokenSyntax.cs index 3253e9f..56c486f 100644 --- a/CrowEditBase/src/Compiler/SyntaxNodes/SingleTokenSyntax.cs +++ b/CrowEditBase/src/Compiler/SyntaxNodes/SingleTokenSyntax.cs @@ -11,13 +11,20 @@ namespace CrowEditBase } #endregion - protected Token token; + public readonly Token token; #region SyntaxNode implementation public override TokenType Type => token.Type; public override int SpanStart => token.Start; public override int SpanEnd => token.End; public override bool IsComplete => token.Type != TokenType.Unknown && token.Length > 0; - #endregion - } + public override bool IsSimilar(object other) + { + return other is SingleTokenSyntax sts ? + sts.Type == Type : other is TokenType tt ? Type == tt : false; + + } + #endregion + + } } \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/SyntaxNodes/SyntaxNode.cs b/CrowEditBase/src/Compiler/SyntaxNodes/SyntaxNode.cs index 092d54b..bb639d6 100644 --- a/CrowEditBase/src/Compiler/SyntaxNodes/SyntaxNode.cs +++ b/CrowEditBase/src/Compiler/SyntaxNodes/SyntaxNode.cs @@ -2,6 +2,7 @@ // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System.Collections.Generic; +using System.ComponentModel; using Crow.Text; namespace CrowEditBase @@ -79,91 +80,12 @@ namespace CrowEditBase public string AsText() { return Span.Length < 0 ? "" : Root.GetText(Span).ToString(); } - public bool IsSimilar (SyntaxNode other) => this.GetType() == other?.GetType(); + public virtual bool IsSimilar (object other) => this.GetType() == other?.GetType(); public class CompareOnStartLine : IComparer { public int Compare(SyntaxNode x, SyntaxNode y) => x.StartLocation.Line - y.StartLocation.Line; } public override string ToString() => $"{this.GetType().Name}"; - - #region to clean - /*public int StartLine { get; private set; } - public virtual int LineCount => lineCount;*/ - - /*List exceptions = new List(); - public IEnumerable Exceptions => exceptions; - public void AddException(SyntaxException e) => exceptions.Add(e); - public void ResetExceptions(SyntaxException e) => exceptions.Clear(); - public IEnumerable GetAllExceptions() { - foreach (SyntaxException e in exceptions) - yield return e; - foreach (SyntaxNode n in Children) { - foreach (SyntaxException ce in n.GetAllExceptions()) - yield return ce; - } - }*/ - - //public int IndexOf (SyntaxNode node) => children.IndexOf (node); - - /*public virtual int TokenIndexBase { get; private set; } - public virtual int TokenCount => lastTokenOfset.HasValue ? lastTokenOfset.Value + 1 : 0; - public int? LastTokenIndex => lastTokenOfset.HasValue ? TokenIndexBase + lastTokenOfset.Value : null; - - public int EndLine { - set { - lineCount = value - StartLine + 1; - } - get => StartLine + lineCount - 1; - }*/ - - /* - public void Replace (SyntaxNode newNode) { - Parent.replaceChild (this, newNode); - } - void replaceChild (SyntaxNode oldNode, SyntaxNode newNode) { - int idx = children.IndexOf (oldNode); - children[idx] = newNode; - newNode.Parent = this; - int tokIdxDiff = newNode.TokenCount - oldNode.TokenCount; - int lineDiff = newNode.EndLine - oldNode.EndLine; - if (tokIdxDiff == 0 && lineDiff == 0) - return; - - SyntaxNode curNode = this; - while (curNode != null) { - curNode.lineCount += lineDiff; - curNode.TokenCount += tokIdxDiff; - if (curNode is SyntaxRootNode) - break; - while (++idx < curNode.children.Count) - curNode.children[idx].offset (tokIdxDiff, lineDiff); - idx = curNode.Parent.children.IndexOf (curNode); - curNode = curNode.Parent; - } - }*/ - /*void offset (int tokenOffset, int lineOffset) { - TokenIndexBase += tokenOffset; - StartLine += lineOffset; - foreach (SyntaxNode child in children) { - child.offset (tokenOffset, lineOffset); - } - }*/ -/* - public T FindNodeIncludingPosition (int pos) { - foreach (SyntaxNode node in children) { - if (node.Contains (pos)) - return node.FindNodeIncludingPosition (pos); - } - - return this is T tt ? tt : default; - }*/ - /*public void Dump (int level = 0) { - Console.WriteLine ($"{new string('\t', level)}{this}"); - foreach (SyntaxNode node in children) - node.Dump (level + 1); - }*/ - //public override string ToString() => $"l:({StartLine,3},{LineCount,3}) tks:{TokenIndexBase},{TokenCount} {this.GetType().Name}"; - #endregion } } \ No newline at end of file diff --git a/CrowEditBase/src/SourceEditor.cs b/CrowEditBase/src/SourceEditor.cs index 4e10f6d..43a31e1 100644 --- a/CrowEditBase/src/SourceEditor.cs +++ b/CrowEditBase/src/SourceEditor.cs @@ -127,13 +127,18 @@ namespace CrowEditBase protected void tryGetSuggestions () { if (currentLoc.HasValue && Document is SourceDocument srcDoc && srcDoc.IsParsed) { int pos = srcDoc.GetAbsolutePosition(CurrentLoc.Value); - var tmp = srcDoc.GetSuggestions (pos, currentTokenIndex, currentNode, CurrentLoc.Value); - if (tmp?.Count == 1 && tmp[0].TryCast(out Suggestion sug) && sug.Change.HasNoEffect(srcDoc.source)) - Suggestions = null; - else - Suggestions = tmp; - } else - Suggestions = null; + if (pos > 0) { + SyntaxNode node = srcDoc.FindNodeIncludingPosition(pos-1); + if (node != null) { + var tmp = srcDoc.GetSuggestions (pos, + srcDoc.FindTokenIndexIncludingPosition(pos -1), node, CurrentLoc.Value); + if (!(tmp?.Count == 1 && tmp[0].TryCast(out Suggestion sug) && sug.Change.HasNoEffect(srcDoc.source))) + Suggestions = tmp; + return; + } + } + } + Suggestions = null; } void showOverlay () { lock (IFace.UpdateMutex) { @@ -426,7 +431,7 @@ namespace CrowEditBase return; } - if (Document is SourceDocument doc) { + if (Document is SourceDocument doc && doc.IsParsed) { switch (e.Key) { /*case Key.F3: doc.Root?.Dump(); @@ -593,26 +598,28 @@ namespace CrowEditBase } protected override void drawContent (IContext gr) { + sourceDocument.EnterReadLock (); + + double lineHeight = fe.Ascent + fe.Descent; + updateMargin (); + + bool printLineNumbers = App.PrintLineNumbers; + Color marginBG = App.MarginBackground; + Color marginFG = Colors.Ivory; + Rectangle cb = ClientRectangle; + RectangleD marginRect = new RectangleD (cb.X, cb.Y, leftMargin - leftMarginRightGap, cb.Height); + + gr.SetSource (marginBG); + gr.Rectangle (marginRect); + gr.Fill(); + if (!parsingOk) { base.drawContent (gr); + sourceDocument.ExitReadLock (); return; } - sourceDocument.EnterReadLock (); try { - double lineHeight = fe.Ascent + fe.Descent; - updateMargin (); - - bool printLineNumbers = App.PrintLineNumbers; - Color marginBG = App.MarginBackground; - Color marginFG = Colors.Ivory; - Rectangle cb = ClientRectangle; - RectangleD marginRect = new RectangleD (cb.X, cb.Y, leftMargin - leftMarginRightGap, cb.Height); - - gr.SetSource (marginBG); - gr.Rectangle (marginRect); - gr.Fill(); - gr.Translate (-ScrollX, 0); double lineNumWidth = gr.TextExtents (Document.LinesCount.ToString()).Width; @@ -901,7 +908,7 @@ namespace CrowEditBase RegisterForGraphicUpdate(); } void updateCurrentTokAndNode() { - if (currentLoc.HasValue && Document is SourceDocument srcdoc) { + if (currentLoc.HasValue && Document is SourceDocument srcdoc && srcdoc.Tokens.Length > 0) { int pos = srcdoc.GetAbsolutePosition(currentLoc.Value); currentTokenIndex = srcdoc.FindTokenIndexIncludingPosition(pos); Token tok = srcdoc.GetTokenByIndex(currentTokenIndex); diff --git a/plugins/CECrowPlugin/src/Parsing/IML/ImlDocument.cs b/plugins/CECrowPlugin/src/Parsing/IML/ImlDocument.cs index f4c42a3..11ac338 100644 --- a/plugins/CECrowPlugin/src/Parsing/IML/ImlDocument.cs +++ b/plugins/CECrowPlugin/src/Parsing/IML/ImlDocument.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using Crow.Text; +using System.Collections; using System.Collections.Generic; using Crow; using IML = Crow.IML; @@ -18,9 +19,16 @@ using System.Diagnostics; namespace CECrowPlugin { + public static class Extensions { + public static ImlTokenType GetTokenType (this Token tok) { + return (ImlTokenType)tok.Type; + } + public static void SetTokenType (this Token tok, ImlTokenType type) { + tok.Type = (TokenType)type; + } + public static bool Is(this Token tok, ImlTokenType type) => (ImlTokenType)tok.Type == type; + } public class ImlDocument : XmlDocument { - - public ImlDocument (string fullPath, string editorPath) : base (fullPath, editorPath) { App.GetService ()?.Start (); @@ -28,6 +36,7 @@ namespace CECrowPlugin if (msbp.IsCrowProject) }*/ } + protected override SyntaxAnalyser CreateSyntaxAnalyser() => new ImlSyntaxAnalyser (ImmutableBufferCopy); public override string GetTokenTypeString (TokenType tokenType) => ((ImlTokenType)tokenType).ToString(); @@ -46,8 +55,8 @@ namespace CECrowPlugin if (widgetType == null) return null; - IEnumerable widgetTypes = widgetType.Assembly.GetExportedTypes ().Where(t=> - widgetType.IsAssignableFrom (t) && !t.IsAbstract); + IEnumerable widgetTypes = widgetType.Assembly.GetExportedTypes ().Where( + t=>!t.IsAbstract && widgetType.IsAssignableFrom (t)); int curNameLength = 0; if (!string.IsNullOrEmpty(curName)) { widgetTypes = widgetTypes.Where(t=>t.Name.StartsWith(curName, StringComparison.OrdinalIgnoreCase)); @@ -114,13 +123,21 @@ namespace CECrowPlugin } return null; } - /*public override IList GetSuggestions (int absoluteTextPos, int currentTokenIndex, SyntaxNode CurrentNode, CharLocation loc) { + public override IList GetSuggestions (int absoluteTextPos, int currentTokenIndex, SyntaxNode CurrentNode, CharLocation loc) { + Token tok = GetTokenByIndex(currentTokenIndex); + Console.Write($"{absoluteTextPos}({tok.Span}){tok.GetTokenType()}"); + if (currentTokenIndex > 0) + Console.Write($" prev:{GetTokenByIndex(currentTokenIndex-1).GetTokenType()}"); + if (currentTokenIndex < Tokens.Length - 1) + Console.Write($" next:{GetTokenByIndex(currentTokenIndex+1).GetTokenType()}"); + Console.WriteLine($" node:{CurrentNode} ({loc})"); + IList sugs = base.GetSuggestions (absoluteTextPos, currentTokenIndex, CurrentNode, loc); if (sugs != null) return sugs; - +/* + - Token tok = GetTokenByIndex(currentTokenIndex); if (tok.GetTokenType() == XmlTokenType.ElementOpen) return new List (allWidgetNames); @@ -188,9 +205,9 @@ namespace CECrowPlugin //else if (tok.Type == TokenType.ElementName) // Suggestions = getAllCrowTypeMembers (eltStartTag.NameToken.Value.AsString (Source)).ToList (); } else { - } + }*/ return null; - }*/ + } public override Color GetColorForToken(Token token) { diff --git a/plugins/CECrowPlugin/src/Parsing/IML/ImlSyntaxAnalyser.cs b/plugins/CECrowPlugin/src/Parsing/IML/ImlSyntaxAnalyser.cs index 5c1fb3d..e399849 100644 --- a/plugins/CECrowPlugin/src/Parsing/IML/ImlSyntaxAnalyser.cs +++ b/plugins/CECrowPlugin/src/Parsing/IML/ImlSyntaxAnalyser.cs @@ -1,134 +1,18 @@ -// Copyright (c) 2021-2021 Bruyère Jean-Philippe +// Copyright (c) 2021-2025 Bruyère Jean-Philippe // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -using System; -using System.Collections.Generic; -using System.Linq; using CrowEditBase; using CrowEdit.Xml; -using System.Threading.Tasks; -using System.Threading; namespace CECrowPlugin { public class ImlSyntaxAnalyser : XmlSyntaxAnalyser { public ImlSyntaxAnalyser (ReadOnlyTextBuffer document) : base (document) {} - - public override async Task Process (CancellationToken cancel = default) { - - return await base.Process(); - -/* - ImlDocument xmlDoc = source as ImlDocument; - Exceptions = new List (); - currentNode = new XMLRootSyntax (xmlDoc); - currentLine = 0; - Span toks = source.Tokens; - tokIdx = 0; - - while (tokIdx < toks.Length) { - Token curTok = toks[tokIdx]; - if (curTok.Type == TokenType.LineBreak) - currentLine++; - else if (!curTok.Type.HasFlag (TokenType.Trivia)) { - if (currentNode is ElementStartTagSyntax tag) { - if (curTok.GetTokenType() == XmlTokenType.AttributeName) { - AttributeSyntax attribute = new AttributeSyntax (currentLine, tokIdx); - attribute.name = 0; - currentNode = currentNode.AddChild (attribute); - } else if (curTok.GetTokenType() == XmlTokenType.ElementName) - tag.name = tokIdx - tag.TokenIndexBase; - else if (curTok.GetTokenType() == XmlTokenType.ClosingSign) { - storeCurrentNode (); - currentNode.RemoveChild (tag); - currentNode = currentNode.AddChild (new ElementSyntax (tag)); - } else if (curTok.GetTokenType() == XmlTokenType.EmptyElementClosing) { - storeCurrentNode (); - currentNode.RemoveChild (tag); - currentNode = currentNode.AddChild (new EmptyElementSyntax (tag)); - setCurrentNodeEndLine (currentLine); - currentNode = currentNode.Parent; - } else { - Exceptions.Add (new SyntaxException ("Unexpected Token", curTok)); - storeCurrentNode (-1); - continue; - } - } else if (currentNode is ElementSyntax elt) { - if (curTok.GetTokenType() == XmlTokenType.ElementOpen) - currentNode = currentNode.AddChild (new ElementStartTagSyntax (currentLine, tokIdx)); - else if (curTok.GetTokenType() == XmlTokenType.EndElementOpen) { - elt.EndTag = new ElementEndTagSyntax (currentLine, tokIdx); - currentNode = elt.AddChild (elt.EndTag); - } - } else if (currentNode is AttributeSyntax attrib) { - if (curTok.GetTokenType() == XmlTokenType.EqualSign) - if (attrib.equal.HasValue) - Exceptions.Add (new SyntaxException ("Extra equal sign in attribute syntax", curTok)); - else - attrib.equal = tokIdx - attrib.TokenIndexBase; - else if (curTok.GetTokenType() == XmlTokenType.AttributeValueOpen) - attrib.valueOpen = tokIdx - attrib.TokenIndexBase; - else if (curTok.GetTokenType() == XmlTokenType.AttributeValue) - attrib.valueTok = tokIdx - attrib.TokenIndexBase; - else if (curTok.GetTokenType() == XmlTokenType.AttributeValueClose) { - attrib.valueClose = tokIdx - attrib.TokenIndexBase; - storeCurrentNode (); - } else { - Exceptions.Add (new SyntaxException ("Unexpected Token", curTok)); - storeCurrentNode (-1); - continue; - } - } else if (currentNode is ElementEndTagSyntax eltEndTag) { - if (curTok.GetTokenType() == XmlTokenType.ElementName) - eltEndTag.name = tokIdx - eltEndTag.TokenIndexBase; - else if (curTok.GetTokenType() == XmlTokenType.ClosingSign) { - //go up 2 times - storeCurrentNode (); storeCurrentNode (); - } else { - Exceptions.Add (new SyntaxException ("Unexpected Token", curTok)); - storeCurrentNode (-1); - storeCurrentNode (-1); - continue; - } - } else if (currentNode is XMLRootSyntax) { - switch (curTok.GetTokenType()) { - case XmlTokenType.ElementOpen: - currentNode = currentNode.AddChild (new ElementStartTagSyntax (currentLine, tokIdx)); - break; - case XmlTokenType.PI_Start: - currentNode = currentNode.AddChild (new ProcessingInstructionSyntax (currentLine, tokIdx)); - break; - default: - Exceptions.Add (new SyntaxException ("Unexpected Token", curTok)); - break; - } - } else if (currentNode is ProcessingInstructionSyntax pi) { - if (curTok.GetTokenType() == XmlTokenType.PI_Target) - pi.name = tokIdx - pi.TokenIndexBase; - else if (curTok.GetTokenType() == XmlTokenType.PI_End) { - storeCurrentNode (); - } else if (curTok.GetTokenType() == XmlTokenType.AttributeName) { - AttributeSyntax attribute = new AttributeSyntax (currentLine, tokIdx); - attribute.name = 0; - currentNode = currentNode.AddChild (attribute); - } else { - Exceptions.Add (new SyntaxException ("Unexpected Token", curTok)); - storeCurrentNode (-1); - continue; - } - } - } - tokIdx++; - } - while (currentNode.Parent != null) { - if (!currentNode.LastTokenOffset.HasValue) - storeCurrentNode (-1); - else - currentNode = currentNode.Parent; - } - setCurrentNodeEndLine (currentLine); - */ - } + protected override Token[] tokenize() + { + Tokenizer tokenizer = new ImlTokenizer(); + return tokenizer.Tokenize(source.Source.Span); + } } } \ No newline at end of file diff --git a/plugins/CEXmlPlugin/src/Parsing/XmlDocument.cs b/plugins/CEXmlPlugin/src/Parsing/XmlDocument.cs index 38d70f7..43a3d2d 100644 --- a/plugins/CEXmlPlugin/src/Parsing/XmlDocument.cs +++ b/plugins/CEXmlPlugin/src/Parsing/XmlDocument.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2021 Bruyère Jean-Philippe +// Copyright (c) 2013-2025 Bruyère Jean-Philippe // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) @@ -10,6 +10,7 @@ using System.Collections.Generic; using System; using Crow; using System.Linq; +using System.Diagnostics; namespace CrowEdit.Xml { @@ -23,7 +24,40 @@ namespace CrowEdit.Xml protected virtual IEnumerable getAttributeNameSuggestions(string eltName, string attribName, TextChange change) => null; protected virtual IEnumerable getAttributeValueSuggestions(string eltName, string attribName, string attribValue, TextChange change) => null; public override IList GetSuggestions (int absoluteTextPos, int currentTokenIndex, SyntaxNode CurrentNode, CharLocation loc) { - /*Token tok = GetTokenByIndex(currentTokenIndex); + Console.WriteLine($"absPos:{absoluteTextPos} tokIdx:{currentTokenIndex} node:{CurrentNode} charLoc:{loc}"); + Token tok = GetTokenByIndex(currentTokenIndex); + XmlTokenType tokType = tok.GetTokenType(); + if (CurrentNode is SingleTokenSyntax sts) { + XmlTokenType tk = sts.token.GetTokenType(); + if (CurrentNode.Parent is ElementTagSyntax ets) { + if (ets is ElementEndTagSyntax eets) { + if (eets.Parent is ElementSyntax es) { + string name = es.StartTag?.Name; + if (!string.IsNullOrEmpty(name)){ + Suggestion sug = new Suggestion(name); + if (tk == XmlTokenType.ElementName && name.StartsWith(sts.AsText(), StringComparison.OrdinalIgnoreCase)) + sug.Change = new TextChange (tok.Start, tok.Length, sug.Caption); + else if (tk == XmlTokenType.EndElementOpen) + sug.Change = new TextChange (tok.End, 0, sug.Caption); + else + return null; + if (!eets.HasClosingToken) + sug.Change.ChangedText += ">"; + return new List([sug]); + } + } + } else { + string txtEnd = ets.HasClosingToken ? "" : ">"; + if (tk == XmlTokenType.ElementOpen) { + return getElementNameSuggestions("", new TextChange(tok.End, 0, txtEnd)).ToList(); + } + if (tk == XmlTokenType.ElementName) { + return getElementNameSuggestions(sts.AsText(), new TextChange(tok.Start, tok.Length, txtEnd)).ToList(); + } + } + } + } + /* if (tok.Start != absoluteTextPos //middle of edited tok && currentTokenIndex >= CurrentNode?.Root.TokenCount - 1) //occurs when curTok is last tok of text { diff --git a/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxAnalyser.cs b/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxAnalyser.cs index 96bb444..e678241 100644 --- a/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxAnalyser.cs +++ b/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxAnalyser.cs @@ -117,14 +117,15 @@ namespace CrowEdit.Xml } void processElementNode(MultiNodeSyntax node) { ElementStartTagSyntax start = new ElementStartTagSyntax(Read()); + MultiNodeSyntax elt = null; if (accept (start, XmlTokenType.ElementName)) { while (skipTriviaAndComments(node)) { if (accept (start, XmlTokenType.EmptyElementClosing)) { - node.AddChild(new EmptyElementSyntax(start)); + elt = new EmptyElementSyntax(start); break; } if (accept (start, XmlTokenType.ClosingSign)) { - node.AddChild(processElement(new ElementSyntax(start))); + elt = processElement(new ElementSyntax(start)); break; } if (Peek().Is(XmlTokenType.AttributeName)) @@ -133,17 +134,26 @@ namespace CrowEdit.Xml start.AddChild(new UnexpectedTokenSyntax(Read())); } } else { - start.AddChild(new UnexpectedTokenSyntax(Read())); - node.AddChild(new ElementSyntax(start)); + if (!EOF) + start.AddChild(new UnexpectedTokenSyntax(Read())); + elt = new ElementSyntax(start); } - } - public override async Task Process (CancellationToken cancel = default) { + if (elt == null) + elt = new ElementSyntax(start); + node.AddChild(elt); + } + protected virtual Token[] tokenize() { Tokenizer tokenizer = new XmlTokenizer(); - Token[] tokens = tokenizer.Tokenize(source.Source.Span); + return tokenizer.Tokenize(source.Source.Span); + } + + public override async Task Process (CancellationToken cancel = default) { + Token[] tokens = tokenize(); tokIdx = 0; this.cancel = cancel;//? Root = new XMLRootSyntax (source, tokens); + while (!EOF) { if (cancel.IsCancellationRequested) break; diff --git a/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxNodes.cs b/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxNodes.cs index 7e870c2..e877d53 100644 --- a/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxNodes.cs +++ b/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxNodes.cs @@ -26,35 +26,45 @@ namespace CrowEdit.Xml public abstract class ElementTagSyntax : MultiNodeSyntax { // public override bool IsComplete => base.IsComplete & name.HasValue & close.HasValue; + protected ElementTagSyntax (){} protected ElementTagSyntax (Token openTok) { AddChild(new SingleTokenSyntax(openTok)); } + public override bool IsComplete => ChildSequenceIs(); + public string Name => Children.ElementAtOrDefault(1) is SingleTokenSyntax sts && + sts.token.GetTokenType() == XmlTokenType.ElementName ? sts.AsText(): ""; + public abstract bool HasClosingToken { get; } } /*public class ElementNameSyntax : SingleTokenSyntax { public ElementNameSyntax(Token name) : base(name) {} }*/ public class ElementStartTagSyntax : ElementTagSyntax { public ElementStartTagSyntax (Token openTok) : base(openTok) {} + public override bool HasClosingToken => Children.LastOrDefault() is SingleTokenSyntax sts && sts.token.GetTokenType() == XmlTokenType.ClosingSign; + } public class ElementEndTagSyntax : ElementTagSyntax { public ElementEndTagSyntax (Token openTok) : base(openTok) {} + public override bool HasClosingToken => Children.LastOrDefault() is SingleTokenSyntax sts && sts.token.GetTokenType() == XmlTokenType.ClosingSign; } - public class EmptyElementSyntax : MultiNodeSyntax { + public class EmptyElementSyntax : ElementTagSyntax { public EmptyElementSyntax (ElementStartTagSyntax startNode) { foreach (var child in startNode.Children) AddChild(child); } + public override bool HasClosingToken => Children.LastOrDefault() is SingleTokenSyntax sts && sts.token.GetTokenType() == XmlTokenType.EmptyElementClosing; //public override bool IsComplete => base.IsComplete && StartTag != null; } public class ElementSyntax : MultiNodeSyntax { - - public override bool IsComplete => base.IsComplete;// & StartTag.IsComplete & (EndTag != null && EndTag.IsComplete); - + public override bool IsComplete => StartTag != null && EndTag != null && StartTag.IsComplete && EndTag.IsComplete && StartTag.Name == EndTag.Name; public ElementSyntax (ElementStartTagSyntax startNode) { AddChild (startNode); } + + public ElementStartTagSyntax StartTag => Children.ElementAtOrDefault(0) as ElementStartTagSyntax; + public ElementEndTagSyntax EndTag => Children.LastOrDefault() as ElementEndTagSyntax; } public class AttributeSyntax : MultiNodeSyntax { //public override bool IsComplete => base.IsComplete & name.HasValue & equal.HasValue & valueTok.HasValue & valueOpen.HasValue & valueClose.HasValue;