From: Jean-Philippe Bruyère Date: Sat, 26 Jun 2021 08:05:23 +0000 (+0200) Subject: new extensible editor with new parser, save commit X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=518c647ee98eb18bb8ed38b0b8f884c21e9844e1;p=jp%2Fcrowedit.git new extensible editor with new parser, save commit --- diff --git a/Crow.Coding/Crow.Coding.csproj b/Crow.Coding/Crow.Coding.csproj index 11bb5de..eeca2c0 100644 --- a/Crow.Coding/Crow.Coding.csproj +++ b/Crow.Coding/Crow.Coding.csproj @@ -11,6 +11,6 @@ - + \ No newline at end of file diff --git a/CrowEdit.csproj b/CrowEdit.csproj index afab445..7234e31 100644 --- a/CrowEdit.csproj +++ b/CrowEdit.csproj @@ -11,8 +11,11 @@ - - + + + + + diff --git a/CrowEditBase/CrowEditBase.csproj b/CrowEditBase/CrowEditBase.csproj new file mode 100644 index 0000000..1d442f5 --- /dev/null +++ b/CrowEditBase/CrowEditBase.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.1 + false + + + + + + + + + + + + \ No newline at end of file diff --git a/CrowEditBase/Properties/AssemblyInfo.cs b/CrowEditBase/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d864652 --- /dev/null +++ b/CrowEditBase/Properties/AssemblyInfo.cs @@ -0,0 +1,5 @@ +// Copyright (c) 2013-2020 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +[assembly: Crow.Crow ] + diff --git a/CrowEditBase/icons/blank-file.svg b/CrowEditBase/icons/blank-file.svg new file mode 100644 index 0000000..8136979 --- /dev/null +++ b/CrowEditBase/icons/blank-file.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/compile.svg b/CrowEditBase/icons/compile.svg new file mode 100644 index 0000000..c1a14e7 --- /dev/null +++ b/CrowEditBase/icons/compile.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/CrowEditBase/icons/copy-file.svg b/CrowEditBase/icons/copy-file.svg new file mode 100644 index 0000000..63c2dd3 --- /dev/null +++ b/CrowEditBase/icons/copy-file.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/curly-brackets.svg b/CrowEditBase/icons/curly-brackets.svg new file mode 100644 index 0000000..89ef798 --- /dev/null +++ b/CrowEditBase/icons/curly-brackets.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/edit.svg b/CrowEditBase/icons/edit.svg new file mode 100644 index 0000000..366862c --- /dev/null +++ b/CrowEditBase/icons/edit.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/eraser.svg b/CrowEditBase/icons/eraser.svg new file mode 100644 index 0000000..5dd73ba --- /dev/null +++ b/CrowEditBase/icons/eraser.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/file.svg b/CrowEditBase/icons/file.svg new file mode 100644 index 0000000..9d06b00 --- /dev/null +++ b/CrowEditBase/icons/file.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/folder.svg b/CrowEditBase/icons/folder.svg new file mode 100644 index 0000000..f59e2da --- /dev/null +++ b/CrowEditBase/icons/folder.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/CrowEditBase/icons/light-bulb.svg b/CrowEditBase/icons/light-bulb.svg new file mode 100644 index 0000000..89ff236 --- /dev/null +++ b/CrowEditBase/icons/light-bulb.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/move-arrows.svg b/CrowEditBase/icons/move-arrows.svg new file mode 100644 index 0000000..ecbb2f9 --- /dev/null +++ b/CrowEditBase/icons/move-arrows.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/open.svg b/CrowEditBase/icons/open.svg new file mode 100644 index 0000000..bd8d7d9 --- /dev/null +++ b/CrowEditBase/icons/open.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/paint-brush.svg b/CrowEditBase/icons/paint-brush.svg new file mode 100644 index 0000000..2bdd5be --- /dev/null +++ b/CrowEditBase/icons/paint-brush.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CrowEditBase/icons/palette.svg b/CrowEditBase/icons/palette.svg new file mode 100644 index 0000000..8e425f7 --- /dev/null +++ b/CrowEditBase/icons/palette.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/paste-on-document.svg b/CrowEditBase/icons/paste-on-document.svg new file mode 100644 index 0000000..b0a705e --- /dev/null +++ b/CrowEditBase/icons/paste-on-document.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/pin.svg b/CrowEditBase/icons/pin.svg new file mode 100644 index 0000000..b36340b --- /dev/null +++ b/CrowEditBase/icons/pin.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/question.svg b/CrowEditBase/icons/question.svg new file mode 100644 index 0000000..fb8e3d3 --- /dev/null +++ b/CrowEditBase/icons/question.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/redo.svg b/CrowEditBase/icons/redo.svg new file mode 100644 index 0000000..59fcc90 --- /dev/null +++ b/CrowEditBase/icons/redo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/save.svg b/CrowEditBase/icons/save.svg new file mode 100644 index 0000000..6aa6714 --- /dev/null +++ b/CrowEditBase/icons/save.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/scissors.svg b/CrowEditBase/icons/scissors.svg new file mode 100644 index 0000000..4b5a225 --- /dev/null +++ b/CrowEditBase/icons/scissors.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/search.svg b/CrowEditBase/icons/search.svg new file mode 100644 index 0000000..f3eb368 --- /dev/null +++ b/CrowEditBase/icons/search.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/text-file.svg b/CrowEditBase/icons/text-file.svg new file mode 100644 index 0000000..826bc32 --- /dev/null +++ b/CrowEditBase/icons/text-file.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Border.svg b/CrowEditBase/icons/toolbox/Crow.Border.svg new file mode 100644 index 0000000..09eb7ac --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Border.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Button.svg b/CrowEditBase/icons/toolbox/Crow.Button.svg new file mode 100644 index 0000000..01f5c6c --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Button.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.CheckBox.svg b/CrowEditBase/icons/toolbox/Crow.CheckBox.svg new file mode 100644 index 0000000..2f0b083 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.CheckBox.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.ColorPicker.svg b/CrowEditBase/icons/toolbox/Crow.ColorPicker.svg new file mode 100644 index 0000000..517a26a --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.ColorPicker.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.ComboBox.svg b/CrowEditBase/icons/toolbox/Crow.ComboBox.svg new file mode 100644 index 0000000..1cb88fa --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.ComboBox.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Container.svg b/CrowEditBase/icons/toolbox/Crow.Container.svg new file mode 100644 index 0000000..d7d1dc8 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Container.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.DirectoryView.svg b/CrowEditBase/icons/toolbox/Crow.DirectoryView.svg new file mode 100644 index 0000000..9029469 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.DirectoryView.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Docker.svg b/CrowEditBase/icons/toolbox/Crow.Docker.svg new file mode 100644 index 0000000..e38a283 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Docker.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Expandable.svg b/CrowEditBase/icons/toolbox/Crow.Expandable.svg new file mode 100644 index 0000000..1c56d56 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Expandable.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.FileDialog.svg b/CrowEditBase/icons/toolbox/Crow.FileDialog.svg new file mode 100644 index 0000000..25142ea --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.FileDialog.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Grid.svg b/CrowEditBase/icons/toolbox/Crow.Grid.svg new file mode 100644 index 0000000..6151f97 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Grid.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Group.svg b/CrowEditBase/icons/toolbox/Crow.Group.svg new file mode 100644 index 0000000..eae67f6 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Group.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.GroupBox.svg b/CrowEditBase/icons/toolbox/Crow.GroupBox.svg new file mode 100644 index 0000000..e469779 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.GroupBox.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.HorizontalStack.svg b/CrowEditBase/icons/toolbox/Crow.HorizontalStack.svg new file mode 100644 index 0000000..f8e7025 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.HorizontalStack.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.IMLContainer.svg b/CrowEditBase/icons/toolbox/Crow.IMLContainer.svg new file mode 100644 index 0000000..b5687ba --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.IMLContainer.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Image.svg b/CrowEditBase/icons/toolbox/Crow.Image.svg new file mode 100644 index 0000000..11356c0 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Image.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Label.svg b/CrowEditBase/icons/toolbox/Crow.Label.svg new file mode 100644 index 0000000..65bb85b --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Label.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.ListBox.svg b/CrowEditBase/icons/toolbox/Crow.ListBox.svg new file mode 100644 index 0000000..40d1673 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.ListBox.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Menu.svg b/CrowEditBase/icons/toolbox/Crow.Menu.svg new file mode 100644 index 0000000..b6b2111 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Menu.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.MenuItem.svg b/CrowEditBase/icons/toolbox/Crow.MenuItem.svg new file mode 100644 index 0000000..c8bd847 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.MenuItem.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.MessageBox.svg b/CrowEditBase/icons/toolbox/Crow.MessageBox.svg new file mode 100644 index 0000000..16ebd72 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.MessageBox.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.ProgressBar.svg b/CrowEditBase/icons/toolbox/Crow.ProgressBar.svg new file mode 100644 index 0000000..884f185 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.ProgressBar.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.RadioButton.svg b/CrowEditBase/icons/toolbox/Crow.RadioButton.svg new file mode 100644 index 0000000..6c33e84 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.RadioButton.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.ScrollBar.svg b/CrowEditBase/icons/toolbox/Crow.ScrollBar.svg new file mode 100644 index 0000000..91a1f84 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.ScrollBar.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Scroller.svg b/CrowEditBase/icons/toolbox/Crow.Scroller.svg new file mode 100644 index 0000000..bbc9719 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Scroller.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Shape.svg b/CrowEditBase/icons/toolbox/Crow.Shape.svg new file mode 100644 index 0000000..de5dd37 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Shape.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Slider.svg b/CrowEditBase/icons/toolbox/Crow.Slider.svg new file mode 100644 index 0000000..fe41c2b --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Slider.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Spinner.svg b/CrowEditBase/icons/toolbox/Crow.Spinner.svg new file mode 100644 index 0000000..5fa848a --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Spinner.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Splitter.svg b/CrowEditBase/icons/toolbox/Crow.Splitter.svg new file mode 100644 index 0000000..9fcd9dd --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Splitter.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.TabItem.svg b/CrowEditBase/icons/toolbox/Crow.TabItem.svg new file mode 100644 index 0000000..a9aaed3 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.TabItem.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.TabView.svg b/CrowEditBase/icons/toolbox/Crow.TabView.svg new file mode 100644 index 0000000..942358b --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.TabView.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.TemplatedContainer.svg b/CrowEditBase/icons/toolbox/Crow.TemplatedContainer.svg new file mode 100644 index 0000000..34a0aa2 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.TemplatedContainer.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.TemplatedGroup.svg b/CrowEditBase/icons/toolbox/Crow.TemplatedGroup.svg new file mode 100644 index 0000000..14ea6f3 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.TemplatedGroup.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.TextBox.svg b/CrowEditBase/icons/toolbox/Crow.TextBox.svg new file mode 100644 index 0000000..c1fc2bb --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.TextBox.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.TreeView.svg b/CrowEditBase/icons/toolbox/Crow.TreeView.svg new file mode 100644 index 0000000..b863291 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.TreeView.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.VerticalStack.svg b/CrowEditBase/icons/toolbox/Crow.VerticalStack.svg new file mode 100644 index 0000000..c3c3ec9 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.VerticalStack.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Window.svg b/CrowEditBase/icons/toolbox/Crow.Window.svg new file mode 100644 index 0000000..74b0a24 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Window.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/Crow.Wrapper.svg b/CrowEditBase/icons/toolbox/Crow.Wrapper.svg new file mode 100644 index 0000000..9e69e41 --- /dev/null +++ b/CrowEditBase/icons/toolbox/Crow.Wrapper.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/bar-chart.svg b/CrowEditBase/icons/toolbox/bar-chart.svg new file mode 100644 index 0000000..ff86c96 --- /dev/null +++ b/CrowEditBase/icons/toolbox/bar-chart.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/bar-menu.svg b/CrowEditBase/icons/toolbox/bar-menu.svg new file mode 100644 index 0000000..87ec061 --- /dev/null +++ b/CrowEditBase/icons/toolbox/bar-menu.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/bullets.svg b/CrowEditBase/icons/toolbox/bullets.svg new file mode 100644 index 0000000..81fb1f0 --- /dev/null +++ b/CrowEditBase/icons/toolbox/bullets.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/calendar.svg b/CrowEditBase/icons/toolbox/calendar.svg new file mode 100644 index 0000000..9ceaa1e --- /dev/null +++ b/CrowEditBase/icons/toolbox/calendar.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/check-square-1.svg b/CrowEditBase/icons/toolbox/check-square-1.svg new file mode 100644 index 0000000..e198007 --- /dev/null +++ b/CrowEditBase/icons/toolbox/check-square-1.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/toolbox/database.svg b/CrowEditBase/icons/toolbox/database.svg new file mode 100644 index 0000000..65a8f06 --- /dev/null +++ b/CrowEditBase/icons/toolbox/database.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/ellipsis.svg b/CrowEditBase/icons/toolbox/ellipsis.svg new file mode 100644 index 0000000..cff94cc --- /dev/null +++ b/CrowEditBase/icons/toolbox/ellipsis.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/file-code.svg b/CrowEditBase/icons/toolbox/file-code.svg new file mode 100644 index 0000000..a2fd2d1 --- /dev/null +++ b/CrowEditBase/icons/toolbox/file-code.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/grab.svg b/CrowEditBase/icons/toolbox/grab.svg new file mode 100644 index 0000000..25bc571 --- /dev/null +++ b/CrowEditBase/icons/toolbox/grab.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/hash.svg b/CrowEditBase/icons/toolbox/hash.svg new file mode 100644 index 0000000..82196fb --- /dev/null +++ b/CrowEditBase/icons/toolbox/hash.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/line-list.svg b/CrowEditBase/icons/toolbox/line-list.svg new file mode 100644 index 0000000..ec15f7b --- /dev/null +++ b/CrowEditBase/icons/toolbox/line-list.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/list.svg b/CrowEditBase/icons/toolbox/list.svg new file mode 100644 index 0000000..9aad88f --- /dev/null +++ b/CrowEditBase/icons/toolbox/list.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/modal-list.svg b/CrowEditBase/icons/toolbox/modal-list.svg new file mode 100644 index 0000000..f1d8f70 --- /dev/null +++ b/CrowEditBase/icons/toolbox/modal-list.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/options.svg b/CrowEditBase/icons/toolbox/options.svg new file mode 100644 index 0000000..a56f6be --- /dev/null +++ b/CrowEditBase/icons/toolbox/options.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/package.svg b/CrowEditBase/icons/toolbox/package.svg new file mode 100644 index 0000000..07f8b37 --- /dev/null +++ b/CrowEditBase/icons/toolbox/package.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/padding.svg b/CrowEditBase/icons/toolbox/padding.svg new file mode 100644 index 0000000..d93b246 --- /dev/null +++ b/CrowEditBase/icons/toolbox/padding.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/picture-file.svg b/CrowEditBase/icons/toolbox/picture-file.svg new file mode 100644 index 0000000..c45a6ad --- /dev/null +++ b/CrowEditBase/icons/toolbox/picture-file.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/pointer.svg b/CrowEditBase/icons/toolbox/pointer.svg new file mode 100644 index 0000000..605c0e2 --- /dev/null +++ b/CrowEditBase/icons/toolbox/pointer.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/puzzle-piece.svg b/CrowEditBase/icons/toolbox/puzzle-piece.svg new file mode 100644 index 0000000..b09c47e --- /dev/null +++ b/CrowEditBase/icons/toolbox/puzzle-piece.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/refresh-file.svg b/CrowEditBase/icons/toolbox/refresh-file.svg new file mode 100644 index 0000000..248e420 --- /dev/null +++ b/CrowEditBase/icons/toolbox/refresh-file.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/sliders.svg b/CrowEditBase/icons/toolbox/sliders.svg new file mode 100644 index 0000000..81f72d1 --- /dev/null +++ b/CrowEditBase/icons/toolbox/sliders.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/split-browser-1.svg b/CrowEditBase/icons/toolbox/split-browser-1.svg new file mode 100644 index 0000000..4dfd93a --- /dev/null +++ b/CrowEditBase/icons/toolbox/split-browser-1.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/table.svg b/CrowEditBase/icons/toolbox/table.svg new file mode 100644 index 0000000..0b42122 --- /dev/null +++ b/CrowEditBase/icons/toolbox/table.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/toolbox/tasks.svg b/CrowEditBase/icons/toolbox/tasks.svg new file mode 100644 index 0000000..8793c88 --- /dev/null +++ b/CrowEditBase/icons/toolbox/tasks.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/CrowEditBase/icons/toolbox/warning.svg b/CrowEditBase/icons/toolbox/warning.svg new file mode 100644 index 0000000..f5a2573 --- /dev/null +++ b/CrowEditBase/icons/toolbox/warning.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/tools.svg b/CrowEditBase/icons/tools.svg new file mode 100644 index 0000000..5ad8a8d --- /dev/null +++ b/CrowEditBase/icons/tools.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CrowEditBase/icons/trash.svg b/CrowEditBase/icons/trash.svg new file mode 100644 index 0000000..e73a5e3 --- /dev/null +++ b/CrowEditBase/icons/trash.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/undo.svg b/CrowEditBase/icons/undo.svg new file mode 100644 index 0000000..f78f134 --- /dev/null +++ b/CrowEditBase/icons/undo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/icons/zoom-in.svg b/CrowEditBase/icons/zoom-in.svg new file mode 100644 index 0000000..540a93b --- /dev/null +++ b/CrowEditBase/icons/zoom-in.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/icons/zoom-out.svg b/CrowEditBase/icons/zoom-out.svg new file mode 100644 index 0000000..6bd256f --- /dev/null +++ b/CrowEditBase/icons/zoom-out.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/images/save.svg b/CrowEditBase/images/save.svg new file mode 100644 index 0000000..7bdc551 --- /dev/null +++ b/CrowEditBase/images/save.svg @@ -0,0 +1,3421 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/SourceDocument.cs b/CrowEditBase/src/Compiler/SourceDocument.cs new file mode 100644 index 0000000..6a9cee2 --- /dev/null +++ b/CrowEditBase/src/Compiler/SourceDocument.cs @@ -0,0 +1,100 @@ +// Copyright (c) 2021-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Linq; +using System.Collections.Generic; +using System.IO; +using Crow; +using Crow.Text; +using System.Diagnostics; +using System.Collections; + +namespace CrowEditBase +{ + public abstract class SourceDocument : TextDocument { + public SourceDocument (Interface iFace, string fullPath) + : base (iFace, fullPath) { + } + protected Token[] tokens; + protected SyntaxNode RootNode; + public Token[] Tokens => tokens; + public Token FindTokenIncludingPosition (int pos) { + if (pos == 0 || tokens == null || tokens.Length == 0) + return default; + int idx = Array.BinarySearch (tokens, 0, tokens.Length, new Token () {Start = pos}); + + return idx == 0 ? tokens[0] : idx < 0 ? tokens[~idx - 1] : tokens[idx - 1]; + } + public SyntaxNode FindNodeIncludingPosition (int pos) { + if (RootNode == null) + return null; + if (!RootNode.Contains (pos)) + return null; + return RootNode.FindNodeIncludingPosition (pos); + } + public T FindNodeIncludingPosition (int pos) { + if (RootNode == null) + return default; + if (!RootNode.Contains (pos)) + return default; + return RootNode.FindNodeIncludingPosition (pos); + } + protected override void reloadFromFile () { + base.reloadFromFile (); + parse (); + } + protected override void apply(TextChange change) + { + base.apply(change); + parse (); + } + + protected abstract Tokenizer CreateTokenizer (); + protected abstract SyntaxAnalyser CreateSyntaxAnalyser (); + void parse () { + Tokenizer tokenizer = CreateTokenizer (); + tokens = tokenizer.Tokenize (Source); + + SyntaxAnalyser syntaxAnalyser = CreateSyntaxAnalyser (); + Stopwatch sw = Stopwatch.StartNew (); + syntaxAnalyser.Process (); + sw.Stop(); + RootNode = syntaxAnalyser.Root; + + Console.WriteLine ($"Syntax Analysis done in {sw.ElapsedMilliseconds}(ms) {sw.ElapsedTicks}(ticks)"); + foreach (SyntaxException ex in syntaxAnalyser.Exceptions) + Console.WriteLine ($"{ex}"); + + /*foreach (Token t in Tokens) + Console.WriteLine ($"{t,-40} {Source.AsSpan(t.Start, t.Length).ToString()}"); + syntaxAnalyser.Root.Dump();*/ + } + public virtual Crow.Color GetColorForToken (TokenType tokType) { + if (tokType.HasFlag (TokenType.Punctuation)) + return Colors.DarkGrey; + if (tokType.HasFlag (TokenType.Trivia)) + return Colors.DimGrey; + if (tokType == TokenType.Keyword) + return Colors.DarkSlateBlue; + return Colors.Red; + } + protected Token currentToken; + protected SyntaxNode currentNode; + public abstract IList GetSuggestions (int pos); + /// + /// + /// + /// + + /// + /// complete current token with selected item from the suggestion overlay. + /// It may set a new position or a new selection. + /// + /// selected object of suggestion overlay + /// new position or selection, null if normal position after text changes + /// the TextChange to apply to the source + public abstract TextChange? GetCompletionForCurrentToken (object suggestion, out TextSpan? newSelection); + } +} \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/SyntaxAnalyser.cs b/CrowEditBase/src/Compiler/SyntaxAnalyser.cs new file mode 100644 index 0000000..f58f67d --- /dev/null +++ b/CrowEditBase/src/Compiler/SyntaxAnalyser.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2021-2021 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using System.Collections.Generic; +using System.Linq; + +namespace CrowEditBase +{ + public class SyntaxException : Exception { + public readonly Token Token; + public SyntaxException(string message, Token token = default, Exception innerException = null) + : base (message, innerException) { + Token = token; + } + } + public abstract class SyntaxAnalyser { + protected SourceDocument source; + public abstract SyntaxNode Root { get; } + public List Exceptions { get; protected set; } + public SyntaxAnalyser (SourceDocument source) { + this.source = source; + } + public abstract void Process (); + } +} \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/SyntaxNode.cs b/CrowEditBase/src/Compiler/SyntaxNode.cs new file mode 100644 index 0000000..37c8dc2 --- /dev/null +++ b/CrowEditBase/src/Compiler/SyntaxNode.cs @@ -0,0 +1,60 @@ +// Copyright (c) 2021-2021 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using System.Collections.Generic; +using System.Linq; + +namespace CrowEditBase +{ + public class SyntaxNode { + public SyntaxNode Parent { get; private set; } + List children = new List (); + + public readonly Token StartToken; + public Token? EndToken { get; internal set; } + public SyntaxNode (Token tokStart, Token? tokEnd = null) { + StartToken = tokStart; + EndToken = tokEnd; + } + + public virtual bool IsComplete => EndToken.HasValue; + + internal SyntaxNode AddChild (SyntaxNode child) { + children.Add (child); + child.Parent = this; + return child; + } + internal void RemoveChild (SyntaxNode child) { + children.Remove (child); + child.Parent = null; + } + public T GetChild () => children.OfType ().FirstOrDefault (); + public SyntaxNode FindNodeIncludingPosition (int pos) { + foreach (SyntaxNode node in children) { + if (node.Contains (pos)) + return node.FindNodeIncludingPosition (pos); + } + return this; + } + public T FindNodeIncludingPosition (int pos) { + foreach (SyntaxNode node in children) { + if (node.Contains (pos)) + return node.FindNodeIncludingPosition (pos); + } + + return this is T tt ? tt : default; + } + public virtual SyntaxNode Root => Parent.Root; + public bool Contains (int pos) => + EndToken.HasValue ? + StartToken.Start <= pos && EndToken.Value.End >= pos : false; + + public void Dump (int level = 0) { + Console.WriteLine ($"{new string('\t', level)}{this}"); + foreach (SyntaxNode node in children) + node.Dump (level + 1); + } + public override string ToString() => $"{this.GetType().Name}: {StartToken} -> {EndToken}"; + } +} \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/Token.cs b/CrowEditBase/src/Compiler/Token.cs new file mode 100644 index 0000000..86354ac --- /dev/null +++ b/CrowEditBase/src/Compiler/Token.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2021-2021 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using Crow.Text; +using System.Diagnostics.CodeAnalysis; + +namespace CrowEditBase +{ + public struct Token : IComparable, IEquatable { + public TokenType Type; + public int Start; + public int Length; + public int End => Start + Length; + public TextSpan Span => new TextSpan (Start, End); + public string AsString (ReadOnlySpan source) + => source.Slice (Start, Length).ToString(); + + public Token (TokenType type, int pos) { + Type = type; + Start = pos; + Length = 1; + } + public Token (TokenType type, int start, int end) { + Type = type; + Start = start; + Length = end - start; + } + + public int CompareTo([AllowNull] Token other) + => Start - other.Start; + public bool Equals([AllowNull] Token other) + => Type == other.Type && Start == other.Start && Length == other.Length; + public override bool Equals(object obj) + => obj is Token other ? Equals (other) : false; + public override int GetHashCode() => HashCode.Combine (Type, Start, Length); + public override string ToString() => $"{Type}:{Start},{Length};"; + } +} \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/TokenType.cs b/CrowEditBase/src/Compiler/TokenType.cs new file mode 100644 index 0000000..e35993c --- /dev/null +++ b/CrowEditBase/src/Compiler/TokenType.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2013-2021 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; + +namespace CrowEditBase +{ + [Flags] + public enum TokenType { + Unknown, + Trivia = 0x0100, + WhiteSpace = 0x4100, + Tabulation = 0x4101, + LineBreak = 0x4102, + LineComment = 0x0103, + BlockCommentStart = 0x0104, + BlockComment = 0x0105, + BlockCommentEnd = 0x0106, + Name = 0x0200, + Punctuation = 0x0400, + Operator = 0x0800, + Keyword = 0x1000, + } +} \ No newline at end of file diff --git a/CrowEditBase/src/Compiler/Tokenizer.cs b/CrowEditBase/src/Compiler/Tokenizer.cs new file mode 100644 index 0000000..80cc2a4 --- /dev/null +++ b/CrowEditBase/src/Compiler/Tokenizer.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2013-2021 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +namespace CrowEditBase +{ + public abstract class Tokenizer { + + public Tokenizer () {} + public abstract Token[] Tokenize (string source); + } +} diff --git a/CrowEditBase/src/Document.cs b/CrowEditBase/src/Document.cs new file mode 100644 index 0000000..57e0b67 --- /dev/null +++ b/CrowEditBase/src/Document.cs @@ -0,0 +1,105 @@ +// Copyright (c) 2021-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.IO; +using System.Threading; +using Crow; +using System.Runtime.CompilerServices; + +namespace CrowEditBase +{ + public abstract class Document : IValueChange, ISelectable { + #region IValueChange implementation + public event EventHandler ValueChanged; + public void NotifyValueChanged (string MemberName, object _value) + { + //Debug.WriteLine ("Value changed: {0}->{1} = {2}", this, MemberName, _value); + ValueChanged.Raise (this, new ValueChangeEventArgs (MemberName, _value)); + } + public void NotifyValueChanged (object _value, [CallerMemberName] string caller = null) + { + NotifyValueChanged (caller, _value); + } + #endregion + + #region ISelectable implementation + public event EventHandler Selected; + public event EventHandler Unselected; + public virtual bool IsSelected { + get { return isSelected; } + set { + if (value == isSelected) + return; + + isSelected = value; + + NotifyValueChanged ("IsSelected", isSelected); + } + } + public void SelectDocument () => IsSelected = true; + public void UnselectDocument () => IsSelected = true; + #endregion + public Document (Interface iFace, string fullPath) { + this.iFace = iFace; + initCommands (); + FullPath = fullPath; + } + protected Interface iFace; + public event EventHandler CloseEvent; + + protected ReaderWriterLockSlim editorRWLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); + public void EnterReadLock () => editorRWLock.EnterReadLock (); + public void ExitReadLock () => editorRWLock.ExitReadLock (); + + public abstract bool TryGetState (object client, out T state); + public abstract void RegisterClient (object client); + public abstract void UnregisterClient (object client); + + DateTime accessTime; + string fullPath; + bool isSelected; + + public string FullPath { + get => fullPath; + set { + if (fullPath == value) + return; + + fullPath = value; + + NotifyValueChanged (fullPath); + NotifyValueChanged ("FileName", (object)FileName); + NotifyValueChanged ("FileDirectory", (object)Extension); + NotifyValueChanged ("Extension", (object)Extension); + } + } + public string FileDirectory => System.IO.Path.GetDirectoryName (FullPath); + public string FileName => System.IO.Path.GetFileName (FullPath); + public string Extension => System.IO.Path.GetExtension (FullPath); + public bool ExternalyModified => File.Exists (FullPath) ? + (DateTime.Compare (accessTime, System.IO.File.GetLastWriteTime (FullPath)) < 0) : false; + public void OnQueryClose (object sender, EventArgs e){ + CloseEvent.Raise (this, null); + } + protected abstract void initCommands (); + protected abstract void writeToDisk (); + protected abstract void readFromDisk (); + protected abstract void initNewFile (); + protected virtual void reloadFromFile () { + editorRWLock.EnterWriteLock (); + try { + if (File.Exists (FullPath)) + readFromDisk (); + else + initNewFile (); + } finally { + editorRWLock.ExitWriteLock (); + } + } + public abstract bool IsDirty { get; } + + public override string ToString() => FullPath; + } +} \ No newline at end of file diff --git a/CrowEditBase/src/Editor.cs b/CrowEditBase/src/Editor.cs new file mode 100644 index 0000000..4f32829 --- /dev/null +++ b/CrowEditBase/src/Editor.cs @@ -0,0 +1,119 @@ +// Copyright (c) 2013-2021 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using Glfw; +using Crow.Text; +using System.Collections.Generic; +using Crow.Cairo; +using System.Threading.Tasks; +using System.Linq; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Collections; +using CrowEditBase; +using System.Threading; + +namespace Crow +{ + public interface IDocumentClient { + + } + public class Editor : TextBox { + #region CTOR + protected Editor () : base () { + initCommands (); + + Thread t = new Thread (backgroundThreadFunc); + t.IsBackground = true; + t.Start (); + } + #endregion + TextDocument document; + protected bool disableTextChangedEvent; + + public Command CMDCut, CMDCopy, CMDPaste; + void initCommands () { + CMDCut = new Command ("Cut", Cut, "#CrowEditBase.ui.icons.scissors.svg", false); + CMDCopy = new Command ("Copy", Copy, "#CrowEditBase.ui.icons.copy-file.svg", false); + CMDPaste = new Command ("Paste", Paste, "#CrowEditBase.ui.icons.paste-on-document.svg", true); + + ContextCommands = new CommandGroup (CMDCut, CMDCopy, CMDPaste); + } + public override int CurrentColumn { + get => base.CurrentColumn; + set { + if (CurrentColumn == value) + return; + base.CurrentColumn = value; + CMDCopy.CanExecute = CMDCut.CanExecute = !SelectionIsEmpty; + } + } + public override int CurrentLine { + get => base.CurrentLine; + set { + if (CurrentLine == value) + return; + base.CurrentLine = value; + CMDCopy.CanExecute = CMDCut.CanExecute = !SelectionIsEmpty; + } + } + protected override CharLocation? CurrentLoc { + get => base.CurrentLoc; + set { + if (currentLoc == value) + return; + base.CurrentLoc = value; + CMDCopy.CanExecute = CMDCut.CanExecute = !SelectionIsEmpty; + } + } + /*protected override CharLocation? SelectionStart { + get => base.SelectionStart; + set { + if (SelectionStart == value) + return; + base.SelectionStart = value; + CMDCopy.CanExecute = CMDCut.CanExecute = !SelectionIsEmpty; + } + }*/ + + + public TextDocument Document { + get => document; + set { + if (document == value) + return; + + document?.UnregisterClient (this); + document = value; + document?.RegisterClient (this); + + NotifyValueChangedAuto (document); + RegisterForGraphicUpdate (); + } + } + public override void OnTextChanged(object sender, TextChangeEventArgs e) + { + if (disableTextChangedEvent) + return; + base.OnTextChanged(sender, e); + } + protected override void onFocused(object sender, EventArgs e) + { + base.onFocused(sender, e); + IFace.NotifyValueChanged ("CurrentEditor", this); + } + protected void backgroundThreadFunc () { + while (true) { + if (Document != null && document.TryGetState (this, out List changes)) { + disableTextChangedEvent = true; + foreach (TextChange tc in changes) + update (tc); + disableTextChangedEvent = false; + } + Thread.Sleep (200); + } + } + } +} \ No newline at end of file diff --git a/CrowEditBase/src/ImlParsing/SyntaxAnalyser.cs b/CrowEditBase/src/ImlParsing/SyntaxAnalyser.cs new file mode 100644 index 0000000..9317854 --- /dev/null +++ b/CrowEditBase/src/ImlParsing/SyntaxAnalyser.cs @@ -0,0 +1,133 @@ +// Copyright (c) 2021-2021 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using System.Collections.Generic; +using System.Linq; +using CrowEditBase; + +namespace CrowEdit.Xml +{ + public class XmlSyntaxAnalyser : SyntaxAnalyser { + public override SyntaxNode Root => CurrentNode; + public XmlSyntaxAnalyser (XmlDocument source) : base (source) { + this.source = source; + } + + SyntaxNode CurrentNode; + Token previousTok; + IEnumerator iter; + + public override void Process () { + XmlDocument xmlDoc = source as XmlDocument; + Exceptions = new List (); + CurrentNode = new IMLRootSyntax (xmlDoc); + previousTok = default; + iter = xmlDoc.Tokens.AsEnumerable().GetEnumerator (); + + bool notEndOfSource = iter.MoveNext (); + while (notEndOfSource) { + if (!iter.Current.Type.HasFlag (TokenType.Trivia)) { + if (CurrentNode is ElementStartTagSyntax tag) { + if (iter.Current.GetTokenType() == XmlTokenType.AttributeName) { + AttributeSyntax attribute = new AttributeSyntax (iter.Current); + attribute.NameToken = iter.Current; + CurrentNode = CurrentNode.AddChild (attribute); + } else if (iter.Current.GetTokenType() == XmlTokenType.ElementName) + tag.NameToken = iter.Current; + else if (iter.Current.GetTokenType() == XmlTokenType.ClosingSign) { + tag.EndToken = iter.Current; + CurrentNode = tag.Parent; + CurrentNode.RemoveChild (tag); + CurrentNode = CurrentNode.AddChild (new ElementSyntax (tag)); + } else if (iter.Current.GetTokenType() == XmlTokenType.EmptyElementClosing) { + tag.EndToken = iter.Current; + CurrentNode = tag.Parent; + CurrentNode.RemoveChild (tag); + CurrentNode.AddChild (new EmptyElementSyntax (tag)); + } else { + Exceptions.Add (new SyntaxException ("Unexpected Token", iter.Current)); + CurrentNode.EndToken = previousTok; + CurrentNode = CurrentNode.Parent; + continue; + } + } else if (CurrentNode is ElementSyntax elt) { + if (iter.Current.GetTokenType() == XmlTokenType.ElementOpen) + CurrentNode = CurrentNode.AddChild (new ElementStartTagSyntax (iter.Current)); + else if (iter.Current.GetTokenType() == XmlTokenType.EndElementOpen) { + elt.EndTag = new ElementEndTagSyntax (iter.Current); + CurrentNode = elt.AddChild (elt.EndTag); + } + } else if (CurrentNode is AttributeSyntax attrib) { + if (iter.Current.GetTokenType() == XmlTokenType.EqualSign) + if (attrib.EqualToken.HasValue) + Exceptions.Add (new SyntaxException ("Extra equal sign in attribute syntax", iter.Current)); + else + attrib.EqualToken = iter.Current; + else if (iter.Current.GetTokenType() == XmlTokenType.AttributeValueOpen) + attrib.ValueOpenToken = iter.Current; + else if (iter.Current.GetTokenType() == XmlTokenType.AttributeValue) + attrib.ValueToken = iter.Current; + else if (iter.Current.GetTokenType() == XmlTokenType.AttributeValueClose) { + attrib.ValueCloseToken = attrib.EndToken = iter.Current; + CurrentNode = CurrentNode.Parent; + } else { + Exceptions.Add (new SyntaxException ("Unexpected Token", iter.Current)); + CurrentNode.EndToken = previousTok; + CurrentNode = CurrentNode.Parent; + continue; + } + } else if (CurrentNode is ElementEndTagSyntax eltEndTag) { + if (iter.Current.GetTokenType() == XmlTokenType.ElementName) + eltEndTag.NameToken = iter.Current; + else if (iter.Current.GetTokenType() == XmlTokenType.ClosingSign) { + eltEndTag.EndToken = eltEndTag.Parent.EndToken = iter.Current; + CurrentNode = eltEndTag.Parent.Parent; + } else { + Exceptions.Add (new SyntaxException ("Unexpected Token", iter.Current)); + eltEndTag.EndToken = eltEndTag.Parent.EndToken = previousTok; + CurrentNode = CurrentNode.Parent.Parent; + continue; + } + } else if (CurrentNode is IMLRootSyntax) { + switch (iter.Current.GetTokenType()) { + case XmlTokenType.ElementOpen: + CurrentNode = CurrentNode.AddChild (new ElementStartTagSyntax (iter.Current)); + break; + case XmlTokenType.PI_Start: + CurrentNode = CurrentNode.AddChild (new ProcessingInstructionSyntax (iter.Current)); + break; + default: + Exceptions.Add (new SyntaxException ("Unexpected Token", iter.Current)); + break; + } + } else if (CurrentNode is ProcessingInstructionSyntax pi) { + if (iter.Current.GetTokenType() == XmlTokenType.PI_Target) + pi.NameToken = iter.Current; + else if (iter.Current.GetTokenType() == XmlTokenType.PI_End) { + pi.EndToken = iter.Current; + CurrentNode = CurrentNode.Parent; + } else if (iter.Current.GetTokenType() == XmlTokenType.AttributeName) { + AttributeSyntax attribute = new AttributeSyntax (iter.Current); + attribute.NameToken = iter.Current; + CurrentNode = CurrentNode.AddChild (attribute); + } else { + Exceptions.Add (new SyntaxException ("Unexpected Token", iter.Current)); + pi.EndToken = previousTok; + CurrentNode = CurrentNode.Parent; + continue; + } + } + } + + previousTok = iter.Current; + notEndOfSource = iter.MoveNext (); + } + while (CurrentNode.Parent != null) { + if (!CurrentNode.EndToken.HasValue) + CurrentNode.EndToken = previousTok; + CurrentNode = CurrentNode.Parent; + } + } + } +} \ No newline at end of file diff --git a/CrowEditBase/src/ImlParsing/SyntaxNodes.cs b/CrowEditBase/src/ImlParsing/SyntaxNodes.cs new file mode 100644 index 0000000..f693ec3 --- /dev/null +++ b/CrowEditBase/src/ImlParsing/SyntaxNodes.cs @@ -0,0 +1,81 @@ +// Copyright (c) 2013-2021 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using System.Collections.Generic; +using System.Linq; + +using CrowEditBase; + +namespace CrowEdit.Xml +{ + + public class IMLRootSyntax : SyntaxNode { + internal readonly XmlDocument source; + public override SyntaxNode Root => this; + public IMLRootSyntax (XmlDocument source) + : base (source.Tokens.FirstOrDefault (), source.Tokens.LastOrDefault ()) { + this.source = source; + } + } + public class ProcessingInstructionSyntax : SyntaxNode { + public Token PIStartToken => StartToken; + public Token? PIEndToken => EndToken.HasValue && EndToken.Value.GetTokenType() == XmlTokenType.PI_End ? EndToken : null; + public Token? NameToken { get; internal set; } + public override bool IsComplete => base.IsComplete & NameToken.HasValue; + public ProcessingInstructionSyntax (Token startTok) + : base (startTok) { + } + } + + public abstract class ElementTagSyntax : SyntaxNode { + public Token OpenToken => StartToken; + public Token? NameToken { get; internal set; } + public Token? CloseToken => EndToken.HasValue && EndToken.Value.GetTokenType() == XmlTokenType.ClosingSign ? EndToken : null; + public override bool IsComplete => base.IsComplete & NameToken.HasValue & CloseToken.HasValue; + protected ElementTagSyntax (Token startTok) + : base (startTok) { + } + } + public class ElementStartTagSyntax : ElementTagSyntax { + public ElementStartTagSyntax (Token startTok) + : base (startTok) { + } + } + public class ElementEndTagSyntax : ElementTagSyntax { + public ElementEndTagSyntax (Token startTok) + : base (startTok) { + } + } + + public class EmptyElementSyntax : SyntaxNode { + public readonly ElementStartTagSyntax StartTag; + public EmptyElementSyntax (ElementStartTagSyntax startNode) : base (startNode.StartToken, startNode.EndToken) { + StartTag = startNode; + AddChild (StartTag); + } + } + + public class ElementSyntax : SyntaxNode { + public readonly ElementStartTagSyntax StartTag; + public ElementEndTagSyntax EndTag { get; internal set; } + + public override bool IsComplete => base.IsComplete & StartTag.IsComplete & (EndTag != null && EndTag.IsComplete); + + public ElementSyntax (ElementStartTagSyntax startTag) + : base (startTag.StartToken) { + StartTag = startTag; + AddChild (StartTag); + } + } + + public class AttributeSyntax : SyntaxNode { + public Token? NameToken { get; internal set; } + public Token? EqualToken { get; internal set; } + public Token? ValueOpenToken { get; internal set; } + public Token? ValueCloseToken { get; internal set; } + public Token? ValueToken { get; internal set; } + public AttributeSyntax (Token startTok) : base (startTok) {} + public override bool IsComplete => base.IsComplete & NameToken.HasValue & EqualToken.HasValue & ValueToken.HasValue & ValueOpenToken.HasValue & ValueCloseToken.HasValue; + } +} \ No newline at end of file diff --git a/CrowEditBase/src/ImlParsing/XmlDocument.cs b/CrowEditBase/src/ImlParsing/XmlDocument.cs new file mode 100644 index 0000000..ca07214 --- /dev/null +++ b/CrowEditBase/src/ImlParsing/XmlDocument.cs @@ -0,0 +1,177 @@ +// Copyright (c) 2013-2021 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Linq; +using Crow.Text; +using System.Collections.Generic; +using System.Diagnostics; +using Crow; +using IML = Crow.IML; +using System.Collections; +using System.Reflection; +using CrowEditBase; + +namespace CrowEdit.Xml +{ + public static class Extensions { + public static XmlTokenType GetTokenType (this Token tok) { + return (XmlTokenType)tok.Type; + } + public static void SetTokenType (this Token tok, XmlTokenType type) { + tok.Type = (TokenType)type; + } + } + public class XmlDocument : SourceDocument { + + public XmlDocument (Interface iFace, string fullPath) + : base (iFace, fullPath) { + + } + protected override Tokenizer CreateTokenizer() => new XmlTokenizer (); + protected override SyntaxAnalyser CreateSyntaxAnalyser() => new XmlSyntaxAnalyser (this); + + string[] allWidgetNames = typeof (Widget).Assembly.GetExportedTypes ().Where(t=>typeof(Widget).IsAssignableFrom (t)) + .Select (s => s.Name).ToArray (); + + + IEnumerable getAllCrowTypeMembers (string crowTypeName) { + Type crowType = IML.Instantiator.GetWidgetTypeFromName (crowTypeName); + return crowType.GetMembers (BindingFlags.Public | BindingFlags.Instance). + Where (m=>((m is PropertyInfo pi && pi.CanWrite) || (m is EventInfo)) && + m.GetCustomAttribute() == null); + } + MemberInfo getCrowTypeMember (string crowTypeName, string memberName) { + Type crowType = IML.Instantiator.GetWidgetTypeFromName (crowTypeName); + return crowType.GetMember (memberName, BindingFlags.Public | BindingFlags.Instance).FirstOrDefault (); + } + public override IList GetSuggestions (int pos) { + currentToken = FindTokenIncludingPosition (pos); + currentNode = FindNodeIncludingPosition (pos); +#if DEBUG + Console.WriteLine ($"Current Token: {currentToken} Current Node: {currentNode}"); +#endif + + if (currentToken.GetTokenType() == XmlTokenType.ElementOpen) + return new List (allWidgetNames); + if (currentToken.GetTokenType() == XmlTokenType.ElementName) + return allWidgetNames.Where (s => s.StartsWith (currentToken.AsString (Source), StringComparison.OrdinalIgnoreCase)).ToList (); + if (currentNode is AttributeSyntax attribNode) { + if (currentNode.Parent is ElementTagSyntax eltTag) { + if (eltTag.NameToken.HasValue) { + if (currentToken.GetTokenType() == XmlTokenType.AttributeName) { + return getAllCrowTypeMembers (eltTag.NameToken.Value.AsString (Source)) + .Where (s => s.Name.StartsWith (currentToken.AsString (Source), StringComparison.OrdinalIgnoreCase)).ToList (); + } else if (attribNode.NameToken.HasValue) { + if (currentToken.GetTokenType() == XmlTokenType.AttributeValue) { + MemberInfo mi = getCrowTypeMember ( + eltTag.NameToken.Value.AsString (Source), attribNode.NameToken.Value.AsString (Source)); + if (mi is PropertyInfo pi) { + if (pi.Name == "Style") + return iFace.Styling.Keys + .Where (s => s.StartsWith (currentToken.AsString (Source), StringComparison.OrdinalIgnoreCase)).ToList (); + if (pi.PropertyType.IsEnum) + return Enum.GetNames (pi.PropertyType) + .Where (s => s.StartsWith (currentToken.AsString (Source), StringComparison.OrdinalIgnoreCase)).ToList (); + if (pi.PropertyType == typeof(bool)) + return (new string[] {"true", "false"}). + Where (s => s.StartsWith (currentToken.AsString (Source), StringComparison.OrdinalIgnoreCase)).ToList (); + if (pi.PropertyType == typeof (Measure)) + return (new string[] {"Stretched", "Fit"}). + Where (s => s.StartsWith (currentToken.AsString (Source), StringComparison.OrdinalIgnoreCase)).ToList (); + if (pi.PropertyType == typeof (Fill)) + return FastEnumUtility.FastEnum.GetValues () + .Where (s => s.ToString().StartsWith (currentToken.AsString (Source), StringComparison.OrdinalIgnoreCase)).ToList (); + } + } else if (currentToken.GetTokenType() == XmlTokenType.AttributeValueOpen) { + MemberInfo mi = getCrowTypeMember ( + eltTag.NameToken.Value.AsString (Source), attribNode.NameToken.Value.AsString (Source)); + if (mi is PropertyInfo pi) { + if (pi.Name == "Style") + return iFace.Styling.Keys.ToList (); + if (pi.PropertyType.IsEnum) + return Enum.GetNames (pi.PropertyType).ToList (); + if (pi.PropertyType == typeof(bool)) + return new List (new string[] {"true", "false"}); + if (pi.PropertyType == typeof (Fill)) + return FastEnumUtility.FastEnum.GetValues ().ToList (); + if (pi.PropertyType == typeof (Measure)) + return new List (new string[] {"Stretched", "Fit"}); + } + } + } + } + } + } else if (currentToken.GetTokenType() != XmlTokenType.AttributeValueClose && + currentToken.GetTokenType() != XmlTokenType.EmptyElementClosing && + currentToken.GetTokenType() != XmlTokenType.ClosingSign && + currentNode is ElementStartTagSyntax eltStartTag) { + if (currentToken.GetTokenType() == XmlTokenType.AttributeName) + return getAllCrowTypeMembers (eltStartTag.NameToken.Value.AsString (Source)) + .Where (s => s.Name.StartsWith (currentToken.AsString (Source), StringComparison.OrdinalIgnoreCase)).ToList (); + //else if (currentToken.Type == TokenType.ElementName) + // Suggestions = getAllCrowTypeMembers (eltStartTag.NameToken.Value.AsString (Source)).ToList (); + } else { + /*SyntaxNode curNode = source.FindNodeIncludingPosition (pos); + Console.WriteLine ($"Current Node: {curNode}"); + if (curNode is ElementStartTagSyntax eltStartTag && + (currentToken.Type != TokenType.ClosingSign && currentToken.Type != TokenType.EmptyElementClosing && currentToken.Type != TokenType.Unknown)) { + Suggestions = getAllCrowTypeMembers (eltStartTag.NameToken.Value.AsString (Source)).ToList (); + } else*/ + + } + return null; + } + public override TextChange? GetCompletionForCurrentToken (object suggestion, out TextSpan? newSelection) { + newSelection = null; + + string selectedSugg = suggestion is MemberInfo mi ? + mi.Name : suggestion?.ToString (); + if (selectedSugg == null) + return null; + + if (currentToken.GetTokenType() == XmlTokenType.ElementOpen || + currentToken.GetTokenType() == XmlTokenType.WhiteSpace || + currentToken.GetTokenType() == XmlTokenType.AttributeValueOpen) + return new TextChange (currentToken.End, 0, selectedSugg); + + if (currentToken.GetTokenType() == XmlTokenType.AttributeName && currentNode is AttributeSyntax attrib) { + if (attrib.ValueToken.HasValue) { + TextChange tc = new TextChange (currentToken.Start, currentToken.Length, selectedSugg); + newSelection = new TextSpan( + attrib.ValueToken.Value.Start + tc.CharDiff + 1, + attrib.ValueToken.Value.End + tc.CharDiff - 1 + ); + return tc; + } else { + newSelection = TextSpan.FromStartAndLength (currentToken.Start + selectedSugg.Length + 2); + return new TextChange (currentToken.Start, currentToken.Length, selectedSugg + "=\"\""); + } + } + + return new TextChange (currentToken.Start, currentToken.Length, selectedSugg); + } + + public override Color GetColorForToken(TokenType tokType) + { + XmlTokenType xmlTokType = (XmlTokenType)tokType; + if (xmlTokType.HasFlag (XmlTokenType.Punctuation)) + return Colors.DarkGrey; + if (xmlTokType.HasFlag (XmlTokenType.Trivia)) + return Colors.DimGrey; + else if (xmlTokType == XmlTokenType.ElementName) + return Colors.Green; + if (xmlTokType == XmlTokenType.AttributeName) + return Colors.Blue; + if (xmlTokType == XmlTokenType.AttributeValue) + return Colors.OrangeRed; + if (xmlTokType == XmlTokenType.EqualSign) + return Colors.Black; + if (xmlTokType == XmlTokenType.PI_Target) + return Colors.DarkSlateBlue; + return Colors.Red; + + } + } +} \ No newline at end of file diff --git a/CrowEditBase/src/ImlParsing/XmlTokenType.cs b/CrowEditBase/src/ImlParsing/XmlTokenType.cs new file mode 100644 index 0000000..afd7643 --- /dev/null +++ b/CrowEditBase/src/ImlParsing/XmlTokenType.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2013-2021 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; + +namespace CrowEdit.Xml +{ + [Flags] + public enum XmlTokenType { + Unknown, + Trivia = 0x0100, + WhiteSpace = 0x4100, + Tabulation = 0x4101, + LineBreak = 0x4102, + LineComment = 0x0103, + BlockCommentStart = 0x0104, + BlockComment = 0x0105, + BlockCommentEnd = 0x0106, + Name = 0x0200, + ElementName = 0x0201, + AttributeName = 0x0202, + PI_Target = 0x0203, + Punctuation = 0x0400, + PI_Start = 0x0401,// '' + ElementOpen = 0x0403,// '<' + EndElementOpen = 0x0404,// '' + ClosingSign = 0x0406,// '>' + DTDObjectOpen = 0x04A0,// ' +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using Crow.Text; +using System.Collections.Generic; +using CrowEditBase; + +namespace CrowEdit.Xml +{ + public class XmlTokenizer : Tokenizer { + enum States + { + Init,//first statement of prolog, xmldecl should only apear in this state + prolog,//misc before doctypedecl + ProcessingInstrucitons, + DTD, + DTDObject,//doctype finished + Xml, + StartTag,//inside start tag + Content,//after start tag with no closing slash + EndTag + } + + States curState; + int startOfTok; + + + public XmlTokenizer () {} + bool readName (ref SpanCharReader reader) { + if (reader.EndOfSpan) + return false; + char c = reader.Peak; + if (char.IsLetter(c) || c == '_' || c == ':') { + reader.Advance (); + while (reader.TryPeak (ref c)) { + if (!(char.IsLetterOrDigit(c) || c == '.' || c == '-' || c == '\xB7')) + return true; + reader.Advance (); + } + return true; + } + return false; + } + protected List Toks; + + void skipWhiteSpaces (ref SpanCharReader reader) { + while(!reader.EndOfSpan) { + switch (reader.Peak) { + case '\x85': + case '\x2028': + case '\xA': + reader.Read(); + addTok (ref reader, XmlTokenType.LineBreak); + break; + case '\xD': + reader.Read(); + if (reader.IsNextCharIn ('\xA', '\x85')) + reader.Read(); + addTok (ref reader, XmlTokenType.LineBreak); + break; + case '\x20': + case '\x9': + char c = reader.Read(); + while (reader.TryPeak (c)) + reader.Read(); + addTok (ref reader, c == '\x20' ? XmlTokenType.WhiteSpace : XmlTokenType.Tabulation); + break; + default: + return; + } + } + } + void addTok (ref SpanCharReader reader, XmlTokenType tokType) { + if (reader.CurrentPosition == startOfTok) + return; + Toks.Add (new Token((TokenType)tokType, startOfTok, reader.CurrentPosition)); + startOfTok = reader.CurrentPosition; + } + public override Token[] Tokenize (string source) { + SpanCharReader reader = new SpanCharReader(source); + + startOfTok = 0; + int curObjectLevel = 0; + curState = States.Init; + Toks = new List(100); + + while(!reader.EndOfSpan) { + + skipWhiteSpaces (ref reader); + + if (reader.EndOfSpan) + break; + + switch (reader.Peak) { + case '<': + reader.Advance (); + if (reader.TryPeak ('?')) { + reader.Advance (); + addTok (ref reader, XmlTokenType.PI_Start); + readName (ref reader); + addTok (ref reader, XmlTokenType.PI_Target); + curState = States.ProcessingInstrucitons; + } else if (reader.TryPeak ('!')) { + reader.Advance (); + if (reader.TryPeak ("--")) { + reader.Advance (2); + addTok (ref reader, XmlTokenType.BlockCommentStart); + if (reader.TryReadUntil ("-->")) { + addTok (ref reader, XmlTokenType.BlockComment); + reader.Advance (3); + addTok (ref reader, XmlTokenType.BlockCommentEnd); + } else if (reader.TryPeak ("-->")) { + reader.Advance (3); + addTok (ref reader, XmlTokenType.BlockCommentEnd); + } + } else { + addTok (ref reader, XmlTokenType.DTDObjectOpen); + if (readName (ref reader)) { + addTok (ref reader, XmlTokenType.Keyword); + curState = States.DTDObject; + } + } + } else if (reader.TryPeak('/')) { + reader.Advance (); + addTok (ref reader, XmlTokenType.EndElementOpen); + if (readName (ref reader)) { + addTok (ref reader, XmlTokenType.ElementName); + if (reader.TryPeak('>')) { + reader.Advance (); + addTok (ref reader, XmlTokenType.ClosingSign); + + if (--curObjectLevel > 0) + curState = States.Content; + else + curState = States.Xml; + } + } + }else{ + addTok (ref reader, XmlTokenType.ElementOpen); + if (readName (ref reader)) { + addTok (ref reader, XmlTokenType.ElementName); + curState = States.StartTag; + } + } + break; + case '?': + reader.Advance (); + if (reader.TryPeak ('>')){ + reader.Advance (); + addTok (ref reader, XmlTokenType.PI_End); + }else + addTok (ref reader, XmlTokenType.Unknown); + curState = States.prolog; + break; + case '\'': + case '"': + char q = reader.Read(); + addTok (ref reader, XmlTokenType.AttributeValueOpen); + if (reader.TryReadUntil (q)) { + addTok (ref reader, XmlTokenType.AttributeValue); + reader.Advance (); + addTok (ref reader, XmlTokenType.AttributeValueClose); + } else + addTok (ref reader, XmlTokenType.AttributeValue); + break; + case '=': + reader.Advance(); + addTok (ref reader, XmlTokenType.EqualSign); + break; + case '>': + reader.Advance(); + addTok (ref reader, XmlTokenType.ClosingSign); + curObjectLevel++; + curState = States.Content; + break; + case '/': + reader.Advance(); + if (reader.TryRead ('>')) { + addTok (ref reader, XmlTokenType.EmptyElementClosing); + if (--curObjectLevel > 0) + curState = States.Content; + else + curState = States.Xml; + }else + addTok (ref reader, XmlTokenType.Unknown); + break; + default: + if (curState == States.StartTag || curState == States.ProcessingInstrucitons) { + if (readName(ref reader)) + addTok (ref reader, XmlTokenType.AttributeName); + else if (reader.TryAdvance()) + addTok (ref reader, XmlTokenType.Unknown); + } else { + reader.TryReadUntil ('<'); + addTok (ref reader, XmlTokenType.Content); + } + break; + } + } + + return Toks.ToArray(); + } + + } +} diff --git a/CrowEditBase/src/SourceEditor.cs b/CrowEditBase/src/SourceEditor.cs new file mode 100644 index 0000000..fc05117 --- /dev/null +++ b/CrowEditBase/src/SourceEditor.cs @@ -0,0 +1,390 @@ +// Copyright (c) 2013-2021 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using Glfw; +using Crow.Text; +using System.Collections.Generic; +using Crow.Cairo; +using System.Threading.Tasks; +using System.Linq; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Collections; +using CrowEditBase; + +namespace Crow +{ + public class SourceEditor : Editor { + object TokenMutex = new object(); + + + + + ListBox overlay; + IList suggestions; + volatile bool disableSuggestions; + public IList Suggestions { + get => suggestions; + set { + suggestions = value; + NotifyValueChangedAuto (suggestions); + if (suggestions == null || suggestions.Count == 0) + hideOverlay (); + else + showOverlay (); + } + } + bool suggestionsActive => overlay != null && overlay.IsVisible; + + public override void OnTextChanged(object sender, TextChangeEventArgs e) + { + if (disableTextChangedEvent) + return; + + base.OnTextChanged(sender, e); + + if (!disableSuggestions && HasFocus) + tryGetSuggestions (); + + RegisterForGraphicUpdate(); + + //Console.WriteLine ($"{pos}: {suggestionTok.AsString (_text)} {suggestionTok}"); + } + + + + protected void tryGetSuggestions () { + if (currentLoc.HasValue && Document is SourceDocument srcDoc) + Suggestions = srcDoc.GetSuggestions (lines.GetAbsolutePosition (CurrentLoc.Value)); + else + Suggestions = null; + } + void showOverlay () { + lock (IFace.UpdateMutex) { + if (overlay == null) { + overlay = IFace.LoadIMLFragment(@" + + + + + + + + + + + + + + + + + + + + + "); + overlay.DataSource = this; + overlay.Loaded += (sender, arg) => (sender as ListBox).SelectedIndex = 0; + } else + overlay.IsVisible = true; + overlay.RegisterForLayouting(LayoutingType.Sizing); + } + } + void hideOverlay () { + if (overlay == null) + return; + overlay.IsVisible = false; + } + void completeToken () { + if (Document is SourceDocument srcDoc) { + TextChange? change = srcDoc.GetCompletionForCurrentToken (overlay.SelectedItem, out TextSpan? nextSelection); + if (change.HasValue) + update (change.Value); + if (nextSelection.HasValue) + Selection = nextSelection.Value; + } + hideOverlay (); + } + public override void onMouseDown (object sender, MouseButtonEventArgs e) { + hideOverlay (); + base.onMouseDown (sender, e); + } + + public override void onKeyDown(object sender, KeyEventArgs e) + { + TextSpan selection = Selection; + + if (SelectionIsEmpty) { + if (suggestionsActive) { + switch (e.Key) { + case Key.Escape: + hideOverlay (); + return; + case Key.Left: + case Key.Right: + hideOverlay (); + break; + case Key.End: + case Key.Home: + case Key.Down: + case Key.Up: + case Key.PageDown: + case Key.PageUp: + overlay.onKeyDown (this, e); + return; + case Key.Tab: + case Key.Enter: + case Key.KeypadEnter: + completeToken (); + return; + } + } else if (e.Key == Key.Space && IFace.Ctrl) { + tryGetSuggestions (); + return; + } + } else if (e.Key == Key.Tab && !selection.IsEmpty) { + int lineStart = lines.GetLocation (selection.Start).Line; + int lineEnd = lines.GetLocation (selection.End).Line; + + disableSuggestions = true; + + if (IFace.Shift) { + for (int l = lineStart; l <= lineEnd; l++) { + if (Text[lines[l].Start] == '\t') + update (new TextChange (lines[l].Start, 1, "")); + else if (Char.IsWhiteSpace (Text[lines[l].Start])) { + int i = 1; + while (i < lines[l].Length && i < Interface.TAB_SIZE && Char.IsWhiteSpace (Text[i])) + i++; + update (new TextChange (lines[l].Start, i, "")); + } + } + + }else{ + for (int l = lineStart; l <= lineEnd; l++) + update (new TextChange (lines[l].Start, 0, "\t")); + } + + selectionStart = new CharLocation (lineStart, 0); + CurrentLoc = new CharLocation (lineEnd, lines[lineEnd].Length); + + disableSuggestions = false; + + return; + } + base.onKeyDown(sender, e); + } + + protected override void drawContent (Context gr) { + if (!(Document is SourceDocument xmlDoc)) { + base.drawContent (gr); + return; + } + //lock(TokenMutex) { + xmlDoc.EnterReadLock (); + try { + if (xmlDoc.Tokens == null || xmlDoc.Tokens.Length == 0) { + base.drawContent (gr); + return; + } + + Rectangle cb = ClientRectangle; + fe = gr.FontExtents; + double lineHeight = fe.Ascent + fe.Descent; + + CharLocation selStart = default, selEnd = default; + bool selectionNotEmpty = false; + + if (HasFocus) { + if (currentLoc?.Column < 0) { + updateLocation (gr, cb.Width, ref currentLoc); + NotifyValueChanged ("CurrentColumn", CurrentColumn); + } else + updateLocation (gr, cb.Width, ref currentLoc); + + if (overlay != null && overlay.IsVisible) { + Point p = new Point((int)currentLoc.Value.VisualCharXPosition - ScrollX, (int)(lineHeight * (currentLoc.Value.Line + 1) - ScrollY)); + if (p.Y < 0 || p.X < 0) + hideOverlay (); + else { + p += ScreenCoordinates (Slot).TopLeft; + overlay.Left = p.X; + overlay.Top = p.Y; + } + } + if (selectionStart.HasValue) { + updateLocation (gr, cb.Width, ref selectionStart); + if (CurrentLoc.Value != selectionStart.Value) + selectionNotEmpty = true; + } + if (selectionNotEmpty) { + if (CurrentLoc.Value.Line < selectionStart.Value.Line) { + selStart = CurrentLoc.Value; + selEnd = selectionStart.Value; + } else if (CurrentLoc.Value.Line > selectionStart.Value.Line) { + selStart = selectionStart.Value; + selEnd = CurrentLoc.Value; + } else if (CurrentLoc.Value.Column < selectionStart.Value.Column) { + selStart = CurrentLoc.Value; + selEnd = selectionStart.Value; + } else { + selStart = selectionStart.Value; + selEnd = CurrentLoc.Value; + } + } else + IFace.forceTextCursor = true; + } + + double spacePixelWidth = gr.TextExtents (" ").XAdvance; + int x = 0, y = 0; + double pixX = cb.Left; + + + Foreground.SetAsSource (IFace, gr); + gr.Translate (-ScrollX, -ScrollY); + + + ReadOnlySpan sourceBytes = xmlDoc.Source.AsSpan(); + Span bytes = stackalloc byte[128]; + TextExtents extents; + int tokPtr = 0; + Token tok = xmlDoc.Tokens[tokPtr]; + bool multilineToken = false; + + ReadOnlySpan buff = sourceBytes; + + + for (int i = 0; i < lines.Count; i++) { + //if (!cancelLinePrint (lineHeight, lineHeight * y, cb.Height)) { + + if (multilineToken) { + if (tok.End < lines[i].End) {//last incomplete line of multiline token + buff = sourceBytes.Slice (lines[i].Start, tok.End - lines[i].Start); + } else {//print full line + buff = sourceBytes.Slice (lines[i].Start, lines[i].Length); + } + } + + while (tok.Start < lines[i].End) { + if (!multilineToken) { + if (tok.End > lines[i].End) {//first line of multiline + multilineToken = true; + buff = sourceBytes.Slice (tok.Start, lines[i].End - tok.Start); + } else + buff = sourceBytes.Slice (tok.Start, tok.Length); + + gr.SetSource(xmlDoc.GetColorForToken (tok.Type)); + } + + int size = buff.Length * 4 + 1; + if (bytes.Length < size) + bytes = size > 512 ? new byte[size] : stackalloc byte[size]; + + int encodedBytes = Crow.Text.Encoding.ToUtf8 (buff, bytes); + + if (encodedBytes > 0) { + bytes[encodedBytes++] = 0; + gr.TextExtents (bytes.Slice (0, encodedBytes), out extents); + gr.MoveTo (pixX, lineHeight * y + fe.Ascent); + gr.ShowText (bytes.Slice (0, encodedBytes)); + pixX += extents.XAdvance; + x += buff.Length; + } + + if (multilineToken) { + if (tok.End < lines[i].End)//last incomplete line of multiline token + multilineToken = false; + else + break; + } + + if (++tokPtr >= xmlDoc.Tokens.Length) + break; + tok = xmlDoc.Tokens[tokPtr]; + } + + if (HasFocus && selectionNotEmpty) { + RectangleD lineRect = new RectangleD (cb.X, lineHeight * y + cb.Top, pixX, lineHeight); + RectangleD selRect = lineRect; + + if (i >= selStart.Line && i <= selEnd.Line) { + if (selStart.Line == selEnd.Line) { + selRect.X = selStart.VisualCharXPosition + cb.X; + selRect.Width = selEnd.VisualCharXPosition - selStart.VisualCharXPosition; + } else if (i == selStart.Line) { + double newX = selStart.VisualCharXPosition + cb.X; + selRect.Width -= (newX - selRect.X) - 10.0; + selRect.X = newX; + } else if (i == selEnd.Line) + selRect.Width = selEnd.VisualCharXPosition - selRect.X + cb.X; + else + selRect.Width += 10.0; + + buff = sourceBytes.Slice(lines[i].Start, lines[i].Length); + int size = buff.Length * 4 + 1; + if (bytes.Length < size) + bytes = size > 512 ? new byte[size] : stackalloc byte[size]; + + int encodedBytes = Crow.Text.Encoding.ToUtf8 (buff, bytes); + + gr.SetSource (SelectionBackground); + gr.Rectangle (selRect); + if (encodedBytes < 0) + gr.Fill (); + else { + gr.FillPreserve (); + gr.Save (); + gr.Clip (); + gr.SetSource (SelectionForeground); + gr.MoveTo (lineRect.X, lineRect.Y + fe.Ascent); + gr.ShowText (bytes.Slice (0, encodedBytes)); + gr.Restore (); + } + Foreground.SetAsSource (IFace, gr); + } + } + + if (!multilineToken) { + if (++tokPtr >= xmlDoc.Tokens.Length) + break; + tok = xmlDoc.Tokens[tokPtr]; + } + + x = 0; + pixX = 0; + + y++; + + + /* } else if (tok2.Type == TokenType.Tabulation) { + int spaceRounding = x % tabSize; + int spaces = spaceRounding == 0 ? + tabSize * tok2.Length : + spaceRounding + tabSize * (tok2.Length - 1); + x += spaces; + pixX += spacePixelWidth * spaces; + continue; + } else if (tok2.Type == TokenType.WhiteSpace) { + x += tok2.Length; + pixX += spacePixelWidth * tok2.Length;*/ + } + //gr.Translate (ScrollX, ScrollY); + } finally { + xmlDoc.ExitReadLock (); + } + + } + } +} \ No newline at end of file diff --git a/CrowEditBase/src/TextDocument.cs b/CrowEditBase/src/TextDocument.cs new file mode 100644 index 0000000..e51d246 --- /dev/null +++ b/CrowEditBase/src/TextDocument.cs @@ -0,0 +1,218 @@ +// Copyright (c) 2021-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Linq; +using System.Collections.Generic; +using System.IO; +using Crow; +using Crow.Text; + +namespace CrowEditBase +{ + public class TextDocument : Document { + public TextDocument (Interface iFace, string fullPath) + : base (iFace, fullPath) { + reloadFromFile (); + } + + string source, origSource; + System.Text.Encoding encoding = System.Text.Encoding.UTF8; + + public string Source { + get => source; + set { + if (source == value) + return; + source = value; + NotifyValueChanged (source); + } + } + + public override bool IsDirty => origSource != source; + /// dictionnary of object per document client, when not null, client must reload content of document. + Dictionary> registeredClients = new Dictionary>(); + public override bool TryGetState(object client, out T state) { + state = default; + if (editorRWLock.TryEnterReadLock (10)) { + try { + state = (T)(object)registeredClients[client]; + registeredClients[client] = null; + } finally { + editorRWLock.ExitReadLock (); + } + } + return state != null; + } + public override void RegisterClient(object client) + { + editorRWLock.EnterWriteLock (); + registeredClients.Add (client, null); + notifyClient (client, new TextChange (0, 0, Source)); + editorRWLock.ExitWriteLock (); + } + public override void UnregisterClient(object client) + { + editorRWLock.EnterWriteLock (); + registeredClients.Remove (client); + editorRWLock.ExitWriteLock (); + } + void notifyClients (TextChange tc, object triggeringClient = null) { + object[] clients = registeredClients.Keys.ToArray (); + for (int i = 0; i < clients.Length; i++) { + if (clients[i] != triggeringClient) + notifyClient (clients[i], tc); + } + } + void notifyClient (object client, TextChange tc) { + if (registeredClients[client] == null) + registeredClients[client] = new List (); + registeredClients[client].Add (tc); + } + + + + protected override void writeToDisk () { + using (Stream s = new FileStream(FullPath, FileMode.Create)) { + using (StreamWriter sw = new StreamWriter (s, encoding)) + sw.Write (source); + } + origSource = source; + NotifyValueChanged ("IsDirty", IsDirty); + } + protected override void readFromDisk() + { + using (Stream s = new FileStream (FullPath, FileMode.Open)) { + using (StreamReader sr = new StreamReader (s)) { + Source = origSource = sr.ReadToEnd (); + encoding = sr.CurrentEncoding; + } + } + } + protected override void initNewFile() + { + Source = origSource = ""; + } + protected override void reloadFromFile () { + editorRWLock.EnterWriteLock (); + try { + if (File.Exists (FullPath)) + readFromDisk (); + else + initNewFile (); + resetUndoRedo (); + } finally { + editorRWLock.ExitWriteLock (); + } + } + protected Stack undoStack = new Stack (); + protected Stack redoStack = new Stack (); + + public Command CMDUndo, CMDRedo, CMDSave, CMDSaveAs; + + protected override void initCommands () { + CMDUndo = new Command ("Undo", undo, "#CrowEdit.ui.icons.reply.svg", false); + CMDRedo = new Command ("Redo", redo, "#CrowEdit.ui.icons.share-arrow.svg", false); + CMDSave = new Command ("save", Save, "#CrowEdit.ui.icons.inbox.svg"); + CMDSaveAs = new Command ("Save As...", SaveAs, "#CrowEdit.ui.icons.inbox.svg"); + } + public void SaveAs () { + iFace.LoadIMLFragment ( + "" + ).DataSource = this; + } + public void Save () { + if (File.Exists (FullPath)) + writeToDisk (); + else + SaveAs (); + } + + protected void saveFileDialog_OkClicked (object sender, EventArgs e) + { + FileDialog fd = sender as FileDialog; + + if (string.IsNullOrEmpty (fd.SelectedFileFullPath)) + return; + + if (File.Exists(fd.SelectedFileFullPath)) { + MessageBox.ShowModal (iFace, MessageBox.Type.YesNo, "File exists, overwrite?") + .Yes += (sender2, e2) => { + FullPath = fd.SelectedFileFullPath; + writeToDisk (); + }; + return; + } + FullPath = fd.SelectedFileFullPath; + writeToDisk (); + } + + + protected void undo () { + editorRWLock.EnterWriteLock (); + try { + if (undoStack.TryPop (out TextChange tc)) { + redoStack.Push (tc.Inverse (source)); + CMDRedo.CanExecute = true; + apply (tc); + notifyClients (tc); + //editor.SetCursorPosition (tch.End + tch.ChangedText.Length); + } + if (undoStack.Count == 0) + CMDUndo.CanExecute = false; + } finally { + editorRWLock.ExitWriteLock (); + } + } + protected void redo () { + editorRWLock.EnterWriteLock (); + try { + if (redoStack.TryPop (out TextChange tc)) { + undoStack.Push (tc.Inverse (source)); + CMDUndo.CanExecute = true; + apply (tc); + notifyClients (tc); + } + if (redoStack.Count == 0) + CMDRedo.CanExecute = false; + } finally { + editorRWLock.ExitWriteLock (); + } + + } + protected void resetUndoRedo () { + undoStack.Clear (); + redoStack.Clear (); + CMDUndo.CanExecute = false; + CMDRedo.CanExecute = false; + } + protected bool disableTextChangedEvent = false; + protected virtual void apply (TextChange change) { + Span tmp = stackalloc char[source.Length + (change.ChangedText.Length - change.Length)]; + ReadOnlySpan src = source.AsSpan (); + src.Slice (0, change.Start).CopyTo (tmp); + if (!string.IsNullOrEmpty (change.ChangedText)) + change.ChangedText.AsSpan ().CopyTo (tmp.Slice (change.Start)); + src.Slice (change.End).CopyTo (tmp.Slice (change.Start + change.ChangedText.Length)); + Source = tmp.ToString (); + } + protected void applyTextChange (TextChange change, object triggeringEditor = null) { + editorRWLock.EnterWriteLock (); + try { + undoStack.Push (change.Inverse (source)); + redoStack.Clear (); + CMDUndo.CanExecute = true; + CMDRedo.CanExecute = false; + apply (change); + notifyClients (change, triggeringEditor); + } finally { + editorRWLock.ExitWriteLock (); + } + + } + protected void onTextChanged (object sender, TextChangeEventArgs e) { + applyTextChange (e.Change, sender); + } + } +} \ No newline at end of file diff --git a/CrowEditBase/ui/CategoryExp.template b/CrowEditBase/ui/CategoryExp.template new file mode 100755 index 0000000..f546492 --- /dev/null +++ b/CrowEditBase/ui/CategoryExp.template @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file diff --git a/CrowEditBase/ui/ContextMenu.template b/CrowEditBase/ui/ContextMenu.template new file mode 100644 index 0000000..38e6b7b --- /dev/null +++ b/CrowEditBase/ui/ContextMenu.template @@ -0,0 +1,42 @@ + + + + + + + + + + diff --git a/CrowEditBase/ui/IDE.style b/CrowEditBase/ui/IDE.style new file mode 100644 index 0000000..5a3c67b --- /dev/null +++ b/CrowEditBase/ui/IDE.style @@ -0,0 +1,25 @@ +icon { + Width="14"; + Height="14"; +} +MemberViewLabel { + Margin="1"; + Height="Fit"; + Width="50%"; + Background="White"; +} +MemberViewHStack { + Focusable="true"; + Height="Fit"; + Spacing="1"; + MouseEnter="{Background=SteelBlue}"; + MouseLeave="{Background=Transparent}"; +} + +IcoBut { + Template = "#Crow.Coding.ui.IcoBut.template"; + MinimumSize = "10,10"; + Width = "8"; + Height = "14"; + Background = "White"; +} diff --git a/CrowEditBase/ui/IcoBut.template b/CrowEditBase/ui/IcoBut.template new file mode 100644 index 0000000..a9fc28a --- /dev/null +++ b/CrowEditBase/ui/IcoBut.template @@ -0,0 +1,7 @@ + + + + diff --git a/CrowEditBase/ui/ItemTemplates/Enum.template b/CrowEditBase/ui/ItemTemplates/Enum.template new file mode 100755 index 0000000..efd9e43 --- /dev/null +++ b/CrowEditBase/ui/ItemTemplates/Enum.template @@ -0,0 +1,47 @@ + + + diff --git a/CrowEditBase/ui/ItemTemplates/Fill.template b/CrowEditBase/ui/ItemTemplates/Fill.template new file mode 100755 index 0000000..56d4a4d --- /dev/null +++ b/CrowEditBase/ui/ItemTemplates/Fill.template @@ -0,0 +1,19 @@ + + + \ No newline at end of file diff --git a/CrowEditBase/ui/icons/basic_floppydisk.svg b/CrowEditBase/ui/icons/basic_floppydisk.svg new file mode 100644 index 0000000..55d901d --- /dev/null +++ b/CrowEditBase/ui/icons/basic_floppydisk.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + diff --git a/CrowEditBase/ui/icons/blank-file.svg b/CrowEditBase/ui/icons/blank-file.svg new file mode 100644 index 0000000..8136979 --- /dev/null +++ b/CrowEditBase/ui/icons/blank-file.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/ui/icons/center-align.svg b/CrowEditBase/ui/icons/center-align.svg new file mode 100644 index 0000000..92e3fac --- /dev/null +++ b/CrowEditBase/ui/icons/center-align.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/CrowEditBase/ui/icons/cogwheel.svg b/CrowEditBase/ui/icons/cogwheel.svg new file mode 100644 index 0000000..c104c47 --- /dev/null +++ b/CrowEditBase/ui/icons/cogwheel.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/ui/icons/copy-file.svg b/CrowEditBase/ui/icons/copy-file.svg new file mode 100644 index 0000000..63c2dd3 --- /dev/null +++ b/CrowEditBase/ui/icons/copy-file.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/ui/icons/edit.svg b/CrowEditBase/ui/icons/edit.svg new file mode 100644 index 0000000..73569d8 --- /dev/null +++ b/CrowEditBase/ui/icons/edit.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/ui/icons/file-code.svg b/CrowEditBase/ui/icons/file-code.svg new file mode 100644 index 0000000..2dc00db --- /dev/null +++ b/CrowEditBase/ui/icons/file-code.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/CrowEditBase/ui/icons/folder.svg b/CrowEditBase/ui/icons/folder.svg new file mode 100644 index 0000000..ee1f82b --- /dev/null +++ b/CrowEditBase/ui/icons/folder.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/ui/icons/font-file.svg b/CrowEditBase/ui/icons/font-file.svg new file mode 100644 index 0000000..20beac1 --- /dev/null +++ b/CrowEditBase/ui/icons/font-file.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/ui/icons/light-bulb.svg b/CrowEditBase/ui/icons/light-bulb.svg new file mode 100644 index 0000000..4193a75 --- /dev/null +++ b/CrowEditBase/ui/icons/light-bulb.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/ui/icons/paragraph.svg b/CrowEditBase/ui/icons/paragraph.svg new file mode 100644 index 0000000..826aa63 --- /dev/null +++ b/CrowEditBase/ui/icons/paragraph.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/ui/icons/paste-on-document.svg b/CrowEditBase/ui/icons/paste-on-document.svg new file mode 100644 index 0000000..b0a705e --- /dev/null +++ b/CrowEditBase/ui/icons/paste-on-document.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/ui/icons/previous.svg b/CrowEditBase/ui/icons/previous.svg new file mode 100644 index 0000000..566c8a3 --- /dev/null +++ b/CrowEditBase/ui/icons/previous.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/ui/icons/question.svg b/CrowEditBase/ui/icons/question.svg new file mode 100644 index 0000000..fb8e3d3 --- /dev/null +++ b/CrowEditBase/ui/icons/question.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/ui/icons/reply.svg b/CrowEditBase/ui/icons/reply.svg new file mode 100644 index 0000000..d008cb3 --- /dev/null +++ b/CrowEditBase/ui/icons/reply.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/ui/icons/scissors.svg b/CrowEditBase/ui/icons/scissors.svg new file mode 100644 index 0000000..4b5a225 --- /dev/null +++ b/CrowEditBase/ui/icons/scissors.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/ui/icons/search.svg b/CrowEditBase/ui/icons/search.svg new file mode 100644 index 0000000..4a931b3 --- /dev/null +++ b/CrowEditBase/ui/icons/search.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/ui/icons/share-arrow.svg b/CrowEditBase/ui/icons/share-arrow.svg new file mode 100644 index 0000000..e0eb246 --- /dev/null +++ b/CrowEditBase/ui/icons/share-arrow.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/CrowEditBase/ui/icons/sign-out.svg b/CrowEditBase/ui/icons/sign-out.svg new file mode 100644 index 0000000..c5951fc --- /dev/null +++ b/CrowEditBase/ui/icons/sign-out.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/ui/icons/text-file.svg b/CrowEditBase/ui/icons/text-file.svg new file mode 100644 index 0000000..eafca90 --- /dev/null +++ b/CrowEditBase/ui/icons/text-file.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/CrowEditBase/ui/icons/text-label.svg b/CrowEditBase/ui/icons/text-label.svg new file mode 100644 index 0000000..8fa9196 --- /dev/null +++ b/CrowEditBase/ui/icons/text-label.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/ui/icons/tools.svg b/CrowEditBase/ui/icons/tools.svg new file mode 100644 index 0000000..5326f19 --- /dev/null +++ b/CrowEditBase/ui/icons/tools.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/CrowEditBase/ui/icons/zoom-in.svg b/CrowEditBase/ui/icons/zoom-in.svg new file mode 100644 index 0000000..60c41d1 --- /dev/null +++ b/CrowEditBase/ui/icons/zoom-in.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEditBase/ui/icons/zoom-out.svg b/CrowEditBase/ui/icons/zoom-out.svg new file mode 100644 index 0000000..bd4eec3 --- /dev/null +++ b/CrowEditBase/ui/icons/zoom-out.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/clean.sh b/clean.sh old mode 100644 new mode 100755 diff --git a/src/CrowEdit.cs b/src/CrowEdit.cs index a826bbe..1caf7dc 100644 --- a/src/CrowEdit.cs +++ b/src/CrowEdit.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère +// Copyright (c) 2013-2021 Jean-Philippe Bruyère // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) @@ -9,11 +9,13 @@ using System.Collections.Generic; using Crow.Text; using System.Reflection; using System.Runtime.InteropServices; +using CrowEditBase; +using System.Linq; namespace CrowEdit { public class CrowEdit : Interface - { + { #if NETCOREAPP static IntPtr resolveUnmanaged(Assembly assembly, String libraryName) { @@ -27,68 +29,53 @@ namespace CrowEdit } Console.WriteLine($"[UNRESOLVE] {assembly} {libraryName}"); return IntPtr.Zero; + } + static void Main () + { + using (CrowEdit win = new CrowEdit ()) + win.Run (); } - static CrowEdit() { System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).ResolvingUnmanagedDll += resolveUnmanaged; + Interface.CrowAssemblyNames = new string[] {"CrowEditBase"}; } + public CrowEdit () : base(800, 600) { } + #endif public Command CMDNew, CMDOpen, CMDSave, CMDSaveAs, CMDQuit, CMDShowLeftPane, - CMDUndo, CMDRedo, CMDCut, CMDCopy, CMDPaste, CMDHelp, CMDAbout, CMDOptions; + CMDHelp, CMDAbout, CMDOptions; - const string _defaultFileName = "unnamed.txt"; - string source = ""; - int dirtyUndoLevel;// - public new bool IsDirty { get { return undoStack.Count != dirtyUndoLevel; } } - public string Source { - get => source; - set { - if (source == value) - return; - source = value; - NotifyValueChanged (source); - } + + PluginsLoadContext pluginsCtx; + void initPlugins () { + pluginsCtx = new PluginsLoadContext (); } - public CommandGroup EditorCommands => new CommandGroup (CMDUndo, CMDRedo, CMDCut, CMDCopy, CMDPaste, CMDSave, CMDSaveAs); + public ObservableList OpenedDocuments = new ObservableList (); + + const string _defaultFileName = "unnamed.txt"; - Stack undoStack = new Stack (); - Stack redoStack = new Stack (); void undo () { - if (undoStack.TryPop (out TextChange tch)) { - redoStack.Push (tch.Inverse (source)); - CMDRedo.CanExecute = true; - apply (tch); - editor.SetCursorPosition (tch.End + tch.ChangedText.Length); - } - if (undoStack.Count == 0) - CMDUndo.CanExecute = false; } void redo () { - if (redoStack.TryPop (out TextChange tch)) { - undoStack.Push (tch.Inverse (source)); - CMDUndo.CanExecute = true; - apply (tch); - editor.SetCursorPosition (tch.End + tch.ChangedText.Length); - } - if (redoStack.Count == 0) - CMDRedo.CanExecute = false; - } - bool disableTextChangedEvent = false; - void apply (TextChange change) { - Span tmp = stackalloc char[source.Length + (change.ChangedText.Length - change.Length)]; - ReadOnlySpan src = source.AsSpan (); - src.Slice (0, change.Start).CopyTo (tmp); - if (!string.IsNullOrEmpty(change.ChangedText)) - change.ChangedText.AsSpan ().CopyTo (tmp.Slice (change.Start)); - src.Slice (change.End).CopyTo (tmp.Slice (change.Start + change.ChangedText.Length)); - disableTextChangedEvent = true; - Source = tmp.ToString (); - disableTextChangedEvent = false; - NotifyValueChanged ("IsDirty", IsDirty); } + TextDocument currentDocument; + public TextDocument CurrentDocument { + get => currentDocument; + set { + if (currentDocument == value) + return; + + currentDocument?.UnselectDocument (); + + currentDocument = value; + NotifyValueChanged (currentDocument); + + currentDocument?.SelectDocument (); + } + } public string CurrentDir { get => Configuration.Global.Get("CurrentDir"); set { @@ -133,53 +120,66 @@ namespace CrowEdit } void initCommands (){ - CMDNew = new Command(new Action(() => onNewFile())) { Caption = "New", Icon = new SvgPicture("#CrowEdit.ui.icons.blank-file.svg")}; - CMDOpen = new Command(new Action(() => openFileDialog())) { Caption = "Open...", Icon = new SvgPicture("#CrowEdit.ui.icons.outbox.svg")}; - CMDSave = new Command(new Action(() => saveFileDialog())) { Caption = "Save", Icon = new SvgPicture("#CrowEdit.ui.icons.inbox.svg"), CanExecute = false}; - CMDSaveAs = new Command(new Action(() => saveFileDialog())) { Caption = "Save As...", Icon = new SvgPicture("#CrowEdit.ui.icons.inbox.svg"), CanExecute = false}; - CMDQuit = new Command(new Action(() => base.Quit())) { Caption = "Quit", Icon = new SvgPicture("#CrowEdit.ui.icons.sign-out.svg")}; - CMDUndo = new Command(new Action(() => undo())) { Caption = "Undo", Icon = new SvgPicture("#CrowEdit.ui.icons.reply.svg"), CanExecute = false}; - CMDRedo = new Command(new Action(() => redo())) { Caption = "Redo", Icon = new SvgPicture("#CrowEdit.ui.icons.share-arrow.svg"), CanExecute = false}; - CMDCut = new Command(new Action(() => Quit ())) { Caption = "Cut", Icon = new SvgPicture("#CrowEdit.ui.icons.scissors.svg"), CanExecute = false}; - CMDCopy = new Command(new Action(() => Quit ())) { Caption = "Copy", Icon = new SvgPicture("#CrowEdit.ui.icons.copy-file.svg"), CanExecute = false}; - CMDPaste = new Command(new Action(() => Quit ())) { Caption = "Paste", Icon = new SvgPicture("#CrowEdit.ui.icons.paste-on-document.svg"), CanExecute = false}; + CMDNew = new Command("New", createNewFile, "#CrowEdit.ui.icons.blank-file.svg"); + CMDOpen = new Command("Open...", openFileDialog, "#CrowEdit.ui.icons.outbox.svg"); + + + CMDQuit = new Command("Quit", base.Quit, "#CrowEdit.ui.icons.sign-out.svg"); + CMDHelp = new Command(new Action(() => System.Diagnostics.Debug.WriteLine("help"))) { Caption = "Help", Icon = new SvgPicture("#CrowEdit.ui.icons.question.svg")}; - CMDOptions = new Command(new Action(() => openOptionsDialog())) { Caption = "Editor Options", Icon = new SvgPicture("#CrowEdit.ui.icons.tools.svg")}; - CMDShowLeftPane = new Command (new Action (() => ShowLeftPane = !ShowLeftPane)) { Caption = "Show Left Pane" }; + + CMDOptions = new Command("Editor Options", openOptionsDialog, new SvgPicture("#CrowEdit.ui.icons.tools.svg")); + CMDShowLeftPane = new Command ("Show Left Pane", () => ShowLeftPane = !ShowLeftPane); } - void onNewFile(){ - if (IsDirty) { - MessageBox mb = MessageBox.ShowModal (this, MessageBox.Type.YesNo, "Current file has unsaved changes, are you sure?"); - mb.Yes += (sender, e) => newFile(); - } else - newFile (); + void createNewFile(){ + openOrCreateFile (Path.Combine (CurFileDir, _defaultFileName)); } void openOptionsDialog() => Load ("#CrowEdit.ui.EditorOptions.crow").DataSource = this; - void openFileDialog() => Load ("#CrowEdit.ui.openFile.crow").DataSource = this; - void saveFileDialog() => Load ("#CrowEdit.ui.saveFile.crow").DataSource = this; + void openFileDialog() => + LoadIMLFragment ( + @"").DataSource = this; + void openFileDialog_OkClicked (object sender, EventArgs e) { FileDialog fd = sender as FileDialog; if (string.IsNullOrEmpty (fd.SelectedFile)) return; - openFile (fd.SelectedFile, fd.SelectedDirectory); - } - void saveFileDialog_OkClicked (object sender, EventArgs e) - { - FileDialog fd = sender as FileDialog; - if (string.IsNullOrEmpty (fd.SelectedFile)) - return; - save (fd.SelectedFile, fd.SelectedDirectory); - } - void onTextChanged (object sender, TextChangeEventArgs e) - { - if (disableTextChangedEvent) - return; - undoStack.Push (e.Change.Inverse(source)); - redoStack.Clear (); - CMDUndo.CanExecute = true; - CMDRedo.CanExecute = false; - apply (e.Change); + TextDocument doc = OpenedDocuments.FirstOrDefault (d => d.FullPath == fd.SelectedFileFullPath); + if (doc != null) + CurrentDocument = doc; + else + openOrCreateFile (fd.SelectedFileFullPath); + } + + void openOrCreateFile (string filePath) { + TextDocument doc = null; + CurrentFilePath = filePath; + string ext = Path.GetExtension (CurrentFilePath); + switch (ext) { + case ".crow": + doc = new Xml.XmlDocument (this, CurrentFilePath); + break; + default: + doc = new TextDocument (this, CurrentFilePath); + break; + } + + doc.CloseEvent += onQueryCloseDocument; + OpenedDocuments.Add (doc); + CurrentDocument = doc; + } + void closeDocument (TextDocument doc) { + int idx = OpenedDocuments.IndexOf (doc); + OpenedDocuments.Remove (doc); + if (doc == CurrentDocument) { + if (OpenedDocuments.Count > 0) + CurrentDocument = OpenedDocuments[Math.Min (idx, OpenedDocuments.Count - 1)]; + else + CurrentDocument = null; + } } void goUpDirClick (object sender, MouseButtonEventArgs e) { @@ -195,63 +195,24 @@ namespace CrowEdit return; if (fi is DirectoryInfo) return; - - OnOpenFile (Path.GetFileName (fi.FullName), Path.GetDirectoryName (fi.FullName)); + TextDocument doc = OpenedDocuments.FirstOrDefault (d => d.FullPath == fi.FullName); + if (doc != null) + CurrentDocument = doc; + else + openOrCreateFile (fi.FullName); } - void OnOpenFile (string filePath, string directory) { - if (IsDirty) { - MessageBox mb = MessageBox.ShowModal (this, MessageBox.Type.YesNo, "Current file has unsaved changes, are you sure?"); - mb.Yes += (sender, e) => openFile (filePath, directory); + + void onQueryCloseDocument (object sender, EventArgs e) { + TextDocument doc = sender as TextDocument; + if (doc.IsDirty) { + MessageBox mb = MessageBox.ShowModal (this, + MessageBox.Type.YesNoCancel, $"{doc.FileName} has unsaved changes.\nSave it now?"); + mb.Yes += (object _sender, EventArgs _e) => { doc.Save (); closeDocument (doc); }; + mb.No += (object _sender, EventArgs _e) => closeDocument (doc); } else - openFile (filePath, directory); + closeDocument (doc); } - void newFile () { - CurrentFilePath = Path.Combine (CurFileDir, _defaultFileName); - disableTextChangedEvent = true; - Source = ""; - disableTextChangedEvent = false; - resetUndoRedo (); - } - void openFile (string filePath, string directory) { - CurrentFilePath = Path.Combine(directory, filePath); - reloadFromFile (); - resetUndoRedo (); - } - void save (string filePath, string directory) { - CurrentFilePath = Path.Combine (directory, filePath); - using (StreamWriter sr = new StreamWriter (CurrentFilePath)) { - sr.Write (source); - } - dirtyUndoLevel = undoStack.Count; - - NotifyValueChanged ("IsDirty", IsDirty); - } - - void reloadFromFile () { - disableTextChangedEvent = true; - if (File.Exists (CurrentFilePath)) { - using (Stream s = new FileStream (CurrentFilePath, FileMode.Open)) { - using (StreamReader sr = new StreamReader (s)) - Source = sr.ReadToEnd (); - } - } - disableTextChangedEvent = false; - resetUndoRedo (); - } - void resetUndoRedo () { - undoStack.Clear (); - redoStack.Clear (); - CMDUndo.CanExecute = false; - CMDRedo.CanExecute = false; - dirtyUndoLevel = 0; - } - static void Main () - { - using (CrowEdit win = new CrowEdit ()) - win.Run (); - } - public CrowEdit () : base(800, 600) {} protected override void OnInitialized () { base.OnInitialized (); @@ -260,22 +221,9 @@ namespace CrowEdit CurrentDir = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments); initCommands (); - Load ("#CrowEdit.ui.main.crow").DataSource = this; - editor = FindByName ("tb") as TextBox; - - if (ReopenLastFile) - reloadFromFile (); - } - TextBox editor; - void textView_KeyDown (object sender, Crow.KeyEventArgs e) { - if (Ctrl && e.Key == Glfw.Key.W) { - if (Shift) - CMDRedo.Execute (); - else - CMDUndo.Execute (); + Load ("#CrowEdit.ui.main.crow").DataSource = this; - } - } + } } } diff --git a/src/PluginsLoadContext.cs b/src/PluginsLoadContext.cs new file mode 100644 index 0000000..09c70fe --- /dev/null +++ b/src/PluginsLoadContext.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2021-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.Loader; + +namespace CrowEdit +{ + public class PluginsLoadContext : AssemblyLoadContext { + List loadedPlugins = new List (); + public PluginsLoadContext () + : base ("CrowEditPluginsContext", true) { + + } + protected override Assembly Load(AssemblyName assemblyName) + { + return loadedPlugins.Contains (assemblyName.Name) ? + base.Load(assemblyName) : AssemblyLoadContext.Default.LoadFromAssemblyName (assemblyName); + } + + } +} diff --git a/ui/CrowEdit.style b/ui/CrowEdit.style index ed6a861..7df64a6 100644 --- a/ui/CrowEdit.style +++ b/ui/CrowEdit.style @@ -1,4 +1,6 @@ -MenuIconSize = "14"; +DocTabViewBackground = "DarkGrey"; +SelectedTabBackground = "Onyx"; +MenuIconSize = "14"; MenuItem { Template = "#CrowEdit.ui.MenuItem.template"; @@ -9,3 +11,20 @@ MenuIcon { Width = "${MenuIconSize}"; Height = "${MenuIconSize}"; } + +suggestionsListBox { + Template = "#CrowEdit.ui.Suggestions.template"; + Width = "Fit"; + Height = "Fit"; + MaximumSize = "300, 120"; + Background = "Jet"; + UseLoadingThread = "false"; +} + +Editor { + Background="White"; + Foreground="Black"; + Text=""; + Multiline="true"; +} + diff --git a/ui/MenuItem.template b/ui/MenuItem.template index 8c8f1d9..7c4e208 100644 --- a/ui/MenuItem.template +++ b/ui/MenuItem.template @@ -1,23 +1,19 @@  diff --git a/ui/Suggestions.template b/ui/Suggestions.template new file mode 100644 index 0000000..8bc5574 --- /dev/null +++ b/ui/Suggestions.template @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/ui/main.crow b/ui/main.crow index ee1fe96..da8385e 100755 --- a/ui/main.crow +++ b/ui/main.crow @@ -1,19 +1,19 @@  - + - - + + - - - - - - + + + + + + @@ -23,7 +23,7 @@ - + @@ -32,38 +32,59 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/ui/openFile.crow b/ui/openFile.crow deleted file mode 100644 index 433c534..0000000 --- a/ui/openFile.crow +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/ui/saveFile.crow b/ui/saveFile.crow deleted file mode 100644 index f5fb1f5..0000000 --- a/ui/saveFile.crow +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/ui/sourceEditor.itmp b/ui/sourceEditor.itmp new file mode 100644 index 0000000..6c7dc13 --- /dev/null +++ b/ui/sourceEditor.itmp @@ -0,0 +1,24 @@ + + + + + + + + + + + + + +