From: Jean-Philippe Bruyère Date: Fri, 12 Jun 2020 11:23:04 +0000 (+0200) Subject: Crow.Coding library X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=28ccf83dbdf3eeadf8d016bd768327ee844478fe;p=jp%2Fcrowedit.git Crow.Coding library --- diff --git a/.gitignore b/.gitignore index 88e1fca..a9f5d34 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ Thumbs.db #dotCover *.dotCover +.vs diff --git a/CodeBufferEventArgs.cs b/CodeBufferEventArgs.cs deleted file mode 100644 index 07dd25b..0000000 --- a/CodeBufferEventArgs.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Crow.Coding -{ - public class CodeBufferEventArgs : EventArgs { - public int LineStart; - public int LineCount; - - public CodeBufferEventArgs(int lineNumber) { - LineStart = lineNumber; - LineCount = 1; - } - public CodeBufferEventArgs(int lineStart, int lineCount) { - LineStart = lineStart; - LineCount = lineCount; - } - } - -} - diff --git a/Crow.Coding/Crow.Coding.csproj b/Crow.Coding/Crow.Coding.csproj new file mode 100644 index 0000000..48dc4ec --- /dev/null +++ b/Crow.Coding/Crow.Coding.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.0 + false + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Crow.Coding/Properties/AssemblyInfo.cs b/Crow.Coding/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d864652 --- /dev/null +++ b/Crow.Coding/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/Crow.Coding/icons/blank-file.svg b/Crow.Coding/icons/blank-file.svg new file mode 100644 index 0000000..8136979 --- /dev/null +++ b/Crow.Coding/icons/blank-file.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/compile.svg b/Crow.Coding/icons/compile.svg new file mode 100644 index 0000000..c1a14e7 --- /dev/null +++ b/Crow.Coding/icons/compile.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Crow.Coding/icons/copy-file.svg b/Crow.Coding/icons/copy-file.svg new file mode 100644 index 0000000..63c2dd3 --- /dev/null +++ b/Crow.Coding/icons/copy-file.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/curly-brackets.svg b/Crow.Coding/icons/curly-brackets.svg new file mode 100644 index 0000000..89ef798 --- /dev/null +++ b/Crow.Coding/icons/curly-brackets.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/icons/edit.svg b/Crow.Coding/icons/edit.svg new file mode 100644 index 0000000..366862c --- /dev/null +++ b/Crow.Coding/icons/edit.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/eraser.svg b/Crow.Coding/icons/eraser.svg new file mode 100644 index 0000000..5dd73ba --- /dev/null +++ b/Crow.Coding/icons/eraser.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/file.svg b/Crow.Coding/icons/file.svg new file mode 100644 index 0000000..9d06b00 --- /dev/null +++ b/Crow.Coding/icons/file.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/folder.svg b/Crow.Coding/icons/folder.svg new file mode 100644 index 0000000..f59e2da --- /dev/null +++ b/Crow.Coding/icons/folder.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Crow.Coding/icons/light-bulb.svg b/Crow.Coding/icons/light-bulb.svg new file mode 100644 index 0000000..89ff236 --- /dev/null +++ b/Crow.Coding/icons/light-bulb.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/icons/move-arrows.svg b/Crow.Coding/icons/move-arrows.svg new file mode 100644 index 0000000..ecbb2f9 --- /dev/null +++ b/Crow.Coding/icons/move-arrows.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/open.svg b/Crow.Coding/icons/open.svg new file mode 100644 index 0000000..bd8d7d9 --- /dev/null +++ b/Crow.Coding/icons/open.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/icons/paint-brush.svg b/Crow.Coding/icons/paint-brush.svg new file mode 100644 index 0000000..2bdd5be --- /dev/null +++ b/Crow.Coding/icons/paint-brush.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Crow.Coding/icons/palette.svg b/Crow.Coding/icons/palette.svg new file mode 100644 index 0000000..8e425f7 --- /dev/null +++ b/Crow.Coding/icons/palette.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/paste-on-document.svg b/Crow.Coding/icons/paste-on-document.svg new file mode 100644 index 0000000..b0a705e --- /dev/null +++ b/Crow.Coding/icons/paste-on-document.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/pin.svg b/Crow.Coding/icons/pin.svg new file mode 100644 index 0000000..b36340b --- /dev/null +++ b/Crow.Coding/icons/pin.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/question.svg b/Crow.Coding/icons/question.svg new file mode 100644 index 0000000..fb8e3d3 --- /dev/null +++ b/Crow.Coding/icons/question.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/icons/redo.svg b/Crow.Coding/icons/redo.svg new file mode 100644 index 0000000..59fcc90 --- /dev/null +++ b/Crow.Coding/icons/redo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/save.svg b/Crow.Coding/icons/save.svg new file mode 100644 index 0000000..6aa6714 --- /dev/null +++ b/Crow.Coding/icons/save.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/icons/scissors.svg b/Crow.Coding/icons/scissors.svg new file mode 100644 index 0000000..4b5a225 --- /dev/null +++ b/Crow.Coding/icons/scissors.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/search.svg b/Crow.Coding/icons/search.svg new file mode 100644 index 0000000..f3eb368 --- /dev/null +++ b/Crow.Coding/icons/search.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/text-file.svg b/Crow.Coding/icons/text-file.svg new file mode 100644 index 0000000..826bc32 --- /dev/null +++ b/Crow.Coding/icons/text-file.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Border.svg b/Crow.Coding/icons/toolbox/Crow.Border.svg new file mode 100644 index 0000000..09eb7ac --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Border.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Button.svg b/Crow.Coding/icons/toolbox/Crow.Button.svg new file mode 100644 index 0000000..01f5c6c --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Button.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.CheckBox.svg b/Crow.Coding/icons/toolbox/Crow.CheckBox.svg new file mode 100644 index 0000000..2f0b083 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.CheckBox.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.ColorPicker.svg b/Crow.Coding/icons/toolbox/Crow.ColorPicker.svg new file mode 100644 index 0000000..517a26a --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.ColorPicker.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.ComboBox.svg b/Crow.Coding/icons/toolbox/Crow.ComboBox.svg new file mode 100644 index 0000000..1cb88fa --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.ComboBox.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Container.svg b/Crow.Coding/icons/toolbox/Crow.Container.svg new file mode 100644 index 0000000..d7d1dc8 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Container.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.DirectoryView.svg b/Crow.Coding/icons/toolbox/Crow.DirectoryView.svg new file mode 100644 index 0000000..9029469 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.DirectoryView.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Docker.svg b/Crow.Coding/icons/toolbox/Crow.Docker.svg new file mode 100644 index 0000000..e38a283 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Docker.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Expandable.svg b/Crow.Coding/icons/toolbox/Crow.Expandable.svg new file mode 100644 index 0000000..1c56d56 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Expandable.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.FileDialog.svg b/Crow.Coding/icons/toolbox/Crow.FileDialog.svg new file mode 100644 index 0000000..25142ea --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.FileDialog.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Grid.svg b/Crow.Coding/icons/toolbox/Crow.Grid.svg new file mode 100644 index 0000000..6151f97 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Grid.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Group.svg b/Crow.Coding/icons/toolbox/Crow.Group.svg new file mode 100644 index 0000000..eae67f6 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Group.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.GroupBox.svg b/Crow.Coding/icons/toolbox/Crow.GroupBox.svg new file mode 100644 index 0000000..e469779 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.GroupBox.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.HorizontalStack.svg b/Crow.Coding/icons/toolbox/Crow.HorizontalStack.svg new file mode 100644 index 0000000..f8e7025 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.HorizontalStack.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.IMLContainer.svg b/Crow.Coding/icons/toolbox/Crow.IMLContainer.svg new file mode 100644 index 0000000..b5687ba --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.IMLContainer.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Image.svg b/Crow.Coding/icons/toolbox/Crow.Image.svg new file mode 100644 index 0000000..11356c0 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Image.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Label.svg b/Crow.Coding/icons/toolbox/Crow.Label.svg new file mode 100644 index 0000000..65bb85b --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Label.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.ListBox.svg b/Crow.Coding/icons/toolbox/Crow.ListBox.svg new file mode 100644 index 0000000..40d1673 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.ListBox.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Menu.svg b/Crow.Coding/icons/toolbox/Crow.Menu.svg new file mode 100644 index 0000000..b6b2111 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Menu.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.MenuItem.svg b/Crow.Coding/icons/toolbox/Crow.MenuItem.svg new file mode 100644 index 0000000..c8bd847 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.MenuItem.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.MessageBox.svg b/Crow.Coding/icons/toolbox/Crow.MessageBox.svg new file mode 100644 index 0000000..16ebd72 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.MessageBox.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.ProgressBar.svg b/Crow.Coding/icons/toolbox/Crow.ProgressBar.svg new file mode 100644 index 0000000..884f185 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.ProgressBar.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.RadioButton.svg b/Crow.Coding/icons/toolbox/Crow.RadioButton.svg new file mode 100644 index 0000000..6c33e84 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.RadioButton.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.ScrollBar.svg b/Crow.Coding/icons/toolbox/Crow.ScrollBar.svg new file mode 100644 index 0000000..91a1f84 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.ScrollBar.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Scroller.svg b/Crow.Coding/icons/toolbox/Crow.Scroller.svg new file mode 100644 index 0000000..bbc9719 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Scroller.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Shape.svg b/Crow.Coding/icons/toolbox/Crow.Shape.svg new file mode 100644 index 0000000..de5dd37 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Shape.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Slider.svg b/Crow.Coding/icons/toolbox/Crow.Slider.svg new file mode 100644 index 0000000..fe41c2b --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Slider.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Spinner.svg b/Crow.Coding/icons/toolbox/Crow.Spinner.svg new file mode 100644 index 0000000..5fa848a --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Spinner.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Splitter.svg b/Crow.Coding/icons/toolbox/Crow.Splitter.svg new file mode 100644 index 0000000..9fcd9dd --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Splitter.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.TabItem.svg b/Crow.Coding/icons/toolbox/Crow.TabItem.svg new file mode 100644 index 0000000..a9aaed3 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.TabItem.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.TabView.svg b/Crow.Coding/icons/toolbox/Crow.TabView.svg new file mode 100644 index 0000000..942358b --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.TabView.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.TemplatedContainer.svg b/Crow.Coding/icons/toolbox/Crow.TemplatedContainer.svg new file mode 100644 index 0000000..34a0aa2 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.TemplatedContainer.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.TemplatedGroup.svg b/Crow.Coding/icons/toolbox/Crow.TemplatedGroup.svg new file mode 100644 index 0000000..14ea6f3 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.TemplatedGroup.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.TextBox.svg b/Crow.Coding/icons/toolbox/Crow.TextBox.svg new file mode 100644 index 0000000..c1fc2bb --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.TextBox.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.TreeView.svg b/Crow.Coding/icons/toolbox/Crow.TreeView.svg new file mode 100644 index 0000000..b863291 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.TreeView.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.VerticalStack.svg b/Crow.Coding/icons/toolbox/Crow.VerticalStack.svg new file mode 100644 index 0000000..c3c3ec9 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.VerticalStack.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Window.svg b/Crow.Coding/icons/toolbox/Crow.Window.svg new file mode 100644 index 0000000..74b0a24 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Window.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/Crow.Wrapper.svg b/Crow.Coding/icons/toolbox/Crow.Wrapper.svg new file mode 100644 index 0000000..9e69e41 --- /dev/null +++ b/Crow.Coding/icons/toolbox/Crow.Wrapper.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/bar-chart.svg b/Crow.Coding/icons/toolbox/bar-chart.svg new file mode 100644 index 0000000..ff86c96 --- /dev/null +++ b/Crow.Coding/icons/toolbox/bar-chart.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/bar-menu.svg b/Crow.Coding/icons/toolbox/bar-menu.svg new file mode 100644 index 0000000..87ec061 --- /dev/null +++ b/Crow.Coding/icons/toolbox/bar-menu.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/bullets.svg b/Crow.Coding/icons/toolbox/bullets.svg new file mode 100644 index 0000000..81fb1f0 --- /dev/null +++ b/Crow.Coding/icons/toolbox/bullets.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/calendar.svg b/Crow.Coding/icons/toolbox/calendar.svg new file mode 100644 index 0000000..9ceaa1e --- /dev/null +++ b/Crow.Coding/icons/toolbox/calendar.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/check-square-1.svg b/Crow.Coding/icons/toolbox/check-square-1.svg new file mode 100644 index 0000000..e198007 --- /dev/null +++ b/Crow.Coding/icons/toolbox/check-square-1.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/icons/toolbox/database.svg b/Crow.Coding/icons/toolbox/database.svg new file mode 100644 index 0000000..65a8f06 --- /dev/null +++ b/Crow.Coding/icons/toolbox/database.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/ellipsis.svg b/Crow.Coding/icons/toolbox/ellipsis.svg new file mode 100644 index 0000000..cff94cc --- /dev/null +++ b/Crow.Coding/icons/toolbox/ellipsis.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/file-code.svg b/Crow.Coding/icons/toolbox/file-code.svg new file mode 100644 index 0000000..a2fd2d1 --- /dev/null +++ b/Crow.Coding/icons/toolbox/file-code.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/grab.svg b/Crow.Coding/icons/toolbox/grab.svg new file mode 100644 index 0000000..25bc571 --- /dev/null +++ b/Crow.Coding/icons/toolbox/grab.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/hash.svg b/Crow.Coding/icons/toolbox/hash.svg new file mode 100644 index 0000000..82196fb --- /dev/null +++ b/Crow.Coding/icons/toolbox/hash.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/line-list.svg b/Crow.Coding/icons/toolbox/line-list.svg new file mode 100644 index 0000000..ec15f7b --- /dev/null +++ b/Crow.Coding/icons/toolbox/line-list.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/list.svg b/Crow.Coding/icons/toolbox/list.svg new file mode 100644 index 0000000..9aad88f --- /dev/null +++ b/Crow.Coding/icons/toolbox/list.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/modal-list.svg b/Crow.Coding/icons/toolbox/modal-list.svg new file mode 100644 index 0000000..f1d8f70 --- /dev/null +++ b/Crow.Coding/icons/toolbox/modal-list.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/options.svg b/Crow.Coding/icons/toolbox/options.svg new file mode 100644 index 0000000..a56f6be --- /dev/null +++ b/Crow.Coding/icons/toolbox/options.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/package.svg b/Crow.Coding/icons/toolbox/package.svg new file mode 100644 index 0000000..07f8b37 --- /dev/null +++ b/Crow.Coding/icons/toolbox/package.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/padding.svg b/Crow.Coding/icons/toolbox/padding.svg new file mode 100644 index 0000000..d93b246 --- /dev/null +++ b/Crow.Coding/icons/toolbox/padding.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/picture-file.svg b/Crow.Coding/icons/toolbox/picture-file.svg new file mode 100644 index 0000000..c45a6ad --- /dev/null +++ b/Crow.Coding/icons/toolbox/picture-file.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/pointer.svg b/Crow.Coding/icons/toolbox/pointer.svg new file mode 100644 index 0000000..605c0e2 --- /dev/null +++ b/Crow.Coding/icons/toolbox/pointer.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/puzzle-piece.svg b/Crow.Coding/icons/toolbox/puzzle-piece.svg new file mode 100644 index 0000000..b09c47e --- /dev/null +++ b/Crow.Coding/icons/toolbox/puzzle-piece.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/refresh-file.svg b/Crow.Coding/icons/toolbox/refresh-file.svg new file mode 100644 index 0000000..248e420 --- /dev/null +++ b/Crow.Coding/icons/toolbox/refresh-file.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/sliders.svg b/Crow.Coding/icons/toolbox/sliders.svg new file mode 100644 index 0000000..81f72d1 --- /dev/null +++ b/Crow.Coding/icons/toolbox/sliders.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/split-browser-1.svg b/Crow.Coding/icons/toolbox/split-browser-1.svg new file mode 100644 index 0000000..4dfd93a --- /dev/null +++ b/Crow.Coding/icons/toolbox/split-browser-1.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/table.svg b/Crow.Coding/icons/toolbox/table.svg new file mode 100644 index 0000000..0b42122 --- /dev/null +++ b/Crow.Coding/icons/toolbox/table.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/toolbox/tasks.svg b/Crow.Coding/icons/toolbox/tasks.svg new file mode 100644 index 0000000..8793c88 --- /dev/null +++ b/Crow.Coding/icons/toolbox/tasks.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Crow.Coding/icons/toolbox/warning.svg b/Crow.Coding/icons/toolbox/warning.svg new file mode 100644 index 0000000..f5a2573 --- /dev/null +++ b/Crow.Coding/icons/toolbox/warning.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/tools.svg b/Crow.Coding/icons/tools.svg new file mode 100644 index 0000000..5ad8a8d --- /dev/null +++ b/Crow.Coding/icons/tools.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Crow.Coding/icons/trash.svg b/Crow.Coding/icons/trash.svg new file mode 100644 index 0000000..e73a5e3 --- /dev/null +++ b/Crow.Coding/icons/trash.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/icons/undo.svg b/Crow.Coding/icons/undo.svg new file mode 100644 index 0000000..f78f134 --- /dev/null +++ b/Crow.Coding/icons/undo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/icons/zoom-in.svg b/Crow.Coding/icons/zoom-in.svg new file mode 100644 index 0000000..540a93b --- /dev/null +++ b/Crow.Coding/icons/zoom-in.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/icons/zoom-out.svg b/Crow.Coding/icons/zoom-out.svg new file mode 100644 index 0000000..6bd256f --- /dev/null +++ b/Crow.Coding/icons/zoom-out.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/images/save.svg b/Crow.Coding/images/save.svg new file mode 100644 index 0000000..7bdc551 --- /dev/null +++ b/Crow.Coding/images/save.svg @@ -0,0 +1,3421 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Crow.Coding/src/CodeBuffer/CodeBuffer.cs b/Crow.Coding/src/CodeBuffer/CodeBuffer.cs new file mode 100644 index 0000000..48e00b6 --- /dev/null +++ b/Crow.Coding/src/CodeBuffer/CodeBuffer.cs @@ -0,0 +1,533 @@ +// +// CodeTextBuffer.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2017 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Diagnostics; +using System.Threading; + +namespace Crow.Coding +{ + /// + /// Code buffer, lines are arranged in a List'string', new line chars are removed during string.split on '\n...', + /// + public class CodeBuffer + { + public ReaderWriterLockSlim editMutex = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); + + //those events are handled in SourceEditor to help keeping sync between textbuffer,parser and editor. + //modified lines are marked for reparse + #region Events + public event EventHandler LineUpadateEvent; + public event EventHandler LineRemoveEvent; + public event EventHandler LineAdditionEvent; + public event EventHandler FoldingEvent; + public event EventHandler BufferCleared; + public event EventHandler SelectionChanged; + public event EventHandler PositionChanged; + #endregion + + string lineBreak = Interface.LineBreak; + List lines = new List(); + public int longestLineIdx = 0; + public int longestLineCharCount = 0; + /// + /// real position in char arrays, tab = 1 char + /// + int _currentLine = 0; + int _currentCol = 0; + + public int LineCount { get { return lines.Count;}} + public int IndexOf (CodeLine cl) { + return lines.IndexOf (cl); + } + + public CodeLine this[int i] + { + get { return i < LineCount ? lines[i] : null; } + set { + if (lines [i] == value) + return; + editMutex.EnterWriteLock (); + lines [i] = value; + editMutex.ExitWriteLock (); + LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); + } + } + + public void RemoveAt(int i){ + editMutex.EnterWriteLock (); + lines.RemoveAt (i); + editMutex.ExitWriteLock (); + LineRemoveEvent.Raise (this, new CodeBufferEventArgs (i)); + } + public void Insert(int i, string item){ + editMutex.EnterWriteLock (); + lines.Insert (i, item); + editMutex.ExitWriteLock (); + LineAdditionEvent.Raise (this, new CodeBufferEventArgs (i)); + } + public void Add(CodeLine item){ + editMutex.EnterWriteLock (); + lines.Add (item); + editMutex.ExitWriteLock (); + LineAdditionEvent.Raise (this, new CodeBufferEventArgs (lines.Count - 1)); + } + public void AddRange (string[] items){ + int start = lines.Count; + editMutex.EnterWriteLock (); + for (int i = 0; i < items.Length; i++) + lines.Add (items [i]); + editMutex.ExitWriteLock (); + LineAdditionEvent.Raise (this, new CodeBufferEventArgs (start, items.Length)); + } + public void AddRange (CodeLine[] items){ + int start = lines.Count; + editMutex.EnterWriteLock (); + lines.AddRange (items); + editMutex.ExitWriteLock (); + LineAdditionEvent.Raise (this, new CodeBufferEventArgs (start, items.Length)); + } + public void Clear () { + editMutex.EnterWriteLock (); + longestLineCharCount = 0; + lines.Clear (); + editMutex.ExitWriteLock (); + BufferCleared.Raise (this, null); + } + public void UpdateLine(int i, string newContent){ + editMutex.EnterWriteLock (); + this [i].Content = newContent; + editMutex.ExitWriteLock (); + LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); + } + public void AppenedLine(int i, string newContent){ + editMutex.EnterWriteLock (); + this [i].Content += newContent; + editMutex.ExitWriteLock (); + LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); + } + public void RemoveLeadingTab (int l) { + if (this [l] [0] == '\t') { + UpdateLine (l, this [l].Content.Substring (1)); + return; + } + int i = 0; + while (i < Interface.TAB_SIZE) { + if (this [l] [i] != ' ') + break; + i++; + } + if (i > 0) + UpdateLine (l, this [l].Content.Substring (i)); + } + public void ToogleFolding (int line) { + if (!this [line].IsFoldable) + return; + editMutex.EnterWriteLock (); + this [line].IsFolded = !this [line].IsFolded; + editMutex.ExitWriteLock (); + FoldingEvent.Raise (this, new CodeBufferEventArgs (line)); + } + public void Load(string rawSource, string lineBrkRegex = @"\r\n|\r|\n|\\\n") { + this.Clear(); + if (string.IsNullOrEmpty (rawSource)) + return; + + AddRange (Regex.Split (rawSource, lineBrkRegex)); + + lineBreak = detectLineBreakKind (rawSource); + + /*for (int i = 0; i < LineCount; i++) { + ToogleFolding (i); + }*/ + } + + /// + /// Finds the longest visual line as printed on screen with tabulation replaced with n spaces + /// + public void FindLongestVisualLine(){ + longestLineCharCount = 0; + editMutex.EnterReadLock (); + for (int i = 0; i < this.LineCount; i++) { + if (lines[i].PrintableLength > longestLineCharCount) { + longestLineCharCount = lines[i].PrintableLength; + longestLineIdx = i; + } + } + editMutex.ExitReadLock (); + //Debug.WriteLine ("Longest line: {0}->{1}", longestLineIdx, longestLineCharCount); + } + /// line break could be '\r' or '\n' or '\r\n' + static string detectLineBreakKind(string buffer){ + string strLB = ""; + + if (string.IsNullOrEmpty(buffer)) + return Interface.LineBreak; + int i = 0; + while ( i < buffer.Length) { + if (buffer [i] == '\r') { + strLB += '\r'; + i++; + } + if (i < buffer.Length) { + if (buffer [i] == '\r') + return "\r"; + if (buffer[i] == '\n') + strLB += '\n'; + } + if (!string.IsNullOrEmpty (strLB)) + return strLB; + i++; + } + return Interface.LineBreak; + } + /// + /// return all lines with linebreaks + /// + public string FullText{ + get { + if (lines.Count == 0) + return ""; + string tmp = ""; + editMutex.EnterReadLock (); + for (int i = 0; i < lines.Count -1; i++) + tmp += lines [i].Content + this.lineBreak; + tmp += lines [lines.Count - 1].Content; + editMutex.ExitReadLock (); + return tmp; + } + } + + /// + /// unfolded and not in folds line count + /// + public int UnfoldedLines { + get { + int i = 0, vl = 0; + editMutex.EnterReadLock (); + while (i < LineCount) { + if (this [i].IsFolded) { + i = GetEndNodeIndex (i); + if (i < 0) { + Console.WriteLine ("error folding"); + break; + } + } + i++; + vl++; + } + editMutex.ExitReadLock (); + //Debug.WriteLine ("unfolded lines: " + vl); + return vl; + } + } + + /// + /// convert visual position to buffer position + /// + Point getBuffPos (Point visualPos) { + int i = 0; + int buffCol = 0; + while (i < visualPos.X) { + if (this [visualPos.Y] [buffCol] == '\t') + i += Interface.TAB_SIZE; + else + i++; + buffCol++; + } + return new Point (buffCol, visualPos.Y); + } + + public int GetEndNodeIndex (int line) { + return IndexOf (this [line].SyntacticNode.EndLine); + } + + int ConverteTabulatedPosOfCurLine (int column) { + int tmp = 0; + int i = 0; + while (i < lines [_currentLine].Content.Length){ + if (lines [_currentLine].Content [i] == '\t') + tmp += 4; + else + tmp++; + if (tmp > column) + break; + i++; + } + return i; + } + + public int CurrentTabulatedColumn { + get { + return lines [_currentLine].Content.Substring (0, _currentCol). + Replace ("\t", new String (' ', Interface.TAB_SIZE)).Length; + } + } + /// + /// Gets visual position computed from actual buffer position + /// +// public Point TabulatedPosition { +// get { return new Point (TabulatedColumn, _currentLine); } +// } + /// + /// set buffer current position from visual position + /// +// public void SetBufferPos(Point tabulatedPosition) { +// CurrentPosition = getBuffPos(tabulatedPosition); +// } + + #region Editing and moving cursor + Point selStartPos = -1; //selection start (row,column) + Point selEndPos = -1; //selection end (row,column) + + public bool SelectionInProgress { get { return selStartPos >= 0; }} + public void SetSelStartPos () { + selStartPos = selEndPos = CurrentPosition; + SelectionChanged.Raise (this, null); + } + public void SetSelEndPos () { + selEndPos = CurrentPosition; + SelectionChanged.Raise (this, null); + } + public void SetSelectionOnFullLines () { + if (!SelectionInProgress) + return; + Point s = new Point (0, SelectionStart.Y); + Point e = new Point (this [SelectionEnd.Y].Length, SelectionEnd.Y); + selStartPos = s; + selEndPos = e; + SelectionChanged.Raise (this, null); + } + /// + /// Set selection in buffer to -1, empty selection + /// + public void ResetSelection () { + selStartPos = selEndPos = -1; + SelectionChanged.Raise (this, null); + } + + public string SelectedText { + get { + if (SelectionIsEmpty) + return ""; + Point selStart = SelectionStart; + Point selEnd = SelectionEnd; + if (selStart.Y == selEnd.Y) + return this [selStart.Y].Content.Substring (selStart.X, selEnd.X - selStart.X); + string tmp = ""; + tmp = this [selStart.Y].Content.Substring (selStart.X); + for (int l = selStart.Y + 1; l < selEnd.Y; l++) { + tmp += Interface.LineBreak + this [l].Content; + } + tmp += Interface.LineBreak + this [selEnd.Y].Content.Substring (0, selEnd.X); + return tmp; + } + } + /// + /// ordered selection start and end positions in char units + /// + public Point SelectionStart { + get { return selEndPos < 0 || selStartPos.Y < selEndPos.Y ? selStartPos : + selStartPos.Y > selEndPos.Y ? selEndPos : + selStartPos.X < selEndPos.X ? selStartPos : selEndPos; } + } + public Point SelectionEnd { + get { return selEndPos < 0 || selStartPos.Y > selEndPos.Y ? selStartPos : + selStartPos.Y < selEndPos.Y ? selEndPos : + selStartPos.X > selEndPos.X ? selStartPos : selEndPos; } + } + public bool SelectionIsEmpty + { get { return selEndPos == selStartPos; } } + int requestedColumn = -1; + /// + /// Current column in buffer coordinate, tabulation = 1 char + /// + public int CurrentColumn{ + get { return _currentCol; } + set { + if (value == _currentCol) + return; + + editMutex.EnterReadLock (); + + if (value < 0) + _currentCol = 0; + else if (value > lines [_currentLine].Length) + _currentCol = lines [_currentLine].Length; + else + _currentCol = value; + + requestedColumn = CurrentTabulatedColumn; + + editMutex.ExitReadLock (); + + PositionChanged.Raise (this, null); + } + } + /// + /// Current row in buffer coordinate, tabulation = 1 char + /// + public int CurrentLine{ + get { return _currentLine; } + set { + if (value == _currentLine) + return; + + editMutex.EnterReadLock (); + + if (LineCount == 0) + _currentLine = 0; + else if (value >= lines.Count) + _currentLine = lines.Count-1; + else if (value < 0) + _currentLine = 0; + else + _currentLine = value; +// if (_currentCol < 0) +// requestedColumn = tabu _currentCol; + int tabulatedRequestedCol = ConverteTabulatedPosOfCurLine(requestedColumn); + if (requestedColumn > lines [_currentLine].PrintableLength) + _currentCol = lines [_currentLine].Length; + else + //_currentCol = requestedColumn; + _currentCol = tabulatedRequestedCol; + //Debug.WriteLine ("buff cur line: " + _currentLine); + + editMutex.ExitReadLock(); + + PositionChanged.Raise (this, null); + } + } + public CodeLine CurrentCodeLine { + get { return this [_currentLine]; } + } + /// + /// Current position in buffer coordinate, tabulation = 1 char + /// + public Point CurrentPosition { + get { return new Point(CurrentColumn, CurrentLine); } +// set { +// _currentCol = value.X; +// _currentLine = value.Y; +// } + } + /// + /// get char at current position in buffer + /// + protected Char CurrentChar { get { return lines [CurrentLine] [CurrentColumn]; } } + + public void GotoWordStart(){ + if (this[CurrentLine].Length == 0) + return; + CurrentColumn--; + //skip white spaces + while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0) + CurrentColumn--; + while (char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0) + CurrentColumn--; + if (!char.IsLetterOrDigit (this.CurrentChar)) + CurrentColumn++; + } + public void GotoWordEnd(){ + //skip white spaces + if (CurrentColumn >= this [CurrentLine].Length - 1) + return; + while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < this [CurrentLine].Length-1) + CurrentColumn++; + while (char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < this [CurrentLine].Length-1) + CurrentColumn++; + if (char.IsLetterOrDigit (this.CurrentChar)) + CurrentColumn++; + } + public void DeleteChar() + { + editMutex.EnterWriteLock (); + if (SelectionIsEmpty) { + if (CurrentColumn == 0) { + if (CurrentLine == 0) { + editMutex.ExitWriteLock (); + return; + } + CurrentLine--; + CurrentColumn = this [CurrentLine].Length; + AppenedLine (CurrentLine, this [CurrentLine + 1].Content); + RemoveAt (CurrentLine + 1); + editMutex.ExitWriteLock (); + return; + } + CurrentColumn--; + UpdateLine (CurrentLine, this [CurrentLine].Content.Remove (CurrentColumn, 1)); + } else { + int linesToRemove = SelectionEnd.Y - SelectionStart.Y + 1; + int l = SelectionStart.Y; + + if (linesToRemove > 0) { + UpdateLine (l, this [l].Content.Remove (SelectionStart.X, this [l].Length - SelectionStart.X) + + this [SelectionEnd.Y].Content.Substring (SelectionEnd.X, this [SelectionEnd.Y].Length - SelectionEnd.X)); + l++; + for (int c = 0; c < linesToRemove - 1; c++) + RemoveAt (l); + CurrentLine = SelectionStart.Y; + CurrentColumn = SelectionStart.X; + } else + UpdateLine (l, this [l].Content.Remove (SelectionStart.X, SelectionEnd.X - SelectionStart.X)); + CurrentColumn = SelectionStart.X; + ResetSelection (); + } + editMutex.ExitWriteLock (); + } + /// + /// Insert new string at caret position, should be sure no line break is inside. + /// + /// String. + public void Insert(string str) + { + if (!SelectionIsEmpty) + this.DeleteChar (); + string[] strLines = Regex.Split (str, "\r\n|\r|\n|" + @"\\n").ToArray(); + UpdateLine (CurrentLine, this [CurrentLine].Content.Insert (CurrentColumn, strLines[0])); + CurrentColumn += strLines[0].Length; + for (int i = 1; i < strLines.Length; i++) { + InsertLineBreak (); + UpdateLine (CurrentLine, this [CurrentLine].Content.Insert (CurrentColumn, strLines[i])); + CurrentColumn += strLines[i].Length; + } + } + /// + /// Insert a line break. + /// + public void InsertLineBreak() + { + if (CurrentColumn > 0) { + Insert (CurrentLine + 1, this [CurrentLine].Content.Substring (CurrentColumn)); + UpdateLine (CurrentLine, this [CurrentLine].Content.Substring (0, CurrentColumn)); + } else + Insert(CurrentLine, ""); + + CurrentColumn = 0; + CurrentLine++; + } + #endregion + } +} + diff --git a/Crow.Coding/src/CodeBuffer/CodeBufferEventArgs.cs b/Crow.Coding/src/CodeBuffer/CodeBufferEventArgs.cs new file mode 100644 index 0000000..07dd25b --- /dev/null +++ b/Crow.Coding/src/CodeBuffer/CodeBufferEventArgs.cs @@ -0,0 +1,20 @@ +using System; + +namespace Crow.Coding +{ + public class CodeBufferEventArgs : EventArgs { + public int LineStart; + public int LineCount; + + public CodeBufferEventArgs(int lineNumber) { + LineStart = lineNumber; + LineCount = 1; + } + public CodeBufferEventArgs(int lineStart, int lineCount) { + LineStart = lineStart; + LineCount = lineCount; + } + } + +} + diff --git a/Crow.Coding/src/CodeBuffer/CodeLine.cs b/Crow.Coding/src/CodeBuffer/CodeLine.cs new file mode 100644 index 0000000..70c6be2 --- /dev/null +++ b/Crow.Coding/src/CodeBuffer/CodeLine.cs @@ -0,0 +1,82 @@ +using System; +using System.Text; +using System.Collections.Generic; +using System.Linq; + +namespace Crow.Coding +{ + public class CodeLine + { + public string Content; + public List Tokens; + public int EndingState = 0; + public Node SyntacticNode; + public ParserException exception; + + public CodeLine (string _content){ + Content = _content; + Tokens = null; + exception = null; + } + + public char this[int i] + { + get { return Content[i]; } + set { + if (Content [i] == value) + return; + StringBuilder sb = new StringBuilder(Content); + sb[i] = value; + Content = sb.ToString(); + Tokens = null; + //LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); + } + } + public bool IsFoldable { get { return SyntacticNode != null && SyntacticNode.EndLine != SyntacticNode.StartLine + && SyntacticNode.EndLine != null; } } + public int FoldingLevel { get { return IsFoldable ? SyntacticNode.Level : 0; } } + public bool IsFolded = false; + public bool IsParsed { + get { return Tokens != null; } + } + public string PrintableContent { + get { + return string.IsNullOrEmpty (Content) ? "" : Content.Replace ("\t", new String (' ', Interface.TAB_SIZE)); + } + } + public int PrintableLength { + get { + return PrintableContent.Length; + } + } + public int Length { + get { + return string.IsNullOrEmpty (Content) ? 0 : Content.Length; + } + } + public int FirstNonBlankTokIndex { + get { return Tokens == null ? -1 : Tokens.FindIndex (tk=>tk.Type != BufferParser.TokenType.WhiteSpace); } + } + + public void SetLineInError (ParserException ex) { + Tokens = null; + exception = ex; + } + +// public static implicit operator string(CodeLine sl) { +// return sl == null ? "" : sl.Content; +// } + public static implicit operator CodeLine(string s) { + return new CodeLine(s); + } + public static bool operator ==(string s1, CodeLine s2) + { + return string.Equals (s1, s2.Content); + } + public static bool operator !=(string s1, CodeLine s2) + { + return !string.Equals (s1, s2.Content); + } + } +} + diff --git a/Crow.Coding/src/CodeBuffer/Node.cs b/Crow.Coding/src/CodeBuffer/Node.cs new file mode 100644 index 0000000..9db5542 --- /dev/null +++ b/Crow.Coding/src/CodeBuffer/Node.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace Crow.Coding +{ + public class Node + { + public Node Parent; + public string Name; + public string Type; + public CodeLine StartLine; + public CodeLine EndLine; + public Dictionary Attributes = new Dictionary (); + + public List Children = new List(); + + public Node () + { + } + + public void AddChild (Node child) { + child.Parent = this; + Children.Add (child); + } + + public int Level { + get { return Parent == null ? 1 : Parent.Level + 1; } + } + + public override string ToString () + { + return string.Format ("Name:{0}, Type:{1}\n\tparent:{2}", Name, Type, Parent); + } + } +} + diff --git a/Crow.Coding/src/CodeBuffer/TextBuffer.cs b/Crow.Coding/src/CodeBuffer/TextBuffer.cs new file mode 100644 index 0000000..e8c8718 --- /dev/null +++ b/Crow.Coding/src/CodeBuffer/TextBuffer.cs @@ -0,0 +1,527 @@ +// +// CodeTextBuffer.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2017 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Diagnostics; +using System.Threading; +using System.Text; + +namespace Crow.Text +{ + /// + /// Code buffer, lines are arranged in a List, new line chars are removed during string.split on '\n...', + /// + public class TextBuffer + { + public ReaderWriterLockSlim editMutex = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); + static Regex slb = new Regex ("\\n");//single char line break used internaly + Regex reghexLineBrk = new Regex(@"\r\n|\r|\n|\\\n");//original text line break regex + + #region Events + public event EventHandler LineUpadateEvent; + public event EventHandler LineRemoveEvent; + public event EventHandler LineAdditionEvent; + public event EventHandler BufferCleared; + public event EventHandler SelectionChanged; + public event EventHandler PositionChanged; + #endregion + + StringBuilder buffer = new StringBuilder(); + string lineBreak = Interface.LineBreak;//detected linebreak kind in original source + List lineLength = new List();//line length table + /// + /// real position in char arrays, tab = 1 char + /// + int _currentLine = 0; + int _currentCol = 0; + + /// + /// Gets the total line count. + /// + public int LineCount { get { return lineLength.Count;}} + + /// + /// get a substring in the buffer + /// + /// a new string + /// absolute index in the buffer + /// length of the substring + public string GetSubString (int idx, int length){ + return buffer.ToString (idx, length); + } + /// + /// Gets length of a line. + /// + /// length of line + /// line nuber + public int GetLineLength (int lineIdx) { + return lineLength [lineIdx]; + } + /// + /// get a single charactere in the buffer + /// + /// a single char + /// absolute index in the buffer + public char GetCharAt (int idx){ + return buffer [idx]; + } + /// + /// return full text with original line breaks + /// + /// a string containing the full text + public string FullText { + get { return buffer.Replace("\n", lineBreak).ToString (); } + set { + Load (value); + } + } + /// + /// Gets the buffer pointer of a line + /// + /// absolute index of the line in the buffer + /// line number + public int GetBufferIndexOfLine (int i) { + int ptr = 0; + editMutex.EnterReadLock (); + for (int j = 0; j < i; j++) { + ptr += lineLength [j]; + } + editMutex.ExitReadLock (); + return ptr; + } + /// + /// Gets the buffer pointer for the current position + /// + /// absolute index in the buffer of current position + public int BufferIndexOfCurrentPosition { + get {return GetBufferIndexOfLine (_currentLine) + _currentCol;} + } + public int this[int i] + { + get { + int ptr = 0; + editMutex.EnterReadLock (); + for (int j = 0; j < i; j++) { + ptr += lineLength [j]; + } + editMutex.ExitReadLock (); + return ptr; + } + } + /// + /// remove line number i + /// + /// index of the line + public void RemoveLine(int i){ + editMutex.EnterWriteLock (); + buffer.Remove (GetBufferIndexOfLine (i), lineLength [i]); + lineLength.RemoveAt (i); + editMutex.ExitWriteLock (); + LineRemoveEvent.Raise (this, new TextBufferEventArgs (i)); + } + /// + /// insert string without linebreaks at position i in buff + /// + /// absolute index in the buffer + /// linebreak free string + public void InsertAt(int i, string str){ + editMutex.EnterWriteLock (); + buffer.Insert (this [i], str); + lineLength.Insert (i, str.Length); + editMutex.ExitWriteLock (); + LineAdditionEvent.Raise (this, new TextBufferEventArgs (i)); + } + public void AddLine(string str){ + editMutex.EnterWriteLock (); + if (lineLength.LastOrDefault() == 0) { + buffer.Append (str); + lineLength [lineLength.Count - 1] = str.Length; + lineLength.Add (0); + } + editMutex.ExitWriteLock (); + LineAdditionEvent.Raise (this, new TextBufferEventArgs (lineLength.Count - 1)); + } + public void AddRange (string[] items){ + int start = lineLength.Count; + editMutex.EnterWriteLock (); + for (int i = 0; i < items.Length; i++) + AddLine (items [i]); + editMutex.ExitWriteLock (); + LineAdditionEvent.Raise (this, new TextBufferEventArgs (start, items.Length)); + } + public void Clear () { + editMutex.EnterWriteLock (); + lineLength.Clear (); + buffer.Clear (); + editMutex.ExitWriteLock (); + BufferCleared.Raise (this, null); + } + public void UpdateLine(int i, string newContent){ + editMutex.EnterWriteLock (); + int ptrL = this [i]; + buffer.Remove (ptrL, lineLength [i]); + buffer.Insert (ptrL, newContent); + lineLength [i] = newContent.Length; + editMutex.ExitWriteLock (); + LineUpadateEvent.Raise (this, new TextBufferEventArgs (i)); + } + public void AppenedLine(int i, string newContent){ + editMutex.EnterWriteLock (); + int ptr = this [i] + lineLength [i]; + if (i < LineCount - 1) + ptr--; + buffer.Insert(ptr, newContent); + lineLength [i] += newContent.Length; + editMutex.ExitWriteLock (); + LineUpadateEvent.Raise (this, new TextBufferEventArgs (i)); + } + /// + /// Insert new string at caret position, should be sure no line break is inside. + /// + /// String. + public void InsertAt(string str) + { + if (!SelectionIsEmpty) + this.Delete (); + + editMutex.EnterWriteLock (); + + string tmp = reghexLineBrk.Replace (str, "\n");//use single char line break in buffer + int buffPtr = this [CurrentLine] + CurrentColumn; + buffer.Insert (buffPtr, tmp); + + int lPtr = CurrentLine, strPtr = 0; + int remainingLength = lineLength [lPtr] - CurrentColumn; + lineLength [lPtr] = CurrentColumn; + foreach (Match match in slb.Matches(tmp)) + { + lineLength [lPtr] += match.Index + 1 - strPtr; + lPtr++; + lineLength.Insert (lPtr, 0); + strPtr = match.Index + 1; + //CurrentLine++; + } + remainingLength += tmp.Length - strPtr; + lineLength [lPtr] += remainingLength; + + CurrentLine = lPtr; + if (strPtr == 0) + CurrentColumn += tmp.Length; + else + CurrentColumn = tmp.Length - strPtr; + + editMutex.ExitWriteLock (); + if (strPtr>0) + LineAdditionEvent.Raise (this, null); + else + LineUpadateEvent.Raise (this, null); + } + /// + /// Insert a line break. + /// + public void InsertLineBreak() + { + editMutex.EnterWriteLock (); + buffer.Insert (this [CurrentLine] + CurrentColumn, '\n'); + int lgdiff = lineLength [CurrentLine] - CurrentColumn; + lineLength.Insert (CurrentLine + 1, lineLength [CurrentLine] - CurrentColumn); + lineLength [CurrentLine] = CurrentColumn + 1; + editMutex.ExitWriteLock (); + LineAdditionEvent.Raise (this, null); + CurrentColumn = 0; + CurrentLine++; + } + public void Delete() + { + editMutex.EnterWriteLock (); + if (SelectionIsEmpty) { + if (CurrentColumn == 0) { + if (CurrentLine == 0) { + editMutex.ExitWriteLock (); + return; + } + + buffer.Remove (this [CurrentLine] - 1, 1); + + int col = lineLength [CurrentLine - 1] - 1; + lineLength [CurrentLine - 1] += lineLength [CurrentLine] - 1; + lineLength.RemoveAt (CurrentLine); + + CurrentLine--; + CurrentColumn = col; + + editMutex.ExitWriteLock (); + LineRemoveEvent.Raise (this, null); + return; + } + CurrentColumn--; + buffer.Remove (this [CurrentLine] + CurrentColumn, 1); + lineLength [CurrentLine]--; + } else { + int linesToRemove = SelectionEnd.Y - SelectionStart.Y; + int ptr = this [SelectionStart.Y] + SelectionStart.X; + int length = lineLength [SelectionStart.Y] - SelectionStart.X; + int l = 1; + while (l <= linesToRemove ) { + length += lineLength [SelectionStart.Y + l]; + l++; + } + length -= lineLength [SelectionEnd.Y] - SelectionEnd.X; + buffer.Remove (ptr, length); + lineLength [SelectionStart.Y] = SelectionStart.X + lineLength [SelectionEnd.Y] - SelectionEnd.X; + lineLength.RemoveRange (SelectionStart.Y + 1, linesToRemove); + + CurrentLine = SelectionStart.Y; + CurrentColumn = SelectionStart.X; + ResetSelection (); + + if (linesToRemove > 0) + LineRemoveEvent.Raise (this, null); + else + LineUpadateEvent.Raise (this, null); + } + editMutex.ExitWriteLock (); + } + public void Load(string rawSource) { + this.Clear(); + + if (string.IsNullOrEmpty (rawSource)) + return; + + lineBreak = reghexLineBrk.Match (rawSource).Value;//store original line break + string tmp = reghexLineBrk.Replace (rawSource, "\n");//use single char line break in buffer + int ptr = 0; + foreach (Match match in slb.Matches(tmp)) + { + int l = match.Index + 1; + int lg = l - ptr; + lineLength.Add (lg); + ptr = l; + } + lineLength.Add (0); + + buffer = new StringBuilder (tmp); + } + +// public int CurrentTabulatedColumn { +// get { +//// return lines [_currentLine].Content.Substring (0, _currentCol). +//// Replace ("\t", new String (' ', Interface.TabSize)).Length; +// } +// } + + #region moving cursor an selection + Point selStartPos = -1; //selection start (row,column) + Point selEndPos = -1; //selection end (row,column) + + public bool SelectionInProgress { get { return selStartPos >= 0; }} + public void SetSelStartPos () { + selStartPos = selEndPos = CurrentPosition; + SelectionChanged.Raise (this, null); + } + public void SetSelEndPos () { + selEndPos = CurrentPosition; + SelectionChanged.Raise (this, null); + } + /// + /// Set selection in buffer to -1, empty selection + /// + public void ResetSelection () { + selStartPos = selEndPos = -1; + SelectionChanged.Raise (this, null); + } + /// + /// ordered selection start and end positions in char units + /// + public Point SelectionStart { + get { return selEndPos < 0 || selStartPos.Y < selEndPos.Y ? selStartPos : + selStartPos.Y > selEndPos.Y ? selEndPos : + selStartPos.X < selEndPos.X ? selStartPos : selEndPos; } + } + public Point SelectionEnd { + get { return selEndPos < 0 || selStartPos.Y > selEndPos.Y ? selStartPos : + selStartPos.Y < selEndPos.Y ? selEndPos : + selStartPos.X > selEndPos.X ? selStartPos : selEndPos; } + } + public bool SelectionIsEmpty + { get { return selEndPos == selStartPos; } } + /// + /// Current column in buffer coordinate, tabulation = 1 char + /// + public int CurrentColumn{ + get { return _currentCol; } + set { + if (value == _currentCol) + return; + + editMutex.EnterWriteLock (); + + if (value < 0) + _currentCol = 0; + else if (value >= lineLength [_currentLine]) { + if (_currentLine < LineCount -1 && value > lineLength [_currentLine] - 1) + _currentCol = lineLength [_currentLine] - 1; + else + _currentCol = lineLength [_currentLine]; + }else + _currentCol = value; + + editMutex.ExitWriteLock (); + + PositionChanged.Raise (this, null); + } + } + /// + /// Current row in buffer coordinate, tabulation = 1 char + /// + public int CurrentLine{ + get { return _currentLine; } + set { + if (value == _currentLine) + return; + + editMutex.EnterWriteLock (); + + if (value >= lineLength.Count) + _currentLine = lineLength.Count-1; + else if (value < 0) + _currentLine = 0; + else + _currentLine = value; + + int c = _currentCol; + _currentCol = 0; + CurrentColumn = c; + + editMutex.ExitWriteLock(); + + PositionChanged.Raise (this, null); + } + } + /// + /// Current position in buffer coordinate, tabulation = 1 char + /// + public Point CurrentPosition { + get { return new Point(CurrentColumn, CurrentLine); } + } + /// + /// get char at current position in buffer + /// + protected Char CurrentChar { get { return buffer[this [CurrentLine]]; } } + public string SelectedText { + get { + if (SelectionIsEmpty) + return ""; + Point selStart = SelectionStart; + Point selEnd = SelectionEnd; + + int ptr = this [selStart.Y] + selStart.X; + int length = lineLength[selStart.Y] - selStart.X; + for (int i = selStart.Y+1; i <= selEnd.Y; i++) + length += lineLength [i]; + length -= lineLength[selEnd.Y] - selEnd.X; + + return buffer.ToString (ptr, length); + } + } + + public void GotoWordStart(){ + if (_currentCol == 0) + MoveLeft (); + if (_currentCol == 0) + return; + int ptrStart = BufferIndexOfCurrentPosition; + int ptr = ptrStart; + char c; + //skip white spaces + do { + ptr--; + c = this.GetCharAt (ptr); + } while (!char.IsLetterOrDigit (c) && c != '\n' && ptr > 1); + + do { + ptr--; + c = this.GetCharAt (ptr); + } while (char.IsLetterOrDigit (c) && c != '\n' && ptr > 1); + + if (ptr == 0) + CurrentColumn = 0; + else + CurrentColumn -= ptrStart - ptr - 1; + } + public void GotoWordEnd(){ + int limx = GetLineLength (_currentLine); + if (_currentLine < lineLength.Count - 1) + limx--; + + if (_currentCol == limx) { + MoveRight (); + limx = GetLineLength (_currentLine); + if (_currentLine < lineLength.Count - 1) + limx--; + } + + int ptrLine = GetBufferIndexOfLine(_currentLine); + int ptrCol = _currentCol; + + char c; + //skip white spaces + do { + c = GetCharAt (ptrLine+ptrCol); + ptrCol++; + } while (!char.IsLetterOrDigit (c) && ptrCol < limx); + + do { + c = GetCharAt (ptrLine + ptrCol); + ptrCol++; + } while (char.IsLetterOrDigit (c) && ptrCol < limx); + CurrentColumn = ptrCol - 1; + } + /// + /// Moves cursor one char to the left, move up if cursor reaches start of line + /// + public void MoveLeft(){ + if (CurrentColumn == 0) { + CurrentLine--; + CurrentColumn = int.MaxValue; + } else + CurrentColumn--; + } + /// + /// Moves cursor one char to the right, move down if cursor reaches end of line + /// + public void MoveRight(){ + if (_currentLine < LineCount -1){ + if (CurrentColumn >= lineLength [CurrentLine] - 1) { + CurrentColumn = 0; + CurrentLine++; + return; + } + } + CurrentColumn++; + } + + #endregion + } +} + diff --git a/Crow.Coding/src/CodeBuffer/TextBufferEventArgs.cs b/Crow.Coding/src/CodeBuffer/TextBufferEventArgs.cs new file mode 100644 index 0000000..3b67bb1 --- /dev/null +++ b/Crow.Coding/src/CodeBuffer/TextBufferEventArgs.cs @@ -0,0 +1,20 @@ +using System; + +namespace Crow.Text +{ + public class TextBufferEventArgs : EventArgs { + public int LineStart; + public int LineCount; + + public TextBufferEventArgs(int lineNumber) { + LineStart = lineNumber; + LineCount = 1; + } + public TextBufferEventArgs(int lineStart, int lineCount) { + LineStart = lineStart; + LineCount = lineCount; + } + } + +} + diff --git a/Crow.Coding/src/CodeBuffer/TextEditor.cs b/Crow.Coding/src/CodeBuffer/TextEditor.cs new file mode 100644 index 0000000..7006798 --- /dev/null +++ b/Crow.Coding/src/CodeBuffer/TextEditor.cs @@ -0,0 +1,697 @@ +// +// ScrollingTextBox.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.ComponentModel; +using Crow.Cairo; +using Glfw; + +namespace Crow.Text +{ + /// + /// Scrolling text box optimized for monospace fonts, for coding + /// + public class TextEditor : ScrollingObject { + #region CTOR + public TextEditor (): base() + { + buffer = new TextBuffer (); + buffer.LineUpadateEvent += Buffer_LineUpadateEvent; + buffer.LineAdditionEvent += Buffer_LineAdditionEvent;; + buffer.LineRemoveEvent += Buffer_LineRemoveEvent; + buffer.BufferCleared += Buffer_BufferCleared; + buffer.SelectionChanged += Buffer_SelectionChanged; + buffer.PositionChanged += Buffer_PositionChanged; + //buffer.Add (""); + } + #endregion + + string oldSource = ""; + volatile bool isDirty = false; + + #region private and protected fields + int visibleLines = 1; + int visibleColumns = 1; + + TextBuffer buffer; + + Color selBackground; + Color selForeground; + int selStartCol; + int selEndCol; + + protected Rectangle rText; + protected FontExtents fe; + protected TextExtents te; + + Point mouseLocalPos; + bool doubleClicked = false; + #endregion + + /// + /// Updates visible line in widget, adapt max scroll y and updatePrintedLines + /// + void updateVisibleLines(){ + visibleLines = (int)Math.Floor ((double)ClientRectangle.Height / (fe.Ascent+fe.Descent)); + NotifyValueChanged ("VisibleLines", visibleLines); + updateMaxScrollY (); + RegisterForGraphicUpdate (); +// System.Diagnostics.Debug.WriteLine ("update visible lines: " + visibleLines); +// System.Diagnostics.Debug.WriteLine ("update MaxScrollY: " + MaxScrollY); + } + void updateVisibleColumns(){ + visibleColumns = (int)Math.Floor ((double)(ClientRectangle.Width)/ fe.MaxXAdvance); + NotifyValueChanged ("VisibleColumns", visibleColumns); + RegisterForGraphicUpdate (); +// System.Diagnostics.Debug.WriteLine ("update visible columns: {0} leftMargin:{1}",visibleColumns, leftMargin); +// System.Diagnostics.Debug.WriteLine ("update MaxScrollX: " + MaxScrollX); + } + void updateMaxScrollX (int longestTabulatedLineLength) { + MaxScrollX = Math.Max (0, longestTabulatedLineLength - visibleColumns); + if (longestTabulatedLineLength > 0) + NotifyValueChanged ("ChildWidthRatio", Slot.Width * visibleColumns / longestTabulatedLineLength); + } + void updateMaxScrollY () { + int lc = buffer.LineCount; + MaxScrollY = Math.Max (0, lc - visibleLines); + if (lc > 0) + NotifyValueChanged ("ChildHeightRatio", Slot.Height * visibleLines / lc); + + } + + + + #region Editor overrides + /*protected override void updateEditorFromProjFile () + { + buffer.editMutex.EnterWriteLock (); + loadSource (); + buffer.editMutex.ExitWriteLock (); + + isDirty = false; + oldSource = projFile.Source; + projFile.RegisteredEditors [this] = true; + } + protected override void updateProjFileFromEditor () + { + buffer.editMutex.EnterWriteLock (); + string newsrc = buffer.FullText; + buffer.editMutex.ExitWriteLock (); + projFile.UpdateSource (this, newsrc); + } + protected override bool EditorIsDirty { + get { return isDirty; } + set { isDirty = value; } + } + protected override bool IsReady { + get { return projFile != null && buffer != null; } + }*/ + #endregion + + #region Buffer events handlers + void Buffer_BufferCleared (object sender, EventArgs e) + { + //editorMutex.EnterWriteLock (); + + MaxScrollX = MaxScrollY = 0; + RegisterForGraphicUpdate (); + notifyPositionChanged (); + isDirty = true; + + //editorMutex.ExitWriteLock (); + } + void Buffer_LineAdditionEvent (object sender, TextBufferEventArgs e) + { + updateMaxScrollY (); + RegisterForGraphicUpdate (); + isDirty = true; + } + void Buffer_LineRemoveEvent (object sender, TextBufferEventArgs e) + { + updateMaxScrollY (); + RegisterForGraphicUpdate (); + notifyPositionChanged (); + isDirty = true; + } + void Buffer_LineUpadateEvent (object sender, TextBufferEventArgs e) + { + RegisterForGraphicUpdate (); + notifyPositionChanged (); + isDirty = true; + } + void Buffer_PositionChanged (object sender, EventArgs e) + { + int cc = getTabulatedColumn (buffer.CurrentPosition); + + if (cc > visibleColumns + ScrollX) { + ScrollX = cc - visibleColumns; + } else if (cc < ScrollX) + ScrollX = cc; + + if (buffer.CurrentLine >= visibleLines + ScrollY - 1) + ScrollY = buffer.CurrentLine - visibleLines + 1; + else if (buffer.CurrentLine < ScrollY) + ScrollY = buffer.CurrentLine; + + RegisterForGraphicUpdate (); + notifyPositionChanged (); + } + + void Buffer_SelectionChanged (object sender, EventArgs e) + { + RegisterForGraphicUpdate (); + } + #endregion + + void notifyPositionChanged (){ + try { + NotifyValueChanged ("CurrentLine", buffer.CurrentLine+1); + NotifyValueChanged ("CurrentColumn", buffer.CurrentColumn+1); + } catch (Exception ex) { + Console.WriteLine (ex.ToString ()); + } + } + + #region Public Crow Properties + public int CurrentLine{ + get { return buffer == null ? 0 : buffer.CurrentLine+1; } + set { + try { + int l = value - 1; + if (l == buffer.CurrentLine) + return; + buffer.CurrentLine = l; + } catch (Exception ex) { + Console.WriteLine ("Error cur column: " + ex.ToString ()); + } + } + } + public int CurrentColumn{ + get { return buffer == null ? 0 : buffer.CurrentColumn+1; } + set { + try { + if (value - 1 == buffer.CurrentColumn) + return; + buffer.CurrentColumn = value - 1; + } catch (Exception ex) { + Console.WriteLine ("Error cur column: " + ex.ToString ()); + } + } + } + [DefaultValue("Blue")] + public virtual Color SelectionBackground { + get { return selBackground; } + set { + if (value == selBackground) + return; + selBackground = value; + NotifyValueChanged ("SelectionBackground", selBackground); + RegisterForRedraw (); + } + } + [DefaultValue("White")] + public virtual Color SelectionForeground { + get { return selForeground; } + set { + if (value == selForeground) + return; + selForeground = value; + NotifyValueChanged ("SelectionForeground", selForeground); + RegisterForRedraw (); + } + } + public override int ScrollY { + get { + return base.ScrollY; + } + set { + if (value == base.ScrollY) + return; + //editorMutex.EnterWriteLock (); + base.ScrollY = value; + //editorMutex.ExitWriteLock (); + RegisterForGraphicUpdate (); + } + } + #endregion + + + void loadSource () { + /*buffer.Load (projFile.Source); + projFile.RegisteredEditors [this] = true;*/ + updateMaxScrollY (); + RegisterForGraphicUpdate (); + } + + int getTabulatedColumn (int col, int line) { + return buffer.GetSubString (buffer [line], + buffer.GetLineLength (line)).Substring(0,col).Replace ("\t", new String (' ', Interface.TAB_SIZE)).Length; + } + int getTabulatedColumn (Point pos) { + return getTabulatedColumn (pos.X,pos.Y); + } + + #region Drawing + void drawLines(Context gr, Rectangle cb) { + int longestTabulatedLine = 0; + for (int i = 0; i < visibleLines; i++) { + int lineIndex = i + ScrollY; + if (lineIndex >= buffer.LineCount)//TODO:need optimize + break; + + double y = cb.Y + (fe.Ascent+fe.Descent) * i, x = cb.X; + + int lineLength = buffer.GetLineLength (lineIndex); + if (lineIndex < buffer.LineCount - 1)//dont print line break + lineLength--; + string lstr = buffer.GetSubString (buffer [lineIndex], + lineLength).Replace ("\t", new String (' ', Interface.TAB_SIZE)); + + int lstrLength = lstr.Length; + if (lstrLength > longestTabulatedLine) + longestTabulatedLine = lstrLength; + + if (ScrollX < lstrLength) + lstr = lstr.Substring (ScrollX); + else + lstr = ""; + + gr.MoveTo (x, y + fe.Ascent); + gr.ShowText (lstr); + gr.Fill (); + + if (!buffer.SelectionIsEmpty && lineIndex >= buffer.SelectionStart.Y && lineIndex <= buffer.SelectionEnd.Y) { + double rLineX = x, + rLineY = y, + rLineW = lstr.Length * fe.MaxXAdvance; + + if (lineIndex == buffer.SelectionStart.Y) { + rLineX += (selStartCol - ScrollX) * fe.MaxXAdvance; + rLineW -= (selStartCol - ScrollX) * fe.MaxXAdvance; + } + if (lineIndex == buffer.SelectionEnd.Y) + rLineW -= (lstr.Length - selEndCol + ScrollX) * fe.MaxXAdvance; + + gr.Save (); + gr.Operator = Operator.Source; + gr.Rectangle (rLineX, rLineY, rLineW, (fe.Ascent+fe.Descent)); + gr.SetSource (SelectionBackground); + gr.FillPreserve (); + gr.Clip (); + gr.Operator = Operator.Over; + gr.SetSource (SelectionForeground); + gr.MoveTo (x, y + fe.Ascent); + gr.ShowText (lstr); + gr.Fill (); + gr.Restore (); + } + + + } + + updateMaxScrollX(longestTabulatedLine); + } + #endregion + + #region GraphicObject overrides + public override Font Font { + get { return base.Font; } + set { + base.Font = value; + + using (ImageSurface img = new ImageSurface (Format.Argb32, 1, 1)) { + using (Context gr = new Context (img)) { + gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); + gr.SetFontSize (Font.Size); + + fe = gr.FontExtents; + } + } + MaxScrollY = 0; + RegisterForGraphicUpdate (); + } + } + public override int measureRawSize(LayoutingType lt) + { + if (lt == LayoutingType.Height) + return (int)Math.Ceiling((fe.Ascent+fe.Descent) * buffer.LineCount) + Margin * 2; + + return 0;// (int)(fe.MaxXAdvance * buffer.GetLineLength(buffer.longestLineIdx)) + Margin * 2; + } + public override void OnLayoutChanges (LayoutingType layoutType) + { + base.OnLayoutChanges (layoutType); + + if (layoutType == LayoutingType.Height) + updateVisibleLines (); + else if (layoutType == LayoutingType.Width) + updateVisibleColumns (); + } + + protected override void onDraw (Context gr) + { + base.onDraw (gr); + + gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); + gr.SetFontSize (Font.Size); + gr.FontOptions = Interface.FontRenderingOptions; + gr.Antialias = Interface.Antialias; + + Rectangle cb = ClientRectangle; + + Foreground.SetAsSource (gr); + + buffer.editMutex.EnterReadLock (); + //editorMutex.EnterReadLock (); + + #region draw text cursor + if (buffer.SelectionInProgress){ + selStartCol = getTabulatedColumn (buffer.SelectionStart); + selEndCol = getTabulatedColumn (buffer.SelectionEnd); + }else if (HasFocus && CurrentLine >= 0){ + gr.LineWidth = 1.0; + double cursorX = cb.X + (getTabulatedColumn(buffer.CurrentPosition) - ScrollX) * fe.MaxXAdvance ; + double cursorY = cb.Y + (buffer.CurrentLine - ScrollY) * (fe.Ascent+fe.Descent); + gr.MoveTo (0.5 + cursorX, cursorY); + gr.LineTo (0.5 + cursorX, cursorY + fe.Ascent+fe.Descent); + gr.Stroke(); + } + #endregion + + drawLines (gr, cb); + + //editorMutex.ExitReadLock (); + + buffer.editMutex.ExitReadLock (); + + } + #endregion + + int getBufferColFromVisualCol (int line, int column) { + int i = 0; + int buffCol = 0; + int buffPtr = buffer [line]; + while (i < column && buffCol < buffer.GetLineLength(line)) { + if (buffer.GetCharAt(buffPtr + buffCol) == '\t') + i += Interface.TAB_SIZE; + else + i++; + buffCol++; + } + return buffCol; + } + + + #region Mouse handling + + int hoverLine = -1; + public int HoverLine { + get { return hoverLine; } + set { + if (hoverLine == value) + return; + hoverLine = value; + NotifyValueChanged ("HoverLine", hoverLine); + } + } + void updateHoverLine () { +// int hvl = (int)Math.Max (0, Math.Floor (mouseLocalPos.Y / (fe.Ascent+fe.Descent))); +// hvl = Math.Min (PrintedLines.Count, hvl); +// HoverLine = buffer.IndexOf (PrintedLines[hvl]); + } + void updateCurrentPosFromMouseLocalPos(){ + + buffer.CurrentLine = ScrollY + (int)Math.Max (0, Math.Floor (mouseLocalPos.Y / (fe.Ascent+fe.Descent))); + int curVisualCol = ScrollX + (int)Math.Round ((mouseLocalPos.X) / fe.MaxXAdvance); + buffer.CurrentColumn = getBufferColFromVisualCol (buffer.CurrentLine, curVisualCol); + } + + public override void onMouseEnter (object sender, MouseMoveEventArgs e) + { + base.onMouseEnter (sender, e); + IFace.MouseCursor = MouseCursor.ibeam; + } + public override void onMouseLeave (object sender, MouseMoveEventArgs e) + { + base.onMouseLeave (sender, e); + IFace.MouseCursor = MouseCursor.arrow; + } + public override void onMouseMove (object sender, MouseMoveEventArgs e) + { + base.onMouseMove (sender, e); + + mouseLocalPos = e.Position - ScreenCoordinates(Slot).TopLeft - ClientRectangle.TopLeft; + + updateHoverLine (); + + if (!IFace.IsDown(MouseButton.Left) || !buffer.SelectionInProgress) + return; + + //mouse is down + updateCurrentPosFromMouseLocalPos(); + buffer.SetSelEndPos (); + } + public override void onMouseDown (object sender, MouseButtonEventArgs e) + { + if (!Focusable) + return; + + base.onMouseDown (sender, e); + + if (doubleClicked) { + doubleClicked = false; + return; + } + + updateCurrentPosFromMouseLocalPos (); + buffer.SetSelStartPos (); + } + public override void onMouseUp (object sender, MouseButtonEventArgs e) + { + base.onMouseUp (sender, e); + + if (buffer.SelectionIsEmpty) + buffer.ResetSelection (); + } + + public override void onMouseDoubleClick (object sender, MouseButtonEventArgs e) + { + //doubleClicked = true; + base.onMouseDoubleClick (sender, e); + + buffer.GotoWordStart (); + buffer.SetSelStartPos (); + buffer.GotoWordEnd (); + buffer.SetSelEndPos (); + } + + public override void onMouseWheel (object sender, MouseWheelEventArgs e) + { + base.onMouseWheel (sender, e); + } + #endregion + + #region Keyboard handling + public override void onKeyDown (object sender, KeyEventArgs e) + { + //base.onKeyDown (sender, e); + + Key key = e.Key; + + /*if (IFace.Ctrl) { + switch (key) { + case Key.S: + projFile.Save (); + break; + case Key.W: + editorMutex.EnterWriteLock (); + if (IFace.Shift) + projFile.Redo (null); + else + projFile.Undo (null); + editorMutex.ExitWriteLock (); + break; + default: + Console.WriteLine (""); + break; + } + }*/ + + switch (key) + { + case Key.Backspace: + buffer.Delete (); + break; + case Key.Delete: + if (buffer.SelectionIsEmpty) + buffer.MoveRight (); +// else if (e.Shift) +// IFace.Clipboard = buffer.SelectedText; + buffer.Delete (); + break; + case Key.Enter: + case Key.KeypadEnter: + if (!buffer.SelectionIsEmpty) + buffer.Delete (); + buffer.InsertLineBreak (); + break; + case Key.Escape: + buffer.ResetSelection (); + break; + case Key.Home: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (IFace.Ctrl) + buffer.CurrentLine = 0; + buffer.CurrentColumn = 0; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (IFace.Ctrl) + buffer.CurrentLine = 0; + buffer.CurrentColumn = 0; + break; + case Key.End: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (IFace.Ctrl) + buffer.CurrentLine = int.MaxValue; + buffer.CurrentColumn = int.MaxValue; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (IFace.Ctrl) + buffer.CurrentLine = int.MaxValue; + buffer.CurrentColumn = int.MaxValue; + break; + case Key.Insert: + if (IFace.Shift) + buffer.InsertAt (IFace.Clipboard); + else if (IFace.Ctrl && !buffer.SelectionIsEmpty) + IFace.Clipboard = buffer.SelectedText; + break; + case Key.Left: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (IFace.Ctrl) + buffer.GotoWordStart (); + else + buffer.MoveLeft (); + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (IFace.Ctrl) + buffer.GotoWordStart (); + else + buffer.MoveLeft(); + break; + case Key.Right: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (IFace.Ctrl) + buffer.GotoWordEnd (); + else + buffer.MoveRight (); + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (IFace.Ctrl) + buffer.GotoWordEnd (); + else + buffer.MoveRight (); + break; + case Key.Up: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + CurrentLine--; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + CurrentLine--; + break; + case Key.Down: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + CurrentLine++; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + CurrentLine++; + break; + case Key.Menu: + break; + case Key.PageDown: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + CurrentLine += visibleLines; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + CurrentLine += visibleLines; + break; + case Key.PageUp: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + CurrentLine -= visibleLines; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + CurrentLine -= visibleLines; + break; + case Key.Tab: + buffer.InsertAt ("\t"); + break; + default: + break; + } + RegisterForGraphicUpdate(); + } + public override void onKeyPress (object sender, KeyPressEventArgs e) + { + base.onKeyPress (sender, e); + + buffer.InsertAt (e.KeyChar.ToString()); + buffer.ResetSelection (); + } + #endregion + } +} \ No newline at end of file diff --git a/Crow.Coding/src/CodeBuffer/TextFormatting.cs b/Crow.Coding/src/CodeBuffer/TextFormatting.cs new file mode 100644 index 0000000..f7b2e51 --- /dev/null +++ b/Crow.Coding/src/CodeBuffer/TextFormatting.cs @@ -0,0 +1,19 @@ +using System; + +namespace Crow.Coding +{ + public struct TextFormatting { + public Color Foreground; + public Color Background; + public bool Bold; + public bool Italic; + + public TextFormatting(Color fg, Color bg, bool bold = false, bool italic = false){ + Foreground = fg; + Background = bg; + Bold = bold; + Italic = italic; + } + } +} + diff --git a/Crow.Coding/src/CodeBuffer/Token.cs b/Crow.Coding/src/CodeBuffer/Token.cs new file mode 100644 index 0000000..5896d75 --- /dev/null +++ b/Crow.Coding/src/CodeBuffer/Token.cs @@ -0,0 +1,71 @@ +// +// Token.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2017 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +using System; + +namespace Crow.Coding +{ + public struct Token + { + public BufferParser.TokenType Type; + public string Content; + public Point Start; + public Point End; + + public string PrintableContent { + get { return string.IsNullOrEmpty(Content) ? "" : Content.Replace("\t", new String(' ', Interface.TAB_SIZE)); } + } + +// public Token (TokenType tokType, string content = ""){ +// Type = tokType; +// Content = content; +// } + + public bool IsNull { get { return IsEmpty && Type == BufferParser.TokenType.Unknown; }} + public bool IsEmpty { get { return string.IsNullOrEmpty(Content); }} + + public static bool operator == (Token t, System.Enum tt){ + return Convert.ToInt32(t.Type) == Convert.ToInt32(tt); + } + public static bool operator != (Token t, System.Enum tt){ + return Convert.ToInt32(t.Type) != Convert.ToInt32(tt); + } + public static bool operator == (System.Enum tt, Token t){ + return Convert.ToInt32(t.Type) == Convert.ToInt32(tt); + } + public static bool operator != (System.Enum tt, Token t){ + return Convert.ToInt32(t.Type) != Convert.ToInt32(tt); + } + + public static Token operator +(Token t, char c){ + t.Content += c; + return t; + } + public static Token operator +(Token t, string s){ + t.Content += s; + return t; + } + public override string ToString () + { + return string.Format ("[Tok{2}->{3}:{0}: {1}]", Type,Content,Start,End); + } + } +} + diff --git a/Crow.Coding/src/Parsers/BufferParser.cs b/Crow.Coding/src/Parsers/BufferParser.cs new file mode 100644 index 0000000..8458e6e --- /dev/null +++ b/Crow.Coding/src/Parsers/BufferParser.cs @@ -0,0 +1,381 @@ +using System; +using System.IO; +using Crow; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace Crow.Coding +{ + /// + /// base class for tokenizing sources + /// + public abstract class BufferParser + { + /// + /// Default tokens, this enum may be overriden in derived parser with the new keyword, + /// see XMLParser for example. + /// + public enum TokenType { + Unknown = 0, + WhiteSpace = 1, + NewLine = 2, + LineComment = 3, + BlockCommentStart = 4, + BlockComment = 5, + BlockCommentEnd = 6, + Preprocessor = 7, + Identifier = 8, + Keyword = 9, + OpenBlock = 10, + CloseBlock = 11, + StatementEnding = 12, + OperatorOrPunctuation = 13, + IntegerLitteral = 14, + RealLitteral = 15, + StringLitteralOpening = 16, + StringLitteralClosing = 17, + StringLitteral = 18, + CharLitteralOpening = 19, + CharLitteralClosing = 20, + CharLitteral = 21, + BoolLitteral = 22, + NullLitteral = 23, + Type = 24, + } + + #region CTOR + public BufferParser (CodeBuffer _buffer) + { + buffer = _buffer; + + buffer.LineUpadateEvent += Buffer_LineUpadateEvent; + //buffer.LineAdditionEvent += Buffer_LineAdditionEvent;; + buffer.LineRemoveEvent += Buffer_LineRemoveEvent; + buffer.BufferCleared += Buffer_BufferCleared; + } + + #endregion + + #region Buffer events handlers + void Buffer_BufferCleared (object sender, EventArgs e) + { + + } + void Buffer_LineAdditionEvent (object sender, CodeBufferEventArgs e) + { + + } + void Buffer_LineRemoveEvent (object sender, CodeBufferEventArgs e) + { + reparseSource (); + } + void Buffer_LineUpadateEvent (object sender, CodeBufferEventArgs e) + { + for (int i = 0; i < e.LineCount; i++) + TryParseBufferLine (e.LineStart + i); + reparseSource (); + } + #endregion + + internal int currentLine = 0; + internal int currentColumn = 0; + + int syntTreeDepth = 0; + public int SyntacticTreeDepth { + get { return syntTreeDepth;} + set { + syntTreeDepth = value; + if (syntTreeDepth > SyntacticTreeMaxDepth) + SyntacticTreeMaxDepth = syntTreeDepth; + } + } + public int SyntacticTreeMaxDepth = 0; + + protected CodeBuffer buffer; + protected Token currentTok; + protected bool eol = true; + protected Point CurrentPosition { + get { return new Point (currentLine, currentColumn); } + set { + currentLine = value.Y; + currentColumn = value.X; + } + } + + public Node RootNode; + + public abstract void ParseCurrentLine(); + public abstract void SyntaxAnalysis (); + public void reparseSource () { + for (int i = 0; i < buffer.LineCount; i++) { + if (!buffer[i].IsParsed) + TryParseBufferLine (i); + } + try { + SyntaxAnalysis (); + } catch (Exception ex) { + Debug.WriteLine ("Syntax Error: " + ex.ToString ()); + if (ex is ParserException) + SetLineInError (ex as ParserException); + } + } + public void TryParseBufferLine(int lPtr) { + Console.WriteLine ($"TryParseBufferLine: {lPtr}"); + buffer [lPtr].exception = null; + currentLine = lPtr; + currentColumn = 0; + eol = false; + + try { + ParseCurrentLine (); + } catch (Exception ex) { + Debug.WriteLine (ex.ToString ()); + if (ex is ParserException) + SetLineInError (ex as ParserException); + } + + } + + public virtual void SetLineInError(ParserException ex) { + currentTok = default(Token); + if (ex.Line >= buffer.LineCount) + ex.Line = buffer.LineCount - 1; + if (buffer [ex.Line].IsFolded) + buffer.ToogleFolding (ex.Line); + buffer [ex.Line].SetLineInError (ex); + } + public virtual string LineBrkRegex { + get { return @"\r\n|\r|\n|\\\\n"; } + } + void updateFolding () { + // Stack foldings = new Stack(); + // bool inStartTag = false; + // + // for (int i = 0; i < parser.Tokens.Count; i++) { + // TokenList tl = parser.Tokens [i]; + // tl.foldingTo = null; + // int fstTK = tl.FirstNonBlankTokenIndex; + // if (fstTK > 0 && fstTK < tl.Count - 1) { + // if (tl [fstTK + 1] != XMLParser.TokenType.ElementName) + // continue; + // if (tl [fstTK] == XMLParser.TokenType.ElementStart) { + // //search closing tag + // int tkPtr = fstTK+2; + // while (tkPtr < tl.Count) { + // if (tl [tkPtr] == XMLParser.TokenType.ElementClosing) + // + // tkPtr++; + // } + // if (tl.EndingState == (int)XMLParser.States.Content) + // foldings.Push (tl); + // else if (tl.EndingState == (int)XMLParser.States.StartTag) + // inStartTag = true; + // continue; + // } + // if (tl [fstTK] == XMLParser.TokenType.ElementEnd) { + // TokenList tls = foldings.Pop (); + // int fstTKs = tls.FirstNonBlankTokenIndex; + // if (tls [fstTK + 1].Content == tl [fstTK + 1].Content) { + // tl.foldingTo = tls; + // continue; + // } + // parser.CurrentPosition = tls [fstTK + 1].Start; + // parser.SetLineInError(new ParserException(parser, "closing tag not corresponding")); + // } + // + // } + // } + } + + #region low level parsing + protected void addCharToCurTok(char c, Point position){ + currentTok.Start = position; + currentTok += c; + } + /// + /// Read one char from current position in buffer and store it into the current token + /// + /// if true, set the Start position of the current token to the current position + protected void readToCurrTok(bool startOfTok = false){ + if (startOfTok) + currentTok.Start = CurrentPosition; + currentTok += Read(); + } + /// + /// read n char from the buffer and store it into the current token + /// + protected void readToCurrTok(int length) { + for (int i = 0; i < length; i++) + currentTok += Read (); + } + /// + /// Save current token into current TokensLine and raz current token + /// + protected void saveAndResetCurrentTok() { + currentTok.End = CurrentPosition; + buffer[currentLine].Tokens.Add (currentTok); + currentTok = default(Token); + } + /// + /// read one char and add current token to current TokensLine, current token is reset + /// + /// Type of the token + /// set start of token to current position + protected void readAndResetCurrentTok(System.Enum type, bool startToc = false) { + readToCurrTok (); + saveAndResetCurrentTok (type); + } + /// + /// Save current tok + /// + /// set the type of the tok + protected void saveAndResetCurrentTok(System.Enum type) { + currentTok.Type = (TokenType)type; + saveAndResetCurrentTok (); + } + protected void setPreviousTokOfTypeTo (TokenType inType, TokenType newType) { + for (int i = currentLine; i >= 0; i--) { + int j = buffer [i].Tokens.Count - 1; + while (j >= 0) { + if (buffer [i].Tokens [j].Type == inType) { + Token t = buffer [i].Tokens [j]; + t.Type = newType; + buffer [i].Tokens [j] = t; + return; + } + j--; + } + } + } + /// + /// Peek next char, emit '\n' if current column > buffer's line length + /// Throw error if eof is true + /// + protected virtual char Peek() { + if (eol) + throw new ParserException (currentLine, currentColumn, "Unexpected End of line"); + return currentColumn < buffer [currentLine].Length ? + buffer [currentLine] [currentColumn] : '\n'; + } + /// + /// Peek n char from buffer or less if remaining char in buffer's line is less than requested + /// if end of line is reached, no '\n' will be emitted, instead, empty string is returned. '\n' should be checked only + /// with single char Peek(). + /// Throw error is eof is true + /// + /// Length. + protected virtual string Peek(int length) { + if (eol) + return "";//throw new ParserException (currentLine, currentColumn, "Unexpected End of Line"); + int lg = Math.Min(length, Math.Max (buffer [currentLine].Length - currentColumn, buffer [currentLine].Length - currentColumn - length)); + if (lg == 0) + return ""; + return buffer [currentLine].Content.Substring (currentColumn, lg); + } + /// + /// read one char from buffer at current position, if '\n' is read, current line is incremented + /// and column is reset to 0 + /// + protected virtual char Read() { + char c = Peek (); + if (c == '\n') + eol = true; + currentColumn++; + return c; + } + protected virtual string Read(int charCount){ + string tmp = ""; + for (int i = 0; i < charCount; i++) { + if (eol) + break; + tmp += Read (); + } + return tmp; + } + /// + /// read until end of line is reached + /// + /// string read + protected virtual string ReadLine () { + StringBuilder tmp = new StringBuilder(); + char c = Read (); + while (!eol) { + tmp.Append (c); + c = Read (); + } + return tmp.ToString(); + } + /// + /// read until end expression is reached or end of line. + /// + /// string read minus the ending expression that has to be read after + /// Expression to search for + protected virtual string ReadLineUntil (string endExp){ + string tmp = ""; + + while (!eol) { + if (buffer [currentLine].Length - currentColumn - endExp.Length < 0) { + tmp += ReadLine(); + break; + } + if (string.Equals (Peek (endExp.Length), endExp)) + return tmp; + tmp += Read(); + } + return tmp; + } + /// + /// skip white spaces, but not line break. Save spaces in a WhiteSpace token. + /// + protected void SkipWhiteSpaces () { + if (currentTok.Type != TokenType.Unknown) + throw new ParserException (currentLine, currentColumn, "current token should be reset to unknown (0) before skiping white spaces"); + while (!eol) { + if (!char.IsWhiteSpace (Peek ())||Peek()=='\n') + break; + readToCurrTok (currentTok.Type == TokenType.Unknown); + currentTok.Type = TokenType.WhiteSpace; + } + if (currentTok.Type != TokenType.Unknown) + saveAndResetCurrentTok (); + } + #endregion + + protected Node addChildNode (Node curNode, CodeLine cl, int tokPtr, string type = "") { + Node n = new Node () { Name = cl.Tokens [tokPtr].Content, StartLine = cl, Type = type }; + curNode.AddChild (n); + if (cl.SyntacticNode == null) + cl.SyntacticNode = n; + SyntacticTreeDepth++; + return n; + } + protected void closeNodeAndGoUp (ref Node n, CodeLine cl, string type = ""){ + while (n != null) { + if (n.Type == type) { + n.EndLine = cl; + n = n.Parent; + SyntacticTreeDepth--; + break; + } + n = n.Parent; + SyntacticTreeDepth--; + } + } + protected void closeNodeAndGoUp (ref Node n, CodeLine cl){ + SyntacticTreeDepth--; + n.EndLine = cl; + n = n.Parent; + } + + protected void initSyntaxAnalysis () { + RootNode = new Node () { Name = "RootNode", Type="Root" }; + SyntacticTreeDepth = SyntacticTreeMaxDepth = 0; + } + + + protected void throwParserException(string msg){ + throw new ParserException (currentLine, currentColumn, msg); + } + } +} \ No newline at end of file diff --git a/Crow.Coding/src/Parsers/CSharpParser.cs b/Crow.Coding/src/Parsers/CSharpParser.cs new file mode 100644 index 0000000..23655aa --- /dev/null +++ b/Crow.Coding/src/Parsers/CSharpParser.cs @@ -0,0 +1,386 @@ +using System; +using Crow; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text.RegularExpressions; +using System.Linq; + +namespace Crow.Coding +{ + public class CSharpParser : BufferParser + { + #region keywords + string[] keywords = new string[] { + "abstract", + "as", + "ascending", + "async", + "await", + "base", + "bool", + "break", + "byte", + "case", + "catch", + "char", + "checked", + "class", + "const", + "continue", + "decimal", + "default", + "delegate", + "descending", + "do", + "double", + "dynamic", + "else", + "enum", + "equals", + "event", + "explicit", + "extern", + "false", + "finally", + "fixed", + "float", + "for", + "foreach", + "from", + "get", + "goto", + "group", + "if", + "implicit", + "in", + "int", + "interface", + "internal", + "is", + "join", + "let", + "lock", + "long", + "nameof", + "namespace", + "new", + "null", + "object", + "operator", + "orderby", + "out", + "override", + "params", + "partial", + "private", + "protected", + "public", + "readonly", + "ref", + "return", + "sbyte", + "sealed", + "select", + "set", + "short", + "sizeof", + "stackalloc", + "static", + "string", + "struct", + "switch", + "this", + "throw", + "true", + "try", + "typeof", + "uint", + "ulong", + "unchecked", + "unsafe", + "ushort", + "using", + "value", + "var", + "virtual", + "void", + "volatile", + "when", + "where", + "while", + "yield " + }; + #endregion + + public enum States + { + init, + BlockComment, + InNameSpace, + InClass, + InMember, + Unknown, + } + + public CSharpParser (CodeBuffer _buffer) : base(_buffer) + { + } + + #region Regular Expression for validity checks + static Regex rxValidChar = new Regex(@"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf}"); + static Regex rxNameStartChar = new Regex(@"_|\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}"); + static Regex rxNameChar = new Regex(@"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf}"); + static Regex rxNewLineChar = new Regex(@"\u000D|\u000A|\u0085|\u2028|\u2029"); + static Regex rxWhiteSpaceChar = new Regex(@"\p{Zs}|\u0009|\u000B|\u000C"); + static Regex rxDecimal = new Regex(@"[0-9]+"); + static Regex rxHexadecimal = new Regex(@"[0-9a-fA-F]+"); + + public static bool CharIsValidCharStartName (char c) { + return rxNameStartChar.IsMatch(new string(new char[]{c})); + } + public static bool CharIsValidCharName (char c) { + return rxNameChar.IsMatch(new string(new char[]{c})); + } + + public bool nextCharIsValidCharStartName + { + get { return CharIsValidCharStartName(Peek()); } + } + public bool nextCharIsValidCharName + { + get { return CharIsValidCharName(Peek()); } + } + #endregion + + States curState = States.init; + States savedState = States.init; + + public override void ParseCurrentLine () + { + //Debug.WriteLine (string.Format("parsing line:{0}", currentLine)); + CodeLine cl = buffer [currentLine]; + cl.Tokens = new List (); + + + //retrieve current parser state from previous line + if (currentLine > 0) + curState = (States)buffer[currentLine - 1].EndingState; + else + curState = States.init; + + States previousEndingState = (States)cl.EndingState; + + while (! eol) { + if (currentTok.IsNull) + SkipWhiteSpaces (); + + if (curState == States.BlockComment) { + currentTok.Start = CurrentPosition; + currentTok.Type = (BufferParser.TokenType)TokenType.BlockComment; + currentTok += ReadLineUntil ("*/"); + if (Peek (2) == "*/") { + readToCurrTok (2); + curState = savedState; + } + saveAndResetCurrentTok (); + continue; + } + + switch (Peek()) { + case '\n': + eol = true; + if (!currentTok.IsNull) + saveAndResetCurrentTok (); + break; + case '#': + readToCurrTok (true); + currentTok += ReadLine (); + saveAndResetCurrentTok (TokenType.Preprocessor); + break; + case '/': + readToCurrTok (true); + switch (Peek ()) { + case '*': + readToCurrTok (); + currentTok += ReadLine (); + //currentTok.Type = (Parser.TokenType)TokenType.BlockComment; + savedState = curState; + curState = States.BlockComment; + saveAndResetCurrentTok (TokenType.BlockComment); + break; + case '/': + //readToCurrTok (); + currentTok += ReadLine (); + saveAndResetCurrentTok (TokenType.LineComment); + //currentTok.Type = (Parser.TokenType)TokenType.LineComment; + break; + default: + currentTok += ReadLine (); + saveAndResetCurrentTok (TokenType.Unknown); + break; + } + break; + case '{': + if (currentTok.IsNull) + readAndResetCurrentTok (TokenType.OpenBlock, true); + else + readToCurrTok (); + break; + case '}': + if (currentTok.IsNull) + readAndResetCurrentTok (TokenType.CloseBlock, true); + else + readToCurrTok (); + break; + case '\\'://unicode escape sequence + if (!(currentTok.Type == TokenType.Identifier || + currentTok.IsEmpty || currentTok.Type == TokenType.StringLitteral || currentTok.Type == TokenType.CharLitteral)) { + saveAndResetCurrentTok (); + } + Point pos = CurrentPosition; + Read (); + char escChar = Read (); + + if (escChar == 'u') { + char c = char.ConvertFromUtf32 (int.Parse (Read (4), System.Globalization.NumberStyles.HexNumber))[0]; + if (currentTok.IsEmpty) { + if (!CharIsValidCharStartName (c)) + throwParserException ("expecting identifier start"); + currentTok.Start = pos; + currentTok.Type = TokenType.Identifier; + } else if (currentTok.Type == TokenType.Identifier) { + if (!CharIsValidCharName (c)) + throwParserException ("expecting identifier valid char"); + } + currentTok += c; + break; + } + currentTok += new String (new char[] { '\\', escChar }); + break; + case '\'': + if (currentTok.IsNull) { + readAndResetCurrentTok (TokenType.CharLitteralOpening, true); + currentTok.Type = TokenType.CharLitteral; + } else if (currentTok.Type == TokenType.CharLitteral) { + saveAndResetCurrentTok (); + readAndResetCurrentTok (TokenType.CharLitteralClosing, true); + } else if (currentTok.Type == TokenType.StringLitteral){ + readToCurrTok (); + } else + throwParserException ("unexpected character: (\')"); + break; + case '"': + if (currentTok.IsNull) { + readAndResetCurrentTok (TokenType.StringLitteralOpening, true); + currentTok.Type = TokenType.StringLitteral; + } else if (currentTok.Type == TokenType.StringLitteral) { + saveAndResetCurrentTok (); + readAndResetCurrentTok (TokenType.StringLitteralClosing, true); + } else + throwParserException ("unexpected character: (\")"); + break; + default: + if (currentTok.Type == TokenType.StringLitteral || currentTok.Type == TokenType.CharLitteral) { + readToCurrTok (currentTok.IsEmpty); + } else if (currentTok.IsNull) { + if (nextCharIsValidCharStartName) { + readToCurrTok (true); + while (nextCharIsValidCharName) + readToCurrTok (); + + if (keywords.Contains (currentTok.Content)) + saveAndResetCurrentTok (TokenType.Keyword); + else + saveAndResetCurrentTok (TokenType.Identifier); + continue; + } else + readAndResetCurrentTok(TokenType.Unknown, true); + } else + readAndResetCurrentTok(TokenType.Unknown, true); + break; + } + } + + if (cl.EndingState != (int)curState && currentLine < buffer.LineCount - 1) + buffer [currentLine + 1].Tokens = null; + + cl.EndingState = (int)curState; + } + + public override void SyntaxAnalysis () + { + initSyntaxAnalysis (); + Node currentNode = RootNode; + + int ptrLine = 0; + while (ptrLine < buffer.LineCount) { + CodeLine cl = buffer [ptrLine]; + if (cl.Tokens == null){ + ptrLine++; + continue; + } + cl.SyntacticNode = null; + + int tokPtr = 0; + bool onlyWhiteSpace = true; + while (tokPtr < cl.Tokens.Count) { + if (cl.Tokens [tokPtr].Type == TokenType.WhiteSpace) { + tokPtr++; + continue; + } + + if (cl.Tokens [tokPtr].Type == TokenType.LineComment && onlyWhiteSpace) { + int startLine = ptrLine; + ptrLine++; + while (ptrLine < buffer.LineCount) { + int idx = buffer [ptrLine].FirstNonBlankTokIndex; + if (idx < 0) + break; + if (buffer [ptrLine].Tokens [idx].Type != TokenType.LineComment) + break; + ptrLine++; + } + ptrLine--; + if (ptrLine - startLine > 0) { + currentNode = addChildNode (currentNode, cl, tokPtr, "comment"); + closeNodeAndGoUp (ref currentNode, buffer [ptrLine], "comment"); + } + break; + } + + switch (cl.Tokens [tokPtr].Type) { + case TokenType.BlockCommentStart: + case TokenType.OpenBlock: + currentNode = addChildNode (currentNode, cl, tokPtr); + break; + case TokenType.CloseBlock: + case TokenType.BlockCommentEnd: + closeNodeAndGoUp (ref currentNode, cl); + break; + case TokenType.Preprocessor: + if (cl.Tokens [tokPtr].Content.StartsWith ("#region", StringComparison.Ordinal)) { + currentNode = addChildNode (currentNode, cl, tokPtr, "region"); + } else if (cl.Tokens [tokPtr].Content.StartsWith ("#endregion", StringComparison.Ordinal)) { + + closeNodeAndGoUp (ref currentNode, cl,"region"); + } + break; + } + onlyWhiteSpace = false; + tokPtr++; + } + ptrLine++; + } + /*ptrLine = 0; + while (ptrLine < buffer.LineCount) { + CodeLine cl = buffer [ptrLine]; + if (cl.IsFoldable) { + if (cl.SyntacticNode.Type == "comment" || cl.SyntacticNode.Type == "region") + cl.IsFolded = true; + } + ptrLine++; + }*/ + } + } +} + diff --git a/Crow.Coding/src/Parsers/StyleParser.cs b/Crow.Coding/src/Parsers/StyleParser.cs new file mode 100644 index 0000000..9a1e74c --- /dev/null +++ b/Crow.Coding/src/Parsers/StyleParser.cs @@ -0,0 +1,179 @@ +using System; +using Crow; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text.RegularExpressions; +using System.Linq; + +namespace Crow.Coding +{ + public class StyleParser : BufferParser + { + enum States { init, classNames, members, value, endOfStatement } + + public StyleParser (CodeBuffer _buffer) : base(_buffer) + { + } + + #region Character ValidityCheck + static Regex rxValidChar = new Regex(@"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf}"); + static Regex rxNameStartChar = new Regex(@"_|\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}"); + static Regex rxNameChar = new Regex(@"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf}"); + static Regex rxDecimal = new Regex(@"[0-9]+"); + static Regex rxHexadecimal = new Regex(@"[0-9a-fA-F]+"); + + public bool nextCharIsValidCharStartName + { + get { return rxNameStartChar.IsMatch(new string(new char[]{Peek()})); } + } + public bool nextCharIsValidCharName + { + get { return rxNameChar.IsMatch(new string(new char[]{Peek()})); } + } + #endregion + + States curState = States.classNames; + + public override void ParseCurrentLine () + { + //Debug.WriteLine (string.Format("parsing line:{0}", currentLine)); + CodeLine cl = buffer [currentLine]; + cl.Tokens = new List (); + + //retrieve current parser state from previous line + if (currentLine > 0) + curState = (States)buffer[currentLine - 1].EndingState; + else + curState = States.init; + + States previousEndingState = (States)cl.EndingState; + + while (! eol) { + SkipWhiteSpaces (); + + if (eol) + break; + + if (Peek () == '\n') { + if (currentTok != TokenType.Unknown) + throw new ParserException (currentLine, currentColumn, "Unexpected end of line"); + Read (); + eol = true; + continue; + } + + switch (Peek()) { + case '/': + readToCurrTok (true); + switch (Peek ()) { + case '/': + currentTok += ReadLine (); + saveAndResetCurrentTok (TokenType.LineComment); + break; + default: + currentTok += ReadLine (); + saveAndResetCurrentTok (TokenType.Unknown); + break; + } + break; + case ',': + if (curState != States.init && curState != States.classNames ) + throw new ParserException (currentLine, currentColumn, "Unexpected char ','"); + readAndResetCurrentTok (TokenType.OperatorOrPunctuation, true); + curState = States.classNames; + break; + case '{': + if (!(curState == States.init || curState == States.classNames)) + throw new ParserException (currentLine, currentColumn, "Unexpected char '{'"); + readAndResetCurrentTok (TokenType.OpenBlock, true); + curState = States.members; + break; + case '}': + if (curState != States.members) + throw new ParserException (currentLine, currentColumn, "Unexpected char '}'"); + readAndResetCurrentTok (TokenType.CloseBlock, true); + curState = States.classNames; + break; + case '=': + if (curState == States.classNames) + throw new ParserException (currentLine, currentColumn, "Unexpected char '='"); + setPreviousTokOfTypeTo (TokenType.Type, TokenType.Identifier); + readAndResetCurrentTok (TokenType.OperatorOrPunctuation, true); + curState = States.value; + break; + case '"': + if (curState != States.value) + throw new ParserException (currentLine, currentColumn, "Unexpected char '\"'"); + readAndResetCurrentTok (TokenType.StringLitteralOpening, true); + + while (!eol) { + currentTok += ReadLineUntil ("\""); + if (currentTok.Content [currentTok.Content.Length - 1] == '\\') + readToCurrTok (); + else + break; + } + if (eol) + throw new ParserException (currentLine, currentColumn, "Unexpected end of line"); + saveAndResetCurrentTok (TokenType.StringLitteral); + + readAndResetCurrentTok (TokenType.StringLitteralClosing, true); + curState = States.endOfStatement; + break; + case ';': + if (curState != States.endOfStatement) + throw new ParserException (currentLine, currentColumn, "Unexpected end of statement"); + readAndResetCurrentTok (TokenType.StatementEnding, true); + curState = States.members; + break; + default: + if (currentTok.Type != TokenType.Unknown) + throw new ParserException (currentLine, currentColumn, "error curtok not null"); + if (curState == States.value) + throw new ParserException (currentLine, currentColumn, "expecting value enclosed in '\"'"); + if (curState == States.endOfStatement) + throw new ParserException (currentLine, currentColumn, "expecting end of statement"); + + if (nextCharIsValidCharStartName) { + readToCurrTok (true); + while (nextCharIsValidCharName) + readToCurrTok (); + } + saveAndResetCurrentTok (TokenType.Type); + break; + } + } + + if (cl.EndingState != (int)curState && currentLine < buffer.LineCount - 1) + buffer [currentLine + 1].Tokens = null; + + cl.EndingState = (int)curState; + } + public override void SyntaxAnalysis () + { + initSyntaxAnalysis (); + Node currentNode = RootNode; + + for (int i = 0; i < buffer.LineCount; i++) { + CodeLine cl = buffer[i]; + if (cl.Tokens == null) + continue; + cl.SyntacticNode = null; + + int tokPtr = 0; + while (tokPtr < cl.Tokens.Count) { + switch (cl.Tokens [tokPtr].Type) { + case TokenType.OpenBlock: + currentNode = addChildNode (currentNode, cl, tokPtr, "style"); + break; + case TokenType.CloseBlock: + closeNodeAndGoUp (ref currentNode, cl, "style"); + break; + } + tokPtr++; + } + } + } + } +} + diff --git a/Crow.Coding/src/Parsers/XMLParser.cs b/Crow.Coding/src/Parsers/XMLParser.cs new file mode 100644 index 0000000..e5bfd89 --- /dev/null +++ b/Crow.Coding/src/Parsers/XMLParser.cs @@ -0,0 +1,314 @@ +using System; +using Crow; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Diagnostics; +using System.Linq; + +namespace Crow.Coding +{ + public class XMLParser : BufferParser + { + public new enum TokenType { + Unknown = BufferParser.TokenType.Unknown, + WhiteSpace = BufferParser.TokenType.WhiteSpace, + NewLine = BufferParser.TokenType.NewLine, + LineComment = BufferParser.TokenType.LineComment, + BlockCommentStart = BufferParser.TokenType.BlockCommentStart, + BlockComment = BufferParser.TokenType.BlockComment, + BlockCommentEnd = BufferParser.TokenType.BlockCommentEnd, + ElementName = BufferParser.TokenType.Type, + AttributeName = BufferParser.TokenType.Identifier, + ElementClosing = BufferParser.TokenType.StatementEnding, + Affectation = BufferParser.TokenType.OperatorOrPunctuation, + AttributeValueOpening = BufferParser.TokenType.StringLitteralOpening, + AttributeValueClosing = BufferParser.TokenType.StringLitteralClosing, + AttributeValue = BufferParser.TokenType.StringLitteral, + XMLDecl = BufferParser.TokenType.Preprocessor, + Doctype = BufferParser.TokenType.Keyword, + ElementStart = 50, + ElementEnd = 51, + Content = 60, + } + + public enum States + { + init, //first statement of prolog, xmldecl should only apear in this state + prolog, //misc before doctypedecl + InternalSubset, //doctype declaration subset + ExternalSubsetInit, + ExternalSubset, + BlockComment, + DTDEnd,//doctype finished + XML,//normal xml + StartTag,//inside start tag + Content,//after start tag with no closing slash + EndTag + } + + #region CTOR + public XMLParser (CodeBuffer _buffer) : base(_buffer) {} + #endregion + + enum Keywords + { + DOCTYPE, + ELEMENT, + ATTLIST, + ENTITY, + NOTATION + } + + States curState = States.init; + + #region Regular Expression for validity checks + //private static Regex rxValidChar = new Regex("[\u0020-\uD7FF]"); + private static Regex rxValidChar = new Regex(@"\u0009|\u000A|\u000D|[\u0020-\uD7FF]|[\uE000-\uFFFD]"); //| [\u10000-\u10FFFF] unable to set those plans + private static Regex rxNameStartChar = new Regex(@":|[A-Z]|_|[a-z]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u02FF]|[\u0370-\u037D]|[\u037F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]"); // | [\u10000-\uEFFFF] + private static Regex rxNameChar = new Regex(@":|[A-Z]|_|[a-z]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u02FF]|[\u0370-\u037D]|[\u037F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]|-|\.|[0-9]|\u00B7|[\u0300-\u036F]|[\u203F-\u2040]");//[\u10000-\uEFFFF]| + private static Regex rxDecimal = new Regex(@"[0-9]+"); + private static Regex rxHexadecimal = new Regex(@"[0-9a-fA-F]+"); + private static Regex rxAttributeValue = new Regex(@"[^<]"); + private static Regex rxEntityValue = new Regex(@"[^<]"); + private static Regex rxPubidChar = new Regex(@"\u0020|\u000D|\u000A|[a-zA-Z0-9]|[-\(\)\+\,\./:=\?;!\*#@\$_%]"); + #endregion + + #region Character ValidityCheck + public bool nextCharIsValidCharStartName + { + get { return rxNameStartChar.IsMatch(new string(new char[]{Peek()})); } + } + public bool nextCharIsValidCharName + { + get { return rxNameChar.IsMatch(new string(new char[]{Peek()})); } + } + #endregion + + public override void SetLineInError (ParserException ex) + { + base.SetLineInError (ex); + //buffer[ex.Line].Tokens.EndingState = (int)States.init; + } + + public override void ParseCurrentLine () + { + //Debug.WriteLine (string.Format("parsing line:{0}", currentLine)); + CodeLine cl = buffer [currentLine]; + cl.Tokens = new List (); + + //retrieve current parser state from previous line + if (currentLine > 0) + curState = (States)buffer[currentLine - 1].EndingState; + else + curState = States.init; + + States previousEndingState = (States)cl.EndingState; + + + while (! eol) { + if (curState != States.Content) + SkipWhiteSpaces (); + + if (eol) + break; + + if (Peek () == '\n') { + if (currentTok != TokenType.Unknown) + throw new ParserException (currentLine, currentColumn, "Unexpected end of line"); + Read (); + eol = true; + continue; + } + + if (curState == States.BlockComment) { + if (currentTok != TokenType.Unknown) + Debugger.Break (); + + currentTok.Start = CurrentPosition; + currentTok.Type = (BufferParser.TokenType)TokenType.BlockComment; + currentTok += ReadLineUntil ("-->"); + if (Peek (3) == "-->") { + readToCurrTok (3); + curState = States.XML; + } + saveAndResetCurrentTok (); + continue; + } + + switch (Peek()) { + case '<': + readToCurrTok (true); + switch (Peek()) { + case '?': + if (curState != States.init) + throw new ParserException (currentLine, currentColumn, "xml decl may appear only on first line"); + readToCurrTok (); + currentTok += ReadLineUntil ("?>"); + if (Peek (2) != "?>") + throw new ParserException (currentLine, currentColumn, "expecting '?>'"); + readToCurrTok (2); + saveAndResetCurrentTok (TokenType.XMLDecl); + curState = States.prolog; + break; + case '!': + readToCurrTok (); + switch (Peek()) { + case '-': + readToCurrTok (); + if (Peek () != '-') + throw new ParserException (currentLine, currentColumn, "Expecting comment start tag"); + readToCurrTok (); + currentTok += ReadLineUntil ("--"); + if (Peek (3) == "-->") { + readToCurrTok (3); + }else + curState = States.BlockComment; + saveAndResetCurrentTok (TokenType.BlockComment); + break; + case 'D': + case 'd': + string tmp = Read (7); + currentTok += tmp; + if (!string.Equals (tmp, "DOCTYPE", StringComparison.OrdinalIgnoreCase)) + throw new ParserException (currentLine, currentColumn, "Expecting 'doctype'"); + saveAndResetCurrentTok (TokenType.Doctype); + break; + default: + throw new ParserException (currentLine, currentColumn, "error"); + } + break; + default: + if (!(curState == States.Content || curState == States.XML || curState == States.init || curState == States.prolog)) + throw new ParserException (currentLine, currentColumn, "Unexpected char: '<'"); + if (Peek () == '/') { + curState = States.EndTag; + readToCurrTok (); + saveAndResetCurrentTok (TokenType.ElementEnd); + } else { + curState = States.StartTag; + saveAndResetCurrentTok (TokenType.ElementStart); + } + + if (!nextCharIsValidCharStartName) + throw new ParserException (currentLine, currentColumn, "Expected element name"); + + readToCurrTok (true); + while (nextCharIsValidCharName) + readToCurrTok (); + + saveAndResetCurrentTok (TokenType.ElementName); + break; + } + break; + case '/': + if (curState != States.StartTag) + throw new ParserException (currentLine, currentColumn, "Unexpected char: '/'"); + readToCurrTok (true); + if (Peek () != '>') + throw new ParserException (currentLine, currentColumn, "Expecting '>'"); + readAndResetCurrentTok (TokenType.ElementEnd); + + curState = States.XML; + break; + case '>': + readAndResetCurrentTok (TokenType.ElementClosing, true); + switch (curState) { + case States.EndTag: + curState = States.XML; + break; + case States.StartTag: + curState = States.Content; + break; + default: + throw new ParserException (currentLine, currentColumn, "Unexpected char: '>'"); + } + break; + default: + switch (curState) { + case States.StartTag: + if (!nextCharIsValidCharStartName) + throw new ParserException (currentLine, currentColumn, "Expected attribute name"); + readToCurrTok (true); + while (nextCharIsValidCharName) + readToCurrTok (); + saveAndResetCurrentTok (TokenType.AttributeName); + + SkipWhiteSpaces (); + + if (Peek () != '=') + throw new ParserException (currentLine, currentColumn, "Expecting: '='"); + readAndResetCurrentTok (TokenType.Affectation, true); + + SkipWhiteSpaces (); + + char openAttVal = Peek (); + if (openAttVal != '"' && openAttVal != '\'') + throw new ParserException (currentLine, currentColumn, "Expecting attribute value enclosed either in '\"' or in \"'\""); + readAndResetCurrentTok (TokenType.AttributeValueOpening, true); + + currentTok.Start = CurrentPosition; + currentTok.Content = ReadLineUntil (new string (new char[]{ openAttVal })); + saveAndResetCurrentTok (TokenType.AttributeValue); + + if (Peek () != openAttVal) + throw new ParserException (currentLine, currentColumn, string.Format ("Expecting {0}", openAttVal)); + readAndResetCurrentTok (TokenType.AttributeValueClosing, true); + break; + case States.Content: + currentTok.Start = CurrentPosition; + currentTok.Content = ReadLineUntil ("<"); + saveAndResetCurrentTok (TokenType.Content); + break; + default: + throw new ParserException (currentLine, currentColumn, "unexpected char: " + Peek ()); + } + break; + } + } + + if (cl.EndingState != (int)curState && currentLine < buffer.LineCount - 1) + buffer [currentLine + 1].Tokens = null; + + cl.EndingState = (int)curState; + } + + public override void SyntaxAnalysis () + { + initSyntaxAnalysis (); + Node currentNode = RootNode; + + for (int i = 0; i < buffer.LineCount; i++) { + CodeLine cl = buffer[i]; + if (cl.Tokens == null) + continue; + cl.SyntacticNode = null; + + int tokPtr = 0; + while (tokPtr < cl.Tokens.Count) { + switch ((XMLParser.TokenType)cl.Tokens [tokPtr].Type) { + case TokenType.ElementStart: + tokPtr++; + currentNode = addChildNode (currentNode, cl, tokPtr, "Element"); + break; + case TokenType.ElementEnd: + tokPtr++; + if (tokPtr < cl.Tokens.Count) { + if ((XMLParser.TokenType)cl.Tokens [tokPtr].Type == TokenType.ElementName && + cl.Tokens [tokPtr].Content != currentNode.Name) + throw new ParserException (currentLine, currentColumn, "Closing tag mismatch"); + } + closeNodeAndGoUp (ref currentNode, cl, "Element"); + break; + case TokenType.ElementClosing: + //currentNode = currentNode.Parent; + break; + default: + break; + } + tokPtr++; + } + } + } + } +} + diff --git a/Crow.Coding/src/Parsers2/Tokenizer.cs b/Crow.Coding/src/Parsers2/Tokenizer.cs new file mode 100644 index 0000000..bcda1ec --- /dev/null +++ b/Crow.Coding/src/Parsers2/Tokenizer.cs @@ -0,0 +1,67 @@ +// +// Tokenizer.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Crow.Text; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace Crow.Coding2 +{ + public static class TokenType { + public const int Undefine = 0; + public const int WhiteSpace = 0; + } + public class Token { + public int ptr; + public int length; + } + + public class Tokenizer + { + #region Regular Expression for validity checks + public Regex rxValidChar = new Regex(@"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf}"); + public Regex rxNameStartChar = new Regex(@"_|\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}"); + public Regex rxNameChar = new Regex(@"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf}"); + public Regex rxNewLineChar = new Regex(@"\u000D|\u000A|\u0085|\u2028|\u2029"); + public Regex rxWhiteSpaceChar = new Regex(@"\p{Zs}|\u0009|\u000B|\u000C"); + public Regex rxDecimal = new Regex(@"[0-9]+"); + public Regex rxHexadecimal = new Regex(@"[0-9a-fA-F]+"); + #endregion + + public List Tokens; + + public Tokenizer (TextBuffer buffer) + { + } + + + public void Tokenize () { + + } + + } +} + diff --git a/Crow.Coding/src/SourceEditor.cs b/Crow.Coding/src/SourceEditor.cs new file mode 100644 index 0000000..e32efd9 --- /dev/null +++ b/Crow.Coding/src/SourceEditor.cs @@ -0,0 +1,1195 @@ +// Copyright (c) 2013-2020 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.ComponentModel; +using Crow.Cairo; +using System.Collections.Generic; +using System.Diagnostics; +using Glfw; +using System.IO; +using System.Threading; + +namespace Crow.Coding +{ + /// + /// Scrolling text box optimized for monospace fonts, for coding + /// + public class SourceEditor : ScrollingObject { + #region CTOR + public SourceEditor (): base() + { + formatting.Add ((int)XMLParser.TokenType.AttributeName, new TextFormatting (Colors.DarkSlateGrey, Colors.Transparent)); + formatting.Add ((int)XMLParser.TokenType.ElementName, new TextFormatting (Colors.DarkBlue, Colors.Transparent)); + formatting.Add ((int)XMLParser.TokenType.ElementStart, new TextFormatting (Colors.Black, Colors.Transparent)); + formatting.Add ((int)XMLParser.TokenType.ElementEnd, new TextFormatting (Colors.Black, Colors.Transparent)); + formatting.Add ((int)XMLParser.TokenType.ElementClosing, new TextFormatting (Colors.Black, Colors.Transparent)); + + formatting.Add ((int)XMLParser.TokenType.AttributeValueOpening, new TextFormatting (Colors.Crimson, Colors.Transparent)); + formatting.Add ((int)XMLParser.TokenType.AttributeValueClosing, new TextFormatting (Colors.Crimson, Colors.Transparent)); + formatting.Add ((int)XMLParser.TokenType.AttributeValue, new TextFormatting (Colors.FireBrick, Colors.Transparent, false, true)); + formatting.Add ((int)XMLParser.TokenType.XMLDecl, new TextFormatting (Colors.ForestGreen, Colors.Transparent)); + formatting.Add ((int)XMLParser.TokenType.Content, new TextFormatting (Colors.DimGrey, Colors.Transparent, false, true)); + + formatting.Add ((int)BufferParser.TokenType.BlockComment, new TextFormatting (Colors.Grey, Colors.Transparent, false, true)); + formatting.Add ((int)BufferParser.TokenType.LineComment, new TextFormatting (Colors.Grey, Colors.Transparent, false, true)); + formatting.Add ((int)BufferParser.TokenType.OperatorOrPunctuation, new TextFormatting (Colors.Black, Colors.Transparent)); + formatting.Add ((int)BufferParser.TokenType.Keyword, new TextFormatting (Colors.Teal, Colors.Transparent)); + //formatting.Add ((int)BufferParser.TokenType.Keyword, new TextFormatting (Color.DarkCyan, Color.Transparent)); + + parsing.Add (".crow", "Crow.Coding.XMLParser"); + parsing.Add (".svg", "Crow.Coding.XMLParser"); + parsing.Add (".template", "Crow.Coding.XMLParser"); + parsing.Add (".csproj", "Crow.Coding.XMLParser"); + parsing.Add (".xml", "Crow.Coding.XMLParser"); + parsing.Add (".cs", "Crow.Coding.CSharpParser"); + parsing.Add (".style", "Crow.Coding.StyleParser"); + + buffer = new CodeBuffer (); + buffer.LineUpadateEvent += Buffer_LineUpadateEvent; + buffer.LineAdditionEvent += Buffer_LineAdditionEvent;; + buffer.LineRemoveEvent += Buffer_LineRemoveEvent; + buffer.BufferCleared += Buffer_BufferCleared; + buffer.SelectionChanged += Buffer_SelectionChanged; + buffer.PositionChanged += Buffer_PositionChanged; + buffer.FoldingEvent += Buffer_FoldingEvent; + buffer.Add (new CodeLine("")); + } + #endregion + + ReaderWriterLockSlim editorMutex = new ReaderWriterLockSlim (LockRecursionPolicy.SupportsRecursion); + + string oldSource = ""; + //save requested position on error, and try it on next move + int requestedLine = 0, requestedCol = 0; + volatile bool isDirty = false; + + const int leftMarginGap = 3;//gap between items in margin and text + const int foldSize = 9;//folding rectangles size + int foldMargin = 9;// { get { return parser == null ? 0 : parser.SyntacticTreeMaxDepth * foldHSpace; }}//folding margin size + + #region private and protected fields + bool foldingEnabled = true; + int leftMargin = 0; //margin used to display line numbers, folding errors,etc... + int visibleLines = 1; + int visibleColumns = 1; + int firstPrintedLine = -1; + int printedCurrentLine = 0;//Index of the currentline in the PrintedLines array + string filePath; + + CodeBuffer buffer; + BufferParser parser; + List PrintedLines;//list of lines visible in the Editor depending on scrolling and folding + + Dictionary formatting = new Dictionary(); + Dictionary parsing = new Dictionary(); + + Color selBackground; + Color selForeground; + int selStartCol; + int selEndCol; + + protected Rectangle rText; + protected FontExtents fe; + protected TextExtents te; + + Point mouseLocalPos; + bool doubleClicked = false; + #endregion + + void measureLeftMargin () { + leftMargin = 0; + if (PrintLineNumbers) + leftMargin += (int)Math.Ceiling((double)buffer.LineCount.ToString().Length * fe.MaxXAdvance) +6; + if (foldingEnabled) + leftMargin += foldMargin; + if (leftMargin > 0) + leftMargin += leftMarginGap; + updateVisibleColumns (); + } + void findLongestLineAndUpdateMaxScrollX() { + buffer.FindLongestVisualLine (); + updateMaxScrollX (); +// Debug.WriteLine ("SourceEditor: Find Longest line and update maxscrollx: {0} visible cols:{1}", MaxScrollX, visibleColumns); + } + /// + /// Updates visible line in widget, adapt max scroll y and updatePrintedLines + /// + void updateVisibleLines(){ + visibleLines = (int)Math.Floor ((double)ClientRectangle.Height / (fe.Ascent+fe.Descent)); + NotifyValueChanged ("VisibleLines", visibleLines); + updateMaxScrollY (); + updatePrintedLines (); + RegisterForGraphicUpdate (); +// System.Diagnostics.Debug.WriteLine ("update visible lines: " + visibleLines); +// System.Diagnostics.Debug.WriteLine ("update MaxScrollY: " + MaxScrollY); + } + void updateVisibleColumns(){ + visibleColumns = (int)Math.Floor ((double)(ClientRectangle.Width - leftMargin)/ fe.MaxXAdvance); + NotifyValueChanged ("VisibleColumns", visibleColumns); + updateMaxScrollX (); +// System.Diagnostics.Debug.WriteLine ("update visible columns: {0} leftMargin:{1}",visibleColumns, leftMargin); +// System.Diagnostics.Debug.WriteLine ("update MaxScrollX: " + MaxScrollX); + } + void updateMaxScrollX () { + MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns); + if (buffer.longestLineCharCount > 0) + NotifyValueChanged ("ChildWidthRatio", Slot.Width * visibleColumns / buffer.longestLineCharCount); + } + void updateMaxScrollY () { + if (parser == null || !foldingEnabled) { + MaxScrollY = Math.Max (0, buffer.LineCount - visibleLines); + if (buffer.UnfoldedLines > 0) + NotifyValueChanged ("ChildHeightRatio", Slot.Height * visibleLines / buffer.UnfoldedLines); + } else { + MaxScrollY = Math.Max (0, buffer.UnfoldedLines - visibleLines); + if (buffer.UnfoldedLines > 0) + NotifyValueChanged ("ChildHeightRatio", Slot.Height * visibleLines / buffer.UnfoldedLines); + } + } + void updatePrintedLines () { + buffer.editMutex.EnterReadLock (); + editorMutex.EnterWriteLock (); + + PrintedLines = new List (); + int curL = 0; + int i = 0; + + while (curL < buffer.LineCount && i < ScrollY) { + if (buffer [curL].IsFolded) + curL = buffer.GetEndNodeIndex (curL); + curL++; + i++; + } + + firstPrintedLine = curL; + i = 0; + while (i < visibleLines && curL < buffer.LineCount) { + PrintedLines.Add (buffer [curL]); + + if (buffer [curL].IsFolded) + curL = buffer.GetEndNodeIndex (curL); + + curL++; + i++; + } + + buffer.editMutex.ExitReadLock (); + editorMutex.ExitWriteLock (); + } + void updateOnScreenCurLineFromBuffCurLine(){ + printedCurrentLine = PrintedLines.IndexOf (buffer.CurrentCodeLine); + } + void toogleFolding (int line) { + if (parser == null || !foldingEnabled) + return; + buffer.ToogleFolding (line); + } + + #region Editor overrides + /*protected override void updateEditorFromProjFile () + { + Debug.WriteLine("\t\tSourceEditor updateEditorFromProjFile"); + + //buffer.editMutex.EnterWriteLock (); + loadSource (); + //buffer.editMutex.ExitWriteLock (); + + isDirty = false; + oldSource = projFile.Source; + CurrentLine = requestedLine; + CurrentColumn = requestedCol; + projFile.RegisteredEditors [this] = true; + } + protected override void updateProjFileFromEditor () + { + Debug.WriteLine("\t\tSourceEditor updateProjFileFromEditor"); + + buffer.editMutex.EnterWriteLock (); + string newsrc = buffer.FullText; + buffer.editMutex.ExitWriteLock (); + projFile.UpdateSource (this, newsrc); + } + protected override bool EditorIsDirty { + get { return isDirty; } + set { isDirty = value; } + } + protected override bool IsReady { + get { return projFile != null && buffer != null; } + }*/ + #endregion + + #region Buffer events handlers + void Buffer_BufferCleared (object sender, EventArgs e) + { + editorMutex.EnterWriteLock (); + + buffer.longestLineCharCount = 0; + buffer.longestLineIdx = 0; + measureLeftMargin (); + MaxScrollX = MaxScrollY = 0; + PrintedLines = null; + RegisterForGraphicUpdate (); + notifyPositionChanged (); + isDirty = true; + + editorMutex.ExitWriteLock (); + } + void Buffer_LineAdditionEvent (object sender, CodeBufferEventArgs e) + { + for (int i = 0; i < e.LineCount; i++) { + int lptr = e.LineStart + i; + int charCount = buffer[lptr].PrintableLength; + if (charCount > buffer.longestLineCharCount) { + buffer.longestLineIdx = lptr; + buffer.longestLineCharCount = charCount; + }else if (lptr <= buffer.longestLineIdx) + buffer.longestLineIdx++; + if (parser == null) + continue; + parser.TryParseBufferLine (e.LineStart + i); + } + + if (parser != null) + parser.reparseSource (); + + measureLeftMargin (); + + updatePrintedLines (); + updateMaxScrollY (); + RegisterForGraphicUpdate (); + notifyPositionChanged (); + isDirty = true; + } + void Buffer_LineRemoveEvent (object sender, CodeBufferEventArgs e) + { + bool trigFindLongestLine = false; + for (int i = 0; i < e.LineCount; i++) { + int lptr = e.LineStart + i; + if (lptr <= buffer.longestLineIdx) + trigFindLongestLine = true; + } + if (trigFindLongestLine) + findLongestLineAndUpdateMaxScrollX (); + + measureLeftMargin (); + updatePrintedLines (); + updateMaxScrollY (); + RegisterForGraphicUpdate (); + notifyPositionChanged (); + isDirty = true; + } + void Buffer_LineUpadateEvent (object sender, CodeBufferEventArgs e) + { + bool trigFindLongestLine = false; + for (int i = 0; i < e.LineCount; i++) { + + int lptr = e.LineStart + i; + if (lptr == buffer.longestLineIdx) + trigFindLongestLine = true; + else if (buffer[lptr].PrintableLength > buffer.longestLineCharCount) { + buffer.longestLineCharCount = buffer[lptr].PrintableLength; + buffer.longestLineIdx = lptr; + } + } + if (trigFindLongestLine) + findLongestLineAndUpdateMaxScrollX (); + + RegisterForGraphicUpdate (); + notifyPositionChanged (); + isDirty = true; + } + void Buffer_PositionChanged (object sender, EventArgs e) + { + //Console.WriteLine ("Position changes: ({0},{1})", buffer.CurrentLine, buffer.CurrentColumn); + int cc = buffer.CurrentTabulatedColumn; + + if (cc > visibleColumns + ScrollX) { + ScrollX = cc - visibleColumns; + } else if (cc < ScrollX) + ScrollX = cc; + + RegisterForGraphicUpdate (); + updateOnScreenCurLineFromBuffCurLine (); + notifyPositionChanged (); + } + + void Buffer_SelectionChanged (object sender, EventArgs e) + { + RegisterForGraphicUpdate (); + } + void Buffer_FoldingEvent (object sender, CodeBufferEventArgs e) + { + updatePrintedLines (); + updateOnScreenCurLineFromBuffCurLine (); + updateMaxScrollY (); + RegisterForGraphicUpdate (); + } + #endregion + + void notifyPositionChanged (){ + try { + NotifyValueChanged ("CurrentLine", buffer.CurrentLine+1); + NotifyValueChanged ("CurrentColumn", buffer.CurrentColumn+1); + NotifyValueChanged ("CurrentLineHasError", CurrentLineHasError); + NotifyValueChanged ("CurrentLineError", CurrentLineError); + } catch (Exception ex) { + Console.WriteLine (ex.ToString ()); + } + } + + #region Public Crow Properties + public int CurrentLine{ + get { return buffer == null ? 0 : buffer.CurrentLine+1; } + set { + try { + int l = value - 1; + if (l == buffer.CurrentLine) + return; + buffer.CurrentLine = l; + l = buffer.CurrentLine; //reaffect from buffer where bound check is made + if ((bool)buffer [l]?.IsFolded) + buffer.ToogleFolding (l);//unfold current line + } catch (Exception ex) { + requestedLine = value - 1; + Console.WriteLine ("Error cur column: " + ex); + } + } + } + public int CurrentColumn{ + get { return buffer == null ? 0 : buffer.CurrentColumn+1; } + set { + try { + if (value - 1 == buffer.CurrentColumn) + return; + buffer.CurrentColumn = value - 1; + } catch (Exception ex) { + requestedCol = value - 1; + Console.WriteLine ("Error cur column: " + ex.ToString ()); + } + } + } + public bool PrintLineNumbers + { + get { return Configuration.Global.Get ("PrintLineNumbers"); } + set { + if (PrintLineNumbers == value) + return; + Configuration.Global.Set ("PrintLineNumbers", value); + NotifyValueChanged ("PrintLineNumbers", PrintLineNumbers); + measureLeftMargin (); + RegisterForGraphicUpdate (); + } + } + [DefaultValue("SteelBlue")] + public virtual Color SelectionBackground { + get { return selBackground; } + set { + if (value == selBackground) + return; + selBackground = value; + NotifyValueChanged ("SelectionBackground", selBackground); + RegisterForRedraw (); + } + } + [DefaultValue("White")] + public virtual Color SelectionForeground { + get { return selForeground; } + set { + if (value == selForeground) + return; + selForeground = value; + NotifyValueChanged ("SelectionForeground", selForeground); + RegisterForRedraw (); + } + } + public override int ScrollY { + get { + return base.ScrollY; + } + set { + if (value == base.ScrollY) + return; + base.ScrollY = value; + updatePrintedLines (); + updateOnScreenCurLineFromBuffCurLine (); + RegisterForGraphicUpdate (); + } + } + public ParserException CurrentLineError { + get { return buffer?.CurrentCodeLine?.exception; } + } + public bool CurrentLineHasError { + get { return buffer == null ? false : buffer.CurrentCodeLine == null ? false : + buffer.CurrentCodeLine.exception != null; } + } + public string FilePath { + get => filePath; + set { + if (filePath == value) + return; + filePath = value; + if (!string.IsNullOrEmpty (filePath)) { + parser = getParserFromExt (System.IO.Path.GetExtension (filePath)); + loadSource(); + } + } + } + #endregion + + BufferParser getParserFromExt (string extension) { + if (string.IsNullOrEmpty(extension)) + return null; + if (!parsing.ContainsKey(extension)) + return null; + Type parserType = Type.GetType (parsing [extension]); + if (parserType == null) + return null; + return (BufferParser)Activator.CreateInstance (parserType, buffer ); + } + void loadSource () { + + try { + using (StreamReader sr = new StreamReader (filePath)) { + if (parser == null) + buffer.Load (sr.ReadToEnd()); + else//parser may have special linebrk rules + buffer.Load (sr.ReadToEnd (), parser.LineBrkRegex); + } + } catch (Exception ex) { + Debug.WriteLine (ex.ToString ()); + } + + updateMaxScrollY (); + MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns); + updatePrintedLines (); + + RegisterForGraphicUpdate (); + } + + /// + /// Current editor line, when set, update buffer.CurrentLine + /// + int PrintedCurrentLine { + get { return printedCurrentLine;} + set { + if (value < 0) { + ScrollY += value; + printedCurrentLine = 0; + } else if (PrintedLines.Count < visibleLines && value >= PrintedLines.Count) { + printedCurrentLine = PrintedLines.Count - 1; + }else if (value >= visibleLines) { + ScrollY += value - visibleLines + 1; + printedCurrentLine = visibleLines - 1; + }else + printedCurrentLine = value; + //Debug.WriteLine ("printed current line:" + printedCurrentLine.ToString ()); + //update position in buffer + buffer.CurrentLine = buffer.IndexOf (PrintedLines[printedCurrentLine]); + } + } + int getTabulatedColumn (int col, int line) { + return buffer [line].Content.Substring (0, col).Replace ("\t", new String (' ', Interface.TAB_SIZE)).Length; + } + int getTabulatedColumn (Point pos) { + return getTabulatedColumn (pos.X,pos.Y); + } + /// + /// Moves cursor one char to the left, move up if cursor reaches start of line + /// + /// true if move succeed + public bool MoveLeft(){ + if (buffer.CurrentColumn == 0) { + if (printedCurrentLine == 0) + return false; + PrintedCurrentLine--; + buffer.CurrentColumn = int.MaxValue; + } else + buffer.CurrentColumn--; + return true; + } + /// + /// Moves cursor one char to the right, move down if cursor reaches end of line + /// + /// true if move succeed + public bool MoveRight(){ + if (buffer.CurrentColumn >= buffer.CurrentCodeLine.Length) { + if (PrintedCurrentLine == buffer.UnfoldedLines - 1) + return false; + buffer.CurrentColumn = 0; + PrintedCurrentLine++; + } else + buffer.CurrentColumn++; + return true; + } + + #region Drawing + void drawLine(Context gr, Rectangle cb, int i) { + CodeLine cl = PrintedLines[i]; + int lineIndex = buffer.IndexOf(cl); + + double y = cb.Y + (fe.Ascent+fe.Descent) * i, x = cb.X; + + //Draw line numbering + Color mgFg = Colors.Jet; + Color mgBg = Colors.Grey; + if (PrintLineNumbers){ + Rectangle mgR = new Rectangle ((int)x, (int)y, leftMargin - leftMarginGap, (int)Math.Ceiling((fe.Ascent+fe.Descent))); + if (cl.exception != null) { + mgBg = Colors.Red; + if (buffer.CurrentLine == lineIndex) + mgFg = Colors.White; + else + mgFg = Colors.LightGrey; + }else if (buffer.CurrentLine == lineIndex && HasFocus) { + mgFg = Colors.Black; + mgBg = Colors.DarkGrey; + } + string strLN = (lineIndex+1).ToString (); + gr.SetSource (mgBg); + gr.Rectangle (mgR); + gr.Fill(); + gr.SetSource (mgFg); + + gr.MoveTo (cb.X + (int)(gr.TextExtents (buffer.LineCount.ToString()).Width - gr.TextExtents (strLN).Width), y + fe.Ascent); + gr.ShowText (strLN); + gr.Fill (); + } + + + //draw folding + if (foldingEnabled){ + + Rectangle rFld = new Rectangle (cb.X + leftMargin - leftMarginGap - foldMargin, + (int)(y + (fe.Ascent + fe.Descent) / 2.0 - foldSize / 2.0), foldSize, foldSize); + + gr.SetSource (Colors.Black); + gr.LineWidth = 1.0; + + int level = 0; + bool closingNode = false; + + if (currentNode != null) { + if (cl == currentNode.EndLine) { + currentNode = currentNode.Parent; + closingNode = true; + } + if (currentNode != null) + level = currentNode.Level - 1; + } + + /*for (int l = 0; l < level; l++) { + gr.MoveTo (rFld.Center.X + 0.5, y); + gr.LineTo (rFld.Center.X + 0.5, y + fe.Ascent + fe.Descent); + rFld.Left += foldHSpace; + }*/ + if (level > 0) { + gr.MoveTo (rFld.Center.X + 0.5, y); + gr.LineTo (rFld.Center.X + 0.5, y + fe.Ascent + fe.Descent); + } + if (closingNode) { + gr.MoveTo (rFld.Center.X + 0.5, y); + gr.LineTo (rFld.Center.X + 0.5, y + fe.Ascent / 2 + 0.5); + gr.LineTo (rFld.Center.X + 0.5 + foldSize / 2, y + fe.Ascent / 2 + 0.5); + closingNode = false; + } + gr.SetDash (new double[]{ 1.5 },0.0); + gr.SetSource (Colors.Grey); + gr.Stroke (); + gr.SetDash (new double[]{}, 0.0); + + if (cl.IsFoldable) { + gr.Rectangle (rFld); + gr.SetSource (Colors.White); + gr.Fill(); + gr.SetSource (Colors.Black); + gr.Rectangle (rFld, 1.0); + if (cl.IsFolded) { + gr.MoveTo (rFld.Center.X + 0.5, rFld.Y + 2); + gr.LineTo (rFld.Center.X + 0.5, rFld.Bottom - 2); + }else + currentNode = cl.SyntacticNode; + + gr.MoveTo (rFld.Left + 2, rFld.Center.Y + 0.5); + gr.LineTo (rFld.Right - 2, rFld.Center.Y + 0.5); + gr.Stroke (); + } + } + + gr.SetSource (Foreground); + x += leftMargin; + + if (cl.Tokens == null) + drawRawCodeLine (gr, x, y, i, lineIndex); + else + drawParsedCodeLine (gr, x, y, i, lineIndex); + } + Node currentNode = null; +// void drawParsed(Context gr){ +// if (PrintedLines == null) +// return; +// +// gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); +// gr.SetFontSize (Font.Size); +// gr.FontOptions = Interface.FontRenderingOptions; +// gr.Antialias = Interface.Antialias; +// +// Rectangle cb = ClientRectangle; +// gr.Save (); +// CairoHelpers.CairoRectangle (gr, cb, CornerRadius); +// gr.Clip (); +// +// bool selectionInProgress = false; +// +// Foreground.SetAsSource (gr); +// +// #region draw text cursor +// if (SelBegin != SelRelease) +// selectionInProgress = true; +// else if (HasFocus){ +// gr.LineWidth = 1.0; +// double cursorX = + leftMargin + cb.X + (CurrentColumn - ScrollX) * fe.MaxXAdvance; +// gr.MoveTo (0.5 + cursorX, cb.Y + printedCurrentLine * (fe.Ascent+fe.Descent)); +// gr.LineTo (0.5 + cursorX, cb.Y + (printedCurrentLine + 1) * (fe.Ascent+fe.Descent)); +// gr.Stroke(); +// } +// #endregion +// +// for (int i = 0; i < PrintedLines.Count; i++) +// drawTokenLine (gr, i, selectionInProgress, cb); +// +// gr.Restore (); +// } + void drawRawCodeLine(Context gr, double x, double y, int i, int lineIndex) { + string lstr = buffer[lineIndex].PrintableContent; + if (ScrollX < lstr.Length) + lstr = lstr.Substring (ScrollX); + else + lstr = ""; + + gr.MoveTo (x, y + fe.Ascent); + gr.ShowText (lstr); + gr.Fill (); + + if (!buffer.SelectionIsEmpty && lineIndex >= buffer.SelectionStart.Y && lineIndex <= buffer.SelectionEnd.Y) { + double rLineX = x, + rLineY = y, + rLineW = lstr.Length * fe.MaxXAdvance; + + //System.Diagnostics.Debug.WriteLine ("sel start: " + buffer.SelectionStart + " sel end: " + buffer.SelectionEnd); + if (lineIndex == buffer.SelectionStart.Y) { + rLineX += (selStartCol - ScrollX) * fe.MaxXAdvance; + rLineW -= selStartCol * fe.MaxXAdvance; + } + if (lineIndex == buffer.SelectionEnd.Y) + rLineW -= (lstr.Length - selEndCol) * fe.MaxXAdvance; + + gr.Save (); + gr.Operator = Operator.Source; + gr.Rectangle (rLineX, rLineY, rLineW, (fe.Ascent+fe.Descent)); + gr.SetSource (SelectionBackground); + gr.FillPreserve (); + gr.Clip (); + gr.Operator = Operator.Over; + gr.SetSource (SelectionForeground); + gr.MoveTo (x, y + fe.Ascent); + gr.ShowText (lstr); + gr.Fill (); + gr.Restore (); + } + } + void drawParsedCodeLine (Context gr, double x, double y, int i, int lineIndex) { + int lPtr = 0; + CodeLine cl = PrintedLines[i]; + + for (int t = 0; t < cl.Tokens.Count; t++) { + string lstr = cl.Tokens [t].PrintableContent; + if (lPtr < ScrollX) { + if (lPtr - ScrollX + lstr.Length <= 0) { + lPtr += lstr.Length; + continue; + } + lstr = lstr.Substring (ScrollX - lPtr); + lPtr += ScrollX - lPtr; + } + Color bg = this.Background; + Color fg = this.Foreground; + Color selbg = this.SelectionBackground; + Color selfg = this.SelectionForeground; + FontSlant fts = FontSlant.Normal; + FontWeight ftw = FontWeight.Normal; + + if (formatting.ContainsKey ((int)cl.Tokens [t].Type)) { + TextFormatting tf = formatting [(int)cl.Tokens [t].Type]; + bg = tf.Background; + fg = tf.Foreground; + if (tf.Bold) + ftw = FontWeight.Bold; + if (tf.Italic) + fts = FontSlant.Italic; + } + + gr.SelectFontFace (Font.Name, fts, ftw); + gr.SetSource (fg); + + gr.MoveTo (x, y + fe.Ascent); + gr.ShowText (lstr); + gr.Fill (); + + if (buffer.SelectionInProgress && lineIndex >= buffer.SelectionStart.Y && lineIndex <= buffer.SelectionEnd.Y && + !(lineIndex == buffer.SelectionStart.Y && lPtr + lstr.Length <= selStartCol) && + !(lineIndex == buffer.SelectionEnd.Y && selEndCol <= lPtr)) { + + double rLineX = x, + rLineY = y, + rLineW = lstr.Length * fe.MaxXAdvance; + double startAdjust = 0.0; + + if ((lineIndex == buffer.SelectionStart.Y) && (selStartCol < lPtr + lstr.Length) && (selStartCol > lPtr)) + startAdjust = (selStartCol - lPtr) * fe.MaxXAdvance; + rLineX += startAdjust; + if ((lineIndex == buffer.SelectionEnd.Y) && (selEndCol < lPtr + lstr.Length)) + rLineW = (selEndCol - lPtr) * fe.MaxXAdvance; + rLineW -= startAdjust; + + gr.Save (); + gr.Operator = Operator.Source; + gr.Rectangle (rLineX, rLineY, rLineW, (fe.Ascent+fe.Descent)); + gr.SetSource (selbg); + gr.FillPreserve (); + gr.Clip (); + gr.Operator = Operator.Over; + gr.SetSource (selfg); + gr.MoveTo (x, y + fe.Ascent); + gr.ShowText (lstr); + gr.Fill (); + gr.Restore (); + } + x += (int)lstr.Length * fe.MaxXAdvance; + lPtr += lstr.Length; + } + } + + #endregion + + #region GraphicObject overrides + public override Font Font { + get { return base.Font; } + set { + base.Font = value; + using (Context gr = new Context (IFace.surf)) { + gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); + gr.SetFontSize (Font.Size); + + fe = gr.FontExtents; + } + MaxScrollY = 0; + RegisterForGraphicUpdate (); + } + } + public override int measureRawSize(LayoutingType lt) + { + if (lt == LayoutingType.Height) + return (int)Math.Ceiling((fe.Ascent+fe.Descent) * buffer.LineCount) + Margin * 2; + + return (int)(fe.MaxXAdvance * buffer.longestLineCharCount) + Margin * 2 + leftMargin; + } + public override void OnLayoutChanges (LayoutingType layoutType) + { + base.OnLayoutChanges (layoutType); + + if (layoutType == LayoutingType.Height) + updateVisibleLines (); + else if (layoutType == LayoutingType.Width) + updateVisibleColumns (); + } + + protected override void onDraw (Context gr) + { + base.onDraw (gr); + + gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); + gr.SetFontSize (Font.Size); + gr.FontOptions = Interface.FontRenderingOptions; + gr.Antialias = Interface.Antialias; + + Rectangle cb = ClientRectangle; + + Foreground.SetAsSource (gr); + + buffer.editMutex.EnterReadLock (); + editorMutex.EnterReadLock (); + + #region draw text cursor + if (buffer.SelectionInProgress){ + selStartCol = getTabulatedColumn (buffer.SelectionStart); + selEndCol = getTabulatedColumn (buffer.SelectionEnd); + }else if (HasFocus && printedCurrentLine >= 0){ + gr.LineWidth = 1.0; + double cursorX = cb.X + (getTabulatedColumn(buffer.CurrentPosition) - ScrollX) * fe.MaxXAdvance + leftMargin; + gr.MoveTo (0.5 + cursorX, cb.Y + (printedCurrentLine) * (fe.Ascent+fe.Descent)); + gr.LineTo (0.5 + cursorX, cb.Y + (printedCurrentLine + 1) * (fe.Ascent+fe.Descent)); + gr.Stroke(); + } + #endregion + + if (PrintedLines?.Count > 0) { + int unfoldedLines = buffer.UnfoldedLines; + currentNode = null; + CodeLine cl = PrintedLines[0]; + int idx0 = buffer.IndexOf(cl); + int li = idx0-1; + while (li >= 0) { + if (buffer [li].IsFoldable && !buffer [li].IsFolded) { + if (buffer.IndexOf(buffer [li].SyntacticNode.EndLine) > idx0){ + currentNode = buffer [li].SyntacticNode; + break; + } + } + li--; + } + + for (int i = 0; i < visibleLines; i++) { + if (i + ScrollY >= unfoldedLines)//TODO:need optimize + break; + drawLine (gr, cb, i); + } + } + + editorMutex.ExitReadLock (); + + buffer.editMutex.ExitReadLock (); + + } + #endregion + + #region Mouse handling + + int hoverLine = -1; + public int HoverLine { + get { return hoverLine; } + set { + if (hoverLine == value) + return; + hoverLine = value; + NotifyValueChanged ("HoverLine", hoverLine); + NotifyValueChanged ("HoverError", buffer [hoverLine].exception); + } + } + void updateHoverLine () { + if (PrintedLines.Count > 0) { + int hvl = (int)Math.Max (0, Math.Floor (mouseLocalPos.Y / (fe.Ascent + fe.Descent))); + hvl = Math.Min (PrintedLines.Count - 1, hvl); + HoverLine = buffer.IndexOf (PrintedLines [hvl]); + } else + HoverLine = 0; + } + void updateCurrentPosFromMouseLocalPos(){ + PrintedCurrentLine = (int)Math.Max (0, Math.Floor (mouseLocalPos.Y / (fe.Ascent+fe.Descent))); + int curVisualCol = ScrollX + (int)Math.Round ((mouseLocalPos.X - leftMargin) / fe.MaxXAdvance); + + int i = 0; + int buffCol = 0; + while (i < curVisualCol && buffCol < buffer.CurrentCodeLine.Length) { + if (buffer.CurrentCodeLine[buffCol] == '\t') + i += Interface.TAB_SIZE; + else + i++; + buffCol++; + } + buffer.CurrentColumn = buffCol; + } + public override void onMouseEnter (object sender, MouseMoveEventArgs e) + { + base.onMouseEnter (sender, e); + if (e.X - ScreenCoordinates(Slot).X < leftMargin + ClientRectangle.X) + IFace.MouseCursor = MouseCursor.arrow; + else + IFace.MouseCursor = MouseCursor.ibeam; + } + public override void onMouseLeave (object sender, MouseMoveEventArgs e) + { + base.onMouseLeave (sender, e); + IFace.MouseCursor = MouseCursor.arrow; + } + public override void onMouseMove (object sender, MouseMoveEventArgs e) + { + base.onMouseMove (sender, e); + + mouseLocalPos = e.Position - ScreenCoordinates(Slot).TopLeft - ClientRectangle.TopLeft; + + updateHoverLine (); + + if (!IFace.IsDown (MouseButton.Left)) { + if (mouseLocalPos.X < leftMargin) + IFace.MouseCursor = MouseCursor.arrow; + else + IFace.MouseCursor = MouseCursor.ibeam; + return; + } + + if (!HasFocus || !buffer.SelectionInProgress) + return; + + //mouse is down + updateCurrentPosFromMouseLocalPos(); + buffer.SetSelEndPos (); + } + public override void onMouseDown (object sender, MouseButtonEventArgs e) + { + if (!this.Focusable) + return; + + if (mouseLocalPos.X >= leftMargin) + base.onMouseDown (sender, e); + + if (doubleClicked) { + doubleClicked = false; + return; + } + + if (mouseLocalPos.X < leftMargin) { + toogleFolding (hoverLine); + //toogleFolding (buffer.IndexOf (PrintedLines [(int)Math.Max (0, Math.Floor (mouseLocalPos.Y / (fe.Ascent+fe.Descent)))])); + return; + } + + updateCurrentPosFromMouseLocalPos (); + buffer.SetSelStartPos (); + } + public override void onMouseUp (object sender, MouseButtonEventArgs e) + { + base.onMouseUp (sender, e); + + if (buffer.SelectionIsEmpty) + buffer.ResetSelection (); + } + + public override void onMouseDoubleClick (object sender, MouseButtonEventArgs e) + { + doubleClicked = true; + base.onMouseDoubleClick (sender, e); + + buffer.GotoWordStart (); + buffer.SetSelStartPos (); + buffer.GotoWordEnd (); + buffer.SetSelEndPos (); + } + public void MakeSelection (int lineStart, int colStart, int lineEnd, int colEnd) { + buffer.CurrentLine = lineStart; + buffer.CurrentColumn = colStart; + buffer.SetSelStartPos (); + buffer.CurrentLine = lineEnd; + buffer.CurrentColumn = colEnd; + buffer.SetSelEndPos (); + } + public override void onMouseWheel (object sender, MouseWheelEventArgs e) + { + base.onMouseWheel (sender, e); + } + #endregion + + #region Keyboard handling + public override void onKeyDown (object sender, KeyEventArgs e) + { + //base.onKeyDown (sender, e); + + Key key = e.Key; + /* + if (IFace.Ctrl) { + switch (key) { + case Key.S: + projFile.Save (); + break; + case Key.Z: + editorMutex.EnterWriteLock (); + if (IFace.Shift) + projFile.Redo (null); + else + projFile.Undo (null); + editorMutex.ExitWriteLock (); + break; + default: + Console.WriteLine (""); + break; + } + } + */ + switch (key) + { + case Key.Backspace: + buffer.DeleteChar (); + break; + case Key.Delete: + if (buffer.SelectionIsEmpty) + MoveRight (); + else if (IFace.Shift) + IFace.Clipboard = buffer.SelectedText; + buffer.DeleteChar (); + break; + case Key.Enter: + case Key.KeypadEnter: + if (!buffer.SelectionIsEmpty) + buffer.DeleteChar (); + buffer.InsertLineBreak (); + break; + case Key.Escape: + buffer.ResetSelection (); + break; + case Key.Home: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (IFace.Ctrl) + buffer.CurrentLine = 0; + buffer.CurrentColumn = 0; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (IFace.Ctrl) + buffer.CurrentLine = 0; + buffer.CurrentColumn = 0; + break; + case Key.End: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (IFace.Ctrl) + buffer.CurrentLine = int.MaxValue; + buffer.CurrentColumn = int.MaxValue; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (IFace.Ctrl) + buffer.CurrentLine = int.MaxValue; + buffer.CurrentColumn = int.MaxValue; + break; + case Key.Insert: + if (IFace.Shift) + buffer.Insert (IFace.Clipboard); + else if (IFace.Ctrl && !buffer.SelectionIsEmpty) + IFace.Clipboard = buffer.SelectedText; + break; + case Key.Left: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (IFace.Ctrl) + buffer.GotoWordStart (); + else + MoveLeft (); + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (IFace.Ctrl) + buffer.GotoWordStart (); + else + MoveLeft(); + break; + case Key.Right: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + if (IFace.Ctrl) + buffer.GotoWordEnd (); + else + MoveRight (); + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + if (IFace.Ctrl) + buffer.GotoWordEnd (); + else + MoveRight (); + break; + case Key.Up: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + PrintedCurrentLine--; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + PrintedCurrentLine--; + break; + case Key.Down: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + PrintedCurrentLine++; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + PrintedCurrentLine++; + break; + case Key.Menu: + break; + case Key.PageDown: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + PrintedCurrentLine += visibleLines; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + PrintedCurrentLine += visibleLines; + break; + case Key.PageUp: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty) + buffer.SetSelStartPos (); + PrintedCurrentLine -= visibleLines; + buffer.SetSelEndPos (); + break; + } + buffer.ResetSelection (); + PrintedCurrentLine -= visibleLines; + break; + case Key.Tab: + if (IFace.Shift) { + if (buffer.SelectionIsEmpty || + (buffer.SelectionStart.Y == buffer.SelectionEnd.Y)) { + //TODO + break; + } + for (int i = buffer.SelectionStart.Y; i <= buffer.SelectionEnd.Y; i++) + buffer.RemoveLeadingTab (i); + buffer.SetSelectionOnFullLines (); + } else { + if (buffer.SelectionIsEmpty || + (buffer.SelectionStart.Y == buffer.SelectionEnd.Y)) { + buffer.Insert ("\t"); + break; + } + for (int i = buffer.SelectionStart.Y; i <= buffer.SelectionEnd.Y; i++) { + buffer.UpdateLine (i, "\t" + buffer [i].Content); + } + } + + break; + case Key.F8: + toogleFolding (buffer.CurrentLine); + break; + default: + break; + } + RegisterForGraphicUpdate(); + } + public override void onKeyPress (object sender, KeyPressEventArgs e) + { + base.onKeyPress (sender, e); + + buffer.Insert (e.KeyChar.ToString()); + buffer.ResetSelection (); + } + #endregion + } +} \ No newline at end of file diff --git a/Crow.Coding/ui/CategoryExp.template b/Crow.Coding/ui/CategoryExp.template new file mode 100755 index 0000000..f546492 --- /dev/null +++ b/Crow.Coding/ui/CategoryExp.template @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file diff --git a/Crow.Coding/ui/ContextMenu.template b/Crow.Coding/ui/ContextMenu.template new file mode 100644 index 0000000..38e6b7b --- /dev/null +++ b/Crow.Coding/ui/ContextMenu.template @@ -0,0 +1,42 @@ + + + + + + + + + + diff --git a/Crow.Coding/ui/IDE.style b/Crow.Coding/ui/IDE.style new file mode 100644 index 0000000..7fbe631 --- /dev/null +++ b/Crow.Coding/ui/IDE.style @@ -0,0 +1,29 @@ +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"; +} + +MenuItem { + Template = "#Crow.Coding.ui.MenuItem.template"; +} \ No newline at end of file diff --git a/Crow.Coding/ui/IcoBut.template b/Crow.Coding/ui/IcoBut.template new file mode 100644 index 0000000..a9fc28a --- /dev/null +++ b/Crow.Coding/ui/IcoBut.template @@ -0,0 +1,7 @@ + + + + diff --git a/Crow.Coding/ui/ItemTemplates/Enum.template b/Crow.Coding/ui/ItemTemplates/Enum.template new file mode 100755 index 0000000..efd9e43 --- /dev/null +++ b/Crow.Coding/ui/ItemTemplates/Enum.template @@ -0,0 +1,47 @@ + + + diff --git a/Crow.Coding/ui/ItemTemplates/Fill.template b/Crow.Coding/ui/ItemTemplates/Fill.template new file mode 100755 index 0000000..56d4a4d --- /dev/null +++ b/Crow.Coding/ui/ItemTemplates/Fill.template @@ -0,0 +1,19 @@ + + + \ No newline at end of file diff --git a/Crow.Coding/ui/MenuItem.template b/Crow.Coding/ui/MenuItem.template new file mode 100644 index 0000000..2fc2936 --- /dev/null +++ b/Crow.Coding/ui/MenuItem.template @@ -0,0 +1,29 @@ + + + + + + + diff --git a/Crow.Coding/ui/icons/basic_floppydisk.svg b/Crow.Coding/ui/icons/basic_floppydisk.svg new file mode 100644 index 0000000..55d901d --- /dev/null +++ b/Crow.Coding/ui/icons/basic_floppydisk.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + diff --git a/Crow.Coding/ui/icons/blank-file.svg b/Crow.Coding/ui/icons/blank-file.svg new file mode 100644 index 0000000..8136979 --- /dev/null +++ b/Crow.Coding/ui/icons/blank-file.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/ui/icons/center-align.svg b/Crow.Coding/ui/icons/center-align.svg new file mode 100644 index 0000000..92e3fac --- /dev/null +++ b/Crow.Coding/ui/icons/center-align.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Crow.Coding/ui/icons/cogwheel.svg b/Crow.Coding/ui/icons/cogwheel.svg new file mode 100644 index 0000000..c104c47 --- /dev/null +++ b/Crow.Coding/ui/icons/cogwheel.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/ui/icons/edit.svg b/Crow.Coding/ui/icons/edit.svg new file mode 100644 index 0000000..73569d8 --- /dev/null +++ b/Crow.Coding/ui/icons/edit.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/ui/icons/file-code.svg b/Crow.Coding/ui/icons/file-code.svg new file mode 100644 index 0000000..2dc00db --- /dev/null +++ b/Crow.Coding/ui/icons/file-code.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Crow.Coding/ui/icons/folder.svg b/Crow.Coding/ui/icons/folder.svg new file mode 100644 index 0000000..ee1f82b --- /dev/null +++ b/Crow.Coding/ui/icons/folder.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/ui/icons/font-file.svg b/Crow.Coding/ui/icons/font-file.svg new file mode 100644 index 0000000..20beac1 --- /dev/null +++ b/Crow.Coding/ui/icons/font-file.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/ui/icons/light-bulb.svg b/Crow.Coding/ui/icons/light-bulb.svg new file mode 100644 index 0000000..4193a75 --- /dev/null +++ b/Crow.Coding/ui/icons/light-bulb.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/ui/icons/paragraph.svg b/Crow.Coding/ui/icons/paragraph.svg new file mode 100644 index 0000000..826aa63 --- /dev/null +++ b/Crow.Coding/ui/icons/paragraph.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/ui/icons/previous.svg b/Crow.Coding/ui/icons/previous.svg new file mode 100644 index 0000000..566c8a3 --- /dev/null +++ b/Crow.Coding/ui/icons/previous.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/ui/icons/question.svg b/Crow.Coding/ui/icons/question.svg new file mode 100644 index 0000000..fb8e3d3 --- /dev/null +++ b/Crow.Coding/ui/icons/question.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/ui/icons/reply.svg b/Crow.Coding/ui/icons/reply.svg new file mode 100644 index 0000000..d008cb3 --- /dev/null +++ b/Crow.Coding/ui/icons/reply.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/ui/icons/search.svg b/Crow.Coding/ui/icons/search.svg new file mode 100644 index 0000000..4a931b3 --- /dev/null +++ b/Crow.Coding/ui/icons/search.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/ui/icons/share-arrow.svg b/Crow.Coding/ui/icons/share-arrow.svg new file mode 100644 index 0000000..e0eb246 --- /dev/null +++ b/Crow.Coding/ui/icons/share-arrow.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Crow.Coding/ui/icons/sign-out.svg b/Crow.Coding/ui/icons/sign-out.svg new file mode 100644 index 0000000..c5951fc --- /dev/null +++ b/Crow.Coding/ui/icons/sign-out.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/ui/icons/text-file.svg b/Crow.Coding/ui/icons/text-file.svg new file mode 100644 index 0000000..eafca90 --- /dev/null +++ b/Crow.Coding/ui/icons/text-file.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Crow.Coding/ui/icons/text-label.svg b/Crow.Coding/ui/icons/text-label.svg new file mode 100644 index 0000000..8fa9196 --- /dev/null +++ b/Crow.Coding/ui/icons/text-label.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/ui/icons/tools.svg b/Crow.Coding/ui/icons/tools.svg new file mode 100644 index 0000000..5326f19 --- /dev/null +++ b/Crow.Coding/ui/icons/tools.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Crow.Coding/ui/icons/zoom-in.svg b/Crow.Coding/ui/icons/zoom-in.svg new file mode 100644 index 0000000..60c41d1 --- /dev/null +++ b/Crow.Coding/ui/icons/zoom-in.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Crow.Coding/ui/icons/zoom-out.svg b/Crow.Coding/ui/icons/zoom-out.svg new file mode 100644 index 0000000..bd4eec3 --- /dev/null +++ b/Crow.Coding/ui/icons/zoom-out.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CrowEdit.csproj b/CrowEdit.csproj index f2dad2e..065e4ff 100644 --- a/CrowEdit.csproj +++ b/CrowEdit.csproj @@ -1,125 +1,21 @@ - - - - Debug - AnyCPU - {AAA67D93-458E-4DD7-9CDA-4EC7F73D47FF} - Exe - CrowEdit - CrowEdit - v4.5 - CrowEdit.CrowEdit - $(SolutionDir)build/$(Configuration) - $(SolutionDir)build/obj/$(Configuration) - - - - - - - - true - full - false - prompt - 4 - false - DEBUG;TRACE - $(SolutionDir)build\obj\$(Configuration) - $(SolutionDir)build\$(Configuration) - - - none - true - 0 - false - $(SolutionDir)build\obj\$(Configuration) - $(SolutionDir)build\$(Configuration) - - - Program - %24{TargetName} - %24{SolutionDir}\build\%24{ProjectConfigName} - false - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - - - - - - packages\OpenTK.2.0.0\lib\net20\OpenTK.dll - - - packages\Crow.OpenTK.0.6.0\lib\net45\Crow.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Crow.MenuItem.template - - - - - - - - - - - - - - - + + + net472 + Exe + false + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CrowEdit.sln b/CrowEdit.sln index 6d85afc..2c7ab77 100644 --- a/CrowEdit.sln +++ b/CrowEdit.sln @@ -3,15 +3,27 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2012 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrowEdit", "CrowEdit.csproj", "{AAA67D93-458E-4DD7-9CDA-4EC7F73D47FF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crow.Coding", "Crow.Coding\Crow.Coding.csproj", "{78842EE4-8A2F-4C75-AEC6-C95F15AD3994}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crow", "..\crow\Crow\Crow.csproj", "{0D18388D-8DDB-43DE-A608-0C65F0AEC4E0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Release|Any CPU = Release|Any CPU Debug|Any CPU = Debug|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {78842EE4-8A2F-4C75-AEC6-C95F15AD3994}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {78842EE4-8A2F-4C75-AEC6-C95F15AD3994}.Debug|Any CPU.Build.0 = Debug|Any CPU + {78842EE4-8A2F-4C75-AEC6-C95F15AD3994}.Release|Any CPU.ActiveCfg = Release|Any CPU + {78842EE4-8A2F-4C75-AEC6-C95F15AD3994}.Release|Any CPU.Build.0 = Release|Any CPU {AAA67D93-458E-4DD7-9CDA-4EC7F73D47FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AAA67D93-458E-4DD7-9CDA-4EC7F73D47FF}.Debug|Any CPU.Build.0 = Debug|Any CPU {AAA67D93-458E-4DD7-9CDA-4EC7F73D47FF}.Release|Any CPU.ActiveCfg = Release|Any CPU {AAA67D93-458E-4DD7-9CDA-4EC7F73D47FF}.Release|Any CPU.Build.0 = Release|Any CPU + {0D18388D-8DDB-43DE-A608-0C65F0AEC4E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D18388D-8DDB-43DE-A608-0C65F0AEC4E0}.Release|Any CPU.Build.0 = Release|Any CPU + {0D18388D-8DDB-43DE-A608-0C65F0AEC4E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D18388D-8DDB-43DE-A608-0C65F0AEC4E0}.Debug|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection EndGlobal diff --git a/CrowWindow.cs b/CrowWindow.cs index 9a0c4df..f1d644e 100644 --- a/CrowWindow.cs +++ b/CrowWindow.cs @@ -23,7 +23,6 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - using System; using System.Threading; using OpenTK; diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..92af439 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,9 @@ + + + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\obj\$(Configuration)\ + MIT + Jean-Philippe Bruyère + 7.2 + + diff --git a/OpenGL/Extensions.cs b/OpenGL/Extensions.cs deleted file mode 100644 index 7b26f76..0000000 --- a/OpenGL/Extensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -// -// Extensions.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2017 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -using System; -using OpenTK; -using Crow; - -namespace Crow -{ - public static partial class Extensions { - public static Vector4 ToVector4(this Color c){ - float[] f = c.floatArray; - return new Vector4 (f [0], f [1], f [2], f [3]); - } - public static Vector3 Transform(this Vector3 v, Matrix4 m){ - return Vector4.Transform(new Vector4(v, 1), m).Xyz; - } - public static bool IsInBetween(this int v, int min, int max){ - return v >= min & v <= max; - } - - } -} - diff --git a/OpenGL/Shader.cs b/OpenGL/Shader.cs deleted file mode 100644 index f91a982..0000000 --- a/OpenGL/Shader.cs +++ /dev/null @@ -1,336 +0,0 @@ -// -// Shader.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2016 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -using System; -using System.Diagnostics; -using System.IO; -using OpenTK; -using OpenTK.Graphics.OpenGL; - -namespace Crow -{ - public class Shader : IDisposable - { - #region CTOR - public Shader () - { - Init (); - } - public Shader (string vertResPath, string fragResPath = null, string geomResPath = null) - { - VertSourcePath = vertResPath; - FragSourcePath = fragResPath; - GeomSourcePath = geomResPath; - - loadSourcesFiles (); - - Init (); - } - #endregion - - public string VertSourcePath, - FragSourcePath, - GeomSourcePath; - #region Sources - protected string _vertSource = @" - #version 300 es - precision lowp float; - - uniform mat4 mvp; - - layout(location = 0) in vec3 in_position; - layout(location = 1) in vec2 in_tex; - - out vec2 texCoord; - - void main(void) - { - texCoord = in_tex; - gl_Position = mvp * vec4(in_position, 1.0); - }"; - - protected string _fragSource = @" - #version 300 es - precision lowp float; - - uniform sampler2D tex; - - in vec2 texCoord; - out vec4 out_frag_color; - - void main(void) - { - out_frag_color = texture( tex, texCoord);//vec4(1,0,0,1); - }"; - string _geomSource = @""; -// #version 330 -// layout(triangles) in; -// layout(triangle_strip, max_vertices=3) out; -// void main() -// { -// for(int i=0; i<3; i++) -// { -// gl_Position = gl_in[i].gl_Position; -// EmitVertex(); -// } -// EndPrimitive(); -// }"; - #endregion - - #region Private and protected fields - public int vsId, fsId, gsId, pgmId, mvpLocation; - - Matrix4 mvp = Matrix4.Identity; - #endregion - - - #region Public properties - public virtual string vertSource - { - get { return _vertSource;} - set { _vertSource = value; } - } - public virtual string fragSource - { - get { return _fragSource;} - set { _fragSource = value; } - } - public virtual string geomSource - { - get { return _geomSource; } - set { _geomSource = value; } - } - - public virtual Matrix4 MVP{ - set { mvp = value; } - get { return mvp; } - } - #endregion - - #region Public functions - /// - /// configure sources and compile - /// - public virtual void Init() - { - Compile (); - } - public void Reload(){ - loadSourcesFiles (); - Compile (); - } - public void SetSource(ShaderType shaderType, string _source){ - switch (shaderType) { - case ShaderType.FragmentShader: - fragSource = _source; - return; - case ShaderType.VertexShader: - vertSource = _source; - return; - case ShaderType.GeometryShader: - geomSource = _source; - return; - } - } - public string GetSource(ShaderType shaderType){ - switch (shaderType) { - case ShaderType.FragmentShader: - return fragSource; - case ShaderType.VertexShader: - return vertSource; - case ShaderType.GeometryShader: - return geomSource; - } - return ""; - } - public string GetSourcePath(ShaderType shaderType){ - switch (shaderType) { - case ShaderType.FragmentShader: - return FragSourcePath; - case ShaderType.VertexShader: - return VertSourcePath; - case ShaderType.GeometryShader: - return GeomSourcePath; - } - return ""; - } - public virtual void Compile() - { - Dispose (); - - pgmId = GL.CreateProgram(); - - if (!string.IsNullOrEmpty(vertSource)) - { - vsId = GL.CreateShader(ShaderType.VertexShader); - compileShader(vsId, vertSource); - } - if (!string.IsNullOrEmpty(fragSource)) - { - fsId = GL.CreateShader(ShaderType.FragmentShader); - compileShader(fsId, fragSource); - - } - if (!string.IsNullOrEmpty(geomSource)) - { - gsId = GL.CreateShader(ShaderType.GeometryShader); - compileShader(gsId,geomSource); - } - - if (vsId != 0) - GL.AttachShader(pgmId, vsId); - if (fsId != 0) - GL.AttachShader(pgmId, fsId); - if (gsId != 0) - GL.AttachShader(pgmId, gsId); - - BindVertexAttributes (); - - string info; - GL.LinkProgram(pgmId); - GL.GetProgramInfoLog(pgmId, out info); - - if (!string.IsNullOrEmpty (info)) { - Debug.WriteLine ("Linkage:"); - Debug.WriteLine (info); - } - - info = null; - - GL.ValidateProgram(pgmId); - GL.GetProgramInfoLog(pgmId, out info); - if (!string.IsNullOrEmpty (info)) { - Debug.WriteLine ("Validation:"); - Debug.WriteLine (info); - } - - GL.UseProgram (pgmId); - - GetUniformLocations (); - BindSamplesSlots (); - - Disable (); - } - - protected virtual void BindVertexAttributes() - { - GL.BindAttribLocation(pgmId, 0, "in_position"); - GL.BindAttribLocation(pgmId, 1, "in_tex"); - } - protected virtual void GetUniformLocations() - { - mvpLocation = GL.GetUniformLocation(pgmId, "mvp"); - } - protected virtual void BindSamplesSlots(){ - GL.Uniform1(GL.GetUniformLocation (pgmId, "tex"), 0); - } - public void SetMVP(Matrix4 _mvp){ - GL.UniformMatrix4(mvpLocation, false, ref _mvp); - } - public virtual void Enable(){ - GL.UseProgram (pgmId); - } - public virtual void Disable(){ - GL.UseProgram (0); - } - public static void Enable(Shader s) - { - if (s == null) - return; - s.Enable (); - } - public static void Disable(Shader s) - { - if (s == null) - return; - s.Disable (); - } - #endregion - - void loadSourcesFiles(){ - Stream s; - - if (!string.IsNullOrEmpty (VertSourcePath)) { - s = Crow.Interface.GetStreamFromPath (VertSourcePath); - if (s != null) { - using (StreamReader sr = new StreamReader (s)) { - vertSource = sr.ReadToEnd (); - } - } - } - - if (!string.IsNullOrEmpty (FragSourcePath)) { - s = Crow.Interface.GetStreamFromPath (FragSourcePath); - if (s != null) { - using (StreamReader sr = new StreamReader (s)) { - fragSource = sr.ReadToEnd (); - } - } - } - - if (!string.IsNullOrEmpty (GeomSourcePath)) { - s = Crow.Interface.GetStreamFromPath (GeomSourcePath); - if (s != null) { - using (StreamReader sr = new StreamReader (s)) { - geomSource = sr.ReadToEnd (); - } - } - } - } - void compileShader(int shader, string source) - { - GL.ShaderSource(shader, source); - GL.CompileShader(shader); - - string info; - GL.GetShaderInfoLog(shader, out info); - Debug.WriteLine(info); - - int compileResult; - GL.GetShader(shader, ShaderParameter.CompileStatus, out compileResult); - if (compileResult != 1) - { - Debug.WriteLine("Compile Error!"); - Debug.WriteLine(source); - } - } - public override string ToString () - { - return string.Format ("{0} {1} {2}", VertSourcePath, FragSourcePath, GeomSourcePath); - } - - #region IDisposable implementation - public virtual void Dispose () - { - if (GL.IsProgram (pgmId)) - GL.DeleteProgram (pgmId); - - if (GL.IsShader (vsId)) - GL.DeleteShader (vsId); - if (GL.IsShader (fsId)) - GL.DeleteShader (fsId); - if (GL.IsShader (gsId)) - GL.DeleteShader (gsId); - } - #endregion - } -} - diff --git a/OpenGL/Texture.cs b/OpenGL/Texture.cs deleted file mode 100644 index ce24808..0000000 --- a/OpenGL/Texture.cs +++ /dev/null @@ -1,94 +0,0 @@ -// -// Texture.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2016 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -using System; -using System.Drawing; -using OpenTK.Graphics.OpenGL; -using System.Drawing.Imaging; -using System.IO; -using System.Diagnostics; - -namespace Crow -{ - public class Texture - { - public string Map; - public int texRef; - public int Width; - public int Height; - - public Texture(string _mapPath, bool flipY = true) - { - using (Stream s = Interface.GetStreamFromPath (_mapPath)) { - - try { - Map = _mapPath; - - Bitmap bitmap = new Bitmap (s); - - if (flipY) - bitmap.RotateFlip (RotateFlipType.RotateNoneFlipY); - - BitmapData data = bitmap.LockBits (new System.Drawing.Rectangle (0, 0, bitmap.Width, bitmap.Height), - ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - - createTexture (data.Scan0, data.Width, data.Height); - - bitmap.UnlockBits (data); - - GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear); - GL.TexParameter (TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); - - GL.GenerateMipmap (GenerateMipmapTarget.Texture2D); - - } catch (Exception ex) { - Debug.WriteLine ("Error loading texture: " + Map + ":" + ex.Message); - } - } - } - - public Texture(int width, int height) - { - createTexture (IntPtr.Zero, width, height); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Clamp); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Clamp); - } - - void createTexture(IntPtr data, int width, int height) - { - Width = width; - Height = height; - - GL.GenTextures(1, out texRef); - GL.BindTexture(TextureTarget.Texture2D, texRef); - GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0, - OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data); - } - - public static implicit operator int(Texture t) - { - return t == null ? 0: t.texRef; - } - } - -} diff --git a/OpenGL/vaoMesh.cs b/OpenGL/vaoMesh.cs deleted file mode 100644 index 19acd73..0000000 --- a/OpenGL/vaoMesh.cs +++ /dev/null @@ -1,221 +0,0 @@ -// -// vaoMesh.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2016 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -using System; -using OpenTK; -using OpenTK.Graphics.OpenGL; - -namespace Crow -{ - public class vaoMesh : IDisposable - { - public int vaoHandle, - positionVboHandle, - texVboHandle, - eboHandle; - - public Vector3[] positions; - public Vector2[] texCoords; - public int[] indices; - - public vaoMesh() - { - } - - public vaoMesh (Vector3[] _positions, Vector2[] _texCoord, int[] _indices) - { - positions = _positions; - texCoords = _texCoord; - indices = _indices; - - CreateBuffers (); - } - - public vaoMesh (float x, float y, float z, float width, float height, float TileX = 1f, float TileY = 1f) - { - positions = - new Vector3[] { - new Vector3 (x - width / 2, y + height / 2, z), - new Vector3 (x - width / 2, y - height / 2, z), - new Vector3 (x + width / 2, y + height / 2, z), - new Vector3 (x + width / 2, y - height / 2, z) - }; - texCoords = new Vector2[] { - new Vector2 (0, TileY), - new Vector2 (0, 0), - new Vector2 (TileX, TileY), - new Vector2 (TileX, 0) - }; - indices = new int[] { 0, 1, 2, 3 }; - - CreateBuffers (); - } - public static vaoMesh CreateCube(){ - vaoMesh tmp = new vaoMesh (); - tmp.positions = new Vector3[] - { - new Vector3(-1.0f, -1.0f, -1.0f), - new Vector3( -1.0f, -1.0f, 1.0f), - new Vector3( 1.0f, -1.0f, -1.0f), - new Vector3(1.0f, -1.0f, 1.0f), - new Vector3(1.0f, 1.0f, -1.0f), - new Vector3( 1.0f, 1.0f, 1.0f), - new Vector3( -1.0f, 1.0f, -1.0f), - new Vector3(-1.0f, 1.0f, 1.0f) - }; - tmp.indices = new int[] - { - // front face - 0, 2, 1, 1, 2, 3, - // top face - 2, 4, 3, 3, 4, 5, - // back face - 4, 6, 5, 5, 6, 7, - // left face - 6, 0, 7, 7, 0, 1, - // bottom face - 1, 3, 7, 7, 3, 5, - // right face -// 1, 5, 6, 6, 2, 1, - }; - tmp.texCoords = new Vector2[] - { - new Vector2(0, 0), - new Vector2(0, 1), - new Vector2(1, 0), - new Vector2(1, 1), - new Vector2(0, 0), - new Vector2(0, 1), - new Vector2(1, 0), - new Vector2(1, 1), - }; - tmp.CreateBuffers (); - return tmp; -// Normals = new Vector3[] -// { -// new Vector3(-1.0f, -1.0f, 1.0f), -// new Vector3( 1.0f, -1.0f, 1.0f), -// new Vector3( 1.0f, 1.0f, 1.0f), -// new Vector3(-1.0f, 1.0f, 1.0f), -// new Vector3(-1.0f, -1.0f, -1.0f), -// new Vector3( 1.0f, -1.0f, -1.0f), -// new Vector3( 1.0f, 1.0f, -1.0f), -// new Vector3(-1.0f, 1.0f, -1.0f), -// }; -// -// Colors = new int[] -// { -// Utilities.ColorToRgba32(Color.DarkRed), -// Utilities.ColorToRgba32(Color.DarkRed), -// Utilities.ColorToRgba32(Color.Gold), -// Utilities.ColorToRgba32(Color.Gold), -// Utilities.ColorToRgba32(Color.DarkRed), -// Utilities.ColorToRgba32(Color.DarkRed), -// Utilities.ColorToRgba32(Color.Gold), -// Utilities.ColorToRgba32(Color.Gold), -// }; - } - public void CreateBuffers(){ - CreateVBOs (); - CreateVAOs (); - } - protected void CreateVBOs() - { - positionVboHandle = GL.GenBuffer(); - GL.BindBuffer(BufferTarget.ArrayBuffer, positionVboHandle); - GL.BufferData(BufferTarget.ArrayBuffer, - new IntPtr(positions.Length * Vector3.SizeInBytes), - positions, BufferUsageHint.StaticDraw); - - if (texCoords != null) { - texVboHandle = GL.GenBuffer (); - GL.BindBuffer (BufferTarget.ArrayBuffer, texVboHandle); - GL.BufferData (BufferTarget.ArrayBuffer, - new IntPtr (texCoords.Length * Vector2.SizeInBytes), - texCoords, BufferUsageHint.StaticDraw); - } - - GL.BindBuffer(BufferTarget.ArrayBuffer, 0); - - if (indices != null) { - eboHandle = GL.GenBuffer (); - GL.BindBuffer (BufferTarget.ElementArrayBuffer, eboHandle); - GL.BufferData (BufferTarget.ElementArrayBuffer, - new IntPtr (sizeof(uint) * indices.Length), - indices, BufferUsageHint.StaticDraw); - GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0); - } - } - protected void CreateVAOs() - { - vaoHandle = GL.GenVertexArray(); - GL.BindVertexArray(vaoHandle); - - GL.EnableVertexAttribArray(0); - GL.BindBuffer(BufferTarget.ArrayBuffer, positionVboHandle); - GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, true, Vector3.SizeInBytes, 0); - - if (texCoords != null) { - GL.EnableVertexAttribArray (1); - GL.BindBuffer (BufferTarget.ArrayBuffer, texVboHandle); - GL.VertexAttribPointer (1, 2, VertexAttribPointerType.Float, true, Vector2.SizeInBytes, 0); - } - if (indices != null) - GL.BindBuffer(BufferTarget.ElementArrayBuffer, eboHandle); - - GL.BindVertexArray(0); - } - - public void Render(BeginMode _primitiveType){ - GL.BindVertexArray(vaoHandle); - if (indices == null) - GL.DrawArrays (_primitiveType, 0, positions.Length); - else - GL.DrawElements(_primitiveType, indices.Length, - DrawElementsType.UnsignedInt, IntPtr.Zero); - GL.BindVertexArray (0); - } - public void Render(BeginMode _primitiveType, int[] _customIndices){ - GL.BindVertexArray(vaoHandle); - GL.DrawElements(_primitiveType, _customIndices.Length, - DrawElementsType.UnsignedInt, _customIndices); - GL.BindVertexArray (0); - } - public void Render(BeginMode _primitiveType, int instances){ - - GL.BindVertexArray(vaoHandle); - GL.DrawElementsInstanced(_primitiveType, indices.Length, - DrawElementsType.UnsignedInt, IntPtr.Zero, instances); - GL.BindVertexArray (0); - } - - #region IDisposable implementation - public void Dispose () - { - GL.DeleteBuffer (positionVboHandle); - GL.DeleteBuffer (texVboHandle); - GL.DeleteBuffer (eboHandle); - GL.DeleteVertexArray (vaoHandle); - } - #endregion - - } -} \ No newline at end of file diff --git a/ParsingException.cs b/ParsingException.cs deleted file mode 100644 index 88c51c9..0000000 --- a/ParsingException.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; - -namespace Crow.Coding -{ - public class ParsingException : Exception - { - public int Line; - public int Column; - public ParsingException(Parser parser, string txt) - : base(string.Format("Parser exception ({0},{1}): {2}", parser.currentLine, parser.currentColumn, txt)) - { - Line = parser.currentLine; - Column = parser.currentColumn; - } - public ParsingException(Parser parser, string txt, Exception innerException) - : base(txt, innerException) - { - txt = string.Format("Parser exception ({0},{1}): {2}", parser.currentLine, parser.currentColumn, txt); - } - } -} - diff --git a/src/CSharpParser.cs b/src/CSharpParser.cs deleted file mode 100644 index 7d2e28e..0000000 --- a/src/CSharpParser.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using Crow; - -namespace Crow.Coding -{ - public class CSharpParser : Parser - { - public new enum TokenType { - Unknown = Parser.TokenType.Unknown, - WhiteSpace = Parser.TokenType.WhiteSpace, - LineComment = Parser.TokenType.LineComment, - BlockComment = Parser.TokenType.BlockComment, - OpenParenth, - CloseParenth, - OpenBlock, - CloseBlock, - StatementEnding, - UnaryOp, - BinaryOp, - Affectation, - StringLiteral, - CharacterLiteral, - DigitalLiteral, - Literal, - Identifier, - Indexer, - Type, - Preprocessor, - } - - public CSharpParser (CodeBuffer _buffer) : base(_buffer) - { - } - - public override void ParseCurrentLine () - { - throw new NotImplementedException (); - } - public override void SyntaxAnalysis () - { - throw new NotImplementedException (); - } - } -} - diff --git a/src/CodeBuffer.cs b/src/CodeBuffer.cs deleted file mode 100644 index 399d10a..0000000 --- a/src/CodeBuffer.cs +++ /dev/null @@ -1,454 +0,0 @@ -// -// CodeTextBuffer.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2017 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using System.Diagnostics; - -namespace Crow.Coding -{ - /// - /// Code buffer, lines are arranged in a List, new line chars are removed during string.split on '\n...', - /// - public class CodeBuffer - { - public object EditMutex = new object(); - //those events are handled in SourceEditor to help keeping sync between textbuffer,parser and editor. - //modified lines are marked for reparse - #region Events - public event EventHandler LineUpadateEvent; - public event EventHandler LineRemoveEvent; - public event EventHandler LineAdditionEvent; - public event EventHandler FoldingEvent; - public event EventHandler BufferCleared; - public event EventHandler SelectionChanged; - public event EventHandler PositionChanged; - #endregion - - #region CTOR - public CodeBuffer () { - - } - #endregion - - string lineBreak = Interface.LineBreak; - List lines = new List(); - public int longestLineIdx = 0; - public int longestLineCharCount = 0; - /// - /// real position in char arrays, tab = 1 char - /// - int _currentLine = 0; - int _currentCol = 0; - - public int LineCount { get { return lines.Count;}} - public int IndexOf (CodeLine cl) { - return lines.IndexOf (cl); - } - - public CodeLine this[int i] - { - get { return lines[i]; } - set { - if (lines [i] == value) - return; - lock (EditMutex) { - lines [i] = value; - LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); - } - } - } - - public void RemoveAt(int i){ - lock (EditMutex) { - lines.RemoveAt (i); - LineRemoveEvent.Raise (this, new CodeBufferEventArgs (i)); - } - } - public void Insert(int i, string item){ - lock (EditMutex) { - lines.Insert (i, item); - LineAdditionEvent.Raise (this, new CodeBufferEventArgs (i)); - } - } - public void Add(CodeLine item){ - lock (EditMutex) { - lines.Add (item); - LineAdditionEvent.Raise (this, new CodeBufferEventArgs (lines.Count - 1)); - } - } - public void AddRange (string[] items){ - int start = lines.Count; - lock (EditMutex) { - for (int i = 0; i < items.Length; i++) - lines.Add (items [i]); - LineAdditionEvent.Raise (this, new CodeBufferEventArgs (start, items.Length)); - } - } - public void AddRange (CodeLine[] items){ - int start = lines.Count; - lock (EditMutex) { - lines.AddRange (items); - LineAdditionEvent.Raise (this, new CodeBufferEventArgs (start, items.Length)); - } - } - public void Clear () { - lock (EditMutex) { - longestLineCharCount = 0; - lines.Clear (); - BufferCleared.Raise (this, null); - } - } - public void UpdateLine(int i, string newContent){ - lock (EditMutex) { - this [i].Content = newContent; - LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); - } - } - public void AppenedLine(int i, string newContent){ - lock (EditMutex) { - this [i].Content += newContent; - LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); - } - } - public void ToogleFolding (int line) { - if (!this [line].IsFoldable) - return; - lock (EditMutex) { - this [line].IsFolded = !this [line].IsFolded; - FoldingEvent.Raise (this, new CodeBufferEventArgs (line)); - } - } - public void Load(string rawSource) { - this.Clear(); - - if (string.IsNullOrEmpty (rawSource)) - return; - - AddRange (Regex.Split (rawSource, "\r\n|\r|\n|\\\\n")); - - lineBreak = detectLineBreakKind (rawSource); - } - - /// - /// Finds the longest visual line as printed on screen with tabulation replaced with n spaces - /// - public void FindLongestVisualLine(){ - longestLineCharCount = 0; - for (int i = 0; i < this.LineCount; i++) { - if (lines[i].PrintableLength > longestLineCharCount) { - longestLineCharCount = lines[i].PrintableLength; - longestLineIdx = i; - } - } - Debug.WriteLine ("Longest line: {0}->{1}", longestLineIdx, longestLineCharCount); - } - /// line break could be '\r' or '\n' or '\r\n' - static string detectLineBreakKind(string buffer){ - string strLB = ""; - - if (string.IsNullOrEmpty(buffer)) - return Interface.LineBreak; - int i = 0; - while ( i < buffer.Length) { - if (buffer [i] == '\r') { - strLB += '\r'; - i++; - } - if (i < buffer.Length) { - if (buffer [i] == '\r') - return "\r"; - if (buffer[i] == '\n') - strLB += '\n'; - } - if (!string.IsNullOrEmpty (strLB)) - return strLB; - i++; - } - return Interface.LineBreak; - } - /// - /// return all lines with linebreaks - /// - public string FullText{ - get { - if (lines.Count == 0) - return ""; - string tmp = ""; - for (int i = 0; i < lines.Count -1; i++) - tmp += lines [i].Content + this.lineBreak; - tmp += lines [lines.Count - 1].Content; - return tmp; - } - } - - /// - /// unfolded and not in folds line count - /// - public int UnfoldedLines { - get { - int i = 0, vl = 0; - while (i < LineCount) { - if (this [i].IsFolded) - i = GetEndNodeIndex (i); - i++; - vl++; - } - //Debug.WriteLine ("unfolded lines: " + vl); - return vl; - } - } - - /// - /// convert visual position to buffer position - /// - Point getBuffPos (Point visualPos) { - int i = 0; - int buffCol = 0; - while (i < visualPos.X) { - if (this [visualPos.Y] [buffCol] == '\t') - i += Interface.TabSize; - else - i++; - buffCol++; - } - return new Point (buffCol, visualPos.Y); - } - - public int GetEndNodeIndex (int line) { - return IndexOf (this [line].SyntacticNode.EndLine); - } - /// - /// Gets visual position computed from actual buffer position - /// -// public Point TabulatedPosition { -// get { return new Point (TabulatedColumn, _currentLine); } -// } - /// - /// set buffer current position from visual position - /// -// public void SetBufferPos(Point tabulatedPosition) { -// CurrentPosition = getBuffPos(tabulatedPosition); -// } - - #region Editing and moving cursor - Point selStartPos = -1; //selection start (row,column) - Point selEndPos = -1; //selection end (row,column) - - public bool SelectionInProgress { get { return selStartPos >= 0; }} - public void SetSelStartPos () { - selStartPos = selEndPos = CurrentPosition; - SelectionChanged.Raise (this, null); - } - public void SetSelEndPos () { - selEndPos = CurrentPosition; - SelectionChanged.Raise (this, null); - } - /// - /// Set selection in buffer to -1, empty selection - /// - public void ResetSelection () { - selStartPos = selEndPos = -1; - SelectionChanged.Raise (this, null); - } - - public string SelectedText { - get { - if (SelectionIsEmpty) - return ""; - Point selStart = SelectionStart; - Point selEnd = SelectionEnd; - if (selStart.Y == selEnd.Y) - return this [selStart.Y].Content.Substring (selStart.X, selEnd.X - selStart.X); - string tmp = ""; - tmp = this [selStart.Y].Content.Substring (selStart.X); - for (int l = selStart.Y + 1; l < selEnd.Y; l++) { - tmp += Interface.LineBreak + this [l].Content; - } - tmp += Interface.LineBreak + this [selEnd.Y].Content.Substring (0, selEnd.X); - return tmp; - } - } - /// - /// ordered selection start and end positions in char units - /// - public Point SelectionStart { - get { return selEndPos < 0 || selStartPos.Y < selEndPos.Y ? selStartPos : - selStartPos.Y > selEndPos.Y ? selEndPos : - selStartPos.X < selEndPos.X ? selStartPos : selEndPos; } - } - public Point SelectionEnd { - get { return selEndPos < 0 || selStartPos.Y > selEndPos.Y ? selStartPos : - selStartPos.Y < selEndPos.Y ? selEndPos : - selStartPos.X > selEndPos.X ? selStartPos : selEndPos; } - } - public bool SelectionIsEmpty - { get { return selEndPos == selStartPos; } } - /// - /// Current column in buffer coordinate, tabulation = 1 char - /// - public int CurrentColumn{ - get { return _currentCol; } - set { - if (value == _currentCol) - return; - if (value < 0) - _currentCol = 0; - else if (value > lines [_currentLine].Length) - _currentCol = lines [_currentLine].Length; - else - _currentCol = value; - - PositionChanged.Raise (this, null); - } - } - /// - /// Current row in buffer coordinate, tabulation = 1 char - /// - public int CurrentLine{ - get { return _currentLine; } - set { - if (value == _currentLine) - return; - if (value >= lines.Count) - _currentLine = lines.Count-1; - else if (value < 0) - _currentLine = 0; - else - _currentLine = value; - - if (_currentCol > lines [_currentLine].Length) - _currentCol = lines [_currentLine].Length; - Debug.WriteLine ("buff cur line: " + _currentLine); - PositionChanged.Raise (this, null); - } - } - public CodeLine CurrentCodeLine { - get { return this [_currentLine]; } - } - /// - /// Current position in buffer coordinate, tabulation = 1 char - /// - public Point CurrentPosition { - get { return new Point(CurrentColumn, CurrentLine); } -// set { -// _currentCol = value.X; -// _currentLine = value.Y; -// } - } - /// - /// get char at current position in buffer - /// - protected Char CurrentChar { get { return lines [CurrentLine] [CurrentColumn]; } } - - public void GotoWordStart(){ - if (this[CurrentLine].Length == 0) - return; - CurrentColumn--; - //skip white spaces - while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0) - CurrentColumn--; - while (char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn > 0) - CurrentColumn--; - if (!char.IsLetterOrDigit (this.CurrentChar)) - CurrentColumn++; - } - public void GotoWordEnd(){ - //skip white spaces - if (CurrentColumn >= this [CurrentLine].Length - 1) - return; - while (!char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < this [CurrentLine].Length-1) - CurrentColumn++; - while (char.IsLetterOrDigit (this.CurrentChar) && CurrentColumn < this [CurrentLine].Length-1) - CurrentColumn++; - if (char.IsLetterOrDigit (this.CurrentChar)) - CurrentColumn++; - } - public void DeleteChar() - { - lock (EditMutex) { - if (SelectionIsEmpty) { - if (CurrentColumn == 0) { - if (CurrentLine == 0) - return; - CurrentLine--; - CurrentColumn = this [CurrentLine].Length; - AppenedLine (CurrentLine, this [CurrentLine + 1].Content); - RemoveAt (CurrentLine + 1); - return; - } - CurrentColumn--; - UpdateLine (CurrentLine, this [CurrentLine].Content.Remove (CurrentColumn, 1)); - } else { - int linesToRemove = SelectionEnd.Y - SelectionStart.Y + 1; - int l = SelectionStart.Y; - - if (linesToRemove > 0) { - UpdateLine (l, this [l].Content.Remove (SelectionStart.X, this [l].Length - SelectionStart.X) + - this [SelectionEnd.Y].Content.Substring (SelectionEnd.X, this [SelectionEnd.Y].Length - SelectionEnd.X)); - l++; - for (int c = 0; c < linesToRemove - 1; c++) - RemoveAt (l); - CurrentLine = SelectionStart.Y; - CurrentColumn = SelectionStart.X; - } else - UpdateLine (l, this [l].Content.Remove (SelectionStart.X, SelectionEnd.X - SelectionStart.X)); - CurrentColumn = SelectionStart.X; - ResetSelection (); - } - } - } - /// - /// Insert new string at caret position, should be sure no line break is inside. - /// - /// String. - public void Insert(string str) - { - if (!SelectionIsEmpty) - this.DeleteChar (); - string[] strLines = Regex.Split (str, "\r\n|\r|\n|" + @"\\n").ToArray(); - UpdateLine (CurrentLine, this [CurrentLine].Content.Insert (CurrentColumn, strLines[0])); - CurrentColumn += strLines[0].Length; - for (int i = 1; i < strLines.Length; i++) { - InsertLineBreak (); - UpdateLine (CurrentLine, this [CurrentLine].Content.Insert (CurrentColumn, strLines[i])); - CurrentColumn += strLines[i].Length; - } - } - /// - /// Insert a line break. - /// - public void InsertLineBreak() - { - if (CurrentColumn > 0) { - Insert (CurrentLine + 1, this [CurrentLine].Content.Substring (CurrentColumn)); - UpdateLine (CurrentLine, this [CurrentLine].Content.Substring (0, CurrentColumn)); - } else - Insert(CurrentLine, ""); - - CurrentColumn = 0; - CurrentLine++; - } - #endregion - } -} - diff --git a/src/CodeLine.cs b/src/CodeLine.cs deleted file mode 100644 index 622584c..0000000 --- a/src/CodeLine.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Text; -using System.Collections.Generic; - -namespace Crow.Coding -{ - public class CodeLine - { - public string Content; - public List Tokens; - public int EndingState = 0; - public Node SyntacticNode; - public ParsingException exception; - - public CodeLine (string _content){ - Content = _content; - Tokens = null; - exception = null; - } - - public char this[int i] - { - get { return Content[i]; } - set { - if (Content [i] == value) - return; - StringBuilder sb = new StringBuilder(Content); - sb[i] = value; - Content = sb.ToString(); - Tokens = null; - //LineUpadateEvent.Raise (this, new CodeBufferEventArgs (i)); - } - } - public bool IsFoldable { get { return SyntacticNode != null; } } - public bool IsFolded = false; - public bool IsParsed { - get { return Tokens != null; } - } - public string PrintableContent { - get { - return string.IsNullOrEmpty (Content) ? "" : Content.Replace ("\t", new String (' ', Interface.TabSize)); - } - } - public int PrintableLength { - get { - return PrintableContent.Length; - } - } - public int Length { - get { - return string.IsNullOrEmpty (Content) ? 0 : Content.Length; - } - } - - public void SetLineInError (ParsingException ex) { - Tokens = null; - exception = ex; - } - -// public static implicit operator string(CodeLine sl) { -// return sl == null ? "" : sl.Content; -// } - public static implicit operator CodeLine(string s) { - return new CodeLine(s); - } - public static bool operator ==(string s1, CodeLine s2) - { - return string.Equals (s1, s2.Content); - } - public static bool operator !=(string s1, CodeLine s2) - { - return !string.Equals (s1, s2.Content); - } - } -} - diff --git a/src/CrowEdit.cs b/src/CrowEdit.cs index a9af7dc..54cb1c4 100644 --- a/src/CrowEdit.cs +++ b/src/CrowEdit.cs @@ -1,23 +1,7 @@ -// -// Main.cs +// Copyright (c) 2013-2020 Jean-Philippe Bruyère // -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2017 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + using System; using Crow; using System.IO; @@ -25,11 +9,11 @@ using System.Collections.Generic; namespace CrowEdit { - public class CrowEdit : CrowWindow + public class CrowEdit : Interface { - public Command CMDNew, CMDOpen, CMDSave, CMDSaveAs, CMDQuit, CMDUndo, CMDRedo, CMDCut, CMDCopy, CMDPaste, CMDHelp, CMDAbout, CMDOptions; + public Command CMDNew, CMDOpen, CMDSave, CMDSaveAs, CMDQuit, CMDShowLeftPane, + CMDUndo, CMDRedo, CMDCut, CMDCopy, CMDPaste, CMDHelp, CMDAbout, CMDOptions; - string _curDir = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments); string _curFilePath = "unamed.txt"; string _text = "", _origText=""; @@ -46,7 +30,7 @@ namespace CrowEdit redoStack.Clear (); CMDRedo.CanExecute = false; _text = value; - NotifyValueChanged ("Text", _text); + NotifyValueChanged (_text); NotifyValueChanged ("IsDirty", IsDirty); } } @@ -54,12 +38,12 @@ namespace CrowEdit public bool IsDirty { get { return _text != _origText; }} public string CurrentDir { - get { return Configuration.Get("CurrentDir"); } + get { return Configuration.Global.Get("CurrentDir"); } set { if (CurrentDir == value) return; - Configuration.Set ("CurrentDir", value); - NotifyValueChanged ("CurrentDir", _curDir); + Configuration.Global.Set ("CurrentDir", value); + NotifyValueChanged (CurrentDir); } } public string CurFilePath { @@ -68,36 +52,43 @@ namespace CrowEdit if (_curFilePath == value) return; _curFilePath = value; - NotifyValueChanged ("CurFilePath", _curFilePath); + NotifyValueChanged (_curFilePath); } } + bool showLeftPane; + public bool ShowLeftPane { + get { return Configuration.Global.Get ("ShowLeftPane"); } + set { + if (ShowLeftPane == value) + return; + Configuration.Global.Set ("ShowLeftPane", value); + NotifyValueChanged (ShowLeftPane); + } + } + public string CurFileFullPath { get { return Path.Combine(CurrentDir,CurFilePath); }} void initCommands(){ - CMDNew = new Command(new Action(() => newFile())) { Caption = "New", Icon = new SvgPicture("#CrowEdit.ui.icons.blank-file.svg")}; + 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(() => Quit (null, null))) { Caption = "Quit", Icon = new SvgPicture("#CrowEdit.ui.icons.sign-out.svg")}; + 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 (null, null))) { Caption = "Cut", Icon = new SvgPicture("#CrowEdit.ui.icons.scissors.svg"), CanExecute = false}; - CMDCopy = new Command(new Action(() => Quit (null, null))) { Caption = "Copy", Icon = new SvgPicture("#CrowEdit.ui.icons.copy-file.svg"), CanExecute = false}; - CMDPaste = new Command(new Action(() => Quit (null, null))) { Caption = "Paste", Icon = new SvgPicture("#CrowEdit.ui.icons.paste-on-document.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}; 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" }; } - void newFile(){ - CurFilePath = "unamed.txt"; - _origText = _text = ""; - NotifyValueChanged ("Text", _text); - NotifyValueChanged ("IsDirty", false); - redoStack.Clear (); - undoStack.Clear (); - CMDRedo.CanExecute = false; - CMDUndo.CanExecute = false; - NotifyValueChanged ("CurFileFullPath", CurFileFullPath); + 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 undo(){ string step = undoStack [undoStack.Count -1]; @@ -107,7 +98,7 @@ namespace CrowEdit _text = step; - NotifyValueChanged ("Text", _text); + NotifyValueChanged ("Text", (object)_text); NotifyValueChanged ("IsDirty", IsDirty); if (undoStack.Count == 0) @@ -119,14 +110,14 @@ namespace CrowEdit CMDUndo.CanExecute = true; redoStack.RemoveAt(redoStack.Count -1); _text = step; - NotifyValueChanged ("Text", _text); + NotifyValueChanged ("Text", (object)_text); NotifyValueChanged ("IsDirty", IsDirty); if (redoStack.Count == 0) CMDRedo.CanExecute = false; } void openOptionsDialog(){ - GraphicObject ed = this.FindByName("editor"); + Widget ed = this.FindByName("editor"); Load ("#CrowEdit.ui.EditorOptions.crow").DataSource = ed; } void openFileDialog(){ @@ -140,15 +131,7 @@ namespace CrowEdit FileDialog fd = sender as FileDialog; if (string.IsNullOrEmpty (fd.SelectedFile)) return; - CurFilePath = fd.SelectedFile; - CurrentDir = fd.SelectedDirectory; - -// redoStack.Clear (); -// undoStack.Clear (); -// CMDRedo.CanExecute = false; -// CMDUndo.CanExecute = false; -// - NotifyValueChanged ("CurFileFullPath", CurFileFullPath); + openFile (fd.SelectedFile, fd.SelectedDirectory); } void saveFileDialog_OkClicked (object sender, EventArgs e) { @@ -165,28 +148,73 @@ namespace CrowEdit _origText = _text; NotifyValueChanged ("IsDirty", false); - NotifyValueChanged ("CurFileFullPath", CurFileFullPath); + NotifyValueChanged ("CurFileFullPath", (object)CurFileFullPath); } void onTextChanged (object sender, TextChangeEventArgs e) { - System.Diagnostics.Debug.WriteLine ("text changed"); + //System.Diagnostics.Debug.WriteLine ("text changed"); NotifyValueChanged ("IsDirty", IsDirty); } + void goUpDirClick (object sender, MouseButtonEventArgs e) { + string root = Directory.GetDirectoryRoot (CurrentDir); + if (CurrentDir == root) + return; + CurrentDir = Directory.GetParent (CurrentDir).FullName; + } + + void Dv_SelectedItemChanged (object sender, SelectionChangeEventArgs e) { + FileSystemInfo fi = e.NewValue as FileSystemInfo; + if (fi == null) + return; + if (fi is DirectoryInfo) + return; + + OnOpenFile (Path.GetFileName (fi.FullName), Path.GetDirectoryName (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); + } else + openFile (filePath, directory); + } + void newFile () { + CurFilePath = "unamed.txt"; + _origText = _text = ""; + NotifyValueChanged ("Text", (object)_text); + NotifyValueChanged ("IsDirty", false); + redoStack.Clear (); + undoStack.Clear (); + CMDRedo.CanExecute = false; + CMDUndo.CanExecute = false; + NotifyValueChanged ("CurFileFullPath", (object)CurFileFullPath); + } + void openFile (string filePath, string directory) { + CurFilePath = filePath; + CurrentDir = directory; + + redoStack.Clear (); + undoStack.Clear (); + CMDRedo.CanExecute = false; + CMDUndo.CanExecute = false; + + NotifyValueChanged ("CurFileFullPath", (object)CurFileFullPath); + } + [STAThread] static void Main () { - using (CrowEdit win = new CrowEdit ()) { - win.Run (30); - } + using (CrowEdit win = new CrowEdit ()) + win.Run (); } public CrowEdit () - : base(800, 600,"Crow Simple Editor") + : base(800, 600) {} - protected override void OnLoad (EventArgs e) - { - base.OnLoad (e); + protected override void OnInitialized () { + base.OnInitialized (); if (CurrentDir == null) CurrentDir = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments); @@ -194,10 +222,10 @@ namespace CrowEdit this.ValueChanged += CrowEdit_ValueChanged; initCommands (); Load ("#CrowEdit.ui.main.crow").DataSource = this; - NotifyValueChanged ("CurFileFullPath", CurFileFullPath); + NotifyValueChanged ("CurFileFullPath", (object)CurFileFullPath); } - void textView_KeyDown (object sender, Crow.KeyboardKeyEventArgs e) + /*void textView_KeyDown (object sender, Crow.KeyEventArgs e) { if (e.Control) { if (e.Key == Key.W) { @@ -207,7 +235,7 @@ namespace CrowEdit CMDUndo.Execute (); } } - } + }*/ void CrowEdit_ValueChanged (object sender, ValueChangeEventArgs e) { diff --git a/src/CrowEditExtentions.cs b/src/CrowEditExtentions.cs deleted file mode 100644 index a2d7ac6..0000000 --- a/src/CrowEditExtentions.cs +++ /dev/null @@ -1,41 +0,0 @@ -// -// CrowEditExtentions.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2017 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -using System; -using System.Collections.Generic; - -namespace CrowEdit -{ - public static class CrowEditExtentions - { - public static List AllIndexesOf(this string str, string value) { - if (String.IsNullOrEmpty(value)) - throw new ArgumentException("the string to find may not be empty", "value"); - List indexes = new List(); - for (int index = 0;; index += value.Length) { - index = str.IndexOf(value, index); - if (index == -1) - return indexes; - indexes.Add(index); - } - } - } -} - diff --git a/src/Node.cs b/src/Node.cs deleted file mode 100644 index 24818e0..0000000 --- a/src/Node.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Crow.Coding -{ - public class Node - { - public Node Parent; - public string Name; - public string Type; - public CodeLine StartLine; - public CodeLine EndLine; - public Dictionary Attributes = new Dictionary (); - - public List Children = new List(); - - public Node () - { - } - - public void AddChild (Node child) { - child.Parent = this; - Children.Add (child); - } - - public override string ToString () - { - return string.Format ("Name:{0}, Type:{1}\n\tparent:{2}", Name, Type, Parent); - } - } -} - diff --git a/src/Parser.cs b/src/Parser.cs deleted file mode 100644 index 79dc086..0000000 --- a/src/Parser.cs +++ /dev/null @@ -1,297 +0,0 @@ -using System; -using System.IO; -using Crow; -using System.Collections.Generic; -using System.Diagnostics; - -namespace Crow.Coding -{ - /// - /// base class for tokenizing sources - /// - public abstract class Parser - { - /// - /// Default tokens, this enum may be overriden in derived parser with the new keyword, - /// see XMLParser for example. - /// - public enum TokenType { - Unknown, - WhiteSpace, - NewLine, - LineComment, - BlockCommentStart, - BlockComment, - BlockCommentEnd, - Type, - Identifier, - Indexer, - OpenBlock, - CloseBlock, - StatementEnding, - UnaryOp, - BinaryOp, - Affectation, - StringLitteralOpening, - StringLitteralClosing, - StringLitteral, - NumericLitteral, - Preprocessor, - } - - #region CTOR - public Parser (CodeBuffer _buffer) - { - buffer = _buffer; - - buffer.LineUpadateEvent += Buffer_LineUpadateEvent; - //buffer.LineAdditionEvent += Buffer_LineAdditionEvent;; - buffer.LineRemoveEvent += Buffer_LineRemoveEvent; - buffer.BufferCleared += Buffer_BufferCleared; - } - - #endregion - - #region Buffer events handlers - void Buffer_BufferCleared (object sender, EventArgs e) - { - - } - void Buffer_LineAdditionEvent (object sender, CodeBufferEventArgs e) - { - - } - void Buffer_LineRemoveEvent (object sender, CodeBufferEventArgs e) - { - reparseSource (); - } - void Buffer_LineUpadateEvent (object sender, CodeBufferEventArgs e) - { - for (int i = 0; i < e.LineCount; i++) - tryParseBufferLine (e.LineStart + i); - reparseSource (); - } - #endregion - - void updateFolding () { - // Stack foldings = new Stack(); - // bool inStartTag = false; - // - // for (int i = 0; i < parser.Tokens.Count; i++) { - // TokenList tl = parser.Tokens [i]; - // tl.foldingTo = null; - // int fstTK = tl.FirstNonBlankTokenIndex; - // if (fstTK > 0 && fstTK < tl.Count - 1) { - // if (tl [fstTK + 1] != XMLParser.TokenType.ElementName) - // continue; - // if (tl [fstTK] == XMLParser.TokenType.ElementStart) { - // //search closing tag - // int tkPtr = fstTK+2; - // while (tkPtr < tl.Count) { - // if (tl [tkPtr] == XMLParser.TokenType.ElementClosing) - // - // tkPtr++; - // } - // if (tl.EndingState == (int)XMLParser.States.Content) - // foldings.Push (tl); - // else if (tl.EndingState == (int)XMLParser.States.StartTag) - // inStartTag = true; - // continue; - // } - // if (tl [fstTK] == XMLParser.TokenType.ElementEnd) { - // TokenList tls = foldings.Pop (); - // int fstTKs = tls.FirstNonBlankTokenIndex; - // if (tls [fstTK + 1].Content == tl [fstTK + 1].Content) { - // tl.foldingTo = tls; - // continue; - // } - // parser.CurrentPosition = tls [fstTK + 1].Start; - // parser.SetLineInError(new ParsingException(parser, "closing tag not corresponding")); - // } - // - // } - // } - } - public void reparseSource () { - for (int i = 0; i < buffer.LineCount; i++) { - if (!buffer[i].IsParsed) - tryParseBufferLine (i); - } - try { - SyntaxAnalysis (); - } catch (Exception ex) { - Debug.WriteLine ("Syntax Error: " + ex.ToString ()); - if (ex is ParsingException) - SetLineInError (ex as ParsingException); - } - } - public void tryParseBufferLine(int lPtr) { - buffer [lPtr].exception = null; - currentLine = lPtr; - currentColumn = 0; - eol = false; - - try { - ParseCurrentLine (); - } catch (Exception ex) { - Debug.WriteLine (ex.ToString ()); - if (ex is ParsingException) - SetLineInError (ex as ParsingException); - } - - } - - protected CodeBuffer buffer; - - internal int currentLine = 0; - internal int currentColumn = 0; - protected Token currentTok; - protected bool eol = true; - - public Node RootNode; - - protected Point CurrentPosition { - get { return new Point (currentLine, currentColumn); } - set { - currentLine = value.Y; - currentColumn = value.X; - } - } - - public abstract void ParseCurrentLine(); - public abstract void SyntaxAnalysis (); - - public virtual void SetLineInError(ParsingException ex) { - currentTok = default(Token); - if (ex.Line >= buffer.LineCount) - ex.Line = buffer.LineCount - 1; - if (buffer [ex.Line].IsFolded) - buffer.ToogleFolding (ex.Line); - buffer [ex.Line].SetLineInError (ex); - } - - #region low level parsing - /// - /// Read one char from current position in buffer and store it into the current token - /// - /// if true, set the Start position of the current token to the current position - protected void readToCurrTok(bool startOfTok = false){ - if (startOfTok) - currentTok.Start = CurrentPosition; - currentTok += Read(); - } - /// - /// read n char from the buffer and store it into the current token - /// - protected void readToCurrTok(int length) { - for (int i = 0; i < length; i++) - currentTok += Read (); - } - /// - /// Save current token into current TokensLine and raz current token - /// - protected void saveAndResetCurrentTok() { - currentTok.End = CurrentPosition; - buffer[currentLine].Tokens.Add (currentTok); - currentTok = default(Token); - } - /// - /// read one char and add current token to current TokensLine, current token is reset - /// - /// Type of the token - /// set start of token to current position - protected void readAndResetCurrentTok(System.Enum type, bool startToc = false) { - readToCurrTok (); - saveAndResetCurrentTok (type); - } - /// - /// Save current tok - /// - /// set the type of the tok - protected void saveAndResetCurrentTok(System.Enum type) { - currentTok.Type = (TokenType)type; - saveAndResetCurrentTok (); - } - /// - /// Peek next char, emit '\n' if current column > buffer's line length - /// Throw error if eof is true - /// - protected virtual char Peek() { - if (eol) - throw new ParsingException (this, "Unexpected End of line"); - return currentColumn < buffer [currentLine].Length ? - buffer [currentLine] [currentColumn] : '\n'; - } - /// - /// Peek n char from buffer or less if remaining char in buffer's line is less than requested - /// if end of line is reached, no '\n' will be emitted, instead, empty string is returned. '\n' should be checked only - /// with single char Peek(). - /// Throw error is eof is true - /// - /// Length. - protected virtual string Peek(int length) { - if (eol) - throw new ParsingException (this, "Unexpected End of Line"); - int lg = Math.Min(length, Math.Max (buffer [currentLine].Length - currentColumn, buffer [currentLine].Length - currentColumn - length)); - if (lg == 0) - return ""; - return buffer [currentLine].Content.Substring (currentColumn, lg); - } - /// - /// read one char from buffer at current position, if '\n' is read, current line is incremented - /// and column is reset to 0 - /// - protected virtual char Read() { - char c = Peek (); - if (c == '\n') - eol = true; - currentColumn++; - return c; - } - /// - /// read until end of line is reached - /// - /// string read - protected virtual string ReadLine () { - string tmp = ""; - while (!eol) - tmp += Read (); - return tmp; - } - /// - /// read until end expression is reached or end of line. - /// - /// string read minus the ending expression that has to be read after - /// Expression to search for - protected virtual string ReadLineUntil (string endExp){ - string tmp = ""; - - while (!eol) { - if (buffer [currentLine].Length - currentColumn - endExp.Length < 0) { - tmp += ReadLine(); - break; - } - if (string.Equals (Peek (endExp.Length), endExp)) - return tmp; - tmp += Read(); - } - return tmp; - } - /// - /// skip white spaces, but not line break. Save spaces in a WhiteSpace token. - /// - protected void SkipWhiteSpaces () { - if (currentTok.Type != TokenType.Unknown) - throw new ParsingException (this, "current token should be reset to unknown (0) before skiping white spaces"); - while (!eol) { - if (!char.IsWhiteSpace (Peek ())||Peek()=='\n') - break; - readToCurrTok (currentTok.Type == TokenType.Unknown); - currentTok.Type = TokenType.WhiteSpace; - } - if (currentTok.Type != TokenType.Unknown) - saveAndResetCurrentTok (); - } - #endregion - } -} \ No newline at end of file diff --git a/src/SourceEditor.cs b/src/SourceEditor.cs deleted file mode 100644 index 57c5757..0000000 --- a/src/SourceEditor.cs +++ /dev/null @@ -1,1083 +0,0 @@ -// -// ScrollingTextBox.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using System.Xml.Serialization; -using System.ComponentModel; -using System.Collections; -using Cairo; -using System.Text; -using System.Collections.Generic; -using System.Text.RegularExpressions; -using System.Linq; -using System.Diagnostics; -using CrowEdit; -using System.IO; - -namespace Crow.Coding -{ - /// - /// Scrolling text box optimized for monospace fonts, for coding - /// - public class SourceEditor : ScrollingObject - { - #region CTOR - public SourceEditor (): base() - { - formatting.Add ((int)XMLParser.TokenType.AttributeName, new TextFormatting (Color.UnitedNationsBlue, Color.Transparent)); - formatting.Add ((int)XMLParser.TokenType.ElementName, new TextFormatting (Color.DarkBlue, Color.Transparent)); - formatting.Add ((int)XMLParser.TokenType.ElementStart, new TextFormatting (Color.Black, Color.Transparent)); - formatting.Add ((int)XMLParser.TokenType.ElementEnd, new TextFormatting (Color.Black, Color.Transparent)); - formatting.Add ((int)XMLParser.TokenType.ElementClosing, new TextFormatting (Color.Black, Color.Transparent)); - formatting.Add ((int)XMLParser.TokenType.Affectation, new TextFormatting (Color.Black, Color.Transparent)); - - formatting.Add ((int)XMLParser.TokenType.AttributeValueOpening, new TextFormatting (Color.Carmine, Color.Transparent)); - formatting.Add ((int)XMLParser.TokenType.AttributeValueClosing, new TextFormatting (Color.Carmine, Color.Transparent)); - formatting.Add ((int)XMLParser.TokenType.AttributeValue, new TextFormatting (Color.OrangeRed, Color.Transparent, false, true)); - formatting.Add ((int)XMLParser.TokenType.XMLDecl, new TextFormatting (Color.GreenCrayola, Color.Transparent)); - formatting.Add ((int)XMLParser.TokenType.BlockComment, new TextFormatting (Color.Gray, Color.Transparent, false, true)); - - parsing.Add (".crow", "Crow.Coding.XMLParser"); - - buffer = new CodeBuffer (); - buffer.LineUpadateEvent += Buffer_LineUpadateEvent; - buffer.LineAdditionEvent += Buffer_LineAdditionEvent;; - buffer.LineRemoveEvent += Buffer_LineRemoveEvent; - buffer.BufferCleared += Buffer_BufferCleared; - buffer.SelectionChanged += Buffer_SelectionChanged; - buffer.PositionChanged += Buffer_PositionChanged; - buffer.FoldingEvent += Buffer_FoldingEvent; - buffer.Add (new CodeLine("")); - } - #endregion - - const int leftMarginGap = 3;//gap between items in margin and text - const int foldSize = 9;//folding rectangles size - - #region private and protected fields - bool foldingEnabled = true; - string filePath = "unamed.txt"; - int leftMargin = 0; //margin used to display line numbers, folding errors,etc... - int visibleLines = 1; - int visibleColumns = 1; - CodeBuffer buffer; - Parser parser; - Color selBackground; - Color selForeground; -// int _currentCol; //0 based cursor position in string -// int _currentLine; - - - Dictionary formatting = new Dictionary(); - Dictionary parsing = new Dictionary(); - - protected Rectangle rText; - protected FontExtents fe; - protected TextExtents te; - #endregion - - void measureLeftMargin () { - leftMargin = 0; - if (PrintLineNumbers) - leftMargin += (int)Math.Ceiling((double)buffer.LineCount.ToString().Length * fe.MaxXAdvance) +6; - if (foldingEnabled) - leftMargin += foldSize; - if (leftMargin > 0) - leftMargin += leftMarginGap; - updateVisibleColumns (); - } - void findLongestLineAndUpdateMaxScrollX() { - buffer.FindLongestVisualLine (); - MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns); - Debug.WriteLine ("SourceEditor: Find Longest line and update maxscrollx: {0} visible cols:{1}", MaxScrollX, visibleColumns); - } - /// - /// Updates visible line in widget, adapt max scroll y and updatePrintedLines - /// - void updateVisibleLines(){ - visibleLines = (int)Math.Floor ((double)ClientRectangle.Height / fe.Height); - updateMaxScrollY (); - updatePrintedLines (); - System.Diagnostics.Debug.WriteLine ("update visible lines: " + visibleLines); - System.Diagnostics.Debug.WriteLine ("update MaxScrollY: " + MaxScrollY); - } - void updateVisibleColumns(){ - visibleColumns = (int)Math.Floor ((double)(ClientRectangle.Width - leftMargin)/ fe.MaxXAdvance); - MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns); - - System.Diagnostics.Debug.WriteLine ("update visible columns: {0} leftMargin:{1}",visibleColumns, leftMargin); - System.Diagnostics.Debug.WriteLine ("update MaxScrollX: " + MaxScrollX); - } - void updateMaxScrollY () { - if (parser == null || !foldingEnabled) - MaxScrollY = Math.Max (0, buffer.LineCount - visibleLines); - else - MaxScrollY = Math.Max (0, buffer.UnfoldedLines - visibleLines); - } - - int firstPrintedLine = -1; - /// - /// list of lines visible in the Editor depending on scrolling and folding - /// - List PrintedLines; - - void updatePrintedLines () { - lock (buffer.EditMutex) { - PrintedLines = new List (); - int curL = 0; - int i = 0; - - while (curL < buffer.LineCount && i < ScrollY) { - if (buffer [curL].IsFolded) - curL = buffer.GetEndNodeIndex (curL); - curL++; - i++; - } - - firstPrintedLine = curL; - i = 0; - while (i < visibleLines && curL < buffer.LineCount) { - PrintedLines.Add (buffer [curL]); - - if (buffer [curL].IsFolded) - curL = buffer.GetEndNodeIndex (curL); - - curL++; - i++; - } - } - RegisterForGraphicUpdate (); - } - void toogleFolding (int line) { - if (parser == null || !foldingEnabled) - return; - buffer.ToogleFolding (line); - } - - #region Buffer events handlers - void Buffer_BufferCleared (object sender, EventArgs e) - { - buffer.longestLineCharCount = 0; - buffer.longestLineIdx = 0; - measureLeftMargin (); - MaxScrollX = MaxScrollY = 0; - PrintedLines = null; - RegisterForGraphicUpdate (); - } - void Buffer_LineAdditionEvent (object sender, CodeBufferEventArgs e) - { - for (int i = 0; i < e.LineCount; i++) { - int lptr = e.LineStart + i; - int charCount = buffer[lptr].PrintableLength; - if (charCount > buffer.longestLineCharCount) { - buffer.longestLineIdx = lptr; - buffer.longestLineCharCount = charCount; - }else if (lptr <= buffer.longestLineIdx) - buffer.longestLineIdx++; - if (parser == null) - continue; - parser.tryParseBufferLine (e.LineStart + i); - } - measureLeftMargin (); - - if (parser != null) - parser.reparseSource (); - - updatePrintedLines (); - updateMaxScrollY (); - RegisterForGraphicUpdate (); - } - void Buffer_LineRemoveEvent (object sender, CodeBufferEventArgs e) - { - bool trigFindLongestLine = false; - for (int i = 0; i < e.LineCount; i++) { - int lptr = e.LineStart + i; - if (lptr <= buffer.longestLineIdx) - trigFindLongestLine = true; - } - if (trigFindLongestLine) - findLongestLineAndUpdateMaxScrollX (); - - measureLeftMargin (); - updatePrintedLines (); - updateMaxScrollY (); - RegisterForGraphicUpdate (); - } - void Buffer_LineUpadateEvent (object sender, CodeBufferEventArgs e) - { - bool trigFindLongestLine = false; - for (int i = 0; i < e.LineCount; i++) { - - int lptr = e.LineStart + i; - if (lptr == buffer.longestLineIdx) - trigFindLongestLine = true; - else if (buffer[lptr].PrintableLength > buffer.longestLineCharCount) { - buffer.longestLineCharCount = buffer[lptr].PrintableLength; - buffer.longestLineIdx = lptr; - } - } - if (trigFindLongestLine) - findLongestLineAndUpdateMaxScrollX (); - RegisterForGraphicUpdate (); - } - void Buffer_PositionChanged (object sender, EventArgs e) - { - updateOnScreenCurLineFromBuffCurLine (); - } - - void Buffer_SelectionChanged (object sender, EventArgs e) - { - RegisterForGraphicUpdate (); - } - void Buffer_FoldingEvent (object sender, CodeBufferEventArgs e) - { - updatePrintedLines (); - updateMaxScrollY (); - RegisterForGraphicUpdate (); - } - #endregion - - Parser getParserFromExt (string extension) { - if (string.IsNullOrEmpty(extension)) - return null; - if (!parsing.ContainsKey(extension)) - return null; - Type parserType = Type.GetType (parsing [extension]); - if (parserType == null) - return null; - return (Parser)Activator.CreateInstance (parserType, buffer ); - } - - #region Public Crow Properties - [XmlAttributeAttribute] - public bool PrintLineNumbers - { - get { return Configuration.Get ("PrintLineNumbers"); - } - set - { - if (PrintLineNumbers == value) - return; - Configuration.Set ("PrintLineNumbers", value); - NotifyValueChanged ("PrintLineNumbers", PrintLineNumbers); - measureLeftMargin (); - RegisterForGraphicUpdate (); - } - } - [XmlAttributeAttribute] - public string FilePath - { - get { - return buffer == null ? "" : buffer.FullText; - } - set - { - if (filePath == value) - return; - - filePath = value; - NotifyValueChanged ("FilePath", filePath); - - if (!File.Exists (filePath)) - return; - - parser = getParserFromExt (System.IO.Path.GetExtension (filePath)); - - try { - using (StreamReader sr = new StreamReader (filePath)) { - string txt = sr.ReadToEnd (); - buffer.Load (txt); - } - } catch (Exception ex) { - Debug.WriteLine (ex.ToString ()); - } - - updateMaxScrollY (); - MaxScrollX = Math.Max (0, buffer.longestLineCharCount - visibleColumns); - updatePrintedLines (); - - RegisterForGraphicUpdate (); - } - } - [XmlAttributeAttribute][DefaultValue("BlueGray")] - public virtual Color SelectionBackground { - get { return selBackground; } - set { - if (value == selBackground) - return; - selBackground = value; - NotifyValueChanged ("SelectionBackground", selBackground); - RegisterForRedraw (); - } - } - [XmlAttributeAttribute][DefaultValue("White")] - public virtual Color SelectionForeground { - get { return selForeground; } - set { - if (value == selForeground) - return; - selForeground = value; - NotifyValueChanged ("SelectionForeground", selForeground); - RegisterForRedraw (); - } - } -// [XmlAttributeAttribute][DefaultValue(0)] -// public int CurrentColumn{ -// get { return _currentCol; } -//// set { -//// if (value == _currentCol) -//// return; -//// if (value < 0) -//// _currentCol = 0; -//// else if (value > buffer[_currentLine].PrintableLength) -//// _currentCol = buffer[_currentLine].PrintableLength; -//// else -//// _currentCol = value; -//// -//// buffer.SetBufferPos (CurrentPosition); -//// -//// if (_currentCol < ScrollX) -//// ScrollX = _currentCol; -//// else if (_currentCol >= ScrollX + visibleColumns) -//// ScrollX = _currentCol - visibleColumns + 1; -//// -//// NotifyValueChanged ("CurrentColumn", _currentCol); -//// } -// } -// [XmlAttributeAttribute][DefaultValue(0)] -// public int CurrentLine{ -// get { return _currentLine; } -//// set { -//// if (value == _currentLine) -//// return; -//// if (value >= buffer.LineCount) -//// _currentLine = buffer.LineCount-1; -//// else if (value < 0) -//// _currentLine = 0; -//// else -//// _currentLine = value; -//// -//// if (_currentCol > buffer[_currentLine].PrintableLength) -//// CurrentColumn = buffer[_currentLine].PrintableLength;//buffer.setBufferPos is called inside -//// else -//// buffer.SetBufferPos (CurrentPosition); -//// -////// if (_currentLine < ScrollY) -////// ScrollY = _currentLine; -////// else if (_currentLine >= ScrollY + visibleLines) -////// ScrollY = _currentLine - visibleLines + 1; -//// -//// NotifyValueChanged ("CurrentLine", _currentLine); -//// } -// } -// /// -// /// Current position is in the printed coord system, tabulation chars are replaced with 4 spaces, -// /// while in the buffer, the position holds tabulations as single chars -// /// -// /// The current position. -// [XmlIgnore]public Point CurrentPosition { -// get { return new Point(CurrentColumn, CurrentLine); } -// set { -// _currentCol = value.X; -// _currentLine = value.Y; -// -// if (_currentCol < ScrollX) -// ScrollX = _currentCol; -// else if (_currentCol >= ScrollX + visibleColumns) -// ScrollX = _currentCol - visibleColumns + 1; -//// -//// if (_currentLine < ScrollY) -//// ScrollY = _currentLine; -//// else if (_currentLine >= ScrollY + visibleLines) -//// ScrollY = _currentLine - visibleLines + 1; -// -// NotifyValueChanged ("CurrentColumn", _currentCol); -// NotifyValueChanged ("CurrentLine", _currentLine); -// } -// } - -// [XmlIgnore]public string SelectedText -// { -// get { -// if (!selectionIsEmpty) -// buffer.SetSelection (selectionStart, selectionEnd); -// return buffer.SelectedText; -// } -// } - - #endregion - - - void updateOnScreenCurLineFromBuffCurLine(){ - printedCurrentLine = PrintedLines.IndexOf (buffer.CurrentCodeLine); - } - -// void setCurrentLineFromBuffer () { -// _currentLine = buffer.CurrentLine; -// NotifyValueChanged ("CurrentLine", _currentLine); -// } - - public override int ScrollY { - get { - return base.ScrollY; - } - set { - if (value == base.ScrollY) - return; - base.ScrollY = value; - updatePrintedLines (); - } - } - - - /// - /// Index of the currentline in the PrintedLines array - /// - int printedCurrentLine = 0; - - /// - /// Current editor line, when set, update buffer.CurrentLine - /// - int PrintedCurrentLine { - get { return printedCurrentLine;} - set { - if (value < 0) { - ScrollY += value; - printedCurrentLine = 0; - } else if (PrintedLines.Count < visibleLines && value >= PrintedLines.Count) { - printedCurrentLine = PrintedLines.Count - 1; - }else if (value >= visibleLines) { - ScrollY += value - visibleLines + 1; - printedCurrentLine = visibleLines - 1; - }else - printedCurrentLine = value; - Debug.WriteLine ("printed current line:" + printedCurrentLine.ToString ()); - //update position in buffer - buffer.CurrentLine = buffer.IndexOf (PrintedLines[printedCurrentLine]); - } - } - int getTabulatedColumn (int col, int line) { - return buffer [line].Content.Substring (0, col).Replace ("\t", new String (' ', Interface.TabSize)).Length; - } - int getTabulatedColumn (Point pos) { - return getTabulatedColumn (pos.X,pos.Y); - } - /// - /// Moves cursor one char to the left, move up if cursor reaches start of line - /// - /// true if move succeed - public bool MoveLeft(){ - if (buffer.CurrentColumn == 0) { - if (printedCurrentLine == 0) - return false; - PrintedCurrentLine--; - buffer.CurrentColumn = int.MaxValue; - } else - buffer.CurrentColumn--; - return true; - } - /// - /// Moves cursor one char to the right, move down if cursor reaches end of line - /// - /// true if move succeed - public bool MoveRight(){ - if (buffer.CurrentColumn >= buffer.CurrentCodeLine.Length) { - if (PrintedCurrentLine == buffer.UnfoldedLines - 1) - return false; - buffer.CurrentColumn = 0; - PrintedCurrentLine++; - } else - buffer.CurrentColumn++; - return true; - } - #region Drawing - void drawLine(Context gr, Rectangle cb, int i) { - CodeLine cl = PrintedLines[i]; - int lineIndex = buffer.IndexOf(cl); - - double y = cb.Y + fe.Height * i, x = cb.X; - - //Draw line numbering - Color mgFg = Color.Gray; - Color mgBg = Color.White; - if (PrintLineNumbers){ - Rectangle mgR = new Rectangle ((int)x, (int)y, leftMargin - leftMarginGap, (int)Math.Ceiling(fe.Height)); - if (cl.exception != null) { - mgBg = Color.Red; - if (buffer.CurrentLine == lineIndex) - mgFg = Color.White; - else - mgFg = Color.LightGray; - }else if (buffer.CurrentLine == lineIndex) { - mgFg = Color.Black; - } - string strLN = (lineIndex+1).ToString (); - gr.SetSourceColor (mgBg); - gr.Rectangle (mgR); - gr.Fill(); - gr.SetSourceColor (mgFg); - - gr.MoveTo (cb.X + (int)(gr.TextExtents (buffer.LineCount.ToString()).Width - gr.TextExtents (strLN).Width), y + fe.Ascent); - gr.ShowText (strLN); - gr.Fill (); - } - - - //draw folding - if (foldingEnabled){ - if (cl.IsFoldable) { - if (cl.SyntacticNode.StartLine != cl.SyntacticNode.EndLine) { - gr.SetSourceColor (Color.Black); - Rectangle rFld = new Rectangle (cb.X + leftMargin - leftMarginGap - foldSize, (int)(y + fe.Height / 2.0 - foldSize / 2.0), foldSize, foldSize); - gr.Rectangle (rFld, 1.0); - if (cl.IsFolded) { - gr.MoveTo (rFld.Center.X + 0.5, rFld.Y + 2); - gr.LineTo (rFld.Center.X + 0.5, rFld.Bottom - 2); - } - gr.MoveTo (rFld.Left + 2, rFld.Center.Y + 0.5); - gr.LineTo (rFld.Right - 2, rFld.Center.Y + 0.5); - gr.Stroke (); - } - } - } - - gr.SetSourceColor (Foreground); - x += leftMargin; - - if (cl.Tokens == null) - drawRawCodeLine (gr, x, y, i, lineIndex); - else - drawParsedCodeLine (gr, x, y, i, lineIndex); - } -// void drawParsed(Context gr){ -// if (PrintedLines == null) -// return; -// -// gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); -// gr.SetFontSize (Font.Size); -// gr.FontOptions = Interface.FontRenderingOptions; -// gr.Antialias = Interface.Antialias; -// -// Rectangle cb = ClientRectangle; -// gr.Save (); -// CairoHelpers.CairoRectangle (gr, cb, CornerRadius); -// gr.Clip (); -// -// bool selectionInProgress = false; -// -// Foreground.SetAsSource (gr); -// -// #region draw text cursor -// if (SelBegin != SelRelease) -// selectionInProgress = true; -// else if (HasFocus){ -// gr.LineWidth = 1.0; -// double cursorX = + leftMargin + cb.X + (CurrentColumn - ScrollX) * fe.MaxXAdvance; -// gr.MoveTo (0.5 + cursorX, cb.Y + printedCurrentLine * fe.Height); -// gr.LineTo (0.5 + cursorX, cb.Y + (printedCurrentLine + 1) * fe.Height); -// gr.Stroke(); -// } -// #endregion -// -// for (int i = 0; i < PrintedLines.Count; i++) -// drawTokenLine (gr, i, selectionInProgress, cb); -// -// gr.Restore (); -// } - void drawRawCodeLine(Context gr, double x, double y, int i, int lineIndex) { - string lstr = buffer[lineIndex].PrintableContent; - if (ScrollX < lstr.Length) - lstr = lstr.Substring (ScrollX); - else - lstr = ""; - - gr.MoveTo (x, y + fe.Ascent); - gr.ShowText (lstr); - gr.Fill (); - - if (!buffer.SelectionIsEmpty && lineIndex >= buffer.SelectionStart.Y && lineIndex <= buffer.SelectionEnd.Y) { - double rLineX = x, - rLineY = y, - rLineW = lstr.Length * fe.MaxXAdvance; - - //System.Diagnostics.Debug.WriteLine ("sel start: " + buffer.SelectionStart + " sel end: " + buffer.SelectionEnd); - if (lineIndex == buffer.SelectionStart.Y) { - rLineX += (selStartCol - ScrollX) * fe.MaxXAdvance; - rLineW -= selStartCol * fe.MaxXAdvance; - } - if (lineIndex == buffer.SelectionEnd.Y) - rLineW -= (lstr.Length - selEndCol) * fe.MaxXAdvance; - - gr.Save (); - gr.Operator = Operator.Source; - gr.Rectangle (rLineX, rLineY, rLineW, fe.Height); - gr.SetSourceColor (SelectionBackground); - gr.FillPreserve (); - gr.Clip (); - gr.Operator = Operator.Over; - gr.SetSourceColor (SelectionForeground); - gr.MoveTo (x, y + fe.Ascent); - gr.ShowText (lstr); - gr.Fill (); - gr.Restore (); - } - } - void drawParsedCodeLine (Context gr, double x, double y, int i, int lineIndex) { - int lPtr = 0; - CodeLine cl = PrintedLines[i]; - - for (int t = 0; t < cl.Tokens.Count; t++) { - string lstr = cl.Tokens [t].PrintableContent; - if (lPtr < ScrollX) { - if (lPtr - ScrollX + lstr.Length <= 0) { - lPtr += lstr.Length; - continue; - } - lstr = lstr.Substring (ScrollX - lPtr); - lPtr += ScrollX - lPtr; - } - Color bg = this.Background; - Color fg = this.Foreground; - Color selbg = this.SelectionBackground; - Color selfg = this.SelectionForeground; - FontSlant fts = FontSlant.Normal; - FontWeight ftw = FontWeight.Normal; - - if (formatting.ContainsKey ((int)cl.Tokens [t].Type)) { - TextFormatting tf = formatting [(int)cl.Tokens [t].Type]; - bg = tf.Background; - fg = tf.Foreground; - if (tf.Bold) - ftw = FontWeight.Bold; - if (tf.Italic) - fts = FontSlant.Italic; - } - - gr.SelectFontFace (Font.Name, fts, ftw); - gr.SetSourceColor (fg); - - gr.MoveTo (x, y + fe.Ascent); - gr.ShowText (lstr); - gr.Fill (); - - if (buffer.SelectionInProgress && lineIndex >= buffer.SelectionStart.Y && lineIndex <= buffer.SelectionEnd.Y && - !(lineIndex == buffer.SelectionStart.Y && lPtr + lstr.Length <= selStartCol) && - !(lineIndex == buffer.SelectionEnd.Y && selEndCol <= lPtr)) { - - double rLineX = x, - rLineY = y, - rLineW = lstr.Length * fe.MaxXAdvance; - double startAdjust = 0.0; - - if ((lineIndex == buffer.SelectionStart.Y) && (selStartCol < lPtr + lstr.Length) && (selStartCol > lPtr)) - startAdjust = (selStartCol - lPtr) * fe.MaxXAdvance; - rLineX += startAdjust; - if ((lineIndex == buffer.SelectionEnd.Y) && (selEndCol < lPtr + lstr.Length)) - rLineW = (selEndCol - lPtr) * fe.MaxXAdvance; - rLineW -= startAdjust; - - gr.Save (); - gr.Operator = Operator.Source; - gr.Rectangle (rLineX, rLineY, rLineW, fe.Height); - gr.SetSourceColor (selbg); - gr.FillPreserve (); - gr.Clip (); - gr.Operator = Operator.Over; - gr.SetSourceColor (selfg); - gr.MoveTo (x, y + fe.Ascent); - gr.ShowText (lstr); - gr.Fill (); - gr.Restore (); - } - x += (int)lstr.Length * fe.MaxXAdvance; - lPtr += lstr.Length; - } - } - - #endregion - - #region GraphicObject overrides - public override Font Font { - get { return base.Font; } - set { - base.Font = value; - - using (ImageSurface img = new ImageSurface (Format.Argb32, 1, 1)) { - using (Context gr = new Context (img)) { - gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); - gr.SetFontSize (Font.Size); - - fe = gr.FontExtents; - } - } - MaxScrollY = 0; - RegisterForGraphicUpdate (); - } - } - protected override int measureRawSize(LayoutingType lt) - { - if (lt == LayoutingType.Height) - return (int)Math.Ceiling(fe.Height * buffer.LineCount) + Margin * 2; - - return (int)(fe.MaxXAdvance * buffer.longestLineCharCount) + Margin * 2 + leftMargin; - } - public override void OnLayoutChanges (LayoutingType layoutType) - { - base.OnLayoutChanges (layoutType); - - if (layoutType == LayoutingType.Height) - updateVisibleLines (); - else if (layoutType == LayoutingType.Width) - updateVisibleColumns (); - } - - int selStartCol; - int selEndCol; - - protected override void onDraw (Context gr) - { -// if (!System.Threading.Monitor.TryEnter (buffer.EditMutex)) { -// RegisterForGraphicUpdate (); -// return; -// } - - base.onDraw (gr); - - gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); - gr.SetFontSize (Font.Size); - gr.FontOptions = Interface.FontRenderingOptions; - gr.Antialias = Interface.Antialias; - - Rectangle cb = ClientRectangle; - - Foreground.SetAsSource (gr); - - lock (buffer.EditMutex) { - #region draw text cursor - if (buffer.SelectionInProgress){ - selStartCol = getTabulatedColumn (buffer.SelectionStart); - selEndCol = getTabulatedColumn (buffer.SelectionEnd); - }else if (HasFocus){ - gr.LineWidth = 1.0; - double cursorX = cb.X + (getTabulatedColumn(buffer.CurrentPosition) - ScrollX) * fe.MaxXAdvance + leftMargin; - gr.MoveTo (0.5 + cursorX, cb.Y + (printedCurrentLine) * fe.Height); - gr.LineTo (0.5 + cursorX, cb.Y + (printedCurrentLine + 1) * fe.Height); - gr.Stroke(); - } - #endregion - - if (PrintedLines != null) { - for (int i = 0; i < visibleLines; i++) { - if (i + ScrollY >= buffer.UnfoldedLines)//TODO:need optimize - break; - drawLine (gr, cb, i); - } - } - } - //System.Threading.Monitor.Exit (buffer.EditMutex); - } - #endregion - - #region Mouse handling - Point mouseLocalPos; - bool doubleClicked = false; - - void updateCurrentPos(){ -// if (mouseLocalPos.X < 0) -// CurrentColumn--; -// else - PrintedCurrentLine = (int)Math.Max (0, Math.Floor (mouseLocalPos.Y / fe.Height)); - int curVisualCol = ScrollX + (int)Math.Round ((mouseLocalPos.X - leftMargin) / fe.MaxXAdvance); - - int i = 0; - int buffCol = 0; - while (i < curVisualCol && buffCol < buffer.CurrentCodeLine.Length) { - if (buffer.CurrentCodeLine[buffCol] == '\t') - i += Interface.TabSize; - else - i++; - buffCol++; - } - buffer.CurrentColumn = buffCol; - -// if (mouseLocalPos.Y < 0) -// ScrollY--; - } - public override void onMouseEnter (object sender, MouseMoveEventArgs e) - { - base.onMouseEnter (sender, e); - if (e.X - ScreenCoordinates(Slot).X < leftMargin + ClientRectangle.X) - CurrentInterface.MouseCursor = XCursor.Default; - else - CurrentInterface.MouseCursor = XCursor.Text; - } - public override void onMouseLeave (object sender, MouseMoveEventArgs e) - { - base.onMouseLeave (sender, e); - CurrentInterface.MouseCursor = XCursor.Default; - } - public override void onMouseMove (object sender, MouseMoveEventArgs e) - { - base.onMouseMove (sender, e); - - mouseLocalPos = e.Position - ScreenCoordinates(Slot).TopLeft - ClientRectangle.TopLeft; - - if (!e.Mouse.IsButtonDown (MouseButton.Left)) { - if (mouseLocalPos.X < leftMargin) - CurrentInterface.MouseCursor = XCursor.Default; - else - CurrentInterface.MouseCursor = XCursor.Text; - return; - } - - if (!HasFocus || !buffer.SelectionInProgress) - return; - - //mouse is down - updateCurrentPos(); - buffer.SetSelEndPos (); - } - public override void onMouseDown (object sender, MouseButtonEventArgs e) - { - if (!this.Focusable) - return; - - if (mouseLocalPos.X >= leftMargin) - base.onMouseDown (sender, e); - - if (doubleClicked) { - doubleClicked = false; - return; - } - - if (mouseLocalPos.X < leftMargin) { - toogleFolding (buffer.IndexOf (PrintedLines [(int)Math.Max (0, Math.Floor (mouseLocalPos.Y / fe.Height))])); - return; - } - - updateCurrentPos (); - buffer.SetSelStartPos (); - } - public override void onMouseUp (object sender, MouseButtonEventArgs e) - { - base.onMouseUp (sender, e); - - if (buffer.SelectionIsEmpty) - buffer.ResetSelection (); - } - - public override void onMouseDoubleClick (object sender, MouseButtonEventArgs e) - { - doubleClicked = true; - base.onMouseDoubleClick (sender, e); - - buffer.GotoWordStart (); - buffer.SetSelStartPos (); - buffer.GotoWordEnd (); - buffer.SetSelEndPos (); - } - - public override void onMouseWheel (object sender, MouseWheelEventArgs e) - { - base.onMouseWheel (sender, e); - } - #endregion - - #region Keyboard handling - public override void onKeyDown (object sender, KeyboardKeyEventArgs e) - { - //base.onKeyDown (sender, e); - - Key key = e.Key; - - switch (key) - { - case Key.Back: - buffer.DeleteChar (); - break; - case Key.Clear: - break; - case Key.Delete: - if (buffer.SelectionIsEmpty) - MoveRight (); - else if (e.Shift) - CurrentInterface.Clipboard = buffer.SelectedText; - buffer.DeleteChar (); - break; - case Key.Enter: - case Key.KeypadEnter: - if (!buffer.SelectionIsEmpty) - buffer.DeleteChar (); - buffer.InsertLineBreak (); - break; - case Key.Escape: - buffer.ResetSelection (); - break; - case Key.Home: - if (e.Shift) { - if (buffer.SelectionIsEmpty) - buffer.SetSelStartPos (); - if (e.Control) - buffer.CurrentLine = 0; - buffer.CurrentColumn = 0; - buffer.SetSelEndPos (); - break; - } - buffer.ResetSelection (); - if (e.Control) - buffer.CurrentLine = 0; - buffer.CurrentColumn = 0; - break; - case Key.End: - if (e.Shift) { - if (buffer.SelectionIsEmpty) - buffer.SetSelStartPos (); - if (e.Control) - buffer.CurrentLine = int.MaxValue; - buffer.CurrentColumn = int.MaxValue; - buffer.SetSelEndPos (); - break; - } - buffer.ResetSelection (); - if (e.Control) - buffer.CurrentLine = int.MaxValue; - buffer.CurrentColumn = int.MaxValue; - break; - case Key.Insert: - if (e.Shift) - buffer.Insert (CurrentInterface.Clipboard); - else if (e.Control && !buffer.SelectionIsEmpty) - CurrentInterface.Clipboard = buffer.SelectedText; - break; - case Key.Left: - if (e.Shift) { - if (buffer.SelectionIsEmpty) - buffer.SetSelStartPos (); - if (e.Control) - buffer.GotoWordStart (); - else - MoveLeft (); - buffer.SetSelEndPos (); - break; - } - buffer.ResetSelection (); - if (e.Control) - buffer.GotoWordStart (); - else - MoveLeft(); - break; - case Key.Right: - if (e.Shift) { - if (buffer.SelectionIsEmpty) - buffer.SetSelStartPos (); - if (e.Control) - buffer.GotoWordEnd (); - else - MoveRight (); - buffer.SetSelEndPos (); - break; - } - buffer.ResetSelection (); - if (e.Control) - buffer.GotoWordEnd (); - else - MoveRight (); - break; - case Key.Up: - if (e.Shift) { - if (buffer.SelectionIsEmpty) - buffer.SetSelStartPos (); - PrintedCurrentLine--; - buffer.SetSelEndPos (); - break; - } - buffer.ResetSelection (); - PrintedCurrentLine--; - break; - case Key.Down: - if (e.Shift) { - if (buffer.SelectionIsEmpty) - buffer.SetSelStartPos (); - PrintedCurrentLine++; - buffer.SetSelEndPos (); - break; - } - buffer.ResetSelection (); - PrintedCurrentLine++; - break; - case Key.Menu: - break; - case Key.NumLock: - break; - case Key.PageDown: - if (e.Shift) { - if (buffer.SelectionIsEmpty) - buffer.SetSelStartPos (); - PrintedCurrentLine += visibleLines; - buffer.SetSelEndPos (); - break; - } - buffer.ResetSelection (); - PrintedCurrentLine += visibleLines; - break; - case Key.PageUp: - if (e.Shift) { - if (buffer.SelectionIsEmpty) - buffer.SetSelStartPos (); - PrintedCurrentLine -= visibleLines; - buffer.SetSelEndPos (); - break; - } - buffer.ResetSelection (); - PrintedCurrentLine -= visibleLines; - break; - case Key.RWin: - break; - case Key.Tab: - buffer.Insert ("\t"); - break; - case Key.F8: - toogleFolding (buffer.CurrentLine); -// if (parser != null) -// reparseSource (); - break; - default: - break; - } - RegisterForGraphicUpdate(); - } - public override void onKeyPress (object sender, KeyPressEventArgs e) - { - base.onKeyPress (sender, e); - - buffer.Insert (e.KeyChar.ToString()); - buffer.ResetSelection (); - - //RegisterForGraphicUpdate(); - } - #endregion - } -} \ No newline at end of file diff --git a/src/TextFormatting.cs b/src/TextFormatting.cs deleted file mode 100644 index f7b2e51..0000000 --- a/src/TextFormatting.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace Crow.Coding -{ - public struct TextFormatting { - public Color Foreground; - public Color Background; - public bool Bold; - public bool Italic; - - public TextFormatting(Color fg, Color bg, bool bold = false, bool italic = false){ - Foreground = fg; - Background = bg; - Bold = bold; - Italic = italic; - } - } -} - diff --git a/src/Token.cs b/src/Token.cs deleted file mode 100644 index e0cb7e0..0000000 --- a/src/Token.cs +++ /dev/null @@ -1,71 +0,0 @@ -// -// Token.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2017 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -using System; -using CrowEdit; - -namespace Crow.Coding -{ - public struct Token - { - public Parser.TokenType Type; - public string Content; - public Point Start; - public Point End; - - public string PrintableContent { - get { return string.IsNullOrEmpty(Content) ? "" : Content.Replace("\t", new String(' ', Interface.TabSize)); } - } - -// public Token (TokenType tokType, string content = ""){ -// Type = tokType; -// Content = content; -// } - - public bool IsEmpty { get { return string.IsNullOrEmpty(Content); }} - - public static bool operator == (Token t, System.Enum tt){ - return Convert.ToInt32(t.Type) == Convert.ToInt32(tt); - } - public static bool operator != (Token t, System.Enum tt){ - return Convert.ToInt32(t.Type) != Convert.ToInt32(tt); - } - public static bool operator == (System.Enum tt, Token t){ - return Convert.ToInt32(t.Type) == Convert.ToInt32(tt); - } - public static bool operator != (System.Enum tt, Token t){ - return Convert.ToInt32(t.Type) != Convert.ToInt32(tt); - } - - public static Token operator +(Token t, char c){ - t.Content += c; - return t; - } - public static Token operator +(Token t, string s){ - t.Content += s; - return t; - } - public override string ToString () - { - return string.Format ("[Tok{2}->{3}:{0}: {1}]", Type,Content,Start,End); - } - } -} - diff --git a/src/XMLParser.cs b/src/XMLParser.cs deleted file mode 100644 index 2a8ffeb..0000000 --- a/src/XMLParser.cs +++ /dev/null @@ -1,299 +0,0 @@ -using System; -using Crow; -using System.Collections.Generic; -using System.Text.RegularExpressions; -using System.Diagnostics; -using System.Linq; - -namespace Crow.Coding -{ - public class XMLParser : Parser - { - public new enum TokenType { - Unknown = Parser.TokenType.Unknown, - WhiteSpace = Parser.TokenType.WhiteSpace, - NewLine = Parser.TokenType.NewLine, - LineComment = Parser.TokenType.LineComment, - BlockCommentStart = Parser.TokenType.BlockCommentStart, - BlockComment = Parser.TokenType.BlockComment, - BlockCommentEnd = Parser.TokenType.BlockCommentEnd, - Affectation = Parser.TokenType.Affectation, - XMLDecl = Parser.TokenType.Preprocessor, - ElementStart, - ElementEnd, - ElementClosing = Parser.TokenType.StatementEnding, - ElementName = Parser.TokenType.Type, - AttributeName = Parser.TokenType.Identifier, - AttributeValueOpening = Parser.TokenType.StringLitteralOpening, - AttributeValueClosing = Parser.TokenType.StringLitteralClosing, - AttributeValue = Parser.TokenType.StringLitteral, - } - - public enum States - { - init, //first statement of prolog, xmldecl should only apear in this state - prolog, //misc before doctypedecl - InternalSubset, //doctype declaration subset - ExternalSubsetInit, - ExternalSubset, - BlockComment, - DTDEnd,//doctype finished - XML,//normal xml - StartTag,//inside start tag - Content,//after start tag with no closing slash - EndTag - } - - #region CTOR - public XMLParser (CodeBuffer _buffer) : base(_buffer) {} - #endregion - - enum Keywords - { - DOCTYPE, - ELEMENT, - ATTLIST, - ENTITY, - NOTATION - } - - States curState = States.init; - - #region Regular Expression for validity checks - //private static Regex rxValidChar = new Regex("[\u0020-\uD7FF]"); - private static Regex rxValidChar = new Regex(@"\u0009|\u000A|\u000D|[\u0020-\uD7FF]|[\uE000-\uFFFD]"); //| [\u10000-\u10FFFF] unable to set those plans - private static Regex rxNameStartChar = new Regex(@":|[A-Z]|_|[a-z]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u02FF]|[\u0370-\u037D]|[\u037F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]"); // | [\u10000-\uEFFFF] - private static Regex rxNameChar = new Regex(@":|[A-Z]|_|[a-z]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u02FF]|[\u0370-\u037D]|[\u037F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]|-|\.|[0-9]|\u00B7|[\u0300-\u036F]|[\u203F-\u2040]");//[\u10000-\uEFFFF]| - private static Regex rxDecimal = new Regex(@"[0-9]+"); - private static Regex rxHexadecimal = new Regex(@"[0-9a-fA-F]+"); - private static Regex rxAttributeValue = new Regex(@"[^<]"); - private static Regex rxEntityValue = new Regex(@"[^<]"); - private static Regex rxPubidChar = new Regex(@"\u0020|\u000D|\u000A|[a-zA-Z0-9]|[-\(\)\+\,\./:=\?;!\*#@\$_%]"); - #endregion - - #region Character ValidityCheck - public bool nextCharIsValidCharStartName - { - get { return rxNameStartChar.IsMatch(new string(new char[]{Peek()})); } - } - public bool nextCharIsValidCharName - { - get { return rxNameChar.IsMatch(new string(new char[]{Peek()})); } - } - #endregion - - public override void SetLineInError (ParsingException ex) - { - base.SetLineInError (ex); - //buffer[ex.Line].Tokens.EndingState = (int)States.init; - } - - public override void ParseCurrentLine () - { - Debug.WriteLine (string.Format("parsing line:{0}", currentLine)); - CodeLine cl = buffer [currentLine]; - cl.Tokens = new List (); - - //retrieve current parser state from previous line - if (currentLine > 0) - curState = (States)buffer[currentLine - 1].EndingState; - else - curState = States.init; - - States previousEndingState = (States)cl.EndingState; - - - while (! eol) { - SkipWhiteSpaces (); - - if (eol) - break; - - if (Peek () == '\n') { - if (currentTok != TokenType.Unknown) - throw new ParsingException (this, "Unexpected end of line"); - Read (); - eol = true; - continue; - } - - if (curState == States.BlockComment) { - if (currentTok != TokenType.Unknown) - Debugger.Break (); - - currentTok.Start = CurrentPosition; - currentTok.Type = (Parser.TokenType)TokenType.BlockComment; - currentTok += ReadLineUntil ("-->"); - if (Peek (3) == "-->") { - readToCurrTok (3); - curState = States.XML; - } - saveAndResetCurrentTok (); - continue; - } - - switch (Peek()) { - case '<': - readToCurrTok (true); - switch (Peek()) { - case '?': - if (curState != States.init) - throw new ParsingException (this, "xml decl may appear only on first line"); - readToCurrTok (); - currentTok += ReadLineUntil ("?>"); - if (Peek (2) != "?>") - throw new ParsingException (this, "expecting '?>'"); - readToCurrTok (2); - saveAndResetCurrentTok (TokenType.XMLDecl); - curState = States.prolog; - break; - case '!': - readToCurrTok (); - switch (Peek()) { - case '-': - readToCurrTok (); - if (Peek () != '-') - throw new ParsingException (this, "Expecting comment start tag"); - readToCurrTok (); - currentTok += ReadLineUntil ("--"); - if (Peek (3) == "-->") { - readToCurrTok (3); - }else - curState = States.BlockComment; - saveAndResetCurrentTok (TokenType.BlockComment); - break; - default: - throw new ParsingException(this, "error"); - } - break; - default: - if (!(curState == States.Content || curState == States.XML || curState == States.init || curState == States.prolog)) - throw new ParsingException (this, "Unexpected char: '<'"); - if (Peek () == '/') { - curState = States.EndTag; - readToCurrTok (); - saveAndResetCurrentTok (TokenType.ElementEnd); - } else { - curState = States.StartTag; - saveAndResetCurrentTok (TokenType.ElementStart); - } - - if (!nextCharIsValidCharStartName) - throw new ParsingException (this, "Expected element name"); - - readToCurrTok (true); - while (nextCharIsValidCharName) - readToCurrTok (); - - saveAndResetCurrentTok (TokenType.ElementName); - break; - } - break; - case '/': - if (curState != States.StartTag) - throw new ParsingException (this, "Unexpected char: '/'"); - readToCurrTok (true); - if (Peek () != '>') - throw new ParsingException (this, "Expecting '>'"); - readAndResetCurrentTok (TokenType.ElementEnd); - - curState = States.XML; - break; - case '>': - readAndResetCurrentTok (TokenType.ElementClosing, true); - switch (curState) { - case States.EndTag: - curState = States.XML; - break; - case States.StartTag: - curState = States.Content; - break; - default: - throw new ParsingException (this, "Unexpected char: '>'"); - } - break; - default: - switch (curState) { - case States.StartTag: - if (!nextCharIsValidCharStartName) - throw new ParsingException (this, "Expected attribute name"); - readToCurrTok (true); - while (nextCharIsValidCharName) - readToCurrTok (); - saveAndResetCurrentTok (TokenType.AttributeName); - if (Peek () != '=') - throw new ParsingException (this, "Expecting: '='"); - readAndResetCurrentTok (TokenType.Affectation, true); - - char openAttVal = Peek (); - if (openAttVal != '"' && openAttVal != '\'') - throw new ParsingException (this, "Expecting attribute value enclosed either in '\"' or in \"'\""); - readAndResetCurrentTok (TokenType.AttributeValueOpening, true); - - currentTok.Start = CurrentPosition; - currentTok.Content = ReadLineUntil (new string (new char[]{ openAttVal })); - saveAndResetCurrentTok (TokenType.AttributeValue); - - if (Peek () != openAttVal) - throw new ParsingException (this, string.Format ("Expecting {0}", openAttVal)); - readAndResetCurrentTok (TokenType.AttributeValueClosing, true); - break; - default: - throw new ParsingException (this, "unexpected char: " + Peek ()); - } - break; - } - } - - if (cl.EndingState != (int)curState && currentLine < buffer.LineCount - 1) - buffer [currentLine + 1].Tokens = null; - - cl.EndingState = (int)curState; - } - - public override void SyntaxAnalysis () - { - RootNode = new Node () { Name = "RootNode", Type="Root" }; - - Node currentNode = RootNode; - - for (int i = 0; i < buffer.LineCount; i++) { - CodeLine cl = buffer[i]; - if (cl.Tokens == null) - continue; - cl.SyntacticNode = null; - - int tokPtr = 0; - while (tokPtr < cl.Tokens.Count) { - switch ((XMLParser.TokenType)cl.Tokens [tokPtr].Type) { - case TokenType.ElementStart: - tokPtr++; - Node newElt = new Node () { Name = cl.Tokens [tokPtr].Content, StartLine = cl }; - currentNode.AddChild (newElt); - currentNode = newElt; - if (cl.SyntacticNode == null) - cl.SyntacticNode = newElt; - break; - case TokenType.ElementEnd: - tokPtr++; - if (tokPtr < cl.Tokens.Count) { - if ((XMLParser.TokenType)cl.Tokens [tokPtr].Type == TokenType.ElementName && - cl.Tokens [tokPtr].Content != currentNode.Name) - throw new ParsingException (this, "Closing tag mismatch"); - } - currentNode.EndLine = cl; - currentNode = currentNode.Parent; - break; - case TokenType.ElementClosing: - //currentNode = currentNode.Parent; - break; - default: - break; - } - tokPtr++; - } - } - } - } -} - diff --git a/ui/EditorOptions.crow b/ui/EditorOptions.crow index bf2a2bc..81e529b 100644 --- a/ui/EditorOptions.crow +++ b/ui/EditorOptions.crow @@ -1,5 +1,5 @@  - + diff --git a/ui/icons/basic_floppydisk.svg b/ui/icons/basic_floppydisk.svg index 55d901d..85fbab9 100644 --- a/ui/icons/basic_floppydisk.svg +++ b/ui/icons/basic_floppydisk.svg @@ -3,13 +3,13 @@ - - - - - - - - - + + + + + + + + + diff --git a/ui/main.crow b/ui/main.crow index edad15d..07847f9 100755 --- a/ui/main.crow +++ b/ui/main.crow @@ -1,47 +1,61 @@  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +