From: Jean-Philippe Bruyère Date: Tue, 25 Feb 2025 15:47:27 +0000 (+0100) Subject: move lines handling in TextBuffer X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=93f4260651540c0c8994361a891ed610d367ee46;p=jp%2Fcrowedit.git move lines handling in TextBuffer --- diff --git a/CrowEditBase/src/Compiler/SourceDocument.cs b/CrowEditBase/src/Compiler/SourceDocument.cs index 741db82..32e75c2 100644 --- a/CrowEditBase/src/Compiler/SourceDocument.cs +++ b/CrowEditBase/src/Compiler/SourceDocument.cs @@ -47,7 +47,6 @@ namespace CrowEditBase public Token[] Tokens => tokens; public IEnumerable SyntaxRootChildNodes => root?.children; - public LineCollection Lines => lines; public Token FindTokenIncludingPosition (int pos) { if (pos == 0 || tokens == null || tokens.Length == 0) return default; @@ -65,7 +64,7 @@ namespace CrowEditBase /// /// if outermost is true, return oldest ancestor exept root node, useful for folding. /// - public SyntaxNode FindNodeIncludingPosition (int pos, bool outerMost = false) { + /*public SyntaxNode FindNodeIncludingPosition (int pos, bool outerMost = false) { if (root == null) return null; if (!root.Contains (pos)) @@ -90,14 +89,15 @@ namespace CrowEditBase if (!root.Contains (span)) return null; return root.FindNodeIncludingSpan (span); - } + }*/ + protected override void reloadFromFile () { base.reloadFromFile (); parse (); } protected override void apply(TextChange change) { - SyntaxNode editedNode = FindNodeIncludingSpan (new TextSpan (change.Start, change.End)); + SyntaxNode editedNode = root?.FindNodeIncludingSpan (new TextSpan (change.Start, change.End)); base.apply(change); @@ -153,10 +153,10 @@ namespace CrowEditBase } internal void updateCurrentTokAndNode (CharLocation loc) { - int pos = lines.GetAbsolutePosition(loc); + int pos = buffer.GetAbsolutePosition(loc); if (tokens.Length > 0) { currentTokenIndex = FindTokenIndexIncludingPosition (pos); - CurrentNode = FindNodeIncludingSpan (currentToken.Span); + CurrentNode = root?.FindNodeIncludingSpan (currentToken.Span); NotifyValueChanged ("CurrentTokenString", (object)CurrentTokenString); //NotifyValueChanged ("CurrentTokenType", (uint)(currentToken.Type)>>8); NotifyValueChanged ("CurrentTokenType", (object)GetTokenTypeString(currentToken.Type)); diff --git a/CrowEditBase/src/Compiler/SyntaxNode.cs b/CrowEditBase/src/Compiler/SyntaxNode.cs index 0d41864..4de25e4 100644 --- a/CrowEditBase/src/Compiler/SyntaxNode.cs +++ b/CrowEditBase/src/Compiler/SyntaxNode.cs @@ -1,4 +1,4 @@ -// 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; @@ -9,26 +9,6 @@ using Crow; namespace CrowEditBase { - public abstract class SyntaxRootNode : SyntaxNode { - protected readonly SourceDocument source; - public SyntaxRootNode (SourceDocument source) { - this.source = source; - } - public override int TokenIndexBase => 0; - public override int? TokenCount { get => Math.Max (0, source.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 GetText(TextSpan span) => - source.GetText(span); - public string GetTokenString(Token tok) => - source.GetText(tok.Span).ToString(); - } public class SyntaxNode : CrowEditComponent { internal SyntaxNode () {} public SyntaxNode (int startLine, int tokenBase, int? lastTokenIdx = null) { @@ -37,7 +17,9 @@ namespace CrowEditBase if (lastTokenIdx.HasValue) TokenCount = lastTokenIdx - tokenBase; } + bool _isExpanded; + public bool isExpanded { get => _isExpanded; set { diff --git a/CrowEditBase/src/Compiler/SyntaxRootNode.cs b/CrowEditBase/src/Compiler/SyntaxRootNode.cs new file mode 100644 index 0000000..7955c33 --- /dev/null +++ b/CrowEditBase/src/Compiler/SyntaxRootNode.cs @@ -0,0 +1,29 @@ +// 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 Crow.Text; + +namespace CrowEditBase +{ + public abstract class SyntaxRootNode : SyntaxNode { + protected readonly SourceDocument source; + public SyntaxRootNode (SourceDocument source) { + this.source = source; + } + public override int TokenIndexBase => 0; + public override int? TokenCount { get => Math.Max (0, source.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 GetText(TextSpan span) => + source.GetText(span); + public string GetTokenString(Token tok) => + source.GetText(tok.Span).ToString(); + } +} \ No newline at end of file diff --git a/CrowEditBase/src/Editor.cs b/CrowEditBase/src/Editor.cs index f447ad5..00f2e37 100644 --- a/CrowEditBase/src/Editor.cs +++ b/CrowEditBase/src/Editor.cs @@ -384,7 +384,7 @@ namespace Crow } //} - if (document.Lenght > 0) { + if (document.Length > 0) { Foreground?.SetAsSource (IFace, gr); TextExtents extents; @@ -839,7 +839,7 @@ namespace Crow break; case Key.Delete: if (selection.IsEmpty) { - if (selection.Start == document.Lenght) + if (selection.Start == document.Length) return; if (CurrentLoc.Value.Column >= document.GetLine (CurrentLoc.Value.Line).Length) update (new TextChange (selection.Start, document.GetLine (CurrentLoc.Value.Line).LineBreakLength, "")); diff --git a/CrowEditBase/src/TextBuffer.cs b/CrowEditBase/src/TextBuffer.cs new file mode 100644 index 0000000..505098e --- /dev/null +++ b/CrowEditBase/src/TextBuffer.cs @@ -0,0 +1,93 @@ +// Copyright (c) 2021-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Linq; +using System.Collections.Generic; +using Crow.Text; + +namespace CrowEditBase +{ + public class TextBuffer { + static int bufferExpension = 100; + int length; + Memory buffer; + ReadOnlyMemory origBuffer; + LineCollection lines; + public bool mixedLineBreak = false; + public string lineBreak = null; + + 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 int LinesCount => lines.Count; + public int Length => length; + public ReadOnlyMemory ReadOnlyCopy { + get { + return ReadOnlySpan.ToArray(); + } + } + public void ResetDirtyState () { + origBuffer = buffer.ToArray(); + } + public TextBuffer(ReadOnlySpan origText) { + length = origText.Length; + buffer = new char[length + bufferExpension]; + origText.CopyTo(buffer.Span); + lines = new LineCollection (10); + if (IsEmpty) + lines.Add (new TextLine (0, 0, 0)); + else + lines.Update (Span); + } + public void Update (TextChange change) { + ReadOnlySpan orig = buffer.Span; + char[] newBuff = null; + Span tmp; + if (buffer.Length < length + change.CharDiff) { + newBuff = new char[length + change.CharDiff + bufferExpension]; + tmp = newBuff; + orig.Slice(0, change.Start).CopyTo(tmp); + if (change.CharDiff == 0) + orig.Slice(change.End, length - change.End).CopyTo(tmp.Slice(change.End)); + } else + tmp = buffer.Span; + + if (change.CharDiff != 0) + orig.Slice(change.End, length - change.End).CopyTo(tmp.Slice(change.End2)); + if (!string.IsNullOrEmpty (change.ChangedText)) + change.ChangedText.AsSpan ().CopyTo (tmp.Slice (change.Start)); + + if (newBuff != null) + buffer = newBuff; + length += change.CharDiff; + + lines.Update (change); + } + public string GetLineBreak () { + if (string.IsNullOrEmpty (lineBreak)) { + mixedLineBreak = false; + + if (lines.Count == 0 || lines[0].LineBreakLength == 0) + lineBreak = Environment.NewLine; + else { + lineBreak = ReadOnlySpan.GetLineBreak (lines[0]).ToString (); + for (int i = 1; i < lines.Count; i++) { + if (!ReadOnlySpan.GetLineBreak (lines[i]).SequenceEqual (lineBreak)) { + mixedLineBreak = true; + break; + } + } + } + } + return lineBreak; + } + public CharLocation GetLocation (int absolutePosition) => lines.GetLocation (absolutePosition); + public TextLine GetLine (int index) => lines[index]; + 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(); + } +} \ No newline at end of file diff --git a/CrowEditBase/src/TextDocument.cs b/CrowEditBase/src/TextDocument.cs index 9242e5b..43f14c2 100644 --- a/CrowEditBase/src/TextDocument.cs +++ b/CrowEditBase/src/TextDocument.cs @@ -11,48 +11,10 @@ using Crow.Text; using static CrowEditBase.CrowEditBase; using System.Collections.Immutable; using System.Reflection.Metadata; +using System.Diagnostics; namespace CrowEditBase { - public class TextBuffer { - static int bufferExpension = 100; - int lenght; - Memory buffer; - ReadOnlyMemory origBuffer; - public Span Span => buffer.Span.Slice(0, lenght); - public bool IsEmpty => lenght == 0; - public bool IsDirty => origBuffer.Span.Equals(buffer.Span, StringComparison.Ordinal); - public void ResetDirtyState () { - origBuffer = buffer.ToArray(); - } - public TextBuffer(ReadOnlySpan origText) { - lenght = origText.Length; - buffer = new char[lenght + bufferExpension]; - origText.CopyTo(buffer.Span); - } - public void Update (TextChange change) { - ReadOnlySpan orig = buffer.Span; - char[] newBuff = null; - Span tmp; - if (buffer.Length < lenght + change.CharDiff) { - newBuff = new char[lenght + change.CharDiff + bufferExpension]; - tmp = newBuff; - orig.Slice(0, change.Start).CopyTo(tmp); - if (change.CharDiff == 0) - orig.Slice(change.End, lenght - change.End).CopyTo(tmp.Slice(change.End)); - } else - tmp = buffer.Span; - - if (change.CharDiff != 0) - orig.Slice(change.End, lenght - change.End).CopyTo(tmp.Slice(change.End2)); - if (!string.IsNullOrEmpty (change.ChangedText)) - change.ChangedText.AsSpan ().CopyTo (tmp.Slice (change.Start)); - - if (newBuff != null) - buffer = newBuff; - lenght += change.CharDiff; - } - } public class TextDocument : Document { public TextDocument (string fullPath, string editorPath = "default") : base (fullPath, editorPath) { @@ -60,19 +22,8 @@ namespace CrowEditBase } protected TextBuffer buffer; - public ReadOnlySpan source => buffer.Span; - + public ReadOnlySpan source => buffer.ReadOnlySpan; System.Text.Encoding encoding = System.Text.Encoding.UTF8; - protected bool mixedLineBreak = false; - protected string lineBreak = null; - - - - -// NotifyValueChanged ("IsDirty", IsDirty); -// CMDSave.CanExecute = IsDirty; - - protected LineCollection lines; public override bool IsDirty => buffer.IsDirty; /// dictionnary of object per document client, when not null, client must reload content of document. @@ -116,7 +67,6 @@ namespace CrowEditBase } - protected override void writeToDisk () { using (Stream s = new FileStream(FullPath, FileMode.Create)) { using (StreamWriter sw = new StreamWriter (s, encoding)) @@ -134,6 +84,12 @@ 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() { @@ -214,7 +170,6 @@ namespace CrowEditBase protected virtual void apply (TextChange change) { buffer.Update(change); - lines.Update (change); NotifyValueChanged ("IsDirty", IsDirty); CMDSave.CanExecute = IsDirty; @@ -235,40 +190,11 @@ namespace CrowEditBase protected void onTextChanged (object sender, TextChangeEventArgs e) { applyTextChange (e.Change, sender); } - protected void getLines () { - documentRWLock.EnterWriteLock (); - if (lines == null) - lines = new LineCollection (10); - else - lines.Clear (); - if (buffer.IsEmpty) - lines.Add (new TextLine (0, 0, 0)); - else - lines.Update (source); - documentRWLock.ExitWriteLock (); - } public string GetLineBreak () { documentRWLock.EnterReadLock (); try { - if (string.IsNullOrEmpty (lineBreak)) { - mixedLineBreak = false; - - if (lines.Count == 0 || lines[0].LineBreakLength == 0) - lineBreak = Environment.NewLine; - else { - lineBreak = source.GetLineBreak (lines[0]).ToString (); - - for (int i = 1; i < lines.Count; i++) { - ReadOnlySpan lb = source.GetLineBreak (lines[i]); - if (!lb.SequenceEqual (lineBreak)) { - mixedLineBreak = true; - break; - } - } - } - } - return lineBreak; + return buffer.GetLineBreak(); } finally { documentRWLock.ExitReadLock(); } @@ -276,7 +202,7 @@ namespace CrowEditBase public CharLocation GetLocation (int absolutePosition) { documentRWLock.EnterReadLock (); try { - return lines.GetLocation (absolutePosition); + return buffer.GetLocation (absolutePosition); } finally { documentRWLock.ExitReadLock(); } @@ -284,7 +210,7 @@ namespace CrowEditBase public int GetAbsolutePosition (CharLocation loc) { documentRWLock.EnterReadLock (); try { - return lines.GetAbsolutePosition (loc); + return buffer.GetAbsolutePosition (loc); } finally { documentRWLock.ExitReadLock(); } @@ -293,7 +219,7 @@ namespace CrowEditBase get { documentRWLock.EnterReadLock (); try { - return new CharLocation (lines.Count - 1, lines[lines.Count - 1].Length); + return buffer.EndLocation; } finally { documentRWLock.ExitReadLock(); } @@ -301,21 +227,19 @@ namespace CrowEditBase } public int LinesCount { get { - if (lines == null) - getLines(); documentRWLock.EnterReadLock (); try { - return lines.Count; + return buffer.LinesCount; } finally { documentRWLock.ExitReadLock(); } } } - public int Lenght { + public int Length { get { documentRWLock.EnterReadLock (); try { - return source.Length; + return buffer.Length; } finally { documentRWLock.ExitReadLock(); } @@ -324,7 +248,7 @@ namespace CrowEditBase public TextLine GetLine (int index) { documentRWLock.EnterReadLock (); try { - return lines[index]; + return buffer.GetLine(index); } finally { documentRWLock.ExitReadLock(); } @@ -357,13 +281,13 @@ namespace CrowEditBase public virtual CharLocation GetWordStart (CharLocation loc) { documentRWLock.EnterReadLock (); try { - int pos = lines.GetAbsolutePosition (loc); + int pos = buffer.GetAbsolutePosition (loc); //skip white spaces while (pos > 0 && !char.IsLetterOrDigit (source[pos-1])) pos--; while (pos > 0 && char.IsLetterOrDigit (source[pos-1])) pos--; - return lines.GetLocation (pos); + return buffer.GetLocation (pos); } finally { documentRWLock.ExitReadLock(); } @@ -371,13 +295,13 @@ namespace CrowEditBase public virtual CharLocation GetWordEnd (CharLocation loc) { documentRWLock.EnterReadLock (); try { - int pos = lines.GetAbsolutePosition (loc); + int pos = buffer.GetAbsolutePosition (loc); //skip white spaces - while (pos < Lenght - 1 && !char.IsLetterOrDigit (source[pos])) + while (pos < Length - 1 && !char.IsLetterOrDigit (source[pos])) pos++; - while (pos < Lenght - 1 && char.IsLetterOrDigit (source[pos])) + while (pos < Length - 1 && char.IsLetterOrDigit (source[pos])) pos++; - return lines.GetLocation (pos); + return buffer.GetLocation (pos); } finally { documentRWLock.ExitReadLock(); }