From 8e95edc8f3ef2542747b41584b2a5965e0af89e5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jean-Philippe=20Bruy=C3=A8re?= Date: Mon, 7 Jul 2025 14:18:37 +0200 Subject: [PATCH] getStreamFromPath in design, exceptions basic implementation, syntax theme editor/option --- CrowEditBase/icons/IconAlerte.svg | 177 +++++ CrowEditBase/icons/compiler_error.svg | 13 + CrowEditBase/icons/compiler_warning.svg | 53 ++ .../icons/compiler_warning_orange.svg | 54 ++ CrowEditBase/src/Compiler/SourceDocument.cs | 3 +- CrowEditBase/src/Compiler/SyntaxAnalyser.cs | 2 +- .../Compiler/SyntaxNodes/MultiNodeSyntax.cs | 11 + .../src/Compiler/SyntaxNodes/SyntaxNode.cs | 5 +- .../Compiler/SyntaxNodes/SyntaxRootNode.cs | 7 +- CrowEditBase/src/CrowEditBase.cs | 2 - CrowEditBase/src/Document.cs | 6 +- CrowEditBase/src/Extensions.cs | 14 +- CrowEditBase/src/TextFormatting.cs | 80 ++ CrowEditBase/ui/ColorPicker2.template | 29 + CrowEditBase/ui/CrowEdit.style | 4 +- SyntaxThemes/Default.syntax | 28 + SyntaxThemes/Visual Studio Dark.tmTheme | 720 ++++++++++++++++++ SyntaxThemes/Visual Studio.syntax | 24 + default.config | 2 + plugins/CECrowPlugin/src/CrowService.cs | 22 +- plugins/CERoslynPlugin/src/MSBuildProject.cs | 31 +- src/CrowEdit.cs | 114 ++- ui/EditorOptions.crow | 6 + ui/windows/winExceptions.crow | 9 +- ui/windows/winThemeEditor.crow | 31 + 25 files changed, 1406 insertions(+), 41 deletions(-) create mode 100644 CrowEditBase/icons/IconAlerte.svg create mode 100644 CrowEditBase/icons/compiler_error.svg create mode 100644 CrowEditBase/icons/compiler_warning.svg create mode 100644 CrowEditBase/icons/compiler_warning_orange.svg create mode 100644 CrowEditBase/src/TextFormatting.cs create mode 100644 CrowEditBase/ui/ColorPicker2.template create mode 100644 SyntaxThemes/Default.syntax create mode 100644 SyntaxThemes/Visual Studio Dark.tmTheme create mode 100644 SyntaxThemes/Visual Studio.syntax create mode 100644 ui/windows/winThemeEditor.crow diff --git a/CrowEditBase/icons/IconAlerte.svg b/CrowEditBase/icons/IconAlerte.svg new file mode 100644 index 0000000..286dbf3 --- /dev/null +++ b/CrowEditBase/icons/IconAlerte.svg @@ -0,0 +1,177 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CrowEditBase/icons/compiler_error.svg b/CrowEditBase/icons/compiler_error.svg new file mode 100644 index 0000000..1b50568 --- /dev/null +++ b/CrowEditBase/icons/compiler_error.svg @@ -0,0 +1,13 @@ + + + + Layer 1 + + X + + + Layer 2 + + ! + + \ No newline at end of file diff --git a/CrowEditBase/icons/compiler_warning.svg b/CrowEditBase/icons/compiler_warning.svg new file mode 100644 index 0000000..9dac377 --- /dev/null +++ b/CrowEditBase/icons/compiler_warning.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + 3D effect warning triangle + 18/9/07 + + + Tim O'Ryan + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CrowEditBase/icons/compiler_warning_orange.svg b/CrowEditBase/icons/compiler_warning_orange.svg new file mode 100644 index 0000000..233437f --- /dev/null +++ b/CrowEditBase/icons/compiler_warning_orange.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + 3D effect warning triangle + 18/9/07 + + + Tim O'Ryan + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/SourceDocument.cs b/CrowEditBase/src/Compiler/SourceDocument.cs index 258e186..e9245eb 100644 --- a/CrowEditBase/src/Compiler/SourceDocument.cs +++ b/CrowEditBase/src/Compiler/SourceDocument.cs @@ -32,6 +32,7 @@ namespace CrowEditBase public bool IsParsed => root != null && Tokens.Length > 0; public ReadOnlySpan Tokens => root.Tokens; public IEnumerable SyntaxRootChildNodes => root?.children; + public IEnumerable Exceptions => root?.AllExceptions; public SyntaxException CurrentException { get => CrowEditBase.App.CurrentException; set { @@ -168,7 +169,7 @@ namespace CrowEditBase if (cancel.IsCancellationRequested) return; - NotifyValueChanged("Exceptions", syntaxAnalyser?.Exceptions); + NotifyValueChanged("Exceptions", Exceptions); NotifyValueChanged ("SyntaxRootChildNodes", (object)null); NotifyValueChanged ("SyntaxRootChildNodes", SyntaxRootChildNodes); } diff --git a/CrowEditBase/src/Compiler/SyntaxAnalyser.cs b/CrowEditBase/src/Compiler/SyntaxAnalyser.cs index 9496f9f..20cfc6c 100644 --- a/CrowEditBase/src/Compiler/SyntaxAnalyser.cs +++ b/CrowEditBase/src/Compiler/SyntaxAnalyser.cs @@ -18,7 +18,7 @@ namespace CrowEditBase protected ReadOnlyTextBuffer source; protected SyntaxRootNode Root; protected CancellationToken cancel; - public IEnumerable Exceptions => null;// Root?.GetAllExceptions(); + public IEnumerable Exceptions => Root?.GetAllExceptions(source); public abstract Task Process (CancellationToken cancel = default); #region Token handling diff --git a/CrowEditBase/src/Compiler/SyntaxNodes/MultiNodeSyntax.cs b/CrowEditBase/src/Compiler/SyntaxNodes/MultiNodeSyntax.cs index 5081019..e9b64b3 100644 --- a/CrowEditBase/src/Compiler/SyntaxNodes/MultiNodeSyntax.cs +++ b/CrowEditBase/src/Compiler/SyntaxNodes/MultiNodeSyntax.cs @@ -135,5 +135,16 @@ namespace CrowEditBase } } + public IEnumerable GetAllExceptions(ReadOnlyTextBuffer source) { + foreach (SyntaxNode n in children) { + if (n is UnexpectedTokenSyntax uts) + yield return new SyntaxException(uts.Message, source.Lines.GetLocation(uts.SpanStart)); + else if (n is MultiNodeSyntax mns) { + foreach (SyntaxException se in mns.GetAllExceptions(source)) + yield return se; + } + } + } + } } \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/SyntaxNodes/SyntaxNode.cs b/CrowEditBase/src/Compiler/SyntaxNodes/SyntaxNode.cs index 102721e..da66bfe 100644 --- a/CrowEditBase/src/Compiler/SyntaxNodes/SyntaxNode.cs +++ b/CrowEditBase/src/Compiler/SyntaxNodes/SyntaxNode.cs @@ -8,7 +8,10 @@ using Crow.Text; namespace CrowEditBase { public class UnexpectedTokenSyntax : SingleTokenSyntax { - public UnexpectedTokenSyntax(Token tok) : base (tok) { } + public string Message; + public UnexpectedTokenSyntax(Token tok, string message = "Unexpected token") : base (tok) { + Message = message; + } } public class CommentTriviaSyntax : MultiNodeSyntax { bool block; diff --git a/CrowEditBase/src/Compiler/SyntaxNodes/SyntaxRootNode.cs b/CrowEditBase/src/Compiler/SyntaxNodes/SyntaxRootNode.cs index 2e7bf84..981e567 100644 --- a/CrowEditBase/src/Compiler/SyntaxNodes/SyntaxRootNode.cs +++ b/CrowEditBase/src/Compiler/SyntaxNodes/SyntaxRootNode.cs @@ -2,6 +2,8 @@ // // 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; namespace CrowEditBase @@ -17,14 +19,11 @@ namespace CrowEditBase 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 IEnumerable AllExceptions => GetAllExceptions(buffer); public string GetTokenStringByIndex (int idx) => tokens != null ? idx >= 0 && idx < tokens.Length ? GetText(tokens[idx].Span).ToString() : null : null; public Token GetTokenByIndex (int idx) => tokens != null ? diff --git a/CrowEditBase/src/CrowEditBase.cs b/CrowEditBase/src/CrowEditBase.cs index fb3f1c1..4936f62 100644 --- a/CrowEditBase/src/CrowEditBase.cs +++ b/CrowEditBase/src/CrowEditBase.cs @@ -141,8 +141,6 @@ namespace CrowEditBase } #endregion - - public T GetService () where T : Service { T service = Services.OfType().FirstOrDefault (); if (service == null) { diff --git a/CrowEditBase/src/Document.cs b/CrowEditBase/src/Document.cs index 4dd192b..f91fdba 100644 --- a/CrowEditBase/src/Document.cs +++ b/CrowEditBase/src/Document.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) @@ -6,8 +6,6 @@ using System; using System.IO; using System.Threading; using Crow; -using System.Runtime.CompilerServices; -using System.Collections.Generic; using static CrowEditBase.CrowEditBase; namespace CrowEditBase @@ -40,8 +38,6 @@ namespace CrowEditBase public void EnterWriteLock () => documentRWLock.EnterWriteLock (); public void ExitWriteLock () => documentRWLock.ExitWriteLock (); - - public string FullPath { get => fullPath; set { diff --git a/CrowEditBase/src/Extensions.cs b/CrowEditBase/src/Extensions.cs index 581f4a7..1064500 100644 --- a/CrowEditBase/src/Extensions.cs +++ b/CrowEditBase/src/Extensions.cs @@ -1,10 +1,4 @@ -using System; -using System.IO; -using System.Linq; -using System.Collections.Generic; -using System.Text; -using Crow; -using Crow.Text; +using Crow; using CrowEditBase; using System.Reflection; @@ -12,8 +6,8 @@ namespace CrowEdit { public static class Extensions { - public static Picture GetIcon (this MemberInfo mi) - => mi is EventInfo ? new BmpPicture("#Icons.event.png") : new BmpPicture("#Icons.property.png"); - + public static Picture GetIcon (this MemberInfo mi) + => mi is EventInfo ? new SvgPicture("#icons.event.svg") : new SvgPicture("#icons.property.svg"); + public static Picture GetIcon (this SyntaxException se) => new SvgPicture("#icons.IconAlerte.svg"); } } diff --git a/CrowEditBase/src/TextFormatting.cs b/CrowEditBase/src/TextFormatting.cs new file mode 100644 index 0000000..1e7322b --- /dev/null +++ b/CrowEditBase/src/TextFormatting.cs @@ -0,0 +1,80 @@ +// Copyright (c) 2013-2025 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using System.Runtime.CompilerServices; +using Crow; +using Drawing2D; + +namespace CrowEditBase +{ + public class TextFormatting : IValueChange { + #region IValueChange implementation + public event EventHandler ValueChanged; + public virtual void NotifyValueChanged (string MemberName, object _value) { + //Debug.WriteLine ("Value changed: {0}->{1} = {2}", this, MemberName, _value); + if (ValueChanged != null) + ValueChanged.Invoke (this, new ValueChangeEventArgs (MemberName, _value)); + } + public void NotifyValueChanged (object _value, [CallerMemberName] string caller = null) { + if (ValueChanged != null) + NotifyValueChanged (caller, _value); + } + #endregion + Color foreground, background; + bool bold, italic; + public Color Foreground { + get => foreground; + set { + if (foreground == value) + return; + foreground = value; + NotifyValueChanged (foreground); + } + } + public Color Background { + get => background; + set { + if (background == value) + return; + background = value; + NotifyValueChanged (background); + } + } + public bool Bold { + get => bold; + set { + if (bold == value) + return; + bold = value; + NotifyValueChanged (bold); + } + } + public bool Italic { + get => italic; + set { + if (italic == value) + return; + italic = value; + NotifyValueChanged (italic); + } + } + + public TextFormatting(Color fg, Color bg, bool _bold = false, bool _italic = false){ + Foreground = fg; + Background = bg; + Bold = _bold; + Italic = _italic; + } + + public override string ToString () + => $"{Foreground};{Background};{Bold};{Italic}"; + + public static TextFormatting Parse (string str) { + string[] tmp = str.Split (';'); + return new TextFormatting (Color.Parse (tmp[0]), Color.Parse (tmp[1]), bool.Parse (tmp[2]), bool.Parse (tmp[3])); + } + + } +} + diff --git a/CrowEditBase/ui/ColorPicker2.template b/CrowEditBase/ui/ColorPicker2.template new file mode 100644 index 0000000..cba01b2 --- /dev/null +++ b/CrowEditBase/ui/ColorPicker2.template @@ -0,0 +1,29 @@ + + /> + + + + + + + \ No newline at end of file diff --git a/CrowEditBase/ui/CrowEdit.style b/CrowEditBase/ui/CrowEdit.style index 099d56a..83011fe 100644 --- a/CrowEditBase/ui/CrowEdit.style +++ b/CrowEditBase/ui/CrowEdit.style @@ -153,7 +153,7 @@ EnumSelector { Template = "#ui.EnumSelector.template"; //ItemStyle = "CheckBox"; } -CheckBox { +/*CheckBox { Template= "#Crow.CheckBox2.template"; Width = "Stretched"; Height = "Fit"; @@ -164,7 +164,7 @@ CheckBox { Unchecked = "{Background=${ControlIdle}}"; MouseEnter = "{Foreground=${ControlCaptionHoverColor}}"; MouseLeave = "{Foreground=${ControlForeground}}"; -} +}*/ StateBox { Template= "#Crow.CheckBox2.template"; Width = "Stretched"; diff --git a/SyntaxThemes/Default.syntax b/SyntaxThemes/Default.syntax new file mode 100644 index 0000000..2afdb14 --- /dev/null +++ b/SyntaxThemes/Default.syntax @@ -0,0 +1,28 @@ +default = Jet;#DADADA;False;False +leftMargin = Grey;Onyx;False;False +Selection = White;#B6CEE1;False;False +keyword = Blue;Transparent;False;False +DocumentationComment = MediumSeaGreen;Transparent;False;True +Region = DeepPink;Transparent;False;False +FoldedRegion = #FFFFF8;#35B23C;True;False +Documentation = GreenYellow;Transparent;False;False +trivia = Grey;Transparent;False;True +PreprocessorDirective = DeepPink;Transparent;True;False +PreprocessorMessage = DeepPink;Transparent;True;False +PredefinedType = DarkCyan;Transparent;False;False +AccessibilityModifier = RoyalBlue;Transparent;False;False +DisabledText = Grey;Transparent;False;False +identifier = Onyx;Transparent;True;False +Namespace = RoyalBlue;Transparent;True;False +NamedType = DarkGreen;Transparent;True;False +Property = Green;Transparent;True;False +Field = Black;Transparent;False;False +Method = DarkOliveGreen;Transparent;True;False +LiteralExpression = FireBrick;Transparent;False;True +error = Red;Transparent;False;False +TypeSyntax = DarkCyan;Transparent;False;False +TypeDeclaration = Lavender;Transparent;False;False +Punctuation = Black;Transparent;False;False +ContextualKeyword = DarkBlue;Transparent;True;False +InstanceExpression = Jet;Transparent;False;False +NamespaceMemberDeclaration = Jet;Transparent;False;False diff --git a/SyntaxThemes/Visual Studio Dark.tmTheme b/SyntaxThemes/Visual Studio Dark.tmTheme new file mode 100644 index 0000000..692dff2 --- /dev/null +++ b/SyntaxThemes/Visual Studio Dark.tmTheme @@ -0,0 +1,720 @@ + + + + + + + + + name + Visual Studio Dark + author + Niklas Mollenhauer + settings + + + settings + + background + #1E1E1E + caret + #DCDCDC + foreground + #DCDCDC + invisibles + #FFFFFF40 + lineHighlight + #0F0F0F + selection + #264F78 + + + + name + Comment + scope + comment + settings + + fontStyle + + foreground + #399812ff + + + + name + Variable + scope + variable + settings + + fontStyle + + foreground + #ffffffff + + + + name + Keyword + scope + keyword + settings + + fontStyle + + foreground + #23beffff + + + + name + Comparision Operator + scope + keyword.operator.comparison + settings + + fontStyle + + foreground + #ffffffff + + + + name + Assignment Operator + scope + keyword.operator.assignment + settings + + fontStyle + + foreground + #ffffffff + + + + name + Arithmetic Operator + scope + keyword.operator.arithmetic + settings + + fontStyle + + foreground + #ffffffff + + + + name + Number + scope + constant.numeric + settings + + fontStyle + + foreground + #f5ffd8ff + + + + name + User-defined constant + scope + constant + settings + + fontStyle + + foreground + #f3ffd8ff + + + + name + Built-in constant + scope + constant.language + settings + + fontStyle + + foreground + #23beffff + + + + name + Boolean + scope + constant.language.boolean + settings + + fontStyle + + foreground + #23beffff + + + + name + String + scope + string + settings + + fontStyle + + foreground + #ffc08bff + + + + name + String interpolation + scope + constant.character.escape, string source + settings + + fontStyle + + foreground + #ffffdfff + + + + name + Preprocessor line + scope + meta.preprocessor + settings + + fontStyle + + foreground + #bcbcbcff + + + + name + Preprocessor directive + scope + keyword.control.import + settings + + fontStyle + + foreground + #bcbcbcff + + + + name + Function name + scope + entity.name.function, keyword.other.name-of-parameter.objc + settings + + fontStyle + + foreground + #ffffffff + + + + name + Class name + scope + entity.name.type + settings + + fontStyle + + foreground + #12ffeaff + + + + name + Type name + scope + storage.type + settings + + fontStyle + + foreground + #23beffff + + + + name + Type modifier + scope + storage.modifier + settings + + fontStyle + + foreground + #23beffff + + + + name + Inherited class name + scope + entity.other.inherited-class + settings + + fontStyle + + foreground + #12ffeaff + + + + name + Function parameter + scope + variable.parameter + settings + + fontStyle + + + + + name + Function argument and result types + scope + storage.type.method + settings + + fontStyle + + foreground + #5d617cff + + + + name + Section + scope + meta.section entity.name.section, declaration.section entity.name.section + settings + + fontStyle + + + + + name + Library function + scope + support.function + settings + + fontStyle + + foreground + #ffffffff + + + + name + Library object + scope + support.class, support.type + settings + + fontStyle + + foreground + #ffffffff + + + + name + Library constant + scope + support.constant + settings + + fontStyle + + foreground + #f5ffd8ff + + + + name + Library variable + scope + support.variable + settings + + fontStyle + + foreground + #ffffffff + + + + name + JS: Operator + scope + keyword.operator.js + settings + + foreground + #4b6a8fff + + + + name + Invalid + scope + invalid + settings + + foreground + #ff0000ff + + + + name + Invalid trailing whitespace + scope + invalid.deprecated.trailing-whitespace + settings + + background + #ff0000ff + + + + name + Embedded source + scope + text source, string.unquoted + settings + + background + #000000ff + + + + name + Markup XML declaration + scope + meta.xml-processing, declaration.xml-processing + settings + + fontStyle + + foreground + #4b4b2eff + + + + name + Markup DOCTYPE + scope + meta.doctype, declaration.doctype + settings + + fontStyle + + foreground + #808080ff + + + + name + Markup DTD + scope + meta.doctype.DTD, declaration.doctype.DTD + settings + + fontStyle + + + + + name + Markup tag + scope + meta.tag, declaration.tag + settings + + fontStyle + + foreground + #808080ff + + + + name + Markup name of tag + scope + entity.name.tag + settings + + fontStyle + + foreground + #23beffff + + + + name + Markup tag attribute + scope + entity.other.attribute-name + settings + + fontStyle + + foreground + #a8ffffff + + + + name + Markup: Attribute Value + scope + string.quoted.double.xml, string.quoted.double.html + settings + + fontStyle + + foreground + #ffffffff + + + + name + Markup: Heading + scope + markup.heading + settings + + fontStyle + + foreground + #23beffff + + + + name + Markup: Quote + scope + markup.quote + settings + + fontStyle + + foreground + #ffffffff + + + + name + Markup: List + scope + markup.list + settings + + foreground + #ffffffff + + + + name + § css tag-name + scope + meta.selector.css entity.name.tag + settings + + foreground + #ffff79ff + + + + name + § css:pseudo-class + scope + meta.selector.css entity.other.attribute-name.tag.pseudo-class + settings + + foreground + #ffff79ff + + + + name + § css#id + scope + meta.selector.css entity.other.attribute-name.id + settings + + foreground + #ffff79ff + + + + name + § css.class + scope + meta.selector.css entity.other.attribute-name.class + settings + + foreground + #ffff79ff + + + + name + § css property-name: + scope + support.type.property-name.css + settings + + foreground + #beffffff + + + + name + § css property-value; + scope + meta.property-group support.constant.property-value.css, meta.property-value support.constant.property-value.css + settings + + foreground + #ffffffff + + + + name + § css @at-rule + scope + meta.preprocessor.at-rule keyword.control.at-rule + settings + + foreground + #8fffffff + + + + name + C#: XML Comment Tags + scope + source.cs comment.block.documentation.source.cs meta.tag.xml, source.cs comment.block.documentation.source.cs meta.tag.xml entity.name.tag.localname.xml, source.cs comment.block.documentation.source.cs meta.tag.xml entity.other.attribute-name + settings + + foreground + #399812ff + + + + name + GitGutter deleted + scope + markup.deleted.git_gutter + settings + + foreground + #ff0061ff + + + + name + GitGutter inserted + scope + markup.inserted.git_gutter + settings + + foreground + #d4ff00ff + + + + name + GitGutter changed + scope + markup.changed.git_gutter + settings + + foreground + #b17cffff + + + + name + GitGutter ignored + scope + markup.ignored.git_gutter + settings + + foreground + #232323ff + + + + name + GitGutter untracked + scope + markup.untracked.git_gutter + settings + + foreground + #232323ff + + + + name + Git Modified Line + scope + git.changes.x + settings + + background + #00001aff + + + + name + Git Added Line + scope + git.changes.+ + settings + + background + #002800ff + + + + name + Git Remove Line + scope + git.changes.- + settings + + background + #d60000ff + + + + uuid + 2fd1a8f9-ddfd-11e2-a28f-0800200c9a66 + colorSpaceName + sRGB + semanticClass + theme.dark.visual_studio_dark + + \ No newline at end of file diff --git a/SyntaxThemes/Visual Studio.syntax b/SyntaxThemes/Visual Studio.syntax new file mode 100644 index 0000000..e7a1bae --- /dev/null +++ b/SyntaxThemes/Visual Studio.syntax @@ -0,0 +1,24 @@ +default = Red;Transparent;False;False +keyword = Yellow;Transparent;False;False +DocumentationComment = MediumSeaGreen;Transparent;False;True +Documentation = GreenYellow;Transparent;False;False +trivia = Grey;Transparent;False;True +PreprocessorDirective = DeepPink;Transparent;True;False +PreprocessorMessage = DeepPink;Transparent;True;False +PredefinedType = DarkCyan;Transparent;False;False +AccessibilityModifier = RoyalBlue;Transparent;False;False +DisabledText = Grey;Transparent;False;False +identifier = Onyx;Transparent;True;False +Namespace = RoyalBlue;Transparent;True;False +NamedType = DarkGreen;Transparent;True;False +Property = Green;Transparent;True;False +Field = Black;Transparent;False;False +Method = DarkOliveGreen;Transparent;True;False +LiteralExpression = FireBrick;Transparent;False;True +error = Red;Transparent;False;False +TypeSyntax = DarkCyan;Transparent;False;False +TypeDeclaration = Lavender;Transparent;False;False +Punctuation = Black;Transparent;False;False +ContextualKeyword = DarkBlue;Transparent;True;False +InstanceExpression = Jet;Transparent;False;False +NamespaceMemberDeclaration = Jet;Transparent;False;False diff --git a/default.config b/default.config index 4413948..6a2e170 100644 --- a/default.config +++ b/default.config @@ -1,2 +1,4 @@ MainWinWidth=1024 MainWinHeight=768 +SyntaxThemeName=Default +SyntaxThemeDirectory=SyntaxThemes diff --git a/plugins/CECrowPlugin/src/CrowService.cs b/plugins/CECrowPlugin/src/CrowService.cs index bd33ec7..6356f44 100644 --- a/plugins/CECrowPlugin/src/CrowService.cs +++ b/plugins/CECrowPlugin/src/CrowService.cs @@ -18,10 +18,13 @@ using static CECrowPlugin.ForeignWidgetContainer; using Drawing2D; using System.Diagnostics; using Crow; +using System.ComponentModel; +using CERoslynPlugin; namespace CECrowPlugin { public class CrowService : Service { + #region CTOR/DTOR public CrowService () : base () { restoreCrowAssemblies (); initCommands (); @@ -30,6 +33,8 @@ namespace CECrowPlugin ~CrowService() { App.ValueChanged -= app_ValueChanged; } + #endregion + void app_ValueChanged(object instance, ValueChangeEventArgs e) { if (e.MemberName == "CurrentProject") { if (e.NewValue is CERoslynPlugin.SolutionProject sol) @@ -196,6 +201,7 @@ namespace CECrowPlugin NotifyValueChanged(HoverWidgetDesignId); } } + #region dbgIface delegates Func delGetWidgetTypeFromName; Action delResize; @@ -398,6 +404,12 @@ namespace CECrowPlugin foreach (var style in csprj.Flatten.OfType() .Where (pin=>pin.NodeType == NodeType.EmbeddedResource && pin.FullPath.EndsWith (".style", StringComparison.OrdinalIgnoreCase))) yield return style.FullPath; + foreach (var refP in csprj.ReferencedProjects) { + foreach (var style in refP.Flatten.OfType() + .Where (pin=>pin.NodeType == NodeType.EmbeddedResource && + pin.FullPath.EndsWith (".style", StringComparison.OrdinalIgnoreCase))) + yield return style.FullPath; + } } } /*foreach (String item in crowAssemblies) @@ -407,12 +419,18 @@ namespace CECrowPlugin yield return crowAssembly; } Stream getStreamFromPath (string path) { + Stream stream = null; if (App.CurrentProject is CERoslynPlugin.SolutionProject sol) { if (sol.StartupProject is CERoslynPlugin.MSBuildProject csprj) { - return csprj.GetStreamFromTargetPath (path); + if (!csprj.TryGetStreamFromTargetPath (path, out stream)) { + foreach (MSBuildProject refP in csprj.ReferencedProjects) { + if (refP.TryGetStreamFromTargetPath (path, out stream)) + break; + } + } } } - return null; + return stream; } #endregion static string defaultCrowAssemblyLocation => diff --git a/plugins/CERoslynPlugin/src/MSBuildProject.cs b/plugins/CERoslynPlugin/src/MSBuildProject.cs index f432892..0f4fa7c 100644 --- a/plugins/CERoslynPlugin/src/MSBuildProject.cs +++ b/plugins/CERoslynPlugin/src/MSBuildProject.cs @@ -353,26 +353,43 @@ namespace CERoslynPlugin public bool DebugSymbols => bool.Parse (project.GetProperty ("DebugSymbols").EvaluatedValue); public int WarningLevel => int.Parse (project.GetProperty ("WarningLevel").EvaluatedValue); - public Stream GetStreamFromTargetPath (string targetPath) { + public bool TryGetStreamFromTargetPath (string targetPath, out Stream stream) { + stream = null; IEnumerable piNodes = Flatten.OfType(); if (targetPath.StartsWith ('#')) { targetPath = targetPath.Substring (1); MSBuildProjectItemNode pin = piNodes.FirstOrDefault (n => n.NodeType == NodeType.EmbeddedResource && n.HasMetadataValue ("LogicalName", targetPath)); - if (pin != null) - return new FileStream (pin.FullPath, FileMode.Open); + if (pin != null) { + stream = new FileStream (pin.FullPath, FileMode.Open); + return true; + } } else { MSBuildProjectItemNode pin = piNodes.FirstOrDefault (n => n.NodeType == NodeType.None && (n.HasMetadataValue ("CopyToOutputDirectory", "PreserveNewest") || n.HasMetadataValue ("CopyToOutputDirectory", "Always")) && n.EvaluatedInclude == targetPath); - if (pin != null) - return new FileStream (pin.FullPath, FileMode.Open); + if (pin != null) { + stream = new FileStream (pin.FullPath, FileMode.Open); + return true; + } + } + return false; + } + public IEnumerable ReferencedProjects { + get { + string rootPath = Path.GetDirectoryName(FullPath); + var allProjects = solutionProject.FlattenProjetcs.OfType(); + var refProjs = Flatten.OfType(). + Where (r=>r.NodeType == NodeType.ProjectReference); + foreach (var r in refProjs) { + var refP = allProjects.FirstOrDefault(p=>Path.GetRelativePath(rootPath, p.FullPath) == r.EvaluatedInclude.Replace("\\","/")); + if (refP != null) + yield return refP; + } } - return null; } - #region debug void printEvaluatedProperties (ProjectInstance pi) { diff --git a/src/CrowEdit.cs b/src/CrowEdit.cs index aa4a47d..99c1d6f 100644 --- a/src/CrowEdit.cs +++ b/src/CrowEdit.cs @@ -75,6 +75,8 @@ namespace CrowEdit mainDock = w.FindByName ("mainDock") as DockStack; + reloadSyntaxTheme (); + reloadWinConfigs (); lock(UpdateMutex) { @@ -101,6 +103,7 @@ namespace CrowEdit } public Command CMDSave, CMDSaveAs, CMDQuit, CMDHelp, CMDAbout, CMDOptions; + public Command CMDSyntaxTheme_Reload, CMDSyntaxTheme_Save, CMDSyntaxTheme_SaveAs; void initCommands (){ FileCommands = new CommandGroup ("File", @@ -132,7 +135,8 @@ namespace CrowEdit new ActionCommand("Logs", () => LoadWindow ("#CrowEdit.ui.windows.winLogs.crow", this), "#icons.log.svg"), new ActionCommand("Services", () => LoadWindow ("#CrowEdit.ui.windows.winServices.crow", this), "#icons.services.svg"), new ActionCommand("Plugins", () => LoadWindow ("#CrowEdit.ui.windows.winPlugins.crow", this), "#icons.puzzle-piece.svg"), - new ActionCommand("Syntax Tree", () => LoadWindow ("#CrowEdit.ui.windows.winSyntaxExplorer.crow", this), "#icons.plugins.svg") + new ActionCommand("Syntax Tree", () => LoadWindow ("#CrowEdit.ui.windows.winSyntaxExplorer.crow", this), "#icons.plugins.svg"), + new ActionCommand("Syntax Theme Editor", () => LoadWindow ("#CrowEdit.ui.windows.winThemeEditor.crow", this), "#icons.palette.svg") ); CMDHelp = new ActionCommand("Help", () => System.Diagnostics.Debug.WriteLine("help"), "#icons.question.svg"); @@ -142,6 +146,10 @@ namespace CrowEdit ViewCommands, new CommandGroup ("Help", CMDHelp) ); + + CMDSyntaxTheme_Reload = new ActionCommand ("Reload", () => reloadSyntaxTheme ()); + CMDSyntaxTheme_Save = new ActionCommand ("Save", () => saveSyntaxTheme ()); + CMDSyntaxTheme_SaveAs = new ActionCommand ("Save As...", () => saveSyntaxThemeAs ()); } static void loadWindowWithThisDataSource(object sender, string path) { @@ -271,7 +279,7 @@ namespace CrowEdit return; Document doc = OpenedDocuments.FirstOrDefault (d => d.FullPath == lastCurDoc); if (doc != null) - CurrentDocument = doc; + CurrentDocument = doc; } void saveProjectList () { if (Projects.Count == 0) @@ -298,6 +306,108 @@ namespace CrowEdit { return base.GetStreamFromPath(path); } + + #region syntax theme options/loading + Dictionary syntaxTheme; + public Dictionary SyntaxTheme { + get => syntaxTheme; + set { + if (syntaxTheme == value) + return; + syntaxTheme = value; + NotifyValueChanged (SyntaxTheme); + } + } + public string SyntaxThemeDirectory { + get => Configuration.Global.Get ("SyntaxThemeDirectory") ?? + Path.Combine (Path.GetDirectoryName (Assembly.GetEntryAssembly ().Location), "SyntaxThemes"); + set { + if (SyntaxThemeDirectory == value) + return; + Configuration.Global.Set ("SyntaxThemeDirectory", value); + NotifyValueChanged ("SyntaxThemeDirectory", (object)SyntaxThemeDirectory); + NotifyValueChanged ("AvailableSyntaxThemes", (object)AvailableSyntaxThemes); + reloadSyntaxTheme(); + } + } + public string syntaxThemeFile => Path.Combine (SyntaxThemeDirectory, $"{SyntaxThemeName}.syntax"); + public string[] AvailableSyntaxThemes { + get { + if (!Directory.Exists(SyntaxThemeDirectory)) + return null; + string[] tmp = Directory.GetFiles (SyntaxThemeDirectory); + for (int i = 0; i < tmp.Length; i++) + tmp[i] = Path.GetFileNameWithoutExtension (tmp[i]); + return tmp; + } + } + public string SyntaxThemeName { + get => Configuration.Global.Get ("SyntaxThemeName"); + set { + if (SyntaxThemeName == value) + return; + Configuration.Global.Set ("SyntaxThemeName", value); + NotifyValueChanged ("SyntaxThemeName", (object)SyntaxThemeName); + reloadSyntaxTheme (); + } + } + public Command CMDOptions_SyntaxThemeDirectory => new ActionCommand ("...", + () => { + FileDialog dlg = App.LoadIMLFragment (@" + "); + dlg.OkClicked += (sender, e) => SyntaxThemeDirectory = (sender as FileDialog).SelectedFileFullPath; + dlg.DataSource = this; + } + ); + + void reloadSyntaxTheme () { + if (!File.Exists (syntaxThemeFile)) + return; + Dictionary theme = new Dictionary (); + using (StreamReader sr = new StreamReader(syntaxThemeFile)) { + while (!sr.EndOfStream) { + string l = sr.ReadLine (); + string[] tmp = l.Split ('='); + theme.Add (tmp[0].Trim (), TextFormatting.Parse (tmp[1].Trim ())); + } + } + SyntaxTheme = theme; + } + void saveSyntaxTheme () { + using (StreamWriter sw = new StreamWriter (syntaxThemeFile)) { + foreach (string key in SyntaxTheme.Keys) { + sw.WriteLine ($"{key} = {SyntaxTheme[key]}"); + } + } + } + void saveSyntaxThemeAs () + { + FileDialog fd = LoadIMLFragment (@""); + fd.DataSource = this; + fd.OkClicked += (sender, e) => { + FileDialog fd = sender as FileDialog; + + if (string.IsNullOrEmpty (fd.SelectedFileFullPath)) + return; + + if (File.Exists(fd.SelectedFileFullPath)) { + MessageBox mb = MessageBox.ShowModal (this, MessageBox.Type.YesNo, "File exists, overwrite?"); + mb.Yes += (sender2, e2) => { + SyntaxThemeName = Path.GetFileNameWithoutExtension(fd.SelectedFile); + SyntaxThemeDirectory = fd.SelectedDirectory; + saveSyntaxTheme (); + }; + return; + } + + SyntaxThemeName = Path.GetFileNameWithoutExtension(fd.SelectedFile); + saveSyntaxTheme (); + }; + } + #endregion } } diff --git a/ui/EditorOptions.crow b/ui/EditorOptions.crow index ea6465c..a8d36df 100644 --- a/ui/EditorOptions.crow +++ b/ui/EditorOptions.crow @@ -12,5 +12,11 @@ + +