From 6ce240d969289a5bf668d7fcb5346797c64431d2 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jean-Philippe=20Bruy=C3=A8re?= Date: Thu, 27 Feb 2025 01:02:09 +0100 Subject: [PATCH] Syntax analysis immutable copy of source and lineCollection --- CrowEditBase/src/Compiler/SourceDocument.cs | 96 +++-------- CrowEditBase/src/Compiler/SyntaxAnalyser.cs | 36 +++-- CrowEditBase/src/Compiler/SyntaxException.cs | 8 +- CrowEditBase/src/Compiler/SyntaxNode.cs | 1 + CrowEditBase/src/Compiler/SyntaxRootNode.cs | 22 +-- CrowEditBase/src/CrowEditBase.cs | 14 ++ CrowEditBase/src/Document.cs | 2 +- CrowEditBase/src/Editor.cs | 12 +- CrowEditBase/src/SourceEditor.cs | 40 +++-- CrowEditBase/src/TextBuffer.cs | 8 +- CrowEditBase/src/TextDocument.cs | 33 ++-- Directory.Build.props | 2 +- .../CECrowPlugin/src/DebugInterfaceWidget.cs | 4 +- .../src/Parsing/IML/ImlDocument.cs | 23 +-- .../src/Parsing/IML/ImlSyntaxAnalyser.cs | 8 +- .../src/Parsing/Styling/StyleDocument.cs | 9 +- .../src/Parsing/Styling/SyntaxAnalyser.cs | 22 +-- .../src/Parsing/Styling/SyntaxNodes.cs | 4 +- .../CEEbnfPlugin/src/Parsing/EbnfDocument.cs | 6 +- .../src/Parsing/EbnfSyntaxAnalyser.cs | 16 +- .../src/Parsing/EbnfSyntaxNodes.cs | 4 +- plugins/CERoslynPlugin/src/CSDocument.cs | 5 +- .../CERoslynPlugin/src/CSSyntaxAnalyser.cs | 17 +- .../CEXmlPlugin/src/Parsing/XmlDocument.cs | 67 ++++---- .../src/Parsing/XmlSyntaxAnalyser.cs | 41 ++--- .../CEXmlPlugin/src/Parsing/XmlSyntaxNodes.cs | 5 +- ui/windows/winLogs.crow | 153 +++++++++--------- 27 files changed, 323 insertions(+), 335 deletions(-) diff --git a/CrowEditBase/src/Compiler/SourceDocument.cs b/CrowEditBase/src/Compiler/SourceDocument.cs index 32e75c2..d0b2ec4 100644 --- a/CrowEditBase/src/Compiler/SourceDocument.cs +++ b/CrowEditBase/src/Compiler/SourceDocument.cs @@ -16,51 +16,30 @@ namespace CrowEditBase public SourceDocument (string fullPath, string editorPath = "#ui.sourceEditor.itmp") : base (fullPath, editorPath) { } - protected Token[] tokens; protected SyntaxRootNode root; - protected int currentTokenIndex; - SyntaxNode currentNode; - - protected Token currentToken => currentTokenIndex < 0 ? default : tokens[currentTokenIndex]; - protected Token? previousToken { - get { - if (currentTokenIndex < 1) - return null; - return tokens[currentTokenIndex-1]; - } - } - public SyntaxNode CurrentNode { - get => currentNode; - set { - if (currentNode == value) - return; - currentNode = value; - NotifyValueChanged ("CurrentNode", currentNode); - } - } - public string CurrentTokenString => root?.GetTokenStringByIndex (currentTokenIndex); - public Token CurrentToken => currentToken; - public bool IsParsed => tokens.Length > 0 && root != null; public SyntaxRootNode Root => root; + public bool IsParsed => root != null && Tokens.Length > 0; //public SyntaxNode EditedNode { get; protected set; } - public Token[] Tokens => tokens; + public ReadOnlySpan Tokens => root.Tokens; public IEnumerable SyntaxRootChildNodes => root?.children; public Token FindTokenIncludingPosition (int pos) { - if (pos == 0 || tokens == null || tokens.Length == 0) + if (!IsParsed || pos == 0 || Tokens.Length == 0) return default; - int idx = Array.BinarySearch (tokens, 0, tokens.Length, new Token () {Start = pos}); - - return idx == 0 ? tokens[0] : idx < 0 ? tokens[~idx - 1] : tokens[idx]; + int idx = Tokens.BinarySearch(new Token () {Start = pos}); + return idx == 0 ? Tokens[0] : idx < 0 ? Tokens[~idx - 1] : Tokens[idx]; } + public Token GetTokenByIndex(int tokIdx) => IsParsed && tokIdx >= 0 ? + Tokens[Math.Min(Tokens.Length - 1, tokIdx)] : default; public int FindTokenIndexIncludingPosition (int pos) { - if (pos == 0 || tokens == null || tokens.Length == 0) + if (!IsParsed || pos == 0 || Tokens.Length == 0) return default; - int idx = Array.BinarySearch (tokens, 0, tokens.Length, new Token () {Start = pos}); - + int idx = Tokens.BinarySearch(new Token () {Start = pos}); return idx == 0 ? 0 : idx < 0 ? ~idx - 1 : idx - 1; } + + /// /// if outermost is true, return oldest ancestor exept root node, useful for folding. /// @@ -101,21 +80,14 @@ namespace CrowEditBase base.apply(change); - Tokenizer tokenizer = CreateTokenizer (); SyntaxAnalyser syntaxAnalyser = CreateSyntaxAnalyser (); - - if (syntaxAnalyser == null) { - root = null; - return; - } + root = syntaxAnalyser?.Process (); + + NotifyValueChanged("Exceptions", syntaxAnalyser?.Exceptions); //SyntaxNode changedNode = root.FindNodeIncludingSpan (TextSpan.FromStartAndLength (change.Start, change.ChangedText.Length)); - tokens = tokenizer.Tokenize (buffer.Span); - syntaxAnalyser.Process (); - - root = syntaxAnalyser.Root; - NotifyValueChanged("Exceptions", syntaxAnalyser.Exceptions); + /* SyntaxNode newNode = syntaxAnalyser.Root.FindNodeIncludingSpan (TextSpan.FromStartAndLength (change.Start, change.ChangedText.Length)); @@ -145,27 +117,14 @@ namespace CrowEditBase //Console.WriteLine ($"CurrentToken: idx({currentTokenIndex}) {currentToken} {RootNode.Root.GetTokenStringByIndex(currentTokenIndex)}"); } - static bool tryReplaceNode (SyntaxNode editedNode, SyntaxNode newNode) { + /*static bool tryReplaceNode (SyntaxNode editedNode, SyntaxNode newNode) { if (newNode is SyntaxRootNode || editedNode is SyntaxRootNode) return false; editedNode.Replace (newNode); return true; } - internal void updateCurrentTokAndNode (CharLocation loc) { - int pos = buffer.GetAbsolutePosition(loc); - if (tokens.Length > 0) { - currentTokenIndex = FindTokenIndexIncludingPosition (pos); - CurrentNode = root?.FindNodeIncludingSpan (currentToken.Span); - NotifyValueChanged ("CurrentTokenString", (object)CurrentTokenString); - //NotifyValueChanged ("CurrentTokenType", (uint)(currentToken.Type)>>8); - NotifyValueChanged ("CurrentTokenType", (object)GetTokenTypeString(currentToken.Type)); - }else { - currentTokenIndex = -1; - CurrentNode = null; - NotifyValueChanged ("CurrentTokenString", (object)"no token"); - } - } + */ public virtual Color GetColorForToken (TokenType tokType) { if (tokType.HasFlag (TokenType.Punctuation)) @@ -177,9 +136,9 @@ namespace CrowEditBase return Colors.Red; } public virtual string GetTokenTypeString (TokenType tokenType) => tokenType.ToString(); - protected abstract Tokenizer CreateTokenizer (); + //protected abstract Tokenizer CreateTokenizer (); protected abstract SyntaxAnalyser CreateSyntaxAnalyser (); - public abstract IList GetSuggestions (CharLocation loc); + public abstract IList GetSuggestions (Token currentToken, SyntaxNode currentNode, CharLocation loc); /// /// complete current token with selected item from the suggestion overlay. @@ -189,23 +148,14 @@ namespace CrowEditBase /// /// the text change to apply /// new position or selection, null if normal position after text changes /// true if successed - public abstract bool TryGetCompletionForCurrentToken (object suggestion, out TextChange change, out TextSpan? newSelection); - protected bool previousTokHasFlag(TokenType flag) => previousToken.HasValue && previousToken.Value.Type.HasFlag(flag); + public abstract bool TryCompleteToken (Token CurrentToken, SyntaxNode CurrentNode, object suggestion, out TextChange change, out TextSpan? newSelection); + //protected bool previousTokHasFlag(TokenType flag) => previousToken.HasValue && previousToken.Value.Type.HasFlag(flag); void parse () { - Tokenizer tokenizer = CreateTokenizer (); - tokens = tokenizer?.Tokenize (source); SyntaxAnalyser syntaxAnalyser = CreateSyntaxAnalyser (); - Stopwatch sw = Stopwatch.StartNew (); - syntaxAnalyser?.Process (); - sw.Stop(); - root = syntaxAnalyser?.Root; + root = syntaxAnalyser?.Process (); + NotifyValueChanged("Exceptions", syntaxAnalyser?.Exceptions); //CrowEditBase.App.Log (LogType.Low, $"Syntax Analysis done in {sw.ElapsedMilliseconds}(ms) {sw.ElapsedTicks}(ticks)"); - if (syntaxAnalyser == null) - return; - /*foreach (Token t in Tokens) - Console.WriteLine ($"{t,-40} {Source.AsSpan(t.Start, t.Length).ToString()}"); - syntaxAnalyser.Root.Dump();*/ } } diff --git a/CrowEditBase/src/Compiler/SyntaxAnalyser.cs b/CrowEditBase/src/Compiler/SyntaxAnalyser.cs index cad6fe1..3ebbd77 100644 --- a/CrowEditBase/src/Compiler/SyntaxAnalyser.cs +++ b/CrowEditBase/src/Compiler/SyntaxAnalyser.cs @@ -4,22 +4,26 @@ using System; using System.Collections.Generic; using System.Linq; +using Crow.Text; namespace CrowEditBase { public abstract class SyntaxAnalyser { //protected abstract void Parse(SyntaxNode node); - protected SourceDocument source; - public SyntaxRootNode Root { get; protected set; } + protected ReadOnlyMemory source; + protected LineCollection lines; + protected SyntaxRootNode Root; public IEnumerable Exceptions => Root?.GetAllExceptions(); - public SyntaxAnalyser (SourceDocument source) { - this.source = source; + public SyntaxAnalyser (SourceDocument document) { + this.source = document.ImmutableBufferCopy; + this.lines = document.Lines; } - public abstract void Process (); + public abstract SyntaxRootNode Process (); #region Token handling protected Token curTok => tokIdx < 0 ? default : tokens[tokIdx]; - protected Token[] tokens; + protected ReadOnlySpan curTokString => curTok.AsString(source.Span); + protected ReadOnlySpan tokens => Root.Tokens; protected bool EOF => tokIdx == tokens.Length; protected bool tryRead (out Token tok) { if (EOF) { @@ -68,7 +72,7 @@ namespace CrowEditBase #endregion #region parsing context - protected int currentLine, tokIdx; + protected int currentLine = 0, tokIdx = 0; protected SyntaxNode currentNode; #endregion @@ -77,15 +81,23 @@ namespace CrowEditBase /// /// The final token of this node /// the endline number of this node - protected void setEndLineForCurrentNode (int endTokenOffsetFromCurrentTokIdx = 0) { - currentNode.TokenCount = tokIdx - currentNode.TokenIndexBase + endTokenOffsetFromCurrentTokIdx; + protected void finishCurrentNode (int endTokenOffsetFromCurrentTokIdx = 0) { + int count = tokIdx - currentNode.TokenIndexBase + endTokenOffsetFromCurrentTokIdx; + currentNode.TokenCount = count < 0 ? null : count; + if (endTokenOffsetFromCurrentTokIdx < 0) { + Token lastTok = currentNode.LastTokenIndex.HasValue ? + Root.GetTokenByIndex(currentNode.LastTokenIndex.Value) : + Root.GetTokenByIndex(currentNode.TokenIndexBase); + currentNode.EndLine = lines.GetLocation(lastTok.End).Line; + } + //currentNode.EndLine currentNode.EndLine = currentLine; currentNode = currentNode.Parent; } - protected void setEndOfNode (int endTokenOffsetFromCurrentTokIdx = 0, int endLineOffsetFromCurrentLine = 0) { + /*protected void setEndOfNode (int endTokenOffsetFromCurrentTokIdx = 0, int endLineOffsetFromCurrentLine = 0) { currentNode.TokenCount = tokIdx - currentNode.TokenIndexBase + endTokenOffsetFromCurrentTokIdx; currentNode.EndLine = currentLine + endLineOffsetFromCurrentLine; - } + }*/ protected void setCurrentNodeEndLine (int endLine) => currentNode.EndLine = endLine; protected bool skipTrivia(bool skipLineBreaks = true) { @@ -100,7 +112,7 @@ namespace CrowEditBase return !EOF; } protected void addException(string message) { - currentNode.AddException(new SyntaxException(message, curTok)); + currentNode.AddException(new SyntaxException(message, curTok, source)); } diff --git a/CrowEditBase/src/Compiler/SyntaxException.cs b/CrowEditBase/src/Compiler/SyntaxException.cs index 3031acb..6da5a1a 100644 --- a/CrowEditBase/src/Compiler/SyntaxException.cs +++ b/CrowEditBase/src/Compiler/SyntaxException.cs @@ -7,13 +7,13 @@ namespace CrowEditBase { public class SyntaxException : Exception { public readonly Token Token; - public readonly SyntaxAnalyser SyntaxAnalyser; - public SyntaxException(string message, Token token = default, SyntaxAnalyser syntaxAnalyser = null, Exception innerException = null) + public readonly ReadOnlyMemory SourceText; + public SyntaxException(string message, Token token = default, ReadOnlyMemory textBuffer = default, Exception innerException = null) : base (message, innerException) { Token = token; - SyntaxAnalyser = syntaxAnalyser; + SourceText = textBuffer; } - public string TokenString => SyntaxAnalyser.Root.GetTokenString(Token); + public string TokenString => SourceText.Span.Slice(Token.Start,Token.Length).ToString(); public override string ToString() => $"{Message}: {TokenString}"; } } \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/SyntaxNode.cs b/CrowEditBase/src/Compiler/SyntaxNode.cs index 4de25e4..c981973 100644 --- a/CrowEditBase/src/Compiler/SyntaxNode.cs +++ b/CrowEditBase/src/Compiler/SyntaxNode.cs @@ -210,5 +210,6 @@ namespace CrowEditBase return Span.Length < 0 ? "" : Root.GetText(Span).ToString(); } public bool IsSimilar (SyntaxNode other) => this.GetType() == other?.GetType(); + } } \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/SyntaxRootNode.cs b/CrowEditBase/src/Compiler/SyntaxRootNode.cs index 7955c33..17d41f0 100644 --- a/CrowEditBase/src/Compiler/SyntaxRootNode.cs +++ b/CrowEditBase/src/Compiler/SyntaxRootNode.cs @@ -7,23 +7,27 @@ using Crow.Text; namespace CrowEditBase { public abstract class SyntaxRootNode : SyntaxNode { - protected readonly SourceDocument source; - public SyntaxRootNode (SourceDocument source) { + public SyntaxRootNode (ReadOnlyMemory source, Token[] tokens) { this.source = source; + this.tokens = tokens; } + protected readonly ReadOnlyMemory source; + protected Token[] tokens; public override int TokenIndexBase => 0; - public override int? TokenCount { get => Math.Max (0, source.Tokens.Length - 1); internal set {} } + public override int? TokenCount { get => tokens == null ? default : Math.Max (0, tokens.Length - 1); internal set {} } public override SyntaxRootNode Root => this; public override bool IsFoldable => false; public override SyntaxNode NextSiblingOrParentsNextSibling => null; public override void UnfoldToTheTop() {} - public string GetTokenStringByIndex (int idx) => - idx >= 0 && idx < source.Tokens.Length ? Root.GetText(source.Tokens[idx].Span).ToString() : null; - public Token GetTokenByIndex (int idx) => - idx >= 0 && idx < source.Tokens.Length ? source.Tokens[idx] : default; + + public ReadOnlySpan Tokens => tokens; + public string GetTokenStringByIndex (int idx) => tokens != null ? + idx >= 0 && idx < tokens.Length ? GetText(tokens[idx].Span).ToString() : null : null; + public Token GetTokenByIndex (int idx) => tokens != null ? + idx >= 0 && idx < tokens.Length ? tokens[idx] : default : default; public ReadOnlySpan GetText(TextSpan span) => - source.GetText(span); + source.Span.Slice(span.Start, span.Length); public string GetTokenString(Token tok) => - source.GetText(tok.Span).ToString(); + GetText(tok.Span).ToString(); } } \ No newline at end of file diff --git a/CrowEditBase/src/CrowEditBase.cs b/CrowEditBase/src/CrowEditBase.cs index 9ca4749..b6c82db 100644 --- a/CrowEditBase/src/CrowEditBase.cs +++ b/CrowEditBase/src/CrowEditBase.cs @@ -14,6 +14,20 @@ using Drawing2D; namespace CrowEditBase { + public static class Extensions { + public static bool TryCast(this object o, out T result) { + result = default; + if (o != null) { + Type tIn = o.GetType(); + Type tOut = typeof(T); + if (tOut.IsAssignableFrom(tIn)) { + result = (T)o; + return true; + } + } + return false; + } + } public abstract class CrowEditBase : Interface { public static CrowEditBase App; public CrowEditBase (int width, int height, bool singleThreaded = true) : base (width, height, singleThreaded) { diff --git a/CrowEditBase/src/Document.cs b/CrowEditBase/src/Document.cs index 03db4e5..d4a032b 100644 --- a/CrowEditBase/src/Document.cs +++ b/CrowEditBase/src/Document.cs @@ -35,7 +35,7 @@ namespace CrowEditBase public void ExitWriteLock () => documentRWLock.ExitWriteLock (); public abstract bool TryGetState (object client, out T state); - public abstract void RegisterClient (object client); + public abstract void RegisterClient (object client, bool initialState = false); public abstract void UnregisterClient (object client); DateTime accessTime; diff --git a/CrowEditBase/src/Editor.cs b/CrowEditBase/src/Editor.cs index 00f2e37..d3b7381 100644 --- a/CrowEditBase/src/Editor.cs +++ b/CrowEditBase/src/Editor.cs @@ -125,8 +125,10 @@ namespace Crow /// /// Absolute character position in text. public void SetCursorPosition (int position) { + document.EnterReadLock(); CharLocation loc = document.GetLocation (position); loc.Column = Math.Min (loc.Column, document.GetLine (loc.Line).Length); + document.ExitReadLock(); CurrentLoc = loc; } @@ -356,6 +358,7 @@ namespace Crow bool selectionNotEmpty = false; document.EnterReadLock(); + try { //if (HasFocus) { if (currentLoc?.Column < 0) { @@ -555,9 +558,8 @@ namespace Crow protected void updateLocation (IContext gr, ref CharLocation loc) { if (loc.HasVisualX) return; - TextLine ls = document.GetLine (loc.Line); - ReadOnlySpan curLine = document.GetText (ls); - + + ReadOnlySpan curLine = document.GetLineText (loc.Line); if (loc.Column >= 0) { //int encodedBytes = Crow.Text.Encoding2.ToUtf8 (curLine.Slice (0, loc.Column), bytes); #if DEBUG @@ -575,7 +577,7 @@ namespace Crow int totChar = 0; double cPos = 0; - for (int i = 0; i < ls.Length; i++) { + for (int i = 0; i < curLine.Length; i++) { int encodedBytes = curLine.Slice (i, 1).ToUtf8 (bytes, ref totChar, tabSize); bytes[encodedBytes] = 0; @@ -591,7 +593,7 @@ namespace Crow cPos += te.XAdvance; } - loc.Column = ls.Length; + loc.Column = curLine.Length; loc.VisualCharXPosition = cPos; loc.TabulatedColumn = totChar; } diff --git a/CrowEditBase/src/SourceEditor.cs b/CrowEditBase/src/SourceEditor.cs index 9535d9e..9e87ebd 100644 --- a/CrowEditBase/src/SourceEditor.cs +++ b/CrowEditBase/src/SourceEditor.cs @@ -15,7 +15,7 @@ using System.Linq; namespace Crow { public class SourceEditor : Editor { - object TokenMutex = new object(); + int currentTokenIndex = -1; SyntaxNode currentNode; #if DEBUG_NODE SyntaxNode _hoverNode; @@ -36,9 +36,11 @@ namespace Crow return; currentNode = value; NotifyValueChanged ("CurrentNode", currentNode); - RegisterForRedraw (); } } + + public Token CurrentToken => typeof(SourceDocument).IsAssignableFrom(Document?.GetType()) ? + (Document as SourceDocument).GetTokenByIndex(currentTokenIndex) : default; #region suggestions and autocomplete ListBox overlay; @@ -59,10 +61,11 @@ namespace Crow protected void tryGetSuggestions () { if (currentLoc.HasValue && Document is SourceDocument srcDoc && srcDoc.IsParsed) { - IList suggs = srcDoc.GetSuggestions (CurrentLoc.Value); + IList suggs = srcDoc.GetSuggestions (CurrentToken, currentNode, CurrentLoc.Value); + Token tok = CurrentToken; if (suggs != null && suggs.Count == 1 && ( - (suggs[0] is System.Reflection.MemberInfo mi && mi.Name == srcDoc.CurrentTokenString) || - (suggs[0].ToString() == srcDoc.CurrentTokenString) + (suggs[0] is System.Reflection.MemberInfo mi && mi.Name == srcDoc.GetText(tok.Span)) || + (suggs[0].ToString() == srcDoc.GetText(tok.Span)) )){ Suggestions = null; }else @@ -118,7 +121,7 @@ namespace Crow } void completeToken () { if (Document is SourceDocument srcDoc) { - if (srcDoc.TryGetCompletionForCurrentToken (overlay.SelectedItem, out TextChange change, out TextSpan? nextSelection)) { + if (srcDoc.TryCompleteToken (CurrentToken, CurrentNode, overlay.SelectedItem, out TextChange change, out TextSpan? nextSelection)) { update (change); if (nextSelection.HasValue) { Selection = nextSelection.Value; @@ -160,8 +163,7 @@ namespace Crow while (fold != null && fold.StartLine == currentLoc.Value.Line) fold = fold.Parent; fold?.UnfoldToTheTop(); - if (Document is SourceDocument doc) - doc.updateCurrentTokAndNode (currentLoc.Value); + updateCurrentTokAndNode(); } NotifyValueChanged ("CurrentLine", CurrentLine); NotifyValueChanged ("CurrentColumn", CurrentColumn); @@ -497,7 +499,7 @@ namespace Crow } protected override void drawContent (IContext gr) { - if (!(Document is SourceDocument doc)) { + if (!(Document is SourceDocument doc && doc.Root != null)) { base.drawContent (gr); return; } @@ -528,7 +530,6 @@ namespace Crow marginRect.Height = lineHeight; cb.Left += leftMargin; - CharLocation selStart = default, selEnd = default; bool selectionNotEmpty = false; CharLocation? nodeStart = null, nodeEnd = null; @@ -660,7 +661,7 @@ namespace Crow gr.ShowText (bytes.Slice (0, encodedBytes)); } - if (doc.CurrentToken.Equals(tok)) { + if (CurrentToken.Equals(tok)) { /*CharLocation? tokloc = Document.GetLocation (tok.Start); updateLocation (gr, cb.Width, ref tokloc);*/ Rectangle r = new RectangleD(pixX, pixY, extents.Width, lineHeight); @@ -800,8 +801,7 @@ namespace Crow protected override void update (TextChange change) { base.update (change); - if (Document is SourceDocument srcdoc) - srcdoc.updateCurrentTokAndNode (CurrentLoc.Value); + updateCurrentTokAndNode(); if (!disableSuggestions &&!disableTextChangedEvent && HasFocus) tryGetSuggestions (); @@ -817,6 +817,18 @@ namespace Crow } //Console.WriteLine ($"{pos}: {suggestionTok.AsString (_text)} {suggestionTok}"); } - + void updateCurrentTokAndNode() { + if (currentLoc.HasValue && Document is SourceDocument srcdoc) { + currentTokenIndex = srcdoc.GetAbsolutePosition(currentLoc.Value); + Token tok = srcdoc.FindTokenIncludingPosition(currentTokenIndex); + CurrentNode = srcdoc.Root?.FindNodeIncludingSpan(tok.Span); + + NotifyValueChanged("CurrentToken",tok); + } else { + currentTokenIndex = -1; + CurrentNode = null; + NotifyValueChanged("CurrentToken",default); + } + } } } \ No newline at end of file diff --git a/CrowEditBase/src/TextBuffer.cs b/CrowEditBase/src/TextBuffer.cs index 505098e..b4c6eb1 100644 --- a/CrowEditBase/src/TextBuffer.cs +++ b/CrowEditBase/src/TextBuffer.cs @@ -18,10 +18,11 @@ namespace CrowEditBase public bool mixedLineBreak = false; public string lineBreak = null; + internal LineCollection GetLineListCopy() => new LineCollection(lines.ToArray()); public Span Span => buffer.Span.Slice(0, length); public ReadOnlySpan ReadOnlySpan => buffer.Span.Slice(0, length); public bool IsEmpty => length == 0; - public bool IsDirty => origBuffer.Span.Equals(buffer.Span, StringComparison.Ordinal); + public bool IsDirty => !origBuffer.Span.Equals(Span, StringComparison.Ordinal); public int LinesCount => lines.Count; public int Length => length; public ReadOnlyMemory ReadOnlyCopy { @@ -30,7 +31,7 @@ namespace CrowEditBase } } public void ResetDirtyState () { - origBuffer = buffer.ToArray(); + origBuffer = Span.ToArray(); } public TextBuffer(ReadOnlySpan origText) { length = origText.Length; @@ -41,6 +42,7 @@ namespace CrowEditBase lines.Add (new TextLine (0, 0, 0)); else lines.Update (Span); + ResetDirtyState (); } public void Update (TextChange change) { ReadOnlySpan orig = buffer.Span; @@ -86,6 +88,8 @@ namespace CrowEditBase } public CharLocation GetLocation (int absolutePosition) => lines.GetLocation (absolutePosition); public TextLine GetLine (int index) => lines[index]; + public ReadOnlySpan GetText (TextLine line) => GetText(line.Span); + public ReadOnlySpan GetText (TextSpan textSpan) => buffer.Span.Slice(textSpan.Start, textSpan.Length); public int GetAbsolutePosition (CharLocation loc) => lines.GetAbsolutePosition (loc); public CharLocation EndLocation => new CharLocation (lines.Count - 1, lines[lines.Count - 1].Length); public override string ToString() => ReadOnlySpan.ToString(); diff --git a/CrowEditBase/src/TextDocument.cs b/CrowEditBase/src/TextDocument.cs index 43f14c2..1843dee 100644 --- a/CrowEditBase/src/TextDocument.cs +++ b/CrowEditBase/src/TextDocument.cs @@ -23,6 +23,8 @@ namespace CrowEditBase protected TextBuffer buffer; public ReadOnlySpan source => buffer.ReadOnlySpan; + public ReadOnlyMemory ImmutableBufferCopy => buffer.ReadOnlyCopy; + internal LineCollection Lines => buffer.GetLineListCopy(); System.Text.Encoding encoding = System.Text.Encoding.UTF8; public override bool IsDirty => buffer.IsDirty; @@ -40,18 +42,19 @@ namespace CrowEditBase } return state != null; } - public override void RegisterClient(object client) + public override void RegisterClient(object client, bool initState = false) { - documentRWLock.EnterWriteLock (); + EnterWriteLock(); registeredClients.Add (client, null); - //notifyClient (client, new TextChange (0, 0, source)); - documentRWLock.ExitWriteLock (); + if (initState) + notifyClient (client,new TextChange (0, 0,source.ToString())); + ExitWriteLock(); } public override void UnregisterClient(object client) { - documentRWLock.EnterWriteLock (); + EnterWriteLock(); registeredClients.Remove (client); - documentRWLock.ExitWriteLock (); + ExitWriteLock(); } void notifyClients (TextChange tc, object triggeringClient = null) { object[] clients = registeredClients.Keys.ToArray (); @@ -84,12 +87,6 @@ namespace CrowEditBase encoding = sr.CurrentEncoding; } } - ReadOnlyMemory testbuff = buffer.ReadOnlyCopy; - - buffer.Update(new TextChange(0,0,"test")); - - Debug.WriteLine($"buffer: {buffer.ToString()}"); - Debug.WriteLine($"testbuff: {testbuff.ToString()}"); } protected override void initNewFile() { @@ -253,10 +250,18 @@ namespace CrowEditBase documentRWLock.ExitReadLock(); } } + public ReadOnlySpan GetLineText (int index) { + documentRWLock.EnterReadLock (); + try { + return buffer.GetText (buffer.GetLine(index)); + } finally { + documentRWLock.ExitReadLock(); + } + } public ReadOnlySpan GetText (TextLine line) { documentRWLock.EnterReadLock (); try { - return source.GetLine (line); + return buffer.GetText (line); } finally { documentRWLock.ExitReadLock(); } @@ -264,7 +269,7 @@ namespace CrowEditBase public ReadOnlySpan GetText (TextSpan span) { documentRWLock.EnterReadLock (); try { - return source.Slice (span.Start, span.Length); + return buffer.GetText (span); } finally { documentRWLock.ExitReadLock(); } diff --git a/Directory.Build.props b/Directory.Build.props index 01b6d24..4321e71 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,6 +4,6 @@ $(SolutionDir)build\obj\$(Configuration)\ MIT Jean-Philippe Bruyère - 7.2 + diff --git a/plugins/CECrowPlugin/src/DebugInterfaceWidget.cs b/plugins/CECrowPlugin/src/DebugInterfaceWidget.cs index 815cb3a..7b96840 100644 --- a/plugins/CECrowPlugin/src/DebugInterfaceWidget.cs +++ b/plugins/CECrowPlugin/src/DebugInterfaceWidget.cs @@ -104,9 +104,9 @@ namespace Crow if (value is ImlDocument imlDoc) { document?.UnregisterClient (this); - imlSource = ""; document = imlDoc; - document?.RegisterClient (this); + imlSource = default; + document?.RegisterClient (this, true); NotifyValueChangedAuto (document); RegisterForGraphicUpdate (); diff --git a/plugins/CECrowPlugin/src/Parsing/IML/ImlDocument.cs b/plugins/CECrowPlugin/src/Parsing/IML/ImlDocument.cs index 72988c6..acb0487 100644 --- a/plugins/CECrowPlugin/src/Parsing/IML/ImlDocument.cs +++ b/plugins/CECrowPlugin/src/Parsing/IML/ImlDocument.cs @@ -31,7 +31,6 @@ namespace CECrowPlugin if (msbp.IsCrowProject) }*/ } - protected override Tokenizer CreateTokenizer() => new ImlTokenizer (); protected override SyntaxAnalyser CreateSyntaxAnalyser() => new ImlSyntaxAnalyser (this); public override string GetTokenTypeString (TokenType tokenType) => ((ImlTokenType)tokenType).ToString(); @@ -49,20 +48,9 @@ namespace CECrowPlugin Type crowType = IML.Instantiator.GetWidgetTypeFromName (crowTypeName); return crowType.GetMember (memberName, BindingFlags.Public | BindingFlags.Instance).FirstOrDefault (); } - - protected bool previousTokHasFlag(ImlTokenType flag) => previousToken.HasValue && previousToken.Value.Type.HasFlag(flag); - - protected bool tryCast (object objectToCast, out TOUT result) { - result = default; - if (typeof(TOUT).IsAssignableFrom(objectToCast.GetType())) { - result = (TOUT)objectToCast; - return true; - } - return false; - } - public override IList GetSuggestions (CharLocation loc) { - IList sugs = base.GetSuggestions (loc); + public override IList GetSuggestions (Token currentToken, SyntaxNode CurrentNode, CharLocation loc) { + IList sugs = base.GetSuggestions (currentToken, CurrentNode, loc); if (sugs != null) return sugs; @@ -73,8 +61,7 @@ namespace CECrowPlugin return new List (allWidgetNames); if (tok.GetTokenType() == XmlTokenType.ElementName) return allWidgetNames.Where (s => s.StartsWith (root.GetTokenString(tok), StringComparison.OrdinalIgnoreCase)).ToList (); - if ((tok.Type.HasFlag(TokenType.WhiteSpace) || previousTokHasFlag(TokenType.WhiteSpace)) && - tryCast(CurrentNode, out ElementTagSyntax ets)) { + if (tok.Type.HasFlag(TokenType.WhiteSpace) && CurrentNode.TryCast(out ElementTagSyntax ets)) { if (ets.name.HasValue) return getAllCrowTypeMembers (ets.Name).ToList(); return null; @@ -139,8 +126,8 @@ namespace CECrowPlugin }*/ return null; } - public override bool TryGetCompletionForCurrentToken (object suggestion, out TextChange change, out TextSpan? newSelection) { - return base.TryGetCompletionForCurrentToken (suggestion is MemberInfo mi ? mi.Name : suggestion, out change, out newSelection); + public override bool TryCompleteToken (Token tok, SyntaxNode node, object suggestion, out TextChange change, out TextSpan? newSelection) { + return base.TryCompleteToken (tok, node, suggestion is MemberInfo mi ? mi.Name : suggestion, out change, out newSelection); } public override Color GetColorForToken(TokenType tokType) diff --git a/plugins/CECrowPlugin/src/Parsing/IML/ImlSyntaxAnalyser.cs b/plugins/CECrowPlugin/src/Parsing/IML/ImlSyntaxAnalyser.cs index db7aa17..e1b9047 100644 --- a/plugins/CECrowPlugin/src/Parsing/IML/ImlSyntaxAnalyser.cs +++ b/plugins/CECrowPlugin/src/Parsing/IML/ImlSyntaxAnalyser.cs @@ -10,14 +10,12 @@ using CrowEdit.Xml; namespace CECrowPlugin { public class ImlSyntaxAnalyser : XmlSyntaxAnalyser { - public ImlSyntaxAnalyser (ImlDocument source) : base (source) { - this.source = source; - } + public ImlSyntaxAnalyser (ImlDocument document) : base (document) {} - public override void Process () { + public override SyntaxRootNode Process () { - base.Process(); + return base.Process(); /* ImlDocument xmlDoc = source as ImlDocument; diff --git a/plugins/CECrowPlugin/src/Parsing/Styling/StyleDocument.cs b/plugins/CECrowPlugin/src/Parsing/Styling/StyleDocument.cs index 215d764..ac39b23 100644 --- a/plugins/CECrowPlugin/src/Parsing/Styling/StyleDocument.cs +++ b/plugins/CECrowPlugin/src/Parsing/Styling/StyleDocument.cs @@ -22,16 +22,15 @@ namespace CECrowPlugin.Style }*/ } - protected override Tokenizer CreateTokenizer() => new StyleTokenizer (); protected override SyntaxAnalyser CreateSyntaxAnalyser() => new StyleSyntaxAnalyser (this); - public override IList GetSuggestions (CharLocation loc) { - Console.ForegroundColor = ConsoleColor.DarkYellow; + public override IList GetSuggestions (Token currentToken, SyntaxNode CurrentNode, CharLocation loc) { + /*Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine ($"Tok: {this.CurrentTokenString} {((StyleTokenType)CurrentToken.Type).ToString()}"); - Console.ResetColor(); + Console.ResetColor();*/ return null; } - public override bool TryGetCompletionForCurrentToken(object suggestion, out TextChange change, out TextSpan? newSelection) + public override bool TryCompleteToken(Token tok, SyntaxNode node, object suggestion, out TextChange change, out TextSpan? newSelection) { change = default; newSelection = null; diff --git a/plugins/CECrowPlugin/src/Parsing/Styling/SyntaxAnalyser.cs b/plugins/CECrowPlugin/src/Parsing/Styling/SyntaxAnalyser.cs index b4709b7..a6045e5 100644 --- a/plugins/CECrowPlugin/src/Parsing/Styling/SyntaxAnalyser.cs +++ b/plugins/CECrowPlugin/src/Parsing/Styling/SyntaxAnalyser.cs @@ -17,21 +17,21 @@ namespace CECrowPlugin.Style } } public class StyleSyntaxAnalyser : SyntaxAnalyser { - public StyleSyntaxAnalyser (StyleDocument source) : base (source) { - this.source = source; - } + public StyleSyntaxAnalyser (StyleDocument document) : base (document) {} + + public override SyntaxRootNode Process () { + Tokenizer tokenizer = new StyleTokenizer(); + Token[] tokens = tokenizer.Tokenize(source.Span); + + currentNode = Root = new StyleRootSyntax (source, tokens); - public override void Process () { - StyleDocument doc = source as StyleDocument; - currentNode = Root = new StyleRootSyntax (doc); currentLine = 0; - Span toks = source.Tokens; tokIdx = 0; int firstNameIdx = -1; - while (tokIdx < toks.Length) { - Token curTok = toks[tokIdx]; + while (tokIdx < tokens.Length) { + Token curTok = tokens[tokIdx]; if (curTok.Type == TokenType.LineBreak) currentLine++; else if (!curTok.Type.HasFlag (TokenType.Trivia)) { @@ -51,11 +51,13 @@ namespace CECrowPlugin.Style } while (currentNode.Parent != null) { if (!currentNode.TokenCount.HasValue) - setEndLineForCurrentNode (-1); + finishCurrentNode (-1); else currentNode = currentNode.Parent; } setCurrentNodeEndLine (currentLine); + + return Root; } } } \ No newline at end of file diff --git a/plugins/CECrowPlugin/src/Parsing/Styling/SyntaxNodes.cs b/plugins/CECrowPlugin/src/Parsing/Styling/SyntaxNodes.cs index c740e90..0619d71 100644 --- a/plugins/CECrowPlugin/src/Parsing/Styling/SyntaxNodes.cs +++ b/plugins/CECrowPlugin/src/Parsing/Styling/SyntaxNodes.cs @@ -11,9 +11,7 @@ namespace CECrowPlugin.Style { public class StyleRootSyntax : SyntaxRootNode { - public StyleRootSyntax (StyleDocument source) - : base (source) { - } + public StyleRootSyntax (ReadOnlyMemory source, Token[] tokens) : base (source, tokens) { } } public class ConstantDefinitionSyntax : SyntaxNode { internal int? name, equal, valueOpen, valueClose; diff --git a/plugins/CEEbnfPlugin/src/Parsing/EbnfDocument.cs b/plugins/CEEbnfPlugin/src/Parsing/EbnfDocument.cs index 02d976b..e5494bf 100644 --- a/plugins/CEEbnfPlugin/src/Parsing/EbnfDocument.cs +++ b/plugins/CEEbnfPlugin/src/Parsing/EbnfDocument.cs @@ -2,6 +2,7 @@ // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; using Crow.Text; using System.Collections; using CrowEditBase; @@ -22,13 +23,12 @@ namespace CrowEdit.Ebnf public EbnfDocument (string fullPath, string editorPath) : base (fullPath, editorPath) { } - protected override Tokenizer CreateTokenizer() => new EbnfTokenizer (); protected override SyntaxAnalyser CreateSyntaxAnalyser() => new EbnfSyntaxAnalyser (this); - public override IList GetSuggestions (CharLocation loc) { + public override IList GetSuggestions (Token currentToken, SyntaxNode CurrentNode, CharLocation loc) { return null; } - public override bool TryGetCompletionForCurrentToken (object suggestion, out TextChange change, out TextSpan? newSelection) { + public override bool TryCompleteToken (Token tok, SyntaxNode node, object suggestion, out TextChange change, out TextSpan? newSelection) { newSelection = null; change = default; return false; diff --git a/plugins/CEEbnfPlugin/src/Parsing/EbnfSyntaxAnalyser.cs b/plugins/CEEbnfPlugin/src/Parsing/EbnfSyntaxAnalyser.cs index 382a378..d733ab3 100644 --- a/plugins/CEEbnfPlugin/src/Parsing/EbnfSyntaxAnalyser.cs +++ b/plugins/CEEbnfPlugin/src/Parsing/EbnfSyntaxAnalyser.cs @@ -10,9 +10,7 @@ namespace CrowEdit.Ebnf { public class EbnfSyntaxAnalyser : SyntaxAnalyser { - public EbnfSyntaxAnalyser (EbnfDocument source) : base (source) { - this.source = source; - } + public EbnfSyntaxAnalyser (EbnfDocument document) : base (document) {} // ::= NCName '::=' Expression @@ -22,14 +20,15 @@ namespace CrowEdit.Ebnf // Item ::= Primary ( '?' | '*' | '+' )? //NCName | StringLiteral | CharCode | CharClass | '(' Choice ')' // StringLiteral ::= '"' [^"]* '"' | "'" [^']* "'" - - public override void Process() + + public override SyntaxRootNode Process() { - EbnfDocument doc = source as EbnfDocument; - currentNode = Root = new EbnfRootSyntax (doc); + Tokenizer tokenizer = new EbnfTokenizer(); + Token[] tokens = tokenizer.Tokenize(source.Span); + + currentNode = Root = new EbnfRootSyntax (source, tokens); currentLine = 0; tokIdx = 0; - tokens = doc.Tokens; /*while(tokIdx < tokens.Length) { skipTrivia(); @@ -81,6 +80,7 @@ namespace CrowEdit.Ebnf setCurrentNodeEndLine (currentLine); + return Root; } diff --git a/plugins/CEEbnfPlugin/src/Parsing/EbnfSyntaxNodes.cs b/plugins/CEEbnfPlugin/src/Parsing/EbnfSyntaxNodes.cs index c6615d0..b709ffd 100644 --- a/plugins/CEEbnfPlugin/src/Parsing/EbnfSyntaxNodes.cs +++ b/plugins/CEEbnfPlugin/src/Parsing/EbnfSyntaxNodes.cs @@ -11,9 +11,7 @@ namespace CrowEdit.Ebnf { public class EbnfRootSyntax : SyntaxRootNode { - public EbnfRootSyntax (EbnfDocument source) - : base (source) { - } + public EbnfRootSyntax (ReadOnlyMemory source, Token[] tokens) : base (source, tokens) { } } public class EbnfSyntaxNode : SyntaxNode { public EbnfSyntaxNode(int startLine, int tokenBase) diff --git a/plugins/CERoslynPlugin/src/CSDocument.cs b/plugins/CERoslynPlugin/src/CSDocument.cs index efe786d..6a68f39 100644 --- a/plugins/CERoslynPlugin/src/CSDocument.cs +++ b/plugins/CERoslynPlugin/src/CSDocument.cs @@ -44,14 +44,13 @@ namespace CERoslynPlugin } #region SourceDocument abstract class implementation - protected override Tokenizer CreateTokenizer() => new CSTokenizer (); protected override SyntaxAnalyser CreateSyntaxAnalyser() => new CSSyntaxAnalyser (this); - public override IList GetSuggestions (CharLocation loc) + public override IList GetSuggestions (Token currentToken, SyntaxNode CurrentNode, CharLocation loc) { throw new NotImplementedException(); } - public override bool TryGetCompletionForCurrentToken (object suggestion, out TextChange change, out TextSpan? newSelection) + public override bool TryCompleteToken (Token tok, SyntaxNode node, object suggestion, out TextChange change, out TextSpan? newSelection) { throw new NotImplementedException(); } diff --git a/plugins/CERoslynPlugin/src/CSSyntaxAnalyser.cs b/plugins/CERoslynPlugin/src/CSSyntaxAnalyser.cs index 761e47e..1ac8b0e 100644 --- a/plugins/CERoslynPlugin/src/CSSyntaxAnalyser.cs +++ b/plugins/CERoslynPlugin/src/CSSyntaxAnalyser.cs @@ -9,22 +9,23 @@ using CrowEditBase; namespace CERoslynPlugin { public class CSRootSyntax : SyntaxRootNode { - public CSRootSyntax (SourceDocument source) - : base (source) { - } + public CSRootSyntax (ReadOnlyMemory source, Token[] tokens) : base (source, tokens) { } } + public class CSSyntaxAnalyser : SyntaxAnalyser { /*protected override void Parse(SyntaxNode node) { throw new NotImplementedException(); }*/ - public CSSyntaxAnalyser (CSDocument source) : base (source) { - this.source = source; - } + public CSSyntaxAnalyser (CSDocument document) : base (document) {} + + public override SyntaxRootNode Process () { + Tokenizer tokenizer = new CSTokenizer(); + Token[] tokens = tokenizer.Tokenize(source.Span); - public override void Process () { - currentNode = Root = new CSRootSyntax (source); + currentNode = Root = new CSRootSyntax (source, tokens); + return Root; } } } \ No newline at end of file diff --git a/plugins/CEXmlPlugin/src/Parsing/XmlDocument.cs b/plugins/CEXmlPlugin/src/Parsing/XmlDocument.cs index ecdb3d9..196825c 100644 --- a/plugins/CEXmlPlugin/src/Parsing/XmlDocument.cs +++ b/plugins/CEXmlPlugin/src/Parsing/XmlDocument.cs @@ -7,6 +7,7 @@ using System.Collections; using CrowEditBase; using Drawing2D; using System.Collections.Generic; +using System; namespace CrowEdit.Xml { @@ -17,16 +18,14 @@ namespace CrowEdit.Xml public static void SetTokenType (this Token tok, XmlTokenType type) { tok.Type = (TokenType)type; } + } public class XmlDocument : SourceDocument { - public XmlDocument (string fullPath, string editorPath) : base (fullPath, editorPath) { - - } - protected override Tokenizer CreateTokenizer() => new XmlTokenizer (); + public XmlDocument (string fullPath, string editorPath) : base (fullPath, editorPath) { } protected override SyntaxAnalyser CreateSyntaxAnalyser() => new XmlSyntaxAnalyser (this); public override string GetTokenTypeString (TokenType tokenType) => ((XmlTokenType)tokenType).ToString(); - public override IList GetSuggestions (CharLocation loc) { + public override IList GetSuggestions (Token currentToken, SyntaxNode CurrentNode, CharLocation loc) { /*currentToken = FindTokenIncludingPosition (pos); currentNode = FindNodeIncludingPosition (pos);*/ if (currentToken.GetTokenType() == XmlTokenType.EndElementOpen && @@ -37,7 +36,7 @@ namespace CrowEdit.Xml } return null; } - public override bool TryGetCompletionForCurrentToken (object suggestion, out TextChange change, out TextSpan? newSelection) { + public override bool TryCompleteToken (Token tok, SyntaxNode node, object suggestion, out TextChange change, out TextSpan? newSelection) { newSelection = null; change = default; @@ -46,40 +45,40 @@ namespace CrowEdit.Xml if (selectedSugg == null) return false; - Token tok = CurrentToken; XmlTokenType tokType = tok.GetTokenType(); if (tokType.HasFlag(XmlTokenType.WhiteSpace)) { - if (typeof(ElementTagSyntax).IsAssignableFrom(CurrentNode?.GetType())) { - ElementTagSyntax ets = CurrentNode as ElementTagSyntax; + + if (typeof(ElementTagSyntax).IsAssignableFrom(node?.GetType())) { + ElementTagSyntax ets = node as ElementTagSyntax; if (ets.name.HasValue) { - change = new TextChange (currentToken.End, 0, selectedSugg + "=\"\""); + change = new TextChange (tok.End, 0, selectedSugg + "=\"\""); newSelection = TextSpan.FromStartAndLength(change.End2 - 1); } else - change = new TextChange (currentToken.End, 0, selectedSugg + " "); + change = new TextChange (tok.End, 0, selectedSugg + " "); } else { - change = new TextChange (currentToken.End, 0, selectedSugg); + change = new TextChange (tok.End, 0, selectedSugg); } } else if (tokType == XmlTokenType.EndElementOpen) { - change = new TextChange (currentToken.End, 0, selectedSugg + ">"); + change = new TextChange (tok.End, 0, selectedSugg + ">"); } else if (tokType == XmlTokenType.ElementName) { - if (CurrentNode is ElementEndTagSyntax) - change = new TextChange (currentToken.Start, currentToken.Length, selectedSugg + ">"); + if (node is ElementEndTagSyntax) + change = new TextChange (tok.Start, tok.Length, selectedSugg + ">"); else { - change = new TextChange (currentToken.Start, currentToken.Length, selectedSugg + ">"); + change = new TextChange (tok.Start, tok.Length, selectedSugg + ">"); newSelection = TextSpan.FromStartAndLength (change.End2 - 1); } - } else if (CurrentNode is AttributeSyntax attrib) { + } else if (node is AttributeSyntax attrib) { if (tokType == XmlTokenType.AttributeName) { if (attrib.ValueToken.HasValue) { - change = new TextChange (currentToken.Start, currentToken.Length, selectedSugg); + change = new TextChange (tok.Start, tok.Length, selectedSugg); newSelection = new TextSpan( attrib.ValueToken.Value.Start + change.CharDiff + 1, attrib.ValueToken.Value.End + change.CharDiff - 1 ); } else { - change = new TextChange (currentToken.Start, currentToken.Length, selectedSugg + "=\"\""); - newSelection = TextSpan.FromStartAndLength (currentToken.Start + selectedSugg.Length + 2); + change = new TextChange (tok.Start, tok.Length, selectedSugg + "=\"\""); + newSelection = TextSpan.FromStartAndLength (tok.Start + selectedSugg.Length + 2); } } else { int offset = 1; @@ -88,40 +87,40 @@ namespace CrowEdit.Xml offset = 0; } if (tokType == XmlTokenType.AttributeValueOpen) - change = new TextChange (currentToken.End, 0, selectedSugg); + change = new TextChange (tok.End, 0, selectedSugg); else if (tokType == XmlTokenType.AttributeValue) - change = new TextChange (currentToken.Start, currentToken.Length, selectedSugg); + change = new TextChange (tok.Start, tok.Length, selectedSugg); newSelection = TextSpan.FromStartAndLength (change.End2 + offset); } } else if (tokType == XmlTokenType.ElementOpen) { - change = new TextChange (currentToken.End, 0, selectedSugg + " "); + change = new TextChange (tok.End, 0, selectedSugg + " "); } else - change = new TextChange (currentToken.Start, currentToken.Length, selectedSugg); + change = new TextChange (tok.Start, tok.Length, selectedSugg); return true; /***********************************************/ - /*if (currentToken.GetTokenType() == XmlTokenType.ElementOpen || - currentToken.GetTokenType() == XmlTokenType.WhiteSpace || - currentToken.GetTokenType() == XmlTokenType.AttributeValueOpen) { - change = new TextChange (currentToken.End, 0, selectedSugg); + /*if (tok.GetTokenType() == XmlTokenType.ElementOpen || + tok.GetTokenType() == XmlTokenType.WhiteSpace || + tok.GetTokenType() == XmlTokenType.AttributeValueOpen) { + change = new TextChange (tok.End, 0, selectedSugg); return true; } - if (currentToken.GetTokenType() == XmlTokenType.AttributeName && CurrentNode is AttributeSyntax attrib) { + if (tok.GetTokenType() == XmlTokenType.AttributeName && CurrentNode is AttributeSyntax attrib) { if (attrib.ValueToken.HasValue) { - change = new TextChange (currentToken.Start, currentToken.Length, selectedSugg); + change = new TextChange (tok.Start, tok.Length, selectedSugg); newSelection = new TextSpan( attrib.ValueToken.Value.Start + change.CharDiff + 1, attrib.ValueToken.Value.End + change.CharDiff - 1 ); } else { - change = new TextChange (currentToken.Start, currentToken.Length, selectedSugg + "=\"\""); - newSelection = TextSpan.FromStartAndLength (currentToken.Start + selectedSugg.Length + 2); + change = new TextChange (tok.Start, tok.Length, selectedSugg + "=\"\""); + newSelection = TextSpan.FromStartAndLength (tok.Start + selectedSugg.Length + 2); } return true; } - change = new TextChange (currentToken.Start, currentToken.Length, selectedSugg); + change = new TextChange (tok.Start, tok.Length, selectedSugg); return true;*/ } @@ -145,6 +144,6 @@ namespace CrowEdit.Xml return Colors.Red; } - protected bool previousTokHasFlag(XmlTokenType flag) => previousToken.HasValue && previousToken.Value.Type.HasFlag(flag); + //protected bool previousTokHasFlag(XmlTokenType flag) => previousToken.HasValue && previousToken.Value.Type.HasFlag(flag); } } \ No newline at end of file diff --git a/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxAnalyser.cs b/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxAnalyser.cs index d51c085..4083aa9 100644 --- a/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxAnalyser.cs +++ b/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxAnalyser.cs @@ -4,23 +4,23 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection.Metadata.Ecma335; using CrowEditBase; namespace CrowEdit.Xml { public class XmlSyntaxAnalyser : SyntaxAnalyser { - public XmlSyntaxAnalyser (XmlDocument source) : base (source) { - this.source = source; - } + public XmlSyntaxAnalyser (XmlDocument document) : base (document) {} public virtual void ProcessAttributeValueSyntax(AttributeSyntax attrib) { attrib.valueTok = tokIdx - attrib.TokenIndexBase; } - public override void Process () { - XmlDocument xmlDoc = source as XmlDocument; - currentNode = Root = new XMLRootSyntax (xmlDoc); + public override SyntaxRootNode Process () { + Tokenizer tokenizer = new XmlTokenizer(); + Token[] tokens = tokenizer.Tokenize(source.Span); + + currentNode = Root = new XMLRootSyntax (source, tokens); currentLine = 0; tokIdx = 0; - tokens = source.Tokens; while (tokIdx < tokens.Length) { if (curTok.Type == TokenType.LineBreak) @@ -35,18 +35,18 @@ namespace CrowEdit.Xml tag.name = tokIdx - tag.TokenIndexBase; else if (curTok.GetTokenType() == XmlTokenType.ClosingSign) { tag.close = tokIdx - tag.TokenIndexBase; - setEndLineForCurrentNode (); + finishCurrentNode (); currentNode.RemoveChild (tag); currentNode = currentNode.AddChild (new ElementSyntax (tag)); } else if (curTok.GetTokenType() == XmlTokenType.EmptyElementClosing) { - setEndLineForCurrentNode (); + finishCurrentNode (); currentNode.RemoveChild (tag); currentNode = currentNode.AddChild (new EmptyElementSyntax (tag)); setCurrentNodeEndLine (currentLine); currentNode = currentNode.Parent; } else { addException ("Unexpected Token"); - setEndLineForCurrentNode (-1); + finishCurrentNode (-1); continue; } } else if (currentNode is ElementSyntax elt) { @@ -55,6 +55,10 @@ namespace CrowEdit.Xml else if (curTok.GetTokenType() == XmlTokenType.EndElementOpen) { elt.EndTag = new ElementEndTagSyntax (currentLine, tokIdx); currentNode = elt.AddChild (elt.EndTag); + /*} else { + finishCurrentNode(-1); + addException ("Unmatching open/closing element name"); + }*/ } } else if (currentNode is AttributeSyntax attrib) { if (curTok.GetTokenType() == XmlTokenType.EqualSign) @@ -68,10 +72,10 @@ namespace CrowEdit.Xml ProcessAttributeValueSyntax (attrib); else if (curTok.GetTokenType() == XmlTokenType.AttributeValueClose) { attrib.valueClose = tokIdx - attrib.TokenIndexBase; - setEndLineForCurrentNode (); + finishCurrentNode (); } else { addException ("Unexpected Token"); - setEndLineForCurrentNode (-1); + finishCurrentNode (-1); continue; } } else if (currentNode is ElementEndTagSyntax eltEndTag) { @@ -80,11 +84,11 @@ namespace CrowEdit.Xml else if (curTok.GetTokenType() == XmlTokenType.ClosingSign) { eltEndTag.close = tokIdx - eltEndTag.TokenIndexBase; //go up 2 times - setEndLineForCurrentNode (); setEndLineForCurrentNode (); + finishCurrentNode (); finishCurrentNode (); } else { addException ("Unexpected Token"); - setEndLineForCurrentNode (-1); - setEndLineForCurrentNode (-1); + finishCurrentNode (-1); + finishCurrentNode (-1); continue; } } else if (currentNode is XMLRootSyntax) { @@ -104,14 +108,14 @@ namespace CrowEdit.Xml pi.name = tokIdx - pi.TokenIndexBase; else if (curTok.GetTokenType() == XmlTokenType.PI_End) { pi.PIClose = tokIdx - pi.TokenIndexBase; - setEndLineForCurrentNode (); + finishCurrentNode (); } else if (curTok.GetTokenType() == XmlTokenType.AttributeName) { AttributeSyntax attribute = new AttributeSyntax (currentLine, tokIdx); attribute.name = 0; currentNode = currentNode.AddChild (attribute); } else { addException ("Unexpected Token"); - setEndLineForCurrentNode (-1); + finishCurrentNode (-1); continue; } } @@ -120,11 +124,12 @@ namespace CrowEdit.Xml } while (currentNode.Parent != null) { if (!currentNode.TokenCount.HasValue) - setEndLineForCurrentNode (-1); + finishCurrentNode (-1); else currentNode = currentNode.Parent; } setCurrentNodeEndLine (currentLine); + return Root; } } } \ No newline at end of file diff --git a/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxNodes.cs b/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxNodes.cs index 892347f..928b722 100644 --- a/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxNodes.cs +++ b/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxNodes.cs @@ -1,6 +1,7 @@ // Copyright (c) 2013-2021 Bruyère Jean-Philippe // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; using System.Linq; using Crow.Text; using CrowEditBase; @@ -9,9 +10,7 @@ namespace CrowEdit.Xml { public class XMLRootSyntax : SyntaxRootNode { - public XMLRootSyntax (XmlDocument source) - : base (source) { - } + public XMLRootSyntax (ReadOnlyMemory source, Token[] tokens) : base (source, tokens) { } } public class ProcessingInstructionSyntax : SyntaxNode { public int? PIClose, name; diff --git a/ui/windows/winLogs.crow b/ui/windows/winLogs.crow index 80cedd3..98dc2f6 100644 --- a/ui/windows/winLogs.crow +++ b/ui/windows/winLogs.crow @@ -1,88 +1,87 @@  - - - + + + + + + + + + + + + + + + + + + + + + + -- 2.47.3