From: Jean-Philippe Bruyère Date: Tue, 25 Feb 2025 01:38:41 +0000 (+0100) Subject: wip X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=31a46e737b9d03294067935edf97a0345c18bbcd;p=jp%2Fcrowedit.git wip --- diff --git a/CrowEditBase/src/Compiler/SourceDocument.cs b/CrowEditBase/src/Compiler/SourceDocument.cs index 6a28378..48bfd65 100644 --- a/CrowEditBase/src/Compiler/SourceDocument.cs +++ b/CrowEditBase/src/Compiler/SourceDocument.cs @@ -19,6 +19,13 @@ namespace CrowEditBase protected Token[] tokens; protected SyntaxNode RootNode; protected Token currentToken => currentTokenIndex < 0 ? default : tokens[currentTokenIndex]; + protected Token? previousToken { + get { + if (currentTokenIndex < 1) + return null; + return tokens[currentTokenIndex-1]; + } + } SyntaxNode currentNode; public SyntaxNode CurrentNode { get => currentNode; @@ -31,6 +38,7 @@ namespace CrowEditBase } public string CurrentTokenString => RootNode?.Root.GetTokenStringByIndex (currentTokenIndex); public Token CurrentToken => currentToken; + public bool IsParsed => tokens.Length > 0 && RootNode != null; //public SyntaxNode EditedNode { get; protected set; } protected int currentTokenIndex; @@ -44,7 +52,7 @@ namespace CrowEditBase 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 - 1]; + return idx == 0 ? tokens[0] : idx < 0 ? tokens[~idx - 1] : tokens[idx]; } public int FindTokenIndexIncludingPosition (int pos) { if (pos == 0 || tokens == null || tokens.Length == 0) @@ -93,7 +101,6 @@ namespace CrowEditBase base.apply(change); Tokenizer tokenizer = CreateTokenizer (); - tokens = tokenizer.Tokenize (Source); SyntaxAnalyser syntaxAnalyser = CreateSyntaxAnalyser (); if (syntaxAnalyser == null) { @@ -101,6 +108,13 @@ namespace CrowEditBase return; } + SyntaxNode changedNode = RootNode.FindNodeIncludingSpan (TextSpan.FromStartAndLength (change.Start, change.ChangedText.Length)); + + + tokens = tokenizer.Tokenize (Source); + + + syntaxAnalyser.Process (); NotifyValueChanged("Exceptions", syntaxAnalyser.Exceptions); @@ -157,7 +171,7 @@ namespace CrowEditBase if (tokType.HasFlag (TokenType.Punctuation)) return Colors.DarkGrey; if (tokType.HasFlag (TokenType.Trivia)) - return Colors.DimGrey; + return Colors.Silver; if (tokType == TokenType.Keyword) return Colors.DarkSlateBlue; return Colors.Red; @@ -176,6 +190,7 @@ namespace CrowEditBase /// 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); void parse () { Tokenizer tokenizer = CreateTokenizer (); tokens = tokenizer?.Tokenize (Source); diff --git a/CrowEditBase/src/Compiler/SyntaxAnalyser.cs b/CrowEditBase/src/Compiler/SyntaxAnalyser.cs index dcac0eb..ab2df67 100644 --- a/CrowEditBase/src/Compiler/SyntaxAnalyser.cs +++ b/CrowEditBase/src/Compiler/SyntaxAnalyser.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; @@ -7,37 +7,101 @@ using System.Linq; namespace CrowEditBase { - public class SyntaxException : Exception { - public readonly Token Token; - public SyntaxException(string message, Token token = default, Exception innerException = null) - : base (message, innerException) { - Token = token; - } - } public abstract class SyntaxAnalyser { + //protected abstract void Parse(SyntaxNode node); protected SourceDocument source; public abstract SyntaxNode Root { get; } - public List Exceptions { get; protected set; } + public IEnumerable Exceptions => Root?.GetAllExceptions(); public SyntaxAnalyser (SourceDocument source) { this.source = source; } public abstract void Process (); - protected Token curTok; - protected SyntaxNode currentNode; + + #region Token handling + protected Token curTok => tokIdx < 0 ? default : tokens[tokIdx]; + protected Token[] tokens; + protected bool EOF => tokIdx == tokens.Length; + protected bool tryRead (out Token tok) { + if (EOF) { + tok = default; + return false; + } + tok = tokens [tokIdx++]; + return true; + } + protected bool tryPeek (out Token tok) { + if (EOF) { + tok = default; + return false; + } + tok = tokens [tokIdx]; + return true; + } + protected bool tryPeek (Enum expectedType) + => EOF ? false : Enum.Equals(tokens [tokIdx].Type, expectedType); + + protected bool tryRead (out Token tok, Enum expectedType) { + if (EOF) { + tok = default; + return false; + } + tok = tokens [tokIdx++]; + return Enum.Equals(tok.Type, expectedType); + } + protected bool tryPeek (out Token tok, Enum expectedType) { + if (EOF) { + tok = default; + return false; + } + tok = tokens [tokIdx]; + return Enum.Equals(tok.Type, expectedType); + } + protected bool tryPeekFlag (out Token tok, Enum expectedFlag) { + if (EOF) { + tok = default; + return false; + } + tok = tokens [tokIdx]; + return tok.Type.HasFlag(expectedFlag); + } + + #endregion + + #region parsing context protected int currentLine, tokIdx; + protected SyntaxNode currentNode; + #endregion /// /// set current node endToken and line count and set current to current.parent. /// /// The final token of this node /// the endline number of this node - protected void storeCurrentNode (int endTokenOffsetFromCurrentTokIdx = 0) { - currentNode.LastTokenOffset = tokIdx - currentNode.TokenIndexBase + endTokenOffsetFromCurrentTokIdx; + protected void setEndLineForCurrentNode (int endTokenOffsetFromCurrentTokIdx = 0) { + currentNode.TokenCount = tokIdx - currentNode.TokenIndexBase + endTokenOffsetFromCurrentTokIdx; currentNode.EndLine = currentLine; currentNode = currentNode.Parent; } + 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) { + while (tryPeekFlag(out Token tok, TokenType.Trivia)) { + if (tok.Type == TokenType.LineBreak) { + if (!skipLineBreaks) + return true; + currentLine++; + } + tokIdx++; + } + return !EOF; + } + protected void addException(string message) { + currentNode.AddException(new SyntaxException(message, curTok)); + } //bool EOF => tokIdx == tokens.Length; diff --git a/CrowEditBase/src/Compiler/SyntaxException.cs b/CrowEditBase/src/Compiler/SyntaxException.cs new file mode 100644 index 0000000..d889cc3 --- /dev/null +++ b/CrowEditBase/src/Compiler/SyntaxException.cs @@ -0,0 +1,16 @@ +// Copyright (c) 2021-2025 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; + +namespace CrowEditBase +{ + public class SyntaxException : Exception { + public readonly Token Token; + public SyntaxException(string message, Token token = default, Exception innerException = null) + : base (message, innerException) { + Token = token; + } + public override string ToString() => $"{Message}, {Token}"; + } +} \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/SyntaxNode.cs b/CrowEditBase/src/Compiler/SyntaxNode.cs index 4f10b43..4d28970 100644 --- a/CrowEditBase/src/Compiler/SyntaxNode.cs +++ b/CrowEditBase/src/Compiler/SyntaxNode.cs @@ -10,22 +10,31 @@ using Crow; namespace CrowEditBase { public abstract class SyntaxRootNode : SyntaxNode { - internal readonly SourceDocument source; + protected readonly SourceDocument source; public SyntaxRootNode (SourceDocument source) { this.source = source; } public override int TokenIndexBase => 0; - public override int? LastTokenOffset { get => Math.Max (0, source.Tokens.Length - 1); internal set {} } + 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 < Root.source.Tokens.Length ? Root.source.Tokens[idx].AsString (Root.source.Source) : null; - public Token? GetTokenByIndex (int idx) => - idx >= 0 && idx < Root.source.Tokens.Length ? Root.source.Tokens[idx] : default; + idx >= 0 && idx < source.Tokens.Length ? source.Tokens[idx].AsString (source.Source) : 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 class SyntaxNode : CrowEditComponent { + internal SyntaxNode () {} + public SyntaxNode (int startLine, int tokenBase, int? lastTokenIdx = null) { + StartLine = startLine; + TokenIndexBase = tokenBase; + if (lastTokenIdx.HasValue) + TokenCount = lastTokenIdx - tokenBase; + } bool _isExpanded; public bool isExpanded { get => _isExpanded; @@ -43,15 +52,28 @@ namespace CrowEditBase public SyntaxNode Parent { get; private set; } public int StartLine { get; private set; } public virtual int LineCount => lineCount; - public virtual bool IsComplete => LastTokenOffset.HasValue; + public virtual bool IsComplete => TokenCount.HasValue; public virtual bool IsFoldable => IsComplete && Parent.StartLine != StartLine && lineCount > 1; public virtual SyntaxRootNode Root => Parent.Root; public virtual void UnfoldToTheTop () { isFolded = false; Parent.UnfoldToTheTop (); } - protected Token getTokenByIndex (int idx) => idx < Root.source.Tokens.Length ? Root.source.Tokens[idx] : default; + protected Token getTokenByIndex (int idx) => Root.GetTokenByIndex(idx); internal List children = new List (); + List exceptions = new List(); + public void AddException(SyntaxException e) => exceptions.Add(e); + public void ResetExceptions(SyntaxException e) => exceptions.Clear(); + public IEnumerable Exceptions => exceptions; + 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 IEnumerable Children => children; //public int IndexOf (SyntaxNode node) => children.IndexOf (node); public bool HasChilds => children.Count > 0; @@ -101,15 +123,8 @@ namespace CrowEditBase } public virtual int TokenIndexBase { get; private set; } - public virtual int? LastTokenOffset { get; internal set; } - public int? LastTokenIndex => TokenIndexBase + LastTokenOffset; - internal SyntaxNode () {} - public SyntaxNode (int startLine, int tokenBase, int? lastTokenIdx = null) { - StartLine = startLine; - TokenIndexBase = tokenBase; - if (lastTokenIdx.HasValue) - LastTokenOffset = lastTokenIdx - tokenBase; - } + public virtual int? TokenCount { get; internal set; } + public int? LastTokenIndex => TokenIndexBase + TokenCount; internal bool isFolded; internal int lineCount; @@ -125,9 +140,9 @@ namespace CrowEditBase return new TextSpan (children.First().Span.Start, children.Last().Span.End) }*/ try { - Token startTok = getTokenByIndex(TokenIndexBase); - Token endTok = LastTokenOffset.HasValue ? getTokenByIndex (TokenIndexBase+LastTokenOffset.Value) : startTok; - return new TextSpan (startTok.Start, endTok.End); + Token startTok = getTokenByIndex(TokenIndexBase); + Token endTok = TokenCount.HasValue ? getTokenByIndex (TokenIndexBase+TokenCount.Value) : startTok; + return new TextSpan (startTok.Start, endTok.End); }catch{ System.Diagnostics.Debugger.Break (); } @@ -144,7 +159,8 @@ namespace CrowEditBase children.Remove (child); child.Parent = null; } - public T GetChild () => children.OfType ().FirstOrDefault (); + public IEnumerable GetChilds () => children.OfType(); + public void Replace (SyntaxNode newNode) { Parent.replaceChild (this, newNode); } @@ -152,7 +168,7 @@ namespace CrowEditBase int idx = children.IndexOf (oldNode); children[idx] = newNode; newNode.Parent = this; - int tokIdxDiff = newNode.LastTokenOffset.Value - oldNode.LastTokenOffset.Value; + int tokIdxDiff = newNode.TokenCount.Value - oldNode.TokenCount.Value; int lineDiff = newNode.EndLine - oldNode.EndLine; if (tokIdxDiff == 0 && lineDiff == 0) return; @@ -160,7 +176,7 @@ namespace CrowEditBase SyntaxNode curNode = this; while (curNode != null) { curNode.lineCount += lineDiff; - curNode.LastTokenOffset += tokIdxDiff; + curNode.TokenCount += tokIdxDiff; if (curNode is SyntaxRootNode) break; while (++idx < curNode.children.Count) @@ -205,10 +221,9 @@ namespace CrowEditBase foreach (SyntaxNode node in children) node.Dump (level + 1); } - public override string ToString() => $"l:({StartLine,3},{LineCount,3}) tks:{TokenIndexBase},{LastTokenOffset} {this.GetType().Name}"; + public override string ToString() => $"l:({StartLine,3},{LineCount,3}) tks:{TokenIndexBase},{TokenCount} {this.GetType().Name}"; public string AsText() { - TextSpan span = Span; - return Root.source.Source.Substring (span.Start, span.Length); + return Span.Length < 0 ? "" : Root.GetText(Span).ToString(); } public bool IsSimilar (SyntaxNode other) => this.GetType() == other?.GetType(); } diff --git a/CrowEditBase/src/Compiler/TokenReader.cs b/CrowEditBase/src/Compiler/TokenReader.cs new file mode 100644 index 0000000..daf32fb --- /dev/null +++ b/CrowEditBase/src/Compiler/TokenReader.cs @@ -0,0 +1,68 @@ +// Copyright (c) 2021-2021 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; + +namespace CrowEditBase +{ + public struct TokenReader + { + int curPos; + Token[] buffer; + + public TokenReader (Token[] span) { + buffer = span; + curPos = 0; + } + + public int CurrentPosition => curPos; + /// + /// Current reader position is further the end of the buffer. + /// + public bool EndOfSpan => curPos >= buffer.Length; + + public void Seek (int position) => curPos = position; + + public Token Peek => buffer[curPos]; + public Token Read () => buffer[curPos++]; + public bool TryRead (out Token c) { + if (EndOfSpan) { + c = default; + return false; + } + c = Read(); + return true; + } + public bool TryRead (Token c) => EndOfSpan ? false : EqualityComparer.Default.Equals(Read(), c); + + public ReadOnlySpan Read (int length) => buffer.AsSpan().Slice (curPos += length, length); + public void Advance (int increment = 1) => curPos += increment; + public bool TryAdvance (int increment = 1) { + curPos += increment; + return curPos < buffer.Length; + } + /// + /// Retrieve a span of that buffer from provided starting position to the current reader position. + /// + /// + /// + public ReadOnlySpan Get (int fromPosition) => buffer.AsSpan().Slice (fromPosition, curPos - fromPosition); + public bool TryPeek (Token c) => !EndOfSpan && EqualityComparer.Default.Equals(Peek, c); + /// + /// Try peak one char, return false if end of span, true otherwise. + /// + /// + /// + public bool TryPeek (out Token c) { + c = default; + if (EndOfSpan) + return false; + c = buffer[curPos]; + return true; + } + } +} \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/Tokenizer.cs b/CrowEditBase/src/Compiler/Tokenizer.cs index b62d05c..06c33ca 100644 --- a/CrowEditBase/src/Compiler/Tokenizer.cs +++ b/CrowEditBase/src/Compiler/Tokenizer.cs @@ -39,16 +39,20 @@ namespace CrowEditBase Toks.Add (new Token((TokenType)tokType, startOfTok, reader.CurrentPosition)); startOfTok = reader.CurrentPosition; } - protected virtual void skipWhiteSpaces (ref SpanCharReader reader) { + protected virtual void skipWhiteSpaces (ref SpanCharReader reader, bool skipLineBreaksToo = true) { while(!reader.EndOfSpan) { switch (reader.Peek) { case '\x85': case '\x2028': case '\xA': + if (!skipLineBreaksToo) + return; reader.Read(); addTok (ref reader, TokenType.LineBreak); break; case '\xD': + if (!skipLineBreaksToo) + return; reader.Read(); if (reader.IsNextCharIn ('\xA', '\x85')) reader.Read(); diff --git a/CrowEditBase/src/CrowEditBase.cs b/CrowEditBase/src/CrowEditBase.cs index fc80869..985c14b 100644 --- a/CrowEditBase/src/CrowEditBase.cs +++ b/CrowEditBase/src/CrowEditBase.cs @@ -318,7 +318,10 @@ namespace CrowEditBase Widget g = FindByName (path); if (g != null) return g as Window; - g = Load (path); + if (TryGetConfigFromFloatingWinConfigs(path, out string floatingConfig)) + g = DockWindow.CreateFromFloatingConfigString(this, floatingConfig); + else + g = Load (path); g.Name = path; g.DataSource = dataSource; return g as Window; @@ -333,10 +336,74 @@ namespace CrowEditBase } public void CloseWindow (string path){ Widget g = FindByName (path); + if (g is DockWindow dockwin) { + if (dockwin.IsFloating) + saveWinConfigs(); + } if (g != null) DeleteWidget (g); + + } + void saveFloatingWinConfigs() { + StringBuilder floatings = new StringBuilder (512); + DockWindow[] floatingWins = GraphicTree.OfType ().ToArray (); + if (floatingWins.Length > 0) { + for (int i = 0; i < floatingWins.Length - 1; i++) { + floatings.Append (floatingWins[i].FloatingConfigString); + floatings.Append ('|'); + } + floatings.Append (floatingWins[floatingWins.Length - 1].FloatingConfigString); + } + Configuration.Global.Set ("FloatingWinConfigs", floatings.ToString ()); + } + protected bool TryGetConfigFromFloatingWinConfigs(string winPath, out string conf) { + if (Configuration.Global.TryGet("FloatingWinConfigs", out conf) && !string.IsNullOrEmpty(conf)) { + string[] floatings = conf.Split ('|'); + for (int i = 0; i < floatings.Length; i++) { + if (floatings[i].Split(';')[0] == winPath) { + conf = floatings[i]; + return true; + } + } + } + return false; } + protected void saveWinConfigs() { + Configuration.Global.Set ("WinConfigs", mainDock.ExportConfig ()); + saveFloatingWinConfigs(); + + Configuration.Global.Save (); + } + protected DockStack mainDock; + protected void reloadWinConfigs() { + + if (Configuration.Global.TryGet("WinConfigs", out string conf) && !string.IsNullOrEmpty(conf)) + mainDock.ImportConfig (conf, this); + if (Configuration.Global.TryGet("FloatingWinConfigs", out conf) && !string.IsNullOrEmpty(conf)) { + string[] floatings = conf.Split ('|'); + for (int i = 0; i < floatings.Length; i++) + DockWindow.CreateFromFloatingConfigString (this, floatings[i], this); + } + } + protected void reloadLogsConfigs() { + + if (Configuration.Global.TryGet("OpenedLogs", out string conf) && !string.IsNullOrEmpty(conf)) { + string[] logs = conf.Split ('|'); + for (int i = 0; i < logs.Length; i++) + App.GetLog(logs[i]).IsOpened = true; + if (Configuration.Global.TryGet("CurrentLog", out string curLog) && !string.IsNullOrEmpty(curLog)) + App.GetLog(curLog).IsSelected = true; + } + } + protected void saveLogsConfig() { + lock (OpenedLogs) { + string openLogs = OpenedLogs.Count > 0 ? + OpenedLogs.Select(li=>li.Name).Aggregate((a,b) => a + "|" + b) : null; + Configuration.Global.Set("OpenedLogs", openLogs); + Configuration.Global.Set("CurrentLog", CurrentLog?.Name); + } + } public ActionCommand CMDOptions_SelectPluginsDirectory => new ActionCommand ("...", () => { FileDialog dlg = App.LoadIMLFragment (@" @@ -409,7 +476,7 @@ namespace CrowEditBase