From: Jean-Philippe Bruyère Date: Wed, 3 Nov 2021 08:10:04 +0000 (+0100) Subject: wip X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=1c9bd2777198b1bf17ae4b73197df3a70884f4f5;p=jp%2Fcrowedit.git wip --- diff --git a/CrowEditBase/src/Compiler/SourceDocument.cs b/CrowEditBase/src/Compiler/SourceDocument.cs index 753ebb9..420141e 100644 --- a/CrowEditBase/src/Compiler/SourceDocument.cs +++ b/CrowEditBase/src/Compiler/SourceDocument.cs @@ -7,6 +7,7 @@ using Crow; using Crow.Text; using System.Diagnostics; using System.Collections; +using System.Collections.Generic; namespace CrowEditBase { @@ -17,11 +18,26 @@ namespace CrowEditBase protected Token[] tokens; protected SyntaxNode RootNode; protected LineCollection lines; - protected Token currentToken; - protected SyntaxNode currentNode; + protected Token currentToken => currentTokenIndex < 0 ? default : tokens[currentTokenIndex]; + SyntaxNode currentNode; + public SyntaxNode CurrentNode { + get => currentNode; + set { + if (currentNode == value) + return; + currentNode = value; + NotifyValueChanged ("CurrentNode", currentNode); + } + } + public string CurrentTokenString => RootNode?.Root.GetTokenStringByIndex (currentTokenIndex); + public Token CurrentToken => currentToken; + + //public SyntaxNode EditedNode { get; protected set; } + protected int currentTokenIndex; public Token[] Tokens => tokens; public SyntaxNode SyntaxRootNode => RootNode; + public IEnumerable SyntaxRootChildNodes => RootNode?.children; public LineCollection Lines => lines; public Token FindTokenIncludingPosition (int pos) { if (pos == 0 || tokens == null || tokens.Length == 0) @@ -59,14 +75,72 @@ namespace CrowEditBase return default; return RootNode.FindNodeIncludingPosition (pos); } + public SyntaxNode FindNodeIncludingSpan (TextSpan span) { + if (RootNode == null) + return null; + if (!RootNode.Contains (span)) + return null; + return RootNode.FindNodeIncludingSpan (span); + } protected override void reloadFromFile () { base.reloadFromFile (); parse (); } protected override void apply(TextChange change) { + SyntaxNode editedNode = FindNodeIncludingSpan (new TextSpan (change.Start, change.End)); + base.apply(change); - parse (); + + Tokenizer tokenizer = CreateTokenizer (); + tokens = tokenizer.Tokenize (Source); + SyntaxAnalyser syntaxAnalyser = CreateSyntaxAnalyser (); + syntaxAnalyser.Process (); + + SyntaxNode newNode = syntaxAnalyser.Root.FindNodeIncludingSpan (TextSpan.FromStartAndLength (change.Start, change.ChangedText.Length)); + + if (editedNode == null) { + //System.Diagnostics.Debugger.Break (); + RootNode = syntaxAnalyser.Root; + } else if (newNode.IsSimilar (editedNode)) { + if (!tryReplaceNode (editedNode, newNode)) + RootNode = syntaxAnalyser.Root; + } else if (newNode.Parent != null && newNode.Parent.IsSimilar (editedNode)) { + if (!tryReplaceNode (editedNode, newNode.Parent)) + RootNode = syntaxAnalyser.Root; + } else if (editedNode.Parent != null && newNode.IsSimilar (editedNode.Parent)) { + if (!tryReplaceNode (editedNode.Parent, newNode)) + RootNode = syntaxAnalyser.Root; + } else if (newNode.Parent != null && editedNode.Parent != null && newNode.Parent.IsSimilar (editedNode.Parent)) { + if (!tryReplaceNode (editedNode.Parent, newNode.Parent)) + RootNode = syntaxAnalyser.Root; + } else { + //System.Diagnostics.Debugger.Break (); + RootNode = syntaxAnalyser.Root; + } + + //updateCurrentTokAndNode (change.End2); + //EditedNode = editedNode; + + //Console.WriteLine ($"CurrentToken: idx({currentTokenIndex}) {currentToken} {RootNode.Root.GetTokenStringByIndex(currentTokenIndex)}"); + } + static bool tryReplaceNode (SyntaxNode editedNode, SyntaxNode newNode) { + if (newNode is SyntaxRootNode || editedNode is SyntaxRootNode) + return false; + editedNode.Replace (newNode); + return true; + } + + internal void updateCurrentTokAndNode (int pos) { + if (tokens.Length > 0) { + currentTokenIndex = FindTokenIndexIncludingPosition (pos); + CurrentNode = FindNodeIncludingSpan (currentToken.Span); + NotifyValueChanged ("CurrentTokenString", (object)CurrentTokenString); + }else { + currentTokenIndex = -1; + CurrentNode = null; + NotifyValueChanged ("CurrentTokenString", (object)"no token"); + } } public virtual Crow.Color GetColorForToken (TokenType tokType) { @@ -87,9 +161,10 @@ namespace CrowEditBase /// It may set a new position or a new selection. /// /// selected object of suggestion overlay + /// /// the text change to apply /// new position or selection, null if normal position after text changes - /// the TextChange to apply to the source - public abstract TextChange? GetCompletionForCurrentToken (object suggestion, out TextSpan? newSelection); + /// true if successed + public abstract bool TryGetCompletionForCurrentToken (object suggestion, out TextChange change, out TextSpan? newSelection); void parse () { Tokenizer tokenizer = CreateTokenizer (); tokens = tokenizer.Tokenize (Source); diff --git a/CrowEditBase/src/Compiler/SyntaxNode.cs b/CrowEditBase/src/Compiler/SyntaxNode.cs index 2fbcd6d..a771f8d 100644 --- a/CrowEditBase/src/Compiler/SyntaxNode.cs +++ b/CrowEditBase/src/Compiler/SyntaxNode.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using Crow.Text; +using Crow; namespace CrowEditBase { @@ -14,13 +15,31 @@ namespace CrowEditBase this.source = source; } public override int TokenIndexBase => 0; - public override int? LastTokenOffset { get => source.Tokens.Length - 1; set {} } + public override int? LastTokenOffset { 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; } - public class SyntaxNode { + public class SyntaxNode : CrowEditComponent { + bool _isExpanded; + public bool isExpanded { + get => _isExpanded; + set { + if (_isExpanded == value) + return; + _isExpanded = value; + NotifyValueChanged (_isExpanded); + } + } + public void ExpandToTheTop () { + isExpanded = true; + Parent?.ExpandToTheTop (); + } public SyntaxNode Parent { get; private set; } public int StartLine { get; private set; } public virtual int LineCount => lineCount; @@ -31,9 +50,10 @@ namespace CrowEditBase isFolded = false; Parent.UnfoldToTheTop (); } - protected Token getTokenByIndex (int idx) => Root.source.Tokens[idx]; - List children = new List (); + protected Token getTokenByIndex (int idx) => idx < Root.source.Tokens.Length ? Root.source.Tokens[idx] : default; + internal List children = new List (); public IEnumerable Children => children; + //public int IndexOf (SyntaxNode node) => children.IndexOf (node); public bool HasChilds => children.Count > 0; public SyntaxNode NextSibling { get { @@ -81,7 +101,8 @@ namespace CrowEditBase } public virtual int TokenIndexBase { get; private set; } - public virtual int? LastTokenOffset { get; 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; @@ -91,6 +112,7 @@ namespace CrowEditBase } internal bool isFolded; internal int lineCount; + public int EndLine { internal set { lineCount = value - StartLine + 1; @@ -102,8 +124,14 @@ namespace CrowEditBase /*if (HasChilds) { return new TextSpan (children.First().Span.Start, children.Last().Span.End) }*/ + try { Token startTok = getTokenByIndex(TokenIndexBase); - return new TextSpan (startTok.Start, LastTokenOffset.HasValue ? getTokenByIndex (TokenIndexBase+LastTokenOffset.Value).End : startTok.End); + Token endTok = LastTokenOffset.HasValue ? getTokenByIndex (TokenIndexBase+LastTokenOffset.Value) : startTok; + return new TextSpan (startTok.Start, endTok.End); + }catch{ + System.Diagnostics.Debugger.Break (); + } + return default; } } @@ -117,6 +145,37 @@ namespace CrowEditBase child.Parent = null; } public T GetChild () => children.OfType ().FirstOrDefault (); + public void Replace (SyntaxNode newNode) { + Parent.replaceChild (this, newNode); + } + void replaceChild (SyntaxNode oldNode, SyntaxNode newNode) { + int idx = children.IndexOf (oldNode); + children[idx] = newNode; + newNode.Parent = this; + int tokIdxDiff = newNode.LastTokenOffset.Value - oldNode.LastTokenOffset.Value; + int lineDiff = newNode.EndLine - oldNode.EndLine; + if (tokIdxDiff == 0 && lineDiff == 0) + return; + + SyntaxNode curNode = this; + while (curNode != null) { + curNode.lineCount += lineDiff; + curNode.LastTokenOffset += tokIdxDiff; + if (curNode is SyntaxRootNode) + break; + while (++idx < curNode.children.Count) + curNode.children[idx].offset (tokIdxDiff, lineDiff); + idx = curNode.Parent.children.IndexOf (curNode); + curNode = curNode.Parent; + } + } + void offset (int tokenOffset, int lineOffset) { + TokenIndexBase += tokenOffset; + StartLine += lineOffset; + foreach (SyntaxNode child in children) { + child.offset (tokenOffset, lineOffset); + } + } public SyntaxNode FindNodeIncludingPosition (int pos) { foreach (SyntaxNode node in children) { if (node.Contains (pos)) @@ -132,12 +191,25 @@ namespace CrowEditBase return this is T tt ? tt : default; } + public SyntaxNode FindNodeIncludingSpan (TextSpan span) { + foreach (SyntaxNode node in children) { + if (node.Contains (span)) + return node.FindNodeIncludingSpan (span); + } + return 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() => $"{this.GetType().Name}: lines:({StartLine},{LineCount}) tokens:{TokenIndexBase} -> {LastTokenOffset}"; + public override string ToString() => $"l:({StartLine,3},{LineCount,3}) tks:{TokenIndexBase},{LastTokenOffset} {this.GetType().Name}"; + public string AsText() { + TextSpan span = Span; + return Root.source.Source.Substring (span.Start, span.Length); + } + public bool IsSimilar (SyntaxNode other) => this.GetType() == other?.GetType(); } } \ No newline at end of file diff --git a/CrowEditBase/src/CrowEditBase.cs b/CrowEditBase/src/CrowEditBase.cs index 8c13f2b..a8caa2b 100644 --- a/CrowEditBase/src/CrowEditBase.cs +++ b/CrowEditBase/src/CrowEditBase.cs @@ -179,7 +179,7 @@ namespace CrowEditBase } } public string PluginsDirecory { - get => Configuration.Global.Get("PluginsDirecory"); + get => Configuration.Global.Get("PluginsDirecory", defaultPluginsDirectory); set { if (PluginsDirecory == value) return; @@ -283,11 +283,25 @@ namespace CrowEditBase DeleteWidget (g); } - + public ActionCommand CMDOptions_SelectPluginsDirectory => new ActionCommand ("...", + () => { + FileDialog dlg = App.LoadIMLFragment (@" + "); + dlg.OkClicked += (sender, e) => PluginsDirecory = (sender as FileDialog).SelectedFileFullPath; + dlg.DataSource = this; + } + ); + public ActionCommand CMDOptions_ResetPluginsDirectory => new ActionCommand ("Reset", + () => { + PluginsDirecory = defaultPluginsDirectory; + } + ); + static string defaultPluginsDirectory => + Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.UserProfile), ".config", "CrowEdit", "plugins"); protected void loadPlugins () { - if (string.IsNullOrEmpty (PluginsDirecory)) - PluginsDirecory = Path.Combine ( - Environment.GetFolderPath (Environment.SpecialFolder.UserProfile), ".config", "CrowEdit", "plugins"); + if (!Directory.Exists (PluginsDirecory)) + return; foreach (string pluginDir in Directory.GetDirectories (PluginsDirecory)) { Plugin plugin = new Plugin (pluginDir); @@ -295,6 +309,8 @@ namespace CrowEditBase plugin.Load (); } } + public IEnumerable AllLoadContexts => + System.Runtime.Loader.AssemblyLoadContext.All; #region Editor item templates @@ -311,7 +327,7 @@ namespace CrowEditBase - + Configuration.Global.Get ("IndentWithSpace", false); + set { + if (IndentWithSpace == value) + return; + Configuration.Global.Set ("IndentWithSpace", value); + NotifyValueChanged ("IndentWithSpace", IndentWithSpace); + } + } + public int TabulationSize { + get => Configuration.Global.Get ("TabulationSize", 4); + set { + if (TabulationSize == value) + return; + Configuration.Global.Set ("TabulationSize", value); + NotifyValueChanged ("TabulationSize", TabulationSize); + } + } + //Folding public bool FoldingEnabled { get => Crow.Configuration.Global.Get ("FoldingEnabled", true); diff --git a/CrowEditBase/src/Editor.cs b/CrowEditBase/src/Editor.cs index d799121..f55ce22 100644 --- a/CrowEditBase/src/Editor.cs +++ b/CrowEditBase/src/Editor.cs @@ -11,6 +11,7 @@ using System.Linq; using CrowEditBase; using System.Threading; using System.ComponentModel; +using static CrowEditBase.CrowEditBase; namespace Crow { @@ -143,7 +144,7 @@ namespace Crow /// /// Background color for selected text inside this label. /// - [DefaultValue ("SteelBlue")] + [DefaultValue ("LightSteelBlue")] public virtual Color SelectionBackground { get { return selBackground; } set { @@ -329,7 +330,7 @@ namespace Crow if (lines[i].Length == 0) lines.UpdateLineLengthInPixel (i, 0);// (int)Math.Ceiling (fe.MaxXAdvance); else { - gr.TextExtents (_text.GetLine (lines[i]), Interface.TAB_SIZE, out tmp); + gr.TextExtents (_text.GetLine (lines[i]), App.TabulationSize, out tmp); lines.UpdateLineLengthInPixel (i, (int)Math.Ceiling (tmp.XAdvance)); } } @@ -539,7 +540,7 @@ namespace Crow loc.Column = curLine.Length; } #endif - loc.VisualCharXPosition = gr.TextExtents (curLine.Slice (0, loc.Column), Interface.TAB_SIZE).XAdvance + cPos; + loc.VisualCharXPosition = gr.TextExtents (curLine.Slice (0, loc.Column), App.TabulationSize).XAdvance + cPos; location = loc; } else { TextExtents te; @@ -613,7 +614,7 @@ namespace Crow protected override void onDraw (Context gr) { - base.onDraw (gr); + //base.onDraw (gr); setFontForContext (gr); @@ -740,7 +741,7 @@ namespace Crow else update (new TextChange (selection.Start, 1, "")); } else { - if (IFace.Shift) + if (e.Modifiers == Modifier.Shift) IFace.Clipboard = SelectedText; update (new TextChange (selection.Start, selection.Length, "")); } @@ -763,7 +764,7 @@ namespace Crow RegisterForRedraw (); break; case Key.Tab: - update (new TextChange (selection.Start, selection.Length, "\t")); + update (new TextChange (selection.Start, selection.Length, App.IndentWithSpace ? new string(' ', App.TabulationSize) : "\t")); break; case Key.PageUp: checkShift (e); @@ -914,6 +915,7 @@ namespace Crow #endregion protected void update (TextChange change) { + lock (linesMutex) { ReadOnlySpan src = _text.AsSpan (); Span tmp = stackalloc char[src.Length + (change.ChangedText.Length - change.Length)]; diff --git a/CrowEditBase/src/ObservableTask.cs b/CrowEditBase/src/ObservableTask.cs new file mode 100644 index 0000000..0fb0b1f --- /dev/null +++ b/CrowEditBase/src/ObservableTask.cs @@ -0,0 +1,20 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace CrowEditBase +{ + public class ObservableTask : Task { + //; + public ObservableTask (Func func, CancellationToken cancellationToken = new CancellationToken(), TaskCreationOptions options = TaskCreationOptions.None) : + base (func, cancellationToken, options) { + + } + + + } +} + diff --git a/CrowEditBase/src/SourceEditor.cs b/CrowEditBase/src/SourceEditor.cs index 81bfd55..ceac278 100644 --- a/CrowEditBase/src/SourceEditor.cs +++ b/CrowEditBase/src/SourceEditor.cs @@ -16,6 +16,29 @@ namespace Crow { public class SourceEditor : Editor { object TokenMutex = new object(); + SyntaxNode currentNode; +#if DEBUG_NODE + SyntaxNode _hoverNode; + SyntaxNode hoverNode { + get =>_hoverNode; + set { + if (_hoverNode == value) + return; + _hoverNode = value; + RegisterForRedraw (); + } + } +#endif + public SyntaxNode CurrentNode { + get => currentNode; + set { + if (currentNode == value) + return; + currentNode = value; + NotifyValueChanged ("CurrentNode", currentNode); + RegisterForRedraw (); + } + } ListBox overlay; IList suggestions; @@ -40,18 +63,35 @@ namespace Crow base.OnTextChanged(sender, e); + if (Document is SourceDocument srcdoc) + srcdoc.updateCurrentTokAndNode (lines.GetAbsolutePosition(CurrentLoc.Value)); + if (!disableSuggestions && HasFocus) tryGetSuggestions (); RegisterForGraphicUpdate(); + lock (IFace.UpdateMutex) { + if (Document is SourceDocument doc) { + doc.NotifyValueChanged ("SyntaxRootChildNodes", (object)null); + doc.NotifyValueChanged ("SyntaxRootChildNodes", doc.SyntaxRootChildNodes); + CurrentNode?.ExpandToTheTop(); + } + } //Console.WriteLine ($"{pos}: {suggestionTok.AsString (_text)} {suggestionTok}"); } protected void tryGetSuggestions () { - if (currentLoc.HasValue && Document is SourceDocument srcDoc) - Suggestions = srcDoc.GetSuggestions (lines.GetAbsolutePosition (CurrentLoc.Value)); - else + if (currentLoc.HasValue && Document is SourceDocument srcDoc) { + IList suggs = srcDoc.GetSuggestions (lines.GetAbsolutePosition (CurrentLoc.Value)); + if (suggs != null && suggs.Count == 1 && ( + (suggs[0] is System.Reflection.MemberInfo mi && mi.Name == srcDoc.CurrentTokenString) || + (suggs[0].ToString() == srcDoc.CurrentTokenString) + )){ + Suggestions = null; + }else + Suggestions = suggs; + } else Suggestions = null; } void showOverlay () { @@ -102,13 +142,15 @@ namespace Crow } void completeToken () { if (Document is SourceDocument srcDoc) { - TextChange? change = srcDoc.GetCompletionForCurrentToken (overlay.SelectedItem, out TextSpan? nextSelection); - if (change.HasValue) - update (change.Value); - if (nextSelection.HasValue) - Selection = nextSelection.Value; + if (srcDoc.TryGetCompletionForCurrentToken (overlay.SelectedItem, out TextChange change, out TextSpan? nextSelection)) { + update (change); + if (nextSelection.HasValue) { + Selection = nextSelection.Value; + } + } } hideOverlay (); + tryGetSuggestions (); } const int leftMarginGap = 5;//gap between margin start and numbering @@ -140,6 +182,8 @@ namespace Crow while (fold != null && fold.StartLine == currentLoc.Value.Line) fold = fold.Parent; fold?.UnfoldToTheTop(); + if (Document is SourceDocument doc) + doc.updateCurrentTokAndNode (lines.GetAbsolutePosition(currentLoc.Value)); } NotifyValueChanged ("CurrentLine", CurrentLine); NotifyValueChanged ("CurrentColumn", CurrentColumn); @@ -232,6 +276,11 @@ namespace Crow setFontForContext (gr); updateLocation (gr, ClientRectangle.Width, ref hoverLoc); } +#if DEBUG_NODES + if (Document is SourceDocument doc) { + hoverNode = doc.FindNodeIncludingPosition (lines.GetAbsolutePosition (hoverLoc.Value)); + } +#endif } public override void onKeyDown(object sender, KeyEventArgs e) { @@ -261,7 +310,7 @@ namespace Crow completeToken (); return; } - } else if (e.Key == Key.Space && IFace.Ctrl) { + } else if (e.Key == Key.Space && e.Modifiers.HasFlag (Modifier.Control)) { tryGetSuggestions (); return; } @@ -272,13 +321,13 @@ namespace Crow disableSuggestions = true; - if (IFace.Shift) { + if ( e.Modifiers == Modifier.Shift) { for (int l = lineStart; l <= lineEnd; l++) { if (_text[lines[l].Start] == '\t') update (new TextChange (lines[l].Start, 1, "")); else if (Char.IsWhiteSpace (_text[lines[l].Start])) { int i = 1; - while (i < lines[l].Length && i < Interface.TAB_SIZE && Char.IsWhiteSpace (_text[i])) + while (i < lines[l].Length && i < App.TabulationSize && Char.IsWhiteSpace (_text[i])) i++; update (new TextChange (lines[l].Start, i, "")); } @@ -296,8 +345,23 @@ namespace Crow return; } - if (e.Key == Key.F3 && Document is SourceDocument doc) { - doc.SyntaxRootNode?.Dump(); + if (Document is SourceDocument doc) { + switch (e.Key) { + case Key.F3: + doc.SyntaxRootNode?.Dump(); + break; + case Key.Enter: + case Key.KeypadEnter: + //doc.updateCurrentTokAndNode (Selection.Start); + Console.WriteLine ($"*** Current Token: {doc.CurrentToken} Current Node: {doc.CurrentNode}"); + if (string.IsNullOrEmpty (LineBreak)) + detectLineBreak (); + update (new TextChange (selection.Start, selection.Length, LineBreak)); + autoAdjustScroll = true; + IFace.forceTextCursor = true; + e.Handled = true; + return; + } } base.onKeyDown(sender, e); @@ -315,61 +379,76 @@ namespace Crow SyntaxNode getFoldContainingLine (int line) { if (!(Document is SourceDocument doc)) return null; - IEnumerable folds = doc.SyntaxRootNode.FoldableNodes; - if (folds == null) - return null; - return folds.LastOrDefault (n => n.StartLine <= line && n.EndLine >= line); + doc.EnterReadLock(); + try { + IEnumerable folds = doc.SyntaxRootNode.FoldableNodes; + if (folds == null) + return null; + return folds.LastOrDefault (n => n.StartLine <= line && n.EndLine >= line); + } finally { + doc.ExitReadLock (); + } } int getVisualLine (int absoluteLine) { if (!(Document is SourceDocument doc)) return absoluteLine; - int foldedLines = 0; - IEnumerator foldsEnum = doc.SyntaxRootNode.FoldableNodes.GetEnumerator(); - bool notEndOfFolds = foldsEnum.MoveNext(); - while (notEndOfFolds && foldsEnum.Current.StartLine < absoluteLine) { - if (foldsEnum.Current.isFolded) { - foldedLines += foldsEnum.Current.LineCount - 1; - SyntaxNode nextNode = foldsEnum.Current.NextSiblingOrParentsNextSibling; - if (nextNode == null) - break; - notEndOfFolds = foldsEnum.MoveNext(); - while (notEndOfFolds && foldsEnum.Current.StartLine < nextNode.StartLine) + doc.EnterReadLock(); + try { + int foldedLines = 0; + IEnumerator foldsEnum = doc.SyntaxRootNode.FoldableNodes.GetEnumerator(); + bool notEndOfFolds = foldsEnum.MoveNext(); + while (notEndOfFolds && foldsEnum.Current.StartLine < absoluteLine) { + if (foldsEnum.Current.isFolded) { + foldedLines += foldsEnum.Current.LineCount - 1; + SyntaxNode nextNode = foldsEnum.Current.NextSiblingOrParentsNextSibling; + if (nextNode == null) + break; notEndOfFolds = foldsEnum.MoveNext(); - } else - notEndOfFolds = foldsEnum.MoveNext(); + while (notEndOfFolds && foldsEnum.Current.StartLine < nextNode.StartLine) + notEndOfFolds = foldsEnum.MoveNext(); + } else + notEndOfFolds = foldsEnum.MoveNext(); + } + return absoluteLine - foldedLines; + } finally { + doc.ExitReadLock (); } - return absoluteLine - foldedLines; } int countFoldedLinesUntil (int visualLine) { if (!(Document is SourceDocument doc)) return 0; - int foldedLines = 0; - IEnumerator nodeEnum = doc.SyntaxRootNode.FoldableNodes.GetEnumerator (); - if (!nodeEnum.MoveNext()) - return 0; - - int l = 0; - while (l < visualLine + foldedLines) { - if (nodeEnum.Current.StartLine == l) { - if (nodeEnum.Current.isFolded) { - foldedLines += nodeEnum.Current.lineCount - 1; - SyntaxNode nextNode = nodeEnum.Current.NextSiblingOrParentsNextSibling; - if (nextNode == null || !nodeEnum.MoveNext()) - return foldedLines; + doc.EnterReadLock(); + try { + int foldedLines = 0; + IEnumerator nodeEnum = doc.SyntaxRootNode.FoldableNodes.GetEnumerator (); + if (!nodeEnum.MoveNext()) + return 0; - while (nodeEnum.Current.StartLine < nextNode.StartLine) { - if (!nodeEnum.MoveNext()) + int l = 0; + while (l < visualLine + foldedLines) { + if (nodeEnum.Current.StartLine == l) { + if (nodeEnum.Current.isFolded) { + foldedLines += nodeEnum.Current.lineCount - 1; + SyntaxNode nextNode = nodeEnum.Current.NextSiblingOrParentsNextSibling; + if (nextNode == null || !nodeEnum.MoveNext()) return foldedLines; - } - } else if (!nodeEnum.MoveNext()) - return foldedLines; + while (nodeEnum.Current.StartLine < nextNode.StartLine) { + if (!nodeEnum.MoveNext()) + return foldedLines; + } + + } else if (!nodeEnum.MoveNext()) + return foldedLines; + } + l ++; } - l ++; + //Console.WriteLine ($"visualLine: {visualLine} foldedLines: {foldedLines}"); + return foldedLines; + } finally { + doc.ExitReadLock (); } - //Console.WriteLine ($"visualLine: {visualLine} foldedLines: {foldedLines}"); - return foldedLines; } protected override int getAbsoluteLineIndexFromVisualLineMove (int startLine, int visualLineDiff) { @@ -404,6 +483,28 @@ namespace Crow } + protected virtual void fillHighlight (Context gr, int l, CharLocation selStart, CharLocation selEnd, RectangleD selRect, Color color) { + if (selStart.Line == selEnd.Line) { + selRect.X += selStart.VisualCharXPosition; + selRect.Width = selEnd.VisualCharXPosition - selStart.VisualCharXPosition; + } else if (l == selStart.Line) { + selRect.X += selStart.VisualCharXPosition; + selRect.Width -= selStart.VisualCharXPosition - 10.0; + } else if (l == selEnd.Line) + //selRect.Width = selEnd.VisualCharXPosition - selRect.X;// + cb.X; + selRect.Width = selEnd.VisualCharXPosition; + else + selRect.Width += 10.0; + + gr.Operator = Operator.DestOver; + + gr.SetSource (color); + gr.Rectangle (selRect); + gr.Fill (); + Foreground.SetAsSource (IFace, gr); + + gr.Operator = Operator.Over; + } protected override void drawContent (Context gr) { if (!(Document is SourceDocument doc)) { base.drawContent (gr); @@ -432,6 +533,10 @@ namespace Crow CharLocation selStart = default, selEnd = default; bool selectionNotEmpty = false; + CharLocation? nodeStart = null, nodeEnd = null; + + CharLocation? editNodeStart = null, editNodeEnd = null;//debug + CharLocation? hoverNodeStart = null, hoverNodeEnd = null; if (currentLoc?.Column < 0) { updateLocation (gr, cb.Width, ref currentLoc); @@ -439,6 +544,30 @@ namespace Crow } else updateLocation (gr, cb.Width, ref currentLoc); + if (CurrentNode != null) { + TextSpan nodeSpan = CurrentNode.Span; + nodeStart = lines.GetLocation (nodeSpan.Start); + updateLocation (gr, cb.Width, ref nodeStart); + nodeEnd = lines.GetLocation (nodeSpan.End); + updateLocation (gr, cb.Width, ref nodeEnd); + } +#if DEBUG_NODES + if (doc.EditedNode != null) { + TextSpan nodeSpan = doc.EditedNode.Span; + editNodeStart = lines.GetLocation (nodeSpan.Start); + updateLocation (gr, cb.Width, ref editNodeStart); + editNodeEnd = lines.GetLocation (nodeSpan.End); + updateLocation (gr, cb.Width, ref editNodeEnd); + } + if (hoverNode != null) { + TextSpan nodeSpan = hoverNode.Span; + hoverNodeStart = lines.GetLocation (nodeSpan.Start); + updateLocation (gr, cb.Width, ref hoverNodeStart); + hoverNodeEnd = lines.GetLocation (nodeSpan.End); + updateLocation (gr, cb.Width, ref hoverNodeEnd); + } +#endif + if (overlay != null && overlay.IsVisible) { Point p = new Point((int)currentLoc.Value.VisualCharXPosition - ScrollX, (int)(lineHeight * (currentLoc.Value.Line + 1) - ScrollY)); if (p.Y < 0 || p.X < 0) @@ -540,44 +669,17 @@ namespace Crow } RectangleD lineRect = new RectangleD (cb.X, pixY, pixX - cb.X, lineHeight); - if (selectionNotEmpty) { - RectangleD selRect = lineRect; - - if (l >= selStart.Line && l <= selEnd.Line) { - if (selStart.Line == selEnd.Line) { - selRect.X += selStart.VisualCharXPosition; - selRect.Width = selEnd.VisualCharXPosition - selStart.VisualCharXPosition; - } else if (l == selStart.Line) { - selRect.X += selStart.VisualCharXPosition; - selRect.Width -= selStart.VisualCharXPosition - 10.0; - } else if (l == selEnd.Line) - selRect.Width = selEnd.VisualCharXPosition - selRect.X + cb.X; - else - selRect.Width += 10.0; - - buff = sourceBytes.Slice(lines[l].Start, lines[l].Length); - int size = buff.Length * 4 + 1; - if (bytes.Length < size) - bytes = size > 512 ? new byte[size] : stackalloc byte[size]; - - int encodedBytes = Crow.Text.Encoding.ToUtf8 (buff, bytes); - - gr.SetSource (SelectionBackground); - gr.Rectangle (selRect); - if (encodedBytes < 0) - gr.Fill (); - else { - gr.FillPreserve (); - gr.Save (); - gr.Clip (); - gr.SetSource (SelectionForeground); - gr.MoveTo (lineRect.X, lineRect.Y + fe.Ascent); - gr.ShowText (bytes.Slice (0, encodedBytes)); - gr.Restore (); - } - Foreground.SetAsSource (IFace, gr); - } - } + if (CurrentNode != null && l >= nodeStart.Value.Line && l <= nodeEnd.Value.Line) + fillHighlight (gr, l, nodeStart.Value, nodeEnd.Value, lineRect, new Color(0.0,0.1,0.0,0.1));; +#if DEBUG_NODES + if (doc.EditedNode != null && l >= editNodeStart.Value.Line && l <= editNodeEnd.Value.Line) + fillHighlight (gr, l, editNodeStart.Value, editNodeEnd.Value, lineRect, new Color(0,0.5,0,0.2));; + if (hoverNode != null && l >= hoverNodeStart.Value.Line && l <= hoverNodeEnd.Value.Line) + fillHighlight (gr, l, hoverNodeStart.Value, hoverNodeEnd.Value, lineRect, new Color(0,0,0.8,0.1));; +#endif + if (selectionNotEmpty && l >= selStart.Line && l <= selEnd.Line) + fillHighlight (gr, l, selStart, selEnd, lineRect, SelectionBackground); + //Draw line numbering if (printLineNumbers){ diff --git a/CrowEditBase/ui/CrowEdit.style b/CrowEditBase/ui/CrowEdit.style index 89d08c6..17c4d56 100644 --- a/CrowEditBase/ui/CrowEdit.style +++ b/CrowEditBase/ui/CrowEdit.style @@ -30,6 +30,7 @@ Editor { BubbleEvents ="None"; ClipToClientRect = "true"; MouseCursor = "ibeam"; + CacheEnabled = "true"; } icon { @@ -61,8 +62,8 @@ Spinner { Template = "#ui.spinner.template"; } TreeIcon { - Width="18"; - Height="18"; + Width="16"; + Height="16"; } TreeIconSmall { Width="12"; diff --git a/CrowEditBase/ui/MenuButton.template b/CrowEditBase/ui/MenuButton.template index 92414b2..6a9f05d 100644 --- a/CrowEditBase/ui/MenuButton.template +++ b/CrowEditBase/ui/MenuButton.template @@ -1,5 +1,5 @@  -