From af3a87ef16e2f4df9fa01a89fd709125c91188d6 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jean-Philippe=20Bruy=C3=A8re?= Date: Sat, 24 Feb 2018 11:51:12 +0100 Subject: [PATCH] change namespace of CrowIDE to Crow.Coding, move editor source inside, crowIde wip --- Crow.Test/Crow.Test.csproj | 2 - Crow.Test/Test.cs | 1 + Crow.sln | 33 +- CrowIDE/CrowIDE.csproj | 28 +- CrowIDE/src/CrowIDE.cs | 53 +- CrowIDE/src/DesignInterface.cs | 28 +- CrowIDE/src/EditPane.cs | 10 +- CrowIDE/src/ImlVisualEditor.cs | 107 +- CrowIDE/src/MembersView.cs | 2 +- CrowIDE/src/Project.cs | 86 +- CrowIDE/src/ProjectNodes.cs | 49 +- CrowIDE/src/Solution.cs | 31 +- CrowIDE/src/SolutionControler.cs | 2 +- CrowIDE/src/SourceEditor/CSharpParser.cs | 45 + CrowIDE/src/SourceEditor/CodeBuffer.cs | 454 +++++++ .../src/SourceEditor/CodeBufferEventArgs.cs | 20 + CrowIDE/src/SourceEditor/CodeLine.cs | 76 ++ CrowIDE/src/SourceEditor/Node.cs | 32 + CrowIDE/src/SourceEditor/Parser.cs | 297 +++++ CrowIDE/src/SourceEditor/ParsingException.cs | 22 + CrowIDE/src/SourceEditor/SourceEditor.cs | 1107 +++++++++++++++++ CrowIDE/src/SourceEditor/TextFormatting.cs | 19 + CrowIDE/src/SourceEditor/Token.cs | 70 ++ CrowIDE/src/SourceEditor/XMLParser.cs | 299 +++++ CrowIDE/src/VerticalLine.cs | 2 +- CrowIDE/ui/CrowIDE.crow | 8 +- CrowIDE/ui/EditPane.template | 2 +- CrowIDE/ui/EditPaneItems.template | 29 +- CrowIDE/ui/GTreeExplorer.crow | 8 +- CrowIDE/ui/MemberView.crow | 2 +- CrowIDE/ui/ProjectProperties.crow | 2 +- CrowIDE/ui/ProjectTree.template | 2 +- Tests/InterfaceControler.cs | 1 + src/GraphicObjects/TabItem.cs | 2 +- src/Instantiator.cs | 2 +- src/Interface.cs | 24 +- src/StyleReader.cs | 10 +- 37 files changed, 2769 insertions(+), 198 deletions(-) create mode 100644 CrowIDE/src/SourceEditor/CSharpParser.cs create mode 100644 CrowIDE/src/SourceEditor/CodeBuffer.cs create mode 100644 CrowIDE/src/SourceEditor/CodeBufferEventArgs.cs create mode 100644 CrowIDE/src/SourceEditor/CodeLine.cs create mode 100644 CrowIDE/src/SourceEditor/Node.cs create mode 100644 CrowIDE/src/SourceEditor/Parser.cs create mode 100644 CrowIDE/src/SourceEditor/ParsingException.cs create mode 100644 CrowIDE/src/SourceEditor/SourceEditor.cs create mode 100644 CrowIDE/src/SourceEditor/TextFormatting.cs create mode 100644 CrowIDE/src/SourceEditor/Token.cs create mode 100644 CrowIDE/src/SourceEditor/XMLParser.cs diff --git a/Crow.Test/Crow.Test.csproj b/Crow.Test/Crow.Test.csproj index fb59dd1e..cf7b10a0 100644 --- a/Crow.Test/Crow.Test.csproj +++ b/Crow.Test/Crow.Test.csproj @@ -16,14 +16,12 @@ true full false - bin\Debug DEBUG; prompt 4 true - bin\Release prompt 4 diff --git a/Crow.Test/Test.cs b/Crow.Test/Test.cs index e21177f8..d4d97856 100644 --- a/Crow.Test/Test.cs +++ b/Crow.Test/Test.cs @@ -36,6 +36,7 @@ namespace UnitTest public void Init () { iface = new Interface (); + iface.Init (); iface.ProcessResize (bounds); } diff --git a/Crow.sln b/Crow.sln index 61aaa3af..4d3e4799 100644 --- a/Crow.sln +++ b/Crow.sln @@ -9,8 +9,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crow.Test", "Crow.Test\Crow EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrowIDE", "CrowIDE\CrowIDE.csproj", "{B6D911CD-1D09-42FC-B300-9187190F2AE1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crow.Coding", "..\gts\CrowEdit\Crow.Coding\Crow.Coding.csproj", "{78842EE4-8A2F-4C75-AEC6-C95F15AD3994}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -31,39 +29,27 @@ Global {C2980F9B-4798-4C05-99E2-E174810F7C7B}.Debug|Any CPU.Build.0 = Debug|Any CPU {C2980F9B-4798-4C05-99E2-E174810F7C7B}.Release|Any CPU.ActiveCfg = Release|Any CPU {C2980F9B-4798-4C05-99E2-E174810F7C7B}.Release|Any CPU.Build.0 = Release|Any CPU - {78842EE4-8A2F-4C75-AEC6-C95F15AD3994}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {78842EE4-8A2F-4C75-AEC6-C95F15AD3994}.Debug|Any CPU.Build.0 = Debug|Any CPU - {78842EE4-8A2F-4C75-AEC6-C95F15AD3994}.Release|Any CPU.ActiveCfg = Release|Any CPU - {78842EE4-8A2F-4C75-AEC6-C95F15AD3994}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution Policies = $0 $0.StandardHeader = $1 $1.Text = @\n${FileName}\n \nAuthor:\n ${AuthorName} <${AuthorEmail}>\n\nCopyright (c) 2013-2017 Jean-Philippe Bruyère\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the "Software"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE. + $1.IncludeInNewFiles = True $0.DotNetNamingPolicy = $2 + $2.DirectoryNamespaceAssociation = None + $2.ResourceNamePolicy = FileFormatDefault $0.TextStylePolicy = $3 + $3.FileWidth = 120 + $3.TabsToSpaces = False $3.EolMarker = Unix + $3.inheritsSet = VisualStudio + $3.inheritsScope = text/plain $3.scope = text/x-csharp $0.CSharpFormattingPolicy = $4 $4.AfterDelegateDeclarationParameterComma = True + $4.inheritsSet = Mono + $4.inheritsScope = text/x-csharp $4.scope = text/x-csharp - $4.IndentSwitchSection = False - $4.NewLinesForBracesInProperties = False - $4.NewLinesForBracesInAccessors = False - $4.NewLinesForBracesInAnonymousMethods = False - $4.NewLinesForBracesInControlBlocks = False - $4.NewLinesForBracesInAnonymousTypes = False - $4.NewLinesForBracesInObjectCollectionArrayInitializers = False - $4.NewLinesForBracesInLambdaExpressionBody = False - $4.NewLineForElse = False - $4.NewLineForCatch = False - $4.NewLineForFinally = False - $4.NewLineForMembersInObjectInit = False - $4.NewLineForMembersInAnonymousTypes = False - $4.NewLineForClausesInQuery = False - $4.SpacingAfterMethodDeclarationName = True - $4.SpaceAfterMethodCallName = True - $4.SpaceBeforeOpenSquareBracket = True $0.VersionControlPolicy = $5 $5.CommitMessageStyle = $6 $6.Indent = @\t @@ -71,6 +57,7 @@ Global $6.LastFilePostfix = "@:\n " $6.LineAlign = 0 $6.Wrap = False + $5.inheritsSet = Mono description = @C.R.O.W. c# Rapid Open Widgets\n\nCrow is a pure c# widget toolkit with XML definition of interface, bindings, styling...\n version = 0.5 EndGlobalSection diff --git a/CrowIDE/CrowIDE.csproj b/CrowIDE/CrowIDE.csproj index 10944136..1f6b3c8a 100644 --- a/CrowIDE/CrowIDE.csproj +++ b/CrowIDE/CrowIDE.csproj @@ -7,7 +7,7 @@ 2.0 {B6D911CD-1D09-42FC-B300-9187190F2AE1} Exe - CrowIDE + Crow.Coding CrowIDE v4.5 $(SolutionDir)build/$(Configuration) @@ -18,7 +18,7 @@ 0.5 - CrowIDE.CrowIDE + Crow.Coding.CrowIDE true @@ -28,8 +28,6 @@ prompt 4 false - $(SolutionDir)build\obj\$(Configuration) - $(SolutionDir)build\$(Configuration) none @@ -37,8 +35,6 @@ prompt 0 false - $(SolutionDir)build\obj\$(Configuration) - $(SolutionDir)build\$(Configuration) Program @@ -73,10 +69,6 @@ {C2980F9B-4798-4C05-99E2-E174810F7C7B} Crow - - {78842EE4-8A2F-4C75-AEC6-C95F15AD3994} - Crow.Coding - @@ -109,17 +101,29 @@ + + + + + + + + + + + + - CrowIDE.MembersView.template + Crow.Coding.MembersView.template @@ -133,7 +137,7 @@ - CrowIDE.EditPane.template + Crow.Coding.EditPane.template diff --git a/CrowIDE/src/CrowIDE.cs b/CrowIDE/src/CrowIDE.cs index 2e0f2901..eeedeb63 100644 --- a/CrowIDE/src/CrowIDE.cs +++ b/CrowIDE/src/CrowIDE.cs @@ -32,8 +32,9 @@ using Crow.IML; using System.Xml; using System.Linq; using Crow.Coding; +using System.Threading; -namespace CrowIDE +namespace Crow.Coding { class CrowIDE : CrowWindow { @@ -44,23 +45,23 @@ namespace CrowIDE CMDCompile; void initCommands () { - CMDNew = new Command(new Action(() => newFile())) { Caption = "New", Icon = new SvgPicture("#CrowIDE.ui.icons.blank-file.svg"), CanExecute = false}; - CMDOpen = new Command(new Action(() => openFileDialog())) { Caption = "Open...", Icon = new SvgPicture("#CrowIDE.ui.icons.outbox.svg")}; - CMDSave = new Command(new Action(() => saveFileDialog())) { Caption = "Save", Icon = new SvgPicture("#CrowIDE.ui.icons.inbox.svg"), CanExecute = false}; - CMDSaveAs = new Command(new Action(() => saveFileDialog())) { Caption = "Save As...", Icon = new SvgPicture("#CrowIDE.ui.icons.inbox.svg"), CanExecute = false}; - CMDQuit = new Command(new Action(() => Quit (null, null))) { Caption = "Quit", Icon = new SvgPicture("#CrowIDE.ui.icons.sign-out.svg")}; - CMDUndo = new Command(new Action(() => undo())) { Caption = "Undo", Icon = new SvgPicture("#CrowIDE.ui.icons.reply.svg"), CanExecute = false}; - CMDRedo = new Command(new Action(() => redo())) { Caption = "Redo", Icon = new SvgPicture("#CrowIDE.ui.icons.share-arrow.svg"), CanExecute = false}; - CMDCut = new Command(new Action(() => Quit (null, null))) { Caption = "Cut", Icon = new SvgPicture("#CrowIDE.ui.icons.scissors.svg"), CanExecute = false}; - CMDCopy = new Command(new Action(() => Quit (null, null))) { Caption = "Copy", Icon = new SvgPicture("#CrowIDE.ui.icons.copy-file.svg"), CanExecute = false}; - CMDPaste = new Command(new Action(() => Quit (null, null))) { Caption = "Paste", Icon = new SvgPicture("#CrowIDE.ui.icons.paste-on-document.svg"), CanExecute = false}; - CMDHelp = new Command(new Action(() => System.Diagnostics.Debug.WriteLine("help"))) { Caption = "Help", Icon = new SvgPicture("#CrowIDE.ui.icons.question.svg")}; - CMDOptions = new Command(new Action(() => openOptionsDialog())) { Caption = "Editor Options", Icon = new SvgPicture("#CrowIDE.ui.icons.tools.svg")}; - - CMDViewGTExp = new Command(new Action(() => loadWindow ("#CrowIDE.ui.GTreeExplorer.crow"))) { Caption = "Graphic Tree Explorer"}; - CMDViewProps = new Command(new Action(() => loadWindow ("#CrowIDE.ui.MemberView.crow"))) { Caption = "Properties View"}; + CMDNew = new Command(new Action(() => newFile())) { Caption = "New", Icon = new SvgPicture("#Crow.Coding.ui.icons.blank-file.svg"), CanExecute = false}; + CMDOpen = new Command(new Action(() => openFileDialog())) { Caption = "Open...", Icon = new SvgPicture("#Crow.Coding.ui.icons.outbox.svg")}; + CMDSave = new Command(new Action(() => saveFileDialog())) { Caption = "Save", Icon = new SvgPicture("#Crow.Coding.ui.icons.inbox.svg"), CanExecute = false}; + CMDSaveAs = new Command(new Action(() => saveFileDialog())) { Caption = "Save As...", Icon = new SvgPicture("#Crow.Coding.ui.icons.inbox.svg"), CanExecute = false}; + CMDQuit = new Command(new Action(() => Quit (null, null))) { Caption = "Quit", Icon = new SvgPicture("#Crow.Coding.ui.icons.sign-out.svg")}; + CMDUndo = new Command(new Action(() => undo())) { Caption = "Undo", Icon = new SvgPicture("#Crow.Coding.ui.icons.reply.svg"), CanExecute = false}; + CMDRedo = new Command(new Action(() => redo())) { Caption = "Redo", Icon = new SvgPicture("#Crow.Coding.ui.icons.share-arrow.svg"), CanExecute = false}; + CMDCut = new Command(new Action(() => Quit (null, null))) { Caption = "Cut", Icon = new SvgPicture("#Crow.Coding.ui.icons.scissors.svg"), CanExecute = false}; + CMDCopy = new Command(new Action(() => Quit (null, null))) { Caption = "Copy", Icon = new SvgPicture("#Crow.Coding.ui.icons.copy-file.svg"), CanExecute = false}; + CMDPaste = new Command(new Action(() => Quit (null, null))) { Caption = "Paste", Icon = new SvgPicture("#Crow.Coding.ui.icons.paste-on-document.svg"), CanExecute = false}; + CMDHelp = new Command(new Action(() => System.Diagnostics.Debug.WriteLine("help"))) { Caption = "Help", Icon = new SvgPicture("#Crow.Coding.ui.icons.question.svg")}; + CMDOptions = new Command(new Action(() => openOptionsDialog())) { Caption = "Editor Options", Icon = new SvgPicture("#Crow.Coding.ui.icons.tools.svg")}; + + CMDViewGTExp = new Command(new Action(() => loadWindow ("#Crow.Coding.ui.GTreeExplorer.crow"))) { Caption = "Graphic Tree Explorer"}; + CMDViewProps = new Command(new Action(() => loadWindow ("#Crow.Coding.ui.MemberView.crow"))) { Caption = "Properties View"}; CMDCompile = new Command(new Action(() => compileSolution())) { Caption = "Compile"}; - CMDViewProj = new Command(new Action(() => loadWindow ("#CrowIDE.ui.CSProjExplorer.crow"))) { Caption = "Project Explorer"}; + CMDViewProj = new Command(new Action(() => loadWindow ("#Crow.Coding.ui.CSProjExplorer.crow"))) { Caption = "Project Explorer"}; CMDViewProjProps = new Command(new Action(loadProjProps) ){ Caption = "Project Properties"}; } @@ -85,10 +86,8 @@ namespace CrowIDE : base(1024, 800,"UIEditor") { } - ImlVisualEditor imlVE; Instantiator instFileDlg; - Solution currentSolution; protected override void OnLoad (EventArgs e) @@ -102,10 +101,9 @@ namespace CrowIDE this.KeyDown += CrowIDE_KeyDown; - //this.CrowInterface.LoadInterface ("#CrowIDE.ui.imlEditor.crow").DataSource = this; + //this.CrowInterface.LoadInterface ("#Crow.Coding.ui.imlEditor.crow").DataSource = this; //GraphicObject go = this.CrowInterface.LoadInterface (@"ui/test.crow"); - GraphicObject go = AddWidget (@"#CrowIDE.ui.CrowIDE.crow"); - imlVE = go.FindByName ("crowContainer") as ImlVisualEditor; + GraphicObject go = AddWidget (@"#Crow.Coding.ui.CrowIDE.crow"); if (ReopenLastSolution && !string.IsNullOrEmpty(LastOpenSolution)) CurrentSolution = Solution.LoadSolution (LastOpenSolution); @@ -114,7 +112,7 @@ namespace CrowIDE } void loadProjProps () { - //loadWindow ("#CrowIDE.ui.ProjectProperties.crow", currentProject); + //loadWindow ("#Crow.Coding.ui.ProjectProperties.crow", currentProject); } void compileSolution () { //ProjectItem pi = CurrentSolution.SelectedItem; @@ -190,9 +188,9 @@ namespace CrowIDE } }// else if (e.Key == OpenTK.Input.Key.F6) { -// loadWindow ("#CrowIDE.ui.LQIsExplorer.crow"); +// loadWindow ("#Crow.Coding.ui.LQIsExplorer.crow"); // } else if (e.Key == OpenTK.Input.Key.F7) { -// loadWindow ("#CrowIDE.ui.CSProjExplorer.crow"); +// loadWindow ("#Crow.Coding.ui.CSProjExplorer.crow"); // } } void loadWindow(string path, object dataSource = null){ @@ -202,10 +200,7 @@ namespace CrowIDE return; g = CurrentInterface.AddWidget (path); g.Name = path; - if (dataSource == null) - g.DataSource = imlVE; - else - g.DataSource = dataSource; + g.DataSource = dataSource; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine (ex.ToString ()); } diff --git a/CrowIDE/src/DesignInterface.cs b/CrowIDE/src/DesignInterface.cs index 10ceffc5..0ac3e3ba 100644 --- a/CrowIDE/src/DesignInterface.cs +++ b/CrowIDE/src/DesignInterface.cs @@ -27,7 +27,7 @@ using System; using Crow; using System.Globalization; -namespace CrowIDE +namespace Crow.Coding { public class DesignInterface : Interface, IValueChange { @@ -48,17 +48,35 @@ namespace CrowIDE ValueChanged.Raise(this, new ValueChangeEventArgs(MemberName, _value)); } #endregion + public DesignInterface () { - CurrentInterface = this; - CultureInfo.DefaultThreadCurrentCulture = System.Globalization.CultureInfo.InvariantCulture; } - protected override void loadStyling () + public ProjectFile ProjFile; + + public override GraphicObject Load (string path) { - base.loadStyling (); + ProjectFile pi; + if (ProjFile.Project.solution.GetProjectFileFromPath (path, out pi)) + return CreateITorFromIMLFragment (pi.Source).CreateInstance(); + return null; + } + public override Crow.IML.Instantiator GetInstantiator (string path) + { + System.Diagnostics.Debugger.Break (); + return null; } + public override ItemTemplate GetItemTemplate (string path) + { + ProjectFile pi; + + if (ProjFile.Project.solution.GetProjectFileFromPath (path, out pi)) + return new ItemTemplate (this, pi.AbsolutePath); + System.Diagnostics.Debugger.Break (); + return null; + } public override bool ProcessMouseMove (int x, int y) { int deltaX = x - Mouse.X; diff --git a/CrowIDE/src/EditPane.cs b/CrowIDE/src/EditPane.cs index a1103909..d7ddba5a 100644 --- a/CrowIDE/src/EditPane.cs +++ b/CrowIDE/src/EditPane.cs @@ -27,20 +27,14 @@ using System; using Crow; using System.Linq; -namespace CrowIDE +namespace Crow.Coding { public class EditPane : TemplatedGroup { public EditPane () : base() { } - public override object SelectedItem { - get { return base.SelectedItem; } - set { - base.SelectedItem = value; - //Items.FirstOrDefault (i=>i.DataSource == value). - } - } + object selectedItemElement = null; public object SelectedItemElement { diff --git a/CrowIDE/src/ImlVisualEditor.cs b/CrowIDE/src/ImlVisualEditor.cs index 9e7aae39..74187b2e 100644 --- a/CrowIDE/src/ImlVisualEditor.cs +++ b/CrowIDE/src/ImlVisualEditor.cs @@ -27,7 +27,7 @@ using System.IO; using System.Collections.Generic; using Crow.IML; -namespace CrowIDE +namespace Crow.Coding { public class ImlVisualEditor : GraphicObject { @@ -42,15 +42,13 @@ namespace CrowIDE #endregion DesignInterface imlVE; - Instantiator itor; GraphicObject selectedItem; - string imlSource; - ProjectItem projectItem; + ImlProjectItem projectItem; + Exception imlError = null; bool drawGrid; int gridSpacing; - [XmlAttributeAttribute][DefaultValue(true)] public bool DrawGrid { get { return drawGrid; } @@ -97,23 +95,6 @@ namespace CrowIDE [XmlIgnore]public List LQIs { get { return imlVE.LQIs; } } - [XmlIgnore]public string ImlSource { - get { return imlSource; } - set { - if (imlSource == value) - return; - imlSource = value; - - NotifyValueChanged ("ImlSource", ImlSource); - - reloadFromSource (); - } - } - [XmlAttributeAttribute][DefaultValue("")] - public string ImlPath { - get { return projectItem?.Path; } - } - [XmlAttributeAttribute] public ProjectNode ProjectNode { @@ -122,62 +103,56 @@ namespace CrowIDE if (projectItem == value) return; - if (!(value is ProjectItem)) - return; - projectItem = value as ProjectItem; + if (projectItem != null) + projectItem.ValueChanged -= ProjectItem_ValueChanged; + + projectItem = value as ImlProjectItem; - NotifyValueChanged ("ImlPath", ImlPath); + if (projectItem != null) + projectItem.ValueChanged += ProjectItem_ValueChanged; - reloadFromPath (); + NotifyValueChanged ("ProjectNode", projectItem); + + reload (); } } - public List GraphicTree { - get { return imlVE.GraphicTree; } + + void ProjectItem_ValueChanged (object sender, ValueChangeEventArgs e) + { + if (e.MemberName == "Source") + reload (); } - void reloadFromSource(){ - if (string.IsNullOrEmpty (imlSource)) { - reload_iTor (null); - return; + [XmlIgnore]public Exception IMLError { + get { return imlError; } + set { + if (imlError == value) + return; + imlError = value; + NotifyValueChanged ("IMLError", imlError); } + } - Instantiator iTmp; + void reload(){ + if (projectItem == null) + return; + try { - iTmp = Instantiator.CreateFromImlFragment (imlVE, imlSource); + imlVE.ProjFile = projectItem; + imlVE.Styling = projectItem.Project.solution.Styling; + imlVE.DefaultTemplates = projectItem.Project.solution.DefaultTemplates; + imlVE.Instantiators = new Dictionary(); + imlVE.ClearInterface(); + imlVE.LoadIMLFragment(projectItem.Source); + IMLError = null; } catch (Exception ex) { - System.Diagnostics.Debug.WriteLine (ex.ToString()); - return; - } - - reload_iTor (iTmp); - } - void reloadFromPath(){ - string path = Path.Combine (projectItem?.Project.RootDir,projectItem?.Path); - if (!File.Exists (path)){ - System.Diagnostics.Debug.WriteLine ("Path not found: " + path); - reload_iTor (null); - return; - } - using (StreamReader sr = new StreamReader (path)) { - ImlSource = sr.ReadToEnd (); + IMLError = ex; } - NotifyValueChanged ("GraphicTree", null); - NotifyValueChanged ("GraphicTree", GraphicTree); - SelectedItem = null; } - void reload_iTor(Instantiator new_iTot){ - itor = new_iTot; - lock (imlVE.UpdateMutex) { - try { - imlVE.ClearInterface (); - if (itor != null) - imlVE.AddWidget(itor.CreateInstance()); - } catch (Exception ex) { - System.Diagnostics.Debug.WriteLine (ex.ToString()); - } - } + public List GraphicTree { + get { return imlVE.GraphicTree; } } void interfaceThread() @@ -202,7 +177,7 @@ namespace CrowIDE RegisterForRedraw (); } - Thread.Sleep (2); + Thread.Sleep (10); } } @@ -282,7 +257,7 @@ namespace CrowIDE gr.Rectangle (hr, 1.0); } - if (SelectedItem == null) + if (SelectedItem?.Parent == null) return; hr = SelectedItem.ScreenCoordinates(SelectedItem.getSlot ()); hr.Inflate (1); diff --git a/CrowIDE/src/MembersView.cs b/CrowIDE/src/MembersView.cs index 00d3023e..48b3e074 100644 --- a/CrowIDE/src/MembersView.cs +++ b/CrowIDE/src/MembersView.cs @@ -26,7 +26,7 @@ using System.Reflection; using System.Collections.Generic; using System.Linq; -namespace CrowIDE +namespace Crow.Coding { public class PropertyContainer : IValueChange { diff --git a/CrowIDE/src/Project.cs b/CrowIDE/src/Project.cs index 40f14f80..098cfdd2 100644 --- a/CrowIDE/src/Project.cs +++ b/CrowIDE/src/Project.cs @@ -33,7 +33,7 @@ using System.CodeDom.Compiler; using Crow; using System.Text.RegularExpressions; -namespace CrowIDE +namespace Crow.Coding { public class Project: IValueChange { @@ -270,9 +270,7 @@ namespace CrowIDE } return tmp; } - public string Compile () { - GetStyling (); - + public string Compile () { if (ParentProject != null) ParentProject.Compile (); @@ -378,13 +376,83 @@ namespace CrowIDE solution.UpdateErrorList (); return parameters.OutputAssembly; - } + } + + public bool GetProjectFileFromPath (string path, out ProjectFile pi){ + if (path.StartsWith ("#")) + pi = flattenNodes.OfType ().FirstOrDefault + (pp => pp.Type == ItemType.EmbeddedResource && pp.ResourceID == path.Substring (1)); + else + pi = flattenNodes.OfType ().FirstOrDefault (pp => pp.Path == path); + + if (pi != null) + return true; + + foreach (ProjectReference pr in flattenNodes.OfType()) { + Project p = solution.Projects.FirstOrDefault (pp => pp.ProjectGuid == pr.ProjectGUID); + if (p == null) + throw new Exception ("referenced project not found"); + if (p.GetProjectFileFromPath (path, out pi)) + return true; + } + //TODO: search referenced assemblies + return false; + } + + public void GetDefaultTemplates () { + IEnumerable tmpFiles = + flattenNodes.OfType ().Where (pp => pp.Extension == ".template" ); + + foreach (ProjectFile pi in tmpFiles.Where( + pp=>pp.Type == ItemType.None && pp.CopyToOutputDirectory != CopyToOutputState.Never)) { + + string clsName = System.IO.Path.GetFileNameWithoutExtension(pi.Path); + if (solution.DefaultTemplates.ContainsKey (clsName)) + continue; + solution.DefaultTemplates [clsName] = pi.AbsolutePath; + } + foreach (ProjectFile pi in tmpFiles.Where(pp=>pp.Type == ItemType.EmbeddedResource)) { + string resId = pi.ResourceID; + string clsName = resId.Substring (0, resId.Length - 9); + if (solution.DefaultTemplates.ContainsKey (clsName)) + continue; + solution.DefaultTemplates [clsName] = pi.Path; + } + + foreach (ProjectReference pr in flattenNodes.OfType()) { + Project p = solution.Projects.FirstOrDefault (pp => pp.ProjectGuid == pr.ProjectGUID); + if (p == null) + throw new Exception ("referenced project not found"); + p.GetDefaultTemplates (); + } + } +// void searchTemplatesIn(Assembly assembly){ +// if (assembly == null) +// return; +// foreach (string resId in assembly +// .GetManifestResourceNames () +// .Where (r => r.EndsWith (".template", StringComparison.OrdinalIgnoreCase))) { +// string clsName = resId.Substring (0, resId.Length - 9); +// if (DefaultTemplates.ContainsKey (clsName)) +// continue; +// DefaultTemplates[clsName] = "#" + resId; +// } +// } - public void GetStyling () { - foreach (ProjectFile pi in flattenNodes.OfType ().Where (pp=>pp.Type == ItemType.EmbeddedResource)) { - Console.WriteLine (pi.Extension); - } + foreach (ProjectFile pi in flattenNodes.OfType ().Where (pp=>pp.Type == ItemType.EmbeddedResource && pp.Extension == ".style")) { + using (Stream s = new MemoryStream (System.Text.Encoding.UTF8.GetBytes(pi.Source))) { + new StyleReader (solution.Styling, s, pi.ResourceID); + } + } + foreach (ProjectReference pr in flattenNodes.OfType()) { + Project p = solution.Projects.FirstOrDefault (pp => pp.ProjectGuid == pr.ProjectGUID); + if (p == null) + throw new Exception ("referenced project not found"); + p.GetStyling(); + } + + //TODO:get styling from referenced assemblies } } } diff --git a/CrowIDE/src/ProjectNodes.cs b/CrowIDE/src/ProjectNodes.cs index bdabbdd8..ac2602e3 100644 --- a/CrowIDE/src/ProjectNodes.cs +++ b/CrowIDE/src/ProjectNodes.cs @@ -30,7 +30,7 @@ using System.Xml; using System.IO; using Crow; -namespace CrowIDE +namespace Crow.Coding { public enum ItemType { ReferenceGroup, @@ -144,11 +144,24 @@ namespace CrowIDE } public class ProjectFile : ProjectItem { bool isDirty = false; + bool isOpened = false; +// bool isSelected = false; + DateTime accessTime; + string source; object selectedItem; + public ProjectFile (ProjectItem pi) : base (pi.Project, pi.node){ } + public string ResourceID { + get { + return Type != ItemType.EmbeddedResource ? null : + node.SelectSingleNode ("LogicalName") == null ? + Project.Name + "." + Path.Replace ('/', '.') : + LogicalName; + } + } public string LogicalName { get { return node.SelectSingleNode ("LogicalName")?.InnerText; @@ -156,11 +169,39 @@ namespace CrowIDE } public string Source { get { - using (StreamReader sr = new StreamReader (AbsolutePath)) { - return sr.ReadToEnd (); - } + if (!isOpened) { + accessTime = System.IO.File.GetLastWriteTime (AbsolutePath); + using (StreamReader sr = new StreamReader (AbsolutePath)) { + source = sr.ReadToEnd (); + } + isOpened = true; + isDirty = false; + } else { + if (DateTime.Compare ( + accessTime, + System.IO.File.GetLastWriteTime (AbsolutePath)) < 0) + Console.WriteLine ("File has been modified outside CrowIDE"); + } + return source; + } + set { + if (source == value) + return; + source = value; + NotifyValueChanged ("Source", source); } } + +// public bool IsSelected { +// get { return isSelected; } +// set { +// if (isSelected == value) +// return; +// isSelected = value; +// NotifyValueChanged ("IsSelected", isSelected); +// } +// } + public object SelectedItem { get { return selectedItem; } set { diff --git a/CrowIDE/src/Solution.cs b/CrowIDE/src/Solution.cs index c971b847..1c1bac70 100644 --- a/CrowIDE/src/Solution.cs +++ b/CrowIDE/src/Solution.cs @@ -21,7 +21,7 @@ using System.Text.RegularExpressions; using System.Xml.Serialization; using Crow; -namespace CrowIDE{ +namespace Crow.Coding{ public class SolutionProject { public string ProjectHostGuid; public string ProjectName; @@ -45,6 +45,29 @@ namespace CrowIDE{ object selectedItemElement = null; ObservableList openedItems = new ObservableList(); + public Dictionary Styling; + public Dictionary DefaultTemplates; + + //TODO: check project dependencies if no startup proj + + public void ReloadStyling () { + Styling = new Dictionary (); + if (StartupProject != null) + StartupProject.GetStyling (); + } + + public void ReloadDefaultTemplates () { + DefaultTemplates = new Dictionary(); + if (StartupProject != null) + StartupProject.GetDefaultTemplates (); + } + public bool GetProjectFileFromPath (string path, out ProjectFile pi){ + pi = null; + return StartupProject == null ? false : + StartupProject.GetProjectFileFromPath (path, out pi); + } + + public ObservableList OpenedItems { get { return openedItems; } set { @@ -209,7 +232,8 @@ namespace CrowIDE{ value.NotifyValueChanged("IsStartupProject", true); } NotifyValueChanged ("StartupProject", StartupProject); - + ReloadStyling (); + ReloadDefaultTemplates (); } } @@ -384,7 +408,8 @@ namespace CrowIDE{ )); s.UserConfig = new Configuration (s.path + ".user"); - + s.ReloadStyling (); + s.ReloadDefaultTemplates (); return s; } //LoadSolution #endregion diff --git a/CrowIDE/src/SolutionControler.cs b/CrowIDE/src/SolutionControler.cs index dec1a8d2..edb6b0dd 100644 --- a/CrowIDE/src/SolutionControler.cs +++ b/CrowIDE/src/SolutionControler.cs @@ -25,7 +25,7 @@ // THE SOFTWARE. using System; -namespace CrowIDE +namespace Crow.Coding { public class SolutionControler { diff --git a/CrowIDE/src/SourceEditor/CSharpParser.cs b/CrowIDE/src/SourceEditor/CSharpParser.cs new file mode 100644 index 00000000..7d2e28e5 --- /dev/null +++ b/CrowIDE/src/SourceEditor/CSharpParser.cs @@ -0,0 +1,45 @@ +using System; +using Crow; + +namespace Crow.Coding +{ + public class CSharpParser : Parser + { + public new enum TokenType { + Unknown = Parser.TokenType.Unknown, + WhiteSpace = Parser.TokenType.WhiteSpace, + LineComment = Parser.TokenType.LineComment, + BlockComment = Parser.TokenType.BlockComment, + OpenParenth, + CloseParenth, + OpenBlock, + CloseBlock, + StatementEnding, + UnaryOp, + BinaryOp, + Affectation, + StringLiteral, + CharacterLiteral, + DigitalLiteral, + Literal, + Identifier, + Indexer, + Type, + Preprocessor, + } + + public CSharpParser (CodeBuffer _buffer) : base(_buffer) + { + } + + public override void ParseCurrentLine () + { + throw new NotImplementedException (); + } + public override void SyntaxAnalysis () + { + throw new NotImplementedException (); + } + } +} + diff --git a/CrowIDE/src/SourceEditor/CodeBuffer.cs b/CrowIDE/src/SourceEditor/CodeBuffer.cs new file mode 100644 index 00000000..399d10ab --- /dev/null +++ b/CrowIDE/src/SourceEditor/CodeBuffer.cs @@ -0,0 +1,454 @@ +// +// CodeTextBuffer.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2017 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Diagnostics; + +namespace Crow.Coding +{ + /// + /// Code buffer, lines are arranged in a List, new line chars are removed during string.split on '\n...', + /// + public class CodeBuffer + { + public object EditMutex = new object(); + //those events are handled in SourceEditor to help keeping sync between textbuffer,parser and editor. + //modified lines are marked for reparse + #region Events + public event EventHandler LineUpadateEvent; + public event EventHandler LineRemoveEvent; + public event EventHandler LineAdditionEvent; + public event EventHandler FoldingEvent; + public event EventHandler BufferCleared; + public event EventHandler SelectionChanged; + public event EventHandler PositionChanged; + #endregion + + #region CTOR + public CodeBuffer () { + + } + #endregion + + string lineBreak = Interface.LineBreak; + List lines = new List(); + public int longestLineIdx = 0; + public int longestLineCharCount = 0; + /// + /// real position in char arrays, tab = 1 char + /// + int _currentLine = 0; + int _currentCol = 0; + + public int LineCount { get { return lines.Count;}} + public int IndexOf (CodeLine cl) { + return lines.IndexOf (cl); + } + + public CodeLine this[int i] + { + get { return lines[i]; } + set { + if (lines [i] == value) + return; + lock (EditMutex) { + lines [i] = value; + LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); + } + } + } + + public void RemoveAt(int i){ + lock (EditMutex) { + lines.RemoveAt (i); + LineRemoveEvent.Raise (this, new CodeBufferEventArgs (i)); + } + } + public void Insert(int i, string item){ + lock (EditMutex) { + lines.Insert (i, item); + LineAdditionEvent.Raise (this, new CodeBufferEventArgs (i)); + } + } + public void Add(CodeLine item){ + lock (EditMutex) { + lines.Add (item); + LineAdditionEvent.Raise (this, new CodeBufferEventArgs (lines.Count - 1)); + } + } + public void AddRange (string[] items){ + int start = lines.Count; + lock (EditMutex) { + for (int i = 0; i < items.Length; i++) + lines.Add (items [i]); + LineAdditionEvent.Raise (this, new CodeBufferEventArgs (start, items.Length)); + } + } + public void AddRange (CodeLine[] items){ + int start = lines.Count; + lock (EditMutex) { + lines.AddRange (items); + LineAdditionEvent.Raise (this, new CodeBufferEventArgs (start, items.Length)); + } + } + public void Clear () { + lock (EditMutex) { + longestLineCharCount = 0; + lines.Clear (); + BufferCleared.Raise (this, null); + } + } + public void UpdateLine(int i, string newContent){ + lock (EditMutex) { + this [i].Content = newContent; + LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); + } + } + public void AppenedLine(int i, string newContent){ + lock (EditMutex) { + this [i].Content += newContent; + LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); + } + } + public void ToogleFolding (int line) { + if (!this [line].IsFoldable) + return; + lock (EditMutex) { + this [line].IsFolded = !this [line].IsFolded; + FoldingEvent.Raise (this, new CodeBufferEventArgs (line)); + } + } + public void Load(string rawSource) { + this.Clear(); + + if (string.IsNullOrEmpty (rawSource)) + return; + + AddRange (Regex.Split (rawSource, "\r\n|\r|\n|\\\\n")); + + lineBreak = detectLineBreakKind (rawSource); + } + + /// + /// Finds the longest visual line as printed on screen with tabulation replaced with n spaces + /// + public void FindLongestVisualLine(){ + longestLineCharCount = 0; + for (int i = 0; i < this.LineCount; i++) { + if (lines[i].PrintableLength > longestLineCharCount) { + longestLineCharCount = lines[i].PrintableLength; + longestLineIdx = i; + } + } + Debug.WriteLine ("Longest line: {0}->{1}", longestLineIdx, longestLineCharCount); + } + /// line break could be '\r' or '\n' or '\r\n' + static string detectLineBreakKind(string buffer){ + string strLB = ""; + + if (string.IsNullOrEmpty(buffer)) + return Interface.LineBreak; + int i = 0; + while ( i < buffer.Length) { + if (buffer [i] == '\r') { + strLB += '\r'; + i++; + } + if (i < buffer.Length) { + if (buffer [i] == '\r') + return "\r"; + if (buffer[i] == '\n') + strLB += '\n'; + } + if (!string.IsNullOrEmpty (strLB)) + return strLB; + i++; + } + return Interface.LineBreak; + } + /// + /// return all lines with linebreaks + /// + public string FullText{ + get { + if (lines.Count == 0) + return ""; + string tmp = ""; + for (int i = 0; i < lines.Count -1; i++) + tmp += lines [i].Content + this.lineBreak; + tmp += lines [lines.Count - 1].Content; + return tmp; + } + } + + /// + /// unfolded and not in folds line count + /// + public int UnfoldedLines { + get { + int i = 0, vl = 0; + while (i < LineCount) { + if (this [i].IsFolded) + i = GetEndNodeIndex (i); + i++; + vl++; + } + //Debug.WriteLine ("unfolded lines: " + vl); + return vl; + } + } + + /// + /// convert visual position to buffer position + /// + Point getBuffPos (Point visualPos) { + int i = 0; + int buffCol = 0; + while (i < visualPos.X) { + if (this [visualPos.Y] [buffCol] == '\t') + i += Interface.TabSize; + else + i++; + buffCol++; + } + return new Point (buffCol, visualPos.Y); + } + + public int GetEndNodeIndex (int line) { + return IndexOf (this [line].SyntacticNode.EndLine); + } + /// + /// Gets visual position computed from actual buffer position + /// +// public Point TabulatedPosition { +// get { return new Point (TabulatedColumn, _currentLine); } +// } + /// + /// set buffer current position from visual position + /// +// public void SetBufferPos(Point tabulatedPosition) { +// CurrentPosition = getBuffPos(tabulatedPosition); +// } + + #region Editing and moving cursor + Point selStartPos = -1; //selection start (row,column) + Point selEndPos = -1; //selection end (row,column) + + public bool SelectionInProgress { get { return selStartPos >= 0; }} + public void SetSelStartPos () { + selStartPos = selEndPos = CurrentPosition; + SelectionChanged.Raise (this, null); + } + public void SetSelEndPos () { + selEndPos = CurrentPosition; + SelectionChanged.Raise (this, null); + } + /// + /// Set selection in buffer to -1, empty selection + /// + public void ResetSelection () { + selStartPos = selEndPos = -1; + SelectionChanged.Raise (this, null); + } + + public string SelectedText { + get { + if (SelectionIsEmpty) + return ""; + Point selStart = SelectionStart; + Point selEnd = SelectionEnd; + if (selStart.Y == selEnd.Y) + return this [selStart.Y].Content.Substring (selStart.X, selEnd.X - selStart.X); + string tmp = ""; + tmp = this [selStart.Y].Content.Substring (selStart.X); + for (int l = selStart.Y + 1; l < selEnd.Y; l++) { + tmp += Interface.LineBreak + this [l].Content; + } + tmp += Interface.LineBreak + this [selEnd.Y].Content.Substring (0, selEnd.X); + return tmp; + } + } + /// + /// ordered selection start and end positions in char units + /// + public Point SelectionStart { + get { return selEndPos < 0 || selStartPos.Y < selEndPos.Y ? selStartPos : + selStartPos.Y > selEndPos.Y ? selEndPos : + selStartPos.X < selEndPos.X ? selStartPos : selEndPos; } + } + public Point SelectionEnd { + get { return selEndPos < 0 || selStartPos.Y > selEndPos.Y ? selStartPos : + selStartPos.Y < selEndPos.Y ? selEndPos : + selStartPos.X > selEndPos.X ? selStartPos : selEndPos; } + } + public bool SelectionIsEmpty + { get { return selEndPos == selStartPos; } } + /// + /// Current column in buffer coordinate, tabulation = 1 char + /// + public int CurrentColumn{ + get { return _currentCol; } + set { + if (value == _currentCol) + return; + if (value < 0) + _currentCol = 0; + else if (value > lines [_currentLine].Length) + _currentCol = lines [_currentLine].Length; + else + _currentCol = value; + + PositionChanged.Raise (this, null); + } + } + /// + /// Current row in buffer coordinate, tabulation = 1 char + /// + public int CurrentLine{ + get { return _currentLine; } + set { + if (value == _currentLine) + return; + if (value >= lines.Count) + _currentLine = lines.Count-1; + else if (value < 0) + _currentLine = 0; + else + _currentLine = value; + + if (_currentCol > lines [_currentLine].Length) + _currentCol = lines [_currentLine].Length; + Debug.WriteLine ("buff cur line: " + _currentLine); + PositionChanged.Raise (this, null); + } + } + public CodeLine CurrentCodeLine { + get { return this [_currentLine]; } + } + /// + /// Current position in buffer coordinate, tabulation = 1 char + /// + public Point CurrentPosition { + get { return new Point(CurrentColumn, CurrentLine); } +// set { +// _currentCol = value.X; +// _currentLine = value.Y; +// } + } + /// + /// get char at current position in buffer + /// + protected Char CurrentChar { get { return lines [CurrentLine] [CurrentColumn]; } } + + public void GotoWordStart(){ + if (this[CurrentLine].Length == 0) + return; + CurrentColumn--; + //skip white spaces + while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0) + CurrentColumn--; + while (char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0) + CurrentColumn--; + if (!char.IsLetterOrDigit (this.CurrentChar)) + CurrentColumn++; + } + public void GotoWordEnd(){ + //skip white spaces + if (CurrentColumn >= this [CurrentLine].Length - 1) + return; + while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < this [CurrentLine].Length-1) + CurrentColumn++; + while (char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < this [CurrentLine].Length-1) + CurrentColumn++; + if (char.IsLetterOrDigit (this.CurrentChar)) + CurrentColumn++; + } + public void DeleteChar() + { + lock (EditMutex) { + if (SelectionIsEmpty) { + if (CurrentColumn == 0) { + if (CurrentLine == 0) + return; + CurrentLine--; + CurrentColumn = this [CurrentLine].Length; + AppenedLine (CurrentLine, this [CurrentLine + 1].Content); + RemoveAt (CurrentLine + 1); + return; + } + CurrentColumn--; + UpdateLine (CurrentLine, this [CurrentLine].Content.Remove (CurrentColumn, 1)); + } else { + int linesToRemove = SelectionEnd.Y - SelectionStart.Y + 1; + int l = SelectionStart.Y; + + if (linesToRemove > 0) { + UpdateLine (l, this [l].Content.Remove (SelectionStart.X, this [l].Length - SelectionStart.X) + + this [SelectionEnd.Y].Content.Substring (SelectionEnd.X, this [SelectionEnd.Y].Length - SelectionEnd.X)); + l++; + for (int c = 0; c < linesToRemove - 1; c++) + RemoveAt (l); + CurrentLine = SelectionStart.Y; + CurrentColumn = SelectionStart.X; + } else + UpdateLine (l, this [l].Content.Remove (SelectionStart.X, SelectionEnd.X - SelectionStart.X)); + CurrentColumn = SelectionStart.X; + ResetSelection (); + } + } + } + /// + /// Insert new string at caret position, should be sure no line break is inside. + /// + /// String. + public void Insert(string str) + { + if (!SelectionIsEmpty) + this.DeleteChar (); + string[] strLines = Regex.Split (str, "\r\n|\r|\n|" + @"\\n").ToArray(); + UpdateLine (CurrentLine, this [CurrentLine].Content.Insert (CurrentColumn, strLines[0])); + CurrentColumn += strLines[0].Length; + for (int i = 1; i < strLines.Length; i++) { + InsertLineBreak (); + UpdateLine (CurrentLine, this [CurrentLine].Content.Insert (CurrentColumn, strLines[i])); + CurrentColumn += strLines[i].Length; + } + } + /// + /// Insert a line break. + /// + public void InsertLineBreak() + { + if (CurrentColumn > 0) { + Insert (CurrentLine + 1, this [CurrentLine].Content.Substring (CurrentColumn)); + UpdateLine (CurrentLine, this [CurrentLine].Content.Substring (0, CurrentColumn)); + } else + Insert(CurrentLine, ""); + + CurrentColumn = 0; + CurrentLine++; + } + #endregion + } +} + diff --git a/CrowIDE/src/SourceEditor/CodeBufferEventArgs.cs b/CrowIDE/src/SourceEditor/CodeBufferEventArgs.cs new file mode 100644 index 00000000..07dd25b6 --- /dev/null +++ b/CrowIDE/src/SourceEditor/CodeBufferEventArgs.cs @@ -0,0 +1,20 @@ +using System; + +namespace Crow.Coding +{ + public class CodeBufferEventArgs : EventArgs { + public int LineStart; + public int LineCount; + + public CodeBufferEventArgs(int lineNumber) { + LineStart = lineNumber; + LineCount = 1; + } + public CodeBufferEventArgs(int lineStart, int lineCount) { + LineStart = lineStart; + LineCount = lineCount; + } + } + +} + diff --git a/CrowIDE/src/SourceEditor/CodeLine.cs b/CrowIDE/src/SourceEditor/CodeLine.cs new file mode 100644 index 00000000..622584c7 --- /dev/null +++ b/CrowIDE/src/SourceEditor/CodeLine.cs @@ -0,0 +1,76 @@ +using System; +using System.Text; +using System.Collections.Generic; + +namespace Crow.Coding +{ + public class CodeLine + { + public string Content; + public List Tokens; + public int EndingState = 0; + public Node SyntacticNode; + public ParsingException exception; + + public CodeLine (string _content){ + Content = _content; + Tokens = null; + exception = null; + } + + public char this[int i] + { + get { return Content[i]; } + set { + if (Content [i] == value) + return; + StringBuilder sb = new StringBuilder(Content); + sb[i] = value; + Content = sb.ToString(); + Tokens = null; + //LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); + } + } + public bool IsFoldable { get { return SyntacticNode != null; } } + public bool IsFolded = false; + public bool IsParsed { + get { return Tokens != null; } + } + public string PrintableContent { + get { + return string.IsNullOrEmpty (Content) ? "" : Content.Replace ("\t", new String (' ', Interface.TabSize)); + } + } + public int PrintableLength { + get { + return PrintableContent.Length; + } + } + public int Length { + get { + return string.IsNullOrEmpty (Content) ? 0 : Content.Length; + } + } + + public void SetLineInError (ParsingException ex) { + Tokens = null; + exception = ex; + } + +// public static implicit operator string(CodeLine sl) { +// return sl == null ? "" : sl.Content; +// } + public static implicit operator CodeLine(string s) { + return new CodeLine(s); + } + public static bool operator ==(string s1, CodeLine s2) + { + return string.Equals (s1, s2.Content); + } + public static bool operator !=(string s1, CodeLine s2) + { + return !string.Equals (s1, s2.Content); + } + } +} + diff --git a/CrowIDE/src/SourceEditor/Node.cs b/CrowIDE/src/SourceEditor/Node.cs new file mode 100644 index 00000000..24818e01 --- /dev/null +++ b/CrowIDE/src/SourceEditor/Node.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; + +namespace Crow.Coding +{ + public class Node + { + public Node Parent; + public string Name; + public string Type; + public CodeLine StartLine; + public CodeLine EndLine; + public Dictionary Attributes = new Dictionary (); + + public List Children = new List(); + + public Node () + { + } + + public void AddChild (Node child) { + child.Parent = this; + Children.Add (child); + } + + public override string ToString () + { + return string.Format ("Name:{0}, Type:{1}\n\tparent:{2}", Name, Type, Parent); + } + } +} + diff --git a/CrowIDE/src/SourceEditor/Parser.cs b/CrowIDE/src/SourceEditor/Parser.cs new file mode 100644 index 00000000..79dc086f --- /dev/null +++ b/CrowIDE/src/SourceEditor/Parser.cs @@ -0,0 +1,297 @@ +using System; +using System.IO; +using Crow; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Crow.Coding +{ + /// + /// base class for tokenizing sources + /// + public abstract class Parser + { + /// + /// Default tokens, this enum may be overriden in derived parser with the new keyword, + /// see XMLParser for example. + /// + public enum TokenType { + Unknown, + WhiteSpace, + NewLine, + LineComment, + BlockCommentStart, + BlockComment, + BlockCommentEnd, + Type, + Identifier, + Indexer, + OpenBlock, + CloseBlock, + StatementEnding, + UnaryOp, + BinaryOp, + Affectation, + StringLitteralOpening, + StringLitteralClosing, + StringLitteral, + NumericLitteral, + Preprocessor, + } + + #region CTOR + public Parser (CodeBuffer _buffer) + { + buffer = _buffer; + + buffer.LineUpadateEvent += Buffer_LineUpadateEvent; + //buffer.LineAdditionEvent += Buffer_LineAdditionEvent;; + buffer.LineRemoveEvent += Buffer_LineRemoveEvent; + buffer.BufferCleared += Buffer_BufferCleared; + } + + #endregion + + #region Buffer events handlers + void Buffer_BufferCleared (object sender, EventArgs e) + { + + } + void Buffer_LineAdditionEvent (object sender, CodeBufferEventArgs e) + { + + } + void Buffer_LineRemoveEvent (object sender, CodeBufferEventArgs e) + { + reparseSource (); + } + void Buffer_LineUpadateEvent (object sender, CodeBufferEventArgs e) + { + for (int i = 0; i < e.LineCount; i++) + tryParseBufferLine (e.LineStart + i); + reparseSource (); + } + #endregion + + void updateFolding () { + // Stack foldings = new Stack(); + // bool inStartTag = false; + // + // for (int i = 0; i < parser.Tokens.Count; i++) { + // TokenList tl = parser.Tokens [i]; + // tl.foldingTo = null; + // int fstTK = tl.FirstNonBlankTokenIndex; + // if (fstTK > 0 && fstTK < tl.Count - 1) { + // if (tl [fstTK + 1] != XMLParser.TokenType.ElementName) + // continue; + // if (tl [fstTK] == XMLParser.TokenType.ElementStart) { + // //search closing tag + // int tkPtr = fstTK+2; + // while (tkPtr < tl.Count) { + // if (tl [tkPtr] == XMLParser.TokenType.ElementClosing) + // + // tkPtr++; + // } + // if (tl.EndingState == (int)XMLParser.States.Content) + // foldings.Push (tl); + // else if (tl.EndingState == (int)XMLParser.States.StartTag) + // inStartTag = true; + // continue; + // } + // if (tl [fstTK] == XMLParser.TokenType.ElementEnd) { + // TokenList tls = foldings.Pop (); + // int fstTKs = tls.FirstNonBlankTokenIndex; + // if (tls [fstTK + 1].Content == tl [fstTK + 1].Content) { + // tl.foldingTo = tls; + // continue; + // } + // parser.CurrentPosition = tls [fstTK + 1].Start; + // parser.SetLineInError(new ParsingException(parser, "closing tag not corresponding")); + // } + // + // } + // } + } + public void reparseSource () { + for (int i = 0; i < buffer.LineCount; i++) { + if (!buffer[i].IsParsed) + tryParseBufferLine (i); + } + try { + SyntaxAnalysis (); + } catch (Exception ex) { + Debug.WriteLine ("Syntax Error: " + ex.ToString ()); + if (ex is ParsingException) + SetLineInError (ex as ParsingException); + } + } + public void tryParseBufferLine(int lPtr) { + buffer [lPtr].exception = null; + currentLine = lPtr; + currentColumn = 0; + eol = false; + + try { + ParseCurrentLine (); + } catch (Exception ex) { + Debug.WriteLine (ex.ToString ()); + if (ex is ParsingException) + SetLineInError (ex as ParsingException); + } + + } + + protected CodeBuffer buffer; + + internal int currentLine = 0; + internal int currentColumn = 0; + protected Token currentTok; + protected bool eol = true; + + public Node RootNode; + + protected Point CurrentPosition { + get { return new Point (currentLine, currentColumn); } + set { + currentLine = value.Y; + currentColumn = value.X; + } + } + + public abstract void ParseCurrentLine(); + public abstract void SyntaxAnalysis (); + + public virtual void SetLineInError(ParsingException ex) { + currentTok = default(Token); + if (ex.Line >= buffer.LineCount) + ex.Line = buffer.LineCount - 1; + if (buffer [ex.Line].IsFolded) + buffer.ToogleFolding (ex.Line); + buffer [ex.Line].SetLineInError (ex); + } + + #region low level parsing + /// + /// Read one char from current position in buffer and store it into the current token + /// + /// if true, set the Start position of the current token to the current position + protected void readToCurrTok(bool startOfTok = false){ + if (startOfTok) + currentTok.Start = CurrentPosition; + currentTok += Read(); + } + /// + /// read n char from the buffer and store it into the current token + /// + protected void readToCurrTok(int length) { + for (int i = 0; i < length; i++) + currentTok += Read (); + } + /// + /// Save current token into current TokensLine and raz current token + /// + protected void saveAndResetCurrentTok() { + currentTok.End = CurrentPosition; + buffer[currentLine].Tokens.Add (currentTok); + currentTok = default(Token); + } + /// + /// read one char and add current token to current TokensLine, current token is reset + /// + /// Type of the token + /// set start of token to current position + protected void readAndResetCurrentTok(System.Enum type, bool startToc = false) { + readToCurrTok (); + saveAndResetCurrentTok (type); + } + /// + /// Save current tok + /// + /// set the type of the tok + protected void saveAndResetCurrentTok(System.Enum type) { + currentTok.Type = (TokenType)type; + saveAndResetCurrentTok (); + } + /// + /// Peek next char, emit '\n' if current column > buffer's line length + /// Throw error if eof is true + /// + protected virtual char Peek() { + if (eol) + throw new ParsingException (this, "Unexpected End of line"); + return currentColumn < buffer [currentLine].Length ? + buffer [currentLine] [currentColumn] : '\n'; + } + /// + /// Peek n char from buffer or less if remaining char in buffer's line is less than requested + /// if end of line is reached, no '\n' will be emitted, instead, empty string is returned. '\n' should be checked only + /// with single char Peek(). + /// Throw error is eof is true + /// + /// Length. + protected virtual string Peek(int length) { + if (eol) + throw new ParsingException (this, "Unexpected End of Line"); + int lg = Math.Min(length, Math.Max (buffer [currentLine].Length - currentColumn, buffer [currentLine].Length - currentColumn - length)); + if (lg == 0) + return ""; + return buffer [currentLine].Content.Substring (currentColumn, lg); + } + /// + /// read one char from buffer at current position, if '\n' is read, current line is incremented + /// and column is reset to 0 + /// + protected virtual char Read() { + char c = Peek (); + if (c == '\n') + eol = true; + currentColumn++; + return c; + } + /// + /// read until end of line is reached + /// + /// string read + protected virtual string ReadLine () { + string tmp = ""; + while (!eol) + tmp += Read (); + return tmp; + } + /// + /// read until end expression is reached or end of line. + /// + /// string read minus the ending expression that has to be read after + /// Expression to search for + protected virtual string ReadLineUntil (string endExp){ + string tmp = ""; + + while (!eol) { + if (buffer [currentLine].Length - currentColumn - endExp.Length < 0) { + tmp += ReadLine(); + break; + } + if (string.Equals (Peek (endExp.Length), endExp)) + return tmp; + tmp += Read(); + } + return tmp; + } + /// + /// skip white spaces, but not line break. Save spaces in a WhiteSpace token. + /// + protected void SkipWhiteSpaces () { + if (currentTok.Type != TokenType.Unknown) + throw new ParsingException (this, "current token should be reset to unknown (0) before skiping white spaces"); + while (!eol) { + if (!char.IsWhiteSpace (Peek ())||Peek()=='\n') + break; + readToCurrTok (currentTok.Type == TokenType.Unknown); + currentTok.Type = TokenType.WhiteSpace; + } + if (currentTok.Type != TokenType.Unknown) + saveAndResetCurrentTok (); + } + #endregion + } +} \ No newline at end of file diff --git a/CrowIDE/src/SourceEditor/ParsingException.cs b/CrowIDE/src/SourceEditor/ParsingException.cs new file mode 100644 index 00000000..88c51c9b --- /dev/null +++ b/CrowIDE/src/SourceEditor/ParsingException.cs @@ -0,0 +1,22 @@ +using System; + +namespace Crow.Coding +{ + public class ParsingException : Exception + { + public int Line; + public int Column; + public ParsingException(Parser parser, string txt) + : base(string.Format("Parser exception ({0},{1}): {2}", parser.currentLine, parser.currentColumn, txt)) + { + Line = parser.currentLine; + Column = parser.currentColumn; + } + public ParsingException(Parser parser, string txt, Exception innerException) + : base(txt, innerException) + { + txt = string.Format("Parser exception ({0},{1}): {2}", parser.currentLine, parser.currentColumn, txt); + } + } +} + diff --git a/CrowIDE/src/SourceEditor/SourceEditor.cs b/CrowIDE/src/SourceEditor/SourceEditor.cs new file mode 100644 index 00000000..677ba8b7 --- /dev/null +++ b/CrowIDE/src/SourceEditor/SourceEditor.cs @@ -0,0 +1,1107 @@ +// +// ScrollingTextBox.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Xml.Serialization; +using System.ComponentModel; +using System.Collections; +using Cairo; +using System.Text; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Linq; +using System.Diagnostics; +using System.IO; +using System.Threading; + +namespace Crow.Coding +{ + /// + /// Scrolling text box optimized for monospace fonts, for coding + /// + public class SourceEditor : ScrollingObject + { + #region CTOR + public SourceEditor (): base() + { + formatting.Add ((int)XMLParser.TokenType.AttributeName, new TextFormatting (Color.UnitedNationsBlue, Color.Transparent)); + formatting.Add ((int)XMLParser.TokenType.ElementName, new TextFormatting (Color.DarkBlue, Color.Transparent)); + formatting.Add ((int)XMLParser.TokenType.ElementStart, new TextFormatting (Color.Black, Color.Transparent)); + formatting.Add ((int)XMLParser.TokenType.ElementEnd, new TextFormatting (Color.Black, Color.Transparent)); + formatting.Add ((int)XMLParser.TokenType.ElementClosing, new TextFormatting (Color.Black, Color.Transparent)); + formatting.Add ((int)XMLParser.TokenType.Affectation, new TextFormatting (Color.Black, Color.Transparent)); + + formatting.Add ((int)XMLParser.TokenType.AttributeValueOpening, new TextFormatting (Color.Carmine, Color.Transparent)); + formatting.Add ((int)XMLParser.TokenType.AttributeValueClosing, new TextFormatting (Color.Carmine, Color.Transparent)); + formatting.Add ((int)XMLParser.TokenType.AttributeValue, new TextFormatting (Color.OrangeRed, Color.Transparent, false, true)); + formatting.Add ((int)XMLParser.TokenType.XMLDecl, new TextFormatting (Color.GreenCrayola, Color.Transparent)); + formatting.Add ((int)XMLParser.TokenType.BlockComment, new TextFormatting (Color.Gray, Color.Transparent, false, true)); + + parsing.Add (".crow", "Crow.Coding.XMLParser"); + + buffer = new CodeBuffer (); + buffer.LineUpadateEvent += Buffer_LineUpadateEvent; + buffer.LineAdditionEvent += Buffer_LineAdditionEvent;; + buffer.LineRemoveEvent += Buffer_LineRemoveEvent; + buffer.BufferCleared += Buffer_BufferCleared; + buffer.SelectionChanged += Buffer_SelectionChanged; + buffer.PositionChanged += Buffer_PositionChanged; + buffer.FoldingEvent += Buffer_FoldingEvent; + buffer.Add (new CodeLine("")); + + Thread updateSource = new Thread (updateSourceThreadFunc); + updateSource.IsBackground = true; + updateSource.Start (); + } + #endregion + string oldSource = ""; + void updateSourceThreadFunc (){ + while (true) { + if (projNode != null && buffer != null) { + string newSrc = buffer.FullText; + if (projNode.Source != newSrc) + projNode.Source = newSrc; + } + Thread.Sleep (100); + } + } + const int leftMarginGap = 3;//gap between items in margin and text + const int foldSize = 9;//folding rectangles size + + #region private and protected fields + bool foldingEnabled = true; + ProjectFile projNode = null; + int leftMargin = 0; //margin used to display line numbers, folding errors,etc... + int visibleLines = 1; + int visibleColumns = 1; + CodeBuffer buffer; + Parser parser; + Color selBackground; + Color selForeground; +// int _currentCol; //0 based cursor position in string +// int _currentLine; + + + Dictionary formatting = new Dictionary(); + Dictionary parsing = new Dictionary(); + + protected Rectangle rText; + protected FontExtents fe; + protected TextExtents te; + #endregion + + void measureLeftMargin () { + leftMargin = 0; + if (PrintLineNumbers) + leftMargin += (int)Math.Ceiling((double)buffer.LineCount.ToString().Length * fe.MaxXAdvance) +6; + if (foldingEnabled) + leftMargin += foldSize; + if (leftMargin > 0) + leftMargin += leftMarginGap; + updateVisibleColumns (); + } + void findLongestLineAndUpdateMaxScrollX() { + buffer.FindLongestVisualLine (); + MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns); + Debug.WriteLine ("SourceEditor: Find Longest line and update maxscrollx: {0} visible cols:{1}", MaxScrollX, visibleColumns); + } + /// + /// Updates visible line in widget, adapt max scroll y and updatePrintedLines + /// + void updateVisibleLines(){ + visibleLines = (int)Math.Floor ((double)ClientRectangle.Height / fe.Height); + NotifyValueChanged ("VisibleLines", visibleLines); + updateMaxScrollY (); + updatePrintedLines (); + System.Diagnostics.Debug.WriteLine ("update visible lines: " + visibleLines); + System.Diagnostics.Debug.WriteLine ("update MaxScrollY: " + MaxScrollY); + } + void updateVisibleColumns(){ + visibleColumns = (int)Math.Floor ((double)(ClientRectangle.Width - leftMargin)/ fe.MaxXAdvance); + MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns); + + System.Diagnostics.Debug.WriteLine ("update visible columns: {0} leftMargin:{1}",visibleColumns, leftMargin); + System.Diagnostics.Debug.WriteLine ("update MaxScrollX: " + MaxScrollX); + } + void updateMaxScrollY () { + if (parser == null || !foldingEnabled) { + MaxScrollY = Math.Max (0, buffer.LineCount - visibleLines); + if (buffer.UnfoldedLines > 0) + NotifyValueChanged ("ChildHeightRatio", Slot.Height * visibleLines / buffer.UnfoldedLines); + } else { + MaxScrollY = Math.Max (0, buffer.UnfoldedLines - visibleLines); + if (buffer.UnfoldedLines > 0) + NotifyValueChanged ("ChildHeightRatio", Slot.Height * visibleLines / buffer.UnfoldedLines); + } + } + + int firstPrintedLine = -1; + /// + /// list of lines visible in the Editor depending on scrolling and folding + /// + List PrintedLines; + + void updatePrintedLines () { + lock (buffer.EditMutex) { + PrintedLines = new List (); + int curL = 0; + int i = 0; + + while (curL < buffer.LineCount && i < ScrollY) { + if (buffer [curL].IsFolded) + curL = buffer.GetEndNodeIndex (curL); + curL++; + i++; + } + + firstPrintedLine = curL; + i = 0; + while (i < visibleLines && curL < buffer.LineCount) { + PrintedLines.Add (buffer [curL]); + + if (buffer [curL].IsFolded) + curL = buffer.GetEndNodeIndex (curL); + + curL++; + i++; + } + } + RegisterForGraphicUpdate (); + } + void toogleFolding (int line) { + if (parser == null || !foldingEnabled) + return; + buffer.ToogleFolding (line); + } + + #region Buffer events handlers + void Buffer_BufferCleared (object sender, EventArgs e) + { + buffer.longestLineCharCount = 0; + buffer.longestLineIdx = 0; + measureLeftMargin (); + MaxScrollX = MaxScrollY = 0; + PrintedLines = null; + RegisterForGraphicUpdate (); + } + void Buffer_LineAdditionEvent (object sender, CodeBufferEventArgs e) + { + for (int i = 0; i < e.LineCount; i++) { + int lptr = e.LineStart + i; + int charCount = buffer[lptr].PrintableLength; + if (charCount > buffer.longestLineCharCount) { + buffer.longestLineIdx = lptr; + buffer.longestLineCharCount = charCount; + }else if (lptr <= buffer.longestLineIdx) + buffer.longestLineIdx++; + if (parser == null) + continue; + parser.tryParseBufferLine (e.LineStart + i); + } + measureLeftMargin (); + + if (parser != null) + parser.reparseSource (); + + updatePrintedLines (); + updateMaxScrollY (); + RegisterForGraphicUpdate (); + } + void Buffer_LineRemoveEvent (object sender, CodeBufferEventArgs e) + { + bool trigFindLongestLine = false; + for (int i = 0; i < e.LineCount; i++) { + int lptr = e.LineStart + i; + if (lptr <= buffer.longestLineIdx) + trigFindLongestLine = true; + } + if (trigFindLongestLine) + findLongestLineAndUpdateMaxScrollX (); + + measureLeftMargin (); + updatePrintedLines (); + updateMaxScrollY (); + RegisterForGraphicUpdate (); + } + void Buffer_LineUpadateEvent (object sender, CodeBufferEventArgs e) + { + bool trigFindLongestLine = false; + for (int i = 0; i < e.LineCount; i++) { + + int lptr = e.LineStart + i; + if (lptr == buffer.longestLineIdx) + trigFindLongestLine = true; + else if (buffer[lptr].PrintableLength > buffer.longestLineCharCount) { + buffer.longestLineCharCount = buffer[lptr].PrintableLength; + buffer.longestLineIdx = lptr; + } + } + if (trigFindLongestLine) + findLongestLineAndUpdateMaxScrollX (); + + RegisterForGraphicUpdate (); + } + void Buffer_PositionChanged (object sender, EventArgs e) + { + updateOnScreenCurLineFromBuffCurLine (); + } + + void Buffer_SelectionChanged (object sender, EventArgs e) + { + RegisterForGraphicUpdate (); + } + void Buffer_FoldingEvent (object sender, CodeBufferEventArgs e) + { + updatePrintedLines (); + updateMaxScrollY (); + RegisterForGraphicUpdate (); + } + #endregion + + Parser getParserFromExt (string extension) { + if (string.IsNullOrEmpty(extension)) + return null; + if (!parsing.ContainsKey(extension)) + return null; + Type parserType = Type.GetType (parsing [extension]); + if (parserType == null) + return null; + return (Parser)Activator.CreateInstance (parserType, buffer ); + } + + #region Public Crow Properties + [XmlAttributeAttribute] + public bool PrintLineNumbers + { + get { return Configuration.Global.Get ("PrintLineNumbers"); + } + set + { + if (PrintLineNumbers == value) + return; + Configuration.Global.Set ("PrintLineNumbers", value); + NotifyValueChanged ("PrintLineNumbers", PrintLineNumbers); + measureLeftMargin (); + RegisterForGraphicUpdate (); + } + } + [XmlAttributeAttribute] + public ProjectFile ProjectNode + { + get { + return projNode; + } + set + { + if (projNode == value) + return; + + projNode = value; + + NotifyValueChanged ("ProjectNode", projNode); + + if (projNode == null) + return; + + if (!File.Exists (projNode.AbsolutePath)) + return; + + parser = getParserFromExt (System.IO.Path.GetExtension (projNode.Extension)); + + try { + buffer.Load (projNode.Source); + oldSource = projNode.Source; + } catch (Exception ex) { + Debug.WriteLine (ex.ToString ()); + } + + updateMaxScrollY (); + MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns); + updatePrintedLines (); + + RegisterForGraphicUpdate (); + } + } + [XmlAttributeAttribute][DefaultValue("BlueGray")] + public virtual Color SelectionBackground { + get { return selBackground; } + set { + if (value == selBackground) + return; + selBackground = value; + NotifyValueChanged ("SelectionBackground", selBackground); + RegisterForRedraw (); + } + } + [XmlAttributeAttribute][DefaultValue("White")] + public virtual Color SelectionForeground { + get { return selForeground; } + set { + if (value == selForeground) + return; + selForeground = value; + NotifyValueChanged ("SelectionForeground", selForeground); + RegisterForRedraw (); + } + } + + // [XmlAttributeAttribute][DefaultValue(0)] +// public int CurrentColumn{ +// get { return _currentCol; } +//// set { +//// if (value == _currentCol) +//// return; +//// if (value < 0) +//// _currentCol = 0; +//// else if (value > buffer[_currentLine].PrintableLength) +//// _currentCol = buffer[_currentLine].PrintableLength; +//// else +//// _currentCol = value; +//// +//// buffer.SetBufferPos (CurrentPosition); +//// +//// if (_currentCol < ScrollX) +//// ScrollX = _currentCol; +//// else if (_currentCol >= ScrollX + visibleColumns) +//// ScrollX = _currentCol - visibleColumns + 1; +//// +//// NotifyValueChanged ("CurrentColumn", _currentCol); +//// } +// } +// [XmlAttributeAttribute][DefaultValue(0)] +// public int CurrentLine{ +// get { return _currentLine; } +//// set { +//// if (value == _currentLine) +//// return; +//// if (value >= buffer.LineCount) +//// _currentLine = buffer.LineCount-1; +//// else if (value < 0) +//// _currentLine = 0; +//// else +//// _currentLine = value; +//// +//// if (_currentCol > buffer[_currentLine].PrintableLength) +//// CurrentColumn = buffer[_currentLine].PrintableLength;//buffer.setBufferPos is called inside +//// else +//// buffer.SetBufferPos (CurrentPosition); +//// +////// if (_currentLine < ScrollY) +////// ScrollY = _currentLine; +////// else if (_currentLine >= ScrollY + visibleLines) +////// ScrollY = _currentLine - visibleLines + 1; +//// +//// NotifyValueChanged ("CurrentLine", _currentLine); +//// } +// } +// /// +// /// Current position is in the printed coord system, tabulation chars are replaced with 4 spaces, +// /// while in the buffer, the position holds tabulations as single chars +// /// +// /// The current position. +// [XmlIgnore]public Point CurrentPosition { +// get { return new Point(CurrentColumn, CurrentLine); } +// set { +// _currentCol = value.X; +// _currentLine = value.Y; +// +// if (_currentCol < ScrollX) +// ScrollX = _currentCol; +// else if (_currentCol >= ScrollX + visibleColumns) +// ScrollX = _currentCol - visibleColumns + 1; +//// +//// if (_currentLine < ScrollY) +//// ScrollY = _currentLine; +//// else if (_currentLine >= ScrollY + visibleLines) +//// ScrollY = _currentLine - visibleLines + 1; +// +// NotifyValueChanged ("CurrentColumn", _currentCol); +// NotifyValueChanged ("CurrentLine", _currentLine); +// } +// } + +// [XmlIgnore]public string SelectedText +// { +// get { +// if (!selectionIsEmpty) +// buffer.SetSelection (selectionStart, selectionEnd); +// return buffer.SelectedText; +// } +// } + + #endregion + + + void updateOnScreenCurLineFromBuffCurLine(){ + printedCurrentLine = PrintedLines.IndexOf (buffer.CurrentCodeLine); + } + +// void setCurrentLineFromBuffer () { +// _currentLine = buffer.CurrentLine; +// NotifyValueChanged ("CurrentLine", _currentLine); +// } + + public override int ScrollY { + get { + return base.ScrollY; + } + set { + if (value == base.ScrollY) + return; + base.ScrollY = value; + updatePrintedLines (); + } + } + + + /// + /// Index of the currentline in the PrintedLines array + /// + int printedCurrentLine = 0; + + /// + /// Current editor line, when set, update buffer.CurrentLine + /// + int PrintedCurrentLine { + get { return printedCurrentLine;} + set { + if (value < 0) { + ScrollY += value; + printedCurrentLine = 0; + } else if (PrintedLines.Count < visibleLines && value >= PrintedLines.Count) { + printedCurrentLine = PrintedLines.Count - 1; + }else if (value >= visibleLines) { + ScrollY += value - visibleLines + 1; + printedCurrentLine = visibleLines - 1; + }else + printedCurrentLine = value; + Debug.WriteLine ("printed current line:" + printedCurrentLine.ToString ()); + //update position in buffer + buffer.CurrentLine = buffer.IndexOf (PrintedLines[printedCurrentLine]); + } + } + int getTabulatedColumn (int col, int line) { + return buffer [line].Content.Substring (0, col).Replace ("\t", new String (' ', Interface.TabSize)).Length; + } + int getTabulatedColumn (Point pos) { + return getTabulatedColumn (pos.X,pos.Y); + } + /// + /// Moves cursor one char to the left, move up if cursor reaches start of line + /// + /// true if move succeed + public bool MoveLeft(){ + if (buffer.CurrentColumn == 0) { + if (printedCurrentLine == 0) + return false; + PrintedCurrentLine--; + buffer.CurrentColumn = int.MaxValue; + } else + buffer.CurrentColumn--; + return true; + } + /// + /// Moves cursor one char to the right, move down if cursor reaches end of line + /// + /// true if move succeed + public bool MoveRight(){ + if (buffer.CurrentColumn >= buffer.CurrentCodeLine.Length) { + if (PrintedCurrentLine == buffer.UnfoldedLines - 1) + return false; + buffer.CurrentColumn = 0; + PrintedCurrentLine++; + } else + buffer.CurrentColumn++; + return true; + } + #region Drawing + void drawLine(Context gr, Rectangle cb, int i) { + CodeLine cl = PrintedLines[i]; + int lineIndex = buffer.IndexOf(cl); + + double y = cb.Y + fe.Height * i, x = cb.X; + + //Draw line numbering + Color mgFg = Color.Gray; + Color mgBg = Color.White; + if (PrintLineNumbers){ + Rectangle mgR = new Rectangle ((int)x, (int)y, leftMargin - leftMarginGap, (int)Math.Ceiling(fe.Height)); + if (cl.exception != null) { + mgBg = Color.Red; + if (buffer.CurrentLine == lineIndex) + mgFg = Color.White; + else + mgFg = Color.LightGray; + }else if (buffer.CurrentLine == lineIndex) { + mgFg = Color.Black; + } + string strLN = (lineIndex+1).ToString (); + gr.SetSourceColor (mgBg); + gr.Rectangle (mgR); + gr.Fill(); + gr.SetSourceColor (mgFg); + + gr.MoveTo (cb.X + (int)(gr.TextExtents (buffer.LineCount.ToString()).Width - gr.TextExtents (strLN).Width), y + fe.Ascent); + gr.ShowText (strLN); + gr.Fill (); + } + + + //draw folding + if (foldingEnabled){ + if (cl.IsFoldable) { + if (cl.SyntacticNode.StartLine != cl.SyntacticNode.EndLine) { + gr.SetSourceColor (Color.Black); + Rectangle rFld = new Rectangle (cb.X + leftMargin - leftMarginGap - foldSize, (int)(y + fe.Height / 2.0 - foldSize / 2.0), foldSize, foldSize); + gr.Rectangle (rFld, 1.0); + if (cl.IsFolded) { + gr.MoveTo (rFld.Center.X + 0.5, rFld.Y + 2); + gr.LineTo (rFld.Center.X + 0.5, rFld.Bottom - 2); + } + gr.MoveTo (rFld.Left + 2, rFld.Center.Y + 0.5); + gr.LineTo (rFld.Right - 2, rFld.Center.Y + 0.5); + gr.Stroke (); + } + } + } + + gr.SetSourceColor (Foreground); + x += leftMargin; + + if (cl.Tokens == null) + drawRawCodeLine (gr, x, y, i, lineIndex); + else + drawParsedCodeLine (gr, x, y, i, lineIndex); + } +// void drawParsed(Context gr){ +// if (PrintedLines == null) +// return; +// +// gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); +// gr.SetFontSize (Font.Size); +// gr.FontOptions = Interface.FontRenderingOptions; +// gr.Antialias = Interface.Antialias; +// +// Rectangle cb = ClientRectangle; +// gr.Save (); +// CairoHelpers.CairoRectangle (gr, cb, CornerRadius); +// gr.Clip (); +// +// bool selectionInProgress = false; +// +// Foreground.SetAsSource (gr); +// +// #region draw text cursor +// if (SelBegin != SelRelease) +// selectionInProgress = true; +// else if (HasFocus){ +// gr.LineWidth = 1.0; +// double cursorX = + leftMargin + cb.X + (CurrentColumn - ScrollX) * fe.MaxXAdvance; +// gr.MoveTo (0.5 + cursorX, cb.Y + printedCurrentLine * fe.Height); +// gr.LineTo (0.5 + cursorX, cb.Y + (printedCurrentLine + 1) * fe.Height); +// gr.Stroke(); +// } +// #endregion +// +// for (int i = 0; i < PrintedLines.Count; i++) +// drawTokenLine (gr, i, selectionInProgress, cb); +// +// gr.Restore (); +// } + void drawRawCodeLine(Context gr, double x, double y, int i, int lineIndex) { + string lstr = buffer[lineIndex].PrintableContent; + if (ScrollX < lstr.Length) + lstr = lstr.Substring (ScrollX); + else + lstr = ""; + + gr.MoveTo (x, y + fe.Ascent); + gr.ShowText (lstr); + gr.Fill (); + + if (!buffer.SelectionIsEmpty && lineIndex >= buffer.SelectionStart.Y && lineIndex <= buffer.SelectionEnd.Y) { + double rLineX = x, + rLineY = y, + rLineW = lstr.Length * fe.MaxXAdvance; + + //System.Diagnostics.Debug.WriteLine ("sel start: " + buffer.SelectionStart + " sel end: " + buffer.SelectionEnd); + if (lineIndex == buffer.SelectionStart.Y) { + rLineX += (selStartCol - ScrollX) * fe.MaxXAdvance; + rLineW -= selStartCol * fe.MaxXAdvance; + } + if (lineIndex == buffer.SelectionEnd.Y) + rLineW -= (lstr.Length - selEndCol) * fe.MaxXAdvance; + + gr.Save (); + gr.Operator = Operator.Source; + gr.Rectangle (rLineX, rLineY, rLineW, fe.Height); + gr.SetSourceColor (SelectionBackground); + gr.FillPreserve (); + gr.Clip (); + gr.Operator = Operator.Over; + gr.SetSourceColor (SelectionForeground); + gr.MoveTo (x, y + fe.Ascent); + gr.ShowText (lstr); + gr.Fill (); + gr.Restore (); + } + } + void drawParsedCodeLine (Context gr, double x, double y, int i, int lineIndex) { + int lPtr = 0; + CodeLine cl = PrintedLines[i]; + + for (int t = 0; t < cl.Tokens.Count; t++) { + string lstr = cl.Tokens [t].PrintableContent; + if (lPtr < ScrollX) { + if (lPtr - ScrollX + lstr.Length <= 0) { + lPtr += lstr.Length; + continue; + } + lstr = lstr.Substring (ScrollX - lPtr); + lPtr += ScrollX - lPtr; + } + Color bg = this.Background; + Color fg = this.Foreground; + Color selbg = this.SelectionBackground; + Color selfg = this.SelectionForeground; + FontSlant fts = FontSlant.Normal; + FontWeight ftw = FontWeight.Normal; + + if (formatting.ContainsKey ((int)cl.Tokens [t].Type)) { + TextFormatting tf = formatting [(int)cl.Tokens [t].Type]; + bg = tf.Background; + fg = tf.Foreground; + if (tf.Bold) + ftw = FontWeight.Bold; + if (tf.Italic) + fts = FontSlant.Italic; + } + + gr.SelectFontFace (Font.Name, fts, ftw); + gr.SetSourceColor (fg); + + gr.MoveTo (x, y + fe.Ascent); + gr.ShowText (lstr); + gr.Fill (); + + if (buffer.SelectionInProgress && lineIndex >= buffer.SelectionStart.Y && lineIndex <= buffer.SelectionEnd.Y && + !(lineIndex == buffer.SelectionStart.Y && lPtr + lstr.Length <= selStartCol) && + !(lineIndex == buffer.SelectionEnd.Y && selEndCol <= lPtr)) { + + double rLineX = x, + rLineY = y, + rLineW = lstr.Length * fe.MaxXAdvance; + double startAdjust = 0.0; + + if ((lineIndex == buffer.SelectionStart.Y) && (selStartCol < lPtr + lstr.Length) && (selStartCol > lPtr)) + startAdjust = (selStartCol - lPtr) * fe.MaxXAdvance; + rLineX += startAdjust; + if ((lineIndex == buffer.SelectionEnd.Y) && (selEndCol < lPtr + lstr.Length)) + rLineW = (selEndCol - lPtr) * fe.MaxXAdvance; + rLineW -= startAdjust; + + gr.Save (); + gr.Operator = Operator.Source; + gr.Rectangle (rLineX, rLineY, rLineW, fe.Height); + gr.SetSourceColor (selbg); + gr.FillPreserve (); + gr.Clip (); + gr.Operator = Operator.Over; + gr.SetSourceColor (selfg); + gr.MoveTo (x, y + fe.Ascent); + gr.ShowText (lstr); + gr.Fill (); + gr.Restore (); + } + x += (int)lstr.Length * fe.MaxXAdvance; + lPtr += lstr.Length; + } + } + + #endregion + + #region GraphicObject overrides + public override Font Font { + get { return base.Font; } + set { + base.Font = value; + + using (ImageSurface img = new ImageSurface (Format.Argb32, 1, 1)) { + using (Context gr = new Context (img)) { + gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); + gr.SetFontSize (Font.Size); + + fe = gr.FontExtents; + } + } + MaxScrollY = 0; + RegisterForGraphicUpdate (); + } + } + protected override int measureRawSize(LayoutingType lt) + { + if (lt == LayoutingType.Height) + return (int)Math.Ceiling(fe.Height * buffer.LineCount) + Margin * 2; + + return (int)(fe.MaxXAdvance * buffer.longestLineCharCount) + Margin * 2 + leftMargin; + } + public override void OnLayoutChanges (LayoutingType layoutType) + { + base.OnLayoutChanges (layoutType); + + if (layoutType == LayoutingType.Height) + updateVisibleLines (); + else if (layoutType == LayoutingType.Width) + updateVisibleColumns (); + } + + int selStartCol; + int selEndCol; + + protected override void onDraw (Context gr) + { +// if (!System.Threading.Monitor.TryEnter (buffer.EditMutex)) { +// RegisterForGraphicUpdate (); +// return; +// } + + base.onDraw (gr); + + gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); + gr.SetFontSize (Font.Size); + gr.FontOptions = Interface.FontRenderingOptions; + gr.Antialias = Interface.Antialias; + + Rectangle cb = ClientRectangle; + + Foreground.SetAsSource (gr); + + lock (buffer.EditMutex) { + #region draw text cursor + if (buffer.SelectionInProgress){ + selStartCol = getTabulatedColumn (buffer.SelectionStart); + selEndCol = getTabulatedColumn (buffer.SelectionEnd); + }else if (HasFocus){ + gr.LineWidth = 1.0; + double cursorX = cb.X + (getTabulatedColumn(buffer.CurrentPosition) - ScrollX) * fe.MaxXAdvance + leftMargin; + gr.MoveTo (0.5 + cursorX, cb.Y + (printedCurrentLine) * fe.Height); + gr.LineTo (0.5 + cursorX, cb.Y + (printedCurrentLine + 1) * fe.Height); + gr.Stroke(); + } + #endregion + + if (PrintedLines != null) { + for (int i = 0; i < visibleLines; i++) { + if (i + ScrollY >= buffer.UnfoldedLines)//TODO:need optimize + break; + drawLine (gr, cb, i); + } + } + } + //System.Threading.Monitor.Exit (buffer.EditMutex); + } + #endregion + + #region Mouse handling + Point mouseLocalPos; + bool doubleClicked = false; + + void updateCurrentPos(){ +// if (mouseLocalPos.X < 0) +// CurrentColumn--; +// else + PrintedCurrentLine = (int)Math.Max (0, Math.Floor (mouseLocalPos.Y / fe.Height)); + int curVisualCol = ScrollX + (int)Math.Round ((mouseLocalPos.X - leftMargin) / fe.MaxXAdvance); + + int i = 0; + int buffCol = 0; + while (i < curVisualCol && buffCol < buffer.CurrentCodeLine.Length) { + if (buffer.CurrentCodeLine[buffCol] == '\t') + i += Interface.TabSize; + else + i++; + buffCol++; + } + buffer.CurrentColumn = buffCol; + +// if (mouseLocalPos.Y < 0) +// ScrollY--; + } + public override void onMouseEnter (object sender, MouseMoveEventArgs e) + { + base.onMouseEnter (sender, e); + if (e.X - ScreenCoordinates(Slot).X < leftMargin + ClientRectangle.X) + IFace.MouseCursor = XCursor.Default; + else + IFace.MouseCursor = XCursor.Text; + } + public override void onMouseLeave (object sender, MouseMoveEventArgs e) + { + base.onMouseLeave (sender, e); + IFace.MouseCursor = XCursor.Default; + } + public override void onMouseMove (object sender, MouseMoveEventArgs e) + { + base.onMouseMove (sender, e); + + mouseLocalPos = e.Position - ScreenCoordinates(Slot).TopLeft - ClientRectangle.TopLeft; + + if (!e.Mouse.IsButtonDown (MouseButton.Left)) { + if (mouseLocalPos.X < leftMargin) + IFace.MouseCursor = XCursor.Default; + else + IFace.MouseCursor = XCursor.Text; + return; + } + + if (!HasFocus || !buffer.SelectionInProgress) + return; + + //mouse is down + updateCurrentPos(); + buffer.SetSelEndPos (); + } + public override void onMouseDown (object sender, MouseButtonEventArgs e) + { + if (!this.Focusable) + return; + + if (mouseLocalPos.X >= leftMargin) + base.onMouseDown (sender, e); + + if (doubleClicked) { + doubleClicked = false; + return; + } + + if (mouseLocalPos.X < leftMargin) { + toogleFolding (buffer.IndexOf (PrintedLines [(int)Math.Max (0, Math.Floor (mouseLocalPos.Y / fe.Height))])); + return; + } + + updateCurrentPos (); + buffer.SetSelStartPos (); + } + public override void onMouseUp (object sender, MouseButtonEventArgs e) + { + base.onMouseUp (sender, e); + + if (buffer.SelectionIsEmpty) + buffer.ResetSelection (); + } + + public override void onMouseDoubleClick (object sender, MouseButtonEventArgs e) + { + doubleClicked = true; + base.onMouseDoubleClick (sender, e); + + buffer.GotoWordStart (); + buffer.SetSelStartPos (); + buffer.GotoWordEnd (); + buffer.SetSelEndPos (); + } + + public override void onMouseWheel (object sender, MouseWheelEventArgs e) + { + base.onMouseWheel (sender, e); + } + #endregion + + #region Keyboard handling + public override void onKeyDown (object sender, KeyboardKeyEventArgs e) + { + //base.onKeyDown (sender, e); + + Key key = e.Key; + + switch (key) + { + case Key.Back: + buffer.DeleteChar (); + break; + case Key.Clear: + break; + case Key.Delete: + if (buffer.SelectionIsEmpty) + MoveRight (); + else if (e.Shift) + IFace.Clipboard = buffer.SelectedText; + buffer.DeleteChar (); + break; + case Key.Enter: + case Key.KeypadEnter: + if (!buffer.SelectionIsEmpty) + buffer.DeleteChar (); + buffer.InsertLineBreak (); + break; + case Key.Escape: + buffer.ResetSelection (); + break; + case Key.Home: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (e.Control) + buffer.CurrentLine = 0; + buffer.CurrentColumn = 0; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (e.Control) + buffer.CurrentLine = 0; + buffer.CurrentColumn = 0; + break; + case Key.End: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (e.Control) + buffer.CurrentLine = int.MaxValue; + buffer.CurrentColumn = int.MaxValue; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (e.Control) + buffer.CurrentLine = int.MaxValue; + buffer.CurrentColumn = int.MaxValue; + break; + case Key.Insert: + if (e.Shift) + buffer.Insert (IFace.Clipboard); + else if (e.Control && !buffer.SelectionIsEmpty) + IFace.Clipboard = buffer.SelectedText; + break; + case Key.Left: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (e.Control) + buffer.GotoWordStart (); + else + MoveLeft (); + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (e.Control) + buffer.GotoWordStart (); + else + MoveLeft(); + break; + case Key.Right: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (e.Control) + buffer.GotoWordEnd (); + else + MoveRight (); + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (e.Control) + buffer.GotoWordEnd (); + else + MoveRight (); + break; + case Key.Up: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + PrintedCurrentLine--; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + PrintedCurrentLine--; + break; + case Key.Down: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + PrintedCurrentLine++; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + PrintedCurrentLine++; + break; + case Key.Menu: + break; + case Key.NumLock: + break; + case Key.PageDown: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + PrintedCurrentLine += visibleLines; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + PrintedCurrentLine += visibleLines; + break; + case Key.PageUp: + if (e.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + PrintedCurrentLine -= visibleLines; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + PrintedCurrentLine -= visibleLines; + break; + case Key.RWin: + break; + case Key.Tab: + buffer.Insert ("\t"); + break; + case Key.F8: + toogleFolding (buffer.CurrentLine); +// if (parser != null) +// reparseSource (); + break; + default: + break; + } + RegisterForGraphicUpdate(); + } + public override void onKeyPress (object sender, KeyPressEventArgs e) + { + base.onKeyPress (sender, e); + + buffer.Insert (e.KeyChar.ToString()); + buffer.ResetSelection (); + + //RegisterForGraphicUpdate(); + } + #endregion + } +} \ No newline at end of file diff --git a/CrowIDE/src/SourceEditor/TextFormatting.cs b/CrowIDE/src/SourceEditor/TextFormatting.cs new file mode 100644 index 00000000..f7b2e518 --- /dev/null +++ b/CrowIDE/src/SourceEditor/TextFormatting.cs @@ -0,0 +1,19 @@ +using System; + +namespace Crow.Coding +{ + public struct TextFormatting { + public Color Foreground; + public Color Background; + public bool Bold; + public bool Italic; + + public TextFormatting(Color fg, Color bg, bool bold = false, bool italic = false){ + Foreground = fg; + Background = bg; + Bold = bold; + Italic = italic; + } + } +} + diff --git a/CrowIDE/src/SourceEditor/Token.cs b/CrowIDE/src/SourceEditor/Token.cs new file mode 100644 index 00000000..36f0242f --- /dev/null +++ b/CrowIDE/src/SourceEditor/Token.cs @@ -0,0 +1,70 @@ +// +// Token.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2017 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +using System; + +namespace Crow.Coding +{ + public struct Token + { + public Parser.TokenType Type; + public string Content; + public Point Start; + public Point End; + + public string PrintableContent { + get { return string.IsNullOrEmpty(Content) ? "" : Content.Replace("\t", new String(' ', Interface.TabSize)); } + } + +// public Token (TokenType tokType, string content = ""){ +// Type = tokType; +// Content = content; +// } + + public bool IsEmpty { get { return string.IsNullOrEmpty(Content); }} + + public static bool operator == (Token t, System.Enum tt){ + return Convert.ToInt32(t.Type) == Convert.ToInt32(tt); + } + public static bool operator != (Token t, System.Enum tt){ + return Convert.ToInt32(t.Type) != Convert.ToInt32(tt); + } + public static bool operator == (System.Enum tt, Token t){ + return Convert.ToInt32(t.Type) == Convert.ToInt32(tt); + } + public static bool operator != (System.Enum tt, Token t){ + return Convert.ToInt32(t.Type) != Convert.ToInt32(tt); + } + + public static Token operator +(Token t, char c){ + t.Content += c; + return t; + } + public static Token operator +(Token t, string s){ + t.Content += s; + return t; + } + public override string ToString () + { + return string.Format ("[Tok{2}->{3}:{0}: {1}]", Type,Content,Start,End); + } + } +} + diff --git a/CrowIDE/src/SourceEditor/XMLParser.cs b/CrowIDE/src/SourceEditor/XMLParser.cs new file mode 100644 index 00000000..2a8ffeb1 --- /dev/null +++ b/CrowIDE/src/SourceEditor/XMLParser.cs @@ -0,0 +1,299 @@ +using System; +using Crow; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Diagnostics; +using System.Linq; + +namespace Crow.Coding +{ + public class XMLParser : Parser + { + public new enum TokenType { + Unknown = Parser.TokenType.Unknown, + WhiteSpace = Parser.TokenType.WhiteSpace, + NewLine = Parser.TokenType.NewLine, + LineComment = Parser.TokenType.LineComment, + BlockCommentStart = Parser.TokenType.BlockCommentStart, + BlockComment = Parser.TokenType.BlockComment, + BlockCommentEnd = Parser.TokenType.BlockCommentEnd, + Affectation = Parser.TokenType.Affectation, + XMLDecl = Parser.TokenType.Preprocessor, + ElementStart, + ElementEnd, + ElementClosing = Parser.TokenType.StatementEnding, + ElementName = Parser.TokenType.Type, + AttributeName = Parser.TokenType.Identifier, + AttributeValueOpening = Parser.TokenType.StringLitteralOpening, + AttributeValueClosing = Parser.TokenType.StringLitteralClosing, + AttributeValue = Parser.TokenType.StringLitteral, + } + + public enum States + { + init, //first statement of prolog, xmldecl should only apear in this state + prolog, //misc before doctypedecl + InternalSubset, //doctype declaration subset + ExternalSubsetInit, + ExternalSubset, + BlockComment, + DTDEnd,//doctype finished + XML,//normal xml + StartTag,//inside start tag + Content,//after start tag with no closing slash + EndTag + } + + #region CTOR + public XMLParser (CodeBuffer _buffer) : base(_buffer) {} + #endregion + + enum Keywords + { + DOCTYPE, + ELEMENT, + ATTLIST, + ENTITY, + NOTATION + } + + States curState = States.init; + + #region Regular Expression for validity checks + //private static Regex rxValidChar = new Regex("[\u0020-\uD7FF]"); + private static Regex rxValidChar = new Regex(@"\u0009|\u000A|\u000D|[\u0020-\uD7FF]|[\uE000-\uFFFD]"); //| [\u10000-\u10FFFF] unable to set those plans + private static Regex rxNameStartChar = new Regex(@":|[A-Z]|_|[a-z]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u02FF]|[\u0370-\u037D]|[\u037F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]"); // | [\u10000-\uEFFFF] + private static Regex rxNameChar = new Regex(@":|[A-Z]|_|[a-z]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u02FF]|[\u0370-\u037D]|[\u037F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]|-|\.|[0-9]|\u00B7|[\u0300-\u036F]|[\u203F-\u2040]");//[\u10000-\uEFFFF]| + private static Regex rxDecimal = new Regex(@"[0-9]+"); + private static Regex rxHexadecimal = new Regex(@"[0-9a-fA-F]+"); + private static Regex rxAttributeValue = new Regex(@"[^<]"); + private static Regex rxEntityValue = new Regex(@"[^<]"); + private static Regex rxPubidChar = new Regex(@"\u0020|\u000D|\u000A|[a-zA-Z0-9]|[-\(\)\+\,\./:=\?;!\*#@\$_%]"); + #endregion + + #region Character ValidityCheck + public bool nextCharIsValidCharStartName + { + get { return rxNameStartChar.IsMatch(new string(new char[]{Peek()})); } + } + public bool nextCharIsValidCharName + { + get { return rxNameChar.IsMatch(new string(new char[]{Peek()})); } + } + #endregion + + public override void SetLineInError (ParsingException ex) + { + base.SetLineInError (ex); + //buffer[ex.Line].Tokens.EndingState = (int)States.init; + } + + public override void ParseCurrentLine () + { + Debug.WriteLine (string.Format("parsing line:{0}", currentLine)); + CodeLine cl = buffer [currentLine]; + cl.Tokens = new List (); + + //retrieve current parser state from previous line + if (currentLine > 0) + curState = (States)buffer[currentLine - 1].EndingState; + else + curState = States.init; + + States previousEndingState = (States)cl.EndingState; + + + while (! eol) { + SkipWhiteSpaces (); + + if (eol) + break; + + if (Peek () == '\n') { + if (currentTok != TokenType.Unknown) + throw new ParsingException (this, "Unexpected end of line"); + Read (); + eol = true; + continue; + } + + if (curState == States.BlockComment) { + if (currentTok != TokenType.Unknown) + Debugger.Break (); + + currentTok.Start = CurrentPosition; + currentTok.Type = (Parser.TokenType)TokenType.BlockComment; + currentTok += ReadLineUntil ("-->"); + if (Peek (3) == "-->") { + readToCurrTok (3); + curState = States.XML; + } + saveAndResetCurrentTok (); + continue; + } + + switch (Peek()) { + case '<': + readToCurrTok (true); + switch (Peek()) { + case '?': + if (curState != States.init) + throw new ParsingException (this, "xml decl may appear only on first line"); + readToCurrTok (); + currentTok += ReadLineUntil ("?>"); + if (Peek (2) != "?>") + throw new ParsingException (this, "expecting '?>'"); + readToCurrTok (2); + saveAndResetCurrentTok (TokenType.XMLDecl); + curState = States.prolog; + break; + case '!': + readToCurrTok (); + switch (Peek()) { + case '-': + readToCurrTok (); + if (Peek () != '-') + throw new ParsingException (this, "Expecting comment start tag"); + readToCurrTok (); + currentTok += ReadLineUntil ("--"); + if (Peek (3) == "-->") { + readToCurrTok (3); + }else + curState = States.BlockComment; + saveAndResetCurrentTok (TokenType.BlockComment); + break; + default: + throw new ParsingException(this, "error"); + } + break; + default: + if (!(curState == States.Content || curState == States.XML || curState == States.init || curState == States.prolog)) + throw new ParsingException (this, "Unexpected char: '<'"); + if (Peek () == '/') { + curState = States.EndTag; + readToCurrTok (); + saveAndResetCurrentTok (TokenType.ElementEnd); + } else { + curState = States.StartTag; + saveAndResetCurrentTok (TokenType.ElementStart); + } + + if (!nextCharIsValidCharStartName) + throw new ParsingException (this, "Expected element name"); + + readToCurrTok (true); + while (nextCharIsValidCharName) + readToCurrTok (); + + saveAndResetCurrentTok (TokenType.ElementName); + break; + } + break; + case '/': + if (curState != States.StartTag) + throw new ParsingException (this, "Unexpected char: '/'"); + readToCurrTok (true); + if (Peek () != '>') + throw new ParsingException (this, "Expecting '>'"); + readAndResetCurrentTok (TokenType.ElementEnd); + + curState = States.XML; + break; + case '>': + readAndResetCurrentTok (TokenType.ElementClosing, true); + switch (curState) { + case States.EndTag: + curState = States.XML; + break; + case States.StartTag: + curState = States.Content; + break; + default: + throw new ParsingException (this, "Unexpected char: '>'"); + } + break; + default: + switch (curState) { + case States.StartTag: + if (!nextCharIsValidCharStartName) + throw new ParsingException (this, "Expected attribute name"); + readToCurrTok (true); + while (nextCharIsValidCharName) + readToCurrTok (); + saveAndResetCurrentTok (TokenType.AttributeName); + if (Peek () != '=') + throw new ParsingException (this, "Expecting: '='"); + readAndResetCurrentTok (TokenType.Affectation, true); + + char openAttVal = Peek (); + if (openAttVal != '"' && openAttVal != '\'') + throw new ParsingException (this, "Expecting attribute value enclosed either in '\"' or in \"'\""); + readAndResetCurrentTok (TokenType.AttributeValueOpening, true); + + currentTok.Start = CurrentPosition; + currentTok.Content = ReadLineUntil (new string (new char[]{ openAttVal })); + saveAndResetCurrentTok (TokenType.AttributeValue); + + if (Peek () != openAttVal) + throw new ParsingException (this, string.Format ("Expecting {0}", openAttVal)); + readAndResetCurrentTok (TokenType.AttributeValueClosing, true); + break; + default: + throw new ParsingException (this, "unexpected char: " + Peek ()); + } + break; + } + } + + if (cl.EndingState != (int)curState && currentLine < buffer.LineCount - 1) + buffer [currentLine + 1].Tokens = null; + + cl.EndingState = (int)curState; + } + + public override void SyntaxAnalysis () + { + RootNode = new Node () { Name = "RootNode", Type="Root" }; + + Node currentNode = RootNode; + + for (int i = 0; i < buffer.LineCount; i++) { + CodeLine cl = buffer[i]; + if (cl.Tokens == null) + continue; + cl.SyntacticNode = null; + + int tokPtr = 0; + while (tokPtr < cl.Tokens.Count) { + switch ((XMLParser.TokenType)cl.Tokens [tokPtr].Type) { + case TokenType.ElementStart: + tokPtr++; + Node newElt = new Node () { Name = cl.Tokens [tokPtr].Content, StartLine = cl }; + currentNode.AddChild (newElt); + currentNode = newElt; + if (cl.SyntacticNode == null) + cl.SyntacticNode = newElt; + break; + case TokenType.ElementEnd: + tokPtr++; + if (tokPtr < cl.Tokens.Count) { + if ((XMLParser.TokenType)cl.Tokens [tokPtr].Type == TokenType.ElementName && + cl.Tokens [tokPtr].Content != currentNode.Name) + throw new ParsingException (this, "Closing tag mismatch"); + } + currentNode.EndLine = cl; + currentNode = currentNode.Parent; + break; + case TokenType.ElementClosing: + //currentNode = currentNode.Parent; + break; + default: + break; + } + tokPtr++; + } + } + } + } +} + diff --git a/CrowIDE/src/VerticalLine.cs b/CrowIDE/src/VerticalLine.cs index 0cca19fd..03afeea8 100644 --- a/CrowIDE/src/VerticalLine.cs +++ b/CrowIDE/src/VerticalLine.cs @@ -26,7 +26,7 @@ using System; using Crow; -namespace CrowIDE +namespace Crow.Coding { public class VerticalLine : GraphicObject { diff --git a/CrowIDE/ui/CrowIDE.crow b/CrowIDE/ui/CrowIDE.crow index 079635fe..d963b5e5 100644 --- a/CrowIDE/ui/CrowIDE.crow +++ b/CrowIDE/ui/CrowIDE.crow @@ -33,16 +33,16 @@ - + + ItemTemplate="#Crow.Coding.ui.ProjectTree.template"/> + ItemTemplate="#Crow.Coding.ui.MembersItem.template"/> diff --git a/CrowIDE/ui/EditPane.template b/CrowIDE/ui/EditPane.template index 73644d60..b715b5be 100644 --- a/CrowIDE/ui/EditPane.template +++ b/CrowIDE/ui/EditPane.template @@ -1,2 +1,2 @@  - + diff --git a/CrowIDE/ui/EditPaneItems.template b/CrowIDE/ui/EditPaneItems.template index ebe6120f..4055d5ac 100644 --- a/CrowIDE/ui/EditPaneItems.template +++ b/CrowIDE/ui/EditPaneItems.template @@ -10,7 +10,7 @@ + ProjectNode="{}" KeyDown="textView_KeyDown"/> + Height="14" /> - + diff --git a/CrowIDE/ui/MemberView.crow b/CrowIDE/ui/MemberView.crow index 6cdfbf31..170a3fcb 100644 --- a/CrowIDE/ui/MemberView.crow +++ b/CrowIDE/ui/MemberView.crow @@ -1,5 +1,5 @@  + ItemTemplate="#Crow.Coding.ui.MembersItem.template"/> \ No newline at end of file diff --git a/CrowIDE/ui/ProjectProperties.crow b/CrowIDE/ui/ProjectProperties.crow index 1d1ec9f3..e4366b1e 100644 --- a/CrowIDE/ui/ProjectProperties.crow +++ b/CrowIDE/ui/ProjectProperties.crow @@ -60,7 +60,7 @@ + ItemTemplate="#Crow.Coding.ui.ProjectTree.template"> diff --git a/CrowIDE/ui/ProjectTree.template b/CrowIDE/ui/ProjectTree.template index 34a5cc84..41e5e3e7 100644 --- a/CrowIDE/ui/ProjectTree.template +++ b/CrowIDE/ui/ProjectTree.template @@ -2,7 +2,7 @@ - +