From 92615c83a0c2013988f769e157ed34c0954980bf Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jean-Philippe=20Bruy=C3=A8re?= Date: Thu, 3 Jul 2025 19:52:58 +0200 Subject: [PATCH] source structure and code clean --- CrowEditBase/src/Compiler/SourceDocument.cs | 62 ++--- .../Compiler/SyntaxNodes/MultiNodeSyntax.cs | 139 ++++++++++ .../Compiler/SyntaxNodes/SingleTokenSyntax.cs | 23 ++ .../Compiler/{ => SyntaxNodes}/SyntaxNode.cs | 239 ++++-------------- .../{ => SyntaxNodes}/SyntaxRootNode.cs | 8 +- CrowEditBase/src/Document.cs | 53 ++-- CrowEditBase/src/TextDocument.cs | 68 ++--- .../src/Parsing/CSSyntaxAnalyser.cs | 31 +++ .../CsharpSyntaxWalkerBridge.cs} | 80 +----- .../CERoslynPlugin/src/Parsing/SyntaxNodes.cs | 38 +++ .../CEXmlPlugin/src/Parsing/XmlSyntaxNodes.cs | 3 - 11 files changed, 388 insertions(+), 356 deletions(-) create mode 100644 CrowEditBase/src/Compiler/SyntaxNodes/MultiNodeSyntax.cs create mode 100644 CrowEditBase/src/Compiler/SyntaxNodes/SingleTokenSyntax.cs rename CrowEditBase/src/Compiler/{ => SyntaxNodes}/SyntaxNode.cs (55%) rename CrowEditBase/src/Compiler/{ => SyntaxNodes}/SyntaxRootNode.cs (98%) create mode 100644 plugins/CERoslynPlugin/src/Parsing/CSSyntaxAnalyser.cs rename plugins/CERoslynPlugin/src/{CSSyntaxAnalyser.cs => Parsing/CsharpSyntaxWalkerBridge.cs} (67%) create mode 100644 plugins/CERoslynPlugin/src/Parsing/SyntaxNodes.cs diff --git a/CrowEditBase/src/Compiler/SourceDocument.cs b/CrowEditBase/src/Compiler/SourceDocument.cs index 5db2e43..1c5f98f 100644 --- a/CrowEditBase/src/Compiler/SourceDocument.cs +++ b/CrowEditBase/src/Compiler/SourceDocument.cs @@ -16,35 +16,49 @@ using System.Threading.Tasks; namespace CrowEditBase { public abstract class SourceDocument : TextDocument { + #region CTOR public SourceDocument (string fullPath, string editorPath = "#ui.sourceEditor.itmp") : base (fullPath, editorPath) { } + #endregion + protected SyntaxRootNode root; + CancellationTokenSource cancelSource; + Task backgroundCompilationTask; + public SyntaxRootNode Root { + get => root; + } + public bool IsParsed => root != null && Tokens.Length > 0; + public ReadOnlySpan Tokens => root.Tokens; + public IEnumerable SyntaxRootChildNodes => root?.children; + public SyntaxException CurrentException { + get => CrowEditBase.App.CurrentException; + set { + CrowEditBase.App.CurrentException = value; + SetLocation(value.Location); + } + } + + #region commands public Command CMDRefreshSyntaxTree; - protected override void initCommands() { base.initCommands(); CMDRefreshSyntaxTree = new ActionCommand ("Reparse", parse, "#icons.refresh.svg", true); } - public SyntaxRootNode Root { - get => root; - } - - public bool IsParsed => root != null && Tokens.Length > 0; - public ReadOnlySpan Tokens => root.Tokens; - public IEnumerable SyntaxRootChildNodes => root?.children; + #endregion + #region token & node searching + public Token GetTokenByIndex(int tokIdx) => IsParsed && tokIdx >= 0 ? + Tokens[Math.Min(Tokens.Length - 1, tokIdx)] : default; public Token FindTokenIncludingPosition (int pos) { if (!IsParsed || pos == 0 || Tokens.Length == 0) return default; int idx = Tokens.BinarySearch(new Token (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 (!IsParsed || pos == 0 || Tokens.Length == 0) return default; @@ -55,6 +69,7 @@ namespace CrowEditBase /// if outermost is true, return oldest ancestor exept root node, useful for folding. /// public SyntaxNode FindNodeIncludingPosition (int pos, bool outerMost = false) { + if (root == null) return null; if (!root.Contains (pos)) @@ -66,21 +81,9 @@ namespace CrowEditBase } return sn; } - /*public T FindNodeIncludingPosition (int pos) { - if (root == null) - return default; - if (!root.Contains (pos)) - return default; - return root.FindNodeIncludingPosition (pos); - } - public SyntaxNode FindNodeIncludingSpan (TextSpan span) { - if (root == null) - return null; - if (!root.Contains (span)) - return null; - return root.FindNodeIncludingSpan (span); - }*/ + #endregion + #region TextDocument implementation protected override void reloadFromFile () { base.reloadFromFile (); parse (); @@ -124,7 +127,7 @@ namespace CrowEditBase //Console.WriteLine ($"CurrentToken: idx({currentTokenIndex}) {currentToken} {RootNode.Root.GetTokenStringByIndex(currentTokenIndex)}"); } - + #endregion public virtual Color GetColorForToken (Token token) { @@ -143,8 +146,6 @@ namespace CrowEditBase //protected abstract Tokenizer CreateTokenizer (); protected abstract SyntaxAnalyser CreateSyntaxAnalyser (); public abstract IList GetSuggestions (int absoluteTextPos, int currentTokenIndex, SyntaxNode currentNode, CharLocation loc); - CancellationTokenSource cancelSource; - Task backgroundCompilationTask; protected virtual async void parse () { if (backgroundCompilationTask != null && !backgroundCompilationTask.IsCompleted) { cancelSource.Cancel(); @@ -170,13 +171,6 @@ namespace CrowEditBase NotifyValueChanged ("SyntaxRootChildNodes", SyntaxRootChildNodes); } - public SyntaxException CurrentException { - get => CrowEditBase.App.CurrentException; - set { - CrowEditBase.App.CurrentException = value; - SetLocation(value.Location); - } - } } } \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/SyntaxNodes/MultiNodeSyntax.cs b/CrowEditBase/src/Compiler/SyntaxNodes/MultiNodeSyntax.cs new file mode 100644 index 0000000..5081019 --- /dev/null +++ b/CrowEditBase/src/Compiler/SyntaxNodes/MultiNodeSyntax.cs @@ -0,0 +1,139 @@ +// 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 Crow.Text; +using Crow; +using System.Diagnostics; + +namespace CrowEditBase +{ + public abstract class MultiNodeSyntax : SyntaxNode { + #region CTOR + public MultiNodeSyntax() { } + #endregion + + internal bool isFolded; + bool _isExpanded; + + internal List children = new List (); + public IEnumerable Children => children; + public SyntaxNode AddChild (SyntaxNode child) { + children.Add (child); + child.Parent = this; + return child; + } + public void RemoveChild (SyntaxNode child) { + children.Remove (child); + child.Parent = null; + } + public IEnumerable GetChilds () => children.OfType(); + + public bool ChildIs (int idx) => children.Count() <= idx ? false : children[idx] is T; + public bool ChildSequenceIs(params object[] sequ) { + if (children.Count != sequ.Length) + return false; + for (int i = 0; i < sequ.Length; i++) { + if (typeof(Type).IsAssignableFrom(sequ[i].GetType())) { + if ((sequ[i] as Type) != children[i].GetType()) + return false; + } else if (sequ[i].GetType().IsEnum) { + if (!(children[i] is SingleTokenSyntax tok) || (TokenType)sequ[i] != tok.Type) + return false; + } else + return false; + if (!children[i].IsComplete) + return false; + + } + return true; + } + + #region SyntaxNode implementation + public override bool isExpanded { + get => _isExpanded; + set { + bool expand = HasChilds ? value : false; + if (_isExpanded == expand) + return; + _isExpanded = value; + if (isExpanded && Parent is SyntaxNode sn) { + try { + sn.isExpanded = true; + } catch (Exception ex) { + Debug.WriteLine($"SyntaxNode expand to the top failed:{ex.Message}"); + Debug.WriteLine(ex.StackTrace); + } + + } + NotifyValueChanged (_isExpanded); + } + } + public override bool IsComplete { + get { + for (int i = 0; i < children.Count(); i++) { + if (!children[i].IsComplete) + return false; + } + return true; + } + } + public override bool HasChilds => children.Count > 0; + public override int SpanStart => HasChilds ? children[0].SpanStart : 0; + public override int SpanEnd => HasChilds ? children[children.Count - 1].SpanEnd : 0; + public override SyntaxNode FindNodeIncludingSpan (TextSpan span) { + foreach (SyntaxNode node in children) { + if (node.Contains (span)) + return node.FindNodeIncludingSpan (span); + } + return this; + } + public override SyntaxNode FindNodeIncludingPosition (int pos) { + foreach (SyntaxNode node in children) { + if (node.Contains (pos)) + return node.FindNodeIncludingPosition (pos); + } + return this; + } + #endregion + + public virtual bool IsFoldable => IsComplete && !(Parent != Root && Parent.StartLocation.Line == StartLocation.Line) && LineCount > 1; + public virtual void UnfoldToTheTop () { + isFolded = false; + Parent.UnfoldToTheTop (); + } + public virtual int FoldedLineCount { + get { + if (isFolded) + return LineCount; + int tmp = 0; + if (HasChilds) { + foreach (MultiNodeSyntax n in children.OfType().Where (c => c.IsFoldable)) + tmp += n.FoldedLineCount; + } + return tmp; + } + } + + public void ExpandToTheTop () { + isExpanded = true; + Parent?.ExpandToTheTop (); + } + public IEnumerable VisibleFoldableNodes { + get { + if (IsFoldable) { + yield return this; + } + if (!isFolded) { + foreach (MultiNodeSyntax n in Children.OfType()) { + foreach (MultiNodeSyntax folds in n.VisibleFoldableNodes) + yield return folds; + } + } + } + } + + } +} \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/SyntaxNodes/SingleTokenSyntax.cs b/CrowEditBase/src/Compiler/SyntaxNodes/SingleTokenSyntax.cs new file mode 100644 index 0000000..3253e9f --- /dev/null +++ b/CrowEditBase/src/Compiler/SyntaxNodes/SingleTokenSyntax.cs @@ -0,0 +1,23 @@ +// Copyright (c) 2021-2025 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +namespace CrowEditBase +{ + public class SingleTokenSyntax : SyntaxNode { + #region CTOR + public SingleTokenSyntax(Token tok) { + token = tok; + token.syntaxNode = this; + } + #endregion + + protected 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 + } +} \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/SyntaxNode.cs b/CrowEditBase/src/Compiler/SyntaxNodes/SyntaxNode.cs similarity index 55% rename from CrowEditBase/src/Compiler/SyntaxNode.cs rename to CrowEditBase/src/Compiler/SyntaxNodes/SyntaxNode.cs index ed777ea..092d54b 100644 --- a/CrowEditBase/src/Compiler/SyntaxNode.cs +++ b/CrowEditBase/src/Compiler/SyntaxNodes/SyntaxNode.cs @@ -1,29 +1,14 @@ // 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 Crow.Text; -using Crow; -using System.Diagnostics; namespace CrowEditBase { public class UnexpectedTokenSyntax : SingleTokenSyntax { public UnexpectedTokenSyntax(Token tok) : base (tok) { } } - public class SingleTokenSyntax : SyntaxNode { - protected Token token; - 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; - public SingleTokenSyntax(Token tok) { - token = tok; - token.syntaxNode = this; - } - } public class CommentTriviaSyntax : MultiNodeSyntax { bool block; public CommentTriviaSyntax(bool block) { @@ -32,136 +17,10 @@ namespace CrowEditBase public override bool IsComplete => true; } - public abstract class MultiNodeSyntax : SyntaxNode { - public MultiNodeSyntax() { } - internal List children = new List (); - public IEnumerable Children => children; - public SyntaxNode AddChild (SyntaxNode child) { - children.Add (child); - child.Parent = this; - return child; - } - public void RemoveChild (SyntaxNode child) { - children.Remove (child); - child.Parent = null; - } - public IEnumerable GetChilds () => children.OfType(); - - public bool ChildIs (int idx) => children.Count() <= idx ? false : children[idx] is T; - public bool ChildSequenceIs(params object[] sequ) { - if (children.Count != sequ.Length) - return false; - for (int i = 0; i < sequ.Length; i++) { - if (typeof(Type).IsAssignableFrom(sequ[i].GetType())) { - if ((sequ[i] as Type) != children[i].GetType()) - return false; - } else if (sequ[i].GetType().IsEnum) { - if (!(children[i] is SingleTokenSyntax tok) || (TokenType)sequ[i] != tok.Type) - return false; - } else - return false; - if (!children[i].IsComplete) - return false; - - } - return true; - } - public override bool IsComplete { - get { - for (int i = 0; i < children.Count(); i++) { - if (!children[i].IsComplete) - return false; - } - return true; - } - } - public override bool HasChilds => children.Count > 0; - public override int SpanStart => HasChilds ? children[0].SpanStart : 0; - public override int SpanEnd => HasChilds ? children[children.Count - 1].SpanEnd : 0; - /*public override int SpanEnd {//to be removed, used for debugging - get { - if (HasChilds) { - - if (children[children.Count - 1] == this) - System.Diagnostics.Debugger.Break(); - return children[children.Count - 1].SpanEnd; - } else return 0; - } - } */ - - internal bool isFolded; - public virtual bool IsFoldable => IsComplete && !(Parent != Root && Parent.StartLocation.Line == StartLocation.Line) && LineCount > 1; - public void ExpandToTheTop () { - isExpanded = true; - Parent?.ExpandToTheTop (); - } - public virtual void UnfoldToTheTop () { - isFolded = false; - Parent.UnfoldToTheTop (); - } - public IEnumerable VisibleFoldableNodes { - get { - if (IsFoldable) { - yield return this; - } - if (!isFolded) { - foreach (MultiNodeSyntax n in Children.OfType()) { - foreach (MultiNodeSyntax folds in n.VisibleFoldableNodes) - yield return folds; - } - } - } - } - - public virtual int FoldedLineCount { - get { - if (isFolded) - return LineCount; - int tmp = 0; - if (HasChilds) { - foreach (MultiNodeSyntax n in children.OfType().Where (c => c.IsFoldable)) - tmp += n.FoldedLineCount; - } - return tmp; - } - } - public override SyntaxNode FindNodeIncludingSpan (TextSpan span) { - foreach (SyntaxNode node in children) { - if (node.Contains (span)) - return node.FindNodeIncludingSpan (span); - } - return this; - } - public override SyntaxNode FindNodeIncludingPosition (int pos) { - foreach (SyntaxNode node in children) { - if (node.Contains (pos)) - return node.FindNodeIncludingPosition (pos); - } - return this; - } - bool _isExpanded; - public override bool isExpanded { - get => _isExpanded; - set { - bool expand = HasChilds ? value : false; - if (_isExpanded == expand) - return; - _isExpanded = value; - if (isExpanded && Parent is SyntaxNode sn) { - try { - sn.isExpanded = true; - } catch (Exception ex) { - Debug.WriteLine($"SyntaxNode expand to the top failed:{ex.Message}"); - Debug.WriteLine(ex.StackTrace); - } - - } - NotifyValueChanged (_isExpanded); - } - } - } + public abstract class SyntaxNode : CrowEditComponent { + public MultiNodeSyntax Parent { get; internal set; } #region Folding and ?expand? public virtual bool isExpanded { @@ -173,40 +32,19 @@ namespace CrowEditBase } } #endregion - public MultiNodeSyntax Parent { get; internal set; } + public virtual SyntaxRootNode Root => Parent.Root; public virtual TokenType Type => TokenType.Unknown; public virtual int SpanStart => 0; public virtual int SpanEnd => 0; - - public virtual bool HasChilds => false; public virtual bool IsComplete => false; + public virtual SyntaxNode FindNodeIncludingPosition (int pos) => this; + public virtual SyntaxNode FindNodeIncludingSpan (TextSpan span) => this; + public virtual SyntaxNode NextSiblingOrParentsNextSibling + => NextSibling ?? Parent.NextSiblingOrParentsNextSibling; - - /*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; - } - }*/ - - - protected Token getTokenByIndex (int idx) => Root.GetTokenByIndex(idx); - - - //public int IndexOf (SyntaxNode node) => children.IndexOf (node); public SyntaxNode NextSibling { get { if (Parent != null) { @@ -227,8 +65,46 @@ namespace CrowEditBase return null; } } - public virtual SyntaxNode NextSiblingOrParentsNextSibling - => NextSibling ?? Parent.NextSiblingOrParentsNextSibling; + + protected Token getTokenByIndex (int idx) => Root.GetTokenByIndex(idx); + + + public TextSpan Span => new TextSpan (SpanStart, SpanEnd); + public CharLocation StartLocation => Root.GetLocation(SpanStart); + public CharLocation EndLocation => Root.GetLocation(SpanEnd); + public int LineCount => EndLocation.Line - StartLocation.Line + 1; + + public bool Contains (int pos) => Span.Contains (pos); + public bool Contains (TextSpan span) => Span.Contains (span); + public string AsText() { + return Span.Length < 0 ? "" : Root.GetText(Span).ToString(); + } + public bool IsSimilar (SyntaxNode 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; @@ -240,10 +116,6 @@ namespace CrowEditBase } get => StartLine + lineCount - 1; }*/ - public TextSpan Span => new TextSpan (SpanStart, SpanEnd); - public CharLocation StartLocation => Root.GetLocation(SpanStart); - public CharLocation EndLocation => Root.GetLocation(SpanEnd); - public int LineCount => EndLocation.Line - StartLocation.Line + 1; /* public void Replace (SyntaxNode newNode) { @@ -286,27 +158,12 @@ namespace CrowEditBase return this is T tt ? tt : default; }*/ - public virtual SyntaxNode FindNodeIncludingPosition (int pos) => this; - public virtual SyntaxNode FindNodeIncludingSpan (TextSpan span) => this; - - public bool Contains (int pos) => Span.Contains (pos); - public bool Contains (TextSpan span) => Span.Contains (span); /*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}"; - public override string ToString() => $"{this.GetType().Name}"; - public string AsText() { - return Span.Length < 0 ? "" : Root.GetText(Span).ToString(); - } - public bool IsSimilar (SyntaxNode other) => this.GetType() == other?.GetType(); - - - public class CompareOnStartLine : IComparer - { - public int Compare(SyntaxNode x, SyntaxNode y) => x.StartLocation.Line - y.StartLocation.Line; - } + #endregion } } \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/SyntaxRootNode.cs b/CrowEditBase/src/Compiler/SyntaxNodes/SyntaxRootNode.cs similarity index 98% rename from CrowEditBase/src/Compiler/SyntaxRootNode.cs rename to CrowEditBase/src/Compiler/SyntaxNodes/SyntaxRootNode.cs index 2c177e2..2e7bf84 100644 --- a/CrowEditBase/src/Compiler/SyntaxRootNode.cs +++ b/CrowEditBase/src/Compiler/SyntaxNodes/SyntaxRootNode.cs @@ -7,20 +7,22 @@ using Crow.Text; namespace CrowEditBase { public abstract class SyntaxRootNode : MultiNodeSyntax { + #region CTOR public SyntaxRootNode (ReadOnlyTextBuffer buffer, Token[] tokens) { this.buffer = buffer; this.tokens = tokens; } + #endregion + protected readonly ReadOnlyTextBuffer buffer; protected Token[] tokens; + public ReadOnlySpan Tokens => tokens; + public override SyntaxRootNode Root => this; - - public override bool IsFoldable => false; public override void UnfoldToTheTop() {} public override SyntaxNode NextSiblingOrParentsNextSibling => null; - public ReadOnlySpan Tokens => tokens; public string GetTokenStringByIndex (int idx) => tokens != null ? diff --git a/CrowEditBase/src/Document.cs b/CrowEditBase/src/Document.cs index d4a032b..4dd192b 100644 --- a/CrowEditBase/src/Document.cs +++ b/CrowEditBase/src/Document.cs @@ -13,11 +13,17 @@ using static CrowEditBase.CrowEditBase; namespace CrowEditBase { public abstract class Document : CrowEditComponent { + #region CTOR public Document (string fullPath, string editorPath) { initCommands (); EditorPath = editorPath; FullPath = fullPath; } + #endregion + + DateTime accessTime; + string fullPath; + /// /// Editor used to open the document, can't be changed once opened /// @@ -34,12 +40,7 @@ namespace CrowEditBase public void EnterWriteLock () => documentRWLock.EnterWriteLock (); public void ExitWriteLock () => documentRWLock.ExitWriteLock (); - public abstract bool TryGetState (object client, out T state); - public abstract void RegisterClient (object client, bool initialState = false); - public abstract void UnregisterClient (object client); - DateTime accessTime; - string fullPath; public string FullPath { get => fullPath; @@ -60,25 +61,9 @@ namespace CrowEditBase public string Extension => System.IO.Path.GetExtension (FullPath); public bool ExternalyModified => File.Exists (FullPath) ? (DateTime.Compare (accessTime, System.IO.File.GetLastWriteTime (FullPath)) < 0) : false; - public void OnQueryClose (object sender, EventArgs e){ - CloseEvent.Raise (this, null); - } - protected abstract void saveFileDialog_OkClicked (object sender, EventArgs e); - public void SaveAs () { - App.LoadIMLFragment ( - @"" - ).DataSource = this; - } - public void Save () { - if (File.Exists (FullPath)) - writeToDisk (); - else - SaveAs (); - } + #region commands public Command CMDUndo, CMDRedo, CMDSave, CMDSaveAs; - Command CMDClose, CMDCloseOther; public CommandGroup TabCommands => new CommandGroup ( CMDClose, CMDCloseOther @@ -92,11 +77,19 @@ namespace CrowEditBase CMDClose = new ActionCommand ("Close", () => App.CloseDocument (this), "#icons.sign-out.svg"); CMDCloseOther = new ActionCommand ("Close Others", () => App.CloseOthers (this), "#icons.inbox.svg"); } + #endregion + + public abstract bool TryGetState (object client, out T state); + public abstract void RegisterClient (object client, bool initialState = false); + public abstract void UnregisterClient (object client); + protected abstract void saveFileDialog_OkClicked (object sender, EventArgs e); protected abstract void undo(); protected abstract void redo(); protected abstract void writeToDisk (); protected abstract void readFromDisk (); protected abstract void initNewFile (); + public abstract bool IsDirty { get; } + protected virtual void reloadFromFile () { documentRWLock.EnterWriteLock (); try { @@ -108,7 +101,21 @@ namespace CrowEditBase documentRWLock.ExitWriteLock (); } } - public abstract bool IsDirty { get; } + public void OnQueryClose (object sender, EventArgs e){ + CloseEvent.Raise (this, null); + } + public void SaveAs () { + App.LoadIMLFragment ( + @"" + ).DataSource = this; + } + public void Save () { + if (File.Exists (FullPath)) + writeToDisk (); + else + SaveAs (); + } public override string ToString() => FullPath; diff --git a/CrowEditBase/src/TextDocument.cs b/CrowEditBase/src/TextDocument.cs index fec865e..9f4603d 100644 --- a/CrowEditBase/src/TextDocument.cs +++ b/CrowEditBase/src/TextDocument.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2021 Jean-Philippe Bruyère +// Copyright (c) 2021-2025 Jean-Philippe Bruyère // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) @@ -21,21 +21,28 @@ namespace CrowEditBase } } public class TextDocument : Document { + #region CTOR public TextDocument (string fullPath, string editorPath = "default") : base (fullPath, editorPath) { reloadFromFile (); } + #endregion protected TextBuffer buffer; - public ReadOnlySpan source => buffer.ReadOnlySpan; - - public ReadOnlyTextBuffer ImmutableBufferCopy => new ReadOnlyTextBuffer(buffer.ReadOnlyCopy, buffer.GetLineListCopy()); + protected Stack undoStack = new Stack (); + protected Stack redoStack = new Stack (); System.Text.Encoding encoding = System.Text.Encoding.UTF8; + Dictionary> registeredClients = + new Dictionary>(); // dictionnary of object per document client, when not null, client must reload content of document. + protected bool disableTextChangedEvent = false; + public event EventHandler TextChanged; + public ReadOnlySpan source => buffer.ReadOnlySpan; + public ReadOnlyTextBuffer ImmutableBufferCopy => new ReadOnlyTextBuffer(buffer.ReadOnlyCopy, buffer.GetLineListCopy()); + + #region Document interface implementation public override bool IsDirty => buffer.IsDirty; - /// dictionnary of object per document client, when not null, client must reload content of document. - Dictionary> registeredClients = new Dictionary>(); public override bool TryGetState(object client, out T state) { state = default; if (documentRWLock.TryEnterReadLock (10)) { @@ -62,23 +69,6 @@ namespace CrowEditBase registeredClients.Remove (client); ExitWriteLock(); } - protected void notifyClients (TextChange tc, object triggeringClient = null) { - object[] clients = registeredClients.Keys.ToArray (); - for (int i = 0; i < clients.Length; i++) { - if (clients[i] != triggeringClient) - notifyClient (clients[i], tc); - } - } - protected void notifyClient (object client, TextChange tc) { - if (registeredClients[client] == null) - registeredClients[client] = new List (); - registeredClients[client].Add (tc); - } - - public virtual void SetLocation(CharLocation loc) { - notifyClients(new TextChange(GetAbsolutePosition(loc),0)); - } - protected override void writeToDisk () { using (Stream s = new FileStream(FullPath, FileMode.Create)) { using (StreamWriter sw = new StreamWriter (s, encoding)) @@ -113,8 +103,6 @@ namespace CrowEditBase documentRWLock.ExitWriteLock (); } } - protected Stack undoStack = new Stack (); - protected Stack redoStack = new Stack (); protected override void saveFileDialog_OkClicked (object sender, EventArgs e) { FileDialog fd = sender as FileDialog; @@ -166,19 +154,25 @@ namespace CrowEditBase } } + #endregion + protected void resetUndoRedo () { undoStack.Clear (); redoStack.Clear (); CMDUndo.CanExecute = false; CMDRedo.CanExecute = false; } - protected bool disableTextChangedEvent = false; - protected virtual void apply (TextChange change) { - - buffer.Update(change); - - NotifyValueChanged ("IsDirty", IsDirty); - CMDSave.CanExecute = IsDirty; + protected void notifyClients (TextChange tc, object triggeringClient = null) { + object[] clients = registeredClients.Keys.ToArray (); + for (int i = 0; i < clients.Length; i++) { + if (clients[i] != triggeringClient) + notifyClient (clients[i], tc); + } + } + protected void notifyClient (object client, TextChange tc) { + if (registeredClients[client] == null) + registeredClients[client] = new List (); + registeredClients[client].Add (tc); } protected void applyTextChange (TextChange change, object triggeringEditor = null) { documentRWLock.EnterWriteLock (); @@ -301,6 +295,16 @@ namespace CrowEditBase } } + protected virtual void apply (TextChange change) { + + buffer.Update(change); + + NotifyValueChanged ("IsDirty", IsDirty); + CMDSave.CanExecute = IsDirty; + } + public virtual void SetLocation(CharLocation loc) { + notifyClients(new TextChange(GetAbsolutePosition(loc),0)); + } public virtual CharLocation GetWordStart (CharLocation loc) { documentRWLock.EnterReadLock (); try { diff --git a/plugins/CERoslynPlugin/src/Parsing/CSSyntaxAnalyser.cs b/plugins/CERoslynPlugin/src/Parsing/CSSyntaxAnalyser.cs new file mode 100644 index 0000000..e797873 --- /dev/null +++ b/plugins/CERoslynPlugin/src/Parsing/CSSyntaxAnalyser.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2021-2025 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System.Threading; +using System.Threading.Tasks; +using CrowEditBase; +using Microsoft.CodeAnalysis.CSharp; + +namespace CERoslynPlugin +{ + public class CSSyntaxAnalyser : SyntaxAnalyser { + CSDocument csdoc; + public CSSyntaxAnalyser (CSDocument document) : base (document) { + csdoc = document; + } + + public override async Task Process (CancellationToken cancel = default) { + ReadOnlyTextBuffer buff = document.ImmutableBufferCopy; + CsharpSyntaxWalkerBridge bridge = new CsharpSyntaxWalkerBridge(new CSRootSyntax (buff), cancel); + CSharpSyntaxNode csroot = await csdoc.tree.GetRootAsync(cancel); + + if (cancel.IsCancellationRequested) + return null; + + bridge.Visit(csroot); + bridge.Root.SetTokens (bridge.Toks.ToArray()); + Root = bridge.Root; + return Root; + } + } +} \ No newline at end of file diff --git a/plugins/CERoslynPlugin/src/CSSyntaxAnalyser.cs b/plugins/CERoslynPlugin/src/Parsing/CsharpSyntaxWalkerBridge.cs similarity index 67% rename from plugins/CERoslynPlugin/src/CSSyntaxAnalyser.cs rename to plugins/CERoslynPlugin/src/Parsing/CsharpSyntaxWalkerBridge.cs index 085e5a6..c276670 100644 --- a/plugins/CERoslynPlugin/src/CSSyntaxAnalyser.cs +++ b/plugins/CERoslynPlugin/src/Parsing/CsharpSyntaxWalkerBridge.cs @@ -4,83 +4,31 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Threading; -using System.Threading.Tasks; using Crow.Text; using CrowEditBase; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; namespace CERoslynPlugin { - public class CSRootSyntax : SyntaxRootNode { - public CSRootSyntax (ReadOnlyTextBuffer source) : base (source, null) { } - internal void SetTokens(Token[] tokens) { - this.tokens = tokens; - } - } - public class CSToken : SingleTokenSyntax { - SyntaxToken cstoken; - public CSToken(SyntaxToken token, Token tok) : base (tok) { - cstoken = token; - } - public override string ToString() => $"TOK: {cstoken.Kind()}"; - } - public class CSTrivia : SingleTokenSyntax { - SyntaxTrivia cstrivia; - public CSTrivia(SyntaxTrivia token, Token tok) : base (tok) { - cstrivia = token; - } - public override string ToString() => $"Trivia: {cstrivia.Kind()}"; - } - public class CSSyntaxNode : MultiNodeSyntax { - Microsoft.CodeAnalysis.SyntaxNode node; - public CSSyntaxNode(Microsoft.CodeAnalysis.SyntaxNode node) { - this.node = node; - } - public override string ToString() => $"{node.Kind()}"; - - } - - public class CSSyntaxAnalyser : SyntaxAnalyser { - /*protected override void Parse(SyntaxNode node) - { - throw new NotImplementedException(); - }*/ - CSDocument csdoc; - public CSSyntaxAnalyser (CSDocument document) : base (document) { - csdoc = document; - } - - public override async Task Process (CancellationToken cancel = default) { - ReadOnlyTextBuffer buff = document.ImmutableBufferCopy; - CsharpSyntaxWalkerBridge bridge = new CsharpSyntaxWalkerBridge(new CSRootSyntax (buff), cancel); - CSharpSyntaxNode csroot = await csdoc.tree.GetRootAsync(cancel); - - if (cancel.IsCancellationRequested) - return null; - - bridge.Visit(csroot); - bridge.Root.SetTokens (bridge.Toks.ToArray()); - Root = bridge.Root; - return Root; - } - } class CsharpSyntaxWalkerBridge : CSharpSyntaxWalker { - public CSRootSyntax Root; - public List Toks; - MultiNodeSyntax currentNode; - CancellationToken cancel; + #region CTOR public CsharpSyntaxWalkerBridge (CSRootSyntax root, CancellationToken cancel = default) : base (SyntaxWalkerDepth.StructuredTrivia) { this.cancel = cancel; currentNode = Root = root; Toks = new List(100); } + #endregion + + public CSRootSyntax Root; + public List Toks; + MultiNodeSyntax currentNode; + int startOfTok; + CancellationToken cancel; + public override void Visit (Microsoft.CodeAnalysis.SyntaxNode node) { if (cancel.IsCancellationRequested) @@ -91,13 +39,6 @@ namespace CERoslynPlugin currentNode = currentNode.Parent; } - - /*public override void VisitToken(SyntaxToken token) - { - - base.VisitToken(token); - }*/ - public override void VisitToken (SyntaxToken token) { if (cancel.IsCancellationRequested) @@ -120,7 +61,6 @@ namespace CERoslynPlugin VisitTrailingTrivia (token); } - public override void VisitTrivia (SyntaxTrivia trivia) { if (cancel.IsCancellationRequested) @@ -140,7 +80,7 @@ namespace CERoslynPlugin Toks.Add (new Token(span.Start, span.Length, (TokenType)trivia.RawKind)); } } - int startOfTok; + void addTok (ref SpanCharReader reader, int offset, Enum tokType) { if (reader.CurrentPosition == startOfTok) return; diff --git a/plugins/CERoslynPlugin/src/Parsing/SyntaxNodes.cs b/plugins/CERoslynPlugin/src/Parsing/SyntaxNodes.cs new file mode 100644 index 0000000..3f6f267 --- /dev/null +++ b/plugins/CERoslynPlugin/src/Parsing/SyntaxNodes.cs @@ -0,0 +1,38 @@ +// Copyright (c) 2021-2025 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using CrowEditBase; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +namespace CERoslynPlugin +{ + public class CSRootSyntax : SyntaxRootNode { + public CSRootSyntax (ReadOnlyTextBuffer source) : base (source, null) { } + internal void SetTokens(Token[] tokens) { + this.tokens = tokens; + } + } + public class CSToken : SingleTokenSyntax { + SyntaxToken cstoken; + public CSToken(SyntaxToken token, Token tok) : base (tok) { + cstoken = token; + } + public override string ToString() => $"TOK: {cstoken.Kind()}"; + } + public class CSTrivia : SingleTokenSyntax { + SyntaxTrivia cstrivia; + public CSTrivia(SyntaxTrivia token, Token tok) : base (tok) { + cstrivia = token; + } + public override string ToString() => $"Trivia: {cstrivia.Kind()}"; + } + public class CSSyntaxNode : MultiNodeSyntax { + Microsoft.CodeAnalysis.SyntaxNode node; + public CSSyntaxNode(Microsoft.CodeAnalysis.SyntaxNode node) { + this.node = node; + } + public override string ToString() => $"{node.Kind()}"; + + } +} \ No newline at end of file diff --git a/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxNodes.cs b/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxNodes.cs index e5da95d..4fc91f1 100644 --- a/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxNodes.cs +++ b/plugins/CEXmlPlugin/src/Parsing/XmlSyntaxNodes.cs @@ -61,7 +61,4 @@ namespace CrowEdit.Xml AddChild (new SingleTokenSyntax(name)); } } - /*public class AttributeNameSyntax : SingleTokenSyntax { - public AttributeNameSyntax(Token name) : base(name) {} - }*/ } \ No newline at end of file -- 2.47.3